2. Linux I2C 子系统架构:三层模型与数据结构关系

Linux I2C 子系统采用经典的三层分离架构,将协议实现、硬件适配与设备逻辑解耦。理解这三层模型及其核心数据结构,是进行 I2C 设备驱动开发与调试的基础。

2.1 三层模型概述

I2C 子系统从下到上分为:I2C 核心层、I2C 总线驱动(适配器驱动)、I2C 设备驱动。每一层职责清晰,通过标准接口交互。

层次 职责 核心数据结构 典型文件
I2C 核心层 提供通用协议处理、设备注册/注销、总线管理、sysfs 接口 i2c_adapter、i2c_client、i2c_driver drivers/i2c/i2c-core.c
I2C 总线驱动 直接操作 I2C 控制器硬件,实现主机传输时序(SDA/SCL) i2c_algorithm、i2c_adapter drivers/i2c/busses/i2c-xxx.c
I2C 设备驱动 绑定具体 I2C 从设备,实现设备功能逻辑(如触摸屏、传感器) i2c_driver、i2c_client drivers/input/touchscreen/xxx.c
调试要点: 在调试显示驱动中的 I2C 通信时,通常需要同时关注总线驱动层(确认硬件时序正确)和设备驱动层(确认寄存器读写逻辑)。核心层一般不需要修改,但可以通过 i2cdetect、i2cget、i2cset 等用户态工具验证。

2.2 核心数据结构关系图

下图展示了 I2C 子系统中四个关键数据结构之间的关联关系:

I2C 子系统核心数据结构关系图 struct i2c_adapter nr: 总线编号 algo: 算法指针 dev: 设备对象 struct i2c_algorithm master_xfer() smbus_xfer() struct i2c_client addr: 从设备地址 adapter: 所属适配器 name: 设备名称 struct i2c_driver probe() / remove() id_table: 匹配表 algo 指针 clients 链表 driver 指针 probe() 绑定 数据结构绑定流程说明: 1. 总线驱动注册 i2c_adapter,填充 i2c_algorithm 实现 master_xfer。 2. 设备驱动注册 i2c_driver,通过 id_table 或 OF 匹配表声明支持的设备。 3. 核心层根据设备信息(ACPI/DT/板级代码)创建 i2c_client,挂接到对应 adapter。 4. 核心层遍历 adapter 上的 client,匹配 driver 并调用 probe(),完成绑定。

2.3 各层核心数据结构详解

2.3.1 I2C 核心层:i2c_adapter 与 i2c_client

i2c_adapter 代表一个 I2C 控制器(主机),i2c_client 代表一个挂载在该总线上的从设备。核心层维护这两者的注册与生命周期。

// i2c_adapter 关键字段(include/linux/i2c.h)
struct i2c_adapter {
    struct module *owner;
    unsigned int class;               // 设备类,用于自动探测
    const struct i2c_algorithm *algo; // 算法实现
    struct device dev;                // 内核设备模型
    struct list_head clients;         // 挂接的 client 链表
    int nr;                           // 总线编号(如 i2c-0)
    char name[48];                    // 适配器名称
};

// i2c_client 关键字段
struct i2c_client {
    unsigned short addr;              // 7 位从设备地址
    char name[I2C_NAME_SIZE];         // 设备名称
    struct i2c_adapter *adapter;      // 所属适配器
    struct device dev;                // 内核设备模型
    struct i2c_driver *driver;        // 绑定的驱动
};
调试技巧: 在 /sys/bus/i2c/devices/ 目录下可以查看所有注册的 I2C 设备。例如 i2c-0/0-0050 表示总线 0 上地址为 0x50 的设备。通过 cat /sys/bus/i2c/devices/i2c-0/name 可查看适配器名称。

2.3.2 I2C 总线驱动:i2c_algorithm

总线驱动必须实现 i2c_algorithm 中的 master_xfer 函数,该函数负责在硬件上执行实际的 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;  // 功能掩码
};

2.3.3 I2C 设备驱动:i2c_driver

设备驱动通过 i2c_driver 结构体向核心层注册,核心层在匹配到对应 i2c_client 后调用 probe 函数。

struct i2c_driver {
    int (*probe)(struct i2c_client *client, const struct i2c_device_id *id);
    void (*remove)(struct i2c_client *client);
    struct device_driver driver;       // 内核驱动模型
    const struct i2c_device_id *id_table; // 传统 ID 匹配表
    const struct of_device_id *of_match_table; // 设备树匹配表
};

2.4 设备注册流程与匹配机制

I2C 设备驱动的注册与匹配遵循以下步骤:

  1. 总线驱动注册: 调用 i2c_add_adapter() 或 i2c_add_numbered_adapter() 注册适配器,核心层为其分配总线编号并创建 /dev/i2c-N 设备节点。
  2. 设备信息注册: 设备信息可以通过三种方式提供:
    • 设备树(DT): 在 DTS 文件中描述 I2C 从设备,内核解析后自动创建 i2c_client。
    • ACPI: 在 ACPI 表中声明 I2C 设备,主要用于 x86 平台。
    • 板级代码: 通过 i2c_board_info 结构体静态注册,常用于旧版内核。
  3. 设备驱动注册: 调用 i2c_add_driver() 注册驱动,核心层将其加入总线驱动链表。
  4. 匹配与 probe: 核心层遍历所有 i2c_client,使用 id_table 或 of_match_table 匹配驱动,匹配成功则调用 probe()。
常见调试陷阱: 在显示驱动中,如果 I2C 触摸屏或显示控制芯片无法 probe,请优先检查:
1. 设备树中 I2C 地址是否正确(7 位地址,左对齐)。
2. 总线驱动是否已注册(i2cdetect -l 查看)。
3. 驱动 of_match_table 中的 compatible 字符串是否与 DTS 完全一致。

2.5 三层模型交互流程图

下图展示了从用户态到硬件寄存器的完整 I2C 通信路径:

I2C 三层模型通信路径 用户态 (i2cdev / sysfs) I2C 核心层 (i2c-core) I2C 总线驱动 (i2c-algo) I2C 控制器硬件 (SDA/SCL) 各层关键函数 用户态: open/ioctl/read/write 核心层: i2c_transfer() i2c_master_send() i2c_master_recv() 总线驱动: master_xfer() 硬件层: 寄存器读写 调试命令: i2cdetect -y <bus> i2cget -y <bus> <addr>

2.6 关键数据结构对比表

数据结构 所属层次 创建时机 销毁时机 典型数量
i2c_adapter 核心层 / 总线驱动 总线驱动 probe 总线驱动 remove 每个 I2C 控制器 1 个
i2c_algorithm 总线驱动 静态定义 随 adapter 销毁 每个 adapter 1 个
i2c_client 核心层 设备信息注册时 设备移除或驱动卸载 每个从设备 1 个
i2c_driver 设备驱动 模块加载时 模块卸载时 每个设备驱动 1 个
显示驱动场景提示: 在 HDMI 或 DP 显示驱动中,DDC 通道通常使用 I2C 总线(地址 0x50 读取 EDID)。此时 i2c_adapter 由 GPU 或显示控制器驱动注册,i2c_client 由 DRM 核心或 EDID 读取逻辑动态创建,无需单独编写设备驱动。