深入Linux内核fixed-link如何用软件‘伪造’一个PHY设备来驱动MAC直连在嵌入式系统和网络设备开发中我们经常会遇到MAC控制器需要直接连接另一个MAC的情况而中间没有物理PHY芯片。这种场景下Linux内核通过一种精妙的软件模拟机制——fixed-link实现了对无PHY硬件的完美支持。本文将带您深入内核源码揭示这一机制背后的设计哲学与实现细节。1. fixed-link的起源与设计动机现代网络设备中MAC与PHY的协作是网络通信的基础。但在某些特定场景下比如两块开发板通过RJ45接口直连芯片内部两个MAC控制器直接对接低成本设备省略外部PHY芯片这些情况下传统PHY芯片的缺失会导致一系列问题协商机制失效没有PHY意味着无法自动协商速率、双工模式等参数状态监测困难链路状态、错误统计等关键信息无法获取驱动兼容性问题现有网络驱动架构严重依赖PHY抽象Linux内核的解决方案是引入fixed-link机制其核心设计理念是透明兼容让MAC驱动无需修改就能工作配置灵活通过设备树指定连接参数性能无损避免不必要的软件开销2. fixed-link的软件架构剖析fixed-link的实现涉及内核网络子系统的多个关键组件其架构可分为三个层次2.1 虚拟MDIO总线层struct fixed_mdio_bus { struct mii_bus *mii_bus; struct list_head phys; }; static struct fixed_mdio_bus platform_fmb { .phys LIST_HEAD_INIT(platform_fmb.phys), };这个自定义的fixed_mdio_bus结构体是fixed-link架构的核心它与常规MDIO总线的关键差异在于特性常规MDIO总线fixed-link MDIO总线总线操作真实硬件访问软件模拟寄存器读写真实PHY寄存器状态机动态生成中断处理硬件中断轮询或GPIO监测设备发现扫描物理PHY静态配置2.2 虚拟PHY设备层struct fixed_phy { int addr; struct phy_device *phydev; seqcount_t seqcount; struct fixed_phy_status status; int (*link_update)(struct net_device *, struct fixed_phy_status *); struct list_head node; int link_gpio; };每个fixed-link实例都会创建一个fixed_phy结构体其关键设计点包括状态保护机制使用seqcount实现无锁读取GPIO支持允许通过硬件引脚检测实际链路状态回调接口支持动态更新连接状态2.3 设备树交互层Linux内核支持两种fixed-link设备树配置方式传统格式5元组fixed-link 1 1 1000 0 0;现代格式子节点fixed-link { speed 1000; full-duplex; };内核通过of_phy_is_fixed_link()函数智能识别这两种格式bool of_phy_is_fixed_link(struct device_node *np) { /* 检查新格式 */ if (of_get_child_by_name(np, fixed-link)) return true; /* 检查旧格式 */ if (of_get_property(np, fixed-link, len) len (5 * sizeof(__be32))) return true; return false; }3. fixed-link的核心实现机制3.1 虚拟PHY的注册流程fixed-link PHY的完整注册过程如下地址分配使用IDA分配唯一的PHY地址phy_addr ida_simple_get(phy_fixed_ida, 0, PHY_MAX_ADDR, GFP_KERNEL);状态初始化根据设备树配置设置初始状态fp-status *status; // 包含speed、duplex等参数设备创建通过通用PHY接口创建设备实例phy get_phy_device(fmb-mii_bus, phy_addr, false);驱动绑定最终绑定到通用PHY驱动phy-dev.driver genphy_driver.mdiodrv.driver;3.2 寄存器模拟引擎swphy_read_reg()是fixed-link最精妙的部分它动态生成PHY寄存器值int swphy_read_reg(int reg, const struct fixed_phy_status *state) { switch (reg) { case MII_BMCR: // 基本模式控制寄存器 return speed[speed_index].bmcr duplex[duplex_index].bmcr; case MII_BMSR: // 基本模式状态寄存器 return BMSR_ANEGCAPABLE | (state-link ? BMSR_LSTATUS : 0); case MII_LPA: // 链路伙伴能力寄存器 return lpa | (state-pause ? LPA_PAUSE_CAP : 0); default: return 0xffff; } }关键寄存器模拟策略BMCR反映配置的速率和双工模式BMSR始终宣告支持自协商链路状态根据实际更新LPA模拟一个理想链路伙伴的能力PHYID返回0表明这不是真实PHY3.3 与MAC驱动的交互fixed-link与真实PHY在MAC驱动视角下完全一致探测过程phy_dev of_phy_connect(dev, phy_node, adjust_link, 0, phy_if);状态机处理phy_start(phy_dev); // 启动PHY状态机数据收发完全依赖标准网络设备接口无需特殊处理4. fixed-link的高级应用场景4.1 动态链路状态更新通过注册link_update回调可以实现动态链路控制int my_link_update(struct net_device *dev, struct fixed_phy_status *status) { status-link gpio_get_value(gpio_link); status-speed SPEED_1000; return 0; } fixed_phy_add(PHY_POLL, phy_addr, status, -1); fp-link_update my_link_update;4.2 多端口负载均衡在交换机芯片应用中可以创建多个fixed-link实例for (i 0; i PORT_COUNT; i) { status.speed port_speeds[i]; fixed_phy_register(PHY_POLL, status, -1, np); }4.3 与真实PHY混合使用系统可以同时包含真实PHY和fixed-linkphy-handle real_phy; fixed-link { speed 1000; full-duplex; };内核会智能处理这种混合场景优先使用phy-handle指定的真实PHY仅当没有真实PHY时回退到fixed-link5. 性能优化与调试技巧5.1 关键性能指标操作典型延迟ns优化建议寄存器读取50-100避免高频状态轮询链路状态更新100-200使用GPIO中断替代轮询数据包传输无额外开销保持MTU合理设置5.2 调试工具与方法查看fixed-link状态cat /sys/kernel/debug/fixed_phy/status监控MDIO总线活动echo 1 /sys/kernel/debug/tracing/events/mdio/enable cat /sys/kernel/debug/tracing/trace_pipe常见问题排查链路不UP检查设备树fixed-link配置确认link_update回调正确设置状态速率不匹配确保两端MAC配置相同的fixed-link参数检查MAC驱动是否支持配置的速率性能低下避免过于频繁的状态轮询考虑使用GPIO中断检测链路变化