4. Linux内核I2C子系统架构:三层架构模型

Linux内核的I2C子系统采用经典的三层架构设计,将硬件控制、总线管理和设备操作解耦。理解这一架构是开发稳定、可移植的I2C设备驱动的基石。本节将详细剖析I2C核心层、总线驱动层和设备驱动层的职责、交互关系与核心数据结构。

4.1 三层架构总览

I2C子系统从上到下依次为:I2C设备驱动层、I2C核心层和I2C总线驱动层。设备驱动层面向具体的I2C从设备(如传感器、EEPROM、ADC等),总线驱动层面向具体的I2C控制器硬件(如S3C2440、OMAP、i.MX等平台的I2C适配器),核心层则提供统一的注册/注销接口、数据传输模板和地址解析机制。

架构核心思想: 设备驱动不直接操作硬件寄存器,而是通过核心层提供的API(如 i2c_transfer())发起通信;总线驱动负责将核心层的抽象请求转化为具体的硬件时序。这种分层使得设备驱动可以跨平台复用。
Linux I2C 子系统三层架构 I2C 设备驱动层 i2c_driver / i2c_client 实现 probe()、remove()、id_table 调用 i2c_transfer() / i2c_smbus_*() 与从设备通信 I2C 核心层 i2c-core.c / i2c-dev.c 管理 i2c_adapter 与 i2c_driver 的注册/注销 提供 i2c_transfer() 算法模板与 SMBus 兼容层 I2C 总线驱动层 i2c_adapter / i2c_algorithm 实现 master_xfer() / smbus_xfer() 硬件时序 操作 I2C 控制器寄存器(时钟、数据、中断) 数据流向:设备驱动 → 核心层 → 总线驱动 → 硬件;事件上报方向相反

4.2 各层核心数据结构与职责

三层架构由几个关键内核数据结构串联。下表总结了每个层次的核心结构及其主要字段:

层次 核心结构 关键字段 / 方法 职责描述
设备驱动层 struct i2c_driver probe(), remove(), id_table, driver 绑定到特定从设备地址;初始化设备、创建输入/文件系统接口
设备驱动层 struct i2c_client addr, adapter, name, irq 代表一个物理或逻辑I2C从设备,由核心层在匹配时创建
核心层 i2c-core.c i2c_add_driver(), i2c_transfer(), i2c_smbus_*() 驱动/适配器注册、总线匹配、数据传输调度、SMBus协议仿真
总线驱动层 struct i2c_adapter nr, name, algo, algo_data 代表一个I2C控制器实例,提供传输通道
总线驱动层 struct i2c_algorithm master_xfer(), smbus_xfer(), functionality() 实现底层硬件时序:起始/停止条件、字节收发、ACK/NACK检测

4.3 设备驱动层详解

设备驱动层是驱动开发者最常接触的层次。一个典型的I2C设备驱动需要完成以下步骤:

  1. 定义 i2c_driver 结构体:填充 probe、remove 回调以及 id_table(设备ID匹配表)。
  2. 注册驱动:调用 i2c_add_driver() 将驱动注册到核心层。
  3. 在 probe() 中初始化设备:获取 i2c_client 指针,分配私有数据结构,注册中断(如有),创建字符设备或输入设备。
  4. 实现数据传输:通过 i2c_transfer() 或 i2c_smbus_read_byte_data() 等API与从设备交换数据。
最佳实践: 在 probe() 中不要进行耗时操作(如长延时轮询),应使用工作队列或定时器。中断服务程序应尽量短小,复杂数据处理放到下半部(如 threaded IRQ)。

以下是一个简化的设备驱动注册框架:

#include <linux/i2c.h>
#include <linux/module.h>

static int my_sensor_probe(struct i2c_client *client,
                           const struct i2c_device_id *id)
{
    // 1. 检查设备功能
    // 2. 分配私有数据
    // 3. 初始化硬件(如设置采样率)
    // 4. 注册中断 / 创建设备节点
    return 0;
}

static int my_sensor_remove(struct i2c_client *client)
{
    // 释放资源、注销设备
    return 0;
}

static const struct i2c_device_id my_sensor_id[] = {
    { "my-sensor", 0 },
    { }
};
MODULE_DEVICE_TABLE(i2c, my_sensor_id);

static struct i2c_driver my_sensor_driver = {
    .driver = {
        .name = "my_sensor",
    },
    .probe    = my_sensor_probe,
    .remove   = my_sensor_remove,
    .id_table = my_sensor_id,
};

module_i2c_driver(my_sensor_driver);

