Linux驱动编程 step-by-step (二)
简单字符设备驱动
1、主次设备号主设备号标识设备连接的的驱动,此设备好由内核使用,标识在相应驱动下得对应的设备
在linux中设备号是一个32位的dev_t类型
typedef__u32__kernel_dev_t;
typedef__kernel_dev_tdev_t;
typedef__kernel_dev_tdev_t;
crw-------1 rootroot10,1 Apr11
2011 psaux
crw-------1 rootroot4,1 Oct2803:04 tty1
crw-rw-rw-1 roottty4,64 Apr11 2011 ttys0
crw-rw----1 rootuucp4,65 Apr11 2011 ttyS
上图是再/dev目录下用$ls -l 命令显示的部分结果可以看到tty driver的主设备号都为4(各个系统版本有差别),次设备号不同crw-------1 rootroot4,1 Oct2803:04 tty1
crw-rw-rw-1 roottty4,64 Apr11 2011 ttys0
crw-rw----1 rootuucp4,65 Apr11 2011 ttyS
前12位标识主设备号 | MAJOR(dev_t dev) | 获得主设备号 |
后20位标识此设备号 | MINOR(dev_t dev) | 获得此设备号 |
由主次设备号生成设备号
可以使用宏MKDEV
dev_t dev_num = MKDEV(dev_t major, dev_t minor);
2、分配与释放设备号
在linux2.6的字符设备中(kernel3.0也是)首先做的事就是申请一个或者多个设备号
- /*静态分配设备号
- *parameter:
- *first:分配的第一个设备号
- *count:分配的设备个数
- *name:设备名
- *returnvalue:
- *0:success
- *负值:出现错误,错误码
- */
- intregister_chrdev_region(dev_tfirst,unsignedintcount,char*name);
- /*动态分配设备号
- *parameter:
- *dev:用来存储分配的设备号值
- *firstminor:次设备号(一般填0)
- *count:分配的设备个数
- *name:设备名
- *returnvalue:
- *0:success
- *负值:出现错误,错误码
- */
- intalloc_chrdev_region(dev_t*dev,unsignedintfirstminor,unsignedintcount,char*name);
- /*释放设备号
- *parameter:
- *first:设备号
- *count:分配的设备个数
- */
- voidunregister_chrdev_region(dev_tfirst,unsignedintcount);
静态分配设备号,是在已经知道一个可用设备号的时候使用,而程序员在编写程序之前大多并知道设备号是否可用,或者现在可用,不能确保在系统升级时候次设备还是可用的
所以linux社区极力推荐使用动态分配,它会去寻找可用的设备号,而不会产生冲突。在次设备卸载的时候需要释放次设备号。
3、一个没有作用的字符设备驱动
- #include<linux/init.h>
- #include<linux/module.h>
- #include<linux/types.h>
- #include<linux/fs.h>
- #defineSIMPLE_DEBUG1
- #defineDEV_COUNT2
- #defineSIMPLE_NAME"simple_char"
- staticintsimple_major=108;
- staticintsimple_minor=0;
- static__initintsimple_init(void)
- {
- dev_tdev;
- interr;
- #ifSIMPLE_DEBUG
- printk(KERN_INFO"In%s\n",__func__);
- #endif
- dev=MKDEV(simple_major,simple_minor);//求取设备号
- if(dev>0)//设备号有效
- {
- #ifSIMPLE_DEBUG
- printk(KERN_INFO"trytoregisterstaticchardev%d\n",dev);
- #endif
- err=register_chrdev_region(dev,DEV_COUNT,SIMPLE_NAME);//静态分配设备号
- if(err<0)//静态分配出错尝试使用动态分配
- {
- printk(KERN_WARNING"registerstaticchardeverror\n");
- err=alloc_chrdev_region(&dev,0,DEV_COUNT,SIMPLE_NAME);//动态分配设备号
- if(err<0)
- {
- printk(KERN_ERR"registerchardeverrorinline%d\n",__LINE__);
- gotoerror;
- }
- else
- {
- simple_major=MAJOR(dev);//重新计算主设备号
- simple_minor=MINOR(dev);//重新计算此设备号
- }
- }
- else{
- }
- }
- else//设备号无效使用动态分配
- {
- #ifSIMPLE_DEBUG
- printk(KERN_INFO"trytoregisterallocchardev\n");
- #endif
- err=alloc_chrdev_region(&dev,0,DEV_COUNT,SIMPLE_NAME);
- if(err<0)
- {
- printk(KERN_ERR"registerchardeverrorinline%d\n\n",__LINE__);
- gotoerror;
- }
- else
- {
- simple_major=MAJOR(dev);
- simple_minor=MINOR(dev);
- }
- }
- #ifSIMPLE_DEBUG
- printk(KERN_INFO"registerchardevsuccessmajor=%dminor=%d\n",simple_major,simple_minor);
- #endif
- error:
- returnerr;
- }
- static__exitvoidsimple_exit(void)
- {
- dev_tdev;
- #ifSIMPLE_DEBUG
- printk(KERN_INFO"In%s\n",__func__);
- #endif
- dev=MKDEV(simple_major,simple_minor);
- unregister_chrdev_region(dev,DEV_COUNT);//释放设备号
- }
- module_init(simple_init);
- module_exit(simple_exit);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("kai_zhang([email protected])");
- MODULE_DESCRIPTION("simplechardriver!");
这里只在模块初始化的时候去分配设备号,在模块注销的时候去释放次驱动拥有的设备号
在函数里边我们看到用到了在应用编程里边声名狼藉的goto函数,在linux驱动编程时 goto 函数可以让我们的编程更加有条理性,在出现错误时候能更快的去处理。
如果在调用函数检查返回者都去做错误处理则模块函数就显得臃肿,庞大。所以还是建议合理使用goto函数的。
加载次模块后
运行 $cat /proc/devices可以看到simple_char 的设备以及主设备号。
这里我们看到原来假设的主设备号是不可用的,所以使用的动态分配设备号,由此我们申请到主设备号为249,我们可以在上边添加我们的设备,具体操作下一节会讲到。呵呵留点悬念先。