Linux 中字符設備的註冊

 

Linux中字符設備的註冊過程是比較簡單的。咱們一般能夠調用misc_register()函數來註冊一個字符設備。Misc設備是一種字符設備,經過該設備能夠將fops請求轉發給註冊的misc設備,從而實現字符設備的功能。用戶調用該接口註冊Misc字符設備時,能夠動態分配設備Minor號,當獲取Minor號以後調用class_simple_device_add()或者device_create()函數完成字符設備的建立。Misc字符設備註冊函數以下所示:

 

  
  
           
  
  
  1. int misc_register(struct miscdevice * misc)  
  2. {  
  3.     struct miscdevice *c;  
  4.     dev_t dev;  
  5.     int err = 0;  
  6.  
  7.     INIT_LIST_HEAD(&misc->list);  
  8.  
  9.     mutex_lock(&misc_mtx);      //獲取misc設備信號量  
  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) {    //動態分配分配minor號  
  18.         int i = DYNAMIC_MINORS;  
  19.         while (--i >= 0)  
  20.             if ( (misc_minors[i>>3] & (1 << (i&7))) == 0)  
  21.                 break;  
  22.         if (i<0) {  
  23.             mutex_unlock(&misc_mtx);  
  24.             return -EBUSY;  
  25.         }  
  26.         misc->minor = i;  
  27.     }  
  28.  
  29.     if (misc->minor < DYNAMIC_MINORS)  
  30.         misc_minors[misc->minor >> 3] |= 1 << (misc->minor & 7);  
  31.     dev = MKDEV(MISC_MAJOR, misc->minor);  
  32.  
  33.     misc->this_device = device_create(misc_class, misc->parent, dev,  
  34.                       "%s", misc->name);        //建立字符設備(Misc設備)  
  35.     if (IS_ERR(misc->this_device)) {  
  36.         err = PTR_ERR(misc->this_device);  
  37.         goto out;  
  38.     }  
  39.  
  40.     /*  
  41.      * Add it to the front, so that later devices can "override"  
  42.      * earlier defaults  
  43.      */  
  44.     list_add(&misc->list, &misc_list);  //將設備保存至misc設備鏈中,設備訪問時須要操做該鏈表  
  45.  out:  
  46.     mutex_unlock(&misc_mtx);  
  47.     return err;  
須要注意的是2.6.12內核中建立系統設備時須要調用simple_device的接口class_simple_device_add()。在2.6.23中須要調用device_create()函數完成設備註冊。在3.2內核中,simple_device的接口已經不存在了,因此必須調用device_create函數,另外,3.2內核不支持具備相同minor號的字符設備,在2.6.x內核中是支持的。
 
系統是如何完成fops函數調用的呢?回答這個問題須要分析Misc設備打開過程。在打開Misc設備驅動的時候,Misc設備驅動會根據訪問設備的Minor號重定向fops函數集,該程序說明以下:

 

  
  
           
  
  
  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.     mutex_lock(&misc_mtx);  
  9.  
  10.     list_for_each_entry(c, &misc_list, list) {  //經過minor號來匹配對應的fops函數集  
  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;  //重定向fops函數  
  35.     if (file->f_op->open) {     //打開設備  
  36.         err=file->f_op->open(inode,file);  
  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.     return err;  
不少時候咱們不想建立Misc字符設備,想要本身建立一個字符設備類,而後再建立該設備類的字符設備,那麼整個建立過程能夠描述以下:
1,調用register_chrdev_region函數註冊設備號區間
2,調用cdev_alloc函數分配一個字符設備
3,調用cdev_add函數添加內核字符設備
4,調用device_create函數通知udev建立設備節點,而且註冊到sysfs中。
 
register_chrdev_region函數用來分配設備號。在linux系統中維護了一個char_device的Hash表,每一個Major佔用一個Hash項。經過register_chrdev_region函數就是向全局的char_device Hash表註冊設備號。該函數說明以下:

 

  
  
           
  
  
  1. int register_chrdev_region(dev_t from, unsigned count, const char *name)  
  2. {  
  3.     struct char_device_struct *cd;  
  4.     dev_t to = from + count;  
  5.     dev_t n, next;  
  6.  
  7.     for (n = from; n < ton = next) {  
  8.         next = MKDEV(MAJOR(n)+1, 0);    //一個Major設備類最多能夠容納1M個Minor設備  
  9.         if (next > to)  
  10.             next = to;  //取最小設備號  
  11.         cd = __register_chrdev_region(MAJOR(n), MINOR(n),  
  12.                    next - n, name); //註冊設備號區間  
  13.         if (IS_ERR(cd))  
  14.             goto fail;  
  15.     }  
  16.     return 0;  
  17. fail:  
  18.     to = n;  
  19.     for (n = from; n < ton = next) {  
  20.         next = MKDEV(MAJOR(n)+1, 0);  
  21.         kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));  
  22.     }  
  23.     return PTR_ERR(cd);  
當設備號區間分配完成以後,經過cdev_alloc()函數分配一個內核字符設備,而後經過cdev_add函數將該字符設備註冊到內核cdev_map->probes[]數組中,至此,內核的字符設備建立完畢。可是,此時,應用層還沒法看到字符設備節點,所以,能夠調用device_create函數通知udev去建立設備節點,而且將設備添加到sysfs系統樹中。至此,應用層能夠經過設備節點訪問字符設備了。
 
字符設備是Linux中最簡單的一種設備,其系統註冊過程也相對比較簡單。
相關文章
相關標籤/搜索