4.4 总线驱动层详解

总线驱动层负责与具体的I2C控制器硬件交互。每个I2C适配器(即一个I2C总线控制器)对应一个 struct i2c_adapter,其核心是 struct i2c_algorithm 中的 master_xfer() 函数。

master_xfer() 接收一个 struct i2c_msg 数组,每个消息包含:

  • addr:从设备7位或10位地址。
  • flags:读写标志(I2C_M_RD)及其他控制位。
  • len:数据长度。
  • buf:数据缓冲区指针。

总线驱动的典型实现流程:

  1. 解析 i2c_msg 数组,生成对应的硬件时序(START + 地址 + R/W + 数据 + STOP)。
  2. 操作控制器的寄存器:设置时钟分频、发送起始条件、移位数据、检测ACK、产生停止条件。
  3. 处理错误(如无ACK、仲裁丢失、超时),返回实际传输的消息数或负错误码。
注意: 总线驱动通常由SoC厂商或平台维护者提供,设备驱动开发者一般不需要编写总线驱动。但理解其工作原理有助于调试硬件时序问题和分析逻辑分析仪抓取的波形。

4.5 核心层:粘合剂与仲裁者

I2C核心层(drivers/i2c/i2c-core.c)是三层架构的枢纽,主要提供以下功能:

功能模块 说明
驱动/适配器注册 i2c_add_driver() / i2c_add_adapter(),将驱动和适配器加入内核链表
总线匹配 遍历适配器上的设备列表,通过 id_table 或设备树 compatible 属性匹配驱动
数据传输调度 i2c_transfer() 调用适配器的 master_xfer(),处理重试和错误
SMBus 兼容层 将 SMBus 协议(如 i2c_smbus_read_word_data())转换为标准的 I2C 消息
设备模型集成 将 i2c_client 和 i2c_driver 挂接到 Linux 设备模型(sysfs、uevent)
I2C 核心层交互关系 设备驱动 i2c_driver 核心层 i2c-core 总线驱动 i2c_adapter 核心层内部功能 • 驱动/适配器注册管理 • 总线匹配与设备枚举 • i2c_transfer() 调度 • SMBus 协议仿真

4.6 数据流与消息传递

当设备驱动需要读取传感器数据时,数据流经过以下路径:

  1. 设备驱动 调用 i2c_transfer(client->adapter, msg, num)。
  2. 核心层 验证参数,调用适配器的 algo->master_xfer()。
  3. 总线驱动 将 i2c_msg 转换为硬件时序,通过控制器发送到I2C总线。
  4. 从设备响应数据,总线驱动将数据写回 msg->buf。
  5. 返回值逐层返回,设备驱动获得实际传输的消息数或错误码。
关键API: i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) 是核心层最重要的导出函数。它支持一次传输多个消息(组合事务),中间由重复起始条件(Repeated START)连接,无需STOP信号。

4.7 设备树与平台适配

在现代ARM/嵌入式Linux中,I2C设备信息通常通过设备树(Device Tree)描述。核心层在启动时解析设备树,为每个 i2c@... 节点创建 i2c_client,并与匹配的 i2c_driver 绑定。

设备树中I2C设备节点的典型结构:

&i2c1 {
    status = "okay";
    clock-frequency = <100000>;

    temperature-sensor@48 {
        compatible = "ti,tmp102";
        reg = <0x48>;
        interrupt-parent = <&gpio1>;
        interrupts = <16 IRQ_TYPE_EDGE_FALLING>;
    };
};

驱动端通过 of_match_table 匹配 compatible 属性:

static const struct of_device_id my_sensor_of_match[] = {
    { .compatible = "ti,tmp102" },
    { }
};
MODULE_DEVICE_TABLE(of, my_sensor_of_match);

static struct i2c_driver my_sensor_driver = {
    .driver = {
        .name = "tmp102",
        .of_match_table = my_sensor_of_match,
    },
    // ...
};

4.8 本章小结

  • 三层架构 将I2C子系统分为设备驱动层、核心层和总线驱动层,实现了硬件无关性与代码复用。
  • 设备驱动层 关注具体从设备的逻辑,通过 i2c_driver 和 i2c_client 与核心层交互。
  • 核心层 提供注册、匹配、传输调度和SMBus仿真,是系统的中枢。
  • 总线驱动层 封装硬件时序,通过 i2c_algorithm 的 master_xfer() 实现物理层通信。
  • 设备树 是描述I2C设备的主流方式,驱动应优先使用 of_match_table 进行匹配。