2. 设备模型基础:Linux 设备模型核心概念及其注册流程
Linux 设备模型是内核管理硬件设备、驱动和总线拓扑的抽象框架。它为 ACPI、设备树等固件接口提供了统一的设备发现、电源管理和用户空间接口基础。理解 device、driver、bus、class 四个核心对象及其注册流程,是掌握设备匹配机制的前提。
核心要点:设备模型将物理或虚拟硬件抽象为内核对象,通过总线进行匹配与绑定,通过 class 向用户空间提供统一的设备访问接口。
2.1 四大核心对象
| 对象 | 内核结构体 | 职责 | 关键字段 |
|---|---|---|---|
| device | struct device |
代表一个硬件设备或虚拟设备,包含资源、电源状态、拓扑关系 | init_name, bus, driver, of_node, fwnode |
| driver | struct device_driver |
封装驱动逻辑,提供 probe/remove/shutdown 回调 | name, bus, of_match_table, acpi_match_table |
| bus | struct bus_type |
管理设备和驱动的匹配、枚举、热插拔 | name, match, probe, uevent |
| class | struct class |
按功能分类设备,提供 dev_attrs 和 uevent 接口 | name, dev_uevent, devnode |
调试提示:在
/sys/bus/、/sys/devices/、/sys/class/ 下可以实时查看这些对象的拓扑关系。例如 ls /sys/bus/platform/devices/ 列出所有 platform 设备。
2.2 对象关系与注册流程
设备模型的注册遵循“先总线、后设备与驱动”的顺序。核心流程如下:
- 总线注册:调用
bus_register(),创建/sys/bus/<name>目录,初始化 klist 链表。 - 设备注册:调用
device_register(),将设备挂入总线设备链表,触发总线 match 回调。 - 驱动注册:调用
driver_register(),将驱动挂入总线驱动链表,遍历总线设备链表进行匹配。 - 匹配与绑定:总线 match 回调返回真时,调用
driver_probe_device(),执行驱动的 probe 函数。 - class 注册:设备注册时可指定
class,在/sys/class/下创建符号链接。
常见误区:设备注册时如果总线尚未注册,设备会进入 deferred 列表,等待总线出现后重新尝试匹配。调试时可通过
/sys/kernel/debug/devices_deferred 查看延迟设备。
2.3 注册流程 SVG 架构图
2.4 关键数据结构与回调
总线 match 回调是设备与驱动绑定的核心决策点。以下为典型实现:
/* drivers/base/platform.c */
static int platform_match(struct device *dev, struct device_driver *drv)
{
/* 1. 尝试 ACPI 匹配 */
if (acpi_driver_match_device(dev, drv))
return 1;
/* 2. 尝试设备树 OF 匹配 */
if (of_driver_match_device(dev, drv))
return 1;
/* 3. 尝试 ID table 匹配 */
if (platform_match_id(drv->id_table, dev) != NULL)
return 1;
/* 4. 尝试名称直接匹配 */
return strcmp(dev_name(dev), drv->name) == 0;
}
匹配优先级:ACPI → 设备树 → ID table → 名称。调试时可通过
echo 'match +p' > /sys/kernel/debug/dynamic_debug/control 开启总线匹配日志。
2.5 设备、驱动、总线注册核心 API
| API | 功能 | 注意事项 |
|---|---|---|
bus_register() |
注册总线类型,创建 sysfs 目录 | 必须在设备/驱动注册前完成 |
device_register() |
注册设备,加入总线设备链表 | 需提前设置 bus 和 parent |
driver_register() |
注册驱动,触发匹配与 probe | 驱动可携带 of_match_table 或 acpi_match_table |
class_create() |
创建设备类,提供 uevent 接口 | 通常与 device_create() 配合使用 |
2.6 注册流程中的关键链表与 sysfs 路径
内核使用 klist 管理总线上设备和驱动的链表。注册后 sysfs 呈现如下结构:
/sys/bus/platform/
├── devices/ # 所有 platform 设备符号链接
│ ├── f0000000.uart -> ../../../devices/platform/f0000000.uart
│ └── ...
├── drivers/ # 所有 platform 驱动符号链接
│ ├── serial8250
│ └── ...
└── uevent # 总线 uevent 属性
/sys/devices/platform/ # 设备实际目录,包含资源、电源、driver 链接
/sys/class/tty/ # class 分类目录,包含设备节点和属性
调试技巧:使用
udevadm info -a -p /sys/devices/platform/xxx 查看设备完整属性,包括 ACPI 句柄和设备树 compatible 字符串。
2.7 设备模型与固件接口的关联
设备模型通过 struct device 中的 fwnode 和 of_node 字段与 ACPI/设备树固件接口关联:
- ACPI 设备:
fwnode指向struct acpi_device,通过acpi_match_device()匹配 HID/CID。 - 设备树设备:
of_node指向struct device_node,通过of_match_node()匹配 compatible 字符串。 - 统一设备属性:
device_property_read_*()系列 API 屏蔽底层差异,优先读取 ACPI 或设备树属性。