2. 设备树语法基础:节点、属性、标签、路径

设备树(Device Tree)是一种描述硬件资源的数据结构,它使用树形层次结构将系统中的CPU、内存、外设、中断控制器、GPIO控制器等硬件信息组织起来。理解设备树的语法规则是编写设备树源文件(.dts)和开发设备驱动的第一步。

设备树的语法由四个核心元素构成:节点(node)、属性(property)、标签(label)和路径(path)。下面逐一讲解它们的语法规则与使用场景。

2.1 节点(Node)—— 设备树的骨架

节点是设备树的基本组成单元,每个节点代表一个硬件设备、总线、控制器或功能模块。节点采用树形嵌套结构,根节点为/,所有其他节点都是根节点的子节点或后代节点。

节点语法格式:

node-name {
    property1 = <value>;
    property2 = "string";
    child-node {
        ...
    };
};
  • 节点名称:由字母、数字、连字符(-)、下划线(_)组成,不能以数字开头。
  • 节点体:用花括号 { } 包裹,内部包含属性和子节点。
  • 节点结束:用分号 ; 表示节点定义结束。
  • 根节点:/ { ... }; 是设备树的顶层节点,所有硬件描述都从根节点开始。
节点命名规范: 推荐使用 device-name@unit-address 格式,其中 unit-address 是设备在总线上的基地址(如寄存器地址),用于区分同名的设备节点。
示例: uart@44e09000 表示一个UART设备,其寄存器基地址为 0x44e09000。如果设备没有地址(如虚拟设备),可以省略 @unit-address 部分。
设备树节点层次结构 / (根节点) cpu@0 memory@80000000 soc i2c@44e0b000 uart@44e09000 eeprom@50 rtc@68 节点层次:根节点 → 一级子节点(cpu/memory/soc)→ 二级子节点(i2c/uart)→ 三级子节点(eeprom/rtc)

2.2 属性(Property)—— 描述设备特征

属性是附着在节点上的键值对,用于描述设备的硬件特征、配置参数、资源映射等。属性值可以是多种数据类型。

属性语法格式:

property-name = <value>;
property-name = "string";
property-name = [byte-stream];
property-name;  

属性值类型对照表:

数据类型 语法表示 示例 说明
32位整数 <value> <0x1000> 单个32位无符号整数
整数数组 <val1 val2 ...> <0x1000 0x2000 0x3000> 多个32位整数,空格分隔
字符串 "string" "ti,omap3-uart" 双引号包裹的文本
字符串列表 "str1", "str2" "okay", "disabled" 逗号分隔的多个字符串
字节流 [hex bytes] [01 02 03 0a] 十六进制字节序列,空格分隔
布尔属性 仅属性名,无值 status; 存在即为 true,不存在为 false
混合类型 <val> "str" [bytes] <0x100> "reg" [00 01] 不同类型值组合,空格分隔
常见陷阱: 属性值中的逗号、分号、引号、尖括号必须严格匹配。例如 <0x1000> 不能写成 <0x1000> 缺少尖括号;字符串必须使用双引号,不能使用单引号。

标准属性示例:

uart@44e09000 {
    compatible = "ti,omap3-uart";       // 字符串:驱动兼容性匹配
    reg = <0x44e09000 0x2000>;          // 整数数组:寄存器基地址+长度
    interrupts = <0x2d>;                // 整数:中断号
    status = "okay";                     // 字符串:设备状态
    clock-frequency = <48000000>;       // 整数:时钟频率
    dmas = <&edma 0x08>;               // 引用+整数:DMA通道
};

2.3 标签(Label)—— 节点的引用别名

标签是为节点定义的引用别名,可以在设备树的其他位置通过 &label 语法引用该节点。标签极大简化了节点路径的书写,尤其在覆盖(overlay)和引用其他节点时非常有用。

标签语法格式:

label: node-name {
    // 节点内容
};

标签引用语法:

&label {
    // 追加或覆盖属性
};

标签使用示例:

// 定义带标签的节点
uart0: uart@44e09000 {
    compatible = "ti,omap3-uart";
    reg = <0x44e09000 0x2000>;
    status = "okay";
};

