[第2课时 · 6/8] Kernel 如何使用设备树

0. 这一节要解决什么

DTB 是一份静态的硬件描述,而系统里活跃的是设备实例与驱动。本节回答:内核启动时怎样把 DTB 的每个节点变成可操作的设备——以及反过来,一个设备不工作时,怎样按这条链路定位卡在哪一步。

这一节也是第 3 课时「Linux 设备模型」的直接铺垫。

1. 从 DTB 到 probe 的五步

U-Boot 传入 DTB → Kernel 解析节点 → 按父总线建设备实例
                → compatible 匹配驱动 → probe 初始化设备
步骤 发生什么 在哪观察
1. 传入 U-Boot 把 DTB 放进内存,地址随交接参数传给内核 U-Boot 侧 booti 命令(第 1 课时)
2. 解析 内核把 DTB 展开为内存中的 device_node 树 /proc/device-tree/(与 DTB 同构)
3. 建实例 每个带 compatible 的节点,按其父总线创建对应设备:platform 总线 / i2c_client / spi_device /sys/bus/platform/devices/、/sys/bus/i2c/devices/
4. 匹配 设备的 compatible 与驱动的 of_match_table 逐条比对 /sys/bus/*/drivers/*/
5. probe 驱动的 probe 函数执行:申请资源、初始化硬件、注册到子系统 dmesg 里的 probe 日志

核心观念:设备树只提供事实,不实现行为。 节点只是「报名」,驱动才是「干活的人」。

2. 设备能工作的三个条件

条件 内容 不满足时
设备树条件 节点存在、status 允许启用、reg / 中断 / phandle 引用描述正确 设备实例根本不会被创建
驱动条件 驱动已配置并构建(Y 或 M 且已加载)、compatible 命中匹配表 实例存在但无驱动认领,dmesg 无 probe 日志
运行条件 时钟、电源、复位、引脚复用等资源就绪 probe 报错(-EPROBE_DEFER、timeout 等)

compatible 和 &标签 只是匹配与引用的线索:它们不会代替驱动实现,也不保证 probe 成功。

3. 走一遍完整例子

以一个触摸屏为例:

&i2c3 {
        touch@14 {
                compatible = "goodix,gt911";      /* ① 身份 */
                reg = <0x14>;                     /* ② 总线地址 */
                interrupt-parent = <&gpio1>;
                interrupts = <20 IRQ_TYPE_EDGE_FALLING>;
                status = "okay";                  /* ③ 开关 */
        };
};

驱动侧(drivers/input/touchscreen/goodix.c):

static const struct of_device_id goodix_of_match[] = {
        { .compatible = "goodix,gt911" },
        { }
};
MODULE_DEVICE_TABLE(of, goodix_of_match);

对上号的过程:i2c3 总线为 touch@14 建 i2c_client → 用 compatible = "goodix,gt911" 在 i2c 驱动表里找到 goodix.c → 执行 goodix_probe() → 注册 input 设备。验证:

ls /sys/bus/i2c/devices/          # 出现 3-0014(总线 3,地址 0x14)
dmesg | grep -i gt911             # probe 成功日志

4. 运行态观察命令

ls /proc/device-tree/                       # 与 DTB 同构的目录树
cat /proc/device-tree/model                 # 当前板级模型
tr -d '\0' < /proc/device-tree/compatible   # compatible 是带 NUL 的串,要去掉
ls /sys/bus/platform/devices/               # 平台总线设备实例
ls /sys/bus/i2c/devices/                    # i2c 设备实例
dmesg | grep -i "probe\|failed"             # probe 成败

注意:设备树属性常带 NUL 结尾、或本身就是二进制 cell,直接 cat 会显示异常;用 tr -d '\0'、xxd、strings 或 fdtget 辅助查看。

5. probe 失败速查

dmesg 现象 含义 检查方向
完全没有该设备任何日志 实例未创建或驱动缺失 节点 status / 是否编进内核
probe deferred(-EPROBE_DEFER) 依赖的资源还没就绪,内核稍后重试 等待正常;反复 defer 则查依赖(时钟/电源/总线)
probe failed with error -16 等 资源冲突或硬件无响应 地址冲突?接线?供电?
有 probe 日志但设备不工作 初始化成功,后续通信问题 驱动参数、上层应用(第 3 课时展开)

6. Lab:把三个目录对上号

在板子上:

cat /proc/device-tree/model
ls /sys/bus/i2c/devices/
dmesg | grep -i probe

对照记录:一个设备在 DTB(/proc/device-tree)里有节点、在 /sys/bus 里有实例、在 dmesg 里有 probe 日志——三个证据齐了,才构成「设备已被内核接管」的完整链路。缺哪一个,就是排障的入口。

7. 本节小结

  • 五步链:传入 → 解析 → 建实例 → compatible 匹配 → probe
  • 三个条件:设备树条件、驱动条件、运行条件,缺一不可
  • 三个不等式:有节点 ≠ 有驱动;有驱动 ≠ 设备启用;status = okay ≠ probe 成功
  • 观察三件套:/proc/device-tree、/sys/bus/*/devices、dmesg

8. 本节思考

Q1:ls /sys/bus/i2c/devices/ 里没有某个设备,但 /proc/device-tree 里节点存在且 status 为 okay。下一步查什么?

查看答案

节点存在说明设备树条件满足,问题在驱动或运行条件:① 驱动是否编进内核或已加载(.config / lsmod);② 驱动的 of_match_table 是否包含这个 compatible(用 git grep 反查);③ dmesg | grep -i 该设备名看有无 deferred / failed 日志。三者分别对应驱动条件与运行条件——节点存在只是第一步。

Q2:为什么说 -EPROBE_DEFER 通常不是错误?

查看答案

它表示驱动需要的资源(时钟、电源、regulator、另一个总线)此刻还没就绪,内核把 probe 排队稍后重试,等依赖 probe 成功后再回来。这依赖「probe 顺序」机制,是设计行为。只有当依赖本身永远无法就绪(如依赖的设备节点被 disabled)时,defer 才会永远持续——那才是问题。

Q3:本节内容与第 3 课时的「设备模型」是什么关系?

查看答案

本节讲的是单条链路:一个节点如何变成设备并找到驱动。第 3 课时把这条链路系统化:统一抽象出 Device(设备)、Driver(驱动)、Bus(总线,组织前两者并负责匹配)与 Class(同类设备的集合),以及 sysfs、/dev 与 uevent 如何把内核里的状态暴露给用户态。本节的 /sys/bus/... 观察命令,到时会有完整的模型解释。