设备树基础语法:节点、属性、值的表示方法
设备树(Device Tree)这东西,说白了就是一块描述硬件的数据结构。我刚开始接触Linux内核时,总觉得它很神秘——为什么不用C代码直接操作寄存器,非要搞个树形结构出来?后来在项目中踩过几次坑才明白,设备树的存在,是为了让同一份内核能适配不同的硬件板卡。
嗯,咱们今天就从最基础的语法讲起。你只要掌握了节点、属性、值这三要素,设备树就算入门了。
1. 节点(Node)—— 设备树的骨架
设备树里的每个硬件设备,都对应一个节点。节点用花括号 { } 表示,节点名通常写成 设备名@地址 的形式。
节点命名规则:
设备名必须唯一,比如uart、i2c@地址是可选的,但建议加上,用于区分同类型设备- 节点名不能包含空格和特殊字符
举个例子,一个串口节点可以这样写:
uart0: serial@101f0000 {
compatible = "ns16550";
reg = <0x101f0000 0x1000>;
interrupts = <10>;
};
这里的 uart0 是标签(label),方便其他地方引用。serial@101f0000 才是真正的节点名。我在项目中习惯给每个重要节点都加上标签,这样在 &uart0 引用时一目了然。
2. 属性(Property)—— 描述硬件特征
属性就是节点的“身份证信息”。每个属性都有一个名字和一个值。常见的属性有:
| 属性名 | 含义 | 示例 |
|---|---|---|
compatible |
设备兼容性字符串,驱动匹配的关键 | "ti,am335x-uart" |
reg |
寄存器地址和长度 | <0x48020000 0x1000> |
interrupts |
中断号 | <29> |
status |
设备状态(okay/disabled) | "okay" |
避坑指南: 我曾经在项目中把 compatible 写成了 compatiable,结果驱动死活不匹配。内核不会报错,只会默默忽略这个节点。排查了整整一天才发现是拼写错误。所以,compatible 这个单词一定要记牢。
3. 值(Value)—— 数据的表示方式
设备树的值有四种基本类型,我整理成了一张表:
| 类型 | 表示方式 | 示例 | 说明 |
|---|---|---|---|
| 字符串 | 双引号 | "ns16550" |
用于 compatible、status 等 |
| 32位整数 | 尖括号 | <0x1000> |
地址、中断号等 |
| 64位整数 | 两个32位组合 | <0x00000000 0x10000000> |
大地址空间用 |
| 字节数组 | 方括号 | [0x00 0x01 0x02] |
MAC地址、UUID等 |
你想想看,为什么地址要用尖括号?因为设备树编译器(DTC)会把尖括号里的内容解析成32位整数数组。而方括号是逐字节解析的,两者完全不同。
4. 节点嵌套与引用
设备树是树形结构,节点可以嵌套。比如一个I2C控制器下面挂多个设备:
&i2c0 {
status = "okay";
clock-frequency = <100000>;
eeprom@50 {
compatible = "atmel,24c02";
reg = <0x50>;
};
rtc@68 {
compatible = "ds1307";
reg = <0x68>;
};
};
这里 eeprom@50 和 rtc@68 是子节点,它们继承了父节点 i2c0 的某些属性。我个人习惯把子节点的 reg 属性写成设备地址,而不是内存地址,这样更直观。
注意: 子节点的 reg 含义取决于父节点的 #address-cells 和 #size-cells 属性。如果父节点声明了 #address-cells = <1>,那么子节点的 reg 第一个值就是地址,第二个值是长度。这个规则很容易搞混,我建议你在写每个节点前,先确认父节点的这两个属性。
5. 核心逻辑结构图
下面这张图展示了设备树从节点到硬件寄存器的映射流程,我画成了SVG:
这张图把整个流程串起来了。你从设备树节点出发,经过驱动匹配、地址解析,最后通过 ioremap 映射到硬件寄存器。每一步都离不开我们刚才讲的节点、属性、值这三个基础概念。
6. 实战小技巧
最后分享几个我在项目中积累的经验:
- 多用标签: 每个节点都加上标签,方便在
&节点名时引用。我见过有人直接用节点路径引用,代码又长又容易错。 - 属性顺序不重要: 设备树编译器不关心属性顺序,但建议把
compatible放在第一个,方便阅读。 - 注释用
/* */: 设备树支持C风格的注释,但别用//,有些旧版本编译器不支持。 - 检查语法: 写完设备树后,用
dtc -I dts -O dtb -o test.dtb test.dts编译一下,能发现大部分语法错误。
核心总结: 节点是骨架,属性是血肉,值是数据。三者组合起来,就能描述任何硬件设备。你只要记住 compatible 用于驱动匹配,reg 用于地址映射,设备树的基本语法就算掌握了。