设备树语法入门:节点、属性、标签、包含文件
设备树(Device Tree)说白了就是一块板卡的“硬件说明书”。你想想看,Linux 内核要驱动一块板子,总得知道 CPU 是什么、内存多大、I2C 控制器挂在哪、GPIO 怎么映射吧?这些信息以前都硬编码在代码里,换一块板子就得重新编译内核。设备树就是来解决这个问题的——把硬件描述从内核代码里抽出来,变成一份独立的数据文件。
我个人习惯把设备树比作“乐高说明书”。它不关心你怎么拼,只告诉你有哪些零件、零件之间怎么连接。内核拿到这份说明书,就知道怎么初始化硬件了。好,咱们直接进入正题。
2.1 节点(Node)——设备树的基本单元
节点是设备树里最核心的概念。每个节点代表一个硬件设备或总线,比如 CPU、内存、I2C 控制器、UART 端口。节点用一对花括号 { } 包裹,里面可以嵌套子节点。
/ {
cpu {
compatible = "arm,cortex-a7";
};
memory {
device_type = "memory";
reg = <0x80000000 0x10000000>;
};
};
根节点 / 是整棵树的起点,所有设备都挂在它下面。我在项目中遇到过有人把节点名写成了 CPU(大写),结果内核死活认不出来——设备树节点名是大小写敏感的,这一点千万记住。
i2c-controller、serial-port。不要用下划线,那是老式设备树的写法,现在不推荐了。
2.2 属性(Property)——描述节点的“身份证”
属性就是节点的特征描述。每个属性都有一个名字和一个值。常见的属性类型有字符串、整数数组、二进制数据等。
| 属性名 | 含义 | 示例 |
|---|---|---|
compatible |
设备兼容性标识,驱动匹配的关键 | "vendor,device" |
reg |
寄存器地址和大小 | <0x10000000 0x1000> |
interrupts |
中断号 | <0 31 4> |
status |
设备状态(okay/disabled) | "okay" |
这里我要特别强调 compatible 属性。它就像设备的“身份证号”,驱动通过它来匹配设备。格式一般是 "厂商,设备型号",比如 "fsl,imx6ul-uart"。我曾经调试过一个网卡驱动,怎么都加载不上,最后发现是 compatible 字符串里多了一个空格——内核匹配是精确字符串比较,多一个字符都不行。
<> 表示 32 位整数数组,方括号 [] 表示字节数组,双引号 "" 表示字符串。别搞混了,否则编译出来的 dtb 内核根本解析不了。
2.3 标签(Label)——给节点起个“外号”
标签就是节点的别名。你想想看,如果一个节点嵌套得很深,比如 /soc/i2c@021a0000/rtc@68,每次引用它都要写这么一长串,多麻烦。标签就是用来解决这个问题的。
&i2c1 {
status = "okay";
clock-frequency = <100000>;
};
标签用 & 符号引用。我习惯在写设备树时,给所有重要的控制器都加上标签,比如 uart1、i2c2、spi0。这样后续添加子设备时,直接通过标签引用,代码清晰很多。
1st-uart,结果编译直接报错。标签的命名规则和 C 语言变量名一样:字母或下划线开头,后面跟字母、数字、下划线。
2.4 包含文件(Include)——模块化你的设备树
一个复杂的板卡可能有几十个设备,全写在一个文件里会非常臃肿。设备树支持 #include 预处理指令,可以把公共部分拆成单独的文件。
#include "imx6ul.dtsi"
#include "imx6ul-pinfunc.h"
/ {
model = "My Custom Board";
compatible = "my,imx6ul-board";
};
.dtsi 文件是设备树的“头文件”,通常存放 SoC 的公共定义。比如某个芯片厂商会提供 imx6ul.dtsi,里面定义了所有内置外设的节点框架。板卡厂商只需要在自己的 .dts 文件里包含它,然后覆盖或补充自己的配置。
嗯,这里要注意:#include 和 C 语言的 #include 是同一个预处理机制。所以你也可以用 #define 定义宏,在设备树里做条件编译。不过我个人不太推荐这么干——设备树应该保持简洁,宏用多了反而难调试。
.dtsi,板卡级配置放 .dts,外设模块单独拆成 .dtsi。比如 lcd-panel.dtsi、wifi-module.dtsi,这样换屏幕或换 Wi-Fi 模块时,只需要改一行包含路径。
2.5 知识体系总览
说了这么多,咱们用一张图把设备树语法的核心关系串起来。你一看就明白了。
从这张图可以看得很清楚:根节点下面挂子节点,每个节点带若干属性;标签是节点的快捷引用方式;包含文件则把公共定义抽离出来。这四个要素组合在一起,就构成了设备树的完整语法体系。
2.6 实战要点总结
- 节点:每个设备一个节点,嵌套关系反映总线拓扑。根节点
/是唯一入口。 - 属性:
compatible是驱动匹配的关键,reg描述地址空间,status控制设备启用/禁用。 - 标签:用
&引用,避免长路径。命名规则同 C 变量。 - 包含文件:
.dtsi放公共定义,.dts放板级配置。模块化是王道。
#include 路径写错。这些错误编译时都会报错,但错误信息有时候不太直观。我的建议是:写一行编译一行,别攒了一大堆再调试。
好了,设备树语法入门就讲到这里。这四个概念你掌握了,后面写设备树就跟搭积木一样简单。记住:节点是骨架,属性是血肉,标签是快捷方式,包含文件是模块化工具。把这四样用好,任何板卡的设备树你都能驾驭。