新字符设备驱动
以前我们注册字符设备需要指定设备号,现在我们采用新字符设备驱动注册流程让linux内核自动申请设备号。
1、新字符设备驱动原理
1、以前我们使用register_chrdev函数注册字符设备,不再使用设备的时候使用unregister_chrdev函数注销字符设备。
2、新字符设备我们使用alloc_chrdev_region、register_chrdev_region和unregister_chrdev_region函数来完成字符设备的注册。
2、分配和释放设备号方法
1、之前使用register_chrdev函数时需要给定一个主设备号,但这样会有两个问题:
① 需要我们事先确定哪些主设备号没被用
② 会将改主设备号下的所有次设备号全部使用掉,造成浪费。
2、解决以上两个问题最好的办法就是要是使用设备号的时候让Linux内核自己申请,它自己肯定哪些设备没有被用,自己分配可以使用的设备号。
3、如果没有指定设备号的就使用如下函数来申请设备号:
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
4、如果给的了设备的主设备号和次设备号就使用如下函数来完成注册设备
int register_chrdev_region(dev_t from, unsigned count, const char *name)
参数:
① from:要申请的设备号;
② count:要申请的个数,一般都是1;
③ name:设备的名字
5、注销字符设备之后要释放掉设备号使用如下函数
void unregister_chrdev_region(dev_t from, unsigned count)
2.1、申请设备号代码案例
int major; /* 主设备号 */
int minor; /* 次设备号 */
dev_t devid; /* 设备号 */
if (major) {
/* 定义了主设备号 */
devid = MKDEV(major, 0); /* 大部分驱动次设备号都选择 0 */
register_chrdev_region(devid, 1, "test");
} else {
/* 没有定义设备号 */
alloc_chrdev_region(&devid, 0, 1, "test"); /* 申请设备号 */
major = MAJOR(devid); /* 获取分配号的主设备号 */
minor = MINOR(devid); /* 获取分配号的次设备号 */
}
总结,新字符分配设备号有两种方法:
① 如果指定设备号就是要register_chrdev_region
函数;
②如果没指定设备号就是要alloc_chrdev_region
函数去申请设备号,申请后保存在devid内;
③不管使用哪种方法,最后注销设备号都是使用unregister_chrdev_region
函数。
3、字符设备注册方法
3.1、字符设备结构
1、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;
} ;
2、cdev中有两个重要的成员变量:ops和dev,一个是字符设备文件操作函数集合,一个是设备号。
3、编写字符设备驱动之前需要先定义一个cdev结构体变量:struct cdev test_cdev;
3.2、cdev_init函数
1、定义好cdev变量后要使用cdev_init函数对其进行初始化,该函数原型如下:
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
2、参数:显而易见,这里不介绍。
3.3、cdev_add函数
1、cdev_add函数用于向Linux系统添加字符设备,之前需要先使用cdev_init。cdev_add函数原型如下:
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
2、三个参数:cdev结构体变量;设备号;要添加的设备数量。
3.4、注册字符设备代码案例
struct cdev testcdev;
/* 设备操作函数 */
static struct file_operations test_fops = {
.owner = THIS_MODULE,
/* 其他具体的初始项 */
};
testcdev.owner = THIS_MODULE;
cdev_init(&testcdev, &test_fops); /* 初始化 cdev 结构体变量 */
cdev_add(&testcdev, devid, 1); /* 添加字符设备 */
3.5、cdev_del函数
卸载驱动时要是有cdev_del函数删除相应的字符设备,原型如下:
void cdev_del(struct cdev *p)
4、自动创建设备节点
1、之前的驱动实验,我们需要实验mknod手动创建设备节点。本节我们实现自动创建设备节点,加载驱动后会自动在dev目录下生成对应的设备文件。
2、这里介绍一下mdev,Linux是通过mdev来实现设备文件节点的自动创建与删除。Linux下的热插拔也是由mdev来管理。
4.1、创建和删除类
1、自动创建设备节点是在驱动入口函数中完成,一般在cdev_add函数后面添。
2、首先要创建一个class类,class是个结构体,定义在include/linux/device.h
。使用class_create函数创建类,class_create是个宏定义,内容如下:
#define class_create(owner, name)
({
static struct lock_class_key __key;
__class_create(owner, name