4. Linux设备驱动模型

在板级联调中,很多“驱动加载了但设备没起来”“probe 不执行”“/dev 下没有节点”的问题,根源并不在驱动代码本身,而在于没有理清 Linux 设备驱动模型的对象关系。Linux 2.6 之后引入的统一设备模型,用 kobject、kset 等底层机制,把总线、设备、驱动、类、sysfs、udev、设备树串成了一条可观察、可匹配、可自动化的链路。理解这条链路,是后续调试 I2C 传感器、SPI 屏、GPIO 按键、平台设备的基础。

一、核心对象与职责

设备模型的核心思想是:用统一的树状结构描述系统中所有设备,并让“设备”和“驱动”通过“总线”完成匹配。各对象职责如下:

  • 总线(bus):匹配规则的持有者,维护设备链表和驱动链表。当新设备或新驱动注册时,总线负责调用 match 判断能否配对,配对成功后调用驱动的 probe。
  • 设备(device):描述一个具体的硬件实例,如 I2C 地址 0x68 的 RTC、GPIO 编号 12 的按键。它携带资源信息:地址、中断号、时钟、GPIO、寄存器基址等。
  • 驱动(driver):描述“如何操作一类设备”的代码,包含 probe、remove、suspend、resume 以及 id_table 或 of_match_table。
  • 类(class):从“功能视角”对设备分组,如 input、tty、net、rtc、leds。类决定 udev 在 /dev 下创建什么名字的节点。
  • sysfs:把内核对象以文件树形式导出到 /sys,是用户空间观察和配置设备的窗口。
  • udev:用户空间守护进程,监听内核 uevent,根据规则在 /dev 下动态创建/删除设备节点、设置权限和符号链接。
  • 设备树(Device Tree):ARM 等平台上用 DTS/DTB 描述硬件拓扑与资源,内核解析后生成 platform_device、i2c_client 等设备对象。
一句话串联:设备树描述硬件 → 内核生成 device → 总线 match 到 driver → probe 初始化 → class 注册 → sysfs 暴露 → udev 收到 uevent → /dev 出现节点。
Linux 设备驱动模型对象关系与数据流 设备树 DTS / DTB compatible / reg / interrupts 描述硬件资源与拓扑 device 设备对象 platform_device / i2c_client 携带地址、中断、GPIO bus 总线 match() 匹配规则 device 链表 / driver 链表 driver 驱动 probe / remove of_match_table / id_table class 类 input / tty / net / rtc 决定 /dev 节点命名 sysfs 文件系统 /sys/bus /sys/devices /sys/class /sys/dev udev 用户空间 监听 uevent 按规则创建 /dev 节点 /dev 设备节点 /dev/ttyS0 /dev/input/event0 供应用程序 open/read/write 解析生成 注册 match 成功 注册 class 导出 uevent 创建 用户读写

二、匹配与 probe 的完整流程

以 I2C 温度传感器为例,联调时最关心的是“为什么 probe 没被调用”。完整流程如下:

  1. 设备树中定义节点,compatible 为 "vendor,tmp102",reg 为 <0x48>,并挂在对应 I2C 控制器下。
  2. 内核启动解析 DTB,为每个 I2C 子节点创建 i2c_client,并注册到 i2c 总线。
  3. 驱动模块加载时注册 i2c_driver,其 of_match_table 中同样声明 "vendor,tmp102"。
  4. i2c 总线对设备链表和驱动链表逐一 match,compatible 或 id 一致即配对成功。
  5. 配对后调用驱动的 probe,在 probe 中申请资源、注册 hwmon 或 iio 设备、创建 class 属性。
  6. class 注册触发 uevent,udev 在 /dev 或 /sys/class/hwmon 下生成对应节点与属性文件。
# 观察匹配与 probe 是否发生
dmesg | grep -i tmp102
# 查看设备是否挂到总线
ls /sys/bus/i2c/devices/
# 查看驱动是否注册
ls /sys/bus/i2c/drivers/
# 查看设备树节点是否被解析
ls /proc/device-tree/ | grep i2c
# 查看 class 与 udev 结果
ls /sys/class/hwmon/
ls -l /dev/input/

三、sysfs、udev 与设备树的分工

三者常被混淆,实际分工非常清晰:设备树是“硬件描述源”,sysfs 是“内核对象视图”,udev 是“用户空间节点管理器”。设备树只在启动时被解析一次,sysfs 随对象生命周期动态增删,udev 则根据 sysfs 暴露的 uevent 和属性做规则化处理。

对象所在空间主要作用联调观察点
设备树 DTB内核启动阶段描述硬件资源与连接关系/proc/device-tree、dmesg 解析日志
bus内核维护设备/驱动链表并 match/sys/bus/*/devices、drivers
device内核表示硬件实例及资源/sys/devices/ 下对应节点
driver内核实现 probe/remove 操作lsmod、/sys/bus/*/drivers
class内核按功能分组,决定节点命名/sys/class/ 下目录
sysfs内核→用户导出对象属性为文件/sys 下 cat/echo 读写
udev用户空间动态创建 /dev 节点与权限/dev、udevadm info/monitor

四、板级联调中的典型误区与复盘

误区一:只改驱动,不看设备树。 compatible 拼写不一致、reg 地址写错、节点挂错父总线,都会导致 match 失败,probe 永远不执行。联调第一步应是核对 DTS 与驱动 of_match_table。
误区二:以为 /dev 节点是驱动创建的。 驱动通常只注册 class 和 device_create,真正在 /dev 下生成节点的是 udev。若根文件系统没有 udev 或 mdev,节点不会自动出现,需要手动 mknod 或改用 devtmpfs。
复盘方法: 遇到设备不工作时,按“设备树→总线→驱动→class→sysfs→udev→/dev”顺序逐级排查。先用 dmesg 看 probe 是否被调用,再用 /sys/bus 确认匹配关系,最后用 udevadm monitor 观察 uevent 是否到达用户空间。这条链路一旦打通,绝大多数板级驱动问题都能快速定位。

掌握设备模型后,后续调试 platform 设备、I2C/SPI 从设备、GPIO 子系统时,就能从“猜”转变为“按对象关系定位”,这也是板级联调效率提升的关键一步。