4. I2C核心数据结构:struct i2c_adapter、struct i2c_algorithm、struct i2c_client、struct i2c_driver
在Linux I2C子系统驱动框架中,四个核心数据结构构成了整个I2C通信的骨架:适配器(Adapter)、算法(Algorithm)、客户端(Client)和驱动(Driver)。理解这四个结构体之间的关系与职责,是进行I2C驱动移植与设备树绑定的基础。
核心要点:i2c_adapter 代表硬件控制器,i2c_algorithm 封装通信协议,i2c_client 代表挂载在总线上的从设备,i2c_driver 负责驱动从设备。四者通过内核I2C核心层协同工作。
4.1 整体架构关系
下图展示了四个核心数据结构在I2C子系统中的层级关系与数据流向:
4.2 struct i2c_adapter — 适配器结构体
struct i2c_adapter 是对I2C总线控制器硬件的软件抽象。每个I2C控制器对应一个adapter实例,负责管理总线上的所有I2C事务。
4.2.1 关键成员
struct i2c_adapter {
struct module *owner; /* 所属模块 */
unsigned int class; /* 适配器支持的设备类 */
const struct i2c_algorithm *algo; /* 算法指针,核心成员 */
struct device dev; /* 内核设备模型 */
int nr; /* 适配器编号,如 i2c-0 */
char name[48]; /* 适配器名称 */
struct list_head userspace_clients; /* 用户空间客户端列表 */
struct mutex bus_lock; /* 总线访问互斥锁 */
int timeout; /* 传输超时时间(ms) */
int retries; /* 传输重试次数 */
void *algo_data; /* 算法私有数据 */
};
4.2.2 适配器注册流程
| 步骤 | 操作 | 关键函数/宏 |
|---|---|---|
| 1 | 分配并初始化 i2c_adapter 结构体 | i2c_alloc_adapter() 或静态定义 |
| 2 | 绑定 i2c_algorithm 到 algo 指针 | adap->algo = &my_algo; |
| 3 | 设置适配器编号(可选) | adap->nr = bus_number; |
| 4 | 注册到I2C核心层 | i2c_add_adapter() 或 i2c_add_numbered_adapter() |
| 5 | 注销时释放 | i2c_del_adapter() |
实践提示:在设备树匹配的驱动中,adapter通常由I2C控制器驱动(如i2c-imx.c、i2c-omap.c)在probe阶段创建并注册。驱动开发者一般不需要直接操作adapter,而是通过client与从设备通信。
4.3 struct i2c_algorithm — 算法结构体
struct i2c_algorithm 定义了适配器与从设备之间进行数据收发的底层通信方法。它是I2C控制器驱动必须实现的核心接口。
4.3.1 关键成员
struct i2c_algorithm {
/* 标准I2C传输:发送起始条件、地址、数据、停止条件 */
int (*master_xfer)(struct i2c_adapter *adap,
struct i2c_msg *msgs,
int num);
/* SMBus兼容传输(可选) */
int (*smbus_xfer)(struct i2c_adapter *adap,
u16 addr,
unsigned short flags,
char read_write,
u8 command,
int size,
union i2c_smbus_data *data);
/* 功能支持查询 */
u32 (*functionality)(struct i2c_adapter *adap);
};
4.3.2 三种方法对比
| 方法 | 必须实现 | 用途 | 典型实现复杂度 |
|---|---|---|---|
| master_xfer | 是 | 标准I2C消息传输,支持组合读写 | 高(需处理时序、中断、DMA) |
| smbus_xfer | 否 | SMBus协议兼容传输 | 中(可基于master_xfer封装) |
| functionality | 是 | 返回适配器支持的功能掩码 | 低(返回预定义常量) |
注意:master_xfer 是算法结构体中唯一必须实现的函数。如果控制器硬件不支持SMBus协议,smbus_xfer可以设置为NULL,内核会自动使用master_xfer模拟SMBus操作,但效率较低。
4.4 struct i2c_client — 客户端结构体
struct i2c_client 代表挂载在I2C总线上的一个从设备。每个从设备在系统中对应一个client实例,由I2C核心层在设备枚举或设备树解析时创建。
4.4.1 关键成员
struct i2c_client {
unsigned short addr; /* 7位或10位从设备地址 */
char name[I2C_NAME_SIZE]; /* 设备名称,用于驱动匹配 */
struct i2c_adapter *adapter; /* 所属适配器指针 */
struct device dev; /* 内核设备模型 */
int irq; /* 中断号(如支持) */
struct list_head detected; /* 检测链表 */
int flags; /* 标志位(如地址长度) */
};
4.4.2 设备树到client的映射
当设备树中包含I2C从设备节点时,内核在解析过程中自动创建i2c_client实例:
// 设备树示例
&i2c1 {
status = "okay";
clock-frequency = <100000>;
temperature_sensor: tmp102@48 {
compatible = "ti,tmp102";
reg = <0x48>; // 7位从设备地址
interrupt-parent = <&gpio1>;
interrupts = <15 IRQ_TYPE_EDGE_FALLING>;
};
};
// 内核解析后自动创建:
// struct i2c_client *client;
// client->addr = 0x48;
// client->adapter = i2c1_adapter;
// client->irq = gpio_to_irq(15);
4.5 struct i2c_driver — 驱动结构体
struct i2c_driver 是I2C从设备驱动的核心结构体,负责将驱动与client绑定,并提供设备操作接口。
4.5.1 关键成员
struct i2c_driver {
unsigned int class; /* 驱动支持的设备类 */
int (*probe)(struct i2c_client *client,
const struct i2c_device_id *id); /* 设备探测 */
int (*remove)(struct i2c_client *client); /* 设备移除 */
void (*shutdown)(struct i2c_client *client); /* 系统关机 */
int (*suspend)(struct i2c_client *client,
pm_message_t mesg); /* 电源管理-挂起 */
int (*resume)(struct i2c_client *client); /* 电源管理-恢复 */
struct device_driver driver; /* 内核驱动模型 */
const struct i2c_device_id *id_table; /* 设备ID匹配表 */
};
4.5.2 驱动注册与匹配流程
- 定义驱动结构体:填充probe、remove、id_table等成员
- 注册驱动:调用
i2c_add_driver()宏 - 匹配过程:内核将client的name与id_table中的name字段比对,或通过设备树compatible属性匹配
- probe调用:匹配成功后,内核调用驱动的probe函数,传入匹配的client指针
- 设备操作:在probe中通过client调用i2c_transfer()与硬件通信
// 典型I2C驱动模板
static const struct i2c_device_id tmp102_id[] = {
{ "tmp102", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, tmp102_id);
static const struct of_device_id tmp102_of_match[] = {
{ .compatible = "ti,tmp102" },
{ }
};
MODULE_DEVICE_TABLE(of, tmp102_of_match);
static struct i2c_driver tmp102_driver = {
.driver = {
.name = "tmp102",
.of_match_table = tmp102_of_match,
},
.probe = tmp102_probe,
.remove = tmp102_remove,
.id_table = tmp102_id,
};
module_i2c_driver(tmp102_driver);
4.6 四个结构体的协作流程
下图展示了一次典型的I2C读写操作中,四个结构体之间的协作关系:
4.7 数据结构对比总结
| 结构体 | 代表对象 | 核心职责 | 由谁实现 | 生命周期 |
|---|---|---|---|---|
| i2c_adapter | I2C控制器 | 管理总线、持有算法指针 | 控制器驱动 | 与硬件绑定,常驻 |
| i2c_algorithm | 通信协议 | 实现底层收发方法 | 控制器驱动 | 与adapter共存 |
| i2c_client | 从设备 | 描述设备地址、所属总线 | 内核核心层 | 设备枚举时创建,移除时销毁 |
| i2c_driver | 设备驱动 | 提供probe/remove、匹配设备 | 设备驱动开发者 | 模块加载到卸载 |
工程实践要点:
- 编写I2C从设备驱动时,只需关注 i2c_driver 和 i2c_client,通过client调用i2c_transfer()即可通信
- 移植I2C控制器驱动时,必须实现 i2c_algorithm 中的master_xfer,并正确初始化 i2c_adapter
- 设备树中的compatible属性与驱动中的of_match_table形成绑定关系,是驱动匹配的现代标准方式
- 使用
module_i2c_driver()宏可以简化驱动注册代码