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子系统中的层级关系与数据流向:

I2C核心数据结构关系图 用户空间 (应用程序/工具) Linux I2C 核心层 (i2c-core) struct i2c_adapter 硬件控制器抽象 struct i2c_algorithm 通信协议方法集 struct i2c_client 从设备抽象 struct i2c_driver 设备驱动抽象 硬件层 (I2C总线控制器 + 从设备芯片) 说明:adapter + algorithm 构成主控端;client + driver 构成从设备端;核心层负责匹配与数据转发

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 驱动注册与匹配流程

  1. 定义驱动结构体:填充probe、remove、id_table等成员
  2. 注册驱动:调用 i2c_add_driver() 宏
  3. 匹配过程:内核将client的name与id_table中的name字段比对,或通过设备树compatible属性匹配
  4. probe调用:匹配成功后,内核调用驱动的probe函数,传入匹配的client指针
  5. 设备操作:在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读写操作中,四个结构体之间的协作关系:

I2C读写操作协作流程 i2c_driver 调用i2c_transfer() I2C核心层 查找adapter->algo i2c_adapter 持有algo指针 i2c_algorithm master_xfer() 硬件控制器 SDA/SCL时序 数据返回至i2c_driver的调用者 说明:i2c_client 提供目标地址和适配器信息,贯穿整个传输过程

4.7 数据结构对比总结

结构体代表对象核心职责由谁实现生命周期
i2c_adapterI2C控制器管理总线、持有算法指针控制器驱动与硬件绑定,常驻
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() 宏可以简化驱动注册代码