Linux杂项设备驱动

一、Linux杂项设备驱动简介
Linux杂项驱动出现的意义在于:有很多简单的外围字符设备,它们功能相对简单,一个设备占用一个主设备号对于内核资源来说太浪费。
所以对于这些简单的字符设备它们共用一个主设备号,不同的设备使用不同的次设备号.

杂项驱动特点:
主设备号相同,次设备号不同
在文件系统中自动生成设备节点

杂项设备描述结构体
struct miscdevice  {
int minor;//次设备号
const char *name;//设备的名字
const struct file_operations *fops;//设备的操作函数集
struct list_head list;//将miscdevice串成链表使用
struct device *parent;//父设备(设备模型有关)
struct device *this_device;//代表自己的设备(设备模型有关)
};

杂项设备驱动基本原理:
/devices/char/misc.c
static LIST_HEAD(misc_list);//保存所有注册的杂项设备的链表头
static DEFINE_MUTEX(misc_mtx);
#define DYNAMIC_MINORS 64 //最大可注册的杂项设备数目
static unsigned char misc_minors[DYNAMIC_MINORS/8];//杂项设备位图,每位代表该对应的次设备号是否使用

杂项设备注册过程:
(1)在misc_minors[DYNAMIC_MINORS/8]找一个未使用的次设备号
(2)将新的杂项设备挂在misc_list链表中
(3)根据设备号(主设备10+次设备号)新建设备
杂项设备调用过程:
(1)应用程序打开杂项设备节点
(2)根据设备的主设备号得出是杂项设备,调用杂项设备的misc_open
(3)在misc_open中根据次设备号在misc_list链表中找出设备
(4)将设备的fops赋值给文件结构体struct file * file

二、杂项设备原理分析

Linux杂项设备注册的使用了主设备号10,提供只有open函数,设备操作函数集的初始化就在该函数中完成。它主要是根据open函数
传递的struct inode * inode得到设备的次设备号,然后根据次设备号在misc_list链表中查询得到具体设备的操作函数集。
/devices/char/misc.c
static const struct file_operations misc_fops = {
.owner  = THIS_MODULE,
.open = misc_open,
};
将杂项设备注册为字符驱动,主设备号为10,设备操作函数为misc_open

[cpp]  view plain  copy
  1. static int __init misc_init(void)  
  2. {     
  3.     int err;     
  4. #ifdef CONFIG_PROC_FS     
  5.     /*创建一个proc入口项*/    
  6.     proc_create("misc", 0, NULL, &misc_proc_fops);                     
  7. #endif     
  8.     /*在/sys/class/目录下创建一个名为misc的类*/    
  9.     misc_class = class_create(THIS_MODULE, "misc");     
  10.     err = PTR_ERR(misc_class);     
  11.     if (IS_ERR(misc_class))     
  12.         goto fail_remove;     
  13.     err = -EIO;    
  14.     /*注册设备,其中设备的主设备号为MISC_MAJOR,为10。设备名为misc,misc_fops是操作函数的集合*/     
  15.     if (register_chrdev(MISC_MAJOR,"misc",&misc_fops))     
  16.         goto fail_printk;     
  17.     return 0;     
  18.      
  19. fail_printk:     
  20.     printk("unable to get major %d for misc devices/n", MISC_MAJOR);     
  21.     class_destroy(misc_class);     
  22. fail_remove:     
  23.     remove_proc_entry("misc", NULL);     
  24.     return err;     
  25. }   
  26. /*misc作为一个子系统被注册到linux内核中,系统开机后会自动调用misc_init*/    
  27. subsys_initcall(misc_init);     
当应用程序使用open打开杂项设备时,将根据设备节点的次设备号得到设备真正的操作函数集合

