From hjk@linutronix.de Thu Dec 7 01:58:37 2006 From: Hans J. Koch To: Greg KH Cc: tglx@linutronix.de, Benedikt Spranger Subject: UIO: dummy test module for the uio core Date: Thu, 7 Dec 2006 10:58:29 +0100 From: Hans J. Koch This is a dummy module and userspace code that tests out the uio core. From: Hans J. Koch Cc: Thomas Gleixner Cc: Benedikt Spranger Signed-off-by: Greg Kroah-Hartman --- drivers/uio/Kconfig | 11 ++ drivers/uio/Makefile | 1 drivers/uio/uio_dummy.c | 174 +++++++++++++++++++++++++++++++++++++++++++++++ drivers/uio/uio_events.c | 119 ++++++++++++++++++++++++++++++++ 4 files changed, 305 insertions(+) --- a/drivers/uio/Kconfig +++ b/drivers/uio/Kconfig @@ -11,4 +11,15 @@ config UIO If you don't know what to do here, say N. +config UIO_DUMMY + tristate "Userspace I/O dummy driver" + depends on UIO + default n + help + This is an example dummy driver for the Userspace I/O + interface that can be used to test out the core UIO code. It + can be exercised by building and running the userspace program + located in the drivers/uio/uio_events.c file. + + If you don't know what to do here, say N. endmenu --- a/drivers/uio/Makefile +++ b/drivers/uio/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_UIO) += uio.o +obj-$(CONFIG_UIO_DUMMY) += uio_dummy.o --- /dev/null +++ b/drivers/uio/uio_dummy.c @@ -0,0 +1,174 @@ +/* + * driver/uio/uio_dummy.c + * + * Copyright(C) 2005, Benedikt Spranger + * Copyright(C) 2005, Thomas Gleixner + * Copyright(C) 2006, Hans J. Koch + * + * Userspace IO dummy/demo driver + * + * This driver allows testing of the Userspace IO framework. + * It creates a UIO device that triggers events using the + * cyclic timer feature (irq = UIO_IRQ_CYCLIC). + * This file is also meant to serve as a template for writing + * other UIO drivers. + * + * Licensed under the GPLv2 only. + */ + +#define DEBUG 1 + +#include +#include +#include + +#define UIO_DUMMY_MEMSIZE 8192 + +static long freq; + +static struct timer_list poll_timer; + +static struct uio_info uio_dummy_info = { + .name = "uio_dummy", + .version = "0.0.0", + .irq = UIO_IRQ_CUSTOM, + .size = UIO_DUMMY_MEMSIZE, + .memtype = UIO_MEM_LOGICAL, +}; + +static struct uio_device *uio_dummy_idev; + +static long uio_dummy_count; + +static ssize_t show_count(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%ld\n", uio_dummy_count); +} + +static ssize_t store_count(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + uio_dummy_count = simple_strtol(buf, NULL, 10); + return count; +} +static DEVICE_ATTR(count, S_IRUGO|S_IWUSR|S_IWGRP, show_count, store_count); + +static ssize_t show_freq(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%ld\n", freq); +} + +static ssize_t store_freq(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + long tmp = simple_strtol(buf, NULL, 10); + if (tmp < 1) + tmp = 1; + freq = tmp; + return count; +} + +static DEVICE_ATTR(freq, S_IRUGO|S_IWUSR|S_IWGRP, show_freq, store_freq); + +static void uio_dummy_timer(unsigned long data) +{ + struct uio_info *info = (struct uio_info *)data; + unsigned long *addr = (unsigned long *)info->addr; + + uio_dummy_count++; + *addr = uio_dummy_count; + uio_event_notify(uio_dummy_idev); + mod_timer(&poll_timer, jiffies + freq); +} + +static int uio_dummy_probe(struct device *dev) +{ + int ret; + uio_dummy_info.addr = (unsigned long)kmalloc(UIO_DUMMY_MEMSIZE, + GFP_KERNEL); + if (!uio_dummy_info.addr) + return -ENOMEM; + + uio_dummy_info.memtype = UIO_MEM_LOGICAL; + + freq = HZ; + + uio_dummy_idev = uio_register_device(dev, &uio_dummy_info); + if (IS_ERR(uio_dummy_idev)) { + kfree((void *)uio_dummy_info.addr); + return -ENODEV; + } + + ret = device_create_file(dev, &dev_attr_count); + if (ret) + goto error_register; + ret = device_create_file(dev, &dev_attr_freq); + if (ret) + goto error_file_count; + + init_timer(&poll_timer); + poll_timer.data = (unsigned long)&uio_dummy_info; + poll_timer.function = uio_dummy_timer; + mod_timer(&poll_timer, jiffies + freq); + return 0; + +error_file_count: + device_remove_file(dev, &dev_attr_count); +error_register: + uio_unregister_device(uio_dummy_idev); + return ret; +} + +static int uio_dummy_remove(struct device *dev) +{ + del_timer_sync(&poll_timer); + device_remove_file(dev, &dev_attr_freq); + device_remove_file(dev, &dev_attr_count); + uio_unregister_device(uio_dummy_idev); + kfree((void *)uio_dummy_info.addr); + uio_dummy_info.addr = 0; + return 0; +} + +static void uio_dummy_shutdown(struct device *dev) +{ + +} + +static struct platform_device *uio_dummy_device; + +static struct device_driver uio_dummy_driver = { + .name = "uio_dummy", + .bus = &platform_bus_type, + .probe = uio_dummy_probe, + .remove = uio_dummy_remove, + .shutdown = uio_dummy_shutdown, +}; + +/* + * Main initialization/remove routines + */ +static int __init uio_dummy_init(void) +{ + uio_dummy_device = platform_device_register_simple("uio_dummy", -1, + NULL, 0); + if (IS_ERR(uio_dummy_device)) + return PTR_ERR(uio_dummy_device); + + return driver_register(&uio_dummy_driver); +} + +static void __exit uio_dummy_exit(void) +{ + platform_device_unregister(uio_dummy_device); + driver_unregister(&uio_dummy_driver); +} + +module_init(uio_dummy_init); +module_exit(uio_dummy_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Benedikt Spranger"); +MODULE_DESCRIPTION("UIO dummy driver"); --- /dev/null +++ b/drivers/uio/uio_events.c @@ -0,0 +1,119 @@ +/* + * Simple test program for UIO event devices + * Copyright (c) 2006 Hans J. Koch + * + * License: GPL v2 + * + * Usage: uio_events + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define UIO_DEV "/dev/uio0" +#define UIO_SIZE "/sys/class/uio/uio0/size" +#define UIO_ADDR "/sys/class/uio/uio0/addr" + +void err_exit(char *msg) +{ + perror(msg); + exit(2); +} + +int get_mem_size(void) +{ + int result,ret; + FILE* file = fopen(UIO_SIZE,"r"); + if (!file) + err_exit("get_mem_size"); + ret = fscanf(file,"0x%lx",&result); + if (ret<0) + err_exit("get_mem_size/fscanf"); + fclose(file); + return result; +} + +unsigned long get_mem_addr(void) +{ + unsigned long result; + int ret; + FILE* file = fopen(UIO_ADDR,"r"); + if (!file) + err_exit("get_mem_addr"); + ret = fscanf(file,"0x%lx",&result); + if (ret<0) + err_exit("get_mem_addr/fscanf"); + fclose(file); + return result; +} + +int main(int argc, char *argv[]) +{ + int uiofd = open(UIO_DEV,O_RDONLY); + if (uiofd < 0) + err_exit("open (uio dev)"); + + printf("Opened %s\n",UIO_DEV); + + int mem_size = get_mem_size(); + printf ("Memory size: %d bytes.\n", mem_size); + + unsigned long mem_addr = get_mem_addr(); + printf ("Memory addr: 0x%lx\n", mem_addr); + + unsigned long* map_addr = mmap(NULL, + mem_size, + PROT_READ, + MAP_SHARED, + uiofd, + 0); + if (map_addr == ((unsigned long*) -1)) + err_exit("mmap"); + printf("Memory mapped to 0x%lx\n", (unsigned long)map_addr); + + fd_set rd_fds, tmp_fds; + FD_ZERO(&rd_fds); + FD_SET(uiofd,&rd_fds); + + struct timeval timeout; + unsigned long irq_count = 0; + unsigned long tim_count = 0; + + double dt=0.0, cur_tim, last_tim=-1.0; + struct timeval cur_tv; + + while (1) { + printf("\033[1G"); + printf("Total: %d Timeouts: %d dt: %.5f sec. Mem: %d", + irq_count, tim_count, dt, *map_addr); + fflush(stdout); + tmp_fds = rd_fds; + timeout.tv_sec = 2; + timeout.tv_usec = 0; + int ret = select(uiofd+1, &tmp_fds, NULL, NULL, &timeout); + if (ret > 0){ + irq_count++; + gettimeofday(&cur_tv,NULL); + cur_tim = cur_tv.tv_sec + + (double)cur_tv.tv_usec/1000000.0; + if (last_tim > 0.0) dt = (cur_tim - last_tim); + last_tim = cur_tim; + unsigned long dummy; + if (read(uiofd,&dummy,sizeof(dummy)) != sizeof(dummy)) + err_exit("read"); + + } + else if (ret == 0) + tim_count++; + else if (ret < 0) + err_exit("select"); + } + +}