3. 设备树基础语法
设备树(Device Tree)是嵌入式Linux系统中用于描述硬件拓扑结构和资源配置的核心机制。它通过一种树形数据结构,将CPU、内存、外设、总线等硬件信息从内核源码中剥离出来,以数据驱动的方式实现硬件描述与驱动代码的解耦。本章将深入剖析设备树的文件格式、节点与属性的语法规则,以及关键标准属性的使用方法。
3.1 设备树文件格式:DTS / DTSI / DTB
设备树的开发流程涉及三种文件格式,它们分别对应源码、包含文件和编译后的二进制文件。理解这三者的关系是进行设备树开发的第一步。
| 文件格式 | 全称 | 作用 | 可读性 | 编译工具 |
|---|---|---|---|---|
| .dts | Device Tree Source | 设备树源文件,描述单板级硬件 | 文本,可读 | dtc |
| .dtsi | Device Tree Source Include | 设备树包含文件,描述SoC级公共硬件 | 文本,可读 | dtc |
| .dtb | Device Tree Blob | 设备树二进制文件,供Bootloader加载 | 二进制,不可读 | dtc |
.dts / .dtsi → 使用 dtc 工具编译 → 生成 .dtb → Bootloader 将 .dtb 传递给内核。
.dtsi 文件描述芯片内部资源(如中断控制器、时钟、GPIO控制器),板级开发者通过 #include 引入 .dtsi,并在 .dts 中覆盖或添加板级特有的外设节点。
下图展示了从源码到二进制文件的完整编译流程:
3.2 节点(Node)与属性(Property)
设备树的核心结构由节点和属性组成。节点表示硬件设备或总线,属性描述节点的特征和配置参数。
3.2.1 节点语法
每个节点以 节点名@地址 的形式定义,节点名应体现设备类型,地址通常为寄存器基地址或设备编号。节点可以嵌套,形成树形层次结构。
/dts-v1/;
/ {
model = "Embedded Linux Board";
compatible = "vendor,board-name";
#address-cells = <1>;
#size-cells = <1>;
cpu@0 {
device_type = "cpu";
compatible = "arm,cortex-a7";
reg = <0x0>;
};
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x40000000>;
};
soc {
#address-cells = <1>;
#size-cells = <1>;
ranges;
uart@10000000 {
compatible = "vendor,uart";
reg = <0x10000000 0x1000>;
interrupts = <0 33 4>;
status = "okay";
};
ethernet@20000000 {
compatible = "vendor,gmac";
reg = <0x20000000 0x2000>;
interrupts = <0 45 4>;
phy-mode = "rgmii";
status = "disabled";
};
};
};
@地址 部分在节点名重复时用于区分,但地址本身不参与匹配。
3.2.2 属性语法
属性是键值对,用于描述节点的具体参数。属性值支持多种数据类型:
| 数据类型 | 语法示例 | 说明 |
|---|---|---|
| 字符串 | compatible = "vendor,device"; |
双引号包裹,用于设备匹配 |
| 32位整数 | reg = <0x10000000 0x1000>; |
尖括号包裹,多个值用空格分隔 |
| 64位整数 | reg = <0x00000000 0x80000000 0x0 0x40000000>; |
使用两个32位单元表示 |
| 布尔值 | status = "disabled"; |
通过字符串表示,如 "okay" / "disabled" |
| 字节数组 | local-mac-address = [00 11 22 33 44 55]; |
方括号包裹,十六进制字节 |
| 字符串列表 | clock-names = "eth", "mac"; |
多个字符串用逗号分隔 |
3.3 标准属性详解
设备树规范定义了一组标准属性,它们是驱动与设备树绑定的核心接口。以下逐一解析最常用的四个标准属性。
3.3.1 compatible 属性
compatible 是设备树中最重要的属性,用于将设备节点与驱动程序进行匹配。其值为字符串列表,按优先级从高到低排列。
// 驱动匹配时,内核按顺序尝试匹配
compatible = "vendor,device-rev2", "vendor,device-rev1", "vendor,device";
// 驱动端 of_match_table 示例
static const struct of_device_id my_driver_of_match[] = {
{ .compatible = "vendor,device-rev2", },
{ .compatible = "vendor,device-rev1", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_driver_of_match);
厂商名,设备名 格式,如 "fsl,imx6q-uart"、"ti,am335x-gpio"。厂商名使用小写,设备名使用小写字母和连字符。
3.3.2 reg 属性
reg 属性描述设备的地址空间信息,包括寄存器基地址和地址范围长度。其格式由父节点的 #address-cells 和 #size-cells 决定。
| 父节点属性 | 含义 | reg 示例 | 解析 |
|---|---|---|---|
#address-cells = <1>#size-cells = <1> |
32位地址,32位长度 | reg = <0x10000000 0x1000>; |
基址 0x10000000,长度 0x1000 |
#address-cells = <2>#size-cells = <1> |
64位地址,32位长度 | reg = <0x00000000 0x80000000 0x1000>; |
基址 0x0000000080000000,长度 0x1000 |
#address-cells = <1>#size-cells = <0> |
32位地址,无长度 | reg = <0x3>; |
仅地址 0x3(如I2C设备地址) |
3.3.3 interrupts 属性
interrupts 属性描述设备使用的中断资源。其格式依赖于中断控制器(interrupt-parent)的规范。
// 标准 ARM GIC 中断格式
// interrupts = <中断类型 中断号 触发方式>;
// 中断类型: 0=SPI(共享外设中断), 1=PPI(私有外设中断)
// 触发方式: 1=上升沿触发, 2=下降沿触发, 4=高电平触发, 8=低电平触发
uart@10000000 {
compatible = "vendor,uart";
reg = <0x10000000 0x1000>;
interrupt-parent = <&gic>; // 引用中断控制器
interrupts = <0 33 4>; // SPI 中断号33,高电平触发
};
interrupts-extended 属性或使用 interrupt-parent 指定父中断控制器。GPIO 中断的格式通常为 <&gpio1 15 2>,表示 GPIO1 的第15号引脚,下降沿触发。
3.3.4 status 属性
status 属性控制设备的启用状态,是设备树中实现硬件配置灵活性的关键属性。
| 状态值 | 含义 | 使用场景 |
|---|---|---|
"okay" |
设备启用,驱动正常加载 | 板级上实际存在的设备 |
"disabled" |
设备禁用,驱动不加载 | SoC 中存在但板级未使用的设备 |
"reserved" |
设备保留,不分配给操作系统 | 由固件或安全世界使用的设备 |
"fail" |
设备检测失败 | 硬件自检未通过 |
// 在 .dtsi 中默认禁用,在 .dts 中启用
// soc.dtsi
uart1: uart@10001000 {
compatible = "vendor,uart";
reg = <0x10001000 0x1000>;
status = "disabled";
};
// board.dts
#include "soc.dtsi"
&uart1 {
status = "okay";
pinctrl-0 = <&uart1_pins>;
};
3.4 设备树节点与属性的关系图
下图展示了设备树中根节点、子节点、属性以及标准属性之间的层次关系:
3.5 设备树编译与反编译实践
掌握 dtc 工具的使用是设备树开发的基本功。以下列出常用操作命令:
# 编译 .dts 为 .dtb
dtc -I dts -O dtb -o board.dtb board.dts
# 反编译 .dtb 为 .dts(调试用)
dtc -I dtb -O dts -o board_decompiled.dts board.dtb
# 检查 .dtb 文件是否有语法错误
dtc -I dtb -O dtb -o /dev/null board.dtb
# 生成设备树符号表(用于查看节点引用)
dtc -I dts -O dtb -o board.dtb board.dts -@
.dts 文件会丢失注释和部分格式信息,仅用于调试分析,不应作为修改的源文件。始终维护原始的 .dts / .dtsi 文件。
3.6 本章小结
设备树基础语法是嵌入式Linux驱动开发的必修课。通过本章学习,你应该掌握:
- 文件格式:
.dts源文件、.dtsi包含文件、.dtb二进制文件的角色与编译流程。 - 节点语法: 节点命名规则、层次结构、
#address-cells和#size-cells的地址空间定义。 - 属性语法: 字符串、整数、布尔值、字节数组等数据类型的表示方法。
- 标准属性:
compatible驱动匹配、reg地址映射、interrupts中断配置、status设备启用的具体用法。
在下一阶段的网络驱动移植实践中,你将直接操作设备树节点,为网络控制器添加或修改 compatible、reg、interrupts 等属性,实现驱动与硬件信息的正确绑定。