4. I2C 核心数据结构:i2c_adapter、i2c_algorithm、i2c_client、i2c_driver 结构体详解
在车载 Linux 的 I2C 子系统里,驱动开发的核心就是理解并正确使用四个关键数据结构:i2c_adapter(适配器)、i2c_algorithm(通信算法)、i2c_client(从设备客户端)和 i2c_driver(从设备驱动)。它们构成了 I2C 总线驱动模型的主干,也是设备挂载与调试的基石。
4.1 i2c_adapter:I2C 总线适配器
i2c_adapter 是 I2C 控制器在软件层面的抽象,负责管理一条物理 I2C 总线。每个 I2C 控制器(如 SoC 内部的 I2C IP 核)对应一个 adapter 实例。
| 成员 | 类型 | 说明 |
|---|---|---|
owner |
struct module * | 所属模块,通常设为 THIS_MODULE |
class |
unsigned int | 适配器类型,如 I2C_CLASS_HWMON |
algo |
const struct i2c_algorithm * | 指向通信算法实现,adapter 的核心操作 |
algo_data |
void * | 算法私有数据,通常指向控制器寄存器基址 |
bus_lock |
struct mutex | 总线访问互斥锁,防止并发竞态 |
timeout |
int | 传输超时时间(单位 jiffies) |
retries |
int | 传输失败时的重试次数 |
dev |
struct device | 内核设备模型实例,用于 sysfs 和设备树关联 |
nr |
int | 适配器编号(总线号),如 /dev/i2c-1 的 nr=1 |
name |
char[48] | 适配器名称,用于调试和日志 |
list |
struct list_head | 全局 adapter 链表节点 |
在车载场景中,一个典型的 SoC 可能包含 4~8 个 I2C 控制器,每个控制器注册为一个 adapter,分别对应 /dev/i2c-0 到 /dev/i2c-7。adapter 的注册通过 i2c_add_adapter() 或 i2c_add_numbered_adapter() 完成。
4.2 i2c_algorithm:通信算法的抽象
i2c_algorithm 定义了 adapter 与从设备进行数据交换的具体方法。它是硬件操作与上层协议之间的桥梁。
struct i2c_algorithm {
/* 主传输函数:发送或接收一个完整消息 */
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 |
是 | 发送传感器配置、读取 ADC 数据、控制触摸屏 |
smbus_xfer |
否 | 与 PMIC、电池管理芯片的 SMBus 通信 |
functionality |
是 | 返回 I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE 等 |
master_xfer 返回负值(如 -EIO、-ETIMEDOUT),通常表示硬件时序问题或从设备未应答。此时应使用示波器检查 SCL/SDA 波形,而非盲目修改软件。
4.3 i2c_client:从设备客户端
i2c_client 代表挂载在 I2C 总线上的一个具体从设备。它由内核在设备枚举或设备树解析时创建,并绑定到对应的 i2c_driver。
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; /* 检测链表 */
};
在车载设备树中,client 的创建通常由以下节点触发:
&i2c3 {
status = "okay";
clock-frequency = <400000>; /* 400kHz 快速模式 */
temperature_sensor: tmp102@48 {
compatible = "ti,tmp102";
reg = <0x48>; /* 7 位从地址 */
interrupt-parent = <&gpio2>;
interrupts = <5 IRQ_TYPE_EDGE_FALLING>;
};
};
内核解析上述节点后,会调用 i2c_new_client_device() 创建一个 i2c_client,其中 addr=0x48,adapter 指向 i2c3 对应的 adapter。
4.4 i2c_driver:从设备驱动
i2c_driver 是驱动开发者主要实现的结构体,它描述了如何与特定类型的从设备交互。
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);
struct device_driver driver; /* 内核驱动基类 */
const struct i2c_device_id *id_table; /* 设备 ID 表 */
const struct of_device_id *of_match_table; /* 设备树匹配表 */
};
| 回调/成员 | 作用 | 车载实现要点 |
|---|---|---|
probe |
设备匹配成功后调用,完成初始化 | 注册输入设备、创建 sysfs 属性、启动定时轮询 |
remove |
设备移除或驱动卸载时调用 | 释放中断、取消工作队列、注销设备 |
id_table |
传统板级匹配方式 | 用于非设备树平台或兼容性匹配 |
of_match_table |
设备树 compatible 匹配 | 车载主流方式,必须与 dts 节点一致 |
4.5 四大结构体协作关系
下图展示了 adapter、algorithm、client、driver 在 I2C 子系统中的完整协作流程:
4.6 驱动绑定与设备挂载流程
在车载 Linux 启动过程中,I2C 设备从硬件识别到驱动绑定的完整步骤如下:
- Adapter 注册: I2C 控制器驱动在 probe 中调用
i2c_add_numbered_adapter(),将 adapter 加入全局链表,并在/dev下创建 i2c-N 设备节点。 - 设备树解析: 内核遍历
/soc/i2c@.../下的子节点,为每个 compatible 匹配的节点调用of_i2c_register_devices()。 - Client 创建: 对每个有效子节点,调用
i2c_new_client_device()创建i2c_client,并挂载到 adapter 的 clients 链表。 - Driver 匹配: 内核遍历已注册的
i2c_driver,通过of_match_table或id_table进行匹配。 - Probe 调用: 匹配成功后,内核调用 driver 的
probe()函数,传入对应的i2c_client指针。 - 数据传输: 驱动通过
i2c_transfer()或i2c_smbus_read_byte()等 API 发起通信,最终调用 adapter 的master_xfer。
- 设备树中
reg地址与硬件原理图不一致(7 位地址左移错误) clock-frequency超过从设备支持的最大速率(如 400kHz 设备被配置为 1MHz)- adapter 的
master_xfer未正确处理I2C_M_RD标志位 - 多个从设备地址冲突(同一总线上两个设备使用相同地址)
4.7 数据结构对比总结
| 结构体 | 角色 | 谁实现? | 车载典型文件 |
|---|---|---|---|
i2c_adapter |
总线控制器抽象 | SoC 厂商 BSP 工程师 | drivers/i2c/busses/i2c-imx.c |
i2c_algorithm |
硬件通信方法 | SoC 厂商 BSP 工程师 | 同上,i2c_imx_xfer() |
i2c_client |
从设备实例 | 内核(设备树/板级代码) | 自动创建,无需手动编写 |
i2c_driver |
从设备驱动 | 车载驱动开发工程师 | drivers/input/touchscreen/ 等 |
理解这四个结构体的职责与协作关系,是编写可靠车载 I2C 驱动的基础。在实际调试中,通过 /sys/kernel/debug/i2c/ 和 i2cdump 工具可以直观验证每个结构体的运行时状态。