4. 设备树(DTS)基础:设备树语法、节点、属性、compatible与reg
设备树(Device Tree,简称 DT)是一种描述硬件资源的数据结构,它使用树形节点来定义系统中的 CPU、内存、总线、外设及其连接关系。在 Linux 内核中,设备树是驱动与硬件解耦的关键桥梁。本章将系统讲解设备树的语法结构、节点与属性定义,以及 compatible 和 reg 这两个核心属性的作用与用法。
4.1 设备树文件体系与编译
设备树源文件(.dts)经过设备树编译器(DTC)编译为二进制设备树 Blob(.dtb),Bootloader 在启动时将 .dtb 传递给内核。常见的文件类型如下:
| 文件后缀 | 说明 | 作用 |
|---|---|---|
.dts |
设备树源文件 | 描述单板硬件拓扑,可包含 .dtsi |
.dtsi |
设备树包含文件 | 存放 SoC 公共定义,被多个 .dts 引用 |
.dtb |
编译后的二进制 Blob | 内核解析的最终二进制数据 |
.dtbo |
设备树覆盖文件 | 运行时动态修改设备树(overlay) |
编译命令示例:
使用
dtc -I dts -O dtb -o myboard.dtb myboard.dts使用
dtc -I dtb -O dts -o myboard.dts myboard.dtb 可反向反编译。
4.2 设备树语法基础
设备树采用树形结构,根节点为 /,每个节点代表一个硬件设备或总线,节点内可包含子节点和属性。基本语法规则如下:
- 节点定义:
node-name@unit-address,其中unit-address通常为设备基地址。 - 属性赋值:
property-name = <value>;或property-name = "string";。 - 标签引用:使用
&label引用已定义的节点,用于追加或修改属性。 - 包含文件:
#include "soc.dtsi"引入公共定义。
/dts-v1/;
#include "soc.dtsi"
/ {
model = "MyBoard v1.0";
compatible = "vendor,myboard", "vendor,baseboard";
chosen {
stdout-path = &uart0;
};
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x10000000>; /* 256MB */
};
soc {
uart0: serial@10000000 {
compatible = "vendor,uart-ip";
reg = <0x10000000 0x1000>;
interrupts = <0 33 4>;
clock-frequency = <24000000>;
status = "okay";
};
};
};
语法要点:
- 每个语句以分号
;结尾,节点体用花括号{ }包裹。 - 属性值支持
<u32>、<u64>、字符串、字节数组、phandle引用。 status属性控制设备启用状态:"okay"启用,"disabled"禁用。
4.3 节点与属性详解
4.3.1 节点结构
每个设备树节点由节点名、单元地址、属性和子节点组成。节点名应尽量反映设备类型,单元地址用于区分同类型设备。根节点下通常包含以下标准节点:
chosen:传递内核启动参数,如stdout-path、bootargs。memory:描述系统物理内存布局。cpus:描述 CPU 核心信息。aliases:为节点定义别名,方便引用。
4.3.2 常用属性
| 属性名 | 值类型 | 说明 |
|---|---|---|
compatible |
字符串列表 | 设备驱动匹配标识,格式 "厂商,型号" |
reg |
<address length> 数组 |
设备地址空间描述,与 #address-cells、#size-cells 配合 |
interrupts |
<type irq flags> |
中断号与触发类型 |
clocks |
phandle 列表 | 引用时钟控制器节点 |
status |
字符串 | 设备状态:"okay"、"disabled"、"reserved" |
label |
字符串 | 节点标签,用于调试或别名引用 |
4.4 compatible 属性:驱动匹配的核心
compatible 属性是设备树与驱动绑定的关键。内核通过 of_match_table 中的 compatible 字符串与设备树节点的 compatible 属性进行匹配。匹配规则如下:
- 驱动中的
compatible列表与设备树节点的compatible列表按顺序逐一比较。 - 设备树节点的
compatible通常包含多个字符串,从最具体到最通用排列。 - 匹配成功后,内核调用驱动的
probe函数。
/* 设备树节点 */
uart@10000000 {
compatible = "vendor,uart-v2", "vendor,uart";
reg = <0x10000000 0x1000>;
};
/* 驱动匹配表 */
static const struct of_device_id uart_of_match[] = {
{ .compatible = "vendor,uart-v2", },
{ .compatible = "vendor,uart", },
{ /* end */ }
};
MODULE_DEVICE_TABLE(of, uart_of_match);
static struct platform_driver uart_driver = {
.probe = uart_probe,
.remove = uart_remove,
.driver = {
.name = "vendor-uart",
.of_match_table = uart_of_match,
},
};
匹配优先级:设备树节点的
compatible 列表顺序决定匹配优先级。驱动应优先匹配最具体的版本号,再匹配通用版本。例如 "vendor,uart-v2" 优先于 "vendor,uart"。
4.5 reg 属性:地址空间描述
reg 属性用于描述设备在总线上的地址空间,通常包含基地址和长度。其解析依赖于父节点的 #address-cells 和 #size-cells 属性:
#address-cells:表示地址字段占用的u32个数。#size-cells:表示长度字段占用的u32个数。reg属性由(address, length)对组成,每个对按#address-cells + #size-cells个u32排列。
/* 父节点定义地址空间格式 */
soc {
#address-cells = <1>; /* 地址用 1 个 u32 */
#size-cells = <1>; /* 长度用 1 个 u32 */
uart@10000000 {
reg = <0x10000000 0x1000>; /* 基址 0x10000000,长度 0x1000 */
};
ethernet@20000000 {
reg = <0x20000000 0x10000>; /* 基址 0x20000000,长度 0x10000 */
};
};
/* 64 位地址示例 */
bus {
#address-cells = <2>; /* 地址用 2 个 u32(64 位) */
#size-cells = <2>; /* 长度用 2 个 u32(64 位) */
dma@0,0 {
reg = <0x0 0x80000000 0x0 0x100000>; /* 地址 0x80000000,长度 0x100000 */
};
};
4.6 设备树与驱动绑定流程 SVG 图
下图展示了从设备树节点到驱动 probe 的完整绑定流程:
4.7 设备树节点结构 SVG 图
下图展示了一个典型 SoC 设备树的节点层级关系:
4.8 常见问题与避坑指南
常见错误:
- compatible 字符串不匹配:驱动中的
compatible与设备树中的字符串必须完全一致,包括大小写和标点。 - reg 地址空间错误:
#address-cells和#size-cells定义错误会导致reg解析偏移,驱动获取到错误的基地址。 - 中断号配置错误:
interrupts属性中的中断号必须与硬件中断控制器一致,否则驱动无法正确响应中断。 - status 未设置:节点默认
status为"disabled",需显式设置为"okay"才能被驱动识别。
调试技巧:
- 使用
dtc -I dtb -O dts反编译.dtb文件,检查实际传递给内核的设备树内容。 - 内核启动时添加
earlycon和console=ttyS0参数,查看设备树解析日志。 - 在驱动
probe函数中打印pdev->dev.of_node->name和of_get_property()返回值,验证资源获取是否正确。
4.9 本章小结
设备树是 Linux 内核硬件描述的标准方式,掌握其语法和核心属性是进行 Platform 驱动开发的基础。本章重点内容包括:
- 设备树文件体系:
.dts、.dtsi、.dtb的作用与编译方法。 - 节点定义与属性赋值的基本语法规则。
compatible属性作为驱动匹配的核心标识,其字符串顺序决定匹配优先级。reg属性配合#address-cells和#size-cells描述设备地址空间。- 设备树节点到驱动
probe的完整绑定流程,以及资源获取的常用 API。