4. I2C控制器驱动核心数据结构:struct i2c_adapter、struct i2c_algorithm、struct i2c_msg
在Linux内核的I2C子系统中,驱动开发者通过三个核心数据结构完成控制器适配与消息传输:struct i2c_adapter(适配器)、struct i2c_algorithm(算法)和struct i2c_msg(消息)。这三者构成了从上层客户端到底层硬件寄存器的完整调用链。理解它们的职责、成员与协作关系,是编写可移植I2C控制器驱动的第一步。
核心认知:i2c_adapter 代表硬件控制器实例,i2c_algorithm 封装传输方法,i2c_msg 描述单次数据交换。三者分层解耦,使得同一套算法可适配不同硬件,同一适配器可复用多种传输策略。
4.1 struct i2c_adapter —— 适配器实例
struct i2c_adapter 是I2C控制器的软件抽象,每个注册到内核的I2C总线对应一个适配器实例。驱动开发者需要填充该结构体,并通过 i2c_add_adapter() 或 i2c_add_numbered_adapter() 将其注册到I2C核心。
| 成员 | 类型 | 说明 |
|---|---|---|
| nr | int | 总线编号,-1 表示动态分配 |
| name | char[48] | 适配器名称,用于 sysfs 和调试 |
| algo | const struct i2c_algorithm * | 指向传输算法结构体 |
| algo_data | void * | 算法私有数据,通常指向控制器寄存器基址 |
| lock | struct mutex | 总线访问互斥锁 |
| timeout | int | 传输超时时间(单位 jiffies) |
| retries | int | 传输失败重试次数 |
| dev | struct device | 内核设备模型实例 |
| class | unsigned short | 适配器所属 I2C 类标志 |
| quirks | const struct i2c_adapter_quirks * | 硬件缺陷/限制描述 |
移植要点:在平台驱动 probe 函数中,通常先分配 i2c_adapter,设置 nr(或使用 -1 动态分配),绑定 algo,将控制器寄存器地址存入 algo_data,最后调用 i2c_add_numbered_adapter() 完成注册。
4.2 struct i2c_algorithm —— 传输算法
struct i2c_algorithm 是适配器与硬件之间的桥梁,定义了实际的数据传输方法。驱动开发者必须实现 master_xfer 回调,该回调接收一个 i2c_adapter 指针和一个 i2c_msg 数组,负责启动硬件时序完成传输。
struct i2c_algorithm {
/* 标准 I2C 传输:处理 msg 数组,返回成功传输的 msg 数量 */
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);
};
| 回调 | 必须实现 | 典型实现内容 |
|---|---|---|
| master_xfer | 是 | 遍历 msg 数组,配置控制器寄存器,启动 START/STOP,处理 ACK/NACK |
| smbus_xfer | 否 | 封装 SMBus 协议(读字节/写字/块读写等),可复用 master_xfer |
| functionality | 是 | 返回 I2C_FUNC_I2C | I2C_FUNC_SMBUS_* 等标志位 |
常见错误:master_xfer 返回负值表示硬件错误,返回小于 num 的正数表示部分传输成功。调用者会根据返回值决定是否重试。如果驱动总是返回 0,上层客户端将无法区分“忙”与“失败”。
4.3 struct i2c_msg —— 消息描述符
struct i2c_msg 描述一次完整的I2C数据交换,包含从设备地址、读写方向、数据缓冲区及长度。多个 i2c_msg 可以组成数组,实现复合传输(如先写寄存器地址再读数据)。
struct i2c_msg {
__u16 addr; /* 从设备地址(7位或10位) */
__u16 flags; /* 传输标志:I2C_M_RD, I2C_M_TEN, I2C_M_NOSTART 等 */
__u16 len; /* 数据长度(字节) */
__u8 *buf; /* 数据缓冲区指针 */
};
| 标志 | 值 | 含义 |
|---|---|---|
| I2C_M_RD | 0x0001 | 读操作(未设置则为写) |
| I2C_M_TEN | 0x0010 | 10位地址模式 |
| I2C_M_NOSTART | 0x4000 | 不发送 START 条件,用于重复起始 |
| I2C_M_REV_DIR_ADDR | 0x2000 | 反转读写方向位 |
| I2C_M_IGNORE_NAK | 0x1000 | 忽略 NACK 继续传输 |
| I2C_M_STOP | 0x8000 | 强制发送 STOP 条件 |
4.4 三者的协作流程
- 客户端发起传输:调用
i2c_transfer(),传入 adapter 指针和 msg 数组。 - I2C核心加锁:获取 adapter->lock,防止并发访问。
- 调用算法回调:执行 adapter->algo->master_xfer(adap, msgs, num)。
- 硬件操作:master_xfer 内部通过 algo_data 访问控制器寄存器,逐条处理 msg。
- 返回结果:返回成功传输的 msg 数量,I2C核心释放锁并返回给客户端。
移植自检清单:
□ 是否在 probe 中正确初始化 adapter->algo 和 adapter->algo_data?
□ master_xfer 是否处理了 I2C_M_RD 标志?
□ 是否实现了 functionality 回调并返回正确的功能位?
□ 是否设置了合理的 timeout 和 retries 值?
□ 是否在 remove 中调用 i2c_del_adapter() 注销?
□ 是否在 probe 中正确初始化 adapter->algo 和 adapter->algo_data?
□ master_xfer 是否处理了 I2C_M_RD 标志?
□ 是否实现了 functionality 回调并返回正确的功能位?
□ 是否设置了合理的 timeout 和 retries 值?
□ 是否在 remove 中调用 i2c_del_adapter() 注销?