// 在其他地方引用标签,追加属性
&uart0 {
    pinctrl-0 = <&uart0_pins>;
    clock-frequency = <48000000>;
};
标签命名规则: 标签名必须以字母或下划线开头,后跟字母、数字、下划线。标签名不能与节点名、属性名冲突。推荐使用有意义的名称,如 uart0、i2c1、mmc0。
标签(Label)引用与节点覆盖关系 原始节点定义 uart0: uart@44e09000 compatible = "ti,omap3-uart"; reg = <0x44e09000 0x2000>; 标签引用覆盖 &uart0 { pinctrl-0 = <&uart0_pins>; status = "okay"; 编译后合并结果 uart@44e09000 { compatible; reg; pinctrl-0; status; }

2.4 路径(Path)—— 定位节点的地址

路径是从根节点到目标节点的完整地址描述,类似于文件系统中的绝对路径。路径用于在设备树中唯一标识一个节点,常用于 phandle 引用、设备树覆盖(overlay)和驱动代码中的节点查找。

路径语法格式:

/node-name/child-node/grandchild-node
  • 以斜杠 / 开头,表示从根节点开始。
  • 各级节点名称用斜杠 / 分隔。
  • 路径中不包含 @unit-address 部分也可以匹配,但推荐包含以精确指定。

路径示例:

/soc/i2c@44e0b000/eeprom@50
/soc/uart@44e09000
/cpus/cpu@0

路径与标签的对比:

特性 路径(Path) 标签(Label)
语法 /soc/i2c@44e0b000 &i2c0
可读性 冗长,但精确 简洁,易读
唯一性 全局唯一 需保证标签名不重复
使用场景 驱动代码中查找节点(of_find_node_by_path) 设备树源文件内引用、覆盖、overlay
灵活性 固定路径,不易重构 标签名不变即可,节点可移动
实践建议: 在设备树源文件(.dts)中优先使用标签引用,便于维护和覆盖。在驱动代码中,使用路径或 phandle 查找节点,因为驱动无法直接使用标签名。

2.5 节点名称中的 unit-address

当多个同类型设备挂载在同一总线上时,unit-address 用于区分它们。它通常对应设备的寄存器基地址、片选号或端口号。

语法:

device-type@unit-address

示例:

i2c@44e0b000 {
    eeprom@50 {          // 7位I2C地址 0x50
        compatible = "atmel,24c02";
        reg = <0x50>;
    };
    rtc@68 {             // 7位I2C地址 0x68
        compatible = "ds1307";
        reg = <0x68>;
    };
};
注意: unit-address 必须与 reg 属性中的第一个地址值一致,否则设备树编译器(DTC)会报错。例如 eeprom@50 节点的 reg = <0x50> 必须匹配。

2.6 节点与属性的完整示例

下面是一个完整的设备树片段,综合展示了节点、属性、标签、路径的语法:

/dts-v1/;

/ {
    model = "TI AM335x Board";
    compatible = "ti,am335x";

    cpus {
        cpu@0 {
            compatible = "arm,cortex-a8";
            device_type = "cpu";
            reg = <0x0>;
        };
    };

    memory@80000000 {
        device_type = "memory";
        reg = <0x80000000 0x10000000>;  // 256MB
    };

    leds {
        compatible = "gpio-leds";
        led0: led@0 {
            label = "heartbeat";
            gpios = <&gpio1 0x10 0x0>;
            linux,default-trigger = "heartbeat";
        };
    };

    i2c0: i2c@44e0b000 {
        compatible = "ti,omap4-i2c";
        reg = <0x44e0b000 0x1000>;
        interrupts = <0x46>;
        status = "okay";
        clock-frequency = <100000>;

        eeprom@50 {
            compatible = "atmel,24c02";
            reg = <0x50>;
        };
    };
};

// 使用标签引用追加属性
&i2c0 {
    pinctrl-0 = <&i2c0_pins>;
    pinctrl-names = "default";
};

// 使用标签引用覆盖 led0 属性
&led0 {
    linux,default-trigger = "timer";
};
关键点总结:
  • 节点:树形结构,/ 为根,子节点用 { } 包裹。
  • 属性:键值对,支持整数、字符串、字节流、布尔等多种类型。
  • 标签:label: node-name 定义,&label 引用,用于覆盖和追加。
  • 路径:/node/child 格式,用于驱动代码中定位节点。
  • unit-address:节点名中 @ 后的地址,必须与 reg 属性匹配。
设备树语法元素关系总览 设备树节点 属性 标签 路径 unit-address 节点是核心骨架,属性描述特征,标签提供引用别名,路径定位节点位置,unit-address 区分同类型设备