三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

深入Linux内核:fixed-link如何用软件‘伪造’一个PHY设备来驱动MAC直连?

深入Linux内核:fixed-link如何用软件‘伪造’一个PHY设备来驱动MAC直连?

深入Linux内核:fixed-link如何用软件‘伪造’一个PHY设备来驱动MAC直连?

在嵌入式系统和网络设备开发中,我们经常会遇到MAC控制器需要直接连接另一个MAC的情况,而中间没有物理PHY芯片。这种场景下,Linux内核通过一种精妙的软件模拟机制——fixed-link,实现了对无PHY硬件的完美支持。本文将带您深入内核源码,揭示这一机制背后的设计哲学与实现细节。

1. fixed-link的起源与设计动机

现代网络设备中,MAC与PHY的协作是网络通信的基础。但在某些特定场景下,比如:

  • 两块开发板通过RJ45接口直连
  • 芯片内部两个MAC控制器直接对接
  • 低成本设备省略外部PHY芯片

这些情况下,传统PHY芯片的缺失会导致一系列问题:

  1. 协商机制失效:没有PHY意味着无法自动协商速率、双工模式等参数
  2. 状态监测困难:链路状态、错误统计等关键信息无法获取
  3. 驱动兼容性问题:现有网络驱动架构严重依赖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结构体,其关键设计点包括:

  1. 状态保护机制:使用seqcount实现无锁读取
  2. GPIO支持:允许通过硬件引脚检测实际链路状态
  3. 回调接口:支持动态更新连接状态

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的完整注册过程如下:

  1. 地址分配:使用IDA分配唯一的PHY地址

    phy_addr = ida_simple_get(&phy_fixed_ida, 0, PHY_MAX_ADDR, GFP_KERNEL);
  2. 状态初始化:根据设备树配置设置初始状态

    fp->status = *status; // 包含speed、duplex等参数
  3. 设备创建:通过通用PHY接口创建设备实例

    phy = get_phy_device(fmb->mii_bus, phy_addr, false);
  4. 驱动绑定:最终绑定到通用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,表明这不是真实PHY

3.3 与MAC驱动的交互

fixed-link与真实PHY在MAC驱动视角下完全一致:

  1. 探测过程

    phy_dev = of_phy_connect(dev, phy_node, &adjust_link, 0, phy_if);
  2. 状态机处理

    phy_start(phy_dev); // 启动PHY状态机
  3. 数据收发

    • 完全依赖标准网络设备接口
    • 无需特殊处理

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-link:

phy-handle = <&real_phy>; fixed-link { speed = <1000>; full-duplex; };

内核会智能处理这种混合场景:

  1. 优先使用phy-handle指定的真实PHY
  2. 仅当没有真实PHY时回退到fixed-link

5. 性能优化与调试技巧

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

常见问题排查

  1. 链路不UP

    • 检查设备树fixed-link配置
    • 确认link_update回调正确设置状态
  2. 速率不匹配

    • 确保两端MAC配置相同的fixed-link参数
    • 检查MAC驱动是否支持配置的速率
  3. 性能低下

    • 避免过于频繁的状态轮询
    • 考虑使用GPIO中断检测链路变化
← 返回列表