以module的方式注册设备
为什么么要以module的方式注册设备
在前面的博客我记录了如何注册设备和如何注册驱动,我们可以明显的看出设备的注册是很麻烦的,需要修改平台文件,需要配置menuconfig,需要重新编译内核,需要将编译好的zImage烧录到开发板,而驱动的注册就简单多了,只要执行insmod就可以了,那么在学习中有没有什么简单的注册设备的方式呢?有的,设备也可以作为module的方式来注册,虽然说做产品的话绝大多数还是会使用修改平台文件的方式,但是以module的方式来注册设备可以大大简化我们的学习过程,不用频繁的烧录内核
注册设备
分析
我们先看一下平台文件中我们修改的那些变量是如何被调用的
我们当时修改了这个,但可以看出来它并不是一个语句,所以我们向上找
直到找到了这里,从名字看出来,这是一个所有设备组成的数组
我们再找找看哪里调用了这个数组
所以我们看一下这个函数执行了什么功能
int platform_add_devices(struct platform_device **devs, int num)
{
int i, ret = 0;
for (i = 0; i < num; i++) {
ret = platform_device_register(devs[i]);
if (ret) {
while (--i >= 0)
platform_device_unregister(devs[i]);
break;
}
}
return ret;
}
可以看出来,这个函数对数组中的每一个设备都执行了platform_device_register或者platform_device_unregister(这个取决于对应的宏定义是否为1),所以我们可以单独调用platform_device_register来注册设备
我们再来看一下platform_device_register的源码吧
int platform_device_register(struct platform_device *pdev)
{
device_initialize(&pdev->dev);
return platform_device_add(pdev);
}
代码实现
接下来的代码就实现了设备的注册
#include <linux/init.h>
#include <linux/module.h>
/*driver register*/
#include <linux/platform_device.h>
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("TOPEET");
/*回调函数*/
static void hello_release(struct device *dev)
{
printk("hello_release"); //打印hello_release
}
struct platform_device platform_device_hello = {
.name = "my_code_hello", //设备名
.id = -1, //ID
.dev = {
.release = hello_release,//释放所有设备时的回调函数,必须传递,否则释放的时候会报错
}
};
/*module入口函数,注册设备*/
static int hello_init(void)
{
platform_device_register(&platform_device_hello);
return 0;
}
/*module出口设备,卸载设备*/
static void hello_exit(void)
{
platform_device_unregister(&platform_device_hello);
}
module_init(hello_init);
module_exit(hello_exit);
在驱动代码中获取设备注册的信息
代码实现
#include <linux/init.h>
#include <linux/module.h>
/*driver register*/
#include <linux/platform_device.h>
#define DRIVER_NAME "my_code_hello"//注意一定要与设备名相同!!!!
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("TOPEET");
static int hello_probe (struct platform_device *pdv){
printk(KERN_EMERG "\tinitialized\n");
printk("pdv->name is %s\n",pdv->name);//打印name
printk("pdv->id is %d\n",pdv->id);//打印ID
pdv->dev->release(&pdv->dev);//执行release函数
return 0;
}
static int hello_remove (struct platform_device *pdv){
return 0;
}
static void hello_shutdown (struct platform_device *pdv){
}
static int hello_suspend (struct platform_device *pdv){
return 0;
}
static int hello_resume (struct platform_device *pdv){
return 0;
}
struct platform_driver hello_driver = {
.probe = hello_probe,
.remove = hello_remove,
.shutdown = hello_shutdown,
.suspend = hello_suspend,
.resume = hello_resume,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
}
};
static int hello_init(void)
{
int DriverState;
printk(KERN_EMERG "HELLO WORLD enter!\n");
DriverState=platform_driver_register(&hello_driver);
printk(KERN_EMERG "\t%d\n",DriverState);
return 0;
}
static void hello_exit(void)
{
printk(KERN_EMERG "HELLO WORLD exit!\n");
platform_driver_unregister(&hello_driver);
}
module_init(hello_init);
module_exit(hello_exit);
分析
我们在probe(初始化)函数中使用了platform_device 类型结构体中的变量,这个变量就是设备相关的,以参数传入了probe函数中。
当我们不注册设备时,驱动代码是不会执行probe的,所以说如果不注册设备,直接注册驱动,那么关于pdv变量的打印是不会执行的,当我们先注册设备在注册驱动,那么probe中的代码才会执行
代码测试
不注册设备
安装驱动
可以看出probe中的代码没有被执行
注册设备
首先注册设备
查看设备
说明设备注册成功
注册驱动
可以看出probe中的代码被执行了
卸载驱动并释放设备
可以看出在释放设备的时候又调用了release函数打印了hello_release
总结
- 除了最特殊的USB设备,其他的设备驱动,都徐亚驱动工程师注册设备,用module的方式注册设备只是为了方便学习和调试,在绝大多数情况下,都是以平台文件的方式统一在内核文件中来注册设备
- 驱动注册和设备注册都是将代码嵌入到Linux内核中,属于“对中”的部分(不是面向下层硬件,也没有对上层提供接口)
- 设备驱动中设备和驱动是分离的,无论是在平台文件中注册设备,还是以module的方式注册设备,设备都要优先驱动注册,否则驱动无法进入probe