Linux I2C 子系统深度复习与关键问题剖析


支持作者,点击京东购买《Yocto项目实战教程》


在 Linux 内核中,I2C 子系统承担着处理与各类 I2C 外设交互的角色。它不仅是设备模型中的重要一环,也体现了总线驱动模型、设备树机制和字符设备接口的协同设计。本篇博文旨在全面系统地梳理 I2C 子系统的架构、注册流程、驱动模型、字符接口集成与调试方式,帮助你打通从设备树到驱动、从用户空间到内核空间的 I2C 工作机制理解闭环。


在这里插入图片描述

一、I2C 子系统的架构概览

Linux 的 I2C 子系统由以下三部分组成:

角色对应结构体描述
I2C 主控struct i2c_adapter表示一个 I2C 控制器,通常由 SoC 提供并注册
I2C 从设备struct i2c_client表示挂载在 I2C 总线上的一个外设
I2C 驱动struct i2c_driver对某类外设进行管理的驱动,实现 probe/remove

核心流程图:

Device Tree (.dts)
    └──▶ of_i2c_register_devices()
           └──▶ i2c_new_device()
                  └──▶ 匹配 i2c_driver(.of_match_table)
                         └──▶ 调用 probe()

这些流程涉及了 Linux 内核的设备模型(device model)中的总线 - 设备 - 驱动三元组概念。其中 i2c_client 是设备(device),i2c_driver 是驱动(driver),i2c_adapter 是总线(bus),三者之间的关联由 i2c-core 统一管理。


二、设备注册过程详解(以 Device Tree 为例)

在现代设备树体系中,I2C 外设的注册通常由如下方式实现:

&i2c1 {
    status = "okay";
    eeprom@50 {
        compatible = "atmel,24c02";
        reg = <0x50>;
        pagesize = <16>;
    };
};

上面配置中的 eeprom@50 节点会在 I2C 控制器驱动中被解析,过程如下:

  1. I2C 控制器驱动初始化时,调用 of_i2c_register_devices()
  2. 遍历子节点(如 eeprom@50),构建 i2c_client 实例
  3. 调用 i2c_new_device(),注册到 i2c-core
  4. i2c-core 会对所有已注册的 i2c_driver 进行匹配
  5. 匹配成功后,调用 probe()probe_new() 完成初始化

此流程关键在于设备树中 compatible 字段的匹配。


三、I2C 驱动模型特性

3.1 基本结构

static struct i2c_driver at24_driver = {
    .driver = {
        .name = "at24",
        .of_match_table = at24_of_match,
    },
    .probe_new = at24_probe,
    .remove = at24_remove,
};

驱动通过 i2c_add_driver() 注册,在模块加载时由 i2c-core 自动匹配并调用 probe。

3.2 probe_new vs probe

  • .probe() 是老接口,参数需手动转换为 struct i2c_client *
  • .probe_new() 是新接口,更类型安全,推荐使用

3.3 匹配方式

  • of_match_table:基于设备树的 compatible 字符串
  • id_table:适用于非 DT 场景,如平台代码中 i2c_register_board_info()

四、字符设备接口的集成

某些 I2C 设备,如 EEPROM,需要被用户空间直接访问,这时可以结合字符设备(cdev)提供接口。

static struct file_operations at24_fops = {
    .owner = THIS_MODULE,
    .read = at24_read,
    .write = at24_write,
    .llseek = default_llseek,
};

注册流程如下:

  1. alloc_chrdev_region() 分配主设备号
  2. cdev_init() + cdev_add() 注册设备
  3. class_create() + device_create() 生成 /dev/at24char

结合 i2c_smbus_*i2c_transfer 在 read/write 中读写 EEPROM 内容,实现完整的数据通路。


五、I2C 与字符设备的区别与协作

