http://blog.csdn.net/ganggexiongqi/article/details/6794215
AUTHOR: Joseph Yang (杨红刚) <ganggexiongqi@gmail.com>
CONTENT: uio驱动编写 实例2
NOTE: Linux-3.0
LAST MODIFIED:09-20-2011
-----------------------------------------------------------------------------------------------------------
Distributed and Embedded System Lab (分布式嵌入式系统实验室,兰州大学)
===============================================================
原理简述:
在第一个例子的基础上,增加了中断部分。但是,我们没有实际产生中断的硬件。但是我们可以”模拟“硬件中断。
每当中断发生时,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)<ganggexiongqi@gmail.com>
- *
- * 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;
- }