3. Linux I2C 子系统架构:三层模型与数据结构关系
Linux I2C 子系统采用经典的三层软件架构设计,将硬件控制、总线管理与设备驱动解耦。理解这三层模型及其核心数据结构之间的关联,是进行 I2C 设备树配置与驱动开发的基础。
3.1 三层模型概述
I2C 子系统从上到下分为:I2C 设备驱动层、I2C 核心层、I2C 总线驱动层。每一层职责明确,通过标准接口交互。
| 层次 | 职责 | 核心数据结构 | 典型文件 |
|---|---|---|---|
| I2C 设备驱动 | 实现具体外设(如触摸屏、传感器、音频编解码器)的驱动逻辑,通过 I2C 核心层提供的 API 与硬件通信。 | i2c_driver、i2c_client |
drivers/input/touchscreen/ |
| I2C 核心层 | 提供统一的设备注册/注销、总线管理、地址解析、适配器算法调用框架。不依赖具体硬件。 | i2c_adapter、i2c_algorithm、i2c_bus_type |
drivers/i2c/i2c-core.c |
| I2C 总线驱动 | 直接操作 I2C 控制器硬件寄存器,实现时序产生、起始/停止条件、ACK/NACK 检测等底层操作。 | i2c_adapter(实例化)、i2c_algorithm(实现) |
drivers/i2c/busses/i2c-imx.c |
车载场景提示: 在车载 SoC(如 i.MX8、R-Car、J6)中,I2C 总线驱动通常由芯片厂商提供,开发者主要工作集中在 I2C 设备驱动层和设备树配置。理解三层边界有助于快速定位问题归属。
3.2 核心数据结构关系图
下图展示了 I2C 子系统中关键数据结构之间的关联。图中使用不同颜色区分三层归属,箭头表示指针引用或注册关系。
3.3 关键数据结构详解
3.3.1 struct i2c_driver(设备驱动层)
每个 I2C 外设驱动必须定义一个 i2c_driver 实例,并通过 i2c_add_driver() 注册到核心层。核心成员包括:
- probe():设备匹配成功后调用,完成硬件初始化、注册字符设备/输入设备等。
- remove():设备移除时调用,释放资源。
- id_table:
struct of_device_id数组,用于和设备树 compatible 属性匹配。 - driver:内嵌的
struct device_driver,由核心层统一管理。
3.3.2 struct i2c_client(设备驱动层)
代表一个挂载在 I2C 总线上的物理设备。由核心层在设备匹配成功后自动创建,驱动通过 client 指针访问设备:
- addr:7 位或 10 位 I2C 从设备地址。
- adapter:指向所属
i2c_adapter的指针。 - name:设备名称,用于调试和匹配。
- dev:内嵌的
struct device,用于设备模型框架。
3.3.3 struct i2c_adapter(核心层)
代表一个 I2C 控制器(总线适配器)。每个 I2C 控制器硬件对应一个 i2c_adapter 实例:
- nr:总线编号,如
/dev/i2c-1中的 1。 - algo:指向
i2c_algorithm的指针,定义了实际的数据传输方法。 - clients:挂载在此适配器上的设备链表头。
- dev:内嵌设备结构,用于设备模型。
3.3.4 struct i2c_algorithm(核心层)
定义 I2C 控制器与从设备通信的底层方法。总线驱动必须实现以下回调:
| 回调函数 | 功能 | 参数说明 |
|---|---|---|
master_xfer() |
标准 I2C 传输(支持多消息组合) | adapter, msgs, num |
smbus_xfer() |
SMBus 协议传输(可选) | adapter, addr, flags, command, protocol, data |
functionality() |
返回适配器支持的功能掩码 | adapter |
3.4 设备注册与匹配流程
从设备树解析到驱动 probe 的完整链路如下:
- 设备树解析:内核启动时,
of_platform_populate()遍历设备树,为每个 compatible 匹配的 I2C 控制器节点创建platform_device。 - 总线驱动 probe:I2C 总线驱动(如
i2c_imx_probe())被调用,初始化硬件寄存器,调用i2c_add_numbered_adapter()注册i2c_adapter。 - 子节点扫描:核心层在适配器注册后,扫描设备树中该 I2C 控制器下的子节点,为每个子节点创建
i2c_client。 - 驱动匹配:核心层遍历已注册的
i2c_driver,通过of_match_table或id_table进行匹配。 - probe 调用:匹配成功后,调用驱动的
probe()函数,传入i2c_client指针,驱动开始工作。
调试要点: 在车载调试中,若 I2C 设备无法挂载,应依次检查:① 设备树节点 compatible 是否与驱动 id_table 一致;② 总线驱动是否成功注册 adapter;③ 子节点 reg 属性中的地址是否与硬件一致;④ 内核是否编译了对应的驱动模块。
3.5 三层模型与设备树的映射关系
设备树中的 I2C 控制器节点和子节点,最终映射到内核中的三层数据结构:
3.6 车载开发中的常见问题
常见陷阱:
- 地址冲突: 同一 I2C 总线上两个设备使用相同地址(如 0x1a),导致 probe 失败。车载多传感器场景需仔细核对硬件原理图。
- 适配器未注册: 总线驱动 probe 失败(如时钟未使能、复位未释放),导致子节点无法扫描。检查
/sys/bus/i2c/devices/确认 adapter 是否存在。 - id_table 遗漏: 驱动中
of_match_table未包含设备树中的 compatible 字符串,导致匹配失败。 - reg 属性错误: 设备树中 reg 值必须与硬件从设备地址一致,且不能包含读写位(7 位地址左移 1 位是常见误解)。