4. I2C 设备驱动注册流程:i2c_add_driver()、i2c_register_driver() 内部实现、probe 回调触发机制
在 Linux 内核中,I2C 设备驱动的注册是驱动与设备匹配的起点。开发者通常调用 i2c_add_driver() 宏来完成注册,但其背后隐藏着 i2c_register_driver() 的核心逻辑以及 probe 回调的触发链条。理解这一流程,对于调试显示驱动中 I2C 通信失败、设备未找到等问题至关重要。
4.1 驱动注册入口:i2c_add_driver()
i2c_add_driver() 是一个封装宏,定义在 include/linux/i2c.h 中。它简化了驱动开发者的调用方式,将 struct i2c_driver 结构体传递给内核。
关键点:
i2c_add_driver() 不直接执行注册,而是调用 i2c_register_driver(),并传递 THIS_MODULE 以确保模块引用计数正确。
/* include/linux/i2c.h */
#define i2c_add_driver(driver) \
i2c_register_driver(THIS_MODULE, driver)
驱动开发者只需定义 struct i2c_driver 并填充 .probe、.remove、.id_table 等成员,然后调用此宏即可。
4.2 核心注册函数:i2c_register_driver() 内部实现
i2c_register_driver() 是 I2C 子系统提供的核心注册函数,定义在 drivers/i2c/i2c-core-base.c 中。其执行流程可分为三个阶段:
| 阶段 | 函数/操作 | 说明 |
|---|---|---|
| 1. 初始化与链表挂载 | driver_register() |
将驱动注册到 Linux 设备驱动核心(driver core),初始化驱动内嵌的 struct device_driver,并将其加入总线驱动链表。 |
| 2. 创建 sysfs 属性 | i2c_create_sysfs_driver_files() |
在 sysfs 中创建驱动相关的属性文件,便于用户空间查看驱动信息。 |
| 3. 扫描已有设备并触发 probe | i2c_for_each_dev() + __process_new_driver() |
遍历 I2C 总线上的所有设备,对每个设备调用 __process_new_driver(),尝试匹配并触发 probe。 |
理解要点: 阶段 3 是“热插拔”场景的关键。如果设备在驱动注册之前已经存在,内核会立即尝试匹配并调用 probe,无需等待设备重新插入。
以下是 i2c_register_driver() 的简化代码逻辑:
/* drivers/i2c/i2c-core-base.c (简化) */
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
int res;
/* 1. 初始化驱动结构体 */
driver->driver.owner = owner;
driver->driver.bus = &i2c_bus_type;
/* 2. 注册到驱动核心 */
res = driver_register(&driver->driver);
if (res)
return res;
/* 3. 创建 sysfs 文件 */
i2c_create_sysfs_driver_files(driver);
/* 4. 扫描现有设备,尝试匹配 */
i2c_for_each_dev(driver, __process_new_driver);
return 0;
}
4.3 probe 回调触发机制
probe 回调的触发并非直接调用,而是通过 I2C 总线的匹配机制间接完成。整个触发链路如下:
- 设备注册或驱动注册:当新设备添加到 I2C 总线,或新驱动注册到 I2C 总线时,触发匹配流程。
- 总线匹配函数:I2C 总线类型
i2c_bus_type的.match方法为i2c_device_match()。 - 匹配策略:
i2c_device_match()首先尝试of_driver_match_device()(设备树匹配),然后尝试acpi_driver_match_device()(ACPI 匹配),最后回退到i2c_match_id()(ID 表匹配)。 - 调用 probe:匹配成功后,驱动核心调用
i2c_device_probe(),该函数最终调用驱动开发者提供的driver->probe()回调。
常见调试陷阱: 如果 probe 未被调用,请检查:1) 设备地址是否与 id_table 或设备树中的 reg 属性一致;2) 设备树中 compatible 属性是否与驱动匹配;3) 驱动是否已正确编译进内核或加载模块。
4.4 匹配与 probe 触发流程图
下图展示了从驱动注册到 probe 回调被触发的完整流程:
4.5 关键数据结构:struct i2c_driver
驱动开发者需要填充的核心结构体如下:
struct i2c_driver {
unsigned int class; /* 驱动支持的设备类 */
int (*probe)(struct i2c_client *client, const struct i2c_device_id *id);
int (*remove)(struct i2c_client *client);
struct device_driver driver; /* 内嵌的通用驱动结构 */
const struct i2c_device_id *id_table; /* 传统 ID 匹配表 */
const struct of_device_id *of_match_table; /* 设备树匹配表 */
};
最佳实践: 在显示驱动中,建议同时提供
of_match_table(设备树)和 id_table(传统方式),以兼容不同平台。probe 回调中应完成 I2C 设备初始化、寄存器配置、以及向 DRM/KMS 框架注册显示输出。
4.6 总结:注册流程关键点
- 入口统一:
i2c_add_driver()是标准入口,内部调用i2c_register_driver()。 - 核心三步骤:驱动核心注册 → sysfs 创建 → 扫描已有设备。
- 匹配优先级:设备树匹配 > ACPI 匹配 > ID 表匹配。
- probe 触发时机:驱动注册时(扫描已有设备)或设备注册时(热插拔)。
- 调试切入点:若 probe 未调用,优先检查
of_match_table或id_table是否与设备信息一致。