diff options
author | Greg Kroah-Hartman <gregkh@suse.de> | 2011-10-25 09:18:11 +0200 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-10-25 09:18:11 +0200 |
commit | 43a3beb6da994549ec28a9f31727b997a025f958 (patch) | |
tree | 9fea6f7e2abd5ba7ce4d5f725a8ceed0a4e0ab80 /drivers/staging/iio/industrialio-core.c | |
parent | c3b92c8787367a8bb53d57d9789b558f1295cc96 (diff) | |
parent | 68cf162a1af23c35db8e3b78659c99196c9882ff (diff) |
Merge branch 'staging-next' into Linux 3.1
This was done to resolve a conflict in the
drivers/staging/comedi/drivers/ni_labpc.c file that resolved a build
bugfix in Linus's tree with a "better" bugfix that was in the
staging-next tree that resolved the issue in a more complete manner.
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/staging/iio/industrialio-core.c')
-rw-r--r-- | drivers/staging/iio/industrialio-core.c | 961 |
1 files changed, 438 insertions, 523 deletions
diff --git a/drivers/staging/iio/industrialio-core.c b/drivers/staging/iio/industrialio-core.c index 19819e7578c..326e967d54e 100644 --- a/drivers/staging/iio/industrialio-core.c +++ b/drivers/staging/iio/industrialio-core.c @@ -21,21 +21,17 @@ #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,14 +39,22 @@ struct bus_type iio_bus_type = { }; EXPORT_SYMBOL(iio_bus_type); -static const char * const iio_chan_type_name_spec_shared[] = { - [IIO_IN] = "in", - [IIO_OUT] = "out", +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[] = { + [IIO_VOLTAGE] = "voltage", [IIO_CURRENT] = "current", [IIO_POWER] = "power", [IIO_ACCEL] = "accel", - [IIO_IN_DIFF] = "in-in", - [IIO_GYRO] = "gyro", + [IIO_ANGL_VEL] = "anglvel", [IIO_MAGN] = "magn", [IIO_LIGHT] = "illuminance", [IIO_INTENSITY] = "intensity", @@ -60,21 +64,15 @@ 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", -}; - -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 +83,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", }; -int iio_push_event(struct iio_dev *dev_info, - int ev_line, - int ev_code, - s64 timestamp) +/** + * 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 *indio_dev, u64 ev_code, s64 timestamp) { - struct iio_event_interface *ev_int - = &dev_info->event_interfaces[ev_line]; + struct iio_event_interface *ev_int = indio_dev->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; @@ -125,7 +153,6 @@ error_ret: } EXPORT_SYMBOL(iio_push_event); - /* This turns up an awful lot */ ssize_t iio_read_const_attr(struct device *dev, struct device_attribute *attr, @@ -135,7 +162,6 @@ ssize_t iio_read_const_attr(struct device *dev, } EXPORT_SYMBOL(iio_read_const_attr); - static ssize_t iio_event_chrdev_read(struct file *filep, char __user *buf, size_t count, @@ -187,12 +213,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 +227,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 +236,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); -} + if (indio_dev->event_interface == NULL) + return -ENODEV; -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; - - 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 +270,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 +287,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); } @@ -476,54 +390,7 @@ static ssize_t iio_write_channel_info(struct device *dev, return len; } -static int __iio_build_postfix(struct iio_chan_spec const *chan, - bool generic, - const char *postfix, - char **result) -{ - char *all_post; - /* 3 options - generic, extend_name, modified - if generic, extend_name - * and modified cannot apply.*/ - - 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, - chan->extend_name, - postfix); - else - all_post = kasprintf(GFP_KERNEL, "%s_%s", - intermediate, - postfix); - } else - all_post = kasprintf(GFP_KERNEL, "%s_%s", chan->extend_name, - postfix); - if (all_post == NULL) - return -ENOMEM; - *result = all_post; - return 0; -} - +static int __iio_device_attr_init(struct device_attribute *dev_attr, const char *postfix, struct iio_chan_spec const *chan, @@ -539,28 +406,77 @@ int __iio_device_attr_init(struct device_attribute *dev_attr, int ret; char *name_format, *full_postfix; sysfs_attr_init(&dev_attr->attr); - ret = __iio_build_postfix(chan, generic, postfix, &full_postfix); - if (ret) - goto error_ret; - /* Special case for types that uses both channel numbers in naming */ - if (chan->type == IIO_IN_DIFF && !generic) - name_format - = kasprintf(GFP_KERNEL, "%s_%s", - iio_chan_type_name_spec_complex[chan->type], - full_postfix); - else if (generic || !chan->indexed) - name_format - = kasprintf(GFP_KERNEL, "%s_%s", - iio_chan_type_name_spec_shared[chan->type], - full_postfix); - else - name_format - = kasprintf(GFP_KERNEL, "%s%d_%s", - iio_chan_type_name_spec_shared[chan->type], - chan->channel, - full_postfix); + /* Build up postfix of <extend_name>_<modifier>_postfix */ + if (chan->modified) { + if (chan->extend_name) + full_postfix = kasprintf(GFP_KERNEL, "%s_%s_%s", + iio_modifier_names[chan + ->channel2], + chan->extend_name, + postfix); + else + full_postfix = kasprintf(GFP_KERNEL, "%s_%s", + iio_modifier_names[chan + ->channel2], + postfix); + } else { + if (chan->extend_name == NULL) + full_postfix = kstrdup(postfix, GFP_KERNEL); + else + full_postfix = kasprintf(GFP_KERNEL, + "%s_%s", + chan->extend_name, + postfix); + } + if (full_postfix == NULL) { + ret = -ENOMEM; + goto error_ret; + } + if (chan->differential) { /* Differential can not have modifier */ + if (generic) + name_format + = kasprintf(GFP_KERNEL, "%s_%s-%s_%s", + iio_direction[chan->output], + iio_chan_type_name_spec[chan->type], + iio_chan_type_name_spec[chan->type], + full_postfix); + else if (chan->indexed) + name_format + = kasprintf(GFP_KERNEL, "%s_%s%d-%s%d_%s", + iio_direction[chan->output], + iio_chan_type_name_spec[chan->type], + chan->channel, + iio_chan_type_name_spec[chan->type], + chan->channel2, + full_postfix); + else { + WARN_ON("Differential channels must be indexed\n"); + ret = -EINVAL; + goto error_free_full_postfix; + } + } else { /* Single ended */ + if (generic) + name_format + = kasprintf(GFP_KERNEL, "%s_%s_%s", + iio_direction[chan->output], + iio_chan_type_name_spec[chan->type], + full_postfix); + else if (chan->indexed) + name_format + = kasprintf(GFP_KERNEL, "%s_%s%d_%s", + iio_direction[chan->output], + iio_chan_type_name_spec[chan->type], + chan->channel, + full_postfix); + else + name_format + = kasprintf(GFP_KERNEL, "%s_%s_%s", + iio_direction[chan->output], + iio_chan_type_name_spec[chan->type], + full_postfix); + } if (name_format == NULL) { ret = -ENOMEM; goto error_free_full_postfix; @@ -596,13 +512,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 +526,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 +555,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; @@ -658,59 +567,52 @@ error_ret: return ret; } -static int iio_device_add_channel_sysfs(struct iio_dev *dev_info, +static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev, 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, + &indio_dev->dev, + &indio_dev->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), !(i%2), - &dev_info->dev, - &dev_info->channel_attr_list); + &indio_dev->dev, + &indio_dev->channel_attr_list); if (ret == -EBUSY && (i%2 == 0)) { ret = 0; continue; } if (ret < 0) goto error_ret; + attrcount++; } + ret = attrcount; error_ret: return ret; } -static void iio_device_remove_and_free_read_attr(struct iio_dev *dev_info, +static void iio_device_remove_and_free_read_attr(struct iio_dev *indio_dev, 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); } @@ -725,107 +627,91 @@ static ssize_t iio_show_dev_name(struct device *dev, static DEVICE_ATTR(name, S_IRUGO, iio_show_dev_name, NULL); -static int iio_device_register_sysfs(struct iio_dev *dev_info) +static int iio_device_register_sysfs(struct iio_dev *indio_dev) { - int i, ret = 0; + int i, ret = 0, attrcount, attrn, attrcount_orig = 0; struct iio_dev_attr *p, *n; + struct attribute **attr; - 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; - } + /* First count elements in any existing group */ + if (indio_dev->info->attrs) { + attr = indio_dev->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. */ - INIT_LIST_HEAD(&dev_info->channel_attr_list); - if (dev_info->channels) - for (i = 0; i < dev_info->num_channels; i++) { - ret = iio_device_add_channel_sysfs(dev_info, - &dev_info + INIT_LIST_HEAD(&indio_dev->channel_attr_list); + if (indio_dev->channels) + for (i = 0; i < indio_dev->num_channels; i++) { + ret = iio_device_add_channel_sysfs(indio_dev, + &indio_dev ->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 (indio_dev->name) + attrcount++; + + indio_dev->chan_attr_group.attrs + = kzalloc(sizeof(indio_dev->chan_attr_group.attrs[0])* + (attrcount + 1), + GFP_KERNEL); + if (indio_dev->chan_attr_group.attrs == NULL) { + ret = -ENOMEM; + goto error_clear_attrs; } + /* Copy across original attributes */ + if (indio_dev->info->attrs) + memcpy(indio_dev->chan_attr_group.attrs, + indio_dev->info->attrs->attrs, + sizeof(indio_dev->chan_attr_group.attrs[0]) + *attrcount_orig); + attrn = attrcount_orig; + /* Add all elements from the list. */ + list_for_each_entry(p, &indio_dev->channel_attr_list, l) + indio_dev->chan_attr_group.attrs[attrn++] = &p->dev_attr.attr; + if (indio_dev->name) + indio_dev->chan_attr_group.attrs[attrn++] = &dev_attr_name.attr; + + indio_dev->groups[indio_dev->groupcounter++] = + &indio_dev->chan_attr_group; + return 0; error_clear_attrs: list_for_each_entry_safe(p, n, - &dev_info->channel_attr_list, l) { + &indio_dev->channel_attr_list, l) { list_del(&p->l); - iio_device_remove_and_free_read_attr(dev_info, p); + iio_device_remove_and_free_read_attr(indio_dev, 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) +static void iio_device_unregister_sysfs(struct iio_dev *indio_dev) { 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_for_each_entry_safe(p, n, &indio_dev->channel_attr_list, l) { list_del(&p->l); - iio_device_remove_and_free_read_attr(dev_info, p); + iio_device_remove_and_free_read_attr(indio_dev, 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(indio_dev->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[] = { @@ -907,230 +793,214 @@ static ssize_t iio_ev_value_store(struct device *dev, return len; } -static int iio_device_add_event_sysfs(struct iio_dev *dev_info, +static int iio_device_add_event_sysfs(struct iio_dev *indio_dev, 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, - 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 if (chan->differential) + mask = IIO_EVENT_CODE(chan->type, + 0, 0, + i%IIO_EV_DIR_MAX, + i/IIO_EV_DIR_MAX, + 0, + chan->channel, + chan->channel2); + 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]. + &indio_dev->dev, + &indio_dev->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); + &indio_dev->dev, + &indio_dev->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 *indio_dev) { struct iio_dev_attr *p, *n; list_for_each_entry_safe(p, n, - &dev_info->event_interfaces[num]. + &indio_dev->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 *indio_dev) { - int j; - int ret; - INIT_LIST_HEAD(&dev_info->event_interfaces[0].dev_attr_list); + int j, ret, attrcount = 0; + + INIT_LIST_HEAD(&indio_dev->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 < indio_dev->num_channels; j++) { + ret = iio_device_add_event_sysfs(indio_dev, + &indio_dev->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(indio_dev); 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 *indio_dev) { - __iio_remove_all_event_sysfs(dev_info, NULL, i); - return 0; + int j; + + for (j = 0; j < indio_dev->num_channels; j++) + if (indio_dev->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 int iio_device_register_eventset(struct iio_dev *dev_info) +static const char *iio_event_group_name = "events"; +static int iio_device_register_eventset(struct iio_dev *indio_dev) { - 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 (!(indio_dev->info->event_attrs || + iio_check_for_dynamic_events(indio_dev))) 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) { + indio_dev->event_interface = + kzalloc(sizeof(struct iio_event_interface), GFP_KERNEL); + if (indio_dev->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(indio_dev->event_interface); + if (indio_dev->info->event_attrs != NULL) { + attr = indio_dev->info->event_attrs->attrs; + while (*attr++ != NULL) + attrcount_orig++; + } + attrcount = attrcount_orig; + if (indio_dev->channels) { + ret = __iio_add_event_config_attrs(indio_dev); + 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; + indio_dev->event_interface->group.name = iio_event_group_name; + indio_dev->event_interface->group.attrs = + kzalloc(sizeof(indio_dev->event_interface->group.attrs[0]) + *(attrcount + 1), + GFP_KERNEL); + if (indio_dev->event_interface->group.attrs == NULL) { + ret = -ENOMEM; + goto error_free_setup_event_lines; } + if (indio_dev->info->event_attrs) + memcpy(indio_dev->event_interface->group.attrs, + indio_dev->info->event_attrs->attrs, + sizeof(indio_dev->event_interface->group.attrs[0]) + *attrcount_orig); + attrn = attrcount_orig; + /* Add all elements from the list. */ + list_for_each_entry(p, + &indio_dev->event_interface->dev_attr_list, + l) + indio_dev->event_interface->group.attrs[attrn++] = + &p->dev_attr.attr; + indio_dev->groups[indio_dev->groupcounter++] = + &indio_dev->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(indio_dev); + kfree(indio_dev->event_interface); error_ret: return ret; } -static void iio_device_unregister_eventset(struct iio_dev *dev_info) +static void iio_device_unregister_eventset(struct iio_dev *indio_dev) { - int i; - - if (dev_info->info->num_interrupt_lines == 0) + if (indio_dev->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(indio_dev); + kfree(indio_dev->event_interface->group.attrs); + kfree(indio_dev->event_interface); } static void iio_dev_release(struct device *device) { - iio_put(); - kfree(to_iio_dev(device)); + struct iio_dev *indio_dev = container_of(device, struct iio_dev, dev); + cdev_del(&indio_dev->chrdev); + if (indio_dev->modes & INDIO_BUFFER_TRIGGERED) + iio_device_unregister_trigger_consumer(indio_dev); + iio_device_unregister_eventset(indio_dev); + iio_device_unregister_sysfs(indio_dev); } static struct device_type iio_dev_type = { @@ -1154,12 +1024,21 @@ 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(); + + dev->id = ida_simple_get(&iio_ida, 0, 0, GFP_KERNEL); + if (dev->id < 0) { + /* cannot use a dev_err as the name isn't available */ + printk(KERN_ERR "Failed to get id\n"); + kfree(dev); + return NULL; + } + dev_set_name(&dev->dev, "iio:device%d", dev->id); } return dev; @@ -1168,75 +1047,111 @@ EXPORT_SYMBOL(iio_allocate_device); void iio_free_device(struct iio_dev *dev) { - if (dev) - iio_put_device(dev); + if (dev) { + ida_simple_remove(&iio_ida, dev->id); + kfree(dev); + } } EXPORT_SYMBOL(iio_free_device); -int iio_device_register(struct iio_dev *dev_info) +/** + * iio_chrdev_open() - chrdev file open for buffer access and ioctls + **/ +static int iio_chrdev_open(struct inode *inode, struct file *filp) { - int ret; + struct iio_dev *indio_dev = container_of(inode->i_cdev, + struct iio_dev, chrdev); + filp->private_data = indio_dev; - dev_info->id = iio_get_new_ida_val(&iio_ida); - if (dev_info->id < 0) { - ret = dev_info->id; - dev_err(&dev_info->dev, "Failed to get id\n"); - goto error_ret; + return iio_chrdev_buffer_open(indio_dev); +} + +/** + * iio_chrdev_release() - chrdev file close buffer access and ioctls + **/ +static int iio_chrdev_release(struct inode *inode, struct file *filp) +{ + iio_chrdev_buffer_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; } - dev_set_name(&dev_info->dev, "device%d", dev_info->id); + return -EINVAL; +} - ret = device_add(&dev_info->dev); - if (ret) - goto error_free_ida; - ret = iio_device_register_sysfs(dev_info); +static const struct file_operations iio_buffer_fileops = { + .read = iio_buffer_read_first_n_outer_addr, + .release = iio_chrdev_release, + .open = iio_chrdev_open, + .poll = iio_buffer_poll_addr, + .owner = THIS_MODULE, + .llseek = noop_llseek, + .unlocked_ioctl = iio_ioctl, + .compat_ioctl = iio_ioctl, +}; + +int iio_device_register(struct iio_dev *indio_dev) +{ + int ret; + + /* configure elements for the chrdev */ + indio_dev->dev.devt = MKDEV(MAJOR(iio_devt), indio_dev->id); + + ret = iio_device_register_sysfs(indio_dev); if (ret) { - dev_err(dev_info->dev.parent, + dev_err(indio_dev->dev.parent, "Failed to register sysfs interfaces\n"); - goto error_del_device; + goto error_ret; } - ret = iio_device_register_eventset(dev_info); + ret = iio_device_register_eventset(indio_dev); if (ret) { - dev_err(dev_info->dev.parent, + dev_err(indio_dev->dev.parent, "Failed to register event set\n"); goto error_free_sysfs; } - if (dev_info->modes & INDIO_RING_TRIGGERED) - iio_device_register_trigger_consumer(dev_info); + if (indio_dev->modes & INDIO_BUFFER_TRIGGERED) + iio_device_register_trigger_consumer(indio_dev); + ret = device_add(&indio_dev->dev); + if (ret < 0) + goto error_unreg_eventset; + cdev_init(&indio_dev->chrdev, &iio_buffer_fileops); + indio_dev->chrdev.owner = indio_dev->info->driver_module; + ret = cdev_add(&indio_dev->chrdev, indio_dev->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_free_ida: - iio_free_ida_val(&iio_ida, dev_info->id); + device_del(&indio_dev->dev); +error_unreg_eventset: + iio_device_unregister_eventset(indio_dev); +error_free_sysfs: + iio_device_unregister_sysfs(indio_dev); error_ret: return ret; } EXPORT_SYMBOL(iio_device_register); -void iio_device_unregister(struct iio_dev *dev_info) +void iio_device_unregister(struct iio_dev *indio_dev) { - 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); + device_unregister(&indio_dev->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); |