X-Git-Url: https://git.openpandora.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=drivers%2Fstaging%2Fiio%2Findustrialio-core.c;h=83e55b1d6400b43091becc5c7895954f3ac9175f;hb=8ce7375be7036d6e536b6341ec83e5db849cda6e;hp=19819e7578c6175f51a9c8b3b4793cbb4f0ab1b9;hpb=f33c596a2f921696391801b637ed50d514634079;p=pandora-kernel.git diff --git a/drivers/staging/iio/industrialio-core.c b/drivers/staging/iio/industrialio-core.c index 19819e7578c6..83e55b1d6400 100644 --- a/drivers/staging/iio/industrialio-core.c +++ b/drivers/staging/iio/industrialio-core.c @@ -21,21 +21,17 @@ #include #include #include +#include #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);