Linux网络驱动之Fixed-Link(11)

发布时间:2026/6/4 9:40:37

Linux网络驱动之Fixed-Link(11) 接前一篇文章Linux网络驱动之Fixed-Link10本文内容参考linux phy fixed-link-CSDN博客fixed-link 网口驱动设备树-CSDN博客GMAC网卡Fixed-Link模式 - StepForwards - 博客园RTL8367RB的国产P2P替代方案用JL6107-PC的可行性及实现方法-CSDN博客linux phy处理流程一:探测phy设备_phy link过程-CSDN博客设备树 fixed-link 使用说明-CSDN博客Linux 网络驱动-MAC、PHY层驱动框架(三)_ethernet-phy-ieee802.3-c22-CSDN博客特此致谢三、深入了解4. Linux内核中Fixed-Link的相关内容2PHY驱动driver匹配上一回继续讲解of_phy_get_and_connect函数中的of_phy_connect函数代码讲到了其中调用的phy_connect_direct函数中的phy_attach_direct函数。上一回总体介绍了该函数本回深入解析函数代码。为了便于理解和回顾再次贴出phy_attach_direct函数代码在drivers/net/mdio/of_mdio.c中如下/** * phy_attach_direct - attach a network device to a given PHY device pointer * dev: network device to attach * phydev: Pointer to phy_device to attach * flags: PHY devices dev_flags * interface: PHY devices interface * * Description: Called by drivers to attach to a particular PHY * device. The phy_device is found, and properly hooked up * to the phy_driver. If no driver is attached, then a * generic driver is used. The phy_device is given a ptr to * the attaching device, and given a callback for link status * change. The phy_device is returned to the attaching driver. * This function takes a reference on the phy device. */ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, u32 flags, phy_interface_t interface) { struct mii_bus *bus phydev-mdio.bus; struct device *d phydev-mdio.dev; struct module *ndev_owner NULL; bool using_genphy false; int err; /* For Ethernet device drivers that register their own MDIO bus, we * will have bus-owner match ndev_mod, so we do not want to increment * our own module-refcnt here, otherwise we would not be able to * unload later on. */ if (dev) ndev_owner dev-dev.parent-driver-owner; if (ndev_owner ! bus-owner !try_module_get(bus-owner)) { phydev_err(phydev, failed to get the bus module\n); return -EIO; } get_device(d); /* Assume that if there is no driver, that it doesnt * exist, and we should use the genphy driver. */ if (!d-driver) { if (phydev-is_c45) d-driver genphy_c45_driver.mdiodrv.driver; else d-driver genphy_driver.mdiodrv.driver; using_genphy true; } if (!try_module_get(d-driver-owner)) { phydev_err(phydev, failed to get the device driver module\n); err -EIO; goto error_put_device; } if (using_genphy) { err d-driver-probe(d); if (err 0) err device_bind_driver(d); if (err) goto error_module_put; } if (phydev-attached_dev) { dev_err(dev-dev, PHY already attached\n); err -EBUSY; goto error; } phydev-phy_link_change phy_link_change; if (dev) { phydev-attached_dev dev; dev-phydev phydev; if (phydev-sfp_bus_attached) dev-sfp_bus phydev-sfp_bus; else if (dev-sfp_bus) phydev-is_on_sfp_module true; } /* Some Ethernet drivers try to connect to a PHY device before * calling register_netdevice() - netdev_register_kobject() and * does the dev-dev.kobj initialization. Here we only check for * success which indicates that the network device kobject is * ready. Once we do that we still need to keep track of whether * links were successfully set up or not for phy_detach() to * remove them accordingly. */ phydev-sysfs_links false; phy_sysfs_create_links(phydev); if (!phydev-attached_dev) { err sysfs_create_file(phydev-mdio.dev.kobj, dev_attr_phy_standalone.attr); if (err) phydev_err(phydev, error creating phy_standalone sysfs entry\n); } phydev-dev_flags | flags; phydev-interface interface; phydev-state PHY_READY; phydev-interrupts PHY_INTERRUPT_DISABLED; /* Port is set to PORT_TP by default and the actual PHY driver will set * it to different value depending on the PHY configuration. If we have * the generic PHY driver we cant figure it out, thus set the old * legacy PORT_MII value. */ if (using_genphy) phydev-port PORT_MII; /* Initial carrier state is off as the phy is about to be * (re)initialized. */ if (dev) netif_carrier_off(phydev-attached_dev); /* Do initial configuration here, now that * we have certain key parameters * (dev_flags and interface) */ err phy_init_hw(phydev); if (err) goto error; phy_resume(phydev); phy_led_triggers_register(phydev); return err; error: /* phy_detach() does all of the cleanup below */ phy_detach(phydev); return err; error_module_put: module_put(d-driver-owner); d-driver NULL; error_put_device: put_device(d); if (ndev_owner ! bus-owner) module_put(bus-owner); return err; } EXPORT_SYMBOL(phy_attach_direct);来看phy_attach_direct函数具体代码。先来看第1段代码片段/* Assume that if there is no driver, that it doesnt * exist, and we should use the genphy driver. */ if (!d-driver) { if (phydev-is_c45) d-driver genphy_c45_driver.mdiodrv.driver; else d-driver genphy_driver.mdiodrv.driver; using_genphy true; }如果无附加驱动则使用默认phy驱动通用驱动程序即将using_genphy置为true。genphy_c45_driver的初始化代码在kernel/linux-5.10-origin/drivers/net/phy/phy-c45.c中如下struct phy_driver genphy_c45_driver { .phy_id 0xffffffff, .phy_id_mask 0xffffffff, .name Generic Clause 45 PHY, .read_status genphy_c45_read_status, };genphy_driver的初始化代码在kernel/linux-5.10-origin/drivers/net/phy/phy_device.c中如下static struct phy_driver genphy_driver { .phy_id 0xffffffff, .phy_id_mask 0xffffffff, .name Generic PHY, .get_features genphy_read_abilities, .suspend genphy_suspend, .resume genphy_resume, .set_loopback genphy_loopback, };特别说明phy_id是在mac驱动加载中获取的同时创建对应的phy_device并注册与phy驱动进行匹配。接下来看以下代码片段if (using_genphy) { err d-driver-probe(d); if (err 0) err device_bind_driver(d); if (err) goto error_module_put; }如果using_genphy为true也就是使用通用phy驱动则调用驱动的probe函数d-driver-probe。d-driver上边已经设置对于genphy_c45_driver 来说是genphy_c45_driver.mdiodrv.driver对于genphy_driver来说是genphy_driver.mdiodrv.driver。.mdiodrv.driver并未在上边的genphy_c45_driver和genphy_driver的初始化中被赋值而是在kernel/linux-5.10-origin/drivers/net/phy/phy_device.c中的phy_init函数中的phy_driver_register函数中赋值的也就是在初始化的时候动态赋值的相关代码如下tatic int __init phy_init(void) { int rc; ethtool_set_ethtool_phy_ops(phy_ethtool_phy_ops); rc mdio_bus_init(); if (rc) goto err_ethtool_phy_ops; features_init(); rc phy_driver_register(genphy_c45_driver, THIS_MODULE); if (rc) goto err_mdio_bus; rc phy_driver_register(genphy_driver, THIS_MODULE); if (rc) goto err_c45; return 0; err_c45: phy_driver_unregister(genphy_c45_driver); err_mdio_bus: mdio_bus_exit(); err_ethtool_phy_ops: ethtool_set_ethtool_phy_ops(NULL); return rc; }/** * phy_driver_register - register a phy_driver with the PHY layer * new_driver: new phy_driver to register * owner: module owning this PHY */ int phy_driver_register(struct phy_driver *new_driver, struct module *owner) { int retval; /* Either the features are hard coded, or dynamically * determined. It cannot be both. */ if (WARN_ON(new_driver-features new_driver-get_features)) { pr_err(%s: features and get_features must not both be set\n, new_driver-name); return -EINVAL; } new_driver-mdiodrv.flags | MDIO_DEVICE_IS_PHY; new_driver-mdiodrv.driver.name new_driver-name; new_driver-mdiodrv.driver.bus mdio_bus_type; new_driver-mdiodrv.driver.probe phy_probe; new_driver-mdiodrv.driver.remove phy_remove; new_driver-mdiodrv.driver.owner owner; new_driver-mdiodrv.driver.probe_type PROBE_FORCE_SYNCHRONOUS; retval driver_register(new_driver-mdiodrv.driver); if (retval) { pr_err(%s: Error %d in registering driver\n, new_driver-name, retval); return retval; } pr_debug(%s: Registered new driver\n, new_driver-name); return 0; } EXPORT_SYMBOL(phy_driver_register);可以看到不论是genphy_c45_driver还是genphy_driverd-driver-probe都是phy_probe()。phy_probe函数在同文件kernel/linux-5.10-origin/drivers/net/phy/phy_device.c中代码如下/** * phy_probe - probe and init a PHY device * dev: device to probe and init * * Description: Take care of setting up the phy_device structure, * set the state to READY (the drivers init function should * set it to STARTING if needed). */ static int phy_probe(struct device *dev) { struct phy_device *phydev to_phy_device(dev); struct device_driver *drv phydev-mdio.dev.driver; struct phy_driver *phydrv to_phy_driver(drv); int err 0; phydev-drv phydrv; /* Disable the interrupt if the PHY doesnt support it * but the interrupt is still a valid one */ if (!phy_drv_supports_irq(phydrv) phy_interrupt_is_valid(phydev)) phydev-irq PHY_POLL; if (phydrv-flags PHY_IS_INTERNAL) phydev-is_internal true; /* Deassert the reset signal */ phy_device_reset(phydev, 0); if (phydev-drv-probe) { err phydev-drv-probe(phydev); if (err) goto out; } /* Start out supporting everything. Eventually, * a controller will attach, and may modify one * or both of these values */ if (phydrv-features) { linkmode_copy(phydev-supported, phydrv-features); } else if (phydrv-get_features) { err phydrv-get_features(phydev); } else if (phydev-is_c45) { err genphy_c45_pma_read_abilities(phydev); } else { err genphy_read_abilities(phydev); } if (err) goto out; if (!linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev-supported)) phydev-autoneg 0; if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, phydev-supported)) phydev-is_gigabit_capable 1; if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, phydev-supported)) phydev-is_gigabit_capable 1; of_set_phy_supported(phydev); phy_advertise_supported(phydev); /* Get the EEE modes we want to prohibit. We will ask * the PHY stop advertising these mode later on */ of_set_phy_eee_broken(phydev); /* The Pause Frame bits indicate that the PHY can support passing * pause frames. During autonegotiation, the PHYs will determine if * they should allow pause frames to pass. The MAC driver should then * use that result to determine whether to enable flow control via * pause frames. * * Normally, PHY drivers should not set the Pause bits, and instead * allow phylib to do that. However, there may be some situations * (e.g. hardware erratum) where the driver wants to set only one * of these bits. */ if (!test_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev-supported) !test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev-supported)) { linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev-supported); linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev-supported); } /* Set the state to READY by default */ phydev-state PHY_READY; out: /* Re-assert the reset signal on error */ if (err) phy_device_reset(phydev, 1); return err; }总体来看phy_attach_direct函数中检查phydev是否已经匹配了phydrv。如果未匹配则使用通用phydrv。这就是为什么我们并没有专门编写phy驱动但是phy还是能够正常工作的原因。更多内容请看下回。

相关新闻