4. 设备模型核心
Linux设备模型并不是一组孤立的链表,而是由bus_type、device、device_driver三个核心结构体构成的“三角关系”。理解这三者的字段含义、生命周期以及它们之间的匹配与绑定过程,是掌握设备注册与依赖管理的关键。本节从结构体出发,逐步展开到match机制与probe流程。
一、三个核心结构体的职责划分
在include/linux/device.h中,struct bus_type描述一条总线(如platform、PCI、I2C、USB)的共性与操作集;struct device描述一个具体的设备实例;struct device_driver描述一个可驱动某类设备的驱动。三者通过kobject嵌入到sysfs中,形成用户空间可见的层次结构。
| 结构体 | 关键字段 | 作用 |
|---|---|---|
| bus_type | name、match、probe、remove、uevent、devices_kset、drivers_kset | 定义总线匹配规则与设备/驱动注册入口 |
| device | kobj、parent、bus、driver、platform_data、of_node、release | 表示一个设备实例,挂接到总线与父设备 |
| device_driver | name、bus、owner、probe、remove、of_match_table、id_table | 表示驱动能力,声明可支持的设备范围 |
其中bus_type的match回调是匹配的核心,probe回调是绑定的核心。设备与驱动都注册到总线的两个kset中,总线负责在设备或驱动加入时触发匹配尝试。
二、匹配机制:match如何判断“门当户对”
当设备或驱动任一方向注册时,总线会调用bus_type->match(dev, drv)。不同总线的match实现不同,但常见策略包括:
- 名称匹配:比较
dev->init_name或dev_name(dev)与drv->name是否一致,platform总线早期常用。 - ID表匹配:驱动提供
id_table,总线遍历表项与设备ID比较,如I2C、SPI。 - 设备树匹配:通过
of_match_table比较compatible字符串,现代ARM平台主流方式。 - ACPI匹配:通过
acpi_match_table比较_HID/_CID。
以platform总线为例,其match顺序大致为:先尝试of_driver_match_device,再尝试acpi_driver_match_device,最后回退到platform_match_id与名称比较。只要任一策略命中,即认为匹配成功。
static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);
if (of_driver_match_device(dev, drv))
return 1;
if (acpi_driver_match_device(dev, drv))
return 1;
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
return strcmp(pdev->name, drv->name) == 0;
}
三、probe流程:从匹配成功到驱动接管设备
匹配成功后,总线不会直接调用驱动的probe,而是通过device_driver_attach或really_probe完成绑定。核心步骤可概括为:
- 将
dev->driver指向匹配到的驱动,建立双向引用。 - 调用
bus_type->probe(若存在),再调用drv->probe。 - 驱动probe中完成资源申请、寄存器映射、中断注册、子系统注册等。
- 若probe返回0,设备进入bound状态,sysfs中生成
driver符号链接。 - 若probe失败,解绑并释放已申请资源,设备回到未绑定状态。
四、典型场景:platform设备与驱动的注册与绑定
以一颗I2C温度传感器为例,设备树中定义compatible为“vendor,tmp102”,内核启动时由of_platform_populate创建platform_device并注册到platform总线。驱动侧定义of_match_table匹配同一compatible,模块加载时注册platform_driver。总线match命中后调用驱动probe,probe中通过i2c或regmap读取寄存器并注册hwmon设备。
static const struct of_device_id tmp102_of_match[] = {
{ .compatible = "vendor,tmp102" },
{ }
};
MODULE_DEVICE_TABLE(of, tmp102_of_match);
static struct platform_driver tmp102_driver = {
.probe = tmp102_probe,
.remove = tmp102_remove,
.driver = {
.name = "tmp102",
.of_match_table = tmp102_of_match,
},
};
module_platform_driver(tmp102_driver);
当设备与驱动都注册后,可通过sysfs观察绑定结果:
ls /sys/bus/platform/devices/
ls /sys/bus/platform/drivers/
ls -l /sys/bus/platform/devices/*/driver
五、误区与复盘
复盘时建议重点检查:设备树compatible与驱动of_match_table是否一致;驱动是否已加载;总线match是否被正确调用;probe返回值与dmesg日志;sysfs中driver符号链接是否存在。掌握这三个结构体与match/probe链路,就掌握了Linux设备模型的主干。