3、设备树数据类型:字符串、整数、字节数组、phandle与复合类型
设备树里的数据类型,说白了就五种。字符串、整数、字节数组、phandle,还有复合类型。
我刚开始学设备树时,觉得这东西不就是个配置文件嘛。后来踩了几个坑才发现,每种数据类型背后都有讲究。今天咱们一个一个捋清楚。
3.1 字符串(string)
字符串是最直观的类型。用双引号包起来就行。
compatible = "ti,am335x-gpio";
model = "TI AM335x BeagleBone Black";
我个人习惯把 compatible 属性放在每个节点最前面。为什么?因为内核匹配驱动时,第一个找的就是它。
字符串数组也常见。多个字符串用空格隔开:
compatible = "ti,am335x-gpio", "ti,omap-gpio";
内核会依次尝试匹配。匹配到第一个就停。所以顺序很重要。
3.2 整数(integer)
整数在设备树里用 < > 括起来。默认是 32 位,大端序。
reg = <0x48000000 0x1000>;
interrupts = <29 0>;
clock-frequency = <24000000>;
嗯,这里要注意。设备树的整数是大端序。x86 开发机上跑模拟器时,我曾经被这个坑过。明明读出来是 0x12345678,打印出来变成 0x78563412。你想想看,排查了半天才发现是字节序问题。
多个整数可以写在一起:
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 地址必须按字节数组传。用字符串传就不认。当时查了半天手册才发现这个细节。
3.4 phandle(指针句柄)
phandle 是设备树里最灵活的数据类型。它本质上是一个 32 位整数,用来引用另一个节点。
clocks = <&clk_mcu 0>;
interrupt-parent = <&gpio1>;
dmas = <&edma 12>;
看到 & 符号了吗?它表示引用。编译器会把 &clk_mcu 替换成对应节点的 phandle 值。
为什么需要 phandle?因为设备树是树形结构。一个设备可能需要引用另一个设备。比如 GPIO 控制器和它的中断引脚。没有 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 是标志。
还有一种常见的复合类型是 interrupts:
interrupts = <29 0>;
第一个数表示中断号,第二个数表示触发方式。0 表示上升沿触发,1 表示下降沿,2 表示高电平,3 表示低电平。
3.6 实际项目中的选择建议
说了这么多,到底什么时候用哪种类型?我总结了几条经验:
- 字符串:设备名、兼容性标识、版本号。人类可读就行。
- 整数:地址、长度、频率、中断号。需要计算或比较的场景。
- 字节数组:MAC 地址、序列号、加密密钥。固定长度、二进制数据。
- phandle:引用其他节点。设备依赖关系。
- 复合类型:需要同时表达多个信息的场景。比如地址+长度、phandle+参数。
你想想看,如果 reg 属性用字符串写 "0x48000000 0x1000",驱动解析时还得做字符串转整数。多麻烦。直接用整数类型,编译器帮你搞定。
好了,数据类型就这些。下一章咱们聊聊设备树里最常用的属性,比如 reg、interrupts、clocks 这些。到时候你会发现,理解了数据类型,属性就变得很简单了。