Linux_新版字符设备模型和杂项字符设备模型

1.新版本字符设备驱动模型

Linux字符设备驱动开发的基本步骤:字符设备驱动开发重点是使用register_chrdev函数注册字符设备,当不再使用设备的时候就使用unregister_chrdev函数注销字符设备。register_chrdev和unregister_chrdev这两个函数是老版本驱动使用的函数,现在新的字符设备驱动已经不再使用这两个函数,而是使用Linux内核推荐的新字符设备驱动API函数。

1.1.新旧字符设备驱动模型比较

不论是哪种版本的字符设备驱动模型,在编写字符设备驱动时都需要申请设备号以及设备注册(实现file_operations文件操作对象并加载到内核)。使用register_chrdev函数时,以上操作是一步到位,全部完成的。虽然使用register_chrdev在驱动程序注册的过程中变得简单了,但是却存在很大局限性,在申请一个主设备号的同时会将该主设备号下的所有次设备号都使用掉。比如在LED驱动程序中申请的主设备号为250,那么0~1048575(2^20-1)这个区间的次设备号就全部被LED一个设备分走了。这样太浪费次设备号了!一个LED设备肯定只能有一个主设备号,一个次设备号。
新版本字符设备驱动模型则会将申请设备号以及设备注册等操作分开执行,通过调用各自的API函数来完成。而且在申请设备号可以实现申请任意N个设备号(是连续的,主设备号一样,次设备号相邻),使用灵活。

1.2.申请设备号&&释放设备号

申请设备号有两种方法:静态申请设备号以及动态申请设备号。
如果给定了设备的主设备号和次设备号,可以使用静态申请设备号。
如果没有指定设备号,就可以使用动态申请设备号。

头文件:	#include <linux/fs.h>
函数原型:int register_chrdev_region(dev_t from, unsigned count, const char *name)
函数功能:静态申请一个指定的设备号范围
函数参数:from	指定起始设备号(+)
		 count	指定次设备号数量
		 name	设备驱动名字,可通过/proc/devices文件查看
函数返回值:成功返回0,失败返回负数
头文件:	#include <linux/fs.h>
函数原型:int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name)
函数功能:动态申请一个随机的设备号范围
函数参数:dev	存放申请到的设备号
		baseminor	指定申请的起始次设备号,一般写0
		count	指定申请次设备号的数量
		name	设备驱动名字,可通过/proc/devices文件查看
函数返回值:成功返回0,失败返回负数

很显然,不论使用哪个API函数,都需要在加载函数中调用。
设备驱动中申请设备号,如果申请N个,则应用程序可以通过这个N个设备号所对应的设备文件访问到设备驱动
不论使用哪个API函数申请的设备号,统一使用以下函数来释放设备号

头文件:	#include <linux/fs.h>
函数原型:void unregister_chrdev_region(dev_t from, unsigned count)
函数功能:释放一个设备号范围
函数参数:from	起始设备号(包含主次设备号)
		count	连续的此设备号数量
函数返回值:无

释放设备号的API函数需要在卸载函数中调用。

1.3.设备注册

1)为cdev分配空间
对于Linux新版本字符设备驱动模型,使用cdev结构体来表示一个字符设备,cdev结构体在include/linux/cdev.h文件中的定义如下:

struct cdev {
	struct kobject 	kobj;
	struct module 	*owner;			
	const struct 	file_operations *ops;	//设备文件操作方法
	struct list_head list;
	dev_t 			dev;			//驱动的初始完整设备号
	unsigned int 	count;			//驱动的次设备号的个数
};

为cdev分配空间有两种方法,第一种方法是直接定义cdev结构体变量;另外一种方法是调用cdev_alloc()函数来开辟一个cdev结构体空间。

示例:
struct cdev k;	//或者
struct cdev *p = cdev_alloc();

2)初始化cdev

头文件:	#include <linux/cdev.h>
函数原型:void cdev_init(struct cdev *cdev, const struct file_operations *fops);
函数功能:初始化cedv结构体
函数参数:cdev	要初始化的cdev结构体变量
		fops	字符设备文件操作函数集合
函数返回值:无

3)将cdev添加到内核

头文件:	#include <linux/cdev.h>
函数原型:int cdev_add(struct cdev *p, dev_t dev, unsigned int count);
函数功能:向内核添加字符设备(cdev结构体变量)
函数参数:p	指向要添加的字符设备(cdev结构体变量)
		dev	设备申请的设备号
		count	连续次设备号数量
