之前介绍了misc杂项设备的注册(主设备号固定,次设备号上次写的自动)并自动生成设备节点,本文介绍申请自定义的主、次设备号。
了解内容
值得注意的是:注册杂项设备函数misc_register包含了注册设备和自动生成设备节点。
而本文介绍的仅是注册设备。
注册设备函数
字符设备注册函数在路径“include/linux/fs.h”下有定义,分别是:
register_chrdev_region(dev_t, unsigned, const char *);
根据主、次设备号注册设备:- dev_t类型参数是组合设备号;
- unsigned类型参数一般情况是次设备号个数,满了才会跳一个主设备号;
- const char *类型参数是设备名称;
alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);
:动态分配未使用的主、次设备号;- __register_chrdev() :不建议,早期linux使用(当时只有主设备号);
与上对应的取消注册函数为 unregister_chrdev_region(dev_t, unsigned);
注册设备的结构体和宏
设备号需要使用“dev_t”类型,其定义在“include/linux/cdev.h”文件中;
MKDEV(ma,mi)
宏定义用于组合主、次设备号,一般把结果作为注册设备函数的传参,其定义在“include/linlux/kdev_t.h”文件中。
开始
编写静态注册模块
主要变动点:
- 加入头文件:include/linux/cdev.h、include/linlux/kdev_t.h和include/linux/fs.h;
-
insmod传递主次设备号值:dev_major、dev_minor;
- 定义将申请的设备数量DEVICE_NUM(会优先递增次设备号);
- init时注册设备,exit时释放设备。
regdev_demo.c:
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
41
42
43
44
45
46
47
48
#include <linux/init.h>
#include <linux/module.h>
#include <linux/stat.h>
#include <linux/moduleparam.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Jerry");
static dev_t dev; //设备号
static int dev_major; //主设备号
static int dev_minor; //从设备号
#define DEVICE_NAME "dev_demo"
#define DEVICE_NUM 2 //连续分配的设备个数(优秀递增从设备号)
module_param(dev_major,int,S_IRUSR);
module_param(dev_minor,int,S_IRUSR);
static int demo_init(void){
int ret;
dev = MKDEV(dev_major,dev_minor);
if(dev_major == 0) //如果传参主设备号为0,这里选择不注册
{
printk(KERN_EMERG "Register dev error!\n");
return 0;
}
ret = register_chrdev_region(dev,DEVICE_NUM,DEVICE_NAME);
if(ret < 0){
unregister_chrdev_region(dev,DEVICE_NUM); //失败就释放
}
printk(KERN_EMERG "dev_major is : %d!\n",MAJOR(dev));
printk(KERN_EMERG "dev_minor is : %d!\n",MINOR(dev));
return 0;
}
static void demo_exit(void){
unregister_chrdev_region(dev,DEVICE_NUM);
return;
}
module_init(demo_init);
module_exit(demo_exit);
静态的传值验证
编写简单Makefile:参考这里
编译生成模块后,拷贝到开发板中:
-
然后先查看哪些主设备号未被注册,为下一步传值做准备
1
cat /proc/devices
-
上一步我发现主设备号248未被使用,故传值加载
-
加载后进行筛选设备名,可以看到248已经被注册为dev_demo
加入自动分配设备号
主要变动点:
-
使用了
alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);
函数,第一参数是:将被更新的设备号;第二参数是次设备号;第三参数是连续个数;第四参数是设备名;没有使用条件,这里我们在主设备号为0时使用它。
改进后的regdev_demo.c:
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
41
42
43
44
45
46
47
48
#include <linux/init.h>
#include <linux/module.h>
#include <linux/stat.h>
#include <linux/moduleparam.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Jerry");
static dev_t dev; //设备号
static int dev_major; //主设备号
static int dev_minor; //从设备号
#define DEVICE_NAME "dev_demo"
#define DEVICE_NUM 2 //连续分配的设备个数(优秀递增从设备号)
module_param(dev_major,int,S_IRUSR);
module_param(dev_minor,int,S_IRUSR);
static int demo_init(void){
int ret;
dev = MKDEV(dev_major,dev_minor);
if(dev_major == 0){ // 自动分配
ret = alloc_chrdev_region(&dev,dev_minor,DEVICE_NUM,DEVICE_NAME);
}
else{
ret = register_chrdev_region(dev,DEVICE_NUM,DEVICE_NAME);
}
if(ret < 0){
unregister_chrdev_region(dev,DEVICE_NUM); //失败就释放
}
printk(KERN_EMERG "dev_major is : %d!\n",MAJOR(dev));
printk(KERN_EMERG "dev_minor is : %d!\n",MINOR(dev));
return 0;
}
static void demo_exit(void){
unregister_chrdev_region(dev,DEVICE_NUM);
return;
}
module_init(demo_init);
module_exit(demo_exit);
自动分配的验证
编写简单Makefile:参考这里
编译生成模块后,拷贝到开发板中:
-
直接加载模块,不传值(初值为0)
-
加载后进行筛选设备名,可以看到248已经被注册为dev_demo