09
12

Linux kernel module-進入系統核心的鑰匙

記得第一年工作時,leader丟給我的第一項工作就是撰寫新的路由protocol,這個protocol有點像ADHOC的DSR(dynamic source routing),會在封包頭加上路由的資訊,藉由這樣的資訊,讓封包到達目的地,但問題是,我根本連Linux是什麼東東都不太清楚,大學+研所6年讀下來,都活在微軟的世界,碰過的OS只有Windows,所以上班的第一年幾乎每天都在讀Linux的技術文件,和kernel coding的資料,當然,也享受了整整半年多開機當機的慘況,因為kernel module寫的不好,OS就當給你看,經歷過這些雜碎的失敗後,當我寫的module正常的運行在實驗平台時,那種興奮是後來的成功無法取代的

我覺的開始寫複雜的kernel code前,先把演算法相關的程式在user space測試過,再搬到kernel code裡面,在kernel code只要專注kernel API即可(光搞這個就可以耗很多天了),不過我之前碰過一位R公司的RD,他很驕傲的跟我說,他們寫kernel code都不需像我這樣在user space先測,都直接kernel debug,我真的很佩服他們,台灣雖小,神人很多

OK,廢話太多了,我這次要講的kernel module是運行在QT2410上的LED driver,這支driver可透過讀寫proc檔案系統的方式點亮或熄滅LED,如果要點亮LED,下達指令echo 1 > /proc/led,要熄滅LED則下echo 0 > /proc/led,交叉編譯2.6 kernel module的Makefile很簡單,KDIR就是QT2410 Linux核心所在的資料夾路徑,M是kernel module的資料夾所在位置,請參考如下Makefile,如果要縮小kernel module的size,請下指令arm-linux-strip -d led.ko(只去掉debug symbol),這樣子可以減少體積至原來1/10的大小(2007/9/17修改)

  1. MODULE_NAME = led 
  2. KDIR = ../linux-2.6.26 
  3.  
  4. obj-m := $(MODULE_NAME).o 
  5.  
  6. all:
  7. $(MAKE) -C $(KDIR) M=$(PWD) modules 
  8. clean:
  9. $(MAKE) -C $(KDIR) M=$(PWD) clean

LED driver的重點如下
(1)因為QT2410 Linux有MMU,所以對GPIO存取前必需先將physical address轉為virtual address,請參考requestLEDMemRegion這段函式
(2)註冊proc file system,請參考sdr_init的code中,我是如何create_proc_entry和註冊此entry的讀寫函式,雖然user space program跟kernel module溝通的方法還有ioctl,我想我就不多贅述,請多讀Linux Device Driver這本書

LED driver原始碼如下所示

  1. #include <linux/module.h> 
  2. #include <linux/kernel.h> 
  3. #include <linux/init.h> 
  4. #include <linux/proc_fs.h> 
  5. #include <asm/uaccess.h> 
  6. #include <linux/ioport.h> 
  7. #include <asm/io.h> 
  8.  
  9. #define GPIOBAdr 0x56000014
  10.  
  11.  
  12. static struct proc_dir_entry *LEDProc;
  13. static int LEDswitch=0;
  14. static void __iomem *GPIOBBase;
  15. static int LEDRead(char *page, char **start, off_t off, int count, int *eof,void *data) 
  16. { 
  17. char *p = page;
  18. int len;
  19.  
  20. p += sprintf(p, "%d\n", LEDswitch);
  21. len = (p - page) - off;
  22. if (len < 0) 
  23. len = 0;
  24. *eof = (len <= count) ? 1 : 0;
  25. *start = page + off;
  26. return len;
  27. } 
  28.  
  29. static int LEDWrite(struct file *file, const char __user *buffer,unsigned long count, void *data) 
  30. { 
  31. char mode;
  32.  
  33. if (count > 0) 
  34. { 
  35. if (get_user(mode, buffer)) 
  36. return -EFAULT;
  37. if (mode == '0') 
  38. { 
  39. LEDswitch=0;
  40. (*(volatile unsigned *)GPIOBBase)|=0x00000002;
  41. } 
  42. if (mode == '1') 
  43. { 
  44. LEDswitch=1;
  45. (*(volatile unsigned *)GPIOBBase)&=~(0x00000002);
  46. } 
  47. } 
  48. return count;
  49. } 
  50.  
  51. static int requestLEDMemRegion(void) 
  52. { 
  53. if (!request_mem_region(GPIOBAdr, 4, "GPIOBLed")) 
  54. return -EFAULT;
  55. GPIOBBase= ioremap(GPIOBAdr, 4);
  56. if (GPIOBBase==0) return -EFAULT;
  57. else printk("GPIOB virtual address:0x%p\n",GPIOBBase);
  58.  
  59. return 0;
  60. } 
  61.  
  62. static int __init sdr_init(void) 
  63. { 
  64. printk("QT2410 LED driver demo\n");
  65. if (requestLEDMemRegion()==-EFAULT) return -EFAULT;
  66. LEDProc=create_proc_entry("led",0666,NULL);
  67. LEDProc->read_proc=LEDRead;
  68. LEDProc->write_proc=LEDWrite;
  69. LEDProc->owner=THIS_MODULE;
  70. return 0;
  71. } 
  72.  
  73. static void __exit sdr_exit(void) 
  74. { 
  75. remove_proc_entry("led", NULL);
  76. release_mem_region(GPIOBAdr, 4);
  77. printk("LED driver remove successfully\n");
  78. } 
  79. module_init(sdr_init);
  80. module_exit(sdr_exit);
  81.  
  82. MODULE_DESCRIPTION("led module");
  83. MODULE_AUTHOR("Joey Cheng<jemicheng@gmail.com>");
  84. MODULE_LICENSE("GPL");
  85. MODULE_ALIAS("QT2410:LED module");

編譯moduel的畫面如下

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