2006-08-30 06:24:52

by Greg KH

[permalink] [raw]
Subject: [RFC] Simple userspace interface for PCI drivers

A while ago, Thomas and I were sitting in the back of a conference
presentation where the presenter was trying to describe what they did in
order to add the ability to write a userspace PCI driver. As was usual
in a presentation like this, the presenter totaly ignored the real-world
needs for such a framework, and only got it up and working on a single
type of embedded system. But the charts and graphs were quite pretty :)

Thomas and I lamented that we were getting tired of seeing stuff like
this, and it was about time that we added the proper code to the kernel
to provide everything that would be needed in order to write PCI drivers
in userspace in a sane manner. Really all that is needed is a way to
handle the interrupt, everything else can already be done in userspace
(look at X for an example of this...)

Thomas mentioned that he had code to do all of this working in some
customer sites already and that he would get it to me.

Fast forward to OLS of this year, and I bugged Thomas to send me the
code. He did, and then I sat on it for a while longer...

So, here's the code. I think it does a bit too much all at once, but it
is an example of how this can be done. This is working today in some
industrial environments, successfully handling hardware controls of very
large and scary machines. So it is possible to use this interface to
successfully build your own laser wielding robot, all from userspace,
allowing you to keep your special-secret-how-to-control-the-laser
algorithm closed source if you so desire.

In looking at the proposed kevent interface, I think a few things in
this proposed interface can be dropped in favor of using kevents
instead, but I haven't looked at the latest version of that code to make
sure of this.

And the name is a bit ackward, anyone have a better suggestion?

Thomas has also promised to come up with some userspace code that uses
this interface to show how to use it, but seems to have forgotten.
Consider this a reminder :)

Comments?

thanks,

greg k-h

------------------------

From: Thomas Gleixner <[email protected]>
To: Greg KH <[email protected]>
Subject: IIO subsystem

From: Thomas Gleixner <[email protected]>

This allows userspace PCI drivers to be written in a sane manner.

From: Thomas Gleixner <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>

---
Documentation/DocBook/kernel-api.tmpl | 5
drivers/Kconfig | 2
drivers/Makefile | 1
drivers/iio/Kconfig | 11
drivers/iio/Makefile | 4
drivers/iio/iio_base.c | 438 ++++++++++++++++++++++++++++++++++
drivers/iio/iio_dev.c | 366 ++++++++++++++++++++++++++++
drivers/iio/iio_dummy.c | 269 ++++++++++++++++++++
include/linux/iio.h | 108 ++++++++
kernel/signal.c | 4
10 files changed, 1208 insertions(+)

--- gregkh-2.6.orig/Documentation/DocBook/kernel-api.tmpl
+++ gregkh-2.6/Documentation/DocBook/kernel-api.tmpl
@@ -430,6 +430,11 @@ X!Edrivers/pnp/system.c
!Edrivers/pnp/manager.c
!Edrivers/pnp/support.c
</sect1>
+ <sect1><title>Industrial IO device driver</title>
+!Edrivers/iio/iio_base.c
+!Edrivers/iio/iio_dev.c
+!Iinclude/linux/iio.h
+ </sect1>
</chapter>

<chapter id="blkdev">
--- gregkh-2.6.orig/drivers/Kconfig
+++ gregkh-2.6/drivers/Kconfig
@@ -74,4 +74,6 @@ source "drivers/rtc/Kconfig"

source "drivers/dma/Kconfig"

