zoukankan      html  css  js  c++  java
  • MISC混杂设备 struct miscdevice /misc_register()/misc_deregister()【转】

    本文转自:http://blog.csdn.net/angle_birds/article/details/8330407

    Linux系统中,存在一类字符设备,他们共享一个主设备号(10),但此设备号不同,我们称这类设备为混杂设备(miscdeivce),查看/proc/device中可以看到一个名为misc的主设备号为10.所有的混杂设备形成一个链表,对设备访问时内核根据次设备号找到对应的miscdevice设备。相对于普通字符设备驱动,它不需要自己去生成设备文件。

    Linux内核使用struct miscdeivce来描述一个混杂设备

    struct miscdevice  {

        int minor;

        const char *name;

        const struct file_operations *fops;

        struct list_head list;

        struct device *parent;

        struct device *this_device;

        const char *nodename;

        mode_t mode;

    };

    scull 设备驱动只实现最重要的设备方法. 它的 file_operations 结构是如下初始化的:

    struct file_operations scull_fops = {
         .owner =  THIS_MODULE, 
         .llseek =  scull_llseek, 
         .read =  scull_read, 
         .write =  scull_write, 
         .ioctl =  scull_ioctl, 
         .open =  scull_open, 
         .release =  scull_release,  
       };  

    minor是这个混杂设备的次设备号,若由系统自动配置,则可以设置为MISC_DYNANIC_MINOR,name是设备名.使用时只需填写minor次设备号,*name设备名,*fops文件操作函数集即可。

    Linux内核使用misc_register函数注册一个混杂设备,使用misc_deregister移除一个混杂设备。注册成功后,linux内核为自动为该设备创建设备节点,在/dev/下会产生相应的节点。

     注册函数:

    int misc_register(struct miscdevice * misc)

    输入参数:struct miscdevice

    返回值:

    0   表示注册成功。

    负数 表示未成功。

    卸载函数:

    int  misc_deregister(struct miscdevice *misc)

    0  表示成功。

    负数 表示失败。

    在Linux驱动中把无法归类的五花八门的设备定义为混杂设备(用miscdevice结构体表述)。miscdevice共享一个主设备号MISC_MAJOR(即10),但次设备号不同。 所有的miscdevice设备形成了一个链表,对设备访问时内核根据次设备号查找对应的miscdevice设备,然后调用其file_operations结构中注册的文件操作接口进行操作。 在内核中用struct miscdevice表示miscdevice设备,然后调用其file_operations结构中注册的文件操作接口进行操作。miscdevice的API实现在drivers/char/misc.c中。

    下边是描述这个设备的结构体:

    1 struct miscdevice  {      
    2     int minor;                               //次设备号   
    3     const char *name;                        //设备的名称      
    4     const struct file_operations *fops;     //文件操作    
    5     struct list_head list;                  //misc_list的链表头  
    6     struct device *parent;                  //父设备(Linux设备模型中的东东了,哈哈)      
    7     struct device *this_device;             //当前设备,是device_create的返回值,下边会看到  
    8 };  

    然后来看看misc子系统的初始化函数:

     1 static int __init misc_init(void)   
     2 {   
     3     int err;   
     4   
     5 #ifdef CONFIG_PROC_FS   
     6     /*创建一个proc入口项*/  
     7     proc_create("misc", 0, NULL, &misc_proc_fops);                   
     8 #endif   
     9     /*在/sys/class/目录下创建一个名为misc的类*/  
    10     misc_class = class_create(THIS_MODULE, "misc");   
    11     err = PTR_ERR(misc_class);   
    12     if (IS_ERR(misc_class))   
    13         goto fail_remove;   
    14    
    15     err = -EIO;  
    16     /*注册设备,其中设备的主设备号为MISC_MAJOR,为10。设备名为misc,misc_fops是操作函数的集合;包含了open方法*/   
    17     if (register_chrdev(MISC_MAJOR,"misc",&misc_fops))
    18         goto fail_printk;   
    19     return 0;   
    20    
    21 fail_printk:   
    22     printk("unable to get major %d for misc devices/n", MISC_MAJOR);   
    23     class_destroy(misc_class);   
    24 fail_remove:   
    25     remove_proc_entry("misc", NULL);   
    26     return err;   
    27 }   
    28 /*misc作为一个子系统被注册到linux内核中*/  
    29 subsys_initcall(misc_init);   

    下边是register_chrdev函数的实现:

     1 int register_chrdev(unsigned int major, const char *name,  
     2             const struct file_operations *fops)  
     3 {  
     4     struct char_device_struct *cd;  
     5     struct cdev *cdev;  
     6     char *s;  
     7     int err = -ENOMEM;  
     8     /*主设备号是10,次设备号为从0开始,分配256个设备*/  
     9     cd = __register_chrdev_region(major, 0, 256, name);  
    10     if (IS_ERR(cd))  
    11         return PTR_ERR(cd);  
    12     /*分配字符设备*/  
    13     cdev = cdev_alloc();  
    14     if (!cdev)  
    15         goto out2;  
    16   
    17     cdev->owner = fops->owner;  
    18     cdev->ops = fops;  
    19     /*Linux设备模型中的,设置kobject的名字*/  
    20     kobject_set_name(&cdev->kobj, "%s", name);  
    21     for (s = strchr(kobject_name(&cdev->kobj),'/'); s; s = strchr(s, '/'))  
    22         *s = '!';  
    23     /*把这个字符设备注册到系统中*/     
    24     err = cdev_add(cdev, MKDEV(cd->major, 0), 256);  
    25     if (err)  
    26         goto out;  
    27   
    28     cd->cdev = cdev;  
    29   
    30     return major ? 0 : cd->major;  
    31 out:  
    32     kobject_put(&cdev->kobj);  
    33 out2:  
    34     kfree(__unregister_chrdev_region(cd->major, 0, 256));  
    35     return err;  
    36 }  

    来看看这个设备的操作函数的集合:

    1 static const struct file_operations misc_fops = {   
    2     .owner      = THIS_MODULE,   
    3     .open       = misc_open,   
    4 };   

    可以看到这里只有一个打开函数,用户打开miscdevice设备是通过主设备号对应的打开函数,在这个函数中找到次设备号对应的相应的具体设备的open函数。它的实现如下:

     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     /*找到次设备号对应的操作函数集合,让new_fops指向这个具体设备的操作函数集合*/  
    11     list_for_each_entry(c, &misc_list, list) {   
    12         if (c->minor == minor) {   
    13             new_fops = fops_get(c->fops);           
    14             break;   
    15         }   
    16     }   
    17            
    18     if (!new_fops) {   
    19         mutex_unlock(&misc_mtx);   
    20         /*如果没有找到,则请求加载这个次设备号对应的模块*/  
    21         request_module("char-major-%d-%d", MISC_MAJOR, minor);   
    22         mutex_lock(&misc_mtx);   
    23         /*重新遍历misc_list链表,如果没有找到就退出,否则让new_fops指向这个具体设备的操作函数集合*/  
    24         list_for_each_entry(c, &misc_list, list) {   
    25             if (c->minor == minor) {   
    26                 new_fops = fops_get(c->fops);   
    27                 break;   
    28             }   
    29         }   
    30         if (!new_fops)   
    31             goto fail;   
    32     }   
    33    
    34     err = 0;   
    35     /*保存旧打开函数的地址*/  
    36     old_fops = file->f_op;   
    37     /*让主设备号的操作函数集合指针指向具体设备的操作函数集合*/  
    38     file->f_op = new_fops;   
    39     if (file->f_op->open) {  
    40         /*使用具体设备的打开函数打开设备*/   
    41         err=file->f_op->open(inode,file);   
    42         if (err) {   
    43             fops_put(file->f_op);   
    44             file->f_op = fops_get(old_fops);   
    45         }   
    46     }   
    47     fops_put(old_fops);   
    48 fail:   
    49     mutex_unlock(&misc_mtx);   
    50     unlock_kernel();   
    51     return err;   
    52 }   

    再来看看misc子系统对外提供的两个重要的API,misc_register,misc_deregister:

     1 int misc_register(struct miscdevice * misc)   
     2 {   
     3     struct miscdevice *c;   
     4     dev_t dev;   
     5     int err = 0;   
     6     /*初始化misc_list链表*/  
     7     INIT_LIST_HEAD(&misc->list);   
     8     mutex_lock(&misc_mtx);   
     9     /*遍历misc_list链表,看这个次设备号以前有没有被用过,如果次设备号已被占有则退出*/  
    10     list_for_each_entry(c, &misc_list, list) {   
    11         if (c->minor == misc->minor) {   
    12             mutex_unlock(&misc_mtx);   
    13             return -EBUSY;   
    14         }   
    15     }   
    16     /*看是否是需要动态分配次设备号*/  
    17     if (misc->minor == MISC_DYNAMIC_MINOR) {  
    18         /* 
    19          *#define DYNAMIC_MINORS 64 /* like dynamic majors */  
    20          *static unsigned char misc_minors[DYNAMIC_MINORS / 8];   
    21          *这里存在一个次设备号的位图,一共64位。下边是遍历每一位,  
    22          *如果这位为0,表示没有被占有,可以使用,为1表示被占用。         
    23          */  
    24         int i = DYNAMIC_MINORS;   
    25         while (--i >= 0)   
    26             if ( (misc_minors[i>>3] & (1 << (i&7))) == 0)   
    27                 break;   
    28         if (i<0) {   
    29             mutex_unlock(&misc_mtx);   
    30             return -EBUSY;   
    31         }   
    32         /*得到这个次设备号*/  
    33         misc->minor = i;                                           
    34     }   
    35     /*设置位图中相应位为1*/  
    36     if (misc->minor < DYNAMIC_MINORS)   
    37         misc_minors[misc->minor >> 3] |= 1 << (misc->minor & 7);   
    38     /*计算出设备号*/  
    39     dev = MKDEV(MISC_MAJOR, misc->minor);   
    40     /*在/dev下创建设备节点,这就是有些驱动程序没有显式调用device_create,却出现了设备节点的原因*/  
    41     misc->this_device = device_create(misc_class, misc->parent, dev, NULL,   
    42                       "%s", misc->name);   
    43     if (IS_ERR(misc->this_device)) {   
    44         err = PTR_ERR(misc->this_device);   
    45         goto out;   
    46     }   
    47    
    48     /*  
    49      * Add it to the front, so that later devices can "override"  
    50      * earlier defaults  
    51      */   
    52     /*将这个miscdevice添加到misc_list链表中*/  
    53     list_add(&misc->list, &misc_list);   
    54  out:   
    55     mutex_unlock(&misc_mtx);   
    56     return err;   
    57 }   

    这个是miscdevice的卸载函数:

     1 int misc_deregister(struct miscdevice *misc)   
     2 {   
     3     int i = misc->minor;   
     4    
     5     if (list_empty(&misc->list))   
     6         return -EINVAL;   
     7    
     8     mutex_lock(&misc_mtx);   
     9     /*在misc_list链表中删除miscdevice设备*/  
    10     list_del(&misc->list);     
    11     /*删除设备节点*/                            
    12     device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor));             
    13     if (i < DYNAMIC_MINORS && i>0) {  
    14         /*释放位图相应位*/   
    15         misc_minors[i>>3] &= ~(1 << (misc->minor & 7));   
    16     }   
    17     mutex_unlock(&misc_mtx);   
    18     return 0;   
    19 }   

    总结一下miscdevice驱动的注册和卸载流程:

    misc_register:

    匹配次设备号->找到一个没有占用的次设备号(如果需要动态分配的话)->计算设号->创建设备文-

    miscdevice结构体添加到misc_list链表中。

    misc_deregister:

    从mist_list中删除miscdevice->删除设备文件->位图位清零。

  • 相关阅读:
    java+opencv实现图像灰度化
    java实现高斯平滑
    hdu 3415 单调队列
    POJ 3368 Frequent values 线段树区间合并
    UVA 11795 Mega Man's Mission 状态DP
    UVA 11552 Fewest Flops DP
    UVA 10534 Wavio Sequence DP LIS
    UVA 1424 uvalive 4256 Salesmen 简单DP
    UVA 1099 uvalive 4794 Sharing Chocolate 状态DP
    UVA 1169uvalive 3983 Robotruck 单调队列优化DP
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/4704756.html
Copyright © 2011-2022 走看看