02
02

udev-強大的device node管理系統

過年前跑去書店打發無聊的時間,看到這個月又出了兩本跟linux device driver相關的書籍,隨手拿起來翻翻,發現其中有一篇寫到udev framework,裡面詳盡解釋device node在insert kernel module時如何自動建立,並且可隨著使用者更改規則而產生persistent node…等不同於devfs的變化

udev的官方網頁有篇不錯的conference paper,裡面有提到幾個重點,udev的出現為了解決目前在devfs上碰到的3個問題
1.udev能夠根據規則建立device node(這可以解決probing時,device node可能會依probing的順序不同而改變)
2.udev動態建立device node,不會像以前在/dev資料夾下擺一堆多而無用的device node
3.提供user更方便的API存取目前device的資訊,在kernel 2.6以上已提供sysfs這個device管理機制

udev運作的原理很簡單,它透過netlink得知目前kernel有那些module新增了,在收到kernel module新增的netlink訊息之後,它會先掃描user是否有指派device node rule,如果沒有,會自動根據module的major和minor number建立device node.有一點比較特殊的地方是,udev用inotify監聽rule變化的event,所以可以即時改變device node的狀態,udev的架構圖可參考如下

那我做個小實驗,看看udev到底是如何運作的,這個實驗分為兩個部份,一個是character kernel module,而另外一個就是udev的user space program(下載udev-137.tar.gz)

character kernel module寫作的重點在於init時會建立class與create device

  1. charmodule_class=class_create(THIS_MODULE, DEVICE_NAME);
  2. if (IS_ERR(charmodule_class)) 
  3. return -EFAULT;
  4. device_create(charmodule_class, NULL, MKDEV(driver_major, 0), DEVICE_NAME);

character kernel moduel移除時會remove class和destroy device

  1. device_destroy(charmodule_class,MKDEV(driver_major, 0));
  2. class_destroy(charmodule_class);

我把udevd和udevadm移植到實驗平台,並且執行指令udevd –d,當我insert character kernel module時,udevd會自動幫我建立device node,整個實驗過程如下圖

