在嵌入式Linux系统和网络设备开发中,Fixed-Link是一种特殊的网络连接配置方式。它主要用于没有物理PHY芯片的场景,比如开发板与交换机芯片之间的直连,或者虚拟网络设备的内部连接。与常规的通过MDIO总线连接PHY的方式不同,Fixed-Link通过软件配置来模拟物理链路特性。
我第一次接触Fixed-Link是在调试一块定制化的工业控制板时。这块板子的网络接口直接连接到FPGA实现的网络处理模块,省去了PHY芯片以降低成本。当时发现网络接口始终无法up,最终通过深入研究Fixed-Link机制才解决了问题。
常规以太网连接需要MAC通过MDIO总线与PHY芯片通信,进行自协商、链路状态检测等操作。而Fixed-Link模式下:
c复制// 典型Fixed-Link设备树配置示例
fixed-link {
speed = <1000>;
full-duplex;
pause;
asym-pause;
};
Linux内核中Fixed-Link的实现主要涉及以下几个关键部分:
驱动开发者最常遇到的坑是忘记调用fixed_phy_update_state()来更新链路状态,导致网络接口看似配置正确却无法正常工作。
Fixed-Link在设备树中的标准配置包含以下关键参数:
| 参数 | 取值 | 说明 |
|---|---|---|
| speed | 10/100/1000 | 链路速率(Mbps) |
| full-duplex | 布尔值 | 是否全双工 |
| pause | 布尔值 | 是否支持暂停帧 |
| asym-pause | 布尔值 | 是否支持不对称暂停 |
注意:在v4.1以下的内核版本中,配置语法略有不同,需要使用
fixed-link子节点而非直接属性。
这是我在最近一个项目中使用的真实配置:
dts复制ethernet@f0b00000 {
compatible = "vendor,eth-mac";
reg = <0xf0b00000 0x1000>;
fixed-link {
speed = <100>;
full-duplex;
};
phy-mode = "rgmii";
};
这个配置表示:
以下是一个支持Fixed-Link的最小化网络驱动实现框架:
c复制static int eth_drv_probe(struct platform_device *pdev)
{
struct net_device *ndev;
struct eth_priv *priv;
// 1. 分配网络设备
ndev = alloc_etherdev(sizeof(*priv));
// 2. 解析设备树
if (of_phy_is_fixed_link(np)) {
int err = of_phy_register_fixed_link(np);
if (err) {
dev_err(&pdev->dev, "Failed to register fixed PHY\n");
goto err_free;
}
}
// 3. 获取PHY设备
priv->phydev = of_phy_connect(ndev, np, ð_adjust_link, 0, phy_mode);
// 4. 注册网络设备
register_netdev(ndev);
return 0;
err_free:
free_netdev(ndev);
return -ENODEV;
}
of_phy_register_fixed_link() 的内部工作流程:
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 接口无法up | 未调用fixed_phy_update_state() | 确保驱动正确更新PHY状态 |
| 能ping通但吞吐量低 | 双工模式不匹配 | 检查两端双工配置是否一致 |
| 偶发性丢包 | 未配置流控参数 | 在设备树中启用pause参数 |
| 系统启动时报PHY错误 | 内核版本兼容性问题 | 检查设备树语法与内核版本匹配 |
bash复制cat /sys/class/net/eth0/phy_state
bash复制dmesg | grep fixed_phy
bash复制ethtool -s eth0 speed 100 duplex full
我在调试一个FPGA项目时,发现即使配置正确,链路也无法建立。最终通过内核的PHY调试子系统发现是MAC驱动没有正确处理PHY的状态变化通知。解决方法是在驱动中实现适当的PHY状态变更回调。
Fixed-Link模式下,由于没有真实的PHY中断,可以考虑:
c复制// 在驱动中设置NAPI参数
netif_napi_add(ndev, &priv->napi, eth_poll, 64);
对于高速Fixed-Link连接(如1Gbps),需要特别注意:
c复制// 示例:调整接收描述符数量
priv->rx_ring_size = 512;
priv->tx_ring_size = 512;
在分布式交换机架构(DSA)中,Fixed-Link常用于:
典型配置示例:
dts复制switch@0 {
compatible = "marvell,mv88e6085";
ethernet-ports {
port@0 {
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
};
Fixed-Link也常用于:
这种情况下,驱动通常会实现一个简化的fixed_phy_ops:
c复制static const struct fixed_phy_ops virt_phy_ops = {
.link_update = virt_phy_link_update,
.read_status = virt_phy_read_status,
};
不同内核版本对Fixed-Link的支持有所差异:
| 内核版本 | 主要变化 |
|---|---|
| v3.19 | 引入of_fixed_phy_register() |
| v4.1 | 设备树语法变更 |
| v4.5 | 增强状态更新API |
| v5.10 | 新增流控状态支持 |
对于需要跨版本兼容的驱动,建议这样处理:
c复制#if LINUX_VERSION_CODE < KERNEL_VERSION(4,1,0)
/* 旧版本设备树处理 */
#else
/* 新版本处理方式 */
#endif
我在维护一个工业级网络驱动时就遇到了这个问题,最终通过条件编译和运行时检测相结合的方式实现了良好的向后兼容性。