一、AOSP设备树概述:什么是设备树、设备树在Android系统中的角色、设备树与内核的关系
1.1 设备树到底是什么?
设备树,英文叫 Device Tree,简称 DT。说白了,它就是一个描述硬件信息的配置文件。
我刚开始接触 Android 底层时,总觉得这玩意儿很玄乎。后来做了一次平台移植,才真正搞明白——设备树就是一张「硬件清单」。它告诉内核:你的板子上有哪些外设、它们挂在哪条总线上、地址是多少、中断号是什么。
举个例子,你写一个 LED 驱动,传统做法是在驱动代码里硬编码 GPIO 号。换一块板子,GPIO 变了,你得改代码重新编译。有了设备树,你只需要改 dts 文件里的描述,驱动代码完全不用动。
核心要点:设备树实现了「驱动代码」与「硬件配置」的解耦。这是 Android 系统能够适配上千种不同硬件的关键设计。
1.2 设备树在 Android 系统中的角色
在 Android 系统里,设备树扮演着「硬件抽象层」的角色。它位于 Bootloader 和 Kernel 之间,是系统启动时第一个被加载的硬件描述。
我参与过一个项目,客户要求在一款老平台上跑 Android 14。硬件没变,但内核版本从 4.9 升到了 5.10。你猜怎么着?设备树文件几乎没动,只改了几个 compatible 字符串。这就是设备树的威力。
具体来说,设备树在 Android 系统中有三个关键作用:
- 启动阶段硬件枚举:Bootloader 将设备树二进制文件(dtb)传递给内核,内核解析后就知道有哪些硬件可用。
- 驱动匹配与参数传递:驱动通过 compatible 属性匹配设备节点,并从节点中读取寄存器地址、中断号、时钟频率等参数。
- 硬件差异化配置:同一套内核代码,通过不同的设备树文件,可以适配不同型号的手机、平板、车机。
个人经验:我习惯在设备树里把 GPIO 的命名做得规范一些。比如用 gpio-key-power 而不是 gpio-123。这样调试时一眼就能看出哪个引脚是干什么的,省了不少排查时间。
1.3 设备树与内核的关系
这个问题,我经常被刚入行的同事问到。设备树和内核到底是什么关系?
简单来说:设备树是内核的「硬件说明书」。内核本身不关心你用的是哪款 SoC、哪个传感器,它只负责执行逻辑。设备树告诉它:「嘿,这里有个 I2C 控制器,地址是 0x1c00000,你把它初始化一下。」
从代码层面看,关系是这样的:
// 内核驱动中匹配设备树节点
static const struct of_device_id my_i2c_driver_of_match[] = {
{ .compatible = "vendor,my-sensor" },
{ /* sentinel */ }
};
static struct platform_driver my_i2c_driver = {
.probe = my_i2c_probe,
.driver = {
.name = "my_sensor",
.of_match_table = my_i2c_driver_of_match,
},
};
// 设备树中的描述
i2c@1c00000 {
my_sensor@48 {
compatible = "vendor,my-sensor";
reg = <0x48>;
interrupt-parent = <&gpio1>;
interrupts = <5 IRQ_TYPE_EDGE_RISING>;
};
};
内核启动时,会遍历设备树中的所有节点。每个节点都有一个 compatible 属性,内核拿着这个字符串去驱动列表里找匹配项。找到了,就调用驱动的 probe 函数。
为什么会这样设计?你想想看,如果没有设备树,每换一个硬件就要改内核代码、重新编译。有了设备树,内核可以做成通用的,硬件差异全交给 dts 文件去描述。
注意:设备树不是万能的。它只描述「静态硬件信息」,比如寄存器地址、中断号、时钟频率。动态信息(比如传感器当前读数、电池电量)不归设备树管,那是驱动运行时的事情。
1.4 设备树的文件结构
在 AOSP 源码里,设备树文件通常放在 kernel/ 或 device/ 目录下。常见的文件类型有:
| 文件后缀 | 说明 | 用途 |
|---|---|---|
| .dts | 设备树源文件 | 人类可读的文本描述,类似 C 语言源码 |
| .dtsi | 设备树包含文件 | 公共部分的描述,类似 C 语言的 .h 头文件 |
| .dtb | 设备树二进制文件 | 编译后的二进制,内核直接解析 |
| .dtbo | 设备树叠加层 | 用于动态修改设备树,Android 9 之后引入 |
我记得第一次编译设备树时,直接用 make dtbs 命令,结果报了一堆语法错误。后来才发现,dts 文件的语法非常严格,少一个分号、多一个括号都不行。
避坑指南:我曾经在 dts 文件里漏写了一个 #address-cells 属性,结果内核解析 reg 属性时全乱了,外设一个都没认出来。排查了整整两天。所以,写设备树时一定要检查这些基础属性是否完整。
1.5 设备树在 Android 启动流程中的位置
Android 系统的启动流程,大致是:Bootloader → Kernel → Init → Zygote → System Server。设备树在 Bootloader 阶段就参与进来了。
具体流程是这样的:
- Bootloader 从存储分区(通常是 boot 分区或 dtb 分区)加载 dtb 文件。
- Bootloader 将 dtb 文件在内存中的地址传递给内核(通过 r2 寄存器或设备树 blob 地址)。
- 内核启动后,第一时间解析 dtb,建立设备树结构。
- 内核根据设备树信息,初始化各个子系统(中断控制器、时钟、GPIO、I2C 等)。
- 驱动 probe 时,从设备树中读取硬件参数。
这里有个细节:Android 从 9.0 开始引入了「设备树叠加层」(DTBO)。它的作用是让 Bootloader 可以在不修改原始 dtb 的情况下,动态添加或修改设备树节点。比如,同一款 SoC 用在两种不同屏幕的手机上,可以通过 DTBO 来切换显示相关的配置。
关键点:设备树是 Android 系统硬件适配的基石。没有它,内核就无法知道它运行在什么硬件上。你可以把设备树理解为「硬件与软件之间的翻译官」。
1.6 小结
这一章我们聊了设备树的基本概念、在 Android 系统中的作用,以及它和内核的关系。说白了,设备树就是一张硬件配置清单,让内核能够灵活适配不同的硬件平台。
下一章,我会带你手把手写一个完整的设备树文件,从零开始描述一个简单的开发板。到时候你会看到,那些看似复杂的 dts 文件,其实都是有规律可循的。
嗯,先消化一下今天的内容。有什么问题,欢迎随时交流。