Skip to content

版本说明

本教程基于以下内核版本,两套内核的 I2C 设备驱动公共 API 完全一致,写出来的 .ko 在两边都能直接跑:

  • linux-imx 6.12.49 推荐
  • mainline 7.1.0 进阶

源码就躺在仓库的 third_party/linux-imxthird_party/linux_mainline 下,文中所有结构体定义、函数签名都对着这两棵树核对过,可以随时翻。

这一篇要解决什么问题

网上流传的 I2C 驱动教程,绝大多数还停在 4.1.15 那套老写法上:probe 拖着个 const struct i2c_device_id *id 参数,remove 还在老老实实 return 0,连 class_create 都要你手写一个 THIS_MODULE。这些代码原封不动抄到 6.12 / 7.1 上,要么直接编不过(class_create 从 6.4 起只剩一个参数),要么满屏 incompatible pointer type 警告。所以这一篇我们干脆把 AP3216C 的驱动整个推倒重写——从设备树到驱动主体再到测试程序,一行老代码都不照抄,全程用现代内核的 API。

学习路径

我们按"先理框架、再写代码、最后上板"的顺序推进。

🎯 推荐学习路径

阶段一:框架理解

  1. 02_i2c_framework - I2C 驱动框架:适配器、设备、驱动是怎么扣在一起的
  2. 03_i2c_adapter_analysis - I.MX6U 的 I2C 适配器驱动 i2c-imx.c 拆解

阶段二:驱动实现

  1. 04_driver_layer - AP3216C 驱动层:寄存器读写、probe/remove、字符设备
  2. 05_device_tree - 设备树配置:把硬件画进内核的地图

阶段三:实战验证

  1. 06_build_and_test - 编译、上板、拿真实数据

章节目录

学习目标

搞懂 Linux I2C 驱动"总线驱动 / 设备驱动"的分层契约,能用 module_i2c_driver() + 单参数 probe + void remove 写出符合 6.12 / 7.1 规范的设备驱动,并通过 i2c_transfer / i2c_smbus_* 完成寄存器读写,最终把 AP3216C 的 IR / ALS / PS 三路真实数据读到用户空间。

前置知识

  • 字符设备驱动基础(file_operationscdevclass_create
  • 设备树基本语法
  • Platform 驱动模型(03_platform_led_driver 是很好的前置)
延伸阅读

常见问题

Q: 为什么 probe 只有一个参数了,老教程里还有个 id

A: 那是给"非设备树时代"传匹配 ID 用的。现代驱动靠设备树 compatible 取数据,内核早在几个版本前就把这个参数砍了,新内核里 i2c_driver.probe 的签名就是 int (*probe)(struct i2c_client *client),照老写法写会触发指针类型不匹配警告。

Q: remove 为什么不能 return 0

A: I2C 子系统的 remove 回调在 6.x 已经改成返回 void。你再写 int 返回类型并 return 0,赋给 i2c_driver.remove 时类型对不上,编译器会警告,而且这个返回值内核本来也不看。

Q: id_table 还要不要写?

A: 纯设备树匹配的驱动可以不写——只要 of_match_table 能命中,id_table 永远轮不到被查询。留着无害,但属于历史包袱。

继续学习

Built with VitePress