2、DTS文件结构:DTS、DTSI、DTB、DTC的关系与区别
说到设备树,很多初学者一上来就被这四个缩写搞懵了。DTS、DTSI、DTB、DTC,到底谁是谁?
我刚开始接触时也绕了好一阵子。说白了,它们就是设备树在不同阶段的几种形态。就像做菜,有原材料、半成品、成品,还有厨具。嗯,这个比喻可能不太准确,但意思到了。
2.1 先搞清楚这四个东西
咱们一个一个来拆解:
| 缩写 | 全称 | 通俗理解 |
|---|---|---|
| DTS | Device Tree Source | 设备树源文件,文本格式,给人看的 |
| DTSI | Device Tree Source Include | 设备树头文件,公共部分抽出来复用 |
| DTB | Device Tree Blob | 设备树二进制文件,给内核读的 |
| DTC | Device Tree Compiler | 编译器,把DTS转成DTB |
你想想看,DTS就是源码,DTSI是公共头文件,DTC是编译器,DTB是编译后的二进制。这个链条其实很清晰。
2.2 DTS和DTSI的关系
DTSI文件,说白了就是用来被包含的。我习惯把SoC的公共部分、外设的默认配置都放在DTSI里。比如一个芯片有4个UART,但具体哪个板子用了哪几个,那是DTS的事。
举个例子:
// imx6ull.dtsi(SoC公共部分)
/ {
soc {
uart1: serial@02020000 {
compatible = "fsl,imx6ul-uart";
reg = <0x02020000 0x4000>;
status = "disabled";
};
uart2: serial@021e8000 {
compatible = "fsl,imx6ul-uart";
reg = <0x021e8000 0x4000>;
status = "disabled";
};
};
};
// myboard.dts(具体板子)
#include "imx6ull.dtsi"
/ {
model = "MyBoard";
compatible = "mycompany,myboard";
};
&uart1 {
status = "okay";
pinctrl-<0> = <&pinctrl_uart1>;
};
看到没?DTSI里把外设都定义好,但默认关掉。DTS里只打开需要的,再配一下引脚。这样复用性就很高了。
我的习惯:每个芯片平台建一个公共的dtsi目录,把重复的配置都扔进去。不同板子只需要include进来,改改差异部分就行。
2.3 DTC编译器怎么用
DTC是设备树的编译器,一般在Linux源码的scripts/dtc/目录下。系统里也可能直接装了:
# 编译:DTS → DTB
dtc -I dts -O dtb -o myboard.dtb myboard.dts
# 反编译:DTB → DTS
dtc -I dtb -O dts -o myboard.dts myboard.dtb
# 常用参数
# -I:指定输入格式(dts/dtb/fs)
# -O:指定输出格式
# -o:输出文件名
# -@:生成符号表(用于overlay)
我曾经遇到过一个坑:编译时没加-@参数,结果后面做设备树overlay时死活加载不上。查了半天才发现是符号表没生成。嗯,这个参数平时用不到,但用到的时候忘了就很头疼。
注意:反编译出来的DTS和原始DTS不会完全一样。注释会丢失,一些语法格式也会变。所以不要把反编译当成逆向工程的手段,它更多是用来调试和排查问题。
2.4 编译与反编译的实战场景
在实际项目中,我经常用反编译来做一件事:验证内核实际读到的设备树。
流程是这样的:
- 内核启动后,在
/sys/firmware/devicetree/base下能看到设备树内容 - 用
dtc -I fs -O dtb -o from_kernel.dtb /sys/firmware/devicetree/base导出DTB - 再反编译成DTS,看看内核实际用的是不是我们想要的那份
这个方法我用了很多次,尤其是在排查设备树没生效的问题时。有时候你觉得自己编译进去了,但内核实际加载的是另一份。一对比就现原形了。
2.5 知识体系总览
下面这张图,把整个流程串起来了:
2.6 避坑指南
最后分享几个我踩过的坑:
- DTSI的路径问题:include时路径是相对于DTC执行目录的,不是DTS文件所在目录。我一开始总搞错,后来统一用绝对路径或脚本处理。
- DTB的字节序:DTB是大端格式。如果你在x86小端机器上手动解析DTB,记得做字节序转换。不过DTC会处理好这些,一般不用操心。
- 多个DTSI的覆盖顺序:后include的会覆盖前面的同名节点。我曾经因为include顺序搞反,导致某个外设怎么都配不对。
核心要点:
- DTS是给人写的,DTB是给机器读的
- DTSI用于复用公共配置,减少重复代码
- DTC是桥梁,负责转换格式
- 反编译是调试利器,但别依赖它还原原始代码
好了,这一章的内容就这些。记住这四个东西的关系,后面写设备树的时候心里就有底了。