3. PHY 子系统:PHY 状态机、phy_device 结构体、phy_driver 注册流程

PHY(Physical Layer)子系统是 Linux 内核网络驱动中连接 MAC 层与物理介质的核心枢纽。它负责管理物理层芯片的状态转换、链路检测、自动协商以及寄存器读写。理解 PHY 状态机、核心数据结构 phy_device 以及驱动注册流程,是掌握网络驱动中断与链路检测协同的基础。

3.1 PHY 状态机:链路生命周期的核心

内核 PHY 层维护了一个有限状态机,用于描述 PHY 芯片从上电到链路建立、断开及错误恢复的完整过程。状态机定义在 <linux/phy.h> 中,由 phy_state 枚举表示。

核心状态一览: 状态机共包含 8 个状态,驱动在每次中断或轮询时调用 phy_state_machine() 进行状态迁移。
状态 枚举值 说明 典型触发条件
PHY_DOWN 0 PHY 未初始化或已关闭 模块卸载、硬件复位
PHY_READY 1 PHY 硬件就绪,等待启动自动协商 phy_start() 调用
PHY_UP 2 自动协商进行中 phy_start_aneg() 触发
PHY_AN 3 自动协商完成,等待链路建立 AN 完成中断
PHY_RUNNING 4 链路已建立,正常收发数据 链路状态变为 UP
PHY_NOLINK 5 链路断开,等待重连 链路丢失中断
PHY_FORCING 6 强制模式配置中(非自动协商) 用户强制设置速率/双工
PHY_HALTED 7 PHY 暂停,通常用于电源管理 phy_stop() 调用
关键设计点: 状态机在 PHY_RUNNING 和 PHY_NOLINK 之间来回切换,这是中断处理与链路检测协同最频繁的路径。每次切换都会触发 netif_carrier_on/off() 通知网络层。
PHY 状态机迁移图 PHY_DOWN PHY_READY PHY_UP PHY_AN PHY_RUNNING PHY_NOLINK PHY_FORCING PHY_HALTED phy_start() phy_start_aneg() AN 完成 链路 UP 链路丢失 链路恢复 重协商 强制模式 配置完成 phy_stop() 状态机设计要点: 1. PHY_RUNNING ↔ PHY_NOLINK 是中断处理最频繁的路径,每次切换触发 carrier_on/off。 2. PHY_UP → PHY_AN 的迁移由自动协商完成中断驱动,超时则回退到 PHY_NOLINK。 3. PHY_HALTED 是电源管理入口,phy_stop() 可从任何状态进入。 4. 状态机由 workqueue 中的 phy_state_machine() 周期性执行,间隔由 phy_polling 控制。

3.2 phy_device 结构体:PHY 设备的抽象

phy_device 是内核中描述单个 PHY 芯片的核心数据结构,定义在 <linux/phy.h>。它包含了 PHY 的硬件信息、状态、配置回调以及连接关系。

结构体关键字段分类: 以下表格按功能模块划分 phy_device 的主要成员。
分类 字段名 类型 说明
硬件标识 phy_id u32 PHY 芯片的 32 位标识符(OUI + 型号 + 版本)
is_c45 bool 是否为 Clause 45 标准 PHY
dev_flags u32 设备特定标志,由板级代码或驱动设置
状态管理 state enum phy_state 当前 PHY 状态机状态
link bool 当前链路状态(true=UP, false=DOWN)
autoneg bool 是否启用自动协商
链路参数 speed int 当前协商速率(10/100/1000/2500 Mbps)
duplex int 双工模式(DUPLEX_HALF / DUPLEX_FULL)
pause int 流控能力(对称/非对称/无)
驱动绑定 drv struct phy_driver * 指向匹配的 PHY 驱动
bus struct mii_bus * 所属的 MII 总线
中断与轮询 irq int PHY 中断号(0 表示轮询模式)
phy_link_change void (*)(struct phy_device *) 链路变化回调函数
MAC 连接 attached_dev struct net_device * 指向绑定的网络设备(MAC 层)
编程要点: 在中断处理中,驱动通常通过 phydev->link 判断当前链路状态,并通过 phydev->state 确认状态机是否处于 PHY_RUNNING。不要直接修改 state,应使用 phy_state_machine() 或 phy_change() 进行安全迁移。
phy_device 结构体关系图 phy_device struct phy_device 硬件标识 phy_id, is_c45 dev_flags, addr 状态管理 state, link autoneg 驱动绑定 drv → phy_driver bus → mii_bus 链路参数 speed, duplex pause, advertising MAC 连接 attached_dev → net_device 中断与轮询 irq, phy_link_change phy_device 通过 attached_dev 与 net_device 绑定,通过 drv 与 phy_driver 关联,通过 bus 访问 MII 寄存器。

3.3 phy_driver 注册流程:从模块加载到设备匹配

PHY 驱动的注册遵循 Linux 设备驱动模型的标准流程。一个 PHY 驱动需要实现 struct phy_driver 中的回调函数,并通过 phy_driver_register() 或 phy_drivers_register() 注册到内核。

3.3.1 phy_driver 结构体核心回调

回调函数 原型 调用时机 典型实现
config_init int (*)(struct phy_device *) PHY 设备初始化时 配置硬件特定寄存器、校准
config_aneg int (*)(struct phy_device *) 启动自动协商时 写入 AN 广告寄存器、重启 AN
read_status int (*)(struct phy_device *) 状态机轮询或中断时 读取 BMSR/ANLPAR,更新 speed/duplex/link
ack_interrupt int (*)(struct phy_device *) 中断触发后 读取中断状态寄存器以清除中断
config_intr int (*)(struct phy_device *) 使能/禁用中断时 写中断使能寄存器
suspend/resume int (*)(struct phy_device *) 系统电源管理时 进入/退出低功耗模式

3.3.2 注册流程步骤

  1. 定义 phy_driver 结构体: 填充 phy_id、name、features 以及上述回调函数。
  2. 调用注册 API: 在模块初始化函数中调用 phy_driver_register(&my_phy_driver) 或批量注册 phy_drivers_register()。
  3. 内核匹配: 内核通过 phy_id 与 MII 总线上扫描到的 PHY 设备进行匹配,匹配成功后调用 probe 回调。
  4. 设备初始化: 在 probe 中完成硬件初始化,设置 phydev->drv 指向当前驱动。
  5. 状态机启动: 上层调用 phy_start() 后,状态机开始运行,驱动回调被逐步调用。
常见错误:
  • phy_id 掩码设置错误导致驱动无法匹配设备。应使用 PHY_ID_MATCH_EXACT(id) 或 PHY_ID_MATCH_MODEL(id) 宏。
  • read_status 回调未正确更新 phydev->link,导致状态机无法进入 PHY_RUNNING。
  • 中断模式下未实现 ack_interrupt,导致中断风暴。
phy_driver 注册与匹配流程 1. 定义 phy_driver 填充 phy_id, 回调 2. 调用注册 API phy_driver_register() 3. 内核匹配 phy_id 匹配设备 4. probe 回调 硬件初始化 5. 状态机启动 phy_start() 触发 6. 链路检测 中断/轮询驱动 注册流程关键代码示例: static struct phy_driver my_phy_driver = { .phy_id = 0x1234d500, .name = "My