在字符设备驱动基础章节,我们使用的注册字符设备驱动的接口为register_chrdev,在本章节我们要学习一种注册字符设备驱动的新接口register_chrdev_region/alloc_chrdev_region+cdev
因为内核一直在更新,新老设备注册的驱动都有!
在使用新接口注册字符设备驱动的过程中,我们首先要了解cdev结构体相关的函数和设备号相关的概念!
cdev结构体相关的函数有cdev_alloc、cdev_init、cdev_add、cdev_del
dev_t类型中,有需要用到的三个宏,MKDEV(用主设备号和次设备号算出来一个主次设备号)、MAJOR(从主次设备号里提取得到主设备号)、MINOR(从主次设备号里提取得到从设备号)
在驱动开头定义static struct cdev test_cdev(cdev结构体)、static dev_t mydev(dev_t 型设备号)
- /*省略头文件、file_operation结构体、以及动作函数 只保留注册卸载函数*/
-
- #define MYCNT 1;
- #define MYMAJOR 200;
- static struct cdev test_cdev;
- static dev_t mydev;
-
- static int__init chrdev_init(void)
- {
- int ret;
-
- /*
- 使用新的cdev的接口来注册字符设备驱动
- 新的接口注册设备驱动需要两步
- */
-
- /*第一步:注册字符设备、分配主次设备号*/
- mydev=MKDEV(MYMAJOR,0);//主设备号为200,次设备号为0
-
- ret=register_chrdev_region(mydev,MYCNT,MYNAME);//使用此函数事先必须知道设备号,MYNAME设
- // 备名字
-
- if(ret)
- {
- printk(KERN_ERR"unable to register minors for%s\n",MYNAME);
- return -EINVAL;
- }
- printk(KERN_ERR"register_chrdev_region success\n");
- printk(KERN_ERR"major=%d\n minor=%d\n",MAJOR(mydev),MINOR(mydev));
- /*通过最后一个函数打印出来我们的主设备号、从设备号*/
-
- /*静态初始化字符设备*/
- cedv_init(&test_cdev,&test_fops);
- ret=cdev_add(&test_cdev,mydev,MYCNT);//把字符添加到系统中去
-
- if(ret)
- {
- printk(KERN_ERR"unable to cdev_add\n");
- return -EINVAL;
- }
- printk(KERN_ERR"cdev_add success\n");
- }
-
- static void__exit chrdev_exit(void)
- {
- /*第一步:真正的去注销字符设备驱动用cdev_del*/
- cedv_del(&test_cdev);
- /*第二步:去注销申请的主次设备号*/
- unregister_chrdev_region(mydev,MYCNT);
- }
使用register_chrdev_region+cdev_init+cdev_add注册设备驱动,是事先要知道使用的主次设备号时使用的;要先查看cat/proc/devices去查看没有使用的;
更简单、更智能的方法是让内核给我们自动分配一个主设备号!那alloc_chrdev_region就可以实现了!
如上图所示,利用alloc_chrdev_region注册设备,只用改动上述程序的标记部分,因为主设备号是自动分配的,所以就没必要去宏定义主设备号,次设备号一般是从0开始,但不限于从0开始。
注释掉上面这个cdev的结构体,转而用ststic struct cedv *pdev的结构体指针代替!
如此一来,后面使用到cdev结构体相关的test_cdev地址问题都可以用pdev代替!
在使用cdev_init之前,必须调用cdev_alloc()给我们定义的结构体指针pdev申请内存!
pdev=cdev_alloc();给pdev分配内存,指针实例化,调用了内核中的kmalloc等的函数!其他部分不用变,实现的跟上面程序一样的功能!
除此之外,在补充个知识点,为什么说这个呢?就是在分析内核的时候,知道有这两个写法!
在字符设备基础笔记中,我们知道在向内核注册成功一个字符设备驱动以后,我们是使用mknod命令在/dev目录下生成一个设备文件节点,然后应用层通过这个设备节点进行调用驱动里面的一些动作函数,mknod命令每次都要手动操作实现,这个小节我们讨论如何能让系统自动生成驱动的设备文件!
解决方案:udev(嵌入式中用的是mdev)!
什么是udev:应用层的一个应用程序,是BusyBox中的一个命令,内核源码是找不到的。内核驱动和应用层udev之间有一套信息传输机制(netlink协议),应用层启动udev,内核驱动中使用相应接口。驱动注册和注销时信息会被传给udev,由udev在应用层进行设备文件的创建和删除!
class_create 、 device_create //创建函数
device_destory()、class_destory() //销毁函数
这两个函数目的就是发信息给udev,让udev赶紧创建设备文件!
- /*此处省略注册函数主体*/
-
- /*注册字符设备驱动完成后,添加设备类的操作,让内核发信息给udev,让udev自动删除和创建设备文件*/
-
- test_class=class_creat(THIS_MODULE,"aston_class");//参数2是类名要和app文件中设备名字保持一致
- //参数1是固定宏,不用动
- if(IS_ERR(test_class))
- return -EINVAL;
- //最后一个参数就是希望在应用中创建的设备文件的名字,是/dev/aston_test
-
- device_creat(test_class,NULL,mydev,NULL,"aston_test");
-
-
- /*下面的程序是写在卸载函数中的,当驱动卸载后,自动删除设备文件*/
-
- device_destory(test_class,mydev);
- class_destory(test_class);
然后就在app中修改这个设备文件就可以了, #define FILE "aston_test"
/pros/ /sys/ 这两个都是虚拟的文件系统。写或读这个文件就相当于在读写内核中的一些数据结构。
这些文件系统中有一些设备类,就是按照不同设备驱动的特性去归类,方便我们进行管控。
比如刚才,我们通过class_create建立了一个aston_class的类,我们通过cd/sys/class/就可看到
然后cd/aston_class 就可以看到aton_test这个设备文件,再进一步,进入到aton_tests
可以看到,在这个文件里面,有uevent文件,这些文件是内核中自动组织的文件。我们再进一步,查看一下uevent文件。cat uevent!
class文件夹中的每一个文件都是一个类,uevent及class就是内核提供给我们的一个和内核进行交互的一个窗口,通过这个class,我们就可以了解驱动程序安装卸载等过程中内核所发生的事情。