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 个字符,只能包含小写字母、数字、连字符和点号
设备树节点层级结构 / (根节点) cpu@0 memory@80000000 serial@44e09000 L1-cache pinctrl 节点层级说明 • 根节点 / 是设备树的入口,所有设备节点都是其子节点 • 子节点代表具体硬件设备或总线控制器 • 孙节点描述子设备内部的子模块(如 cache、pinctrl) • 节点深度一般不超过 5 层,过深会影响解析效率

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-parent
  • status — 设备状态,可选 "okay"、"disabled"、"reserved"
节点与属性关系 serial@44e09000 compatible = "ti,am3352-uart" reg = <0x44e09000 0x2000> interrupts = <72> status = "okay" 属性作用 ① compatible → 驱动匹配标识 ② reg → 寄存器基地址 + 范围 ③ interrupts → 中断资源描述 ④ status → 设备启用/禁用控制 ⑤ 属性值类型决定解析方式

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。
标签引用关系 &uart0 引用 uart0 节点 标签 uart0_pins pinmux_uart0_pins 实际节点定义 标签工作流程 1. 在节点定义时使用 标签名: 创建标签 2. 在其他位置使用 &标签名 引用该节点 3. 编译器将标签解析为 phandle(指针句柄),驱动通过 phandle 访问节点

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 必须与节点定义完全一致
路径解析流程 路径: /soc/serial@44e09000 步骤1: 定位根节点 / 步骤2: 查找子节点 soc 步骤3: 查找 serial@44e09000 返回 device_node 指针 路径解析由内核函数 of_find_node_by_path() 完成,逐级遍历子节点链表,匹配节点名和地址。

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()