2. 设备树语法入门:节点(node)、属性(property)、标签(label)、路径(path)详解
设备树(Device Tree,DT)是一种描述硬件资源的数据结构,它使用树形层次结构将系统中的处理器、总线、外设、中断控制器、GPIO 控制器等硬件组件组织起来。对于嵌入式 Linux 开发者而言,掌握设备树的语法是编写设备驱动和移植内核的基础。本章将深入解析设备树的四个核心语法要素:节点、属性、标签和路径。
学习提示:设备树源文件(.dts)经过 Device Tree Compiler(dtc)编译生成二进制设备树 Blob(.dtb),内核在启动时解析 .dtb 来枚举硬件。理解语法是读懂和编写 .dts 文件的前提。
2.1 节点(Node):设备树的骨架
节点是设备树的基本组成单元,每个节点代表一个硬件设备、总线、控制器或功能模块。节点通过父子关系形成一棵倒置的树,根节点是 /。
2.1.1 节点定义语法
node-name {
property1 = <value>;
property2 = "string";
child-node {
...
};
};
- 节点名称:由字母、数字、连字符(-)、下划线(_)组成,长度不超过 31 个字符。
- 节点单元地址:当节点代表内存映射设备时,名称后可以跟
@address,如uart@ff010000,其中ff010000是设备基地址。 - 节点体:用花括号
{ }包裹,内部包含属性和子节点。
2.1.2 节点层次结构示例
/ {
model = "TI AM335x BeagleBone Black";
compatible = "ti,am335x-bone-black";
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
compatible = "arm,cortex-a8";
reg = <0x0>;
};
};
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x10000000>; /* 256 MB */
};
ocp {
compatible = "simple-bus";
uart0: serial@44e09000 {
compatible = "ti,am3352-uart", "ti,omap3-uart";
reg = <0x44e09000 0x2000>;
interrupts = <72>;
};
};
};
关键点:根节点
/ 下包含 cpus、memory、ocp 等子节点。每个节点可以嵌套子节点,形成硬件拓扑的精确映射。
2.2 属性(Property):描述节点的特征
属性是附着在节点上的键值对,用于描述设备的特性、配置和资源需求。属性值可以是空、整数、字符串、字节数组或这些类型的组合。
2.2.1 属性值类型
| 属性值类型 | 语法格式 | 示例 | 说明 |
|---|---|---|---|
| 空属性 | property-name; |
interrupt-controller; |
仅表示存在该属性,无值 |
| 32 位整数 | <value> |
<0x1000> |
单个或多个整数,空格分隔 |
| 64 位整数 | <high low> |
<0x0 0x80000000> |
两个 32 位整数拼接 |
| 字符串 | "string" |
"ti,am3352-uart" |
双引号包裹 |
| 字节数组 | [hex bytes] |
[0x00 0x01 0x02] |
十六进制字节序列 |
| 混合类型 | <value> "string" |
<0x1> "default" |
用空格分隔不同类型 |
2.2.2 标准属性
设备树规范定义了一系列标准属性,用于描述设备的核心特征:
- compatible:指定设备兼容性字符串,驱动通过匹配该属性绑定设备。格式为
"厂商,型号",如"ti,am3352-uart"。 - reg:描述设备地址空间,格式为
<address size>,由#address-cells和#size-cells决定单元长度。 - interrupts:描述设备使用的中断号,格式由
#interrupt-cells定义。 - status:设备状态,可选
"okay"(启用)、"disabled"(禁用)、"fail"(故障)。 - model:设备型号字符串,如
"TI AM335x BeagleBone Black"。 - device_type:设备类型,主要用于
memory和cpu节点。
常见错误:属性值类型不匹配是编译错误的主要原因。例如,将字符串值赋给期望整数的属性,或忘记在整数列表外添加尖括号
<>。
2.3 标签(Label):节点的别名
标签是节点的引用别名,用于在设备树的其他位置或外部文件中引用节点。标签以冒号结尾,放在节点名称之前。
2.3.1 标签定义语法
label: node-name {
/* 节点内容 */
};
例如,在之前的示例中:
uart0: serial@44e09000 {
compatible = "ti,am3352-uart", "ti,omap3-uart";
reg = <0x44e09000 0x2000>;
interrupts = <72>;
};
这里 uart0 就是标签,可以在其他位置通过 &uart0 引用该节点。
2.3.2 标签的典型用途
| 用途场景 | 示例 | 说明 |
|---|---|---|
| 覆盖节点属性 | &uart0 { status = "disabled"; }; |
在 board.dts 中禁用 SoC 默认启用的 UART |
| 添加子节点 | &i2c0 { eeprom@50 { ... }; }; |
在 I2C 控制器节点下挂载 EEPROM 设备 |
| 引用中断/GPIO | interrupt-parent = &intc; |
指定设备的中断父控制器 |
| 设备树包含文件 | #include "imx6ul.dtsi" |
.dtsi 文件中定义的标签可在 .dts 中引用 |
最佳实践:在 SoC 级 .dtsi 文件中为所有外设节点定义标签(如
uart1、i2c2、spi0),在板级 .dts 文件中通过 &label 进行定制化配置。这种分离设计提高了代码复用性。
2.4 路径(Path):节点的唯一地址
路径是从根节点到目标节点的完整节点名称序列,使用斜杠分隔。路径用于在设备树中唯一标识一个节点,类似于文件系统中的绝对路径。
2.4.1 路径格式
/soc/uart@44e09000
/ocp/serial@44e09000
/cpus/cpu@0
路径以根节点 / 开头,逐级向下,每级使用节点名称(含单元地址)分隔。
2.4.2 路径的用途
- 设备树覆盖(DT Overlay):通过路径指定要修改的目标节点。
- 内核 API 查找节点:驱动中调用
of_find_node_by_path()通过路径获取节点。 - 设备树调试:在
/proc/device-tree文件系统中,路径直接映射为目录结构。 - Bootloader 修改:U-Boot 通过
fdt set命令使用路径修改设备树。
2.4.3 路径与标签的对比
| 特性 | 路径(Path) | 标签(Label) |
|---|---|---|
| 唯一性 | 全局唯一,从根到叶的完整描述 | 在文件作用域内唯一 |
| 可读性 | 较长,但结构清晰 | 简短,便于记忆 |
| 使用场景 | 调试、Bootloader、内核 API | 设备树覆盖、属性引用 |
| 依赖关系 | 依赖节点名称和层次结构 | 依赖标签定义,可跨文件引用 |
| 编译时检查 | 路径错误会导致编译失败 | 标签未定义会导致编译失败 |
实用技巧:在
/proc/device-tree 下查看设备树时,路径直接对应目录结构。例如,/ocp/serial@44e09000 对应 /proc/device-tree/ocp/serial@44e09000,该目录下的每个文件对应一个属性。
2.5 综合示例:解析一个完整的设备树片段
下面是一个综合示例,展示节点、属性、标签和路径如何协同工作:
/* soc.dtsi - SoC 级定义 */
/ {
compatible = "vendor,soc-model";
soc {
#address-cells = <1>;
#size-cells = <1>;
ranges;
intc: interrupt-controller@48200000 {
compatible = "ti,am33xx-intc";
reg = <0x48200000 0x1000>;
interrupt-controller;
#interrupt-cells = <1>;
};
uart0: serial@44e09000 {
compatible = "ti,am3352-uart", "ti,omap3-uart";
reg = <0x44e09000 0x2000>;
interrupts = <72>;
interrupt-parent = <&intc>;
status = "disabled";
};
};
};
/* board.dts - 板级定制 */
#include "soc.dtsi"
&uart0 {
status = "okay";
pinctrl-0 = <&uart0_pins>;
pinctrl-names = "default";
};
&uart0_pins: pinmux_uart0_pins {
pinctrl-single,pins = <
0x70 0x30 /* UART0_RXD */
0x74 0x00 /* UART0_TXD */
>;
};
解析:
intc和uart0是标签,在 soc.dtsi 中定义。- board.dts 通过
#include "soc.dtsi"包含 SoC 定义。 &uart0引用标签,将status从"disabled"改为"okay",并添加引脚控制属性。interrupt-parent = <&intc>通过标签引用中断控制器节点。- 路径
/soc/serial@44e09000可唯一标识 UART 节点。
2.6 本章小结
设备树的四个核心语法要素构成了硬件描述的基础:
- 节点(Node):树形结构的骨架,每个节点代表一个硬件组件。
- 属性(Property):节点的特征描述,包括兼容性、地址、中断等关键信息。
- 标签(Label):节点的别名,用于跨文件引用和属性覆盖。
- 路径(Path):节点的唯一地址,用于调试和内核 API 查找。