2. Linux I2C 子系统架构:三层架构解析
Linux I2C 子系统采用经典的分层设计思想,将 I2C 总线相关的功能抽象为三个独立的层次:I2C 核心层、I2C 总线驱动层和I2C 设备驱动层。这种分层架构实现了硬件无关性与硬件相关性的解耦,使得驱动开发者可以专注于各自层次的逻辑,而无需关心其他层次的实现细节。
核心设计理念: 三层架构的核心目标是“复用”与“抽象”。核心层提供通用机制,总线层管理硬件时序,设备层处理具体外设协议。任何一层的修改都不应影响其他层的稳定性。
2.1 三层架构总览
下图展示了 Linux I2C 子系统的完整分层结构及各层之间的交互关系:
2.2 各层职责与核心数据结构
每一层都有其明确的数据结构和职责边界。下表总结了各层的核心结构、主要功能及典型实现文件:
| 层次 | 核心数据结构 | 主要职责 | 典型源文件 |
|---|---|---|---|
| I2C 设备驱动层 | struct i2c_driverstruct i2c_client |
实现具体外设的读写协议、中断处理、设备树匹配、sysfs 接口 | drivers/i2c/i2c-core-of.cdrivers/misc/eeprom/at24.c |
| I2C 核心层 | struct i2c_adapterstruct i2c_algorithm |
总线注册/注销、设备枚举、地址分配、SMBus 协议封装、锁定与重试 | drivers/i2c/i2c-core.cdrivers/i2c/i2c-dev.c |
| I2C 总线驱动层 | struct platform_driverstruct i2c_algorithm |
操作 I2C 控制器寄存器、产生 START/STOP/ACK 时序、时钟拉伸处理 | drivers/i2c/busses/i2c-imx.cdrivers/i2c/busses/i2c-omap.c |
2.3 设备驱动层详解
I2C 设备驱动层是驱动开发者最常接触的层次。它负责与具体的 I2C 从设备(如 EEPROM、传感器、音频编解码器)进行通信。该层的主要工作包括:
- 定义 i2c_driver 结构体:填充
probe()、remove()、id_table等回调函数。 - 设备树匹配:通过
of_match_table将设备节点与驱动绑定。 - 中断注册:在
probe()中调用request_threaded_irq()或devm_request_irq()注册中断处理函数。 - 数据传输:调用核心层提供的
i2c_transfer()或i2c_smbus_*()系列函数完成读写操作。
关键点: 设备驱动层不直接操作硬件寄存器,所有 I2C 总线时序操作都委托给核心层和总线驱动层完成。设备驱动只关心“发什么数据”和“收什么数据”。
2.4 核心层详解
I2C 核心层是整个子系统的“中枢神经系统”,它提供了以下关键机制:
- 总线注册与管理:维护系统中所有 I2C 适配器的链表,提供
i2c_add_adapter()/i2c_del_adapter()接口。 - 设备注册与枚举:解析设备树或板级代码,创建
i2c_client实例,并调用匹配的驱动probe()。 - 传输抽象:封装
i2c_transfer()函数,处理消息队列、重试逻辑、总线锁定与原子上下文。 - SMBus 协议支持:提供
i2c_smbus_read_byte()、i2c_smbus_write_word_data()等便捷函数,简化常见协议操作。 - 设备文件接口:通过
i2c-dev.c提供/dev/i2c-N字符设备,允许用户空间直接访问 I2C 总线。
2.5 总线驱动层详解
总线驱动层是离硬件最近的层次,它直接操作 I2C 控制器的寄存器。该层的核心是 struct i2c_algorithm,它定义了底层硬件如何执行 I2C 总线协议:
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);
};
总线驱动层的主要工作包括:
- 实现 master_xfer:解析
i2c_msg数组,逐条生成 START、SLAVE_ADDR、DATA、STOP 等时序。 - 时钟与速率控制:设置 I2C 控制器的时钟分频器,支持标准模式(100kHz)和快速模式(400kHz)。
- 中断处理:处理控制器产生的中断(如传输完成、仲裁丢失、NACK 接收),并唤醒等待的传输线程。
- 电源管理:实现运行时挂起/恢复,在总线空闲时关闭控制器时钟以降低功耗。
常见误区: 初学者常将设备驱动层的
i2c_driver 与总线驱动层的 platform_driver 混淆。总线驱动层通常以平台驱动形式注册,而设备驱动层以 I2C 驱动形式注册。两者通过核心层的适配器机制连接。
2.6 三层交互流程
下面以一次典型的 I2C 读操作为例,展示三层之间的完整调用链:
2.7 分层设计的优势
Linux I2C 子系统的三层架构带来了以下工程优势:
| 优势 | 说明 |
|---|---|
| 可移植性 | 更换 SoC 时只需重写总线驱动层,设备驱动层和核心层无需修改。 |
| 代码复用 | 同一份 EEPROM 驱动可以在不同平台的 I2C 控制器上运行。 |
| 关注点分离 | 设备驱动开发者无需了解 I2C 控制器的寄存器细节,只需调用标准 API。 |
| 稳定性 | 核心层提供经过充分测试的锁定、重试和错误处理机制,减少驱动缺陷。 |
| 调试便利 | 通过 /dev/i2c-N 和 i2c-tools 可以在用户空间直接验证总线通信。 |
实践建议: 在开发新的 I2C 设备驱动时,优先在现有内核源码中查找同类型设备的驱动作为参考。大多数常见外设(如 AT24 EEPROM、BMP280 传感器、DS1307 RTC)在内核中已有现成驱动,理解其分层调用模式是快速上手的关键。