3. Linux I2C 子系统架构:三层架构设计

Linux I2C 子系统采用经典的三层软件架构设计,将协议处理、硬件适配与设备驱动解耦。理解这三层的职责划分与交互接口,是编写稳定、可移植 I2C 驱动的核心基础。

架构核心原则: 上层驱动不感知底层硬件细节,底层驱动不关心上层业务逻辑。I2C 核心层作为中间枢纽,提供统一的注册、通信与数据抽象接口。

3.1 三层架构总览

I2C 子系统从上到下依次为:I2C 设备驱动层、I2C 核心层、I2C 总线驱动层。每一层都有明确的职责边界和标准 API。

Linux I2C 子系统三层架构 I2C 设备驱动层 (I2C Device Driver) i2c_driver / i2c_client 实现:probe() / remove() / 用户空间接口 例如:eeprom、rtc、sensor 驱动 I2C 核心层 (I2C Core) i2c-core.c / i2c-dev.c 功能:总线注册、设备枚举、协议传输、sysfs 接口 关键函数:i2c_add_adapter() / i2c_new_device() / i2c_transfer() I2C 总线驱动层 (I2C Bus Adapter Driver) i2c_adapter / i2c_algorithm 实现:master_xfer() / 硬件寄存器操作 例如:i2c-imx.c / i2c-omap.c / i2c-designware.c 用户态 内核态 硬件层

3.2 各层职责与关键数据结构

层级 核心数据结构 主要职责 典型文件
设备驱动层 struct i2c_driver
struct i2c_client
绑定具体从机设备,实现设备初始化、数据读写、中断处理、用户空间接口(sysfs/ioctl) drivers/misc/eeprom/at24.c
drivers/rtc/rtc-ds1307.c
核心层 struct i2c_adapter
struct i2c_algorithm
管理总线适配器注册/注销、设备枚举与地址分配、协议传输调度、sysfs 属性创建 drivers/i2c/i2c-core.c
drivers/i2c/i2c-dev.c
总线驱动层 struct i2c_adapter
struct i2c_algorithm
直接操作 I2C 控制器硬件寄存器,实现 start/stop/ack 时序,完成单次或多次消息传输 drivers/i2c/busses/i2c-imx.c
drivers/i2c/busses/i2c-omap.c

3.3 设备驱动层详解

设备驱动层是开发者最常接触的层次。它通过 i2c_driver 结构体向核心层注册,当设备树中匹配的节点被枚举时,核心层调用 probe() 函数完成驱动初始化。

// i2c_driver 结构体核心成员
struct i2c_driver {
    int (*probe)(struct i2c_client *client, const struct i2c_device_id *id);
    int (*remove)(struct i2c_client *client);
    struct device_driver driver;
    const struct i2c_device_id *id_table;
    const struct of_device_id *of_match_table;  // 设备树匹配表
};
工程实践: 设备驱动层应尽量使用 i2c_transfer() 或 i2c_smbus_read/write_*() 系列函数进行通信,避免直接操作 adapter 层的 master_xfer 回调。这保证了驱动在 I2C 多主、SMBus 等场景下的兼容性。

3.4 核心层详解

核心层是 I2C 子系统的中枢神经,负责以下关键任务:

  • 总线适配器管理: 通过 i2c_add_adapter() 注册总线适配器,分配总线编号(如 i2c-0、i2c-1)。
  • 设备枚举: 解析设备树中 I2C 子节点,调用 i2c_new_device() 创建 i2c_client 实例。
  • 协议传输调度: 实现 i2c_transfer() 函数,将上层消息队列传递给总线驱动的 master_xfer()。
  • sysfs 接口: 在 /sys/bus/i2c/ 下创建总线、设备和驱动属性文件。
I2C 核心层工作流程 设备树解析 i2c_new_device() 驱动匹配 probe() i2c_transfer() 消息队列调度 master_xfer() 核心层封装了设备枚举、驱动匹配、传输调度等复杂逻辑,上层驱动只需关注业务 核心层通过 i2c-core.c 和 i2c-dev.c 两个文件实现内核态与用户态接口

3.5 总线驱动层详解

总线驱动层直接与硬件 I2C 控制器交互。每个 I2C 控制器对应一个 i2c_adapter 实例,其核心是 i2c_algorithm 结构体中的 master_xfer() 回调函数。

// i2c_algorithm 结构体核心成员
struct i2c_algorithm {
    int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
    int (*smbus_xfer)(struct i2c_adapter *adap, u16 addr, unsigned short flags,
                      char read_write, u8 command, int size, union i2c_smbus_data *data);
};
关键注意事项: 总线驱动层必须正确处理以下硬件细节:
  • 时钟延展(Clock Stretching)——从机拉低 SCL 时的超时处理
  • 仲裁丢失(Arbitration Loss)——多主模式下的冲突检测与重试
  • NAK 处理——从机无应答时的错误恢复
  • 传输超时——根据总线速率计算合理的超时阈值

3.6 三层之间的数据流

一次完整的 I2C 读写操作,数据在三层之间的流动路径如下:

  1. 设备驱动层 调用 i2c_transfer() 或 i2c_smbus_read_byte() 等封装函数。
  2. 核心层 接收请求,构造 struct i2c_msg 消息数组,调用 adapter 的 master_xfer() 回调。
  3. 总线驱动层 将消息转换为硬件时序:发送 start 条件、从机地址、数据字节、stop 条件。
  4. 硬件控制器完成物理电平转换,通过 SDA/SCL 线与从机通信。
  5. 返回路径逆向:总线驱动层返回传输结果 → 核心层传递状态 → 设备驱动层获取数据。
步骤 调用链 所在层级 关键函数/操作
1 驱动发起读写 设备驱动层 i2c_master_send() / i2c_master_recv()
2 核心层封装消息 核心层 i2c_transfer() → __i2c_transfer()
3 总线驱动执行传输 总线驱动层 adap->algo->master_xfer()
4 硬件时序生成 硬件层 SCL 时钟、SDA 数据、start/stop 条件
5 结果返回 逆向逐层返回 传输字节数 / 错误码

3.7 架构设计的工程价值

工程收益总结:
  • 可移植性: 更换 SoC 时只需重写总线驱动层,设备驱动层无需修改。
  • 可扩展性: 新增 I2C 从机设备只需编写设备驱动层代码,复用现有总线驱动。
  • 可维护性: 三层职责清晰,调试时可快速定位问题所在层级。
  • 标准化: 核心层提供统一的 API 和数据结构,降低团队协作成本。