老子为什么写这篇博客
最近在调试imx93板子的ENET QOS口的dpdk功能,因为这个口配合22.11版本的dpdk以及2.7版本的内核,可以让这个网口正常工作,且用dpdk自带的 l2fwd 工具测试成功的,且用自己的一个客户端小程序给该板子的ENET QOS口打流,是可以看到发送的数据可以被正常返回。
But, 我的目标是让这个网口能跑 EtherCAT,问题是Ethercat都是默认100M且无自协商功能的。而默认的ENET QOS跑dpdk是1000M有自协商功能的。所以这个网口无法实现对伺服电机的驱动。
可能的解决思路:用ethtool,网上铺天盖地都是这个,but,因为跑起来dpdk后,ethtool就看不到这个qos口啦,死路一条。我只能在qos不跑dpdk的情况下,修改内核代码,然后编译内核,跑不用dpdk的设备树,板子起来后通过ethtool看对内核代码的修改有无效果(步骤冗长,内核代码不懂)。
正因为看不懂内核代码,所以我需要学习一下这一块,这不有人已经分析过了,还得站在巨人的肩膀上啊。
1. phy 驱动的原理简介
phy 驱动的原理是非常简单的,一般流程如下:
- List item 用轮询/中断的方式通过 mdio 总线读取 phy 芯片的状态。
- 在 phy link 状态变化的情况下,正确配置 mac 的状态。(例如:根据 phy 自协商的速率 10/100/1000M 把mac 配置成对应速率)
2. 什么是PHY、MAC、MDIO等基础
以太网硬件主要分为MAC和PHY两块,他俩之间有两套接口,一个数据接口,一个管理接口,数据接口可能是MII,GMII,RGMII,SGMII,管理接口为MDIO总线。MDIO是双向的,可以读PHY寄存器,也可以写PHY寄存器,而数据接口用于传输数据,对于RGMII而言,如果以太网工作在1000M,则RGMII的频率为125MHz,如果以太网工作在100M,则RGMII的频率为25MHz。
PHY的驱动代码是drivers/net/phy
目录下的phy.c,MAC的驱动代码是drivers/net/ethernet/
硬件架构相关图片
什么是MII:(Media Independent Interface)即媒体独立接口,该接口支持 10Mb/s 与 100Mb/s 的数据传输速率,数据传输的位宽为 4 位。(RMII、SMII、GMII均为从MII简化出来的标准,线少了)
3. 控制器驱动框架
PHY的控制器一般被描述为mdio_bus平台设备(既然是平台设备,那么设备树中必定要有可以被解析为平台设备的节点,也要有对应的平台设备驱动。与SPI驱动类似,PHY设备模型也是在控制器驱动的probe函数中注册的。)如下:
module_platform_driver(fec_enet_uio_driver); //注册控制器平台设备驱动
4. mdio bus
phy设备驱动基于device,driver,bus的链接方式驱动涉及如下重要部分:
- 总线- sturct mii_bus (mii stand for media independent interface)
- 设备- struct phy_device
- 驱动- struct phy_driver
drivers/net/phy/mdio_bus.c
1 struct bus_type mdio_bus_type = {
2 .name = "mdio_bus",
3 .dev_groups = mdio_bus_dev_groups,
4 .match = mdio_bus_match,
5 .uevent = mdio_uevent,
6 };
mdio_bus总线注册相关函数解析
总线注册的入口函数
# \drivers\net\phy\phy_device.c
static int __init phy_init(void)
{
int rc;
rc = mdio_bus_init(); //mdio_bus总线的注册
if (rc)
return rc;
rc = phy_drivers_register(genphy_driver,ARRAY_SIZE(genphy_driver), THIS_MODULE); //通用PHY驱动
if (rc)
mdio_bus_exit();
return rc;
}
subsys_initcall(phy_init);
subsys_initcall(phy_init)
这行的作用非常重要,这一行就决定了内核在启动的时候会调用该函数,注册完了之后紧接着又注册一个通用的PHY驱动。
5. phy_device设备注册相关函数解析
在phy_init函数中不仅注册了mdio_bus总线,还注册了一个通用的PHY驱动作为缺省的内核PHY驱动,但是如果PHY芯片的内部寄存器和802.3定义的并不一样或者需要特殊的功能配置以实现更强的功能,这就需要专有的驱动。
对于市场上存在的主流PHY品牌,一般在内核源码 drivers/net/phy目录下都有对应的驱动。
phy驱动的注册
(1)同一品牌的PHY设备有多种不同的型号,内核为了支持一次可以注册多个型号的PHY的驱动,在include\linux\phy.h中提供了用于注册PHY驱动的宏module_phy_driver。该宏的定义如下:
1 /**
2 * phy_module_driver() - Helper macro for registering PHY drivers
3 * @__phy_drivers: array of PHY drivers to register
4 * @__count: Numbers of members in array
5 *
6 * Helper macro for PHY drivers which do not do anything special in module
7 * init/exit. Each module may only use this macro once, and calling it
8 * replaces module_init() and module_exit().
9 */
10 #define phy_module_driver(__phy_drivers, __count) \
11 static int __init phy_module_init(void) \
12 { \
13 return phy_drivers_register(__phy_drivers, __count, THIS_MODULE); \
14 } \
15 module_init(phy_module_init); \
16 static void __exit phy_module_exit(void) \
17 { \
18 phy_drivers_unregister(__phy_drivers, __count); \
19 } \
20 module_exit(phy_module_exit)
21
22 #define module_phy_driver(__phy_drivers) \
23 phy_module_driver(__phy_drivers, ARRAY_SIZE(__phy_drivers))
24
25 bool phy_driver_is_genphy(struct phy_device *phydev);
26 bool phy_driver_is_genphy_10g(struct phy_device *phydev);
(2)其中phy_driver_register
定义如下
1 /**
2 * phy_driver_register - register a phy_driver with the PHY layer
3 * @new_driver: new phy_driver to register
4 * @owner: module owning this PHY
5 */
6 int phy_driver_register(struct phy_driver *new_driver, struct module *owner)
7 {
8 int retval;
9
10 /* Either the features are hard coded, or dynamically
11 * determined. It cannot be both.
12 */
13 if (WARN_ON(new_driver->features && new_driver->get_features)) {
14 pr_err("%s: features and get_features must not both be set\n",
15 new_driver->name);
16 return -EINVAL;
17 }
18
19 /* PHYLIB device drivers must not match using a DT compatible table
20 * as this bypasses our checks that the mdiodev that is being matched
21 * is backed by a struct phy_device. If such a case happens, we will
22 * make out-of-bounds accesses and lockup in phydev->lock.
23 */
24 if (WARN(new_driver->mdiodrv.driver.of_match_table,
25 "%s: driver must not provide a DT match table\n",
26 new_driver->name))
27 return -EINVAL;
28
29 new_driver->mdiodrv.flags |= MDIO_DEVICE_IS_PHY;
30 new_driver->mdiodrv.driver.name = new_driver->name;
31 new_driver->mdiodrv.driver.bus = &mdio_bus_type;
32 new_driver->mdiodrv.driver.probe = phy_probe;
33 new_driver->mdiodrv.driver.remove = phy_remove;
34 new_driver->mdiodrv.driver.shutdown = phy_shutdown;
35 new_driver->mdiodrv.driver.owner = owner;
36 new_driver->mdiodrv.driver.probe_type = PROBE_FORCE_SYNCHRONOUS;
37
38 retval = driver_register(&new_driver->mdiodrv.driver);
39 if (retval) {
40 pr_err("%s: Error %d in registering driver\n",
41 new_driver->name, retval);
42
43 return retval;
44 }
45
46 pr_debug("%s: Registered new driver\n", new_driver->name);
47
48 return 0;
49 }
50 EXPORT_SYMBOL(phy_driver_register);
51
52 int phy_drivers_register(struct phy_driver *new_driver, int n,
53 struct module *owner)
54 {
55 int i, ret = 0;
56
57 for (i = 0; i < n; i++) {
58 ret = phy_driver_register(new_driver + i, owner);
59 if (ret) {
60 while (i-- > 0)
61 phy_driver_unregister(new_driver + i);
62 break;
63 }
64 }
65 return ret;
66 }
67 EXPORT_SYMBOL(phy_drivers_register);
原文链接:https://blog.csdn.net/pwl999/article/details/128339747 ;本文仅作为交流分享,巩固所学,感谢原作者八辈祖宗。