3. 设备树文件结构:dts、dtsi、dtb、dtbo 文件格式与关系
好,咱们今天来聊聊设备树的那些文件。很多初学者一上来就被 .dts、.dtsi、.dtb、.dtbo 这几个后缀搞晕了。说实话,我当年刚接触时也懵过一阵子。它们到底是什么关系?各自扮演什么角色?
别急,咱们一个一个拆开看。
3.1 源文件:.dts 和 .dtsi
.dts 是设备树源文件(Device Tree Source)。它就是描述硬件信息的文本文件,我们平时写设备树,主要就是在写 .dts 文件。
.dtsi 是设备树头文件(Device Tree Source Include)。它跟 C 语言里的 .h 文件一个道理,用来放一些通用的、可复用的内容。
举个例子。我做过一个项目,同一款芯片用在了三块不同的板子上。三块板子的串口、I2C 控制器都一样,只是外设不同。这时候,我就把芯片公共的部分写进 .dtsi,然后每个板子单独写一个 .dts,用 #include 把 .dtsi 包含进来。
核心思想:.dtsi 放共性,.dts 放个性。这样改一个板子时,不会影响到其他板子。
来看一个简单的例子:
// imx6ull-common.dtsi
/ {
soc {
uart1: serial@02020000 {
compatible = "fsl,imx6ul-uart";
reg = <0x02020000 0x4000>;
status = "disabled";
};
};
};
// imx6ull-myboard.dts
#include "imx6ull-common.dtsi"
/ {
model = "My i.MX6ULL Board";
compatible = "mycompany,imx6ull-board";
};
&uart1 {
status = "okay";
pinctrl-0 = <&pinctrl_uart1>;
};
你看,.dtsi 里定义了 uart1,默认是 disabled。板级 .dts 里用 &uart1 引用它,然后打开并配置引脚。这种写法,我个人觉得非常清爽。
小技巧:我习惯把 SoC 厂商提供的 .dtsi 当作只读文件,绝不修改。自己板子的差异全写在 .dts 里。这样内核升级时,直接替换厂商的 .dtsi 就行,省心很多。
3.2 编译产物:.dtb 和 .dtbo
.dtb 是编译后的二进制文件(Device Tree Blob)。内核启动时,Bootloader 会把 .dtb 加载到内存,然后传给内核。内核解析它,就知道硬件长什么样了。
.dtbo 是设备树覆盖文件(Device Tree Blob Overlay)。它用于运行时动态修改设备树。比如你有一个树莓派,插上一个 HDMI 显示屏,系统不需要重启,直接加载一个 .dtbo 就能把显示驱动挂上。
编译命令很简单:
# .dts 编译成 .dtb
dtc -I dts -O dtb -o myboard.dtb myboard.dts
# .dts 编译成 .dtbo(需要 -@ 选项生成符号)
dtc -I dts -O dtb -@ -o myoverlay.dtbo myoverlay.dts
这里有个坑,我曾经踩过。编译 .dtbo 时,-@ 选项必须加。它会在 .dtbo 里生成符号表,这样内核才能知道这个覆盖文件要修改设备树的哪个节点。不加的话,加载时会报错。
注意:不是所有内核都支持设备树覆盖。你需要确认内核配置中 CONFIG_OF_OVERLAY 是否开启。我遇到过客户拿着不支持覆盖的内核,非要用 .dtbo,结果折腾了两天。
3.3 它们之间的关系
我用一张图来总结它们的关系:
说白了,整个流程就是:你写 .dts 和 .dtsi,然后用 DTC 编译器把它们变成 .dtb。Bootloader 把 .dtb 交给内核,内核就知道硬件怎么玩了。如果你需要热插拔外设,那就用 .dtbo 在运行时打补丁。
3.4 一个常见的误解
有人问我:.dtsi 是不是必须跟 .dts 放在同一个目录?
不是的。你可以用 -i 选项指定搜索路径。比如:
dtc -I dts -O dtb -o myboard.dtb -i /path/to/include myboard.dts
我习惯把厂商的 .dtsi 放在一个独立的 include/ 目录下,板级 .dts 放在 boards/ 目录下。这样结构清晰,找文件也方便。
3.5 总结一下
| 文件类型 | 全称 | 格式 | 作用 |
|---|---|---|---|
| .dts | Device Tree Source | 文本 | 板级设备树源文件 |
| .dtsi | Device Tree Source Include | 文本 | 通用设备树头文件 |
| .dtb | Device Tree Blob | 二进制 | 编译后的设备树,内核启动时使用 |
| .dtbo | Device Tree Blob Overlay | 二进制 | 运行时覆盖,用于动态修改设备树 |
我的建议:刚开始学设备树,先别碰 .dtbo。把 .dts、.dtsi、.dtb 这三者搞明白,能正常编译、启动,就已经入门了。覆盖技术等后面有需求了再研究。
嗯,文件结构这部分就讲到这里。记住一句话:.dtsi 是积木,.dts 是图纸,.dtb 是成品。搞清楚了它们的关系,后面学节点、学绑定就会顺畅很多。