1. 课程导论:为什么需要设备树?网络设备驱动架构概览
1.1 为什么需要设备树?
在早期的 Linux 内核中(如 ARM 平台),板级硬件信息(内存基址、中断号、外设寄存器地址)被硬编码在 arch/arm/mach-xxx 下的 C 文件和头文件中。这种“代码即配置”的方式带来了严重的维护灾难:
- 代码膨胀:每增加一块开发板,就要新增或修改大量 board-*.c 文件。
- 内核与硬件耦合:更换一颗 PHY 芯片或调整 GPIO 引脚,必须重新编译内核。
- 碎片化:同一 SoC 的不同评估板,驱动代码大量重复,社区难以维护。
设备树(Device Tree,DT)的引入彻底解决了上述问题。它将硬件描述从内核源码中剥离,形成一个独立的数据结构——.dts 源文件。内核在启动时解析设备树二进制(.dtb),动态创建 platform_device、i2c_client、spi_device 等内核设备对象,驱动只需匹配 compatible 字符串即可完成绑定。
核心转变: 从“代码描述硬件” → “数据描述硬件”。设备树是硬件拓扑的声明式语言,驱动只关心“如何驱动”,不再关心“硬件在哪里”。
1.2 网络设备驱动架构概览
一个完整的 Linux 网络设备驱动,从上到下涉及三个核心层次:MAC 层、MDIO 总线、PHY 层。它们之间的协作关系决定了网络数据包的收发质量。
架构口诀: MAC 负责数据链路层的帧封装与 DMA 传输,PHY 负责物理层的信号编解码与链路协商,MDIO 总线是 MAC 访问 PHY 寄存器的管理通道。
1.2.1 层次职责对比
| 层次 | 内核模块 | 核心职责 | 设备树节点 |
|---|---|---|---|
| MAC | drivers/net/ethernet/ |
DMA 描述符管理、帧过滤(RFS/RSS)、TSO/GRO 卸载 | ethernet@xxx |
| MDIO 总线 | drivers/net/phy/mdio-bus.c |
PHY 寄存器读写、扫描 PHY 地址、管理 PHY 状态机 | mdio@xxx 或 mdio {} |
| PHY | drivers/net/phy/ |
链路协商(Auto-Neg)、速率/双工切换、信号检测 | ethernet-phy@xx |
1.2.2 网络设备驱动软件架构图
下图展示了从用户空间 socket 到物理网口的数据流,以及设备树节点在内核中的映射关系:
1.3 设备树如何驱动网络设备绑定
设备树在网络驱动中扮演“硬件拓扑描述者”的角色。一个典型的千兆以太网设备树节点包含以下关键属性:
/* 设备树片段:MAC + MDIO + PHY 绑定示例 */
gmac0: ethernet@ff3e0000 {
compatible = "snps,dwmac-4.20a";
reg = <0xff3e0000 0x4000>;
interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "macirq";
phy-mode = "rgmii";
rx-fifo-depth = <4096>;
tx-fifo-depth = <4096>;
mdio0: mdio {
#address-cells = <1>;
#size-cells = <0>;
compatible = "snps,dwmac-mdio";
phy0: ethernet-phy@1 {
reg = <1>;
compatible = "ethernet-phy-ieee802.3-c22";
max-speed = <1000>;
reset-gpios = <&gpio3 15 GPIO_ACTIVE_LOW>;
reset-assert-us = <10000>;
reset-deassert-us = <20000>;
};
};
};
常见陷阱: 很多工程师在移植内核时忘记在
mdio 节点中设置 #address-cells = <1> 和 #size-cells = <0>,导致 PHY 扫描失败,网络无法 link up。这是设备树语法错误中最容易忽略的问题之一。
1.4 设备树绑定流程:从 .dts 到驱动 probe
当内核启动并解析设备树后,网络设备的绑定遵循以下步骤:
- OF 核心解析:遍历
.dtb,为每个compatible匹配的节点创建platform_device。 - MAC 驱动 probe:调用
stmmac_pltfr_probe(),读取phy-mode、interrupts、reg等属性。 - MDIO 总线注册:MAC 驱动解析子节点
mdio {},调用of_mdiobus_register()注册 MDIO 总线。 - PHY 扫描与绑定:MDIO 总线扫描
ethernet-phy@1节点,读取reg地址,通过phy_device_create()创建 PHY 设备。 - PHY 驱动匹配:PHY 设备的
compatible与 PHY 驱动列表匹配,调用phy_probe(),完成初始化。 - 链路建立:MAC 驱动通过
phy_connect()绑定 PHY,启动自动协商,最终建立网络链路。
关键洞察: 设备树中的
phy-mode(如 rgmii、rmii、sgmii)不仅影响 MAC 侧的接口时序配置,还决定了 PHY 驱动初始化时的延迟补偿策略。错误设置会导致丢包或无法 link up。