linux驅動修煉之道-混雜設備

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是操作函數的集合*/   
  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. }  
  37. 來看看這個設備的操作函數的集合:

    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->刪除設備文件->位圖位清零。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章