/* * 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, }; 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); printk("uio_dummy_probe( %p )\n", dev ); 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; printk("uio_register_device...\n"); if (uio_register_device(dev, &uio_dummy_info)) { printk("uio_register_device failed\n"); 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) { printk("uio_dummy_init( )\n" ); 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");