| title | AP3216C I2C 驱动教程 |
|---|
本教程基于以下内核版本,两套内核的 I2C 设备驱动公共 API 完全一致,写出来的 .ko 在两边都能直接跑:
- linux-imx 6.12.49
- mainline 7.1.0
源码就躺在仓库的 third_party/linux-imx 与 third_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。
我们按"先理框架、再写代码、最后上板"的顺序推进。
- 02_i2c_framework - I2C 驱动框架:适配器、设备、驱动是怎么扣在一起的
- 03_i2c_adapter_analysis - I.MX6U 的 I2C 适配器驱动
i2c-imx.c拆解
- 04_driver_layer - AP3216C 驱动层:寄存器读写、
probe/remove、字符设备 - 05_device_tree - 设备树配置:把硬件画进内核的地图
- 06_build_and_test - 编译、上板、拿真实数据
::: tip 学习目标
搞懂 Linux I2C 驱动"总线驱动 / 设备驱动"的分层契约,能用 module_i2c_driver() + 单参数 probe + void remove 写出符合 6.12 / 7.1 规范的设备驱动,并通过 i2c_transfer / i2c_smbus_* 完成寄存器读写,最终把 AP3216C 的 IR / ALS / PS 三路真实数据读到用户空间。
:::
::: info 前置知识
- 字符设备驱动基础(
file_operations、cdev、class_create) - 设备树基本语法
- Platform 驱动模型(03_platform_led_driver 是很好的前置) :::
::: details 延伸阅读
- Linux I2C 子系统文档
- instantiating-i2c-devices
- AP3216C 数据手册(Lite-On) :::
A: 那是给"非设备树时代"传匹配 ID 用的。现代驱动靠设备树 compatible 取数据,内核早在几个版本前就把这个参数砍了,新内核里 i2c_driver.probe 的签名就是 int (*probe)(struct i2c_client *client),照老写法写会触发指针类型不匹配警告。
A: I2C 子系统的 remove 回调在 6.x 已经改成返回 void。你再写 int 返回类型并 return 0,赋给 i2c_driver.remove 时类型对不上,编译器会警告,而且这个返回值内核本来也不看。
A: 纯设备树匹配的驱动可以不写——只要 of_match_table 能命中,id_table 永远轮不到被查询。留着无害,但属于历史包袱。