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/... 观察命令,到时会有完整的模型解释。