[cpp]  view plain  copy
  1. static int misc_open(struct inode * inode, struct file * file)  
  2. {  
  3.     int minor = iminor(inode);  
  4.     struct miscdevice *c;  
  5.     int err = -ENODEV;  
  6.     const struct file_operations *old_fops, *new_fops = NULL;  
  7.       
  8.     lock_kernel();  
  9.     mutex_lock(&misc_mtx);  
  10.     list_for_each_entry(c, &misc_list, list) {//取出misc_list链表中次设备号为minor的设备  
  11.         if (c->minor == minor) {  
  12.             new_fops = fops_get(c->fops);//获得该设备的操作函数集合    
  13.             break;  
  14.         }  
  15.     }  
  16.           
  17.     if (!new_fops) {  
  18.         mutex_unlock(&misc_mtx);  
  19.         request_module("char-major-%d-%d", MISC_MAJOR, minor);  
  20.         mutex_lock(&misc_mtx);  
  21.   
  22.         list_for_each_entry(c, &misc_list, list) {  
  23.             if (c->minor == minor) {  
  24.                 new_fops = fops_get(c->fops);  
  25.                 break;  
  26.             }  
  27.         }  
  28.         if (!new_fops)  
  29.             goto fail;  
  30.     }  
  31.   
  32.     err = 0;  
  33.     old_fops = file->f_op; /*保存旧打开函数的地址*/    
  34.     file->f_op = new_fops;//让设备的文件结构体中的操作函数指针指向设备的操作函数指针  
  35.     if (file->f_op->open) {  
  36.         err=file->f_op->open(inode,file);//调用设备的open函数  
  37.         if (err) {  
  38.             fops_put(file->f_op);  
  39.             file->f_op = fops_get(old_fops);  
  40.         }  
  41.     }  
  42.     fops_put(old_fops);  
  43. fail:  
  44.     mutex_unlock(&misc_mtx);  
  45.     unlock_kernel();  
  46.     return err;  
  47. }  
该函数供驱动人员调用。主要的功能有:给设备分配次设备号;根据设备号在/dev目录下新建设备节点;将杂项设备加入misc_list链表

[cpp]  view plain  copy
  1. int misc_register(struct miscdevice * misc)  
  2. {  
  3.     struct miscdevice *c;  
  4.     dev_t dev;  
  5.     int err = 0;  
  6.     INIT_LIST_HEAD(&misc->list);/*初始化misc_list链表*/   
  7.     mutex_lock(&misc_mtx);  
  8.         /*遍历misc_list链表,看这个次设备号以前有没有被用过,如果次设备号已被占有则退出*/    
  9.     list_for_each_entry(c, &misc_list, list) {  
  10.         if (c->minor == misc->minor) {  
  11.             mutex_unlock(&misc_mtx);  
  12.             return -EBUSY;  
  13.         }  
  14.     }  
  15.         /*  
  16.          *#define DYNAMIC_MINORS 64  
  17.          *static unsigned char misc_minors[DYNAMIC_MINORS / 8];    
  18.          *这里存在一个次设备号的位图,一共64位,下边是遍历每一位;   
  19.          *如果这位为0,表示没有被占有,可以使用,为1表示被占用。          
  20.          */    
  21.     if (misc->minor == MISC_DYNAMIC_MINOR) {  
  22.         int i = DYNAMIC_MINORS;  
  23.         while (--i >= 0)  
  24.             if ( (misc_minors[i>>3] & (1 << (i&7))) == 0)  
  25.                 break;  
  26.         if (i<0) {  
  27.             mutex_unlock(&misc_mtx);  
  28.             return -EBUSY;  
  29.         }  
  30.         misc->minor = i;//得到可用的次设备号  
  31.     }  
  32.   
  33.     if (misc->minor < DYNAMIC_MINORS)  
  34.         misc_minors[misc->minor >> 3] |= 1 << (misc->minor & 7);//设置位图中相应为1  
  35.     dev = MKDEV(MISC_MAJOR, misc->minor);//计算出设备号  
  36.         /*在/dev下创建设备节点,这就是有些驱动程序没有显式调用device_create,却出现了设备节点的原因*/    
  37.     misc->this_device = device_create(misc_class, misc->parent, dev, NULL,"%s", misc->name);  
  38.     if (IS_ERR(misc->this_device)) {  
  39.         err = PTR_ERR(misc->this_device);  
  40.         goto out;  
  41.     }  
  42.   
  43.     /*将这个miscdevice添加到misc_list链表中*/  
  44.     list_add(&misc->list, &misc_list);  
  45.  out:  
  46.     mutex_unlock(&misc_mtx);  
  47.     return err;  
  48. }  
该函数供驱动人员调用。主要功能和misc_register相反:将设备从misc_list中删除;删除/dev目录下的设备

[cpp]  view plain  copy
  1. int misc_deregister(struct miscdevice *misc)  
  2. {  
  3.     int i = misc->minor;//取得设备此设备号  
  4.     if (list_empty(&misc->list))  
  5.         return -EINVAL;  
  6.     mutex_lock(&misc_mtx);  
  7.     list_del(&misc->list);//将misc从misc_list链表中取出  
  8.     device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor));//将/dev目录下的相应设备清除  
  9.     if (i < DYNAMIC_MINORS && i>0) {  
  10.         misc_minors[i>>3] &= ~(1 << (misc->minor & 7));//位图相应为置0  
  11.     }  
  12.     mutex_unlock(&misc_mtx);  
  13.     return 0;  
  14. }  
