Linux设备树(Device Tree)是一种描述硬件配置的树形数据结构,通过节点(Node)和属性(Property)的层级关系,抽象化表示CPU、内存、总线、外设等硬件信息。其核心数据结构及实现逻辑如下:
一、设备树节点(Device Tree Node)
设备树由节点构成,每个节点表示一个硬件设备或逻辑组件。节点可嵌套子节点,形成树状结构。
1. 节点结构
- 节点命名:格式为
node-name@unit-address
,其中node-name
为设备类型(如cpu
、i2c
),unit-address
为设备地址(可选)。 - 根节点:唯一根节点
/
,描述整个系统信息(如compatible
属性定义硬件平台)。 - 子节点:例如CPU节点、内存节点、外设节点等。
2. 内核表示
在Linux内核中,节点通过struct device_node
表示,关键字段包括:
name
: 节点名称。parent
: 父节点指针。child
: 子节点链表。properties
: 属性链表(struct property
)。full_name
: 节点完整路径(如/soc/i2c@021a0000
)。
二、属性(Property)
属性是键值对,描述节点配置信息。属性值可以是字符串、整数、字节数组等。
1. 标准属性示例
compatible
:驱动匹配关键属性,格式为"厂商,型号"
(如compatible = "fsl,imx6ull-gpmi-nand"
)。reg
:设备地址及长度,格式为<address length>
,受父节点#address-cells
和#size-cells
影响。interrupts
:中断号及触发方式(如interrupts = <0 2 4>
表示IRQ 0,边沿触发)。status
:设备状态("okay"
启用,"disabled"
禁用)。
2. 自定义属性
用户可定义任意属性(如custom-data = <0x12 0x34>
),驱动通过of_property_read_u32_array
等API读取。
3. 内核表示
属性通过struct property
表示,关键字段包括:
name
: 属性名。length
: 属性值长度。value
: 属性值指针(字节数组)。
三、设备树二进制格式(DTB)
设备树源文件(.dts)编译为二进制文件(.dtb),内核启动时解析。DTB结构包括:
- Header:魔数(
0xd00dfeed
)、总大小、结构块偏移等。 - Structure Block:线性化节点数据,包含节点开始/结束标记、属性数据。
- Strings Block:所有属性名的字符串表,通过偏移量引用。
四、内核解析流程
- Bootloader传递:U-Boot将DTB加载到内存,通过寄存器(如ARM的
r2
)传递地址给内核。 - 解压与校验:内核验证DTB完整性,解压为内存中的设备树结构。
- 遍历与初始化:
- 使用
of_find_node_by_path
、of_get_property
等API遍历节点和属性。 - 根据
compatible
属性匹配驱动,调用probe
函数初始化设备。
- 使用
五、关键数据结构关系图
设备树结构
├─ / (根节点)
│ ├─ compatible = "vendor,platform"
│ └─ memory@80000000
│ ├─ device_type = "memory"
│ └─ reg = <0x80000000 0x40000000>
├─ soc (SoC节点)
│ ├─ #address-cells = <1>
│ ├─ #size-cells = <1>
│ └─ i2c@021a0000
│ ├─ compatible = "fsl,imx6q-i2c"
│ └─ reg = <0x021a0000 0x4000>
└─ chosen (启动参数节点)
└─ bootargs = "console=ttyS0,115200"
六、操作API示例
#include <linux/of.h>
// 查找节点
struct device_node *np = of_find_node_by_path("/soc/i2c@021a0000");
// 读取属性
const u32 *reg;
u32 reg_len;
of_property_read_u32_array(np, "reg", reg, ®_len);
// 遍历子节点
for_each_child_of_node(np, child) {
// 处理子节点
}
总结
设备树通过节点和属性抽象硬件,内核通过struct device_node
和struct property
解析配置,实现驱动与硬件解耦。这种设计使得同一内核支持多硬件平台,无需修改内核代码,仅需更换设备树文件。