4412开发板学习之Linux驱动开发(三):以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
发布了123 篇原创文章 · 获赞 598 · 访问量 34万+

猜你喜欢

转载自blog.csdn.net/a568713197/article/details/89646242