有一陣子沒寫部落格了,最近都在研究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,請參照如下修改
- static struct resource s3c_led_resources[] = {
- [0] = {
- .start = 0x56000000,
- .end = 0x56000000 + 24,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = IRQ_EINT23,
- .end = IRQ_EINT23,
- .flags = IORESOURCE_IRQ,
- },
- };
-
-
- static struct platform_device s3c_led = {
- .name = "JoeyLED",
- .num_resources = ARRAY_SIZE(s3c_led_resources),
- .resource = s3c_led_resources,
- };
-
-
- static struct platform_device *smdk2410_devices[] __initdata = {
- &s3c_device_usb,
- &s3c_device_lcd,
- &s3c_device_wdt,
- &s3c_device_i2c,
- &s3c_device_iis,
- &s3c_cs89x0,
- &s3c_led,
- };
新增led.c這個UIO driver,範例程式如下,重點在led_probe這個函式,可以看看該如何填寫uio_info這個structure的資料
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/init.h>
- #include <linux/proc_fs.h>
- #include <asm/uaccess.h>
- #include <linux/ioport.h>
- #include <asm/io.h>
- #include <linux/uio_driver.h>
- #include <linux/platform_device.h>
-
- #define GPIOBAdr 0x56000014
-
- #define DRV_NAME "JoeyLED"
- #define DRV_VERSION "0.0.1"
-
-
- static irqreturn_t led_handler(int irq, struct uio_info *dev_info)
- {
- printk("interrupt happened\n");
- return IRQ_HANDLED;
- }
-
- static int __init led_probe(struct platform_device *dev)
- {
- struct resource *regs;
- struct uio_info *info;
-
- printk("probing led\n");
- info = kzalloc(sizeof(struct uio_info), GFP_KERNEL);
- if (!info)
- return -ENOMEM;
- regs = platform_get_resource(dev, IORESOURCE_MEM, 0);
- if (!regs)
- {
- dev_err(&dev->dev, "No memory resource specified\n");
- return -ENODEV;
- }else
- {
- printk("GPIOB reg->start %x reg->end %x \n",regs->start,regs->end );
- }
- info->mem[0].addr = regs->start;
- info->mem[0].size=regs->end - regs->start;
- info->mem[0].internal_addr=ioremap(regs->start, info->mem[0].size);;
- if (!info->mem[0].addr)
- {
- dev_err(&dev->dev, "Invalid memory resource\n");
- return -ENODEV;
- }
-
- info->mem[0].memtype = UIO_MEM_PHYS;
- info->version = "0.0.1";
- info->name="JoeyLED";
- info->irq=platform_get_irq(dev, 0);
- info->irq_flags = IRQF_SHARED;
- info->handler = led_handler;
- if (uio_register_device(&dev->dev, info))
- {
- iounmap(info->mem[0].internal_addr);
- printk("uio_register failed\n");
- return -ENODEV;
- }
- platform_set_drvdata(dev, info);
- printk("probing led success\n");
- return 0;
- }
-
- static int __exit led_remove(struct platform_device *dev)
- {
- struct uio_info *info = platform_get_drvdata(dev);
-
- uio_unregister_device(info);
- platform_set_drvdata(dev, NULL);
- iounmap(info->mem[0].internal_addr);
- kfree(info);
- return 0;
- }
-
- static struct platform_driver led_driver = {
- .probe = led_probe,
- .remove = __devexit_p(led_remove),
- .driver = {
- .name = DRV_NAME,
- .owner = THIS_MODULE,
- },
- };
-
- static int __init led_init(void)
- {
- printk("QT2410 LED driver demo\n");
-
- return platform_driver_register(&led_driver);
- }
-
- static void __exit led_exit(void)
- {
- platform_driver_unregister(&led_driver);
- }
- module_init(led_init);
- module_exit(led_exit);
-
- MODULE_DESCRIPTION("led module");
- MODULE_AUTHOR("Joey Cheng<jemicheng@gmail.com>");
- MODULE_LICENSE("GPL");
- MODULE_ALIAS("QT2410:LED module");
Userspace程式的範例如下
- #include <stdio.h>
- #include <stdlib.h>
- #include<sys/mman.h>
- #include<fcntl.h>
- #include <asm/page.h>
- int main(void)
- {
- int mem_fd,i=0;
- unsigned *led;
-
- mem_fd=open("/dev/uio0",O_RDWR|O_SYNC);
- if (mem_fd <0)
- {
- printf("open device error\n");
- return;
- }
- led=mmap(NULL,24, PROT_READ | PROT_WRITE, MAP_SHARED,mem_fd, 0);
- if (led == MAP_FAILED)
- {
- perror("mmap");
- close(mem_fd);
- exit(-1);
- }
- //led+=4;
- led+=5;
- while(i<3)
- {
- (*(volatile unsigned *)led)|=0x00000002;
- printf("led mem %p content %d\n",led,*(unsigned *)led);
- sleep(3);
- (*(volatile unsigned *)led)&=~(0x00000002);
- printf("led mem %p content %d\n",led,*(unsigned *)led);
- i++;
- sleep(3);
- }
- close(mem_fd);
- }
記得要在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,執行範例如下圖

fw_printenv-control u-boot enviroment variables under linux (2009-05-12 16:35)
rpcapd與netcat-測試嵌入式系統的好工具 (2009-04-09 17:06)
udev-強大的device node管理系統 (2009-02-02 20:35)
Framebuffer兩三事-Test On QT2410 (2009-01-12 16:29)
GDB-刺入bug心臟的寶劍 (2008-10-15 16:11)
Telnet daemon-RS232以外的選擇 (2008-10-03 18:57)
在嵌入式系統切換file system-以squashfs和jffs2為例 (2008-10-01 19:44)
Linux kernel module-進入系統核心的鑰匙 (2008-09-12 14:03)
SQLite-麻雀雖小五臟俱全 (2008-09-05 14:46)