简单字符设备驱动
1、主次设备号
主设备号标识设备连接的的驱动,此设备号由内核使用,标识在相应驱动下得对应的设备
在linux中设备号是一个32位的dev_t类型
typedef __u32 __kernel_dev_t;
typedef __kernel_dev_t dev_t;
typedef __kernel_dev_t dev_t;
crw------- 1 root root 10, 1 Apr 11 2011 psaux
crw------- 1 root root 4, 1 Oct 2803:04 tty1
crw-rw-rw- 1 root tty 4, 64 Apr 11 2011 ttys0
crw-rw---- 1 root uucp 4, 65 Apr 11 2011 ttyS
上图是再/dev目录下用$ls -l 命令显示的部分结果可以看到tty driver的主设备号都为4(各个系统版本有差别),次设备号不同
crw------- 1 root root 4, 1 Oct 2803:04 tty1
crw-rw-rw- 1 root tty 4, 64 Apr 11 2011 ttys0
crw-rw---- 1 root uucp 4, 65 Apr 11 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 : 设备名
- * return value:
- * 0: success
- * 负值:出现错误,错误码
- */
- int register_chrdev_region(dev_t first, unsigned int count, char *name);
- /* 动态分配设备号
- * parameter:
- * dev : 用来存储分配的设备号值
- * firstminor: 次设备号(一般填0)
- * count: 分配的设备个数
- * name : 设备名
- * return value:
- * 0: success
- * 负值:出现错误,错误码
- */
- int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);
- /* 释放设备号
- * parameter:
- * first: 设备号
- * count: 分配的设备个数
- */
- void unregister_chrdev_region(dev_t first, unsigned int count);
静态分配设备号,是在已经知道一个可用设备号的时候使用,而程序员在编写程序之前大多并知道设备号是否可用,或者现在可用,不能确保在系统升级时候次设备还是可用的
所以linux社区极力推荐使用动态分配,它会去寻找可用的设备号,而不会产生冲突。在次设备卸载的时候需要释放次设备号。
3、一个没有作用的字符设备驱动
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/types.h>
- #include <linux/fs.h>
- #define SIMPLE_DEBUG 1
- #define DEV_COUNT 2
- #define SIMPLE_NAME "simple_char"
- static int simple_major = 108;
- static int simple_minor = 0;
- static __init int simple_init(void)
- {
- dev_t dev;
- int err;
- #if SIMPLE_DEBUG
- printk(KERN_INFO "In %s\n", __func__);
- #endif
- dev = MKDEV(simple_major, simple_minor); //求取设备号
- if(dev > 0)//设备号有效
- {
- #if SIMPLE_DEBUG
- printk(KERN_INFO "try to register static char dev %d \n", dev);
- #endif
- err = register_chrdev_region(dev,DEV_COUNT, SIMPLE_NAME); //静态分配设备号
- if(err < 0) //静态分配出错 尝试使用动态分配
- {
- printk(KERN_WARNING "register static char dev error\n");
- err = alloc_chrdev_region(&dev, 0, DEV_COUNT, SIMPLE_NAME); //动态分配设备号
- if(err < 0)
- {
- printk(KERN_ERR "register char dev error in line %d\n",__LINE__);
- goto error;
- }
- else
- {
- simple_major = MAJOR(dev);//重新计算主设备号
- simple_minor = MINOR(dev);//重新计算此设备号
- }
- }
- else{
- }
- }
- else //设备号无效使用动态分配
- {
- #if SIMPLE_DEBUG
- printk(KERN_INFO "try to register alloc char dev \n");
- #endif
- err = alloc_chrdev_region(&dev, 0, DEV_COUNT, SIMPLE_NAME);
- if(err < 0)
- {
- printk(KERN_ERR "register char dev error in line %d\n\n",__LINE__);
- goto error;
- }
- else
- {
- simple_major = MAJOR(dev);
- simple_minor = MINOR(dev);
- }
- }
- #if SIMPLE_DEBUG
- printk(KERN_INFO "register char dev success major = %d minor = %d \n", simple_major, simple_minor);
- #endif
- error:
- return err;
- }
- static __exit void simple_exit(void)
- {
- dev_t dev;
- #if SIMPLE_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(jsha.zk@163.com)");
- MODULE_DESCRIPTION("simple char driver!");
这里只在模块初始化的时候去分配设备号,在模块注销的时候去释放次驱动拥有的设备号
在函数里边我们看到用到了在应用编程里边声名狼藉的goto函数,在linux驱动编程时 goto 函数可以让我们的编程更加有条理性,在出现错误时候能更快的去处理。
如果在调用函数检查返回者都去做错误处理则模块函数就显得臃肿,庞大。所以还是建议合理使用goto函数的。
加载次模块后
运行 $cat /proc/devices可以看到 simple_char 的设备以及主设备号。
这里我们看到原来假设的主设备号是不可用的,所以使用的动态分配设备号,由此我们申请到主设备号为249,我们可以在上边添加我们的设备,具体操作下一节会讲到。呵呵留点悬念先。