3、设备树数据类型:字符串、整数、字节数组、phandle与复合类型

设备树里的数据类型,说白了就五种。字符串、整数、字节数组、phandle,还有复合类型。

我刚开始学设备树时,觉得这东西不就是个配置文件嘛。后来踩了几个坑才发现,每种数据类型背后都有讲究。今天咱们一个一个捋清楚。

3.1 字符串(string)

字符串是最直观的类型。用双引号包起来就行。

compatible = "ti,am335x-gpio";
model = "TI AM335x BeagleBone Black";

我个人习惯把 compatible 属性放在每个节点最前面。为什么?因为内核匹配驱动时,第一个找的就是它。

小技巧:字符串里可以包含逗号。比如 "ti,am335x-gpio",前面的 "ti" 是厂商前缀,后面是设备名。这是 Linux 内核的约定写法。

字符串数组也常见。多个字符串用空格隔开:

compatible = "ti,am335x-gpio", "ti,omap-gpio";

内核会依次尝试匹配。匹配到第一个就停。所以顺序很重要。

3.2 整数(integer)

整数在设备树里用 < > 括起来。默认是 32 位,大端序。

reg = <0x48000000 0x1000>;
interrupts = <29 0>;
clock-frequency = <24000000>;

嗯,这里要注意。设备树的整数是大端序。x86 开发机上跑模拟器时,我曾经被这个坑过。明明读出来是 0x12345678,打印出来变成 0x78563412。你想想看,排查了半天才发现是字节序问题。

避坑指南:我曾经在移植 U-Boot 时,因为没注意整数是大端序,导致 reg 地址解析错误。板子直接起不来。后来我养成了习惯:写设备树时,所有地址和长度都按大端序理解。

多个整数可以写在一起:

reg = <0x48000000 0x1000 0x49000000 0x2000>;

这表示两段地址空间。第一段基址 0x48000000,长度 0x1000。第二段基址 0x49000000,长度 0x2000。

3.3 字节数组(byte array)

字节数组用方括号 [ ] 表示。每个字节两个十六进制数。

local-mac-address = [00 11 22 33 44 55];
device-id = [ab cd ef];

字节数组没有长度限制。但要注意,每个值必须是 0x00 到 0xFF 之间。

我在项目中遇到过一种情况:某款网卡芯片要求 MAC 地址必须按字节数组传。用字符串传就不认。当时查了半天手册才发现这个细节。

重点:字节数组和整数的区别在于:整数是 4 字节对齐的,字节数组可以任意长度。存 MAC 地址、UUID 这类固定长度的数据,用字节数组更合适。

3.4 phandle(指针句柄)

phandle 是设备树里最灵活的数据类型。它本质上是一个 32 位整数,用来引用另一个节点。

clocks = <&clk_mcu 0>;
interrupt-parent = <&gpio1>;
dmas = <&edma 12>;

看到 & 符号了吗?它表示引用。编译器会把 &clk_mcu 替换成对应节点的 phandle 值。

为什么需要 phandle?因为设备树是树形结构。一个设备可能需要引用另一个设备。比如 GPIO 控制器和它的中断引脚。没有 phandle,你没法表达这种关系。

个人经验:我建议把 phandle 引用放在属性列表的最前面。这样读代码时,一眼就能看出这个设备依赖了哪些其他设备。调试时也方便。

phandle 可以带参数。比如 <&clk_mcu 0> 里的 0 表示时钟索引。具体含义由驱动决定。

3.5 复合类型

复合类型就是把上面几种类型混在一起用。最常见的是 reg 属性:

reg = <0x48000000 0x1000>;

这其实是两个整数组成的复合类型。第一个是地址,第二个是长度。

更复杂的例子:

gpio-specifier = <&gpio1 15 GPIO_ACTIVE_HIGH>;

这里混合了 phandle 和整数。&gpio1 是 phandle,15 是引脚号,GPIO_ACTIVE_HIGH 是标志。

注意:复合类型的顺序不能乱。比如 gpio-specifier 的格式是 (phandle, pin, flags)。你写成 (pin, phandle, flags) 驱动就认不出来了。我曾经在写 LED 节点时搞反了顺序,结果灯死活不亮。

还有一种常见的复合类型是 interrupts:

interrupts = <29 0>;

第一个数表示中断号,第二个数表示触发方式。0 表示上升沿触发,1 表示下降沿,2 表示高电平,3 表示低电平。

3.6 实际项目中的选择建议

说了这么多,到底什么时候用哪种类型?我总结了几条经验:

  • 字符串:设备名、兼容性标识、版本号。人类可读就行。
  • 整数:地址、长度、频率、中断号。需要计算或比较的场景。
  • 字节数组:MAC 地址、序列号、加密密钥。固定长度、二进制数据。
  • phandle:引用其他节点。设备依赖关系。
  • 复合类型:需要同时表达多个信息的场景。比如地址+长度、phandle+参数。

你想想看,如果 reg 属性用字符串写 "0x48000000 0x1000",驱动解析时还得做字符串转整数。多麻烦。直接用整数类型,编译器帮你搞定。

核心原则:能用整数就别用字符串。能用 phandle 就别硬编码数字。设备树的设计初衷就是让硬件描述更清晰、更可维护。

好了,数据类型就这些。下一章咱们聊聊设备树里最常用的属性,比如 reg、interrupts、clocks 这些。到时候你会发现,理解了数据类型,属性就变得很简单了。