1. 设备树基础概念
1.1 什么是设备树(Device Tree)
设备树(Device Tree,DT)是一种描述硬件资源的数据结构,它使用树形节点来记录系统中所有可发现的设备、内存映射、中断、时钟、GPIO 等硬件信息。设备树源文件(.dts)经过编译生成设备树二进制文件(.dtb),在系统启动时由引导加载程序传递给内核,内核据此完成设备驱动与硬件资源的绑定。
核心要点:设备树不是代码,而是数据。它把硬件描述从内核源码中剥离出来,使同一份内核二进制可以适配多种硬件平台。
1.2 为什么需要设备树
在设备树出现之前,Linux 内核使用大量的板级文件(board-*.c)来注册硬件信息。这种做法在 ARM 架构上导致了严重的代码膨胀和维护灾难。设备树的引入解决了以下核心问题:
- 硬件描述与内核解耦:同一份内核镜像可以支持多种硬件平台,只需更换
.dtb文件。 - 减少内核代码量:不再需要为每块开发板编写数百行的板级注册代码。
- 支持多平台统一:ARM、RISC-V、x86 等架构统一使用设备树描述硬件。
- 便于社区维护:硬件描述集中管理,驱动代码更通用。
- 动态加载能力:设备树覆盖(Device Tree Overlay)允许运行时添加或修改硬件描述。
历史背景:设备树最初源自 Open Firmware(IEEE 1275)规范,PowerPC 架构最早采用。2011 年 Linux 3.0 开始,ARM 架构强制要求使用设备树,结束了长达十年的板级文件混乱局面。
1.3 设备树与 ACPI 的区别
ACPI(Advanced Configuration and Power Interface)是 x86 平台广泛使用的硬件描述和电源管理标准。设备树与 ACPI 虽然目标相似,但在设计哲学、使用场景和实现方式上有显著差异:
| 对比维度 | 设备树(Device Tree) | ACPI |
|---|---|---|
| 起源 | Open Firmware(IEEE 1275),PowerPC 率先采用 | Intel、Microsoft、Toshiba 联合制定,x86 主导 |
| 主要平台 | ARM、RISC-V、PowerPC、MIPS 等嵌入式/移动平台 | x86/x86_64 服务器、PC、笔记本 |
| 数据格式 | 文本源文件(.dts)→ 二进制(.dtb),人类可读 | ASL 源码 → AML 字节码,需反编译查看 |
| 运行时能力 | 支持设备树覆盖(Overlay),动态加载/卸载 | 支持动态表加载,但机制复杂 |
| 电源管理 | 弱,需驱动自行处理电源控制 | 强,内置 _PS0/_PS3、C-state、P-state 等标准方法 |
| 热插拔 | 通过 Overlay 实现,需手动触发 | 原生支持 PCIe、USB 等热插拔事件 |
| 内核集成度 | 设备树由 bootloader 加载,内核解析 | ACPI 表由固件(UEFI/BIOS)提供,内核解释执行 AML |
| 适用场景 | 嵌入式系统、IoT、开发板、定制硬件 | 服务器、桌面 PC、工作站、复杂电源管理场景 |
重要提示:虽然设备树和 ACPI 都用于硬件描述,但它们在 Linux 内核中走的是完全不同的驱动路径。设备树驱动使用
of_ 系列 API(如 of_property_read_u32),而 ACPI 驱动使用 acpi_ 系列 API(如 acpi_evaluate_object)。两者不能混用。
1.4 设备树的核心文件类型
在实际开发中,你会接触到以下几种设备树相关文件:
| 文件类型 | 扩展名 | 说明 |
|---|---|---|
| 设备树源文件 | .dts |
描述单块开发板或设备的完整硬件树,是编译入口 |
| 设备树包含文件 | .dtsi |
公共硬件描述,类似头文件,可被多个 .dts 包含 |
| 设备树二进制 | .dtb |
.dts 经 dtc 编译后的二进制文件,供内核使用 |
| 设备树覆盖源文件 | .dtso |
描述增量修改的覆盖源文件,用于动态加载 |
| 设备树覆盖二进制 | .dtbo |
.dtso 编译后的覆盖二进制文件 |
开发提示:使用
dtc -I dtb -O dts -o output.dts input.dtb 可以将二进制 .dtb 反编译为可读的 .dts 源文件,这是调试硬件描述问题的常用手段。
1.5 一个简单的设备树示例
以下是一个简化的设备树源文件,展示了基本的节点和属性结构:
/dts-v1/;
/ {
compatible = "acme,coyote", "acme,board";
#address-cells = <1>;
#size-cells = <1>;
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
compatible = "arm,cortex-a9";
reg = <0x0>;
device_type = "cpu";
};
};
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x20000000>; /* 512 MB */
};
soc {
#address-cells = <1>;
#size-cells = <1>;
ranges;
serial@101f1000 {
compatible = "ns16550";
reg = <0x101f1000 0x1000>;
interrupts = <0x1 0x0>;
clock-frequency = <3686400>;
};
i2c@101f2000 {
compatible = "acme,i2c-bus";
reg = <0x101f2000 0x1000>;
interrupts = <0x2 0x0>;
#address-cells = <1>;
#size-cells = <0>;
eeprom@50 {
compatible = "atmel,24c02";
reg = <0x50>;
pagesize = <16>;
};
};
};
};
关键语法说明:
•
•
•
•
•
•
/dts-v1/; — 版本标识,必须位于文件开头•
compatible — 最重要的属性,用于驱动匹配,格式为 "厂商,型号"•
reg — 地址和大小,格式由 #address-cells 和 #size-cells 决定•
interrupts — 中断号与触发类型•
ranges; — 地址转换映射,空值表示 1:1 映射