2. Linux I2C 子系统架构:分层设计与数据流
Linux I2C 子系统采用经典的三层软件架构,将硬件适配器控制、核心协议管理与具体从设备驱动解耦。这种分层设计使得适配器驱动与从设备驱动可以独立开发,并通过 I2C 核心层提供的统一接口完成通信。理解每一层的职责与数据流是进行 I2C 驱动开发与调优的基础。
2.1 三层架构总览
I2C 子系统从上到下依次为:I2C 从设备驱动层、I2C 核心层、I2C 适配器驱动层。核心层作为中间枢纽,向上提供设备驱动注册与消息收发 API,向下定义适配器驱动的操作接口。
架构核心原则:从设备驱动不直接操作硬件寄存器,而是通过核心层调用适配器驱动的算法(algorithm)完成总线时序。适配器驱动不关心挂载在总线上的具体设备类型,只负责将核心层下发的消息转换为 I2C 总线上的 SCL/SDA 信号。
2.2 各层职责详解
2.2.1 I2C 核心层(I2C Core)
核心层是 I2C 子系统的中枢,由 drivers/i2c/i2c-core.c 及其辅助文件实现。它不直接操作硬件,而是提供框架与基础设施。
- 适配器管理:提供
i2c_add_adapter()/i2c_del_adapter()接口,管理适配器在系统中的注册与注销。 - 设备管理:提供
i2c_new_device()/i2c_unregister_device(),将从设备(i2c_client)绑定到特定适配器。 - 驱动管理:维护 i2c_driver 的注册与匹配,当 i2c_client 与 i2c_driver 的 id_table 匹配时,调用 probe()。
- 消息传输:提供
i2c_transfer()和i2c_smbus_xfer()封装,将消息路由到对应适配器的 algorithm。 - sysfs 与用户态接口:通过
i2c-dev.c提供 /dev/i2c-N 字符设备,允许用户态直接访问 I2C 总线。
2.2.2 I2C 适配器驱动层(I2C Adapter Driver)
适配器驱动负责与具体的 I2C 控制器硬件交互。每个 I2C 总线控制器对应一个 i2c_adapter 实例,其核心是 i2c_algorithm 结构体。
| 组件 | 数据结构 / 函数 | 职责说明 |
|---|---|---|
| 适配器实例 | struct i2c_adapter |
包含适配器编号、算法指针、锁、超时参数、DMA 设置等 |
| 算法接口 | struct i2c_algorithm |
定义 master_xfer() 和 smbus_xfer() 回调,实现总线时序 |
| 硬件操作 | 寄存器读写 | 配置控制器时钟、产生 START/STOP/ACK 信号、收发字节 |
| 平台绑定 | platform_driver / of_match_table | 通过设备树或 ACPI 匹配硬件,在 probe() 中初始化适配器 |
适配器驱动开发要点:实现
master_xfer() 时,需要正确处理消息数组(msg[])中的多条消息,支持复合事务(combined transaction)。对于不支持 SMBus 的硬件,核心层会自动将 SMBus 请求拆解为标准 I2C 消息。
2.2.3 I2C 从设备驱动层(I2C Client Driver)
从设备驱动面向具体的 I2C 外设芯片(如传感器、EEPROM、ADC 等),通过核心层提供的 API 与硬件通信。
- 驱动结构:使用
struct i2c_driver注册,包含probe()、remove()、id_table等成员。 - 设备标识:通过
i2c_device_id或设备树 compatible 字符串匹配硬件。 - 通信方式:
i2c_transfer():发送/接收struct i2c_msg数组,支持多消息组合。i2c_smbus_read_byte_data()/i2c_smbus_write_word_data()等:SMBus 封装函数,简化单寄存器读写。
- 生命周期:probe() 中完成设备初始化、注册输入设备或 hwmon 等子系统;remove() 中释放资源。
2.3 数据流与消息传递路径
当从设备驱动发起一次 I2C 读写操作时,数据流经过三个层次,形成完整的调用链。以下以 i2c_smbus_read_byte_data() 为例说明典型路径。
- 从设备驱动调用:
i2c_smbus_read_byte_data(client, reg_addr) - 核心层封装:i2c-core-smbus.c 将 SMBus 协议封装为
struct i2c_msg数组(写寄存器地址 + 读数据)。 - 核心层路由:调用
i2c_transfer(),通过 adapter 的 algorithm 指针找到master_xfer()。 - 适配器驱动执行:
master_xfer()操作硬件寄存器,在 I2C 总线上产生 START、发送设备地址+写位、发送寄存器地址、重复 START、发送设备地址+读位、读取数据、STOP。 - 数据返回:读取的数据通过 msg->buf 返回,逐层传回从设备驱动。
2.4 关键数据结构关系
理解以下四个核心数据结构之间的关系,是掌握 I2C 子系统的关键。
| 数据结构 | 所属层 | 关键成员 | 作用 |
|---|---|---|---|
struct i2c_adapter |
适配器层 | nr, algo, lock, timeout, dev | 代表一个 I2C 总线控制器 |
struct i2c_algorithm |
适配器层 | master_xfer, smbus_xfer | 定义硬件操作回调 |
struct i2c_client |
核心层/设备层 | addr, adapter, driver, name | 代表一个挂载在总线上的从设备 |
struct i2c_driver |
从设备驱动层 | probe, remove, id_table, driver | 从设备驱动的注册结构 |
struct i2c_msg |
核心层 | addr, flags, len, buf | 描述一次数据传输的报文 |
常见误区:初学者容易混淆
i2c_client 与 i2c_driver 的关系。一个 i2c_client 对应一个物理设备实例,由核心层在设备发现时创建;一个 i2c_driver 对应一种设备驱动,可以绑定多个同类型 i2c_client。驱动开发者的主要工作是填充 i2c_driver,而不是直接创建 i2c_client。
2.5 分层设计的优势
- 可移植性:从设备驱动无需修改即可运行在不同 I2C 控制器上,适配器驱动更换不影响上层。
- 模块化:适配器驱动与从设备驱动可以分别编译为内核模块,动态加载/卸载。
- 复用性:核心层提供的 SMBus 封装、sysfs 接口、用户态访问等基础设施,所有驱动均可直接使用。
- 调试便利:可以在核心层插入跟踪点(tracepoint)或使用 i2c-tools 工具,独立验证每一层的功能。