+source "drivers/iio/Kconfig"
+
endmenu
--- gregkh-2.6.orig/drivers/Makefile
+++ gregkh-2.6/drivers/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_FC4) += fc4/
obj-$(CONFIG_SCSI) += scsi/
obj-$(CONFIG_FUSION) += message/
obj-$(CONFIG_IEEE1394) += ieee1394/
+obj-$(CONFIG_IIO) += iio/
obj-y += cdrom/
obj-$(CONFIG_MTD) += mtd/
obj-$(CONFIG_SPI) += spi/
--- /dev/null
+++ gregkh-2.6/drivers/iio/Kconfig
@@ -0,0 +1,11 @@
+menu "Industrial IO"
+config IIO
+ tristate "Industrial IO"
+ help
+ base driver for industrial IO
+
+config IIO_DUMMY
+ tristate "Industrial IO dummy driver"
+ help
+ example/dummy driver for industrial IO
+endmenu
--- /dev/null
+++ gregkh-2.6/drivers/iio/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_IIO) += iio.o
+obj-$(CONFIG_IIO_DUMMY) += iio_dummy.o
+
+iio-objs := iio_base.o iio_dev.o
--- /dev/null
+++ gregkh-2.6/drivers/iio/iio_base.c
@@ -0,0 +1,438 @@
+/*
+ * driver/iio/iio_base.c
+ *
+ * Copyright(C) 2005, Benedikt Spranger <[email protected]>
+ * Copyright(C) 2005, Thomas Gleixner <[email protected]>
+ *
+ * Industrial IO
+ *
+ * Base Functions
+ *
+ * Licensed under the GPLv2 only.
+ */
+
+#define DEBUG
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/iio.h>
+#include <linux/hrtimer.h>
+
+/*
+ * The iio class.
+ */
+
+static struct class *iio_class;
+
+static const char version[] = "0.00";
+static struct list_head iio_devices = LIST_HEAD_INIT(iio_devices);
+static struct rw_semaphore iio_list_sem;
+
+static const char *iio_dev_ext[] = {
+ "",
+ "_in",
+ "_out",
+ "_event"
+};
+
+struct iio_class_dev {
+ struct list_head list;
+ int dev_nr;
+ struct class_device *cdev [4];
+ dev_t dev;
+};
+
+extern struct file_operations iio_event_fops;
+
+/*
+ * attributes
+ */
+static ssize_t show_iio_version(struct class *cd, char *buf)
+{
+ sprintf(buf, "%s\n", version);
+ return strlen(buf) + 1;
+}
+static CLASS_ATTR(version, S_IRUGO, show_iio_version, NULL);
+
+static ssize_t show_addr(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_device *idev = to_iio_device(dev);
+ return sprintf(buf, "0x%lx\n", idev->physaddr);
+}
+static DEVICE_ATTR(physaddr, S_IRUGO, show_addr, NULL);
+
+static ssize_t show_name(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_device *idev = to_iio_device(dev);
+ return sprintf(buf, "%s\n", idev->name);
+}
+static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+
+static ssize_t show_read_offset(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_device *idev = to_iio_device(dev);
+ return sprintf(buf, "0x%lx\n", idev->read_offset);
+}
+static DEVICE_ATTR(read_offset, S_IRUGO, show_read_offset, NULL);
+
+static ssize_t show_size(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_device *idev = to_iio_device(dev);
+ return sprintf(buf, "0x%lx\n", idev->size);
+}
+static DEVICE_ATTR(size, S_IRUGO, show_size, NULL);
+
+static ssize_t show_version(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_device *idev = to_iio_device(dev);
+ return sprintf(buf, "%s\n", idev->version);
+}
+static DEVICE_ATTR(version, S_IRUGO, show_version, NULL);
+
+static ssize_t show_write_offset(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_device *idev = to_iio_device(dev);
+ return sprintf(buf, "0x%lx\n", idev->write_offset);
+}
+static DEVICE_ATTR(write_offset, S_IRUGO, show_write_offset, NULL);
+
+static ssize_t show_listener(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_device *idev = to_iio_device(dev);
+ return sprintf(buf, "%ld\n", idev->event_listener);
+}
+static DEVICE_ATTR(listener, S_IRUGO, show_listener, NULL);
+
+static ssize_t show_event(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_device *idev = to_iio_device(dev);
+ return sprintf(buf, "%d\n", atomic_read(&idev->event));
+}
+static DEVICE_ATTR(event, S_IRUGO, show_event, NULL);
+
+/*
+ * device functions
+ */
+static void iio_dev_release(struct device *dev)
+{
+ pr_debug("%s\n", __FUNCTION__);
+}
+
+static void iio_dev_create(dev_t dev, struct iio_device *idev)
+{
+ int ret;
+ pr_debug("%s\n", __FUNCTION__);
+
+ cdev_init(&idev->cdev, idev->fops);
+ idev->cdev.owner = THIS_MODULE;
+ idev->cdev.ops = idev->fops;
+ ret = cdev_add(&idev->cdev, dev, 3);
+ if (ret)
+ printk(KERN_ERR "%s: cdev_add failed (%d)\n", __FUNCTION__, ret);
+
+ cdev_init(&idev->event_cdev, &iio_event_fops);
+ idev->event_cdev.owner = THIS_MODULE;
+ idev->event_cdev.ops = &iio_event_fops;
+ ret = cdev_add(&idev->event_cdev, dev + 3, 1);
+ if (ret)
+ printk(KERN_ERR "%s: cdev_add failed (%d)\n", __FUNCTION__, ret);
+}
+
+static void iio_dev_remove(struct iio_device *idev)
+{
+ pr_debug("%s\n", __FUNCTION__);
+
+ cdev_del(&idev->cdev);
+ cdev_del(&idev->event_cdev);
+}
+
+static void iio_dev_add_attributes(struct iio_device *idev)
+{
+ int ret;
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ ret = device_create_file(&idev->dev, &dev_attr_name);
+ ret |= device_create_file(&idev->dev, &dev_attr_version);
+ ret |= device_create_file(&idev->dev, &dev_attr_listener);
+ ret |= device_create_file(&idev->dev, &dev_attr_event);
+ if (idev->physaddr)
+ ret |= device_create_file(&idev->dev, &dev_attr_physaddr);
+ if (idev->size)
+ ret |= device_create_file(&idev->dev, &dev_attr_size);
+ if (idev->read_offset >= 0)
+ ret |= device_create_file(&idev->dev, &dev_attr_read_offset);
+ if (idev->write_offset >= 0)
+ ret |= device_create_file(&idev->dev, &dev_attr_write_offset);
+ if (ret)
+ pr_debug("%s: error creating sysfs files\n", __FUNCTION__);
+}
+
+static void iio_dev_del_attributes(struct iio_device *idev)
+{
+ pr_debug("%s\n", __FUNCTION__);
+
+ device_remove_file(&idev->dev, &dev_attr_name);
+ device_remove_file(&idev->dev, &dev_attr_version);
+ device_remove_file(&idev->dev, &dev_attr_listener);
+ device_remove_file(&idev->dev, &dev_attr_event);
+ if (idev->physaddr)
+ device_remove_file(&idev->dev, &dev_attr_physaddr);
+ if (idev->size)
+ device_remove_file(&idev->dev, &dev_attr_size);
+ if (idev->read_offset >= 0)
+ device_remove_file(&idev->dev, &dev_attr_read_offset);
+ if (idev->write_offset >= 0)
+ device_remove_file(&idev->dev, &dev_attr_write_offset);
+}
+
+/*
+ * polling mode
+ */
+
+static void iio_do_poll(unsigned long data)
+{
+ struct iio_device *idev = (struct iio_device *)data;
+
+ iio_interrupt(-1, idev, NULL);
+ mod_timer(&idev->poll_timer, jiffies + idev->freq);
+}
+
+
+#ifdef CONFIG_HIGH_RES_TIMERS
+/*
+ * one shot mode
+ */
+
+int iio_do_oneshot(void *data)
+{
+ struct iio_device *idev = (struct iio_device *)data;
+// struct timespec next;
+
+ atomic_inc(&idev->running);
+ daemonize("iio_oneshot");
+ set_user_nice(current, -10);
+ current->flags |= PF_NOFREEZE;
+//FIXME: Bene: new API
+# if 0
+ while (!atomic_read(&idev->terminate)) {
+ while (ktime_cmp_val(idev->next_event, == , KTIME_ZERO))
+ schedule();
+ ktime_to_timespec(&next, idev->next_event);
+ ktimer_nanosleep(&next, NULL, KTIMER_ABS);
+ ktime_set_scalar(idev->next_event, KTIME_ZERO);
+ iio_interrupt(-2, idev, NULL);
+ }
+# endif
+ atomic_dec(&idev->running);
+ return 0;
+}
+#endif
+/*
+ * class device functions
+ */
+
+static int iio_assign_free_devnr(void)
+{
+ int dev_nr = 0;
+ struct iio_class_dev *entry;
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ if (list_empty(&iio_devices))
+ goto out;
+
+ list_for_each_entry(entry, &iio_devices, list) {
+ if ((entry->dev_nr > dev_nr + 1) && (dev_nr != 0))
+ break;
+ dev_nr = entry->dev_nr;
+ }
+out:
+ return dev_nr + 1;
+}
+
+/**
+ * iio_register_device - register a new industrial IO device
+ *
+ * @parent: parent device
+ * @idev: device capabilities
+ *
+ * returns IIO device driver identifier
+ */
+
+int __devinit iio_register_device(struct device *parent,
+ struct iio_device *idev) {
+ int i, ret = -ENOMEM;
+ struct iio_class_dev *cdev;
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ if (!idev->name || !idev->version)
+ return -EINVAL;
+
+ cdev = kmalloc(sizeof(struct iio_class_dev), GFP_KERNEL);
+ if (!cdev)
+ goto out;
+
+ if (parent) {
+ idev->dev.bus = parent->bus;
+ idev->dev.parent = parent;
+ }
+ idev->dev.release = iio_dev_release;
+
+ down_write(&iio_list_sem);
+ cdev->dev_nr = iio_assign_free_devnr();
+
+ sprintf(idev->dev.bus_id, "iio%d", cdev->dev_nr);
+ if ((ret = device_register(&idev->dev)))
+ goto no_dev;
+
+ alloc_chrdev_region(&cdev->dev, 0, 4, idev->dev.bus_id);
+ iio_dev_create(cdev->dev, idev);
+ iio_dev_add_attributes(idev);
+ list_add_tail(&cdev->list, &iio_devices);
+
+ for (i = 0; i < 4; i++) {
+ cdev->cdev [i] = class_device_create(iio_class, NULL,
+ cdev->dev + i,
+ &idev->dev, "iio%d%s",
+ cdev->dev_nr, iio_dev_ext [i]);
+ if (IS_ERR(cdev->cdev [i])) {
+ printk(KERN_ERR "IIO: device register_failed\n");
+ device_unregister(&idev->dev);
+ iio_dev_remove(idev);
+ unregister_chrdev_region(cdev->dev, 4);
+ ret = PTR_ERR(cdev->cdev [i]);
+ kfree(cdev);
+ break;
+ }
+ }
+
+no_dev:
+ up_write(&iio_list_sem);
+
+ idev->dev.driver_data = (void *)cdev->dev_nr;
+ atomic_set(&idev->running, 0);
+ atomic_set(&idev->terminate, 0);
+
+ if (idev->irq && idev->handler) {
+ init_waitqueue_head(&idev->wait);
+ atomic_set(&idev->event, 0);
+
+ switch (idev->irq) {
+#ifdef CONFIG_HIGH_RES_TIMERS
+ case -2: /* one shot mode */
+ kernel_thread(iio_do_oneshot, idev, CLONE_KERNEL);
+ break;
+#endif
+ case -1: /* cyclic */
+ init_timer(&idev->poll_timer);
+ idev->poll_timer.data = (unsigned long)idev;
+ idev->poll_timer.function = iio_do_poll;
+ mod_timer(&idev->poll_timer, jiffies + idev->freq);
+ break;
+
+ default:
+ /* Make this configurable !!! */
+ ret = request_irq(idev->irq, iio_interrupt,
+ IRQF_SHARED, idev->name, idev);
+ }
+ }
+
+ if (!ret)
+ ret = cdev->dev_nr;
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(iio_register_device);
+
+/**
+ * iio_unregister_device - unregister a industrial IO device
+ * @dev_nr: IIO device driver identifier
+ *
+ * returns 0 on success
+ */
+
+int __devexit iio_unregister_device(int dev_nr)
+{
+ int i, ret = -ENODEV;
+ struct iio_class_dev *entry, *tmp;
+ struct iio_device *idev;
+
+ pr_debug("%s (%d)\n", __FUNCTION__, dev_nr);
+
+ down_write(&iio_list_sem);
+
+ list_for_each_entry_safe(entry, tmp, &iio_devices, list) {
+ if (entry->dev_nr == dev_nr) {
+
+ idev = to_iio_device(entry->cdev [0]->dev);
+
+ atomic_inc(&idev->terminate);
+ while (atomic_read(&idev->running))
+ schedule();
+
+ if (idev->irq == -1)
+ del_timer_sync(&idev->poll_timer);
+
+ iio_dev_remove(idev);
+ unregister_chrdev_region(entry->dev, 4);
+ iio_dev_del_attributes(idev);
+ for (i = 0; i < 4; i++) {
+ class_device_unregister(entry->cdev [i]);
+ }
+ device_unregister(&idev->dev);
+ list_del(&entry->list);
+
+ kfree(entry);
+ break;
+ }
+ }
+
+ up_write(&iio_list_sem);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(iio_unregister_device);
+
+/*
+ * module
+ */
+
+static int __init iio_init(void)
+{
+ int ret;
+
+ init_rwsem(&iio_list_sem);
+ iio_class = class_create(THIS_MODULE, "iio");
+
+ if (IS_ERR(iio_class))
+ return PTR_ERR(iio_class);
+
+ ret = class_create_file(iio_class, &class_attr_version);
+ if (ret)
+ class_destroy(iio_class);
+ return ret;
+}
+
+void __exit iio_exit(void)
+{
+ class_remove_file(iio_class, &class_attr_version);
+ class_destroy(iio_class);
+}
+
+module_init(iio_init)
+module_exit(iio_exit)
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Benedikt Spranger");
--- /dev/null
+++ gregkh-2.6/drivers/iio/iio_dev.c
@@ -0,0 +1,366 @@
+/*
+ * drivers/iio/iio_dev.c
+ *
+ * Copyright(C) 2005, Benedikt Spranger <[email protected]>
+ * Copyright(C) 2005, Thomas Gleixner <[email protected]>
+ *
+ * Industrial IO
+ *
+ * Character Device related functions
+ *
+ * Licensed under the GPLv2 only.
+ */
+
+#define DEBUG 1
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mman.h>
+#include <linux/poll.h>
+#include <linux/iio.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+/**
+ * iio_lseek - IIO framework lseek implementation
+ *
+ */
+loff_t iio_lseek(struct file *filep, loff_t offset, int orig)
+{
+ struct iio_device *idev = filep->private_data;
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ switch (orig) {
+ case 0:
+ filep->f_pos = offset;
+ break;
+ case 1:
+ filep->f_pos += offset;
+ break;
+ case 2:
+ filep->f_pos = idev->size + offset;
+ break;
+ }
+
+ return (filep->f_pos >= idev->size) ? -EINVAL : filep->f_pos;
+}
+EXPORT_SYMBOL_GPL(iio_lseek);
+
+/**
+ * iio_read - IIO framework read implementation
+ *
+ */
+ssize_t iio_read(struct file *filep, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ unsigned int minor = iminor(filep->f_dentry->d_inode);
+ struct iio_device *idev = filep->private_data;
+ int offset;
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ switch (minor) {
+ case 0: if (filep->f_pos + count >= idev->size)
+ count = idev->size - filep->f_pos;
+ offset = 0;
+ break;
+ case 1: if (filep->f_pos + count >= idev->read_len)
+ count = idev->read_len - filep->f_pos;
+ offset = idev->read_offset;
+ break;
+ case 2: if (filep->f_pos + count >= idev->write_len)
+ count = idev->write_len - filep->f_pos;
+ offset = idev->write_offset;
+ break;
+ default: return -EFAULT;
+ }
+
+ if (count < 0)
+ return 0;
+
+ if (!idev->virtaddr) {
+ /* IO Ports */
+ size_t i;
+
+ for (i = 0; i < count; i++)
+ if (__put_user(inb(idev->physaddr + offset + i),
+ buf + i) < 0)
+ return -EFAULT;
+ } else {
+ /* IO Mem */
+ if (copy_to_user(buf, idev->virtaddr + offset +
+ filep->f_pos, count))
+ return -EFAULT;
+ }
+
+ *ppos += count;
+ return count;
+}
+EXPORT_SYMBOL_GPL(iio_read);
+
+/**
+ * iio_write - IIO framework write implementation
+ *
+ */
+ssize_t iio_write(struct file *filep, const char __user * buf, size_t count,
+ loff_t *ppos)
+{
+ unsigned int minor = iminor(filep->f_dentry->d_inode);
+ struct iio_device *idev = filep->private_data;
+ int offset;
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ switch (minor) {
+ case 0: if (filep->f_pos + count >= idev->size)
+ count = idev->size - filep->f_pos;
+ offset = 0;
+ break;
+ case 1: if (filep->f_pos + count >= idev->read_len)
+ count = idev->read_len - filep->f_pos;
+ offset = idev->read_offset;
+ break;
+ case 2: if (filep->f_pos + count >= idev->write_len)
+ count = idev->write_len - filep->f_pos;
+ offset = idev->write_offset;
+ break;
+ default: return -EFAULT;
+ }
+
+ if (count < 0)
+ return 0;
+
+ if (!idev->virtaddr) {
+ /* IO Ports */
+ size_t i;
+ char c;
+
+ for (i = 0; i < count; i++) {
+ if (__get_user(c, buf + i))
+ return -EFAULT;
+ outb(c, idev->physaddr + offset + i);
+ }
+ } else {
+ /* IO Mem */
+ if (copy_from_user(idev->virtaddr + offset + filep->f_pos,
+ buf, count))
+ return -EFAULT;
+ }
+
+ *ppos += count;
+ return count;
+}
+EXPORT_SYMBOL_GPL(iio_write);
+
+/**
+ * iio_open - IIO framework open implementation
+ *
+ */
+int iio_open(struct inode *inode, struct file *filep)
+{
+ struct iio_device *idev;
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ idev = container_of(inode->i_cdev, struct iio_device, cdev);
+ filep->private_data = idev;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iio_open);
+
+/**
+ * iio_mmap - IIO framework mmap implementation
+ *
+ */
+int iio_mmap(struct file *filep, struct vm_area_struct *vma)
+{
+ unsigned int minor = iminor(filep->f_dentry->d_inode);
+ struct iio_device *idev = filep->private_data;
+ unsigned long size = (unsigned long)(vma->vm_end - vma->vm_start);
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ switch (minor) {
+ case 0: if (size > idev->size)
+ return -EINVAL;
+ vma->vm_pgoff = idev->physaddr;
+ break;
+ case 1: if (size > idev->read_len)
+ return -EINVAL;
+ vma->vm_pgoff = idev->physaddr + idev->read_offset;
+ break;
+ case 2: if (size > idev->write_len)
+ return -EINVAL;
+ vma->vm_pgoff = idev->physaddr + idev->write_offset;
+ break;
+ default: return -EINVAL;
+ }
+
+ vma->vm_flags |= VM_LOCKED | VM_IO;
+
+ return remap_pfn_range(vma,
+ vma->vm_start,
+ idev->physaddr >> PAGE_SHIFT,
+ vma->vm_end - vma->vm_start,
+ vma->vm_page_prot);
+}
+EXPORT_SYMBOL_GPL(iio_mmap);
+
+/**
+ * iio_release - IIO framework release implementation
+ *
+ */
+int iio_release(struct inode *inode, struct file *filep)
+{
+ pr_debug("%s\n", __FUNCTION__);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iio_release);
+
+
+/*
+ * event interface
+ */
+int iio_event_open(struct inode *inode, struct file *filep)
+{
+ struct iio_device *idev;
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ idev = container_of(inode->i_cdev, struct iio_device, event_cdev);
+ filep->private_data = idev;
+ idev->event_listener++;
+
+ return 0;
+}
+
+int iio_event_release(struct inode *inode, struct file *filep)
+{
+ struct iio_device *idev = filep->private_data;
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ idev->event_listener--;
+ if (filep->f_flags & FASYNC) {
+ if (idev->fops->fasync)
+ idev->fops->fasync(-1, filep, 0);
+ }
+
+ return 0;
+}
+
+irqreturn_t iio_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct iio_device *idev = (struct iio_device *)dev_id;
+
+ if (idev->handler)
+ idev->handler(irq, dev_id, regs);
+
+ if (idev->event_listener) {
+ atomic_inc(&idev->event);
+ wake_up_interruptible(&idev->wait);
+ kill_fasync(&idev->async_queue, SIGIO, POLL_IN);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int iio_event_fasync(int fd, struct file *filep, int on)
+{
+ struct iio_device *idev = filep->private_data;
+
+ return fasync_helper(fd, filep, on, &idev->async_queue);
+}
+
+static unsigned int iio_event_poll(struct file *filep, poll_table *wait)
+{
+ struct iio_device *idev = filep->private_data;
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ if (!idev->irq)
+ return -EIO;
+
+ poll_wait(filep, &idev->wait, wait);
+
+ return POLLIN | POLLRDNORM;
+}
+
+static ssize_t iio_event_read(struct file *filep, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct iio_device *idev = filep->private_data;
+ DECLARE_WAITQUEUE(wait, current);
+ ssize_t retval;
+ int event_count;
+
+ if (!idev->irq)
+ return -EIO;
+
+ if (count > sizeof(int))
+ count = sizeof(int);
+
+ add_wait_queue(&idev->wait, &wait);
+
+ do {
+ __set_current_state(TASK_INTERRUPTIBLE);
+
+ event_count = atomic_read(&idev->event);
+ if (event_count)
+ break;
+
+ if (filep->f_flags & O_NONBLOCK) {
+ retval = -EAGAIN;
+ goto out;
+ }
+
+ if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ goto out;
+ }
+ schedule();
+ } while (1);
+
+ atomic_dec(&idev->event);
+
+ if (copy_to_user(buf, &event_count, count))
+ retval = -EFAULT;
+ else
+ retval = sizeof(int);
+
+out:
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&idev->wait, &wait);
+
+ return retval;
+}
+
+ssize_t iio_event_write(struct file *filep, const char __user * buf,
+ size_t count, loff_t *ppos)
+{
+ struct iio_device *idev = filep->private_data;
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ if (!idev->event_write)
+ return -EFAULT;
+
+ return idev->event_write(filep, buf, count, ppos);
+}
+
+struct file_operations iio_event_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = iio_event_read,
+ .write = iio_event_write,
+ .poll = iio_event_poll,
+ .open = iio_event_open,
+ .release = iio_event_release,
+ .fasync = iio_event_fasync,
+};
--- /dev/null
+++ gregkh-2.6/drivers/iio/iio_dummy.c
@@ -0,0 +1,269 @@
+/*
+ * IIO dummy driver
+ *
+ * GPLv2 only.
+ */
+
+#define DEBUG 1
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/iio.h>
+
+#include <asm/io.h>
+
+#define MEMSIZE 8192
+
+static irqreturn_t iio_dummy_handler(int irq, void *dev_id,
+ struct pt_regs *reg);
+
+static struct file_operations iio_dummy_fops = {
+ .open = iio_open,
+ .release = iio_release,
+ .read = iio_read,
+ .write = iio_write,
+ .llseek = iio_lseek,
+ .mmap = iio_mmap,
+};
+
+static struct iio_device iio_dummy_idev = {
+ .name = "IIO dummy",
+ .version = "0.00",
+ .fops = &iio_dummy_fops,
+ .size = MEMSIZE,
+ .read_offset = 0,
+ .read_len = MEMSIZE/2,
+ .write_offset = MEMSIZE/2,
+ .write_len = MEMSIZE/2,
+ .irq = -1, /* cyclic timer */
+ .freq = HZ,
+ .irq_type = IIO_EVENT,
+ .handler = iio_dummy_handler,
+};
+
+struct iio_dummy_sig {
+ long pid;
+ int it_sigev_notify; /* notify word of sigevent struct */
+ int it_sigev_signo; /* signo word of sigevent struct */
+ sigval_t it_sigev_value; /* value word of sigevent struct */
+ struct task_struct *it_process; /* process to send signal to */
+ struct sigqueue *sigq; /* signal queue entry. */
+};
+
+static struct iio_dummy_sig iio_dummy_signal;
+static long iio_dummy_count;
+
+static ssize_t show_count(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%ld\n", iio_dummy_count);
+}
+
+static ssize_t store_count(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ iio_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", iio_dummy_idev.freq);
+}
+static ssize_t store_freq(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ iio_dummy_idev.freq = simple_strtol(buf, NULL, 10);
+ return count;
+}
+static DEVICE_ATTR(freq, S_IRUGO|S_IWUSR|S_IWGRP, show_freq, store_freq);
+
+static ssize_t show_sig_pid(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%ld\n", iio_dummy_signal.pid);
+}
+
+static ssize_t store_sig_pid(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ iio_dummy_signal.pid = simple_strtol(buf, NULL, 10);
+ if (iio_dummy_signal.pid == 0) {
+ if (iio_dummy_signal.it_process) {
+ put_task_struct(iio_dummy_signal.it_process);
+ iio_dummy_signal.it_process = NULL;
+ }
+
+ iio_dummy_signal.pid = 0;
+ return count;
+ }
+
+ if (iio_dummy_signal.pid == 1)
+ goto out;
+
+ iio_dummy_signal.it_process = find_task_by_pid(iio_dummy_signal.pid);
+ if (iio_dummy_signal.it_process) {
+ get_task_struct(iio_dummy_signal.it_process);
+ iio_dummy_signal.it_sigev_notify = SIGEV_SIGNAL;
+ iio_dummy_signal.it_sigev_signo = SIGALRM;
+ iio_dummy_signal.it_sigev_value.sival_int = 0;
+
+ return count;
+ }
+out:
+ iio_dummy_signal.pid = 0;
+ return -EINVAL;
+}
+
+static DEVICE_ATTR(sig_pid, S_IRUGO|S_IWUSR|S_IWGRP, show_sig_pid, store_sig_pid);
+
+static int iio_dummy_irqsig(struct iio_dummy_sig *io_sig, int si_private)
+{
+ int ret;
+ struct task_struct *leader;
+
+ if (!io_sig->it_process || !io_sig->sigq)
+ return 0;
+
+ memset(&io_sig->sigq->info, 0, sizeof(siginfo_t));
+ io_sig->sigq->info.si_sys_private = si_private;
+
+ /*
+ * Send signal to a process waiting for an interrupt
+ */
+ io_sig->sigq->info.si_signo = io_sig->it_sigev_signo;
+ io_sig->sigq->info.si_errno = 0;
+ io_sig->sigq->info.si_code = SI_TIMER;
+ io_sig->sigq->info.si_value = io_sig->it_sigev_value;
+
+ ret = send_sigqueue(io_sig->it_sigev_signo, io_sig->sigq,
+ io_sig->it_process);
+
+ if (likely(ret >= 0))
+ return ret;
+
+ io_sig->it_sigev_notify = SIGEV_SIGNAL;
+ leader = io_sig->it_process->group_leader;
+ put_task_struct(io_sig->it_process);
+ io_sig->it_process = leader;
+
+ return send_group_sigqueue(io_sig->it_sigev_signo, io_sig->sigq,
+ io_sig->it_process);
+}
+
+static irqreturn_t iio_dummy_handler(int irq, void *dev_id, struct pt_regs *reg)
+{
+ struct iio_device *idev = (struct iio_device *)dev_id;
+ iio_dummy_count++;
+
+ *((long *) idev->physaddr) = iio_dummy_count;
+ iio_dummy_irqsig(&iio_dummy_signal, 0);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit iio_dummy_probe(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ iio_dummy_idev.physaddr = (unsigned long)kmalloc(MEMSIZE, GFP_KERNEL);
+ if (!iio_dummy_idev.physaddr)
+ return -ENOMEM;
+
+ pdev->dev.driver_data = (void *)iio_register_device(NULL, &iio_dummy_idev);
+
+ iio_dummy_signal.sigq = sigqueue_alloc();
+
+ return (pdev->dev.driver_data) ? 0 : -ENODEV;
+}
+
+static int __devexit iio_dummy_remove(struct device *dev)
+{
+ pr_debug("%s\n", __FUNCTION__);
+ sigqueue_free(iio_dummy_signal.sigq);
+ iio_dummy_signal.sigq = NULL;
+
+ return 0;
+}
+
+static void iio_dummy_shutdown(struct device *dev)
+{
+ pr_debug("%s\n", __FUNCTION__);
+}
+
+struct platform_device *iio_dummy_device;
+
+static struct device_driver iio_dummy_driver = {
+ .name = "iio_dummy",
+ .bus = &platform_bus_type,
+ .probe = iio_dummy_probe,
+ .remove = __devexit_p(iio_dummy_remove),
+ .shutdown = iio_dummy_shutdown,
+};
+
+/*
+ * Main initialization/remove routines
+ */
+
+static int __init iio_dummy_init(void)
+{
+ int ret;
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ iio_dummy_device = platform_device_register_simple("iio_dummy", -1,
+ NULL, 0);
+ if (IS_ERR(iio_dummy_device)) {
+ ret = PTR_ERR(iio_dummy_device);
+ goto out;
+ }
+
+ ret = device_create_file(&iio_dummy_device->dev, &dev_attr_count);
+ if (ret)
+ goto error_register;
+ ret = device_create_file(&iio_dummy_device->dev, &dev_attr_freq);
+ if (ret)
+ goto error_file_count;
+ ret = device_create_file(&iio_dummy_device->dev, &dev_attr_sig_pid);
+ if (ret)
+ goto error_file_freq;
+
+ ret = driver_register(&iio_dummy_driver);
+ if (ret)
+ goto error_file_sig;
+
+ goto out;
+
+error_file_sig:
+ device_remove_file(&iio_dummy_device->dev, &dev_attr_sig_pid);
+error_file_freq:
+ device_remove_file(&iio_dummy_device->dev, &dev_attr_freq);
+error_file_count:
+ device_remove_file(&iio_dummy_device->dev, &dev_attr_count);
+error_register:
+ platform_device_unregister(iio_dummy_device);
+out:
+ return ret;
+}
+
+void __exit iio_dummy_exit(void)
+{
+ pr_debug("%s\n", __FUNCTION__);
+
+ iio_unregister_device((int) iio_dummy_device->dev.driver_data);
+ kfree((void *) iio_dummy_idev.physaddr);
+
+ platform_device_unregister(iio_dummy_device);
+ driver_unregister(&iio_dummy_driver);
+}
+
+module_init(iio_dummy_init);
+module_exit(iio_dummy_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Benedikt Spranger");
+MODULE_DESCRIPTION("IIO dummy driver");
--- /dev/null
+++ gregkh-2.6/include/linux/iio.h
@@ -0,0 +1,108 @@
+/*
+ * include/linux/iio.h
+ *
+ * Copyright(C) 2005, Benedikt Spranger <[email protected]>
+ * Copyright(C) 2005, Thomas Gleixner <[email protected]>
+ *
+ * Industrial IO
+ *
+ * header
+ *
+ * Licensed under the GPLv2 only.
+ */
+
+#ifndef _LINUX_IIO_H_
+#define _LINUX_IIO_H_
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+
+enum iio_irq_type {
+ IIO_EVENT,
+ IIO_THREADED,
+ IIO_NODELAY,
+};
+
+/*
+ * iio_device - IIO device capabilities
+ *
+ * @name: device name
+ * @version: device driver version
+ * @physaddr: physical address
+ * @virtaddr: iomaped address (optional)
+ * @size: size of IO
+ * read_offset: offset to inputs
+ * read_len: input length
+ * write_offset: offset to outputs
+ * write_len: output length
+ * irq: interrupt, -1 for cyclic mode, -2 for oneshot mode
+ * freq: frequency for cyclic mode
+ * next_event: next event for oneshot mode
+ * irq_type: interrupt type
+ */
+
+struct iio_device {
+ /* internals */
+ struct device dev;
+ struct cdev cdev;
+ struct cdev event_cdev;
+ long event_listener;
+ atomic_t event;
+ struct timer_list poll_timer;
+ struct fasync_struct *async_queue;
+ wait_queue_head_t wait;
+ atomic_t event_count;
+ atomic_t waiters;
+ atomic_t running;
+ atomic_t terminate;
+
+ /* attributes */
+ char *name;
+ char *version;
+ unsigned long physaddr;
+ void *virtaddr;
+ unsigned long size;
+ long read_offset;
+ long read_len;
+ long write_offset;
+ long write_len;
+ long irq;
+ long freq;
+ ktime_t next_event;
+ enum iio_irq_type irq_type;
+
+ /* callbacks */
+ irqreturn_t (*handler)(int irq, void *dev_id, struct pt_regs *regs);
+ ssize_t (*event_write)(struct file *filep, const char __user * buf,
+ size_t count, loff_t *ppos);
+
+ /* fops */
+ struct file_operations *fops;
+
+ /* driver private */
+ void *priv;
+};
+
+int iio_register_device(struct device *parent, struct iio_device *idev);
+int iio_unregister_device(int dev_nr);
+
+#define to_iio_device(n) container_of(n, struct iio_device, dev)
+
+/* char device funktions */
+
+loff_t iio_lseek(struct file *filep, loff_t offset, int orig);
+ssize_t iio_read(struct file *filep, char __user *buf, size_t count, loff_t *ppos);
+ssize_t iio_write(struct file *filep, const char __user * buf, size_t count,
+ loff_t *ppos);
+int iio_open(struct inode *inode, struct file *filep);
+int iio_mmap(struct file *filep, struct vm_area_struct *vma);
+int iio_release(struct inode *inode, struct file *filep);
+
+/* event */
+irqreturn_t iio_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+extern struct file_operations iio_event_fops;
+
+#endif /* _LINUX_IIO_H_ */
--- gregkh-2.6.orig/kernel/signal.c
+++ gregkh-2.6/kernel/signal.c
@@ -1296,6 +1296,7 @@ struct sigqueue *sigqueue_alloc(void)
q->flags |= SIGQUEUE_PREALLOC;
return(q);
}
+EXPORT_SYMBOL_GPL(sigqueue_alloc);

void sigqueue_free(struct sigqueue *q)
{
@@ -1317,6 +1318,7 @@ void sigqueue_free(struct sigqueue *q)
q->flags &= ~SIGQUEUE_PREALLOC;
__sigqueue_free(q);
}
+EXPORT_SYMBOL_GPL(sigqueue_free);

int send_sigqueue(int sig, struct sigqueue *q, struct task_struct *p)
{
@@ -1367,6 +1369,7 @@ out_err:

return ret;
}
+EXPORT_SYMBOL_GPL(send_sigqueue);

int
send_group_sigqueue(int sig, struct sigqueue *q, struct task_struct *p)
@@ -1412,6 +1415,7 @@ out:
read_unlock(&tasklist_lock);
return ret;
}
+EXPORT_SYMBOL_GPL(send_group_sigqueue);

