1. 设备树起源与作用:为什么需要设备树?
大家好,我是你们的嵌入式Linux驱动讲师。今天咱们聊聊设备树的起源。
说实话,我刚开始做嵌入式Linux那会儿,还没有设备树这东西。那时候的驱动开发,说白了就是跟板级文件死磕。你想想看,每换一个硬件平台,就得重新写一遍代码,那滋味可不好受。
从板级文件到设备树的演进
在Linux 2.6时代,内核里充斥着大量的板级文件。比如arch/arm/mach-xxx目录下,每个板子都有自己的c文件和头文件。这些文件里定义了内存地址、中断号、GPIO映射等等。
我印象特别深,有一次公司要支持三块不同的开发板,每块板子的UART地址都不一样。我不得不复制粘贴代码,然后手动修改每个地址。那段时间,我做梦都在改宏定义。
板级文件的痛点:
- 代码冗余:每个板子都要写大量重复代码
- 维护困难:改一个硬件参数,要重新编译整个内核
- 碎片化严重:厂商各自为政,内核越来越臃肿
- 二进制不兼容:同一个内核镜像无法在不同板子上运行
为什么会这样?因为硬件描述和驱动代码完全耦合在一起。说白了,就是把硬件信息硬编码到了C语言里。
举个例子,以前我们这样描述一个LED:
// 板级文件中的写法
static struct gpio_led my_leds[] = {
{
.name = "power_led",
.gpio = 32, // GPIO编号硬编码
.active_low = 0,
},
};
static struct gpio_led_platform_data led_data = {
.leds = my_leds,
.num_leds = 1,
};
你看,GPIO编号、极性这些信息都写死在代码里。换一块板子,GPIO变成33了,就得改代码重新编译。这效率,说实话太低了。
设备树的诞生
2005年左右,PowerPC架构率先引入了设备树(Device Tree)的概念。后来ARM社区也意识到这个问题,从Linux 3.x开始全面采用设备树。
设备树的核心思想很简单:把硬件描述从驱动代码中分离出来。硬件信息用文本文件描述,驱动代码只负责解析这些描述。
我个人习惯把设备树比作硬件的"身份证"。它告诉内核:我是谁、我在哪、我有哪些资源。至于怎么用这些资源,那是驱动代码的事。
同样的LED,用设备树描述就变成了这样:
// 设备树中的写法
leds {
compatible = "gpio-leds";
power_led {
label = "power";
gpios = &gpio1 32 GPIO_ACTIVE_LOW;
default-state = "on";
};
};
嗯,这里要注意:设备树文件(.dts)是文本文件,编译成二进制(.dtb)后由bootloader传递给内核。内核启动时解析这个二进制文件,动态构建硬件信息。
设备树在Linux内核中的角色
设备树到底扮演什么角色?我用一张图来说明:
从这张图你能看到,设备树处于硬件和内核之间的桥梁位置。它把硬件信息抽象成树形结构,内核启动时读取这个结构,然后动态创建设备。
设备树解决了哪些问题?
我在项目中遇到过这样一个场景:客户要求同一款芯片支持两种不同的LCD屏。以前的做法是编译两个内核镜像,一个支持屏A,一个支持屏B。有了设备树,只需要在设备树里改几行参数,同一个内核镜像就能适配两种屏。
避坑指南:我曾经因为设备树里一个逗号写成了分号,导致整个系统启动不起来。调试了整整一天才发现。设备树的语法检查很严格,一个标点符号都不能错。
设备树带来的好处,总结起来有几点:
- 硬件描述与驱动分离:改硬件参数不用改驱动代码
- 内核镜像通用化:同一个zImage可以在不同板子上运行
- 减少内核碎片:不再需要为每个板子维护单独的代码
- 启动参数灵活:bootloader可以动态修改设备树
- 社区统一标准:所有架构都采用相同的描述方式
说白了,设备树让嵌入式Linux开发从"手工作坊"进入了"标准化生产"时代。你想想看,以前每个板子都是独一无二的,现在大家用同一套语言描述硬件,这效率提升不是一星半点。
核心要点:设备树不是万能的,但它解决了嵌入式Linux开发中最头疼的问题——硬件描述与驱动代码的耦合。理解这一点,你就抓住了设备树的精髓。
好了,这一章我们聊了设备树的起源和它在内核中的角色。下一章我会带你深入设备树的语法细节,包括节点、属性、标签这些核心概念。到时候我会分享一些实际项目中的踩坑经验,保证让你少走弯路。
可可精品资料库