12
15

UIO-Linux user space I/O driver

有一陣子沒寫部落格了,最近都在研究RTP的CODEC,真佩服那些電機電子的人,可以想到那麼多的好點子壓縮語音和資料,而且壓縮率還非常驚人,以G729A為例,160 byte的語音資料經過壓縮居然只剩10byte…天阿,寫程式也沒這麼精簡

回到主題,UIO這個東西是雖然是今年四月份的時候merge到kernel trunk,但其實在linux界很早就有相關的應用,因為它的技術很簡單,就是直接把kernel memory一對一映射到user space memory,藉由這樣mapping的方式,讓user program直接在user space存取hardware I/O

而UIO在kernel提供了一個簡單的架構,programmer只要設定好IO address和size,就可以使用UIO的功能,UIO會自動幫我們處理VMA相關的轉換動作,而UIO drvier的撰寫順序如下
1.宣告struct uio_info,並填入hardware資料
uio_info.name:drvier的名稱
uio_info.version:driver版本
uio_info.irq:中斷編號
uio_info.irq_flags:request_irq時需傳入的參數
uio_info.handler:中斷handler
uio_info.mem[]:hardware IO映射位址,可以有很多組
2.user space的程式很簡單,就是用open和mmap去存取hardware I/O

QT2410 LED UIO的範例我簡述如下,因為我driver註冊為platform driver,所以有改到arch/arm/mach-s3c2410/mach-smdk2410.c,請參照如下修改

  1. static struct resource s3c_led_resources[] = { 
  2. [0] = { 
  3. .start = 0x56000000,
  4. .end = 0x56000000 + 24,
  5. .flags = IORESOURCE_MEM,
  6. },
  7. [1] = { 
  8. .start = IRQ_EINT23,
  9. .end = IRQ_EINT23,
  10. .flags = IORESOURCE_IRQ,
  11. },
  12. };
  13.  
  14.  
  15. static struct platform_device s3c_led = { 
  16. .name = "JoeyLED",
  17. .num_resources = ARRAY_SIZE(s3c_led_resources),
  18. .resource = s3c_led_resources,
  19. };
  20.  
  21.  
  22. static struct platform_device *smdk2410_devices[] __initdata = { 
  23. &s3c_device_usb,
  24. &s3c_device_lcd,
  25. &s3c_device_wdt,
  26. &s3c_device_i2c,
  27. &s3c_device_iis,
  28. &s3c_cs89x0,
  29. &s3c_led,
  30. };

新增led.c這個UIO driver,範例程式如下,重點在led_probe這個函式,可以看看該如何填寫uio_info這個structure的資料

  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. #include <linux/uio_driver.h> 
  9. #include <linux/platform_device.h> 
  10.  
  11. #define GPIOBAdr 0x56000014
  12.  
  13. #define DRV_NAME "JoeyLED"
  14. #define DRV_VERSION "0.0.1"
  15.  
  16.  
  17. static irqreturn_t led_handler(int irq, struct uio_info *dev_info) 
  18. { 
  19. printk("interrupt happened\n");
  20. return IRQ_HANDLED;
  21. } 
  22.  
  23. static int __init led_probe(struct platform_device *dev) 
  24. { 
  25. struct resource *regs;
  26. struct uio_info *info;
  27.  
  28. printk("probing led\n");
  29. info = kzalloc(sizeof(struct uio_info), GFP_KERNEL);
  30. if (!info) 
  31. return -ENOMEM;
  32. regs = platform_get_resource(dev, IORESOURCE_MEM, 0);
  33. if (!regs) 
  34. { 
  35. dev_err(&dev->dev, "No memory resource specified\n");
  36. return -ENODEV;
  37. }else 
  38. { 
  39. printk("GPIOB reg->start %x reg->end %x \n",regs->start,regs->end );
  40. } 
  41. info->mem[0].addr = regs->start;
  42. info->mem[0].size=regs->end - regs->start;
  43. info->mem[0].internal_addr=ioremap(regs->start, info->mem[0].size);;
  44. if (!info->mem[0].addr) 
  45. { 
  46. dev_err(&dev->dev, "Invalid memory resource\n");
  47. return -ENODEV;
  48. } 
  49.  
  50. info->mem[0].memtype = UIO_MEM_PHYS;
  51. info->version = "0.0.1";
  52. info->name="JoeyLED";
  53. info->irq=platform_get_irq(dev, 0);
  54. info->irq_flags = IRQF_SHARED;
  55. info->handler = led_handler;
  56. if (uio_register_device(&dev->dev, info)) 
  57. { 
  58. iounmap(info->mem[0].internal_addr);
  59. printk("uio_register failed\n");
  60. return -ENODEV;
  61. } 
  62. platform_set_drvdata(dev, info);
  63. printk("probing led success\n");
  64. return 0;
  65. } 
  66.  
  67. static int __exit led_remove(struct platform_device *dev) 
  68. { 
  69. struct uio_info *info = platform_get_drvdata(dev);
  70.  
  71. uio_unregister_device(info);
  72. platform_set_drvdata(dev, NULL);
  73. iounmap(info->mem[0].internal_addr);
  74. kfree(info);
  75. return 0;
  76. } 
  77.  
  78. static struct platform_driver led_driver = { 
  79. .probe = led_probe,
  80. .remove = __devexit_p(led_remove),
  81. .driver = { 
  82. .name = DRV_NAME,
  83. .owner = THIS_MODULE,
  84. },
  85. };
  86.  
  87. static int __init led_init(void) 
  88. { 
  89. printk("QT2410 LED driver demo\n");
  90.  
  91. return platform_driver_register(&led_driver);
  92. } 
  93.  
  94. static void __exit led_exit(void) 
  95. { 
  96. platform_driver_unregister(&led_driver);
  97. } 
  98. module_init(led_init);
  99. module_exit(led_exit);
  100.  
  101. MODULE_DESCRIPTION("led module");
  102. MODULE_AUTHOR("Joey Cheng<jemicheng@gmail.com>");
  103. MODULE_LICENSE("GPL");
  104. MODULE_ALIAS("QT2410:LED module");