三、杂项驱动实例
[cpp]  view plain  copy
  1. #include <linux/miscdevice.h>  
  2. #include <linux/delay.h>  
  3. #include <asm/irq.h>  
  4. #include <mach/regs-gpio.h>  
  5. #include <mach/hardware.h>  
  6. #include <linux/kernel.h>  
  7. #include <linux/module.h>  
  8. #include <linux/init.h>  
  9. #include <linux/mm.h>  
  10. #include <linux/fs.h>  
  11. #include <linux/types.h>  
  12. #include <linux/slab.h>  
  13. #include <linux/errno.h>  
  14. #include <linux/ioctl.h>  
  15. #include <linux/cdev.h>  
  16. #include <linux/string.h>  
  17. #include <linux/list.h>  
  18. #include <asm/uaccess.h>  
  19. #include <asm/atomic.h>  
  20. #include <asm/unistd.h>  
  21.   
  22. #define DEVICE_NAME "led_test"/*注册驱动时自动建立的设备名称*/   
  23. #define IOCTL_GPIO_ON    1  
  24. #define IOCTL_GPIO_OFF  0  
  25. /* 用来指定LED所用的GPIO引脚 */  
  26. static unsigned long gpio_table [] =  
  27. {  
  28.     S3C2410_GPB5,  
  29.     S3C2410_GPB6,  
  30.     S3C2410_GPB7,  
  31.     S3C2410_GPB8,  
  32. };  
  33. /* 用来指定GPIO引脚的功能:输出 */  
  34. static unsigned int gpio_cfg_table [] =  
  35. {  
  36.     S3C2410_GPB5_OUTP,  
  37.     S3C2410_GPB6_OUTP,  
  38.     S3C2410_GPB7_OUTP,  
  39.     S3C2410_GPB8_OUTP,  
  40. };  
  41.   
  42. static int s3c2440_leds_open(struct inode *inode,struct file *filp)/*open函数实现的是led的灯的配置:输出*/   
  43. {  
  44.     int i;  
  45.     for(i=0;i<4;i++)  
  46.     {  
  47.         s3c2410_gpio_cfgpin(gpio_table[i],gpio_cfg_table[i]);  
  48.     }  
  49.     return 0;  
  50. }  
  51.   
  52. static int tq2440_gpio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)/*实现的是led灯的控制*/   
  53. {  
  54.     if (arg > 4)  
  55.     {  
  56.         return -EINVAL;  
  57.     }  
  58.     switch(cmd)  
  59.     {  
  60.         case IOCTL_GPIO_ON:  
  61.             s3c2410_gpio_setpin(gpio_table[arg], 0); // 设置指定引脚的输出电平为0   
  62.             return 0;  
  63.         case IOCTL_GPIO_OFF:  
  64.             s3c2410_gpio_setpin(gpio_table[arg], 1);// 设置指定引脚的输出电平为1   
  65.             return 0;  
  66.         default:  
  67.             return -EINVAL;  
  68.     }  
  69. }  
  70.   
  71. /*驱动框架必备,file_operations 是包含了对这个设备所能进行的操作*/  
  72. static struct file_operations dev_fops = {  
  73.        .owner   =   THIS_MODULE,  
  74.        .ioctl   =   tq2440_gpio_ioctl,  
  75.        .open    =   s3c2440_leds_open,  
  76. };  
  77.   
  78. /*驱动框架必备,驱动信息的打包,用于该驱动程序的注册*/  
  79. static struct miscdevice misc = {     
  80.     .minor = MISC_DYNAMIC_MINOR,  
  81.     .name = DEVICE_NAME,  
  82.     .fops = &dev_fops,  
  83. };  
  84.   
  85. /*驱动框架必备,驱动初始化函数*/  
  86. static int __init dev_init(void)  
  87. {  
  88.     int ret;  
  89.     ret = misc_register(&misc);  
  90.     printk (DEVICE_NAME" initialized\n");  
  91.     return ret;  
  92. }  
  93.   
  94. /*驱动框架必备,驱动退出函数*/  
  95. static void __exit dev_exit(void)  
  96. {  
  97.     misc_deregister(&misc);  
  98.     printk (DEVICE_NAME" is over!\n");  
  99. }  
  100. /*驱动框架必备,模块初始化入口函数*/  
  101. module_init(dev_init);  
  102. /*驱动框架必备,模块结束入口函数*/  
  103. module_exit(dev_exit);  
  104. /*驱动框架必备,通用公共许可*/  
  105. MODULE_LICENSE("GPL");  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值