AUTHOR: Joseph Yang (杨红刚) <[email protected]>
CONTENT: uio驱动编写 实例2
NOTE: linux-3.0
LAST MODIFIED:09-20-2011
-----------------------------------------------------------------------------------------------------------
Distributed and Embedded System Lab (分布式嵌入式系统实验室,兰州大学)
===============================================================
第一个例子: uio驱动编写 实例1
原理简述:
在第一个例子的基础上,增加了中断部分。但是,我们没有实际产生中断的硬件。但是我们可以”模拟“硬件中断。
每当中断发生时,uio_event_notify 将被调用,用来对设备的中断事件计数器()增一,并通知各读进程,
有数据可读。所以,我们通过内核定时器,来周期性的产生中断,而在定时器的处理程序中
调用uio_event_notify,从而产生的效果和有硬件是相同。
如下,是内核部分 simple1.c
/** * This is a simple demon of uio driver. * Last modified by 09-20-2011 Joseph Yang(Yang Honggang)<[email protected]> * * Compile: * Save this file name it simple.c * # echo "obj-m := simpleX.o" > Makefile * # make -Wall -C /lib/modules/`uname -r`/build M=`pwd` modules * Load the module: * #modprobe uio * #insmod simpleX.ko */ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/uio_driver.h> #include <linux/slab.h> /* kmalloc, kfree */ #include <linux/device.h> /* class_create */ #include <linux/kobject.h> /* kobject_uevent */ #define FREQ HZ static long freq = FREQ; static long my_event_count = 0; struct uio_info kpart_info = { .name = "kpart", .version = "0.1", .irq = UIO_IRQ_NONE, }; static int drv_kpart_probe(struct device *dev); static int drv_kpart_remove(struct device *dev); static struct device_driver uio_dummy_driver = { .name = "kpart", .bus = &platform_bus_type, .probe = drv_kpart_probe, .remove = drv_kpart_remove, }; static struct timer_list poll_timer;// generate interruption static void drv_kpart_timer(unsigned long data) { struct uio_info *info = (struct uio_info *)data; unsigned long *addr = (unsigned long *)info->mem[0].addr; unsigned long swap = 0; if (my_event_count == 0) { printk(KERN_EMERG"first timer interrupt \n"); *addr = my_event_count; } else if (my_event_count == 10){ printk(KERN_EMERG"timer interrupt happened 10 times\n" "it works well\n"); } swap = *addr; if ( swap != my_event_count){ printk(KERN_EMERG"counter reset\n"); my_event_count = swap; } else { my_event_count++; *addr = my_event_count; // printk(KERN_EMERG"update counter \n"); } // *addr = my_event_count; uio_event_notify(&kpart_info); // gernerate a interrupt here mod_timer(&poll_timer, jiffies + freq); // reset the timer } static int drv_kpart_probe(struct device *dev) { printk(KERN_EMERG"-----> /// drv_kpart_probe( %p)\n", dev); kpart_info.mem[0].addr = (unsigned long)kmalloc(1024,GFP_KERNEL); if(kpart_info.mem[0].addr == 0) return -ENOMEM; kpart_info.mem[0].memtype = UIO_MEM_LOGICAL; kpart_info.mem[0].size = 1024; // for the timer interruption kpart_info.irq_flags = UIO_IRQ_CUSTOM; if( uio_register_device(dev, &kpart_info)){ kfree((void *)kpart_info.mem[0].addr); return -ENODEV; } //initiate and add the timer init_timer(&poll_timer); poll_timer.data = (unsigned long)&kpart_info; poll_timer.function = drv_kpart_timer; mod_timer(&poll_timer, jiffies + freq);//set timer return 0; } static int drv_kpart_remove(struct device *dev) { uio_unregister_device(&kpart_info); //delet the timer del_timer_sync(&poll_timer); return 0; } static struct platform_device * uio_dummy_device; static int __init uio_kpart_init(void) { uio_dummy_device = platform_device_register_simple("kpart", -1, NULL, 0); printk("&platform_device->dev = (%p)\n", &uio_dummy_device->dev); return driver_register(&uio_dummy_driver); } static void __exit uio_kpart_exit(void) { platform_device_unregister(uio_dummy_device); driver_unregister(&uio_dummy_driver); } module_init(uio_kpart_init); module_exit(uio_kpart_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Benedikt Spranger"); MODULE_DESCRIPTION("UIO dummy driver");
这是用户空间的测试部分。
我们通过mmap返回的地址addr跟驱动的内核部分进行交互。在用户空间写入addr的值0,可以重新设置驱动内核部分的
内部变量my_event_count。从而驱动程序会打印出”update counter“提示。
#include <stdio.h> #include <fcntl.h> #include <stdlib.h> #include <unistd.h> #include <sys/mman.h> #include <errno.h> #define UIO_DEV "/dev/uio0" #define UIO_ADDR "/sys/class/uio/uio0/maps/map0/addr" #define UIO_SIZE "/sys/class/uio/uio0/maps/map0/size" static char uio_addr_buf[16], uio_size_buf[16]; int main(void) { int uio_fd, addr_fd, size_fd; int uio_size; void* uio_addr, *access_address; uio_fd = open(UIO_DEV, /*O_RDONLY*/O_RDWR); addr_fd = open(UIO_ADDR, O_RDONLY); size_fd = open(UIO_SIZE, O_RDONLY); if( addr_fd < 0 || size_fd < 0 || uio_fd < 0) { fprintf(stderr, "open: %s\n", strerror(errno)); exit(-1); } read(addr_fd, uio_addr_buf, sizeof(uio_addr_buf)); close(addr_fd); read(size_fd, uio_size_buf, sizeof(uio_size_buf)); close(size_fd); uio_addr = (void*)strtoul(uio_addr_buf, NULL, 0); uio_size = (int)strtol(uio_size_buf, NULL, 0); access_address = mmap(NULL, uio_size, PROT_READ | PROT_WRITE, MAP_SHARED, uio_fd, 0); if ( access_address == (void*) -1) { fprintf(stderr, "mmap: %s\n", strerror(errno)); exit(-1); } printf("The device address %p (lenth %d)\n" "can be accessed over\n" "logical address %p\n", uio_addr, uio_size, access_address); printf("*access_address = %u\n",*((unsigned long*) access_address)); unsigned long * addr = (unsigned long*) access_address; printf("1: read addr:%u\n", *addr); printf("1: write 0 to access_address\n"); //读写操作 *addr = 0; // sleep(10); // printf("2: read addr:%u\n", *addr); // read out the timer interuption times /* unsigned long counter = 0; int ret; while ((ret = read(uio_fd, &counter, sizeof(counter))) == sizeof(counter)) { printf("Interrupt number is %d\n", counter); } if(ret < 0) fprintf(stderr, "read error: %s\n", strerror(errno)); printf("exit: counter is %d\n", counter); */ munmap(access_address, uio_size); close(uio_fd); return 0; }