From hjk@linutronix.de Thu Dec  7 01:58:37 2006
From: Hans J. Koch <hjk@linutronix.de>
To: Greg KH <greg@kroah.com>
Cc: tglx@linutronix.de, Benedikt Spranger <b.spranger@linutronix.de>
Subject: UIO: dummy test module for the uio core
Date: Thu, 7 Dec 2006 10:58:29 +0100

From: Hans J. Koch <hjk@linutronix.de>

This is a dummy module and userspace code that tests out the uio core.

From: Hans J. Koch <hjk@linutronix.de>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Benedikt Spranger <b.spranger@linutronix.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

---
 drivers/uio/Kconfig      |   11 +++
 drivers/uio/Makefile     |    1 
 drivers/uio/uio_dummy.c  |  171 +++++++++++++++++++++++++++++++++++++++++++++++
 drivers/uio/uio_events.c |  119 ++++++++++++++++++++++++++++++++
 4 files changed, 302 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,171 @@
+/*
+ * driver/uio/uio_dummy.c
+ *
+ * Copyright(C) 2005, Benedikt Spranger <b.spranger@linutronix.de>
+ * Copyright(C) 2005, Thomas Gleixner <tglx@linutronix.de>
+ * Copyright(C) 2006, Hans J. Koch <hjk@linutronix.de>
+ *
+ * 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 <linux/uio_driver.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+
+#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,
+};
+
+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->mem[0].addr;
+
+	uio_dummy_count++;
+	*addr = uio_dummy_count;
+	uio_event_notify(&uio_dummy_info);
+	mod_timer(&poll_timer, jiffies + freq);
+}
+
+static int uio_dummy_probe(struct device *dev)
+{
+	int ret;
+	uio_dummy_info.mem[0].addr = (unsigned long)kmalloc(UIO_DUMMY_MEMSIZE,
+							    GFP_KERNEL);
+	if (!uio_dummy_info.mem[0].addr)
+		return -ENOMEM;
+
+	uio_dummy_info.mem[0].memtype = UIO_MEM_LOGICAL;
+	uio_dummy_info.mem[0].size = UIO_DUMMY_MEMSIZE,
+
+	freq = HZ;
+
+	if (uio_register_device(dev, &uio_dummy_info)) {
+		kfree((void *)uio_dummy_info.mem[0].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_info);
+	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_info);
+	kfree((void *)uio_dummy_info.mem[0].addr);
+	uio_dummy_info.mem[0].addr = 0;
+	uio_dummy_info.mem[0].size = 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 <hjk@linutronix.de>
+ *
+ * License: GPL v2
+ *
+ * Usage: uio_events <event device>
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#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");
+	}
+
+}
