声明
以下都是我刚开始看驱动视频的个人强行解读,如果有误请指出,共同进步。
本节目标
静态方式申请主次设备号
申请主次设备号的函数(本节讲静态,下节动态)
我们打开 include/linux/fs.h 头文件可以看到以下三个函数(具体参数含义后面再说)
当然,写的时候要包含头文件 linux/fs.h
// 动态申请主次设备号(linux分配)
extern int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);
// 静态申请主次设备号(自己指定主次设备号是多少)
extern int register_chrdev_region(dev_t, unsigned, const char *);
// 已被上面俩取代,这个过时了...
extern int __register_chrdev(太多懒得抄);
数据类型
就像文件IO操作有个fd一样,注册设备号也有一个类似的东西
申请主次设备号,必须要用dev_t(其实就是int)来作为数据类型。
其头文件为 linux/cdev.h
处理的宏定义
主次设备号一共32位,高12位用来表示主设备号,低20位用来表示次设备号。
当然,自己去移位也行,但是linux已经给我们封装好了一个宏定义叫MKDEV(ma,mi) ,参数1是主设备号,参数2是次设备号。
其头文件为 linux/kdev_t.h
正文
我们本次编写的模板参考上一节的模块传参,这样我们就可以直接把传入的参数当作主、次设备号。
如果我们insmod的时候,忘了传入参数怎么办?所以我们在声明主次设备号的时候,赋一个初值,如果没有传入参数,就用初值好了。
那么赋值多少好呢?
linux贴心的为我们提供了0,当传入0的时候,表示不指定设备号是多少,由linux去提供空闲的设备号(从大到小找,主设备号就255个,设备号255->1)。
当然,只是为了保险起见,如果不是我们指定多少,那静态还有啥意义,不如直接用动态申请的方式…
以下是模块传参部分的代码
// 初始化主次设备号
int device_major = 0;
int device_minor = 0;
// 模块传参
module_param(device_major, int, S_IRUSR);
module_param(device_minor, int, S_IRUSR);
我们在模块初始化的时候来申请主设备号。
首先要定义一个主次设备号,并把模块传参主、次设备号,合成后赋值给他
dev_t mryang_dev;
mryang_dev = MKDEV(device_major, device_minor);
有了主、次设备号mryang_dev(高12位是主,低20位是次,用MKDEV宏定义合成的)
我们就开始申请
先介绍一下申请函数的参数:
// 参数1:设备号的编号
// 参数2:连续设备编号个数
// 参数3:设备的名字
// 返回:申请状态
int register_chrdev_region(dev_t first,unsigned int count,char *name)
设备编号就传入我们刚刚合成好的mryang_dev
连续设备编号个数传入2
设备的名字我们就叫"mryang_cdev"
我们再声明一个ret来查看是否申请成功
当然,申请了,你退出模块的时候同样要卸载释放,其函数是:
// 参数内容类似申请的函数
void unregister_chrdev_region(dev_t first, unsigned int count);
全部代码:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
// 字符设备申请函数
#include <linux/fs.h>
// 设备号数据类型 dev_t
#include <linux/cdev.h>
// 处理宏定义 MKDEV
#include <linux/kdev_t.h>
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("MrYang");
// 初始化主次设备号
int device_major = 0;
int device_minor = 0;
// 模块传参
module_param(device_major, int, S_IRUSR);
module_param(device_minor, int, S_IRUSR);
static int mryang_init(void)
{
int ret;
dev_t mryang_dev;
// 输出
printk(KERN_EMERG "HELLO MrYang\n");
printk(KERN_EMERG "major: %d, minor: %d\n", device_major, device_minor);
// 申请主设备号
mryang_dev = MKDEV(device_major, device_minor);
ret = register_chrdev_region(mryang_dev, 2, "mryang_cdev");
if(ret < 0)
printk(KERN_EMERG "failed!\n");
else
printk(KERN_EMERG "success!\n");
return 0;
}
static void mryang_exit(void)
{
dev_t mryang_dev;
// 输出
printk(KERN_EMERG "Bye MrYang\n");
// 注销
mryang_dev = MKDEV(device_major, device_minor);
unregister_chrdev_region(mryang_dev, 2);
printk(KERN_EMERG "over!\n");
}
module_init(mryang_init);
module_exit(mryang_exit);
编译完成之后
我们先查看一下linux已经分配的主设备号
cat /proc/devices
我们随便挑一个没有的即可,比如主设备号叫9,次设备号本节不关心,所以设置为0让他自动分配吧。
我们输入:
insmod probe_linux_module.ko device_major=9 device_minor=0
返回:
[ 176.009824] HELLO MrYang
[ 176.010960] major: 9, minor: 0
[ 176.013951] success!
查看主设备号发现编号是9,名字一致,全部正确。
我们还可以试一下不带参数
insmod probe_linux_module.ko
返回:
[ 900.409599] HELLO MrYang
[ 900.410692] major: 0, minor: 0
[ 900.413702] success!
主、次设备号都是动态分配的号码,然后静态方式去申请它。
查看设备号发现被分配为248,也正确。
收尾
学到现在,感觉对于主次设备号大致有点概念了。
主设备号用于区分某一类的设备
次设备号用于区分这一类设备里的哪一个设备。
有了主+次,就可以具体定位到某一设备了。
以上就是我目前的见解。