函数返回值:成功返回0,失败返回负数

1.4.设备注销

头文件:	#include <linux/cdev.h>
函数原型:void cdev_del(struct cdev *p);
函数功能:删除字符设备
函数参数:要删除的字符设备
函数返回值:无

在卸载驱动时,需要使用cdev_del函数从Linux内核中删除相应的字符设备。
另外,如果在加载函数中使用了cdev_alloc函数来开配内存,那那块内存需要在卸载驱动时释放,否则会造成内存泄漏。

释放内存的函数原型为void kfree(void *p); 
所需头文件为#include <linux/slab.h>

1.5.新版本字符设备驱动编程步骤(格式)

模块加载函数:
1分配cdev结构空间: struct cdev led_cdev或者struct cdev *led_cdev = cdev_alloc()
2申请设备号: alloc_chrdev_region()或者register_chrdev_region()
3初始化cdev结构体: cdev_init()
4注册已经初始化好的cdev结构体: cdev_add(结构体,设备号,设备号数量)
模块卸载函数:
1注销cdev结构: cdev_del()
2释放设备号: unregister_chrdev_region(结构体)
3释放cdev结构空间:kfree()

1.6.提取次设备号

之前提到,应用程序通过访问设备文件,设备驱动程序中对应的接口函数会被执行。设备文件与设备驱动程序是通过主设备号进行关联。如果有N个设备文件,它们的主设备号相同,次设备号不同,则在驱动程序中怎么区分应用程序到底是访问哪个设备文件?
可以通过次设备号进行区分。提取次设备号的方法如下:
在这里插入图片描述

2.杂项设备驱动模型

2.1.杂项设备驱动模型概述

杂项设备驱动模型,是对字符设备的一种封装,是一种特殊的字符型设备驱动模型,也是在Linux嵌入式设备中使用的比较多的一种驱动模型。
本质上杂项设备驱动也是一个字符设备驱动,可能相对特殊一点而已。使用杂项设备驱动模型的字符设备其主设备号为10;使用杂项设备驱动模型会自动创建设备文件节点。
总的来讲,如果使用misc驱动可以满足要求的话,那么这可以为开发人员剩下不少麻烦。

2.2.杂项设备驱动模型优点

1)节省主设备号
使用普通字符设备,不管该驱动的主设备号是静态还是动态分配,都会消耗一个主设备号,这太浪费了。如果使用杂项设备驱动的话就好多了。因为内核中已经为杂项设备驱动分配了一个主设备号(10)。当系统中拥有多个杂项设备设备驱动时,那么它们的主设备号相同,而用子设备号来区分它们。
2)使用简单
当使用普通的字符设备驱动时,需要开发人员自己去注册字符驱动,并创建class以自动在/dev下生成设备节点,相对麻烦一点。而misc驱动则无需考虑这些,基本上只需要把一些基本信息通过struct miscdevice交给misc_register()去处理即可。

2.3.杂项设备的核心数据结构

对于杂项设备驱动模型,使用miscdevice结构体来表示一个杂项设备,miscdevice结构体在include/linux/miscdevice.h文件中的定义如下:

struct miscdevice {
	int minor;							//次设备号(0-255)  写255代表自动分配次设备号
	const char *name;			    	//设备文件名,会根据设备名自动创建设备节点
	const struct file_operations *fops;	//字符设备文件操作函数集合
	struct list_head list;            	//以下是内核使用,用户不需要关注
	struct device *parent;
	struct device *this_device;
	const char *nodename;
	umode_t mode;
};
上面结构中核心部分有三个成员:minor,name,fops。这三个成员必须由用户实现。

2.4.杂项设备注册函数

头文件:	#include <linux/miscdevice.h>
函数原型:int misc_register(struct miscdevice  *misc);
函数功能:向内核注册一个杂项设备
函数参数:杂项设备对象
函数返回值:成功返回0,失败返回负数

2.5.杂项设备注销函数

头文件:	#include <linux/miscdevice.h>
函数原型:int misc_deregister(struct miscdevice *misc);
函数功能:从内核删除一个杂项设备
函数参数:杂项设备对象
函数返回值:成功返回0,失败返回负数

2.6.杂项设备模型驱动编程步骤

1)定义并初始化杂项设备对象: struct miscdevice misc_device;
2)在驱动加载函数中注册杂项设备: misc_register
3)在驱动卸载函数中注销杂项设备: misc_deregister

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值