2. 设备树语法入门:节点、属性、标签、路径的基本规则
设备树(Device Tree,DT)是一种描述硬件资源的数据结构,它使用树形层级来组织硬件组件。在嵌入式 Linux 驱动开发中,设备树是驱动与硬件之间的“桥梁”。本章将系统讲解设备树的四个核心语法要素:节点(Node)、属性(Property)、标签(Label)和路径(Path)。
学习提示:设备树语法与 C 语言结构体类似,但更简洁。掌握这四个要素后,你就能读懂 90% 以上的设备树源文件(.dts)。
2.1 节点(Node):设备树的骨架
节点是设备树的基本组成单元,每个节点代表一个硬件设备、总线、控制器或功能模块。节点通过层级嵌套形成树状结构,根节点是 /。
2.1.1 节点定义语法
/dts-v1/;
/ {
// 根节点
model = "TI AM335x BeagleBone Black";
compatible = "ti,am335x-bone-black";
cpu@0 {
compatible = "arm,cortex-a8";
reg = <0x0>;
};
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x20000000>; // 512MB
};
serial@44e09000 {
compatible = "ti,am3352-uart", "ti,omap3-uart";
reg = <0x44e09000 0x2000>;
interrupts = <72>;
};
};
节点名称格式为 node-name@unit-address,其中 @unit-address 通常表示设备在总线上的地址(如内存地址、I2C 从机地址)。
节点命名规则:
- 节点名应反映设备类型(如
serial、i2c、gpio) @unit-address可选,但同一层级有相同节点名时必须加地址区分- 节点名最长 31 个字符,只能包含小写字母、数字、连字符和点号
2.2 属性(Property):描述设备特征
属性是附着在节点上的键值对,用于描述设备的硬件特征、配置参数和驱动绑定信息。每个属性由属性名和属性值组成。
2.2.1 属性类型与格式
| 属性类型 | 语法格式 | 示例 | 说明 |
|---|---|---|---|
| 字符串 | string = "value"; |
model = "TI AM335x"; |
单个字符串,用双引号包裹 |
| 字符串列表 | string-list = "a", "b", "c"; |
compatible = "ti,am3352-uart", "ti,omap3-uart"; |
多个字符串用逗号分隔 |
| 32位整数 | prop = <value>; |
reg = <0x44e09000 0x2000>; |
尖括号包裹,可包含多个整数 |
| 64位整数 | prop = <0x00000000 0x80000000>; |
reg = <0x00000000 0x80000000 0x0 0x20000000>; |
高32位 + 低32位组合 |
| 字节数组 | prop = [hex-bytes]; |
local-mac-address = [00 11 22 33 44 55]; |
方括号包裹十六进制字节 |
| 布尔值 | prop;(无值) |
status = "disabled"; |
存在即 true,不存在即 false |
| 混合类型 | 组合使用 | gpios = <&gpio0 15 0>; |
可包含 phandle、整数等 |
2.2.2 标准属性速查
必备标准属性:
compatible— 驱动匹配的关键,格式为"厂商,设备型号"reg— 设备地址空间,格式为<地址 长度>interrupts— 中断号,依赖interrupt-parentstatus— 设备状态,可选"okay"、"disabled"、"reserved"
2.3 标签(Label):节点的引用别名
标签是设备树中用于引用节点的别名机制。通过标签,可以在设备树的其他位置方便地引用某个节点,而无需写出完整路径。
2.3.1 标签定义与引用
// 定义标签:在节点名前加 & 号
&uart0 {
status = "okay";
pinctrl-0 = &uart0_pins;
};
// 在另一个节点中引用标签
&am33xx_pinmux {
uart0_pins: pinmux_uart0_pins {
pinctrl-single,pins = <
0x170 0x30 // uart0_rxd
0x174 0x00 // uart0_txd
>;
};
};
2.3.2 标签使用场景
| 场景 | 语法 | 说明 |
|---|---|---|
| 节点引用 | &label { ... }; |
在设备树其他位置追加或修改节点内容 |
| phandle 引用 | &label 作为属性值 |
在属性中引用另一个节点(如 GPIO、时钟) |
| 覆盖文件 | &label { ... }; |
设备树覆盖(.dtso)中修改已有节点 |
注意事项:标签名在全局设备树中必须唯一,不能与节点名或其他标签重名。标签通常使用小写字母和下划线,如
uart0_pins、i2c1。
2.4 路径(Path):节点的绝对定位
路径是从根节点到目标节点的完整层级描述,类似于文件系统中的绝对路径。路径用于在设备树中精确定位节点,尤其在驱动代码和 overlay 中频繁使用。
2.4.1 路径语法
// 完整路径格式
/soc/serial@44e09000
// 在驱动代码中使用 of_find_node_by_path()
struct device_node *np;
np = of_find_node_by_path("/soc/serial@44e09000");
if (np) {
// 找到节点,进行操作
}
// 在设备树覆盖中引用路径
&{/soc/serial@44e09000} {
status = "okay";
};
2.4.2 路径与标签对比
| 特性 | 路径(Path) | 标签(Label) |
|---|---|---|
| 定位方式 | 从根节点开始的完整层级 | 全局唯一的别名 |
| 可读性 | 路径长时不易读 | 简短直观 |
| 唯一性 | 天然唯一(层级决定) | 需手动保证不重复 |
| 使用场景 | 驱动代码、动态查找 | 设备树内部引用、overlay |
| 性能 | 需逐级解析 | 直接通过 phandle 访问 |
路径使用建议:
- 在驱动代码中优先使用
of_find_node_by_path()进行初始化查找 - 在设备树覆盖(.dtso)中优先使用标签
&label,更简洁且不易出错 - 路径中的
@unit-address必须与节点定义完全一致
2.5 综合示例:一个完整的设备树片段
下面是一个综合了节点、属性、标签和路径的完整设备树示例,展示它们如何协同工作:
/dts-v1/;
/ {
model = "Embedded Device Tree Demo";
compatible = "demo,device-tree";
soc {
#address-cells = <1>;
#size-cells = <1>;
gpio0: gpio@44e07000 {
compatible = "ti,omap4-gpio";
reg = <0x44e07000 0x1000>;
interrupts = <96>;
gpio-controller;
#gpio-cells = <2>;
};
i2c0: i2c@44e0b000 {
compatible = "ti,omap4-i2c";
reg = <0x44e0b000 0x1000>;
interrupts = <70>;
#address-cells = <1>;
#size-cells = <0>;
eeprom@50 {
compatible = "atmel,24c32";
reg = <0x50>;
pagesize = <32>;
};
};
led0: led {
compatible = "gpio-leds";
gpios = <&gpio0 15 0>; // 通过标签引用 gpio0
label = "user-led0";
};
};
};
// 通过标签追加属性
&led0 {
linux,default-trigger = "heartbeat";
};
关键点总结:
- 节点定义硬件层级,
/是根,子节点用node@addr命名 - 属性描述硬件特征,
compatible是驱动匹配的核心 - 标签提供节点别名,用
&label引用,简化跨文件操作 - 路径是绝对定位方式,驱动代码中常用
of_find_node_by_path()