/*
* Wake up any threads in the parent blocked in wait* syscalls.


2006-08-30 10:05:46

by Thomas Gleixner

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers

On Tue, 2006-08-29 at 23:23 -0700, Greg KH wrote:
> So, here's the code. I think it does a bit too much all at once, but it
> is an example of how this can be done. This is working today in some
> industrial environments, successfully handling hardware controls of very
> large and scary machines. So it is possible to use this interface to
> successfully build your own laser wielding robot, all from userspace,
> allowing you to keep your special-secret-how-to-control-the-laser
> algorithm closed source if you so desire.
>
> In looking at the proposed kevent interface, I think a few things in
> this proposed interface can be dropped in favor of using kevents
> instead, but I haven't looked at the latest version of that code to make
> sure of this.

Yeah, that makes sense.

> And the name is a bit ackward, anyone have a better suggestion?
>
> Thomas has also promised to come up with some userspace code that uses
> this interface to show how to use it, but seems to have forgotten.
> Consider this a reminder :)

Yup. I pinged the customer again to get the "non-secret" :) parts
published.

tglx


2006-08-30 14:22:42

by Matt Porter

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers

On Tue, Aug 29, 2006 at 11:23:38PM -0700, Greg KH wrote:
> A while ago, Thomas and I were sitting in the back of a conference
> presentation where the presenter was trying to describe what they did in
> order to add the ability to write a userspace PCI driver. As was usual
> in a presentation like this, the presenter totaly ignored the real-world
> needs for such a framework, and only got it up and working on a single
> type of embedded system. But the charts and graphs were quite pretty :)
>
> Thomas and I lamented that we were getting tired of seeing stuff like
> this, and it was about time that we added the proper code to the kernel
> to provide everything that would be needed in order to write PCI drivers
> in userspace in a sane manner. Really all that is needed is a way to
> handle the interrupt, everything else can already be done in userspace
> (look at X for an example of this...)

What about portable access to the PCI DMA API from userspace? I didn't
think we had a way to handle DMA buffer management entirely from
userspace. Looking at this code, it seems focused on one particular
problem space of industrial control. If the goal is to allow most
PCI drivers to be implemented in user space then I think it makes further
sense to not limit the subsystem to PCI but any device (like things that
are implemented as platform devices in kernel space).

> Thomas mentioned that he had code to do all of this working in some
> customer sites already and that he would get it to me.
>
> Fast forward to OLS of this year, and I bugged Thomas to send me the
> code. He did, and then I sat on it for a while longer...
>
> So, here's the code. I think it does a bit too much all at once, but it
> is an example of how this can be done. This is working today in some
> industrial environments, successfully handling hardware controls of very
> large and scary machines. So it is possible to use this interface to
> successfully build your own laser wielding robot, all from userspace,
> allowing you to keep your special-secret-how-to-control-the-laser
> algorithm closed source if you so desire.
>
> In looking at the proposed kevent interface, I think a few things in
> this proposed interface can be dropped in favor of using kevents
> instead, but I haven't looked at the latest version of that code to make
> sure of this.
>
> And the name is a bit ackward, anyone have a better suggestion?

Well, if it's focused on industrial controls like it appears from
the code here then the name is fine. If it's a starting point to
become someting more generic then User Space Driver (USD) subsystem
might be nice.

A more generic system would go a long way to get rid of the dubious
secret-sauce binary kernel modules that are popular in embedded
linux projects.

> Thomas has also promised to come up with some userspace code that uses
> this interface to show how to use it, but seems to have forgotten.
> Consider this a reminder :)

That would be nice to see. I can't see how devices and drivers
are registered from user space with what's here. I see
iio_register_device() exported but no clue how userspace tells
the kernel to claim a device or register an iio driver.

-Matt

2006-08-30 16:22:14

by Thomas Gleixner

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers

On Wed, 2006-08-30 at 09:34 -0500, Matt Porter wrote:
> On Tue, Aug 29, 2006 at 11:23:38PM -0700, Greg KH wrote:
> > Thomas and I lamented that we were getting tired of seeing stuff like
> > this, and it was about time that we added the proper code to the kernel
> > to provide everything that would be needed in order to write PCI drivers
> > in userspace in a sane manner. Really all that is needed is a way to
> > handle the interrupt, everything else can already be done in userspace
> > (look at X for an example of this...)
>
> What about portable access to the PCI DMA API from userspace? I didn't
> think we had a way to handle DMA buffer management entirely from
> userspace. Looking at this code, it seems focused on one particular
> problem space of industrial control. If the goal is to allow most
> PCI drivers to be implemented in user space then I think it makes further
> sense to not limit the subsystem to PCI but any device (like things that
> are implemented as platform devices in kernel space).

The code was written to have a unified kernel / userspace interface for
a range of industrial I/O hardware - hence the name.

There is no real limitation for that type of devices and it is not
restricted to PCI in any way. We had no need for DMA in the first place,
but this should be an orthogonal extension.

> > And the name is a bit ackward, anyone have a better suggestion?
>
> Well, if it's focused on industrial controls like it appears from
> the code here then the name is fine. If it's a starting point to
> become someting more generic then User Space Driver (USD) subsystem
> might be nice.

Yup.

> A more generic system would go a long way to get rid of the dubious
> secret-sauce binary kernel modules that are popular in embedded
> linux projects.
>
> > Thomas has also promised to come up with some userspace code that uses
> > this interface to show how to use it, but seems to have forgotten.
> > Consider this a reminder :)
>
> That would be nice to see. I can't see how devices and drivers
> are registered from user space with what's here. I see
> iio_register_device() exported but no clue how userspace tells
> the kernel to claim a device or register an iio driver.

You still need a stub driver in the kernel. See below. The userspace
side is just opening the device which is created by udev and uses the
sysfs provided information to mmap the device space and to register
notifications, which might be replaced by kevent.

The example below is a driver for a SERCOS chip, which is a motion
control bus master. All we have in kernel is the register / unregister
function and the primary interrupt acknowledge of the chip, which allows
us to have this interrupt shared with other interrupt sources.

/**
* $Date: 2006-08-24 15:00:46 +0200 (Do, 24 Aug 2006) $
* $Author: baueh $
* $Rev: 370 $
*
* Sercos kernel stub driver
*
* Copyright (C) 2006 Berger Lahr GmbH
* Author: Thomas Bauer <[email protected]>
*
* This code is GPL V2.
*/

