3. DTS 数据类型:字符串、整数、字节数组、64位整数、复合类型

设备树源文件(DTS)中的属性值必须遵循严格的数据类型定义。与 C 语言不同,DTS 的数据类型更贴近硬件描述需求,主要包含 字符串、整数(32位)、字节数组、64位整数 以及由它们组合而成的 复合类型。理解这些类型是正确编写和解析设备树的基础。

核心原则: DTS 中所有数值型数据默认采用大端序(Big-Endian)存储。这与 ARM 等处理器在设备树规范中的约定一致,驱动解析时需特别注意字节序转换。

3.1 字符串(String)

字符串是最常用的数据类型之一,用于描述设备名称、兼容性标识、状态标志等文本信息。字符串使用双引号 " " 包裹。

  • 单字符串: 直接赋值一个字符串值。
  • 字符串列表: 多个字符串用空格分隔,整体用双引号包裹,内核解析时按 '\0' 分隔。
// 单字符串示例
compatible = "fsl,imx6ul-uart";
status = "okay";

// 字符串列表示例(用于多平台兼容)
compatible = "ti,am335x-uart", "ns16550";
驱动匹配关键: compatible 属性是驱动与设备绑定的核心。驱动通过 of_match_table 匹配该字符串列表中的任意一个值,匹配顺序通常从最具体到最通用。

3.2 整数(32位)

32位整数是 DTS 中最基础的数值类型,使用 < > 尖括号包裹。可以包含十进制、十六进制(0x 前缀)或字符常量。

// 单个32位整数
reg = <0x02000000>;

// 多个32位整数(常用于描述地址和大小)
reg = <0x02000000 0x00001000>;
// 解释:基地址 0x02000000,地址范围 0x1000(4KB)

// 混合进制
interrupts = <0x1f 0x04>;  // 十六进制与十进制混合
表示法 示例 实际值(十进制)
十进制 <100> 100
十六进制 <0x64> 100
字符常量 <'A'> 65(ASCII码)

3.3 字节数组(Byte Array)

字节数组使用方括号 [ ] 包裹,每个字节用两位十六进制数表示,字节之间用空格分隔。常用于存储 MAC 地址、UUID、加密密钥或非标准长度的数据。

// MAC 地址示例
local-mac-address = [00 11 22 33 44 55];

// 任意字节序列
custom-data = [01 02 03 0A 0B 0C];
注意: 字节数组中的每个值必须严格在 0x00 到 0xFF 范围内。与整数不同,字节数组不进行字节序转换,按顺序逐字节存储。

3.4 64位整数(64-bit Integer)

由于 DTS 编译器(DTC)默认将 < > 内的数值视为 32 位,要表示 64 位整数,必须使用 两个连续的 32 位整数 组合,并通过 #address-cells 和 #size-cells 属性来声明。

  • 地址单元(address-cells): 表示地址字段需要几个 32 位整数。
  • 大小单元(size-cells): 表示长度字段需要几个 32 位整数。
// 64位地址示例(例如 64-bit ARM SoC 或 PCIe 地址空间)
/ {
    #address-cells = <2>;  // 地址用2个32位整数表示(共64位)
    #size-cells = <2>;     // 大小用2个32位整数表示(共64位)

    memory@8000000000 {
        device_type = "memory";
        reg = <0x00000000 0x80000000 0x00000001 0x00000000>;
        // 地址:0x00000000_80000000(高32位 + 低32位)
        // 大小:0x00000001_00000000(4GB)
    };
};
解析规则: 驱动读取 reg 属性时,会先读取父节点的 #address-cells 和 #size-cells,然后按此规则拆分 reg 中的整数序列。例如 reg = <A B C D> 在 address-cells=2, size-cells=1 时,表示地址为 (A << 32 | B),大小为 C,D 被忽略或属于下一个 reg 单元。

3.5 复合类型(Composite Type)

复合类型是指将上述基本类型组合在同一个属性中,形成结构化的数据。最常见的复合类型是 中断描述 和 GPIO 描述,它们通常包含控制器 phandle、中断号、触发类型等多个字段。

// 中断复合类型示例
interrupt-parent = <&gpio1>;   // 引用 GPIO 控制器节点
interrupts = <15 0x04>;        // GPIO1 的第15号引脚,上升沿触发

// GPIO 复合类型示例(使用 gpio 宏)
reset-gpios = <&gpio2 3 GPIO_ACTIVE_LOW>;
// 含义:引用 gpio2 控制器,第3号引脚,低电平有效

复合类型的解析完全依赖于 设备树绑定文档(Binding Documentation)。每个控制器节点(如中断控制器、GPIO 控制器)会定义其子节点中相关属性的格式和含义。

3.6 数据类型对比与选择

数据类型 语法符号 典型用途 字节序
字符串 " " compatible, status, model N/A(文本)
32位整数 < > reg, interrupts, clock-frequency 大端序
字节数组 [ ] MAC地址, 自定义原始数据 逐字节存储
64位整数 < > + cells 64位地址空间, 大内存描述 大端序(每32位单元内)
复合类型 混合使用 中断, GPIO, 时钟, DMA 依赖绑定文档

3.7 数据类型解析流程(SVG)

下图展示了 DTC 编译器与内核驱动在解析不同数据类型时的典型流程:

DTS 数据类型解析流程 DTS 数据类型解析流程 DTS 源文件 DTC 编译器 DTB 二进制文件 内核 OF API 解析层 of_property_read_string 字符串解析 of_property_read_u32 整数/64位解析 of_get_property 字节数组/复合解析 设备驱动(Driver Probe) 说明:DTC 编译时保留类型信息,内核 OF API 根据调用函数自动解析对应数据类型

3.8 实战注意事项

  • 字符串列表顺序: compatible 属性中,最具体的兼容字符串应放在最前面,驱动匹配时按顺序尝试。
  • 64位地址对齐: 在 32-bit 系统上描述 64 位地址时,务必正确设置父节点的 #address-cells = <2>,否则地址会被截断。
  • 字节数组与整数的转换: 不要将字节数组 [01 02] 与整数 <0x0102> 混用,前者占用 2 字节,后者占用 4 字节且受字节序影响。
  • 复合类型依赖文档: 编写复合类型属性前,必须查阅对应控制器的设备树绑定文档,确认每个字段的含义和顺序。
常见错误: 在 reg 属性中忘记设置 #address-cells 和 #size-cells,导致驱动解析出的地址或大小完全错误。这是设备树调试中最容易遇到的问题之一。