3. 设备树语法入门:根节点、子节点、属性的基本写法
好,咱们今天正式进入设备树的语法世界。说实话,我刚接触设备树那会儿,看着一堆花括号和分号,第一反应是——这玩意儿到底是代码还是配置文件?后来做多了才发现,它其实就是一个描述硬件信息的树形结构。你把它想象成一棵倒着长的树,根在最上面,树枝往下分,每个树枝上挂着各种属性标签。
我个人习惯把设备树比作一张硬件清单。内核拿到这张清单,就知道板子上接了哪些外设、内存多大、中断怎么连。嗯,咱们今天就先把这张清单的格式搞清楚。
3.1 根节点:整棵树的起点
每个设备树文件有且只有一个根节点。它用斜杠 / 表示,是整个设备树的入口。我见过不少新手在根节点上犯低级错误,比如少写分号,或者把根节点名字写错。
根节点的标准写法是这样的:
/ {
// 这里是根节点的内容
model = "TI AM335x BeagleBone Black";
compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am335x";
};
注意看,根节点用花括号包裹,最后一定要加分号。这里面的 model 和 compatible 就是属性。model 描述板子名称,compatible 告诉内核这个板子兼容哪些平台。我曾经在调试一块新板子时,就是因为 compatible 写错了,内核死活不认我的板子,折腾了大半天。
reg 属性。它代表整个系统,不需要地址。如果你在根节点里写 reg,编译会直接报错。
3.2 子节点:描述具体设备
子节点就是挂在根节点下面的设备。每个子节点描述一个具体的硬件模块,比如串口、I2C控制器、GPIO控制器等。子节点的命名格式是 节点名@地址,其中地址部分是可选的,但如果有地址,一定要写。
来看一个实际例子:
/ {
model = "My Board";
cpu@0 {
compatible = "arm,cortex-a9";
reg = <0>;
};
serial@44e09000 {
compatible = "ti,omap3-uart";
reg = <0x44e09000 0x2000>;
interrupts = <72>;
};
};
这里 cpu@0 和 serial@44e09000 都是子节点。@后面的数字是设备的起始地址或编号。你想想看,如果板子上有两个串口,一个地址是 0x44e09000,另一个是 0x44e0b000,通过地址就能区分开。
子节点里还可以嵌套子节点,形成多层结构。比如 I2C 控制器下面挂多个 I2C 设备:
i2c@44e0b000 {
compatible = "ti,omap4-i2c";
reg = <0x44e0b000 0x1000>;
#address-cells = <1>;
#size-cells = <0>;
eeprom@50 {
compatible = "atmel,24c02";
reg = <0x50>;
};
rtc@68 {
compatible = "dallas,ds1307";
reg = <0x68>;
};
};
这种嵌套结构在实际项目中非常常见。我记得有一次调试一个音频编解码芯片,它在 I2C 总线上,地址是 0x1a。结果我忘了在父节点里设置 #address-cells 和 #size-cells,导致子节点地址解析失败。嗯,这两个属性后面会详细讲,你先记住它们很重要。
3.3 属性:描述设备特征
属性就是键值对,用来描述设备的各种特征。常见的属性类型有字符串、整数、数组、布尔值等。我整理了一个表格,方便你对照:
| 属性类型 | 写法示例 | 说明 |
|---|---|---|
| 字符串 | compatible = "ti,omap3-uart"; |
用双引号包裹 |
| 32位整数 | reg = <0x44e09000>; |
用尖括号包裹 |
| 整数数组 | reg = <0x44e09000 0x2000>; |
多个值用空格隔开 |
| 布尔值 | status = "okay"; |
用字符串表示,okay 或 disabled |
| 字节数组 | local-mac-address = [00 11 22 33 44 55]; |
用方括号包裹,十六进制 |
这里我要特别强调一下 compatible 属性。它是设备树里最重要的属性之一,说白了就是告诉内核:这个设备用什么驱动来匹配。格式是 "厂商,型号",比如 "ti,omap3-uart"。内核驱动里会有一个 of_match_table,里面列了一串 compatible 字符串,设备树里的值和驱动里的值对上,驱动就加载了。
我曾经踩过一个坑:compatible 字符串里厂商名和型号之间用了下划线而不是逗号,结果驱动死活匹配不上。后来查了半天才发现是这里的问题。所以写 compatible 的时候,一定要严格按照 "厂商,型号" 的格式。
3.4 标准属性速览
除了上面提到的,还有几个标准属性你必须要掌握:
- reg:描述设备的地址空间,格式是
<地址 长度>。如果有多个地址段,可以写多组。 - interrupts:描述中断号,具体格式取决于中断控制器。
- status:设备状态,常用
"okay"或"disabled"。调试时很有用。 - label:给节点起个别名,方便在其他地方引用。
- phandle:节点的唯一标识符,通常由编译器自动生成,你不需要手动写。
来看一个综合例子,把这些属性串起来:
/ {
model = "My Custom Board";
compatible = "mycompany,myboard";
gpio@4804c000 {
compatible = "ti,omap4-gpio";
reg = <0x4804c000 0x200>;
interrupts = <29>;
status = "okay";
label = "GPIO1";
#gpio-cells = <2>;
gpio-controller;
};
};
注意看 gpio-controller 这个属性,它没有值,只有名字。这种叫空属性,表示这个节点是一个 GPIO 控制器。类似的还有 interrupt-controller。说白了,就是给节点打一个标签,告诉内核这个节点有什么特殊功能。
dtc -I dtb -O dts 命令把编译好的 dtb 反编译成 dts,看看实际解析出来的内容是什么。我经常用这招来检查属性有没有写对。
3.5 本章知识结构图
下面这张图帮你理清设备树语法的核心逻辑。从根节点出发,子节点层层嵌套,属性附着在节点上描述硬件特征。你把这个结构记在脑子里,写设备树就有方向了。
这张图展示了设备树的核心结构:根节点在最顶层,下面挂载子节点,每个节点可以有自己的属性。子节点还可以继续嵌套子节点,形成树形结构。你写设备树的时候,就按照这个思路来:先确定根节点,再添加子节点,最后给每个节点填上必要的属性。
好了,这一章的内容就到这里。设备树的基本语法其实不复杂,关键是多写多练。我建议你找一块开发板的设备树文件,打开看看,对照着本章的内容逐行分析。看得多了,自然就熟了。