第一章:设备树的前世今生
为什么要有设备树?从平台设备到设备树的演进史
大家好,我是你们的讲师。今天咱们聊聊设备树的起源。
说实话,我刚入行那会儿,Linux内核里还没有设备树这玩意儿。那时候驱动怎么写的?直接在内核代码里硬编码硬件信息。你想想看,换一块开发板,就得改内核源码,重新编译。这日子,谁过谁知道。
一、平台设备时代的痛点
在设备树出现之前,Linux内核主要靠平台设备(Platform Device)来描述硬件。每个板子都有一份 board-xxx.c 文件,里面写满了寄存器地址、中断号、GPIO引脚。
典型代码长这样:
static struct resource uart_resources[] = {
{
.start = 0x10000000,
.end = 0x10000FFF,
.flags = IORESOURCE_MEM,
},
{
.start = 32,
.end = 32,
.flags = IORESOURCE_IRQ,
},
};
static struct platform_device uart_device = {
.name = "my_uart",
.id = 0,
.num_resources = ARRAY_SIZE(uart_resources),
.resource = uart_resources,
};
嗯,这里要注意。每个板子都得写这么一份。我当年在项目里维护过6块不同板子的 board-*.c 文件,每次换平台,光改这些就够头疼的。
二、这种模式有什么问题?
说白了,就是硬件描述和内核代码强耦合。具体来说:
- 代码膨胀:每个板子一份 board 文件,ARM 架构下这类文件一度超过 3000 个
- 维护噩梦:改一个 GPIO 引脚,得重新编译整个内核
- 碎片化严重:同样的 UART 控制器,不同板子要写不同的 platform_device
- 启动慢:内核要枚举所有可能的设备,哪怕板子上根本没有
我记得有一次,客户换了一颗 Flash 芯片,只是引脚重映射了一下。结果呢?我得从内核源码里找到对应的 board 文件,改完再给客户发一个完整的内核镜像。你说这效率...
三、设备树是怎么解决这些问题的?
设备树(Device Tree)的思路其实很简单:把硬件描述从内核代码里抽出来,放到一个独立的数据结构里。这个数据结构就是 .dts 文件。
同样的 UART,用设备树描述就变成了:
uart0: serial@10000000 {
compatible = "my_uart";
reg = <0x10000000 0x1000>;
interrupts = <32>;
status = "okay";
};
你看,是不是清爽多了?硬件信息全在文本里,内核不用再硬编码了。
四、设备树的核心设计思想
我个人习惯把设备树理解成硬件配置单。它做了三件事:
- 描述硬件拓扑:CPU、内存、外设怎么连接的
- 提供配置参数:寄存器地址、中断号、时钟频率等
- 解耦驱动和硬件:驱动只认 compatible 字符串,不关心具体板子
一个小技巧:我在项目中经常用 dtc -I dtb -O dts 反编译设备树,看看实际运行时内核收到了什么配置。这招排查硬件配置问题特别管用。
五、设备树的演进时间线
咱们用一张图来梳理一下:
六、设备树到底带来了什么好处?
咱们用表格对比一下:
| 对比项 | 平台设备时代 | 设备树时代 |
|---|---|---|
| 硬件描述位置 | 内核源码 board-*.c | 独立 .dts 文件 |
| 换板子成本 | 改内核,重新编译 | 换 .dtb 文件即可 |
| 驱动复用性 | 低,与板级代码耦合 | 高,只认 compatible |
| 内核镜像大小 | 大,包含所有板子代码 | 小,只包含驱动逻辑 |
| 启动速度 | 慢,需枚举所有设备 | 快,只初始化存在的设备 |
| 社区维护成本 | 极高,碎片化严重 | 低,统一标准 |
避坑指南:我曾经在一个项目里,直接把 x86 的 ACPI 思路套到 ARM 设备树上,结果发现设备树不支持动态加载。后来才搞清楚,设备树是静态描述,启动时一次性传递给内核。这个坑我踩过,大家注意。
七、设备树的核心文件结构
一个完整的设备树系统包含这些文件:
- .dts:设备树源文件,你直接编写的内容
- .dtsi:设备树头文件,公共部分放这里,比如 SoC 的定义
- .dtb:编译后的二进制文件,内核实际使用的
- .dtbo:设备树覆盖文件,用于运行时修改
编译命令也很简单:
dtc -I dts -O dtb -o myboard.dtb myboard.dts
反编译回来调试:
dtc -I dtb -O dts -o myboard.dts myboard.dtb
八、总结一下
设备树的出现,说白了就是 Linux 内核在硬件描述上的一次解耦革命。它让驱动开发者和板级开发者各司其职:
- 驱动开发者:只关心 compatible 匹配,写通用驱动
- 板级开发者:只关心 .dts 文件,描述硬件连接
- 内核维护者:不用再为每个板子维护一份 board 文件
我个人觉得,理解设备树的关键就一句话:硬件是硬件,驱动是驱动,别混在一起。后面我们会深入讲解设备树的语法、绑定规则,以及怎么和驱动完美配合。