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(通用) #include 包含关系 DTC 编译器 dtc -I dts -O dtb 编译产物(二进制) .dtb(标准) .dtbo(覆盖) 使用场景 • .dtb:Bootloader 加载 → 内核解析 • .dtbo:运行时动态加载 → 修改设备树 典型流程 1. 编写 .dts + .dtsi → 2. DTC 编译 → 3. 生成 .dtb 4. Bootloader 加载 .dtb → 5. 内核启动

说白了,整个流程就是:你写 .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 是成品。搞清楚了它们的关系,后面学节点、学绑定就会顺畅很多。


专注资料整理