3. I2C核心数据结构成员解析

Linux I2C 子系统围绕四个核心数据结构构建:i2c_adapter(适配器)、i2c_algorithm(通信算法)、i2c_client(从设备)和 i2c_driver(驱动)。理解这些结构体的每个成员,是编写正确、高效 I2C 驱动的基石。本章逐一拆解其关键字段、用途与配置要点。

3.1 struct i2c_adapter —— I2C 总线适配器

i2c_adapter 代表一条物理 I2C 总线控制器(如 SoC 的 I2C 外设或 GPIO 模拟的 bit-bang 总线)。它是 I2C 核心与硬件之间的桥梁。

成员 类型 说明
owner struct module * 指向驱动模块自身,通常设为 THIS_MODULE,防止模块被意外卸载。
class unsigned int 适配器所属的 I2C 类标志(如 I2C_CLASS_HWMON),用于设备树匹配时的过滤。
algo const struct i2c_algorithm * 指向该适配器使用的通信算法结构体,定义底层收发函数。
algo_data void * 算法私有数据指针,通常指向硬件寄存器基址或平台数据。
dev struct device 内核设备模型中的 device 实例,用于电源管理、sysfs 和热插拔。
nr int 适配器编号(总线号),由 i2c_add_numbered_adapter() 分配或指定。
name char[48] 适配器名称,显示在 /sys/bus/i2c/devices/i2c-N/name 中。
timeout int 通信超时时间(单位 jiffies),默认由核心提供,驱动可覆盖。
retries int 通信失败时的重试次数,默认 3 次。
lock_ops const struct i2c_lock_operations * 总线锁定操作(可选),用于支持原子上下文或 RT 内核。
注册方式选择:使用 i2c_add_adapter() 让内核自动分配总线号;使用 i2c_add_numbered_adapter() 可指定 nr,常用于已知总线编号的硬件平台。

3.2 struct i2c_algorithm —— 通信算法接口

i2c_algorithm 定义了适配器与 I2C 从设备进行数据收发的底层方法。驱动开发者必须实现至少一种通信方式。

成员 说明
master_xfer 主传输函数,接收 struct i2c_msg 数组,返回成功传输的消息数量。这是最常用的接口。
smbus_xfer SMBus 协议传输函数(可选),用于兼容 SMBus 设备。
functionality 返回适配器支持的功能标志位掩码(如 I2C_FUNC_I2C、I2C_FUNC_SMBUS_BYTE)。
master_async 异步主传输函数(可选),用于支持 DMA 或非阻塞传输。
关键实现原则:master_xfer 必须处理 I2C_M_RD 标志(读方向)、I2C_M_STOP(停止条件)和 I2C_M_NOSTART(重复起始条件)。消息数组可能包含多条消息,用于组合事务(如先写寄存器地址再读数据)。

3.3 struct i2c_client —— I2C 从设备

i2c_client 代表一个连接到 I2C 总线上的从设备。它由 I2C 核心在设备匹配时创建,或由板级代码/设备树静态声明。

成员 类型 说明
addr unsigned short 从设备 7 位或 10 位地址(已左对齐为 7 位格式)。
name char[20] 设备名称,用于与 i2c_driver 的 id_table 匹配。
adapter struct i2c_adapter * 指向所属适配器的指针,驱动通过它发起通信。
dev struct device 内核设备模型实例,包含电源管理、sysfs 入口等。
flags unsigned short 设备标志,如 I2C_CLIENT_TEN(10 位地址)、I2C_CLIENT_SLAVE(从模式)。
irq int 从设备的中断号(若支持),由设备树或 ACPI 提供。
detected struct list_head 用于挂接到适配器的已检测设备链表(动态探测时使用)。
地址冲突风险:同一适配器上不允许两个 i2c_client 使用相同的 addr。动态探测时需注意地址重叠,避免误操作。

3.4 struct i2c_driver —— I2C 设备驱动

i2c_driver 是驱动开发者主要实现的结构体,它定义了驱动与 I2C 核心的交互接口。

