RK3588 kernel-6.1/drivers/i2c/i2c-dev.c 功能详细分析
1. 文档范围与说明
路径说明: 用户表述为「i2c-dev.c 目录」,实际为 单个源文件:
rk3588/kernel-6.1/drivers/i2c/i2c-dev.c(约 782 行)
编译关系: 可选模块,由 CONFIG_I2C_CHARDEV 控制:
1 | obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o |
Rockchip RK3588 产品 defconfig 中通常为 CONFIG_I2C_CHARDEV=y(如 rockchip_linux_defconfig),即 内置 i2c-dev.ko 或编进内核,用户空间可通过 /dev/i2c-N 访问各条 I2C 总线。
文档保存路径: linuxDoc/drivers/i2c/
定位: 为每条已注册的 i2c_adapter 创建字符设备节点,使用户态程序(i2c-tools、libi2c、自研工具)通过 read/write/ioctl 在该总线上与任意从地址通信,不经过 内核中已绑定的 i2c_driver(除非地址冲突检查失败)。
与内核驱动的关系: i2c-dev 打开的是 匿名 i2c_client(未注册到 driver core),与 DT 枚举的正式 client 并行存在;对已被内核驱动占用的地址,I2C_SLAVE 会返回 -EBUSY(I2C_SLAVE_FORCE 可强制覆盖,有破坏内核驱动风险)。
2. 功能总览
| 模块 | 主要符号 | 作用 |
|---|---|---|
| 设备管理 | struct i2c_dev、链表 |
每个 adapter 对应一个 cdev + sysfs 设备 |
| 总线通知 | i2cdev_notifier |
adapter 增删时创建/销毁 /dev/i2c-N |
| 打开/关闭 | i2cdev_open / release |
绑定 adapter、分配匿名 client |
| 读写 | i2cdev_read / write |
需先 I2C_SLAVE,调用 i2c_master_recv/send |
| I2C_RDWR | i2cdev_ioctl_rdwr |
用户态 i2c_msg 批量传输 → i2c_transfer |
| I2C_SMBUS | i2cdev_ioctl_smbus |
SMBus ioctl → i2c_smbus_xfer |
| 其它 ioctl | i2cdev_ioctl |
SLAVE、FUNCS、PEC、RETRIES、TIMEOUT 等 |
| 32 位兼容 | compat_i2cdev_ioctl |
CONFIG_COMPAT 下 arm32 用户态 |
| 模块初始化 | i2c_dev_init / exit |
主设备号 89、class、notifier |
3. 架构与数据流
1 | 用户空间 (i2cget / i2cset / 自定义程序) |
设备节点:
- 主设备号
I2C_MAJOR = 89(include/linux/i2c-dev.h) - 次设备号 =
adap->nr(与i2c-2总线号一致) - 路径:
/dev/i2c-0…/dev/i2c-8(RK3588 最多 9 路内置 I2C,视 DTS 启用情况而定) - sysfs class:
i2c-dev,设备下可读name属性(adapter 名称)
4. 核心数据结构
4.1 struct i2c_dev
1 | struct i2c_dev { |
全局链表 i2c_dev_list + 自旋锁 i2c_dev_list_lock,按 adap->nr 查找。
4.2 打开文件后的 file->private_data
指向 堆上分配的匿名 struct i2c_client:
client->adapter= 打开的 adapter;client->addr= 由I2C_SLAVE/I2C_SLAVE_FORCE设置;client->flags=I2C_M_TEN、I2C_CLIENT_PEC等可由 ioctl 修改;- 未 调用
i2c_new_client_device(),不出现在/sys/bus/i2c/devices/的正式 client 列表中(名称为"i2c-dev N"仅内部使用)。
5. Adapter 注册与 /dev 节点生命周期
5.1 i2c_dev_init()
register_chrdev_region(MKDEV(89, 0), I2C_MINORS, "i2c");class_create(..., "i2c-dev");bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);i2c_for_each_dev(NULL, i2cdev_attach_adapter)— 绑定 已存在 的 adapter。
5.2 i2cdev_attach_adapter()
当 i2c_bus_type 上新增 adapter 类型 设备时:
1 | get_free_i2c_dev(adap) |
5.3 i2cdev_detach_adapter()
adapter 删除时 cdev_device_del,从链表移除并释放 i2c_dev。
与 RK3588: i2c-rk3x probe 成功并 i2c_add_numbered_adapter() 后,自动出现对应 /dev/i2c-N(若 CONFIG_I2C_CHARDEV=y)。
6. 文件操作
6.1 read() / write()
| 项目 | 说明 |
|---|---|
| 前提 | 必须先 ioctl(I2C_SLAVE, addr) 设置从机地址 |
| 实现 | i2c_master_recv / i2c_master_send(内部 i2c_transfer) |
| 长度上限 | 单次最多 8192 字节 |
| 缓冲区 | 内核 kmalloc / memdup_user,再拷贝到用户空间 |
适用于简单连续读写的用户态工具;复杂事务应用 I2C_RDWR。
6.2 ioctl() 命令一览
定义于 include/uapi/linux/i2c-dev.h:
| ioctl | 含义 |
|---|---|
I2C_SLAVE (0x0703) |
设置 7/10 位从地址;若已有 带驱动 的正式 client 占址 → -EBUSY |
I2C_SLAVE_FORCE (0x0706) |
强制设置地址,不检查 是否已有驱动 |
I2C_TENBIT (0x0704) |
启用 10 位地址(UAPI 注释称历史代码对 10 位支持不完整) |
I2C_FUNCS (0x0705) |
返回 i2c_get_functionality(adapter) 功能掩码 |
I2C_RDWR (0x0707) |
提交 i2c_rdwr_ioctl_data(最多 42 条 msg) |
I2C_PEC (0x0708) |
匿名 client 的 PEC 标志(不影响 已注册内核 client) |
I2C_SMBUS (0x0720) |
i2c_smbus_ioctl_data → i2c_smbus_xfer |
I2C_RETRIES (0x0701) |
修改 adapter->retries(影响整条总线) |
I2C_TIMEOUT (0x0702) |
修改 adapter->timeout(单位 10ms,转 jiffies) |
7. I2C_RDWR:i2cdev_ioctl_rdwr()
7.1 流程
- 从用户空间复制
struct i2c_msg数组(memdup_array_user); - 每条消息:
len ≤ 8192,buf用memdup_user拷入内核,置I2C_M_DMA_SAFE; - 若
I2C_M_RECV_LEN:校验读方向与buf[0]长度,并设置msgs[i].len = buf[0]; i2c_transfer(client->adapter, msgs, nmsgs)— 注意:使用 adapter,msg 内自带addr,不依赖I2C_SLAVE设置的client->addr;- 读消息:
copy_to_user回传数据;释放内核缓冲区。
7.2 与 I2C_SLAVE 的关系
注释说明:打开后即可用 I2C_RDWR 向任意地址发消息(地址在每条 i2c_msg 中)。read/write 与 I2C_SMBUS 则需要先 I2C_SLAVE。
7.3 RK3588
最终进入 rk3x_i2c_xfer(),支持组合读写(与 i2c_transfer 路径一致)。
8. I2C_SMBUS:i2cdev_ioctl_smbus()
- 校验
size、read_write合法; - Quick / Send Byte 等无 data 指针情况直接
i2c_smbus_xfer(..., NULL); - 其它从用户空间复制
union i2c_smbus_data,调用i2c_smbus_xfer,读操作再copy_to_user; - 支持
I2C_SMBUS_I2C_BLOCK_BROKEN兼容旧 ABI。
用户态 i2c_smbus_* 库函数通常封装此 ioctl。RK3588 上经 i2c_smbus_xfer_emulated 模拟(见 i2c-core-smbus.c 文档)。
9. 地址占用检查:i2cdev_check_addr()
与 i2c-core-base.c 中 i2c_check_addr_busy() 的差异(文件注释):
| 检查逻辑 | i2c-core | i2c-dev |
|---|---|---|
| 已有 client + 已绑定 driver | 忙 | 忙(I2C_SLAVE 失败) |
| 已有 client + 无 driver | 忙 | 不忙(允许用户态访问) |
并 递归检查 MUX 父子 adapter 上的同名地址(i2cdev_check_mux_parents / _children)。
I2C_SLAVE_FORCE: 跳过 BUSY 检查,可能导致与内核驱动 同时访问同一从设备,仅调试时使用。
10. 与内核 I2C 子系统其它部分的关系
| 组件 | 关系 |
|---|---|
i2c-core-base.c |
i2c_transfer、i2c_get_adapter、i2c_master_send/recv |
i2c-core-smbus.c |
i2c_smbus_xfer |
i2c-core-of.c |
DT 创建的正式 client;与 i2c-dev 匿名 client 独立 |
i2c-rk3x.c |
实际硬件传输 |
adapter sysfs new_device |
另一条 用户态创建设备路径(创建 正式 client) |
生产环境建议: 正常外设由 内核驱动 + DT 管理;/dev/i2c-* 主要用于 调试、校准、产测、无驱动芯片的临时访问。
11. RK3588 使用说明
11.1 配置
1 | CONFIG_I2C_CHARDEV=y |
11.2 用户空间示例
1 | # 列出总线 |
总线号 N 与 DTS aliases { i2cN = &i2cN; } 及 i2c_add_numbered_adapter 的 adap->nr 一致。
11.3 与板级外设共存
例如 rk3588s-tablet.dtsi 中 &i2c2 上的 hym8563@51 已被 hym8563 驱动 占用时:
ioctl(I2C_SLAVE, 0x51)→-EBUSY;I2C_SLAVE_FORCE可强制访问,但可能与 RTC 驱动冲突,导致系统异常。
11.4 权限
设备节点通常属 root:i2c,chmod 666 或由 udev 规则赋权;嵌入式系统常在 init 脚本中 chmod 以便应用访问。
12. 安全与限制
| 限制 | 说明 |
|---|---|
| 单次传输 | read/write/ioctl 单条 msg ≤ 8192 字节 |
I2C_RDWR 条数 |
≤ 42(I2C_RDWR_IOCTL_MAX_MSGS) |
| 次设备号 | adap->nr < I2C_MINORS(通常 256) |
| 10 位地址 | UAPI 注明历史实现问题,慎用 |
| 修改 retries/timeout | 影响 整条 adapter,非单文件描述符私有 |
用户态对 /dev/i2c-* 的访问等价于 root 级硬件控制,产线工具需防止误写 PMIC 等关键器件。
13. 模块初始化与卸载
1 | module_init(i2c_dev_init); |
- 可
=y内置 或=m模块i2c-dev.ko; - 卸载模块会注销 notifier 并删除所有 i2c-dev 字符设备(需无打开的文件描述符)。
14. 源码结构速查(约 782 行)
1 | L42-96 i2c_dev 链表管理 |
15. 结论
i2c-dev.c 实现 I2C 子系统的 用户空间字符设备接口:
- 每条
i2c_adapter对应/dev/i2c-N(主设备号 89); - 通过 匿名 client 提供
I2C_SLAVE+ read/write 及I2C_RDWR/I2C_SMBUS; - 底层统一落到
i2c_transfer/i2c_smbus_xfer,RK3588 上即 rk3x 硬件驱动; - 与 DT 内核驱动 并行,地址冲突时
I2C_SLAVE保护已绑定设备; - Rockchip 产品内核 默认启用
CONFIG_I2C_CHARDEV,调试与工具链普遍依赖本模块。
理解本文件有助于分析 用户态能否访问某 I2C 地址、i2cdetect 与内核驱动冲突、以及 产测程序直接读写总线 的实现路径。
16. 推荐阅读顺序
i2c-dev.c—i2cdev_ioctl、i2cdev_openinclude/uapi/linux/i2c-dev.h— ioctl 编号与结构体Documentation/i2c/dev-interface.rst— 官方用户接口说明i2c-core-base.c—i2c_transfer、i2c_get_adapteri2c-core-smbus.c— SMBus ioctl 底层
17. 相关文档
rk3588-kernel-6.1-i2c-core-base.c功能详细分析_zh.mdrk3588-kernel-6.1-i2c-core-smbus.c功能详细分析_zh.mdrk3588-kernel-6.1-i2c-core-of.c功能详细分析_zh.mdrk3588-kernel-6.1-drivers-i2c-busses目录功能详细分析_zh.mdrk3588-kernel-6.1-drivers-i2c目录功能详细分析_zh.md
文档版本:基于 rk3588/kernel-6.1 源码树 drivers/i2c/i2c-dev.c 分析。
正在加载留言…