之前写过一篇关于如何在Linux内核模块中注册操作I2C设备的文章 ,那篇文章最后介绍的方法虽然可行,但是会带来一个问题:如果内核中已经包含有某设备的驱动时,那么在模块中注册该设备的I2C client之前必须先将内核中的驱动进行反注册解挂,然后才能再次注册模块中定义的驱动。这样做带来的问题就是,当你将模块从内核中卸载后,系统将无法再次注册内核中原有的驱动,导致相应设备无法使用。今天补充的方法可以在挂载模块时使用模块内的设备驱动,而在卸载后恢复回系统原来的驱动。
内容相当简单,上次我们已经可以通过内核提供的接口函数,找到相应I2C总线相应地址I2C设备的I2C client结构指针。而拥有该指针后,其实就可以做很多事了。比如调用 i2c_master_send
接口向该client指向的设备发送I2C命令。这样,如果需要扩展内核中原有的驱动程序,比如向procfs或sysfs中添加相应的用户空间接口等。一般可以在 module_init
中注册sysfs入口的操作函数,然后在操作函数中通过操作该client指针而实现一定的功能。这种方法可以沿用系统内核中原有的设备驱动,可以单纯添加一些系统驱动中没有的功能。
除此之外,还有一种替换内核中现有驱动的方法。通过查阅源代码,可以发现内核中还提供一个 device_reprobe(dev)
的API,该函数接受一个device结构体指针,实现重新匹配设备驱动的操作。同时,I2C client结构体中也有相应的device结构体。我们知道Linux内核匹配I2C设备驱动是通过名称来进行匹配的,所以,我们的方法就是用Hack的方式将系统中获取到的I2C Client结构体的名称改为我们需要的名称。一般修改为我们模块中新建的驱动的名称,这样,当调用 device_reprobe
接口后,系统会将原有驱动remove并重新为相应I2C设备适配一个驱动程序。当然,没有出错的话,它会适配到我们修改的名称指向的驱动。如此,我们便可以在内核模块中编写独立的设备驱动程序了。以下是简单示例框架:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
|
my_probe
, my_remove
等函数即可实现一个完整的驱动。需要注意的是一定要在调用 device_reprobe
接口之前将相应的设备驱动使用 i2c_add_driver
添加到系统中,否则重新适配中会找不到驱动。移除模块时,用同样的方法将I2C client的名称更改为系统中原有驱动的名称,并重新适配驱动,即可实现卸载模块后系统能够使用原有驱动的功能。示例如下:
1 2 3 4 5 6 7 8 9 10 |
|