2. 设备树语法
好,咱们今天聊聊设备树的语法。说实话,我刚接触设备树那会儿,看着满屏的.dts文件,心里直犯嘀咕——这玩意儿不就是个配置文件吗?后来踩了几个坑才明白,它的语法设计其实很讲究,每个关键字都有它的道理。
节点(node):设备树的骨架
节点是设备树最基本的组成单元。你可以把它想象成一颗树上的分叉点,每个节点代表一个硬件设备或总线。
节点长这样:
/ {
cpu {
compatible = "arm,cortex-a7";
reg = <0x00>;
};
memory {
device_type = "memory";
reg = <0x80000000 0x40000000>;
};
};
根节点用/表示,所有设备都挂在这个根下面。每个节点用花括号{}包裹,最后用分号结束。
我的经验:节点命名最好用全小写,单词之间用短横线连接。比如i2c-controller而不是I2C_Controller。Linux内核的维护者对这个很敏感,命名不规范会被打回来重改。
属性(property):描述节点的特征
属性就是节点的“身份证信息”。它告诉内核:这个设备是什么、怎么访问、需要什么资源。
常见的属性类型有:
| 属性类型 | 示例 | 说明 |
|---|---|---|
| 字符串 | compatible = "ti,am335x-uart"; |
设备兼容性标识 |
| 32位整数 | reg = <0x48020000 0x1000>; |
地址和大小 |
| 布尔值 | status = "disabled"; |
设备是否启用 |
| 字符串列表 | interrupt-names = "rx", "tx"; |
中断名称列表 |
这里有个坑——reg属性里的地址和大小,顺序不能搞反。我曾经在调试一个SPI控制器时,把基地址和长度写反了,结果内核访问内存直接崩溃。排查了整整两天才找到问题。
注意:compatible属性是设备树里最重要的属性之一。它的格式是"厂商,设备型号"。比如"ti,am335x-uart",前面的ti是厂商名,后面的am335x-uart是具体型号。内核驱动就是靠这个字符串来匹配设备的。
标签(label):给节点起个“外号”
标签就是节点的别名。为什么要用标签?你想想看,如果一个节点嵌套得很深,比如/soc/bus@48000000/i2c@48020000,每次引用它都要写这么长一串,多麻烦。
标签的用法:
&i2c0 {
status = "okay";
clock-frequency = <400000>;
eeprom@50 {
compatible = "atmel,24c02";
reg = <0x50>;
};
};
这里&i2c0就是引用了一个标签。标签的定义通常在别的地方:
i2c0: i2c@48020000 {
// 节点内容
};
i2c0:就是标签,后面跟着节点名。引用时用&i2c0。
避坑指南:我曾经在一个项目里,给两个不同的节点定义了相同的标签。结果编译设备树时,编译器只认最后一个定义,前面的全被覆盖了。调试时怎么都找不到那个节点,最后用dtc -I dtb -O dts反编译才发现标签冲突了。所以,标签命名一定要全局唯一。
包含机制(include):模块化你的设备树
设备树文件可以像C语言一样使用#include。这个机制让设备树变得可维护、可复用。
最常见的用法:
#include "am335x.dtsi"
#include "am335x-bone-common.dtsi"
/ {
model = "TI AM335x BeagleBone Black";
compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am335x";
&uart0 {
status = "okay";
};
};
.dtsi文件是设备树的“头文件”,通常存放SoC的通用定义。不同的板子可以包含同一个.dtsi,然后覆盖或添加自己的配置。
这样做的好处很明显:
- SoC厂商只需要维护一份
.dtsi文件 - 板卡厂商只需要关注自己板子特有的部分
- 代码复用率高,减少重复劳动
我的习惯:我会把设备树拆成三层:soc.dtsi(SoC通用)、board-common.dtsi(板级通用)、board-specific.dts(具体配置)。这样当产品线有多个变种时,只需要修改最上层的.dts文件就行。
知识体系结构图
下面这张图展示了设备树语法的核心逻辑:
这张图把设备树的四个核心要素串起来了。从根节点出发,每个节点携带属性和标签,最后通过包含机制组合成完整的设备树文件。说白了,这就是一个搭积木的过程——SoC厂商提供基础模块,板卡厂商在上面做定制。
总结一下:节点是骨架,属性是血肉,标签是快捷方式,包含机制是模块化工具。这四个东西配合好了,设备树就能写得既清晰又高效。我见过有些工程师把所有东西都塞在一个.dts文件里,几千行代码看着就头疼。学会用包含机制拆分文件,你的同事会感谢你的。