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