提示:Linux驱动-i2c 驱动框架编写
文章目录
- 前言
- 一、参考资料
- 二、相关文件
- 三、驱动框架编写
- 1、设备树-client 端配置
- 2、i2c 驱动框架
- 框架编码
- 对比平台总线-i2c_add_driver
- 3、i2c 驱动框架实验
- 四、知识点扩展
- 驱动层和client 端匹配机制
- probe 函数对应的 of_device_id 基本规则
- MODULE_DEVICE_TABLE
- 查错方法
- ls /sys/bus/i2c/drivers/
- ls /sys/bus/i2c/devices/
- 驱动缺失 MODULE_DEVICE_TABLE
- 总结
前言
i2C 驱动框架编写,驱动框架学习法,先写一个框架。 既然写框架 基于之前设备树已经配置了,那么写一个基本的框架和设备树节点匹配上。
一、参考资料
本篇内容关联的资料如下:
驱动-I2C-客户端代码编写-编写设备树
驱动-设备树和驱动自动匹配
RK3568驱动指南|第七期-设备树-第65章 设备树下platform_device和platform_driver匹配实验
驱动-平台总线-probe
二、相关文件
kernel/include/linux/i2c.h kernel/drivers/i2c/i2c-core-base.c对应i2c 注册的一些方法,需要了解、熟悉的。
三、驱动框架编写
1、设备树-client 端配置
如之前 驱动-I2C-客户端代码编写-编写设备树 配置,设备树配置如下:
&i2c1{status="okay";myft5x06:my-ft5x06@38{compatible="my-ft5x06";reg=<0x38>;};};这里最重要的不就是compatible属性的值嘛
2、i2c 驱动框架
框架编码
框架编码如下
#include<linux/init.h>#include<linux/module.h>#include<linux/i2c.h>#include<linux/of_device.h>intft5x06_probe(struct i2c_client*client,conststruct i2c_device_id*id){printk(KERN_INFO"###### This is ft5x06 probe ######\n");return0;}intft5x06_remove(struct i2c_client*client){printk(KERN_INFO"###### This is ft5x06_remove ######\n");return0;}staticconststruct of_device_id ft5x06_id[]={{.compatible="my-ft5x06"},{/* Sentinel */},};// 关键补充MODULE_DEVICE_TABLE(of,ft5x06_id);staticstruct i2c_driver ft5x06_driver={.driver={.owner=THIS_MODULE,.name="my-ft5x06",.of_match_table=ft5x06_id,},.probe=ft5x06_probe,.remove=ft5x06_remove,};staticint__initft5x06_driver_init(void){intret;ret=i2c_add_driver(&ft5x06_driver);if(ret<0){printk(KERN_ERR"i2c_add_driver failed ret=%d\n",ret);returnret;}printk(KERN_INFO"###### ft5x06 driver register success ######\n");return0;}staticvoid__exitft5x06_driver_exit(void){i2c_del_driver(&ft5x06_driver);printk(KERN_INFO"###### ft5x06 driver unregister ######\n");}module_init(ft5x06_driver_init);module_exit(ft5x06_driver_exit);MODULE_LICENSE("GPL");对比平台总线-i2c_add_driver
注册平台总线用了方法:i2c_add_driver
看定义地方,路径:kernel/include/linux/i2c.h
externinti2c_register_driver(structmodule*,struct i2c_driver*);externvoidi2c_del_driver(struct i2c_driver*);/* use a define to avoid include chaining to get THIS_MODULE */#definei2c_add_driver(driver)\i2c_register_driver(THIS_MODULE,driver)看这个方法实现地方,路径:kernel/drivers/i2c/i2c-core-base.c
/* * An i2c_driver is used with one or more i2c_client (device) nodes to access * i2c slave chips, on a bus instance associated with some i2c_adapter. */inti2c_register_driver(structmodule*owner,struct i2c_driver*driver){intres;/* Can't register until after driver model init */if(WARN_ON(!is_registered))return-EAGAIN;/* add the driver to the list of i2c drivers in the driver core */driver->driver.owner=owner;driver->driver.bus=&i2c_bus_type;INIT_LIST_HEAD(&driver->clients);/* When registration returns, the driver core * will have called probe() for all matching-but-unbound devices. */res=driver_register(&driver->driver);if(res)returnres;pr_debug("driver [%s] registered\n",driver->driver.name);/* Walk the adapters that are already present */i2c_for_each_dev(driver,__process_new_driver);return0;}看到了平台总线上面最基本的驱动注册方法:res = driver_register(&driver->driver);, 说明i2c 模块本身也是遵守平台总线机制。
3、i2c 驱动框架实验
加载驱动:insmod ft5x06_driver.ko,然后看串口打印日志如下:
其实就是回调了probe函数
四、知识点扩展
驱动层和client 端匹配机制
这个是之前的知识点,但是务必掌握,之前的参考资料:
驱动-设备树和驱动自动匹配
RK3568驱动指南|第七期-设备树-第65章 设备树下platform_device和platform_driver匹配实验
驱动-平台总线-probe
也就是说
probe方法的回调机制是怎样的,它一定是驱动层和client 相互之间匹配成功才会回调的。- 那么如何匹配的呢? 通过
of_match_table函数定义规则,匹配client 端的compatible属性
probe 函数对应的 of_device_id 基本规则
如下:.probe = ft5x06_probe,函数定义
staticconststruct of_device_id ft5x06_id[]={{.compatible="my-ft5x06"},{/* Sentinel */},};第二个参数为什么是一个 空的花括号:{ /* Sentinel */ }, 因为这个就是语法的结束标志,所以一定要有一个花括号{ }。
MODULE_DEVICE_TABLE
如下定义了probe回调方法之后,执行了一个宏定义
staticconststruct of_device_id ft5x06_id[]={{.compatible="my-ft5x06"},{/* Sentinel */},};// 关键补充MODULE_DEVICE_TABLE(of,ft5x06_id);为什么要了解这个,实际测试验证发现 没有这个居然无法匹配client端成功,怎么都不成功,i2c驱动也注册不成功。
后面我查了一下实战项目里面,公司项目里面 还都有这个宏定义添加的,如下:
查错方法
如上对于宏定义MODULE_DEVICE_TABLE没有添加导致的问题 还有其它实战项目中经常遇到的问题,这里给出一些查错方法。
ls /sys/bus/i2c/drivers/
命令:ls /sys/bus/i2c/drivers/就是查节点有木有,没有的话就是注册驱动失败了。
列表里必须出现 my-ft5x06
- ✅ 存在:驱动注册成功,等待匹配设备;
- ❌ 不存在:i2c_add_driver 内部隐性失败(极少,我们日志没有报错,可以暂时排除)`
ls /sys/bus/i2c/devices/
命令:ls /sys/bus/i2c/devices/,就是查地址,地址都没有说明设备树有问题。
正常应该看到 1-0038
- 没有 1-0038 → dts 节点非法 / 冲突,内核没有创建 i2c_client;
- 存在 1-0038,insmod 依然不进 probe → 匹配表问题(MODULE_DEVICE_TABLE)。
驱动缺失 MODULE_DEVICE_TABLE
staticconststruct of_device_id ft5x06_id[]={{.compatible="my-ft5x06"},{},};// 必须添加!!MODULE_DEVICE_TABLE(of,ft5x06_id);没有这条宏,匹配表不会导出到模块信息,of_driver_match_device匹配失败,不会进入probe。
总结
- 这里就是i2c 驱动框架的一个总结、整理,对于遇到的问题也要有自己的解决问题思路。
- i2c 框架理解,其实也就是一套平台总线的封装。