#include <linux/fs.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/iio.h>
#include <linux/delay.h>

#include "berger.h"

static struct pci_device_id sercos_table[] __devinitdata = {
{ MY_VENDOR_ID, MY_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ 0, }
};

MODULE_DEVICE_TABLE(pci, sercos_table);

/*
* Use the generic file operations
*/
static struct file_operations generic_fops = {
.open = iio_open,
.release = iio_release,
.read = iio_read,
.write = iio_write,
.llseek = iio_lseek,
.mmap = iio_mmap,
};

/*
* The chip specific portion of the interrupt handler. The framework code
* takes care of userspace notification when we return IRQ_HANDLED
*/
static irqreturn_t sercos_handler (int irq, void *dev_id, struct pt_regs *reg)
{
struct iio_device *idev = (struct iio_device *) dev_id;

/* Check, if this interrupt is originated from the SERCOS chip */
if (!(*((u16 *)(idev->virtaddr + SERCOS_INTERRUPT_STATUS)) & SERCOS_INTERRUPT_MASK))
return IRQ_NONE;

/* Acknowledge the chip interrupts */
*((u16 *)(idev->virtaddr + SERCOS_INTERRUPT_ACK1_OFFSET)) = SERCOS_INTERRUPT_ACK1;
*((u16 *)(idev->virtaddr + SERCOS_INTERRUPT_ACK2_OFFSET)) = SERCOS_INTERRUPT_ACK2;

return IRQ_HANDLED;
}

static struct iio_device sercos_idev = {
.name = "SERCON816",
.version = "0x0010",
.fops = &generic_fops,
.handler = sercos_handler,
};

static struct sercos_idevs
{
int sercos;
} sercos_idevs;

static int sercos_register (struct pci_dev *pdev, const struct pci_device_id *ent)
{
if (pci_enable_device(pdev) != 0) {
printk(KERN_ERR "sercos: Cannot enable PCI device\n");
return -ENODEV;
}

/* FIXME: Read this from PCI ! */
sercos_idev.size = SERCOS_SIZE;
sercos_idev.virtaddr = ioremap(pci_resource_start (pdev, 0), sercos_idev.size);
sercos_idev.physaddr = pci_resource_start(pdev, 0);
sercos_idev.irq = SERCOS_IRQ;

sercos_idevs.sercos = iio_register_device(&pdev->dev, &sercos_idev);

printk(KERN_INFO "SERCOS = %d\n",sercos_idevs.sercos);
printk(KERN_INFO "SERCOS registered at Phys. address 0x%lx \n", sercos_idev.physaddr);
printk(KERN_INFO "SERCOS registered at Virt. address 0x%x \n", (unsigned int) sercos_idev.virtaddr);
printk(KERN_INFO "SERCOS SIZE 0x%lx \n", sercos_idev.size);

pdev->dev.driver_data = &sercos_idevs;
return 0;
}


static void __devexit sercos_unregister (struct pci_dev *pdev)
{
if (sercos_idevs.sercos)
iio_unregister_device(sercos_idevs.sercos);

release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
}

static struct pci_driver sercos_driver = {
.name = "sercos",
.id_table = sercos_table,
.probe = sercos_register,
.remove = __devexit_p(sercos_unregister),
};

static int __init sercos_init (void)
{
return pci_register_driver (&sercos_driver);
}

static void __exit sercos_exit (void)
{
pci_unregister_driver (&sercos_driver);
}

module_init(sercos_init);
module_exit(sercos_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("BergerLahr");
MODULE_DESCRIPTION("sercos driver");

/*
* Userspace portion of the SERCOS driver - simplified
*
* iionum - the iio device number to access:
* /dev/iioX
* /dev/iioX_event
* /sys/class/iio/iioX
*/
int sercos_driver(int iionum)
{
int fd;
char fnam[64];
size_t maplen;
uint16_t *sercos;

sprintf(fnam, "/dev/iio%d", iionum);
fd = open(fnam, O_RDWR);
if (fd < 0)
return -EIO;

/* Get map length from /sys/class/iioX/size */
if (iiolib_getmaplen(iionum, &maplen)) {
close(fd);
return -ENODEV;
}

/* Map the chip space */
sercos = mmap(NULL, maplen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (sercos == MAP_FAILED) {
close(fd);
return -ENODEV;
}
close(fd);

/* Initialize the chip */
sercos_init_chip(sercos);

/*
* Open the event channel. We are the only user, but this could also
* be done by requesting notification by a signal.
*/
sprintf(fnam, "/dev/iio%d_event", iionum);
fd = open(fnam, O_RDONLY);
if (fd < 0) {
sercos_reset_chip(sercos);
munmap(sercos, maplen);
return -EIO;
}

/* interrupt event loop */
while (!stop_sercos) {
char c;
/* Block on the interrupt event */
read(fd, &c, 1);
sercos_handle_interrupt(sercos);
}

sercos_reset_chip(sercos);
munmap(sercos, maplen);

return 0;
}


2006-08-30 17:07:51

by Manu Abraham

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers

Greg KH wrote:
> A while ago, Thomas and I were sitting in the back of a conference
> presentation where the presenter was trying to describe what they did in
> order to add the ability to write a userspace PCI driver. As was usual
> in a presentation like this, the presenter totaly ignored the real-world
> needs for such a framework, and only got it up and working on a single
> type of embedded system. But the charts and graphs were quite pretty :)
>
> Thomas and I lamented that we were getting tired of seeing stuff like
> this, and it was about time that we added the proper code to the kernel
> to provide everything that would be needed in order to write PCI drivers
> in userspace in a sane manner. Really all that is needed is a way to
> handle the interrupt, everything else can already be done in userspace
> (look at X for an example of this...)


A while back, while we (myself and Andrew De Quincey) were struggling
with new DVB frontend devices that needed math operations to be
performed in the frontends themselves for frequency tracking / locking
etc, we found that eventually userspace was a much better place to have
such code, rather than being in kernel.

After quite some thoughts we found it would be much better to have them
implemented in userspace. We did finally draw up to some conclusions,
eventually.

We came up with some code eventually, but time was limited on our hands
that we went into discussion of the newer delivery systems that came
along, so development was a bit halted in that aspect.

http://www.thadathil.net/dvb/msg_transfer_interface
http://thadathil.net:8000/cgi-bin/hgwebdir.cgi/v4l-dvb-user
http://thadathil.net:8000/cgi-bin/hgwebdir.cgi/libdvbapi

>
> Thomas mentioned that he had code to do all of this working in some
> customer sites already and that he would get it to me.
>
> Fast forward to OLS of this year, and I bugged Thomas to send me the
> code. He did, and then I sat on it for a while longer...
>
> So, here's the code. I think it does a bit too much all at once, but it
> is an example of how this can be done. This is working today in some
> industrial environments, successfully handling hardware controls of very
> large and scary machines. So it is possible to use this interface to
> successfully build your own laser wielding robot, all from userspace,
> allowing you to keep your special-secret-how-to-control-the-laser
> algorithm closed source if you so desire.
>
> In looking at the proposed kevent interface, I think a few things in
> this proposed interface can be dropped in favor of using kevents
> instead, but I haven't looked at the latest version of that code to make
> sure of this.
>
> And the name is a bit ackward, anyone have a better suggestion?
>
> Thomas has also promised to come up with some userspace code that uses
> this interface to show how to use it, but seems to have forgotten.
> Consider this a reminder :)
>
> Comments?


Being a bit excited and it is really interesting to have such a
proposal, it would simplify the matters that held us up even more,
probably. The name sounds fine though. All i was wondering whether there
would be any high latencies for the same using in such a context. But
since the transfers would occur in any way, even with a kernel mode
driver, i think it should be pretty much fine.


Manu


2006-08-30 17:54:12

by Greg KH

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers

On Wed, Aug 30, 2006 at 09:07:44PM +0400, Manu Abraham wrote:
>
> Being a bit excited and it is really interesting to have such a
> proposal, it would simplify the matters that held us up even more,
> probably. The name sounds fine though. All i was wondering whether there
> would be any high latencies for the same using in such a context. But
> since the transfers would occur in any way, even with a kernel mode
> driver, i think it should be pretty much fine.

As mentioned, this framework is being used in industrial settings right
now, where latencies are a huge issue. It works just fine, so I do not
think there are any problems in this area.

thanks,

greg k-h

2006-08-30 17:56:52

by Greg KH

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers

On Wed, Aug 30, 2006 at 09:34:10AM -0500, Matt Porter wrote:
> On Tue, Aug 29, 2006 at 11:23:38PM -0700, Greg KH wrote:
> > A while ago, Thomas and I were sitting in the back of a conference
> > presentation where the presenter was trying to describe what they did in
> > order to add the ability to write a userspace PCI driver. As was usual
> > in a presentation like this, the presenter totaly ignored the real-world
> > needs for such a framework, and only got it up and working on a single
> > type of embedded system. But the charts and graphs were quite pretty :)
> >
> > Thomas and I lamented that we were getting tired of seeing stuff like
> > this, and it was about time that we added the proper code to the kernel
> > to provide everything that would be needed in order to write PCI drivers
> > in userspace in a sane manner. Really all that is needed is a way to
> > handle the interrupt, everything else can already be done in userspace
> > (look at X for an example of this...)
>
> What about portable access to the PCI DMA API from userspace?

It currently does not provide this, but if you know how your card works,
I'm pretty sure you can get this working without such an interface.

If you wish to add this functionality, to make it easier, that would be
great.

> > Thomas mentioned that he had code to do all of this working in some
> > customer sites already and that he would get it to me.
> >
> > Fast forward to OLS of this year, and I bugged Thomas to send me the
> > code. He did, and then I sat on it for a while longer...
> >
> > So, here's the code. I think it does a bit too much all at once, but it
> > is an example of how this can be done. This is working today in some
> > industrial environments, successfully handling hardware controls of very
> > large and scary machines. So it is possible to use this interface to
> > successfully build your own laser wielding robot, all from userspace,
> > allowing you to keep your special-secret-how-to-control-the-laser
> > algorithm closed source if you so desire.
> >
> > In looking at the proposed kevent interface, I think a few things in
> > this proposed interface can be dropped in favor of using kevents
> > instead, but I haven't looked at the latest version of that code to make
> > sure of this.
> >
> > And the name is a bit ackward, anyone have a better suggestion?
>
> Well, if it's focused on industrial controls like it appears from
> the code here then the name is fine. If it's a starting point to
> become someting more generic then User Space Driver (USD) subsystem
> might be nice.
>
> A more generic system would go a long way to get rid of the dubious
> secret-sauce binary kernel modules that are popular in embedded
> linux projects.

It's not focused on industrial controls at all, that just happens to be
the name of it right now, as that is why it was written. It is much
more generic than that.

"USD" is a bit too close to "USB" for a name. Any other ideas?

> > Thomas has also promised to come up with some userspace code that uses
> > this interface to show how to use it, but seems to have forgotten.
> > Consider this a reminder :)
>
> That would be nice to see. I can't see how devices and drivers
> are registered from user space with what's here. I see
> iio_register_device() exported but no clue how userspace tells
> the kernel to claim a device or register an iio driver.

His posted example should show you how this all works together.

thanks,

greg k-h

2006-08-30 20:32:46

by Foli Ayivoh

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers

>
> On Wed, Aug 30, 2006 at 09:34:10AM -0500, Matt Porter wrote:
> > On Tue, Aug 29, 2006 at 11:23:38PM -0700, Greg KH wrote:
> > > A while ago, Thomas and I were sitting in the back of a conference
.
.
.
> more generic than that.
>
> "USD" is a bit too close to "USB" for a name. Any other ideas?
what about U_IO ;-)
>
> > > Thomas has also promised to come up with some userspace code that uses
> > > this interface to show how to use it, but seems to have forgotten.
> > > Consider this a reminder :)
> >
> > That would be nice to see. I can't see how devices and drivers
> > are registered from user space with what's here. I see
> > iio_register_device() exported but no clue how userspace tells
> > the kernel to claim a device or register an iio driver.
>
> His posted example should show you how this all works together.
>
> thanks,
>
> greg k-h
>

2006-08-30 21:01:40

by Matthias Schniedermeyer

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers

Greg KH wrote:
> On Wed, Aug 30, 2006 at 09:34:10AM -0500, Matt Porter wrote:
>>Well, if it's focused on industrial controls like it appears from
>>the code here then the name is fine. If it's a starting point to
>>become someting more generic then User Space Driver (USD) subsystem
>>might be nice.
>>
> It's not focused on industrial controls at all, that just happens to be
> the name of it right now, as that is why it was written. It is much
> more generic than that.
>
> "USD" is a bit too close to "USB" for a name. Any other ideas?

DFU - Drivers for Userspace
DFU - Driver from Userspace
DFUF - Drivers for Userspace Framework
DIFUS - Drive It From Userspace :-)
FUD - Framework (for) Userspace Drivers ;-)
GUDI - Generic Userspace Driver Interface
UDF - Userspace Driver Framework :-)
UDI - Userspace Driver Interface :-)
UUDI - Unified Userspace Driver Interface
UFD - Userspace Framework (for) Drivers

...




Bis denn

--
Real Programmers consider "what you see is what you get" to be just as
bad a concept in Text Editors as it is in women. No, the Real Programmer
wants a "you asked for it, you got it" text editor -- complicated,
cryptic, powerful, unforgiving, dangerous.

2006-08-30 21:25:57

by linux-os (Dick Johnson)

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers


On Wed, 30 Aug 2006, Matthias Schniedermeyer wrote:

> Greg KH wrote:
>> On Wed, Aug 30, 2006 at 09:34:10AM -0500, Matt Porter wrote:
>>> Well, if it's focused on industrial controls like it appears from
>>> the code here then the name is fine. If it's a starting point to
>>> become someting more generic then User Space Driver (USD) subsystem
>>> might be nice.
>>>
>> It's not focused on industrial controls at all, that just happens to be
>> the name of it right now, as that is why it was written. It is much
>> more generic than that.
>>
>> "USD" is a bit too close to "USB" for a name. Any other ideas?
>
> DFU - Drivers for Userspace
> DFU - Driver from Userspace
> DFUF - Drivers for Userspace Framework
> DIFUS - Drive It From Userspace :-)
> FUD - Framework (for) Userspace Drivers ;-)
> GUDI - Generic Userspace Driver Interface
> UDF - Userspace Driver Framework :-)
> UDI - Userspace Driver Interface :-)
> UUDI - Unified Userspace Driver Interface
> UFD - Userspace Framework (for) Drivers
>
> Bis denn
>

Something to consider if/when you expose PCI to user-space is
that protection on ix86 extends to PAGE_SIZE pages. This means
that when you ioremap() some PCI addresses to user-space, user-mode
code can write (or read) well beyond the few bytes that you may
think that you have mapped! When you make a driver in the kernel,
your code can check to see if the read or write is going to be
out-of-bounds. When you mmap() something to user-space, its the
user-space code that needs to protect hardware and/or the kernel.
This is not good.

Let's say that I mapped() 512 bytes to access my controller. If
I have a another controller, perhaps one for my SCSI disk, that's
within that allocated page, I can easily "deprogram" the controller
and fail the system, file-system, or both.

User mode drivers are NOT good. For them to work, too much stuff
needs to be exposed. Then there is the problem of interrupts.
You are not going to be able to make an interrupt 'thunker' like
you could in the old DOS days. The kernel isn't going to call
user-mode code because nobody is going to provide such a potentially
destructive interface. If you think you can handle hardware interrupt
requests with the poll() interface, you are going to be in a world of
hurt for throughput.


Cheers,
Dick Johnson
Penguin : Linux version 2.6.16.24 on an i686 machine (5592.62 BogoMips).
New book: http://www.AbominableFirebug.com/
_


****************************************************************
The information transmitted in this message is confidential and may be privileged. Any review, retransmission, dissemination, or other use of this information by persons or entities other than the intended recipient is prohibited. If you are not the intended recipient, please notify Analogic Corporation immediately - by replying to this message or by sending an email to [email protected] - and destroy all copies of this information, including any attachments, without reading or disclosing them.

Thank you.

2006-08-30 22:10:20

by Greg KH

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers

On Wed, Aug 30, 2006 at 05:25:45PM -0400, linux-os (Dick Johnson) wrote:
> User mode drivers are NOT good. For them to work, too much stuff
> needs to be exposed. Then there is the problem of interrupts.
> You are not going to be able to make an interrupt 'thunker' like
> you could in the old DOS days. The kernel isn't going to call
> user-mode code because nobody is going to provide such a potentially
> destructive interface. If you think you can handle hardware interrupt
> requests with the poll() interface, you are going to be in a world of
> hurt for throughput.

If you feel this way, then don't use them on your system, for your
hardware.

2006-08-30 22:51:09

by Manu Abraham

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers

Greg KH wrote:
> On Wed, Aug 30, 2006 at 09:07:44PM +0400, Manu Abraham wrote:
>> Being a bit excited and it is really interesting to have such a
>> proposal, it would simplify the matters that held us up even more,
>> probably. The name sounds fine though. All i was wondering whether there
>> would be any high latencies for the same using in such a context. But
>> since the transfers would occur in any way, even with a kernel mode
>> driver, i think it should be pretty much fine.
>
> As mentioned, this framework is being used in industrial settings right
> now, where latencies are a huge issue. It works just fine, so I do not
> think there are any problems in this area.

Cool.

Is there some way we can avoid the poll ? It would be a real gain
indeed, if a POLL can be avoided.

Regards,
Manu


2006-08-31 00:19:03

by Greg KH

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers

On Thu, Aug 31, 2006 at 02:50:55AM +0400, Manu Abraham wrote:
> Greg KH wrote:
> > On Wed, Aug 30, 2006 at 09:07:44PM +0400, Manu Abraham wrote:
> >> Being a bit excited and it is really interesting to have such a
> >> proposal, it would simplify the matters that held us up even more,
> >> probably. The name sounds fine though. All i was wondering whether there
> >> would be any high latencies for the same using in such a context. But
> >> since the transfers would occur in any way, even with a kernel mode
> >> driver, i think it should be pretty much fine.
> >
> > As mentioned, this framework is being used in industrial settings right
> > now, where latencies are a huge issue. It works just fine, so I do not
> > think there are any problems in this area.
>
> Cool.
>
> Is there some way we can avoid the poll ? It would be a real gain
> indeed, if a POLL can be avoided.

Use the signal that will be sent to your userspace program when an
interrupt happens.

If you can handle the small latency that causes it should be fine, but
if you can't, then you should be using poll :)

It all depends on the hardware you are using, your processor, and what
your tolerances are on your interrupt handling latency.

thanks,

greg k-h

2006-08-31 07:40:55

by Andrew Morton

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers

On Tue, 29 Aug 2006 23:23:38 -0700
Greg KH <[email protected]> wrote:

> +static ssize_t store_sig_pid(struct device *dev, struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + iio_dummy_signal.pid = simple_strtol(buf, NULL, 10);
> + if (iio_dummy_signal.pid == 0) {
> + if (iio_dummy_signal.it_process) {
> + put_task_struct(iio_dummy_signal.it_process);
> + iio_dummy_signal.it_process = NULL;
> + }
> +
> + iio_dummy_signal.pid = 0;
> + return count;
> + }
> +
> + if (iio_dummy_signal.pid == 1)
> + goto out;
> +
> + iio_dummy_signal.it_process = find_task_by_pid(iio_dummy_signal.pid);
> + if (iio_dummy_signal.it_process) {
> + get_task_struct(iio_dummy_signal.it_process);
> + iio_dummy_signal.it_sigev_notify = SIGEV_SIGNAL;
> + iio_dummy_signal.it_sigev_signo = SIGALRM;
> + iio_dummy_signal.it_sigev_value.sival_int = 0;
> +
> + return count;
> + }
> +out:
> + iio_dummy_signal.pid = 0;
> + return -EINVAL;
> +}

This is racy: find_task_by_pid() needs tasklist_lock or rcu_read_lock().

It doesn't work as a module due to missing __put_task_struct.


It is also rather nasty. Why go shoving some random pid into a sysfs file,
then hang onto a ref on a task_struct for some process which exitted last
week? It would be cleaner and more idiomatic to require that the
controlling process hold an fd open against the instance so that resources
can be managed correctly. Maybe use SIGIO too, so the driver doesn't need
to know about pids and task_structs and things. Which all maps better onto
ioctls than sysfs (ties self to stake)

<looks>

iio_dev.c seems to already be doing this. Why does iio_dummy.c exist?

2006-08-31 08:01:32

by Thomas Gleixner

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers

On Thu, 2006-08-31 at 00:40 -0700, Andrew Morton wrote:
> On Tue, 29 Aug 2006 23:23:38 -0700
> Greg KH <[email protected]> wrote:
>
> > +static ssize_t store_sig_pid(struct device *dev, struct device_attribute *attr,
> > + const char *buf, size_t count)
> > +{
> > + iio_dummy_signal.pid = simple_strtol(buf, NULL, 10);
> > + if (iio_dummy_signal.pid == 0) {
> > + if (iio_dummy_signal.it_process) {
> > + put_task_struct(iio_dummy_signal.it_process);
> > + iio_dummy_signal.it_process = NULL;
> > + }
> > +
> > + iio_dummy_signal.pid = 0;
> > + return count;
> > + }
> > +
> > + if (iio_dummy_signal.pid == 1)
> > + goto out;
> > +
> > + iio_dummy_signal.it_process = find_task_by_pid(iio_dummy_signal.pid);
> > + if (iio_dummy_signal.it_process) {
> > + get_task_struct(iio_dummy_signal.it_process);
> > + iio_dummy_signal.it_sigev_notify = SIGEV_SIGNAL;
> > + iio_dummy_signal.it_sigev_signo = SIGALRM;
> > + iio_dummy_signal.it_sigev_value.sival_int = 0;
> > +
> > + return count;
> > + }
> > +out:
> > + iio_dummy_signal.pid = 0;
> > + return -EINVAL;
> > +}
>
> This is racy: find_task_by_pid() needs tasklist_lock or rcu_read_lock().
>
> It doesn't work as a module due to missing __put_task_struct.
>
>
> It is also rather nasty. Why go shoving some random pid into a sysfs file,
> then hang onto a ref on a task_struct for some process which exitted last
> week? It would be cleaner and more idiomatic to require that the
> controlling process hold an fd open against the instance so that resources
> can be managed correctly. Maybe use SIGIO too, so the driver doesn't need
> to know about pids and task_structs and things. Which all maps better onto
> ioctls than sysfs (ties self to stake)
>
> <looks>
>
> iio_dev.c seems to already be doing this. Why does iio_dummy.c exist?

Uurg. That's an old dummy/test driver which never got updated.

tglx


2006-08-31 08:30:33

by Xavier Bestel

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers

Hi Greg,

On Wed, 2006-08-30 at 08:23, Greg KH wrote:
[...]
> Thomas and I lamented that we were getting tired of seeing stuff like
> this, and it was about time that we added the proper code to the kernel
> to provide everything that would be needed in order to write PCI drivers
> in userspace in a sane manner. Really all that is needed is a way to
> handle the interrupt, everything else can already be done in userspace
> (look at X for an example of this...)
[...]
> So, here's the code.

I know I look like I'm trolling here, but as this is the perfect
stable-binary-driver-ABI some people have been looking for, it would be
wise to make it taint the kernel with its own flag.

Xav

2006-08-31 13:24:50

by Manu Abraham

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers

Greg KH wrote:
> On Thu, Aug 31, 2006 at 02:50:55AM +0400, Manu Abraham wrote:
>> Greg KH wrote:
>>> On Wed, Aug 30, 2006 at 09:07:44PM +0400, Manu Abraham wrote:
>>>> Being a bit excited and it is really interesting to have such a
>>>> proposal, it would simplify the matters that held us up even more,
>>>> probably. The name sounds fine though. All i was wondering whether there
>>>> would be any high latencies for the same using in such a context. But
>>>> since the transfers would occur in any way, even with a kernel mode
>>>> driver, i think it should be pretty much fine.
>>> As mentioned, this framework is being used in industrial settings right
>>> now, where latencies are a huge issue. It works just fine, so I do not
>>> think there are any problems in this area.
>> Cool.
>>
>> Is there some way we can avoid the poll ? It would be a real gain
>> indeed, if a POLL can be avoided.
>
> Use the signal that will be sent to your userspace program when an
> interrupt happens.
>


Ok, that said, If we had RT signalling, that would have been a quantum leap.


> If you can handle the small latency that causes it should be fine, but
> if you can't, then you should be using poll :)
>

hmm.. having a choice does always hurt your brain. ;-)


> It all depends on the hardware you are using, your processor, and what
> your tolerances are on your interrupt handling latency.


Usually in the typical application we have (where latency is an issue),
most probably many of the people have a saturated PCI bus. In most
cases, the IPTV guys have such a scenario. Say > 6 or 7 DVB adapters and
the latency goes very high. What i have seen is that when the bus gets
saturated, the CPU usage shoots of rather abnormally. When the latency
goes higher, the resultant stream is useless and packets needs to be
dropped, eventually that results in Transport Stream discontinuities.

Currently we already have a latency issue, based on the loud cries from
some people.

Thanks,
Manu

2006-08-31 13:49:14

by Andi Kleen

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers

Matt Porter <[email protected]> writes:
>
> What about portable access to the PCI DMA API from userspace?

We'll definitely need this for X11 anyways. Currently it is not
possible to run the standard X server with a IOMMU that isolates
the graphics card because it has no way to get at the GPU MMIO
registers then.

My long-term plan was to integrate it in /sys/bus/pci mmaps
(together with PAT etc.). When you mmap it there the kernel
allocates a DMA mapping and then frees it on unmap.

Then it should hopefully just work.

-Andi

2006-08-31 20:39:08

by Lee Revell

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers

On Thu, 2006-08-31 at 10:30 +0200, Xavier Bestel wrote:
> Hi Greg,
>
> On Wed, 2006-08-30 at 08:23, Greg KH wrote:
> [...]
> > Thomas and I lamented that we were getting tired of seeing stuff like
> > this, and it was about time that we added the proper code to the kernel
> > to provide everything that would be needed in order to write PCI drivers
> > in userspace in a sane manner. Really all that is needed is a way to
> > handle the interrupt, everything else can already be done in userspace
> > (look at X for an example of this...)
> [...]
> > So, here's the code.
>
> I know I look like I'm trolling here, but as this is the perfect
> stable-binary-driver-ABI some people have been looking for, it would be
> wise to make it taint the kernel with its own flag.

Why? There's no technical or legal requirement for userspace drivers to
be GPLed.

Lee

2006-08-31 20:54:28

by Chris Friesen

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers

Lee Revell wrote:

> Why? There's no technical or legal requirement for userspace drivers to
> be GPLed.

I could see a benefit to tainting the kernel once userspace starts
poking at the hardware directly. That way at least we'd know that a
crash might be due to userspace doing bad things.

For instance, consider the case where userspace misprograms a DMA
engine, which starts overwriting random kernel memory.

Chris

2006-08-31 20:58:23

by Xavier Bestel

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers

Le jeudi 31 ao?t 2006 ? 16:39 -0400, Lee Revell a ?crit :
> On Thu, 2006-08-31 at 10:30 +0200, Xavier Bestel wrote:
> > Hi Greg,
> >
> > On Wed, 2006-08-30 at 08:23, Greg KH wrote:
> > [...]
> > > Thomas and I lamented that we were getting tired of seeing stuff like
> > > this, and it was about time that we added the proper code to the kernel
> > > to provide everything that would be needed in order to write PCI drivers
> > > in userspace in a sane manner. Really all that is needed is a way to
> > > handle the interrupt, everything else can already be done in userspace
> > > (look at X for an example of this...)
> > [...]
> > > So, here's the code.
> >
> > I know I look like I'm trolling here, but as this is the perfect
> > stable-binary-driver-ABI some people have been looking for, it would be
> > wise to make it taint the kernel with its own flag.
>
> Why? There's no technical or legal requirement for userspace drivers to
> be GPLed.

Precisely. How do you know the bugreport you received isn't caused by
some weird binary userspace driver hosing the PCI bus ?

Xav

2006-08-31 21:12:11

by Lee Revell

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers

On Thu, 2006-08-31 at 22:58 +0200, Xavier Bestel wrote:
> Precisely. How do you know the bugreport you received isn't caused by
> some weird binary userspace driver hosing the PCI bus ?

Can't X, or any application that access hardware directly by
mmaping /dev/mem, do this now?

Lee

2006-08-31 21:18:00

by Xavier Bestel

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers

Le jeudi 31 ao?t 2006 ? 17:12 -0400, Lee Revell a ?crit :
> On Thu, 2006-08-31 at 22:58 +0200, Xavier Bestel wrote:
> > Precisely. How do you know the bugreport you received isn't caused by
> > some weird binary userspace driver hosing the PCI bus ?
>
> Can't X, or any application that access hardware directly by
> mmaping /dev/mem, do this now?

Yes they can, but X's behavior is pretty well known by now :) and it's
open source.

Xav

2006-08-31 21:34:01

by Manu Abraham

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers

Xavier Bestel wrote:
> Le jeudi 31 ao?t 2006 ? 17:12 -0400, Lee Revell a ?crit :
>> On Thu, 2006-08-31 at 22:58 +0200, Xavier Bestel wrote:
>>> Precisely. How do you know the bugreport you received isn't caused by
>>> some weird binary userspace driver hosing the PCI bus ?
>> Can't X, or any application that access hardware directly by
>> mmaping /dev/mem, do this now?
>
> Yes they can, but X's behavior is pretty well known by now :) and it's
> open source.
>

what about binary drivers in kernel like nVidia or ATI ?

Well above all, user-space drivers can be of help in a lot of other
ways. An example is rapid prototyping. Some times it makes sense as well
to do in user-space, where you can really control things. Drivers using
math is an example


Manu

2006-08-31 21:39:55

by Greg KH

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers

On Thu, Aug 31, 2006 at 02:53:58PM -0600, Chris Friesen wrote:
> Lee Revell wrote:
>
> >Why? There's no technical or legal requirement for userspace drivers to
> >be GPLed.
>
> I could see a benefit to tainting the kernel once userspace starts
> poking at the hardware directly. That way at least we'd know that a
> crash might be due to userspace doing bad things.
>
> For instance, consider the case where userspace misprograms a DMA
> engine, which starts overwriting random kernel memory.

Then you get to keep the two pieces that the kernel just broke into.

We can't "taint" anything here. Userspace is doing these kinds of
things already today. You can mmap a PCI device and plug away at all
sorts of fun things with it right now. You can to /dev/mem fun tricks,
and all sorts of other 'evil' things.

And us kernel developers don't really care.

What this framework does, is give users who already do this kind of
userspace driver thing today, a sane interface in which to do it. One
that is simple and fast, and makes sense, unlike almost every other
proposal like this that I have seen in the past.

We are trying to help people out here. Pushing drivers to userspace is
a good thing from a kernel's perspective, if they work well for the
driver in question. USB has been doing this for years quite well. This
just allows the PCI interrupt to be sanely handled so PCI and other
types of devices like it, can also work in a well defined way.

If a author of a driver doesn't want to put it in the kernel for
whatever reason, that's fine, do it in userspace. Not all types of
drivers make sense in the kernel as they don't need any of the things
kernelspace is good for (speed, caching, common userspace interfaces,
etc.)

So there is nothing to worry about here, except for more Linux based,
laser welding robots taking over your neighborhood manufacturing plant.

And to answer the question that everyones been asking me in private, no,
I have no idea if nVidia or ATI can use this interface to move their
kerneldrivers out into userspace. I've never seen that code and do not
know what their requirements are, nor do I care. Go ask them, they know
this kind of thing, I sure don't. And if they can move them there,
bully for them.

I don't know why people have this morbid fascination with these two,
monstrously horrible closed source drivers. There are way more much
more insidious and common closed source Linux kernel modules out there
right now that everyone in the press seems to be ignoring. Those are
the ones that I worry about...

And those are the ones that this interface might just help out with, and
if that happens, it is a very good thing for Linux overall.

thanks,

greg k-h

2006-08-31 21:41:09

by Greg KH

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers

On Thu, Aug 31, 2006 at 10:58:54PM +0200, Xavier Bestel wrote:
> Le jeudi 31 ao?t 2006 ? 16:39 -0400, Lee Revell a ?crit :
> > On Thu, 2006-08-31 at 10:30 +0200, Xavier Bestel wrote:
> > > Hi Greg,
> > >
> > > On Wed, 2006-08-30 at 08:23, Greg KH wrote:
> > > [...]
> > > > Thomas and I lamented that we were getting tired of seeing stuff like
> > > > this, and it was about time that we added the proper code to the kernel
> > > > to provide everything that would be needed in order to write PCI drivers
> > > > in userspace in a sane manner. Really all that is needed is a way to
> > > > handle the interrupt, everything else can already be done in userspace
> > > > (look at X for an example of this...)
> > > [...]
> > > > So, here's the code.
> > >
> > > I know I look like I'm trolling here, but as this is the perfect
> > > stable-binary-driver-ABI some people have been looking for, it would be
> > > wise to make it taint the kernel with its own flag.
> >
> > Why? There's no technical or legal requirement for userspace drivers to
> > be GPLed.
>
> Precisely. How do you know the bugreport you received isn't caused by
> some weird binary userspace driver hosing the PCI bus ?

You don't know that today. Nothing new here. So no, adding a taint
flag would achieve nothing. Unless you want to do that today for all
the ways a userspace program can directly access a PCI device today?

greg k-h

2006-08-31 22:43:06

by Greg KH

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers

On Thu, Aug 31, 2006 at 05:24:30PM +0400, Manu Abraham wrote:
> Usually in the typical application we have (where latency is an issue),
> most probably many of the people have a saturated PCI bus. In most
> cases, the IPTV guys have such a scenario. Say > 6 or 7 DVB adapters and
> the latency goes very high.

Sure, when you are pushing your hardware to the maximum, you should
expect issues like this. I agree we should do as best as we can for
things like this, but when you over-subscribe your PCI bus by doing
something like this, I really recommend just buying some hardware that
will work better for you (separate PCI busses, etc. The hardware is out
there to do this properly.)

> What i have seen is that when the bus gets saturated, the CPU usage
> shoots of rather abnormally.

As is to be expected.

> When the latency goes higher, the resultant stream is useless and
> packets needs to be dropped, eventually that results in Transport
> Stream discontinuities.

Sure, that's understandable.

> Currently we already have a latency issue, based on the loud cries
> from some people.

Trying to do things the hardware is not ment to do, should not result in
cries from users :)

thanks,

greg k-h

2006-09-01 01:36:34

by Brice Goglin

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers

Greg KH wrote:
> On Wed, Aug 30, 2006 at 09:34:10AM -0500, Matt Porter wrote:
>
>> On Tue, Aug 29, 2006 at 11:23:38PM -0700, Greg KH wrote:
>>
>>> A while ago, Thomas and I were sitting in the back of a conference
>>> presentation where the presenter was trying to describe what they did in
>>> order to add the ability to write a userspace PCI driver. As was usual
>>> in a presentation like this, the presenter totaly ignored the real-world
>>> needs for such a framework, and only got it up and working on a single
>>> type of embedded system. But the charts and graphs were quite pretty :)
>>>
>>> Thomas and I lamented that we were getting tired of seeing stuff like
>>> this, and it was about time that we added the proper code to the kernel
>>> to provide everything that would be needed in order to write PCI drivers
>>> in userspace in a sane manner. Really all that is needed is a way to
>>> handle the interrupt, everything else can already be done in userspace
>>> (look at X for an example of this...)
>>>
>> What about portable access to the PCI DMA API from userspace?
>>
>
> It currently does not provide this, but if you know how your card works,
> I'm pretty sure you can get this working without such an interface.
>
> If you wish to add this functionality, to make it easier, that would be
> great.
>

I might be nice to have something like a copy-block where the
application writes/reads data, while the device does DMA only from/to
there. We would need an easy way to mmap some anonymous DMA-ready memory
in user-space, and something to give the corresponding DMA addresses to
the application.


Additionally, the current code might not be flexible enough regarding
acknowledging of interrupts. It might be good to use the bit that PCI
2.2 defines in the config space to mask/unmask interrupt in a generic
way. Something like : when an interrupt comes, the driver mask the
interrupts using this bit, and then passes the event to user-space. The
user-space interrupt handler acknowledges the interrupt with the device
specific code, and then unmask with the PCI 2.2 bit.

Brice

2006-09-01 03:22:54

by Greg KH

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers

On Thu, Aug 31, 2006 at 09:36:08PM -0400, Brice Goglin wrote:
> I might be nice to have something like a copy-block where the
> application writes/reads data, while the device does DMA only from/to
> there. We would need an easy way to mmap some anonymous DMA-ready memory
> in user-space, and something to give the corresponding DMA addresses to
> the application.

Sure, send a patch :)

> Additionally, the current code might not be flexible enough regarding
> acknowledging of interrupts. It might be good to use the bit that PCI
> 2.2 defines in the config space to mask/unmask interrupt in a generic
> way. Something like : when an interrupt comes, the driver mask the
> interrupts using this bit, and then passes the event to user-space. The
> user-space interrupt handler acknowledges the interrupt with the device
> specific code, and then unmask with the PCI 2.2 bit.

You can do that today with this code. Remember, you have to have a
tiny kernelspace portion of your driver to handle the interrupt. You
can do whatever you want in that interrupt handler.

thanks,

greg k-h

2006-09-01 03:38:11

by Brice Goglin

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers

Greg KH wrote:
>> Additionally, the current code might not be flexible enough regarding
>> acknowledging of interrupts. It might be good to use the bit that PCI
>> 2.2 defines in the config space to mask/unmask interrupt in a generic
>> way. Something like : when an interrupt comes, the driver mask the
>> interrupts using this bit, and then passes the event to user-space. The
>> user-space interrupt handler acknowledges the interrupt with the device
>> specific code, and then unmask with the PCI 2.2 bit.
>>
>
> You can do that today with this code. Remember, you have to have a
> tiny kernelspace portion of your driver to handle the interrupt. You
> can do whatever you want in that interrupt handler.
>

The whole point of masking interrupt with this config-space bit is that
we might not need any tiny kernelspace portion for our driver anymore.
It won't work for devices that are not PCI 2.2 compliant. But, it might
be good for the ones that are?

Brice

2006-09-01 04:28:08

by Greg KH

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers

On Thu, Aug 31, 2006 at 11:37:58PM -0400, Brice Goglin wrote:
> Greg KH wrote:
> >> Additionally, the current code might not be flexible enough regarding
> >> acknowledging of interrupts. It might be good to use the bit that PCI
> >> 2.2 defines in the config space to mask/unmask interrupt in a generic
> >> way. Something like : when an interrupt comes, the driver mask the
> >> interrupts using this bit, and then passes the event to user-space. The
> >> user-space interrupt handler acknowledges the interrupt with the device
> >> specific code, and then unmask with the PCI 2.2 bit.
> >>
> >
> > You can do that today with this code. Remember, you have to have a
> > tiny kernelspace portion of your driver to handle the interrupt. You
> > can do whatever you want in that interrupt handler.
> >
>
> The whole point of masking interrupt with this config-space bit is that
> we might not need any tiny kernelspace portion for our driver anymore.
> It won't work for devices that are not PCI 2.2 compliant. But, it might
> be good for the ones that are?

I don't really know. I think 2.2 PCI devices might still be a bit rare,
but do not know for sure.

thanks,

greg k-h

2006-09-01 18:23:33

by Greg KH

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers

On Thu, Aug 31, 2006 at 03:49:10PM +0200, Andi Kleen wrote:
> Matt Porter <[email protected]> writes:
> >
> > What about portable access to the PCI DMA API from userspace?
>
> We'll definitely need this for X11 anyways. Currently it is not
> possible to run the standard X server with a IOMMU that isolates
> the graphics card because it has no way to get at the GPU MMIO
> registers then.
>
> My long-term plan was to integrate it in /sys/bus/pci mmaps
> (together with PAT etc.). When you mmap it there the kernel
> allocates a DMA mapping and then frees it on unmap.

That sounds very reasonable, looking forward to it.

thanks,

greg k-h

2006-09-03 07:33:32

by Pavel Machek

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers

Hi!

> And the name is a bit ackward, anyone have a better suggestion?

drivers/uio (for userspace io driver)?

And yes, I think this one _should_ taint the kernel. When userspace
starts playing with interrupts, chances of kernel crash are high.

Does it work properly with pci shared interrupts?
--
Thanks for all the (sleeping) penguins.

--
VGER BF report: H 0.000174266

2006-09-03 08:30:22

by Thomas Gleixner

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers

On Fri, 2006-09-01 at 21:17 +0000, Pavel Machek wrote:
> Hi!
>
> > And the name is a bit ackward, anyone have a better suggestion?
>
> drivers/uio (for userspace io driver)?

not too bad.

> And yes, I think this one _should_ taint the kernel. When userspace
> starts playing with interrupts, chances of kernel crash are high.

Userspace does not play with interrupts directly. You still have a small
stub driver, which does the primary interrupt handling, else you would
have a hard time to deal with shared irqs.

> Does it work properly with pci shared interrupts?

See the SERCOS example I posted.

tglx



--
VGER BF report: U 0.500128

2006-09-12 19:42:14

by Sam Ravnborg

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers

On Tue, Aug 29, 2006 at 11:23:38PM -0700, Greg KH wrote:
>
> So, here's the code. I think it does a bit too much all at once, but it
> is an example of how this can be done. This is working today in some
> industrial environments, successfully handling hardware controls of very
> large and scary machines.

At present it is named uio in -mm. But that seems to conflict with a
few cases (uio.h + Solaris).

How about:
Universal User IO => uuio

A quick google did not turn up anything conflicting.

[Lost the mail thread about naming so replying to this old mail instead].

Sam

2006-09-14 06:14:38

by Greg KH

[permalink] [raw]
Subject: Re: [RFC] Simple userspace interface for PCI drivers

On Tue, Sep 12, 2006 at 09:47:14PM +0200, Sam Ravnborg wrote:
> On Tue, Aug 29, 2006 at 11:23:38PM -0700, Greg KH wrote:
> >
> > So, here's the code. I think it does a bit too much all at once, but it
> > is an example of how this can be done. This is working today in some
> > industrial environments, successfully handling hardware controls of very
> > large and scary machines.
>
> At present it is named uio in -mm. But that seems to conflict with a
> few cases (uio.h + Solaris).
>
> How about:
> Universal User IO => uuio
>
> A quick google did not turn up anything conflicting.

Ah, I like that better. I had made the header file be called
uio_driver.h to get around the namespace issue, but uuio sounds nice.

thanks,

greg k-h