对比项I2C 子系统字符设备接口
本质设备模型中的“设备”用户接口模型中的“文件”
注册方式i2c_client + i2c_driverregister_chrdev + cdev_add
面向对象内核驱动(probe),与总线模型紧密集成面向用户空间,提供 read/write API
用户使用通过 sysfs/nvmem/自定义 ioctl 等接口直接通过 /dev/xxx 文件操作
典型应用温度传感器、电源芯片、EEPROMSPI Flash、摄像头等字节访问型设备

小结: I2C 子系统提供“设备管理”,字符设备提供“用户入口”,两者结合实现从内核到底层外设的完整交互。


六、调试与验证流程

6.1 内核侧调试

  • 查看 I2C 总线设备列表:

    ls /sys/class/i2c-adapter/
    
  • 检查设备注册过程:

    dmesg | grep i2c
    
  • 检查驱动匹配过程:

    dmesg | grep probe
    
  • 查看字符设备节点:

    ls -l /dev/at24char
    

6.2 用户态工具验证

  • 总线扫描:

    i2cdetect -y 2
    
  • 写入数据:

    i2cset -y 2 0x50 0x00 0x12
    
  • 读取数据:

    i2cget -y 2 0x50 0x00
    
  • 多字节读写建议结合小工具或字符驱动封装测试


七、十大关键问题深度剖析

1. 什么是 i2c_adapteri2c_client

i2c_adapter 表示主控接口,i2c_client 表示一个挂载在主控下的具体设备(如 EEPROM)。一个适配器可管理多个 client。

2. 设备如何从设备树注册进内核?

通过 of_i2c_register_devices() 递归解析设备树节点,将其转换为 i2c_client,随后与 i2c_driver 进行匹配。

3. I2C 驱动如何匹配设备?

依据设备的 compatible 字段,与驱动中 of_device_id 表中的字符串匹配,成功后调用 probe_new()

4. probe/probe_new 有何区别?

  • probe_new() 类型更严格,不需要手动转换为 i2c_client *
  • probe() 更兼容老内核,但已不推荐使用

5. 所有 I2C 设备都需要字符设备吗?

不是。部分设备通过 sysfs 或专有框架(如 PMIC、RTC)间接提供访问,不需要字符接口。

6. 如何在驱动中读写 I2C 外设?

常用 API:

  • i2c_smbus_read_byte_data() / i2c_smbus_write_byte_data()
  • i2c_transfer() 可组合多个 i2c_msg 进行复杂交互

7. 驱动中如何保存上下文?

使用 dev_set_drvdata(&client->dev, priv) 设置,后续可通过 dev_get_drvdata() 获取私有结构。

8. I2C 驱动与 platform 驱动的区别?

  • 前者由 i2c-core 驱动管理,面向挂载在 I2C 总线的设备
  • 后者由 platform_bus 管理,面向片上资源

9. probe 匹配失败怎么查?

  • 查看 dmesg
  • 确认设备树 compatible 是否写错
  • 驱动是否注册成功,of_match_table 是否生效

10. 字符设备如何对接 I2C 外设?

probe 中注册字符设备,read/write 中通过 i2c_smbus_read/write 与 I2C 外设交互,即可实现用户态读写


八、实践建议与总结

掌握 I2C 子系统的本质,是理解 Linux 驱动模型不可或缺的一环。建议从如下角度出发深入:

  1. 动手写一个 I2C + 字符设备混合驱动(如 at24)
  2. 调试一个真实硬件 I2C 外设,比如使用 i2c-tools 验证写入后再读取
  3. 通过 of_i2c_register_devices() 源码追踪理解 client 注册机制
  4. 对比 SPI/Platform 子系统,理解总线驱动模型的一致性

最终,你应能清晰回答:“设备是怎么被识别的?驱动是怎么匹配的?用户程序是怎么访问的?”


✅ 附加资源

如有需要,我可以继续补充以下内容:

  • 思维导图:概览 I2C 子系统的组成和注册流程
  • at24.c 驱动解析:逐函数讲解字符接口集成流程
  • FAQ 问答:归纳常见 I2C 调试陷阱

支持作者,点击京东购买《Yocto项目实战教程》


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值