成员 说明
probe 设备匹配成功后的探测函数,驱动在此初始化硬件、分配数据结构、注册子系统。
remove 设备移除或驱动卸载时的清理函数,释放资源、注销设备。
shutdown 系统关机时的回调(可选),用于执行优雅断电序列。
suspend / resume 电源管理回调(可选),用于系统休眠/唤醒时的设备状态保存与恢复。
id_table struct i2c_device_id 数组,用于传统板级匹配(非设备树)。
of_match_table struct of_device_id 数组,用于设备树匹配,现代驱动首选。
driver struct device_driver 基类,包含名称、owner、PM 操作等。
匹配优先级:设备树匹配(of_match_table)优先于 id_table。对于设备树平台,只需填充 of_match_table 即可,无需维护两份表。

3.5 核心数据结构关系图

下图展示了四个核心结构体之间的关联与数据流向:

I2C 核心数据结构关系图 I2C 核心数据结构关系图 struct i2c_adapter 总线控制器实例 algo / algo_data / nr struct i2c_algorithm 通信方法集合 master_xfer / smbus_xfer struct i2c_client 从设备实例 addr / adapter / irq struct i2c_driver 驱动行为定义 probe / id_table / of_match algo 指针 adapter 指针 匹配后绑定 数据流向说明 1. i2c_adapter 通过 algo 指针调用 master_xfer 发起总线传输 2. i2c_client 持有 adapter 指针,驱动通过 client->adapter 访问总线 3. i2c_driver 的 probe 接收匹配的 i2c_client,完成设备初始化 4. 所有通信最终由 i2c_algorithm 的 master_xfer 执行硬件操作

3.6 典型驱动注册流程中的结构体使用

以下代码片段展示了一个 I2C 驱动如何填充并使用上述结构体:

/* 1. 定义 i2c_algorithm */
static int my_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
    /* 硬件寄存器操作:发送起始条件、数据、停止条件 */
    return num; /* 返回成功传输的消息数 */
}

static u32 my_functionality(struct i2c_adapter *adap)
{
    return I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE;
}

static const struct i2c_algorithm my_algo = {
    .master_xfer   = my_master_xfer,
    .functionality = my_functionality,
};

/* 2. 定义 i2c_adapter */
static struct i2c_adapter my_adapter = {
    .owner   = THIS_MODULE,
    .class   = I2C_CLASS_HWMON,
    .algo    = &my_algo,
    .name    = "my-i2c-bus",
    .timeout = msecs_to_jiffies(100),
    .retries = 3,
};

/* 3. 定义 i2c_driver */
static const struct of_device_id my_of_match[] = {
    { .compatible = "myvendor,my-i2c-device" },
    { }
};
MODULE_DEVICE_TABLE(of, my_of_match);

static int my_probe(struct i2c_client *client)
{
    /* client->adapter 指向已注册的适配器 */
    /* 使用 i2c_smbus_read_byte() 等 API 通信 */
    return 0;
}

static struct i2c_driver my_driver = {
    .driver = {
        .name = "my-i2c-driver",
        .of_match_table = my_of_match,
    },
    .probe    = my_probe,
    .id_table = NULL, /* 设备树平台可留空 */
};

/* 4. 注册适配器与驱动 */
module_i2c_driver(my_driver);
结构体生命周期:i2c_adapter 和 i2c_driver 通常在模块初始化时注册,i2c_client 由核心在设备匹配时动态创建,驱动无需手动分配。理解这一生命周期有助于避免悬空指针和资源泄漏。

3.7 关键成员配置避坑指南

  • algo 不能为 NULL:注册适配器前必须确保 algo 已赋值,否则 i2c_add_adapter() 会返回 -EINVAL。
  • of_match_table 结尾哨兵:设备树匹配表必须以空结构 { } 结尾,否则内核遍历时会越界。
  • client->irq 检查:使用中断前必须验证 client->irq > 0,部分从设备可能未连接中断线。
  • adapter->retries 调优:对于噪声敏感的总线(如长走线),适当增加重试次数可提高通信可靠性。
  • class 标志过滤:设置正确的 class 可避免核心为不匹配的设备类型浪费探测时间。