Userspace程式的範例如下

  1. #include <stdio.h> 
  2. #include <stdlib.h> 
  3. #include<sys/mman.h> 
  4. #include<fcntl.h> 
  5. #include <asm/page.h> 
  6. int main(void) 
  7. { 
  8. int mem_fd,i=0;
  9. unsigned *led;
  10.  
  11. mem_fd=open("/dev/uio0",O_RDWR|O_SYNC);
  12. if (mem_fd <0) 
  13. { 
  14. printf("open device error\n");
  15. return;
  16. } 
  17. led=mmap(NULL,24, PROT_READ | PROT_WRITE, MAP_SHARED,mem_fd, 0);
  18. if (led == MAP_FAILED) 
  19. { 
  20. perror("mmap");
  21. close(mem_fd);
  22. exit(-1);
  23. } 
  24. //led+=4;
  25. led+=5;
  26. while(i<3) 
  27. { 
  28. (*(volatile unsigned *)led)|=0x00000002;
  29. printf("led mem %p content %d\n",led,*(unsigned *)led);
  30. sleep(3);
  31. (*(volatile unsigned *)led)&=~(0x00000002);
  32. printf("led mem %p content %d\n",led,*(unsigned *)led);
  33. i++;
  34. sleep(3);
  35. } 
  36. close(mem_fd);
  37. }

記得要在kernel選單中選擇Userspace I/O drivers

重編kernel並試著跑跑看上面的範例來點亮板子上的led,修改過的檔案我簡列如下,記得在執行範例前先確定/dev/uio0是否有建立,請先cat /sys/class/uio/uio0/dev/的資訊,確定device node的major和minor(/sys這個資料夾是virtual sysfs mount出來的,請下指令mount -t sysfs sysfs /sys)

1.arch/arm/mach-s3c2410/mach-smdk2410.c
2.drivers/uio/led_kernel.c
3.drivers/uio/Makefile
4.drivers/uio/Kconfig
5.led.c(in user space)

UIO相關的參考資料請點這裡,這邊有個很棒的軟體叫lsuio,可以列出所有UIO的module和其映射的memory address,執行範例如下圖

 

標籤: embedded
評論: 1 | 引用: 0 | 閱讀: 16920
  • 1 
@@!!! [ 2010-02-23 17:39 | 回覆 | 編輯 刪除 ]
請問範例中start = 0x56000000這一個是屬於physical address嗎? 另外,UIO只能將memory mapped I/O的address map出來嗎?
  • 1 
發表評論
暱 稱: 密 碼:
網 址: E - mail:
驗證碼: 驗證碼圖片 選 項:
頭 像:
內 容: