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 描述单次数据交换。三者分层解耦,使得同一套算法可适配不同硬件,同一适配器可复用多种传输策略。
I2C 核心数据结构协作关系 struct i2c_msg 描述单次传输:地址、方向、数据指针、长度、标志 struct i2c_algorithm master_xfer / smbus_xfer / functionality 回调函数集 struct i2c_adapter 硬件实例:nr、name、algo、algo_data、lock、timeout、dev i 调用链:i2c_msg → i2c_algorithm.master_xfer → i2c_adapter 硬件操作

4.1 struct i2c_adapter —— 适配器实例

struct i2c_adapter 是I2C控制器的软件抽象,每个注册到内核的I2C总线对应一个适配器实例。驱动开发者需要填充该结构体,并通过 i2c_add_adapter() 或 i2c_add_numbered_adapter() 将其注册到I2C核心。

struct i2c_adapter 关键成员
成员类型说明
nrint总线编号,-1 表示动态分配
namechar[48]适配器名称,用于 sysfs 和调试
algoconst struct i2c_algorithm *指向传输算法结构体
algo_datavoid *算法私有数据,通常指向控制器寄存器基址
lockstruct mutex总线访问互斥锁
timeoutint传输超时时间(单位 jiffies)
retriesint传输失败重试次数
devstruct device内核设备模型实例
classunsigned short适配器所属 I2C 类标志
quirksconst 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);
};
i2c_algorithm 回调对比
回调必须实现典型实现内容
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_msg.flags 常用标志
标志值含义
I2C_M_RD0x0001读操作(未设置则为写)
I2C_M_TEN0x001010位地址模式
I2C_M_NOSTART0x4000不发送 START 条件,用于重复起始
I2C_M_REV_DIR_ADDR0x2000反转读写方向位
I2C_M_IGNORE_NAK0x1000忽略 NACK 继续传输
I2C_M_STOP0x8000强制发送 STOP 条件
i2c_msg 复合传输示例:先写寄存器地址,再读数据 msg[0]:写 addr=0x50, flags=0 buf=寄存器地址(1字节) msg[1]:读 addr=0x50, flags=I2C_M_RD buf=接收缓冲区(2字节) I2C 控制器 硬件时序 START + 写 + 重复START + 读 + STOP ⚠ 关键行为 • master_xfer 收到 2 个 msg,先执行写操作发送寄存器地址,不发送 STOP • 紧接着执行读操作,控制器硬件自动插入重复起始(Repeated START)

4.4 三者的协作流程

  1. 客户端发起传输:调用 i2c_transfer(),传入 adapter 指针和 msg 数组。
  2. I2C核心加锁:获取 adapter->lock,防止并发访问。
  3. 调用算法回调:执行 adapter->algo->master_xfer(adap, msgs, num)。
  4. 硬件操作:master_xfer 内部通过 algo_data 访问控制器寄存器,逐条处理 msg。
  5. 返回结果:返回成功传输的 msg 数量,I2C核心释放锁并返回给客户端。
移植自检清单:
□ 是否在 probe 中正确初始化 adapter->algo 和 adapter->algo_data?
□ master_xfer 是否处理了 I2C_M_RD 标志?
□ 是否实现了 functionality 回调并返回正确的功能位?
□ 是否设置了合理的 timeout 和 retries 值?
□ 是否在 remove 中调用 i2c_del_adapter() 注销?