文章目录
-
- [1. 背景](#1. 背景)
- [2. 核心函数说明](#2. 核心函数说明)
- [3. 执行流程详解](#3. 执行流程详解)
- [4. 设备何时存在?两种典型场景](#4. 设备何时存在?两种典型场景)
-
- [场景 A:设备先于驱动注册(常见于 Device Tree)](#场景 A:设备先于驱动注册(常见于 Device Tree))
- [场景 B:驱动先于设备注册](#场景 B:驱动先于设备注册)
- [5. 数据结构关系简图](#5. 数据结构关系简图)
- [6. 总结:`i2c_add_driver()` 后的关键流程](#6. 总结:
i2c_add_driver()后的关键流程)
1. 背景
嵌入式开发中,存在很多的外设,如 i2c 设备,对于最常用的嵌入式OS --Linux,内核采用的驱动-总线-设备模型。最近刚好遇到一个i2c外设,设备树提前配置对应i2c的子设备信息,在设备启动后自动加载driver,自动触发匹配机制后,执行对应driver的probe流程。此处进行简单记录。
2. 核心函数说明
i2c_add_driver(struct i2c_driver *driver)
其本质是一个宏,实际调用:
c
i2c_register_driver(THIS_MODULE, driver);
i2c_register_driver(struct module *owner, struct i2c_driver *driver)
是真正的注册函数(定义在 drivers/i2c/i2c-core-base.c)。
3. 执行流程详解
- 步骤 1:注册 I2C 驱动到 I2C 总线
c
driver->driver.owner = owner;
driver->driver.bus = &i2c_bus_type;
- 将
i2c_driver的内嵌device_driver结构挂载到I2C总线(i2c_bus_type)上。 - 设置模块所有者(用于引用计数)。
- 步骤 2:调用 driver_register()
这是 Linux 设备驱动模型的核心函数:
c
ret = driver_register(&driver->driver);
该函数会:
- 将驱动添加到总线的驱动链表中;
- 遍历总线上所有已注册的设备,尝试与当前驱动匹配。
🔍 注意:I2C 设备可能在驱动注册之前或之后被添加。无论哪种情况,只要两者都存在,就会触发匹配。
- 步骤 3:总线匹配(match)
I2C 总线的.match回调是i2c_device_match()(在i2c-core-base.c中):
它按优先级依次尝试匹配:
1.) ACPI 匹配(如果启用)
比较设备的 _HID 或 _CID 与驱动的 acpi_match_table。
2.) OF(Device Tree)匹配
比较设备的 compatible 字符串与驱动的 of_match_table。
这是嵌入式系统中最常见的匹配方式。
3.)I2C 地址 + 名称匹配
如果设备是通过 i2c_board_info 静态注册的(旧式方法),则比较:
i2c_client.name 与 i2c_driver.id_table[i].name
同时检查 I2C 地址是否在 id_table 允许范围内(或通配)
✅ 只要满足任一匹配条件,就认为"驱动支持该设备"。
-
步骤 4:调用
probe()函数
匹配成功后,总线层调用驱动的.probe回调:ret = driver_probe_device(&driver->driver, &client->dev);
这最终会执行:
status = drv->probe(client, i2c_match_id(drv->id_table, client));
其中:
client:匹配成功的struct i2c_client(代表一个 I2C 从设备)- 第二个参数是匹配到的
i2c_device_id(来自id_table)
📌 关键点 :
probe()是驱动初始化硬件、申请资源、注册子系统(如 input、hwmon、regulator 等)的地方。
4. 设备何时存在?两种典型场景
场景 A:设备先于驱动注册(常见于 Device Tree)
- 内核启动时解析 DT,为每个 I2C 节点创建
i2c_client并注册到 I2C 总线。 - 后续模块加载(或 built-in 驱动初始化)调用
i2c_add_driver()。 - 驱动注册时,立即匹配已存在的
client,调用probe()。
场景 B:驱动先于设备注册
- 驱动先注册,但总线上尚无匹配设备 →
probe()暂不执行。 - 后续通过以下方式添加设备:
- 用户空间:
echo <name> <addr> > /sys/bus/i2c/devices/i2c-<bus>/new_device - 内核空间:
i2c_new_client_device(adapter, info)
- 用户空间:
- 设备注册时,I2C 核心会反向匹配已注册的驱动 ,若匹配成功,立即调用
probe()。
✅ 无论先后顺序,只要驱动和设备都存在且匹配,
probe()就会被调用一次。
5. 数据结构关系简图
i2c_bus_type
│
├── drivers: [your_i2c_driver] ──┐
│ │
└── devices: [i2c_client_1] ←───┘ (匹配成功 → probe)
[i2c_client_2]
i2c_driver:描述驱动能力(支持哪些设备)i2c_client:描述一个具体的 I2C 从设备(地址、名称、所属 adapter)
6. 总结:i2c_add_driver() 后的关键流程
| 步骤 | 动作 |
|---|---|
| 1 | 将 i2c_driver 注册到 i2c_bus_type |
| 2 | 遍历总线上所有 i2c_client 设备 |
| 3 | 通过 ACPI / OF / id_table 进行匹配 |
| 4 | 对每个匹配成功的设备,调用 driver->probe(client, id) |
| 5 | probe() 成功 → 设备驱动绑定完成;失败 → 绑定终止 |
⚠️ 注意:
probe()必须可重入、线程安全,且不能睡眠过久(在某些上下文中)。
理解这一流程,对调试 I2C 驱动不加载、设备未识别等问题至关重要。