character kernel module程式如下

  1. #include <linux/module.h> 
  2. #include <linux/kernel.h> 
  3. #include <linux/init.h> 
  4. #include <linux/major.h> 
  5. #include <linux/device.h> 
  6. #include <linux/poll.h> 
  7.  
  8. #define dbg(fmt,args...) printk("[%s]:%d => "fmt,__FUNCTION__,__LINE__,##args)
  9. #define DBG() printk("[%s]:%d => \n",__FUNCTION__,__LINE__)
  10. #define DEVICE_NAME "charmodule"
  11. #define STRING_SIZE 256
  12. #define STRING_CHANGE_TIME 2 //seconds
  13.  
  14. static int driver_major;
  15. static int string_counter=1;
  16. static struct class *charmodule_class;
  17.  
  18. typedef struct _driver_private_data 
  19. { 
  20. wait_queue_head_t wait;
  21. struct fasync_struct *fasync;
  22. struct timer_list char_timer;
  23. char my_string[256];
  24. int flag;
  25.  
  26. }driver_private_data;
  27.  
  28. driver_private_data *gpd;
  29. void MyTimerFunction(unsigned long data);
  30.  
  31. static ssize_t char_read(struct file *file, char __user *buffer,size_t count, loff_t *ppos) 
  32. { 
  33. int retval=0;
  34. driver_private_data *dpd = file->private_data;
  35.  
  36. DBG();
  37. if (count<STRING_SIZE) return -EINVAL;
  38. while ((retval + STRING_SIZE)<= count) 
  39. { 
  40. if (copy_to_user(buffer, dpd->my_string, STRING_SIZE)) 
  41. return -EFAULT;
  42. retval += STRING_SIZE;
  43. } 
  44. dpd->flag=0;
  45. return retval;
  46. } 
  47.  
  48. static ssize_t char_write(struct file *file, const char __user *buffer,size_t count, loff_t *ppos) 
  49. { 
  50. return STRING_SIZE;//just a demo, I dont implement this
  51. } 
  52.  
  53. static int char_open(struct inode *inode, struct file *file) 
  54. { 
  55.  
  56. driver_private_data *dpd;
  57.  
  58. DBG();
  59. dpd=(driver_private_data *)file->private_data;
  60. if (!dpd) 
  61. { 
  62. dpd=(driver_private_data *)kmalloc(sizeof(driver_private_data),GFP_ATOMIC);
  63. file->private_data=dpd;
  64. sprintf(dpd->my_string,"string_counter %d\n",string_counter);
  65. dpd->flag=0;
  66. string_counter++;
  67. init_waitqueue_head(&dpd->wait);
  68. gpd=dpd;
  69. init_timer(&dpd->char_timer);
  70. dpd->char_timer.function = MyTimerFunction;
  71. dpd->char_timer.expires = jiffies + STRING_CHANGE_TIME*HZ;
  72. dpd->char_timer.data = (unsigned long) gpd;
  73. add_timer(&dpd->char_timer);
  74. } 
  75. return 0;
  76. } 
  77. static unsigned int char_poll(struct file *file, poll_table *wait) 
  78. { 
  79. driver_private_data *dpd = file->private_data;
  80. int mask=0;
  81.  
  82. DBG();
  83. poll_wait(file, &dpd->wait, wait);
  84. if (dpd->flag==1) 
  85. mask |= POLLIN | POLLRDNORM;
  86. return mask;
  87. } 
  88.  
  89. static int char_release(struct inode *inode, struct file *file) 
  90. { 
  91. driver_private_data *dpd;
  92.  
  93. DBG();
  94. dpd=(driver_private_data *)file->private_data;
  95. del_timer(&dpd->char_timer);
  96. return 0;
  97. } 
  98.  
  99.  
  100.  
  101.  
  102. static const struct file_operations chardev_fops =
  103. { 
  104. .owner = THIS_MODULE,
  105. .read = char_read,
  106. .write = char_write,
  107. .poll = char_poll,
  108. .open = char_open,
  109. .release = char_release,
  110. };
  111.  
  112. void MyTimerFunction(unsigned long data) 
  113. { 
  114. driver_private_data *dpd=(driver_private_data *)data;
  115. if (dpd) 
  116. { 
  117. sprintf(dpd->my_string,"string_counter %d\n",string_counter);
  118. string_counter++;
  119. //wake_up_interruptible(&dpd->wait);
  120. dpd->flag=1;
  121. init_timer(&dpd->char_timer);
  122. dpd->char_timer.function = MyTimerFunction;
  123. dpd->char_timer.expires = jiffies + STRING_CHANGE_TIME*HZ;
  124. dpd->char_timer.data = (unsigned long) dpd;
  125. add_timer(&dpd->char_timer);
  126. } 
  127. } 
  128.  
  129. static int __init charmodule_init(void) 
  130. { 
  131.  
  132. dbg("char module demo\n");
  133. driver_major=register_chrdev(0, DEVICE_NAME, &chardev_fops);
  134. if (driver_major<0) 
  135. { 
  136. dbg("Register character device failed\n");
  137. return -EFAULT;
  138. } 
  139. else dbg("mknod /dev/%s c %d 0\n",DEVICE_NAME,driver_major);
  140. charmodule_class=class_create(THIS_MODULE, DEVICE_NAME);
  141. if (IS_ERR(charmodule_class)) 
  142. return -EFAULT;
  143. device_create(charmodule_class, NULL, MKDEV(driver_major, 0), DEVICE_NAME);
  144.  
  145. return 0;
  146. } 
  147.  
  148. static void __exit charmodule_exit(void) 
  149. { 
  150. unregister_chrdev(driver_major, DEVICE_NAME);
  151. device_destroy(charmodule_class,MKDEV(driver_major, 0));
  152. class_destroy(charmodule_class);
  153. dbg("remove driver successfully\n");
  154. } 
  155. module_init(charmodule_init);
  156. module_exit(charmodule_exit);
  157.  
  158. MODULE_DESCRIPTION("led module");
  159. MODULE_AUTHOR("Joey Cheng<jemicheng@gmail.com>");
  160. MODULE_LICENSE("GPL");
  161. MODULE_ALIAS("QT2410:char module");

標籤: embedded linux
評論: 0 | 引用: 0 | 閱讀: 22996
發表評論
暱 稱: 密 碼:
網 址: E - mail:
驗證碼: 驗證碼圖片 選 項:
頭 像:
內 容: