staging:iio:add adaptive event types and missing extract_type macro.
[pandora-kernel.git] / drivers / staging / iio / industrialio-core.c
index 19819e7..83e55b1 100644 (file)
 #include <linux/wait.h>
 #include <linux/cdev.h>
 #include <linux/slab.h>
+#include <linux/anon_inodes.h>
 #include "iio.h"
-#include "trigger_consumer.h"
+#include "iio_core.h"
+#include "iio_core_trigger.h"
+#include "chrdev.h"
+#include "sysfs.h"
 
-#define IIO_ID_PREFIX "device"
-#define IIO_ID_FORMAT IIO_ID_PREFIX "%d"
-
-/* IDR to assign each registered device a unique id*/
+/* IDA to assign each registered device a unique id*/
 static DEFINE_IDA(iio_ida);
-/* IDR to allocate character device minor numbers */
-static DEFINE_IDA(iio_chrdev_ida);
-/* Lock used to protect both of the above */
-static DEFINE_SPINLOCK(iio_ida_lock);
 
-dev_t iio_devt;
-EXPORT_SYMBOL(iio_devt);
+static dev_t iio_devt;
 
 #define IIO_DEV_MAX 256
 struct bus_type iio_bus_type = {
@@ -43,13 +39,22 @@ struct bus_type iio_bus_type = {
 };
 EXPORT_SYMBOL(iio_bus_type);
 
+static const char * const iio_data_type_name[] = {
+       [IIO_RAW] = "raw",
+       [IIO_PROCESSED] = "input",
+};
+
+static const char * const iio_direction[] = {
+       [0] = "in",
+       [1] = "out",
+};
+
 static const char * const iio_chan_type_name_spec_shared[] = {
-       [IIO_IN] = "in",
-       [IIO_OUT] = "out",
+       [IIO_VOLTAGE] = "voltage",
        [IIO_CURRENT] = "current",
        [IIO_POWER] = "power",
        [IIO_ACCEL] = "accel",
-       [IIO_IN_DIFF] = "in-in",
+       [IIO_VOLTAGE_DIFF] = "voltage-voltage",
        [IIO_GYRO] = "gyro",
        [IIO_MAGN] = "magn",
        [IIO_LIGHT] = "illuminance",
@@ -60,21 +65,19 @@ static const char * const iio_chan_type_name_spec_shared[] = {
        [IIO_ROT] = "rot",
        [IIO_ANGL] = "angl",
        [IIO_TIMESTAMP] = "timestamp",
+       [IIO_CAPACITANCE] = "capacitance",
 };
 
 static const char * const iio_chan_type_name_spec_complex[] = {
-       [IIO_IN_DIFF] = "in%d-in%d",
+       [IIO_VOLTAGE_DIFF] = "voltage%d-voltage%d",
 };
 
-static const char * const iio_modifier_names_light[] = {
-       [IIO_MOD_LIGHT_BOTH] = "both",
-       [IIO_MOD_LIGHT_IR] = "ir",
-};
-
-static const char * const iio_modifier_names_axial[] = {
+static const char * const iio_modifier_names[] = {
        [IIO_MOD_X] = "x",
        [IIO_MOD_Y] = "y",
        [IIO_MOD_Z] = "z",
+       [IIO_MOD_LIGHT_BOTH] = "both",
+       [IIO_MOD_LIGHT_IR] = "ir",
 };
 
 /* relies on pairs of these shared then separate */
@@ -85,21 +88,51 @@ static const char * const iio_chan_info_postfix[] = {
        [IIO_CHAN_INFO_CALIBBIAS_SHARED/2] = "calibbias",
        [IIO_CHAN_INFO_PEAK_SHARED/2] = "peak_raw",
        [IIO_CHAN_INFO_PEAK_SCALE_SHARED/2] = "peak_scale",
+       [IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW_SHARED/2]
+       = "quadrature_correction_raw",
+       [IIO_CHAN_INFO_AVERAGE_RAW_SHARED/2] = "mean_raw",
+};
+
+/**
+ * struct iio_detected_event_list - list element for events that have occurred
+ * @list:              linked list header
+ * @ev:                        the event itself
+ */
+struct iio_detected_event_list {
+       struct list_head                list;
+       struct iio_event_data           ev;
+};
+
+/**
+ * struct iio_event_interface - chrdev interface for an event line
+ * @dev:               device assocated with event interface
+ * @wait:              wait queue to allow blocking reads of events
+ * @event_list_lock:   mutex to protect the list of detected events
+ * @det_events:                list of detected events
+ * @max_events:                maximum number of events before new ones are dropped
+ * @current_events:    number of events in detected list
+ * @flags:             file operations related flags including busy flag.
+ */
+struct iio_event_interface {
+       wait_queue_head_t                       wait;
+       struct mutex                            event_list_lock;
+       struct list_head                        det_events;
+       int                                     max_events;
+       int                                     current_events;
+       struct list_head dev_attr_list;
+       unsigned long flags;
+       struct attribute_group                  group;
 };
 
-int iio_push_event(struct iio_dev *dev_info,
-                  int ev_line,
-                  int ev_code,
-                  s64 timestamp)
+int iio_push_event(struct iio_dev *dev_info, u64 ev_code, s64 timestamp)
 {
-       struct iio_event_interface *ev_int
-               = &dev_info->event_interfaces[ev_line];
+       struct iio_event_interface *ev_int = dev_info->event_interface;
        struct iio_detected_event_list *ev;
        int ret = 0;
 
        /* Does anyone care? */
        mutex_lock(&ev_int->event_list_lock);
-       if (test_bit(IIO_BUSY_BIT_POS, &ev_int->handler.flags)) {
+       if (test_bit(IIO_BUSY_BIT_POS, &ev_int->flags)) {
                if (ev_int->current_events == ev_int->max_events) {
                        mutex_unlock(&ev_int->event_list_lock);
                        return 0;
@@ -187,12 +220,11 @@ error_ret:
 
 static int iio_event_chrdev_release(struct inode *inode, struct file *filep)
 {
-       struct iio_handler *hand = iio_cdev_to_handler(inode->i_cdev);
-       struct iio_event_interface *ev_int = hand->private;
+       struct iio_event_interface *ev_int = filep->private_data;
        struct iio_detected_event_list *el, *t;
 
        mutex_lock(&ev_int->event_list_lock);
-       clear_bit(IIO_BUSY_BIT_POS, &ev_int->handler.flags);
+       clear_bit(IIO_BUSY_BIT_POS, &ev_int->flags);
        /*
         * In order to maintain a clean state for reopening,
         * clear out any awaiting events. The mask will prevent
@@ -202,23 +234,7 @@ static int iio_event_chrdev_release(struct inode *inode, struct file *filep)
                list_del(&el->list);
                kfree(el);
        }
-       mutex_unlock(&ev_int->event_list_lock);
-
-       return 0;
-}
-
-static int iio_event_chrdev_open(struct inode *inode, struct file *filep)
-{
-       struct iio_handler *hand = iio_cdev_to_handler(inode->i_cdev);
-       struct iio_event_interface *ev_int = hand->private;
-
-       mutex_lock(&ev_int->event_list_lock);
-       if (test_and_set_bit(IIO_BUSY_BIT_POS, &hand->flags)) {
-               fops_put(filep->f_op);
-               mutex_unlock(&ev_int->event_list_lock);
-               return -EBUSY;
-       }
-       filep->private_data = hand->private;
+       ev_int->current_events = 0;
        mutex_unlock(&ev_int->event_list_lock);
 
        return 0;
@@ -227,124 +243,25 @@ static int iio_event_chrdev_open(struct inode *inode, struct file *filep)
 static const struct file_operations iio_event_chrdev_fileops = {
        .read =  iio_event_chrdev_read,
        .release = iio_event_chrdev_release,
-       .open = iio_event_chrdev_open,
        .owner = THIS_MODULE,
        .llseek = noop_llseek,
 };
 
-static void iio_event_dev_release(struct device *dev)
+static int iio_event_getfd(struct iio_dev *indio_dev)
 {
-       struct iio_event_interface *ev_int
-               = container_of(dev, struct iio_event_interface, dev);
-       cdev_del(&ev_int->handler.chrdev);
-       iio_device_free_chrdev_minor(MINOR(dev->devt));
-};
-
-static struct device_type iio_event_type = {
-       .release = iio_event_dev_release,
-};
-
-int iio_device_get_chrdev_minor(void)
-{
-       int ret, val;
-
-ida_again:
-       if (unlikely(ida_pre_get(&iio_chrdev_ida, GFP_KERNEL) == 0))
-               return -ENOMEM;
-       spin_lock(&iio_ida_lock);
-       ret = ida_get_new(&iio_chrdev_ida, &val);
-       spin_unlock(&iio_ida_lock);
-       if (unlikely(ret == -EAGAIN))
-               goto ida_again;
-       else if (unlikely(ret))
-               return ret;
-       if (val > IIO_DEV_MAX)
-               return -ENOMEM;
-       return val;
-}
-
-void iio_device_free_chrdev_minor(int val)
-{
-       spin_lock(&iio_ida_lock);
-       ida_remove(&iio_chrdev_ida, val);
-       spin_unlock(&iio_ida_lock);
-}
-
-static int iio_setup_ev_int(struct iio_event_interface *ev_int,
-                           const char *dev_name,
-                           int index,
-                           struct module *owner,
-                           struct device *dev)
-{
-       int ret, minor;
+       if (indio_dev->event_interface == NULL)
+               return -ENODEV;
 
-       ev_int->dev.bus = &iio_bus_type;
-       ev_int->dev.parent = dev;
-       ev_int->dev.type = &iio_event_type;
-       device_initialize(&ev_int->dev);
-
-       minor = iio_device_get_chrdev_minor();
-       if (minor < 0) {
-               ret = minor;
-               goto error_device_put;
+       mutex_lock(&indio_dev->event_interface->event_list_lock);
+       if (test_and_set_bit(IIO_BUSY_BIT_POS,
+                            &indio_dev->event_interface->flags)) {
+               mutex_unlock(&indio_dev->event_interface->event_list_lock);
+               return -EBUSY;
        }
-       ev_int->dev.devt = MKDEV(MAJOR(iio_devt), minor);
-       dev_set_name(&ev_int->dev, "%s:event%d", dev_name, index);
-
-       ret = device_add(&ev_int->dev);
-       if (ret)
-               goto error_free_minor;
-
-       cdev_init(&ev_int->handler.chrdev, &iio_event_chrdev_fileops);
-       ev_int->handler.chrdev.owner = owner;
-
-       mutex_init(&ev_int->event_list_lock);
-       /* discussion point - make this variable? */
-       ev_int->max_events = 10;
-       ev_int->current_events = 0;
-       INIT_LIST_HEAD(&ev_int->det_events);
-       init_waitqueue_head(&ev_int->wait);
-       ev_int->handler.private = ev_int;
-       ev_int->handler.flags = 0;
-
-       ret = cdev_add(&ev_int->handler.chrdev, ev_int->dev.devt, 1);
-       if (ret)
-               goto error_unreg_device;
-
-       return 0;
-
-error_unreg_device:
-       device_unregister(&ev_int->dev);
-error_free_minor:
-       iio_device_free_chrdev_minor(minor);
-error_device_put:
-       put_device(&ev_int->dev);
-
-       return ret;
-}
-
-static void iio_free_ev_int(struct iio_event_interface *ev_int)
-{
-       device_unregister(&ev_int->dev);
-       put_device(&ev_int->dev);
-}
-
-static int __init iio_dev_init(void)
-{
-       int err;
-
-       err = alloc_chrdev_region(&iio_devt, 0, IIO_DEV_MAX, "iio");
-       if (err < 0)
-               printk(KERN_ERR "%s: failed to allocate char dev region\n",
-                      __FILE__);
-
-       return err;
-}
-
-static void __exit iio_dev_exit(void)
-{
-       if (iio_devt)
-               unregister_chrdev_region(iio_devt, IIO_DEV_MAX);
+       mutex_unlock(&indio_dev->event_interface->event_list_lock);
+       return anon_inode_getfd("iio:event",
+                               &iio_event_chrdev_fileops,
+                               indio_dev->event_interface, O_RDONLY);
 }
 
 static int __init iio_init(void)
@@ -360,9 +277,12 @@ static int __init iio_init(void)
                goto error_nothing;
        }
 
-       ret = iio_dev_init();
-       if (ret < 0)
+       ret = alloc_chrdev_region(&iio_devt, 0, IIO_DEV_MAX, "iio");
+       if (ret < 0) {
+               printk(KERN_ERR "%s: failed to allocate char dev region\n",
+                      __FILE__);
                goto error_unregister_bus_type;
+       }
 
        return 0;
 
@@ -374,7 +294,8 @@ error_nothing:
 
 static void __exit iio_exit(void)
 {
-       iio_dev_exit();
+       if (iio_devt)
+               unregister_chrdev_region(iio_devt, IIO_DEV_MAX);
        bus_unregister(&iio_bus_type);
 }
 
@@ -488,32 +409,14 @@ static int __iio_build_postfix(struct iio_chan_spec const *chan,
        if (generic || (!chan->modified && !chan->extend_name)) {
                all_post = kasprintf(GFP_KERNEL, "%s", postfix);
        } else if (chan->modified) {
-               const char *intermediate;
-               switch (chan->type) {
-               case IIO_INTENSITY:
-                       intermediate
-                               = iio_modifier_names_light[chan->channel2];
-                       break;
-               case IIO_ACCEL:
-               case IIO_GYRO:
-               case IIO_MAGN:
-               case IIO_INCLI:
-               case IIO_ROT:
-               case IIO_ANGL:
-                       intermediate
-                               = iio_modifier_names_axial[chan->channel2];
-                       break;
-               default:
-                       return -EINVAL;
-               }
                if (chan->extend_name)
                        all_post = kasprintf(GFP_KERNEL, "%s_%s_%s",
-                                            intermediate,
+                                            iio_modifier_names[chan->channel2],
                                             chan->extend_name,
                                             postfix);
                else
                        all_post = kasprintf(GFP_KERNEL, "%s_%s",
-                                            intermediate,
+                                            iio_modifier_names[chan->channel2],
                                             postfix);
        } else
                all_post = kasprintf(GFP_KERNEL, "%s_%s", chan->extend_name,
@@ -524,6 +427,7 @@ static int __iio_build_postfix(struct iio_chan_spec const *chan,
        return 0;
 }
 
+static
 int __iio_device_attr_init(struct device_attribute *dev_attr,
                           const char *postfix,
                           struct iio_chan_spec const *chan,
@@ -544,19 +448,22 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
                goto error_ret;
 
        /* Special case for types that uses both channel numbers in naming */
-       if (chan->type == IIO_IN_DIFF && !generic)
+       if (chan->type == IIO_VOLTAGE_DIFF && !generic)
                name_format
-                       = kasprintf(GFP_KERNEL, "%s_%s",
+                       = kasprintf(GFP_KERNEL, "%s_%s_%s",
+                                   iio_direction[chan->output],
                                    iio_chan_type_name_spec_complex[chan->type],
                                    full_postfix);
        else if (generic || !chan->indexed)
                name_format
-                       = kasprintf(GFP_KERNEL, "%s_%s",
+                       = kasprintf(GFP_KERNEL, "%s_%s_%s",
+                                   iio_direction[chan->output],
                                    iio_chan_type_name_spec_shared[chan->type],
                                    full_postfix);
        else
                name_format
-                       = kasprintf(GFP_KERNEL, "%s%d_%s",
+                       = kasprintf(GFP_KERNEL, "%s_%s%d_%s",
+                                   iio_direction[chan->output],
                                    iio_chan_type_name_spec_shared[chan->type],
                                    chan->channel,
                                    full_postfix);
@@ -596,13 +503,12 @@ error_ret:
        return ret;
 }
 
-void __iio_device_attr_deinit(struct device_attribute *dev_attr)
+static void __iio_device_attr_deinit(struct device_attribute *dev_attr)
 {
        kfree(dev_attr->attr.name);
 }
 
 int __iio_add_chan_devattr(const char *postfix,
-                          const char *group,
                           struct iio_chan_spec const *chan,
                           ssize_t (*readfunc)(struct device *dev,
                                               struct device_attribute *attr,
@@ -611,7 +517,7 @@ int __iio_add_chan_devattr(const char *postfix,
                                                struct device_attribute *attr,
                                                const char *buf,
                                                size_t len),
-                          int mask,
+                          u64 mask,
                           bool generic,
                           struct device *dev,
                           struct list_head *attr_list)
@@ -640,12 +546,6 @@ int __iio_add_chan_devattr(const char *postfix,
                        ret = -EBUSY;
                        goto error_device_attr_deinit;
                }
-
-       ret = sysfs_add_file_to_group(&dev->kobj,
-                                     &iio_attr->dev_attr.attr, group);
-       if (ret < 0)
-               goto error_device_attr_deinit;
-
        list_add(&iio_attr->l, attr_list);
 
        return 0;
@@ -661,34 +561,27 @@ error_ret:
 static int iio_device_add_channel_sysfs(struct iio_dev *dev_info,
                                        struct iio_chan_spec const *chan)
 {
-       int ret, i;
-
+       int ret, i, attrcount = 0;
 
        if (chan->channel < 0)
                return 0;
-       if (chan->processed_val)
-               ret = __iio_add_chan_devattr("input", NULL, chan,
-                                            &iio_read_channel_info,
-                                            NULL,
-                                            0,
-                                            0,
-                                            &dev_info->dev,
-                                            &dev_info->channel_attr_list);
-       else
-               ret = __iio_add_chan_devattr("raw", NULL, chan,
-                                            &iio_read_channel_info,
-                                            (chan->type == IIO_OUT ?
-                                            &iio_write_channel_info : NULL),
-                                            0,
-                                            0,
-                                            &dev_info->dev,
-                                            &dev_info->channel_attr_list);
+
+       ret = __iio_add_chan_devattr(iio_data_type_name[chan->processed_val],
+                                    chan,
+                                    &iio_read_channel_info,
+                                    (chan->output ?
+                                     &iio_write_channel_info : NULL),
+                                    0,
+                                    0,
+                                    &dev_info->dev,
+                                    &dev_info->channel_attr_list);
        if (ret)
                goto error_ret;
+       attrcount++;
 
        for_each_set_bit(i, &chan->info_mask, sizeof(long)*8) {
                ret = __iio_add_chan_devattr(iio_chan_info_postfix[i/2],
-                                            NULL, chan,
+                                            chan,
                                             &iio_read_channel_info,
                                             &iio_write_channel_info,
                                             (1 << i),
@@ -701,7 +594,9 @@ static int iio_device_add_channel_sysfs(struct iio_dev *dev_info,
                }
                if (ret < 0)
                        goto error_ret;
+               attrcount++;
        }
+       ret = attrcount;
 error_ret:
        return ret;
 }
@@ -709,8 +604,6 @@ error_ret:
 static void iio_device_remove_and_free_read_attr(struct iio_dev *dev_info,
                                                 struct iio_dev_attr *p)
 {
-       sysfs_remove_file_from_group(&dev_info->dev.kobj,
-                                    &p->dev_attr.attr, NULL);
        kfree(p->dev_attr.attr.name);
        kfree(p);
 }
@@ -727,19 +620,17 @@ static DEVICE_ATTR(name, S_IRUGO, iio_show_dev_name, NULL);
 
 static int iio_device_register_sysfs(struct iio_dev *dev_info)
 {
-       int i, ret = 0;
+       int i, ret = 0, attrcount, attrn, attrcount_orig = 0;
        struct iio_dev_attr *p, *n;
+       struct attribute **attr;
 
+       /* First count elements in any existing group */
        if (dev_info->info->attrs) {
-               ret = sysfs_create_group(&dev_info->dev.kobj,
-                                        dev_info->info->attrs);
-               if (ret) {
-                       dev_err(dev_info->dev.parent,
-                               "Failed to register sysfs hooks\n");
-                       goto error_ret;
-               }
+               attr = dev_info->info->attrs->attrs;
+               while (*attr++ != NULL)
+                       attrcount_orig++;
        }
-
+       attrcount = attrcount_orig;
        /*
         * New channel registration method - relies on the fact a group does
         *  not need to be initialized if it is name is NULL.
@@ -752,14 +643,36 @@ static int iio_device_register_sysfs(struct iio_dev *dev_info)
                                                           ->channels[i]);
                        if (ret < 0)
                                goto error_clear_attrs;
+                       attrcount += ret;
                }
-       if (dev_info->name) {
-               ret = sysfs_add_file_to_group(&dev_info->dev.kobj,
-                                             &dev_attr_name.attr,
-                                             NULL);
-               if (ret)
-                       goto error_clear_attrs;
+
+       if (dev_info->name)
+               attrcount++;
+
+       dev_info->chan_attr_group.attrs
+               = kzalloc(sizeof(dev_info->chan_attr_group.attrs[0])*
+                         (attrcount + 1),
+                         GFP_KERNEL);
+       if (dev_info->chan_attr_group.attrs == NULL) {
+               ret = -ENOMEM;
+               goto error_clear_attrs;
        }
+       /* Copy across original attributes */
+       if (dev_info->info->attrs)
+               memcpy(dev_info->chan_attr_group.attrs,
+                      dev_info->info->attrs->attrs,
+                      sizeof(dev_info->chan_attr_group.attrs[0])
+                      *attrcount_orig);
+       attrn = attrcount_orig;
+       /* Add all elements from the list. */
+       list_for_each_entry(p, &dev_info->channel_attr_list, l)
+               dev_info->chan_attr_group.attrs[attrn++] = &p->dev_attr.attr;
+       if (dev_info->name)
+               dev_info->chan_attr_group.attrs[attrn++] = &dev_attr_name.attr;
+
+       dev_info->groups[dev_info->groupcounter++] =
+               &dev_info->chan_attr_group;
+
        return 0;
 
 error_clear_attrs:
@@ -768,64 +681,28 @@ error_clear_attrs:
                list_del(&p->l);
                iio_device_remove_and_free_read_attr(dev_info, p);
        }
-       if (dev_info->info->attrs)
-               sysfs_remove_group(&dev_info->dev.kobj, dev_info->info->attrs);
-error_ret:
-       return ret;
 
+       return ret;
 }
 
 static void iio_device_unregister_sysfs(struct iio_dev *dev_info)
 {
 
        struct iio_dev_attr *p, *n;
-       if (dev_info->name)
-               sysfs_remove_file_from_group(&dev_info->dev.kobj,
-                                            &dev_attr_name.attr,
-                                            NULL);
+
        list_for_each_entry_safe(p, n, &dev_info->channel_attr_list, l) {
                list_del(&p->l);
                iio_device_remove_and_free_read_attr(dev_info, p);
        }
-
-       if (dev_info->info->attrs)
-               sysfs_remove_group(&dev_info->dev.kobj, dev_info->info->attrs);
-}
-
-/* Return a negative errno on failure */
-int iio_get_new_ida_val(struct ida *this_ida)
-{
-       int ret;
-       int val;
-
-ida_again:
-       if (unlikely(ida_pre_get(this_ida, GFP_KERNEL) == 0))
-               return -ENOMEM;
-
-       spin_lock(&iio_ida_lock);
-       ret = ida_get_new(this_ida, &val);
-       spin_unlock(&iio_ida_lock);
-       if (unlikely(ret == -EAGAIN))
-               goto ida_again;
-       else if (unlikely(ret))
-               return ret;
-
-       return val;
+       kfree(dev_info->chan_attr_group.attrs);
 }
-EXPORT_SYMBOL(iio_get_new_ida_val);
-
-void iio_free_ida_val(struct ida *this_ida, int id)
-{
-       spin_lock(&iio_ida_lock);
-       ida_remove(this_ida, id);
-       spin_unlock(&iio_ida_lock);
-}
-EXPORT_SYMBOL(iio_free_ida_val);
 
 static const char * const iio_ev_type_text[] = {
        [IIO_EV_TYPE_THRESH] = "thresh",
        [IIO_EV_TYPE_MAG] = "mag",
-       [IIO_EV_TYPE_ROC] = "roc"
+       [IIO_EV_TYPE_ROC] = "roc",
+       [IIO_EV_TYPE_THRESH_ADAPTIVE] = "thresh_adaptive",
+       [IIO_EV_TYPE_MAG_ADAPTIVE] = "mag_adaptive",
 };
 
 static const char * const iio_ev_dir_text[] = {
@@ -910,199 +787,188 @@ static ssize_t iio_ev_value_store(struct device *dev,
 static int iio_device_add_event_sysfs(struct iio_dev *dev_info,
                                      struct iio_chan_spec const *chan)
 {
-
-       int ret = 0, i, mask;
+       int ret = 0, i, attrcount = 0;
+       u64 mask = 0;
        char *postfix;
        if (!chan->event_mask)
                return 0;
 
        for_each_set_bit(i, &chan->event_mask, sizeof(chan->event_mask)*8) {
                postfix = kasprintf(GFP_KERNEL, "%s_%s_en",
-                                   iio_ev_type_text[i/IIO_EV_TYPE_MAX],
-                                   iio_ev_dir_text[i%IIO_EV_TYPE_MAX]);
+                                   iio_ev_type_text[i/IIO_EV_DIR_MAX],
+                                   iio_ev_dir_text[i%IIO_EV_DIR_MAX]);
                if (postfix == NULL) {
                        ret = -ENOMEM;
                        goto error_ret;
                }
-               switch (chan->type) {
-                       /* Switch this to a table at some point */
-               case IIO_IN:
-                       mask = IIO_UNMOD_EVENT_CODE(chan->type, chan->channel,
-                                                   i/IIO_EV_TYPE_MAX,
-                                                   i%IIO_EV_TYPE_MAX);
-                       break;
-               case IIO_ACCEL:
+               if (chan->modified)
                        mask = IIO_MOD_EVENT_CODE(chan->type, 0, chan->channel,
-                                                 i/IIO_EV_TYPE_MAX,
-                                                 i%IIO_EV_TYPE_MAX);
-                       break;
-               case IIO_IN_DIFF:
-                       mask = IIO_MOD_EVENT_CODE(chan->type, chan->channel,
+                                                 i/IIO_EV_DIR_MAX,
+                                                 i%IIO_EV_DIR_MAX);
+               else if (chan->type == IIO_VOLTAGE_DIFF)
+                       mask = IIO_MOD_EVENT_CODE(chan->type,
+                                                 chan->channel,
                                                  chan->channel2,
-                                                 i/IIO_EV_TYPE_MAX,
-                                                 i%IIO_EV_TYPE_MAX);
-                       break;
-               default:
-                       printk(KERN_INFO "currently unhandled type of event\n");
-               }
+                                                 i/IIO_EV_DIR_MAX,
+                                                 i%IIO_EV_DIR_MAX);
+               else
+                       mask = IIO_UNMOD_EVENT_CODE(chan->type,
+                                                   chan->channel,
+                                                   i/IIO_EV_DIR_MAX,
+                                                   i%IIO_EV_DIR_MAX);
+
                ret = __iio_add_chan_devattr(postfix,
-                                            NULL,
                                             chan,
                                             &iio_ev_state_show,
                                             iio_ev_state_store,
                                             mask,
-                                            /*HACK. - limits us to one
-                                              event interface - fix by
-                                              extending the bitmask - but
-                                              how far*/
                                             0,
-                                            &dev_info->event_interfaces[0].dev,
-                                            &dev_info->event_interfaces[0].
+                                            &dev_info->dev,
+                                            &dev_info->event_interface->
                                             dev_attr_list);
                kfree(postfix);
                if (ret)
                        goto error_ret;
-
+               attrcount++;
                postfix = kasprintf(GFP_KERNEL, "%s_%s_value",
-                                   iio_ev_type_text[i/IIO_EV_TYPE_MAX],
-                                   iio_ev_dir_text[i%IIO_EV_TYPE_MAX]);
+                                   iio_ev_type_text[i/IIO_EV_DIR_MAX],
+                                   iio_ev_dir_text[i%IIO_EV_DIR_MAX]);
                if (postfix == NULL) {
                        ret = -ENOMEM;
                        goto error_ret;
                }
-               ret = __iio_add_chan_devattr(postfix, NULL, chan,
+               ret = __iio_add_chan_devattr(postfix, chan,
                                             iio_ev_value_show,
                                             iio_ev_value_store,
                                             mask,
                                             0,
-                                            &dev_info->event_interfaces[0]
-                                            .dev,
-                                            &dev_info->event_interfaces[0]
-                                            .dev_attr_list);
+                                            &dev_info->dev,
+                                            &dev_info->event_interface->
+                                            dev_attr_list);
                kfree(postfix);
                if (ret)
                        goto error_ret;
-
+               attrcount++;
        }
-
+       ret = attrcount;
 error_ret:
        return ret;
 }
 
-static inline void __iio_remove_all_event_sysfs(struct iio_dev *dev_info,
-                                               const char *groupname,
-                                               int num)
+static inline void __iio_remove_event_config_attrs(struct iio_dev *dev_info)
 {
        struct iio_dev_attr *p, *n;
        list_for_each_entry_safe(p, n,
-                                &dev_info->event_interfaces[num].
+                                &dev_info->event_interface->
                                 dev_attr_list, l) {
-               sysfs_remove_file_from_group(&dev_info
-                                            ->event_interfaces[num].dev.kobj,
-                                            &p->dev_attr.attr,
-                                            groupname);
                kfree(p->dev_attr.attr.name);
                kfree(p);
        }
 }
 
-static inline int __iio_add_event_config_attrs(struct iio_dev *dev_info, int i)
+static inline int __iio_add_event_config_attrs(struct iio_dev *dev_info)
 {
-       int j;
-       int ret;
-       INIT_LIST_HEAD(&dev_info->event_interfaces[0].dev_attr_list);
+       int j, ret, attrcount = 0;
+
+       INIT_LIST_HEAD(&dev_info->event_interface->dev_attr_list);
        /* Dynically created from the channels array */
-       if (dev_info->channels) {
-               for (j = 0; j < dev_info->num_channels; j++) {
-                       ret = iio_device_add_event_sysfs(dev_info,
-                                                        &dev_info
-                                                        ->channels[j]);
-                       if (ret)
-                               goto error_clear_attrs;
-               }
+       for (j = 0; j < dev_info->num_channels; j++) {
+               ret = iio_device_add_event_sysfs(dev_info,
+                                                &dev_info->channels[j]);
+               if (ret < 0)
+                       goto error_clear_attrs;
+               attrcount += ret;
        }
-       return 0;
+       return attrcount;
 
 error_clear_attrs:
-       __iio_remove_all_event_sysfs(dev_info, NULL, i);
+       __iio_remove_event_config_attrs(dev_info);
 
        return ret;
 }
 
-static inline int __iio_remove_event_config_attrs(struct iio_dev *dev_info,
-                                                 int i)
+static bool iio_check_for_dynamic_events(struct iio_dev *dev_info)
 {
-       __iio_remove_all_event_sysfs(dev_info, NULL, i);
-       return 0;
+       int j;
+
+       for (j = 0; j < dev_info->num_channels; j++)
+               if (dev_info->channels[j].event_mask != 0)
+                       return true;
+       return false;
 }
 
+static void iio_setup_ev_int(struct iio_event_interface *ev_int)
+{
+       mutex_init(&ev_int->event_list_lock);
+       /* discussion point - make this variable? */
+       ev_int->max_events = 10;
+       ev_int->current_events = 0;
+       INIT_LIST_HEAD(&ev_int->det_events);
+       init_waitqueue_head(&ev_int->wait);
+}
+
+static const char *iio_event_group_name = "events";
 static int iio_device_register_eventset(struct iio_dev *dev_info)
 {
-       int ret = 0, i, j;
+       struct iio_dev_attr *p;
+       int ret = 0, attrcount_orig = 0, attrcount, attrn;
+       struct attribute **attr;
 
-       if (dev_info->info->num_interrupt_lines == 0)
+       if (!(dev_info->info->event_attrs ||
+             iio_check_for_dynamic_events(dev_info)))
                return 0;
 
-       dev_info->event_interfaces =
-               kzalloc(sizeof(struct iio_event_interface)
-                       *dev_info->info->num_interrupt_lines,
-                       GFP_KERNEL);
-       if (dev_info->event_interfaces == NULL) {
+       dev_info->event_interface =
+               kzalloc(sizeof(struct iio_event_interface), GFP_KERNEL);
+       if (dev_info->event_interface == NULL) {
                ret = -ENOMEM;
                goto error_ret;
        }
 
-       for (i = 0; i < dev_info->info->num_interrupt_lines; i++) {
-               ret = iio_setup_ev_int(&dev_info->event_interfaces[i],
-                                      dev_name(&dev_info->dev),
-                                      i,
-                                      dev_info->info->driver_module,
-                                      &dev_info->dev);
-               if (ret) {
-                       dev_err(&dev_info->dev,
-                               "Could not get chrdev interface\n");
-                       goto error_free_setup_ev_ints;
-               }
-
-               dev_set_drvdata(&dev_info->event_interfaces[i].dev,
-                               (void *)dev_info);
-
-               if (dev_info->info->event_attrs != NULL)
-                       ret = sysfs_create_group(&dev_info
-                                                ->event_interfaces[i]
-                                                .dev.kobj,
-                                                &dev_info->info
-                                                ->event_attrs[i]);
-
-               if (ret) {
-                       dev_err(&dev_info->dev,
-                               "Failed to register sysfs for event attrs");
-                       goto error_remove_sysfs_interfaces;
-               }
+       iio_setup_ev_int(dev_info->event_interface);
+       if (dev_info->info->event_attrs != NULL) {
+               attr = dev_info->info->event_attrs->attrs;
+               while (*attr++ != NULL)
+                       attrcount_orig++;
+       }
+       attrcount = attrcount_orig;
+       if (dev_info->channels) {
+               ret = __iio_add_event_config_attrs(dev_info);
+               if (ret < 0)
+                       goto error_free_setup_event_lines;
+               attrcount += ret;
        }
 
-       for (i = 0; i < dev_info->info->num_interrupt_lines; i++) {
-               ret = __iio_add_event_config_attrs(dev_info, i);
-               if (ret)
-                       goto error_unregister_config_attrs;
+       dev_info->event_interface->group.name = iio_event_group_name;
+       dev_info->event_interface->group.attrs =
+               kzalloc(sizeof(dev_info->event_interface->group.attrs[0])
+                       *(attrcount + 1),
+                       GFP_KERNEL);
+       if (dev_info->event_interface->group.attrs == NULL) {
+               ret = -ENOMEM;
+               goto error_free_setup_event_lines;
        }
+       if (dev_info->info->event_attrs)
+               memcpy(dev_info->event_interface->group.attrs,
+                      dev_info->info->event_attrs->attrs,
+                      sizeof(dev_info->event_interface->group.attrs[0])
+                      *attrcount_orig);
+       attrn = attrcount_orig;
+       /* Add all elements from the list. */
+       list_for_each_entry(p,
+                           &dev_info->event_interface->dev_attr_list,
+                           l)
+               dev_info->event_interface->group.attrs[attrn++] =
+                       &p->dev_attr.attr;
+
+       dev_info->groups[dev_info->groupcounter++] =
+               &dev_info->event_interface->group;
 
        return 0;
 
-error_unregister_config_attrs:
-       for (j = 0; j < i; j++)
-               __iio_remove_event_config_attrs(dev_info, i);
-       i = dev_info->info->num_interrupt_lines - 1;
-error_remove_sysfs_interfaces:
-       for (j = 0; j < i; j++)
-               if (dev_info->info->event_attrs != NULL)
-                       sysfs_remove_group(&dev_info
-                                  ->event_interfaces[j].dev.kobj,
-                                  &dev_info->info->event_attrs[j]);
-error_free_setup_ev_ints:
-       for (j = 0; j < i; j++)
-               iio_free_ev_int(&dev_info->event_interfaces[j]);
-       kfree(dev_info->event_interfaces);
+error_free_setup_event_lines:
+       __iio_remove_event_config_attrs(dev_info);
+       kfree(dev_info->event_interface);
 error_ret:
 
        return ret;
@@ -1110,27 +976,23 @@ error_ret:
 
 static void iio_device_unregister_eventset(struct iio_dev *dev_info)
 {
-       int i;
-
-       if (dev_info->info->num_interrupt_lines == 0)
+       if (dev_info->event_interface == NULL)
                return;
-       for (i = 0; i < dev_info->info->num_interrupt_lines; i++) {
-               __iio_remove_event_config_attrs(dev_info, i);
-               if (dev_info->info->event_attrs != NULL)
-                       sysfs_remove_group(&dev_info
-                                          ->event_interfaces[i].dev.kobj,
-                                          &dev_info->info->event_attrs[i]);
-       }
-
-       for (i = 0; i < dev_info->info->num_interrupt_lines; i++)
-               iio_free_ev_int(&dev_info->event_interfaces[i]);
-       kfree(dev_info->event_interfaces);
+       __iio_remove_event_config_attrs(dev_info);
+       kfree(dev_info->event_interface->group.attrs);
+       kfree(dev_info->event_interface);
 }
 
 static void iio_dev_release(struct device *device)
 {
-       iio_put();
-       kfree(to_iio_dev(device));
+       struct iio_dev *dev_info = container_of(device, struct iio_dev, dev);
+       cdev_del(&dev_info->chrdev);
+       if (dev_info->modes & INDIO_RING_TRIGGERED)
+               iio_device_unregister_trigger_consumer(dev_info);
+       iio_device_unregister_eventset(dev_info);
+       iio_device_unregister_sysfs(dev_info);
+       ida_simple_remove(&iio_ida, dev_info->id);
+       kfree(dev_info);
 }
 
 static struct device_type iio_dev_type = {
@@ -1154,12 +1016,12 @@ struct iio_dev *iio_allocate_device(int sizeof_priv)
        dev = kzalloc(alloc_size, GFP_KERNEL);
 
        if (dev) {
+               dev->dev.groups = dev->groups;
                dev->dev.type = &iio_dev_type;
                dev->dev.bus = &iio_bus_type;
                device_initialize(&dev->dev);
                dev_set_drvdata(&dev->dev, (void *)dev);
                mutex_init(&dev->mlock);
-               iio_get();
        }
 
        return dev;
@@ -1169,30 +1031,80 @@ EXPORT_SYMBOL(iio_allocate_device);
 void iio_free_device(struct iio_dev *dev)
 {
        if (dev)
-               iio_put_device(dev);
+               kfree(dev);
 }
 EXPORT_SYMBOL(iio_free_device);
 
+/**
+ * iio_chrdev_open() - chrdev file open for ring buffer access and ioctls
+ **/
+static int iio_chrdev_open(struct inode *inode, struct file *filp)
+{
+       struct iio_dev *dev_info = container_of(inode->i_cdev,
+                                               struct iio_dev, chrdev);
+       filp->private_data = dev_info;
+       iio_chrdev_ring_open(dev_info);
+       return 0;
+}
+
+/**
+ * iio_chrdev_release() - chrdev file close ring buffer access and ioctls
+ **/
+static int iio_chrdev_release(struct inode *inode, struct file *filp)
+{
+       iio_chrdev_ring_release(container_of(inode->i_cdev,
+                                            struct iio_dev, chrdev));
+       return 0;
+}
+
+/* Somewhat of a cross file organization violation - ioctls here are actually
+ * event related */
+static long iio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+       struct iio_dev *indio_dev = filp->private_data;
+       int __user *ip = (int __user *)arg;
+       int fd;
+
+       if (cmd == IIO_GET_EVENT_FD_IOCTL) {
+               fd = iio_event_getfd(indio_dev);
+               if (copy_to_user(ip, &fd, sizeof(fd)))
+                       return -EFAULT;
+               return 0;
+       }
+       return -EINVAL;
+}
+
+static const struct file_operations iio_ring_fileops = {
+       .read = iio_ring_read_first_n_outer_addr,
+       .release = iio_chrdev_release,
+       .open = iio_chrdev_open,
+       .poll = iio_ring_poll_addr,
+       .owner = THIS_MODULE,
+       .llseek = noop_llseek,
+       .unlocked_ioctl = iio_ioctl,
+       .compat_ioctl = iio_ioctl,
+};
+
 int iio_device_register(struct iio_dev *dev_info)
 {
        int ret;
 
-       dev_info->id = iio_get_new_ida_val(&iio_ida);
+       dev_info->id = ida_simple_get(&iio_ida, 0, 0, GFP_KERNEL);
        if (dev_info->id < 0) {
                ret = dev_info->id;
                dev_err(&dev_info->dev, "Failed to get id\n");
                goto error_ret;
        }
-       dev_set_name(&dev_info->dev, "device%d", dev_info->id);
+       dev_set_name(&dev_info->dev, "iio:device%d", dev_info->id);
+
+       /* configure elements for the chrdev */
+       dev_info->dev.devt = MKDEV(MAJOR(iio_devt), dev_info->id);
 
-       ret = device_add(&dev_info->dev);
-       if (ret)
-               goto error_free_ida;
        ret = iio_device_register_sysfs(dev_info);
        if (ret) {
                dev_err(dev_info->dev.parent,
                        "Failed to register sysfs interfaces\n");
-               goto error_del_device;
+               goto error_free_ida;
        }
        ret = iio_device_register_eventset(dev_info);
        if (ret) {
@@ -1203,14 +1115,24 @@ int iio_device_register(struct iio_dev *dev_info)
        if (dev_info->modes & INDIO_RING_TRIGGERED)
                iio_device_register_trigger_consumer(dev_info);
 
+       ret = device_add(&dev_info->dev);
+       if (ret < 0)
+               goto error_unreg_eventset;
+       cdev_init(&dev_info->chrdev, &iio_ring_fileops);
+       dev_info->chrdev.owner = dev_info->info->driver_module;
+       ret = cdev_add(&dev_info->chrdev, dev_info->dev.devt, 1);
+       if (ret < 0)
+               goto error_del_device;
        return 0;
 
-error_free_sysfs:
-       iio_device_unregister_sysfs(dev_info);
 error_del_device:
        device_del(&dev_info->dev);
+error_unreg_eventset:
+       iio_device_unregister_eventset(dev_info);
+error_free_sysfs:
+       iio_device_unregister_sysfs(dev_info);
 error_free_ida:
-       iio_free_ida_val(&iio_ida, dev_info->id);
+       ida_simple_remove(&iio_ida, dev_info->id);
 error_ret:
        return ret;
 }
@@ -1218,25 +1140,9 @@ EXPORT_SYMBOL(iio_device_register);
 
 void iio_device_unregister(struct iio_dev *dev_info)
 {
-       if (dev_info->modes & INDIO_RING_TRIGGERED)
-               iio_device_unregister_trigger_consumer(dev_info);
-       iio_device_unregister_eventset(dev_info);
-       iio_device_unregister_sysfs(dev_info);
-       iio_free_ida_val(&iio_ida, dev_info->id);
        device_unregister(&dev_info->dev);
 }
 EXPORT_SYMBOL(iio_device_unregister);
-
-void iio_put(void)
-{
-       module_put(THIS_MODULE);
-}
-
-void iio_get(void)
-{
-       __module_get(THIS_MODULE);
-}
-
 subsys_initcall(iio_init);
 module_exit(iio_exit);