X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=drivers%2Fstaging%2Fiio%2Findustrialio-core.c;h=52188f97cc56ffd7475011cf931a3b395b696723;hb=aaf370db7dad574e166f64cd9ad4129f12198145;hp=3a824146455e3b9b999159c3272c701c5c975ce9;hpb=6f7c8ee585e9db54cb29af1bdb93f29837824933;p=~andy%2Flinux diff --git a/drivers/staging/iio/industrialio-core.c b/drivers/staging/iio/industrialio-core.c index 3a824146455..52188f97cc5 100644 --- a/drivers/staging/iio/industrialio-core.c +++ b/drivers/staging/iio/industrialio-core.c @@ -44,27 +44,51 @@ struct bus_type iio_bus_type = { }; EXPORT_SYMBOL(iio_bus_type); -void __iio_change_event(struct iio_detected_event_list *ev, - int ev_code, - s64 timestamp) -{ - ev->ev.id = ev_code; - ev->ev.timestamp = timestamp; -} -EXPORT_SYMBOL(__iio_change_event); +static const char * const iio_chan_type_name_spec_shared[] = { + [IIO_TIMESTAMP] = "timestamp", + [IIO_ACCEL] = "accel", + [IIO_IN] = "in", + [IIO_IN_DIFF] = "in-in", + [IIO_GYRO] = "gyro", + [IIO_TEMP] = "temp", + [IIO_MAGN] = "magn", + [IIO_INCLI] = "incli", + [IIO_ROT] = "rot", + [IIO_INTENSITY] = "intensity", + [IIO_LIGHT] = "illuminance", + [IIO_ANGL] = "angl", +}; + +static const char * const iio_chan_type_name_spec_complex[] = { + [IIO_IN_DIFF] = "in%d-in%d", +}; -/* Used both in the interrupt line put events and the ring buffer ones */ +static const char * const iio_modifier_names_light[] = { + [IIO_MOD_LIGHT_BOTH] = "both", + [IIO_MOD_LIGHT_IR] = "ir", +}; -/* Note that in it's current form someone has to be listening before events - * are queued. Hence a client MUST open the chrdev before the ring buffer is - * switched on. - */ -int __iio_push_event(struct iio_event_interface *ev_int, - int ev_code, - s64 timestamp, - struct iio_shared_ev_pointer * - shared_pointer_p) +static const char * const iio_modifier_names_axial[] = { + [IIO_MOD_X] = "x", + [IIO_MOD_Y] = "y", + [IIO_MOD_Z] = "z", +}; + +/* relies on pairs of these shared then separate */ +static const char * const iio_chan_info_postfix[] = { + [IIO_CHAN_INFO_SCALE_SHARED/2] = "scale", + [IIO_CHAN_INFO_OFFSET_SHARED/2] = "offset", + [IIO_CHAN_INFO_CALIBSCALE_SHARED/2] = "calibscale", + [IIO_CHAN_INFO_CALIBBIAS_SHARED/2] = "calibbias", +}; + +int iio_push_event(struct iio_dev *dev_info, + int ev_line, + int ev_code, + s64 timestamp) { + struct iio_event_interface *ev_int + = &dev_info->event_interfaces[ev_line]; struct iio_detected_event_list *ev; int ret = 0; @@ -83,9 +107,6 @@ int __iio_push_event(struct iio_event_interface *ev_int, } ev->ev.id = ev_code; ev->ev.timestamp = timestamp; - ev->shared_pointer = shared_pointer_p; - if (ev->shared_pointer) - shared_pointer_p->ev_p = ev; list_add_tail(&ev->list, &ev_int->det_events.list); ev_int->current_events++; @@ -97,85 +118,8 @@ int __iio_push_event(struct iio_event_interface *ev_int, error_ret: return ret; } -EXPORT_SYMBOL(__iio_push_event); - -int iio_push_event(struct iio_dev *dev_info, - int ev_line, - int ev_code, - s64 timestamp) -{ - return __iio_push_event(&dev_info->event_interfaces[ev_line], - ev_code, timestamp, NULL); -} EXPORT_SYMBOL(iio_push_event); -/* Generic interrupt line interrupt handler */ -static irqreturn_t iio_interrupt_handler(int irq, void *_int_info) -{ - struct iio_interrupt *int_info = _int_info; - struct iio_dev *dev_info = int_info->dev_info; - struct iio_event_handler_list *p; - s64 time_ns; - unsigned long flags; - - spin_lock_irqsave(&int_info->ev_list_lock, flags); - if (list_empty(&int_info->ev_list)) { - spin_unlock_irqrestore(&int_info->ev_list_lock, flags); - return IRQ_NONE; - } - - time_ns = iio_get_time_ns(); - list_for_each_entry(p, &int_info->ev_list, list) { - disable_irq_nosync(irq); - p->handler(dev_info, 1, time_ns, !(p->refcount > 1)); - } - spin_unlock_irqrestore(&int_info->ev_list_lock, flags); - - return IRQ_HANDLED; -} - -static struct iio_interrupt *iio_allocate_interrupt(void) -{ - struct iio_interrupt *i = kmalloc(sizeof *i, GFP_KERNEL); - if (i) { - spin_lock_init(&i->ev_list_lock); - INIT_LIST_HEAD(&i->ev_list); - } - return i; -} - -/* Confirming the validity of supplied irq is left to drivers.*/ -int iio_register_interrupt_line(unsigned int irq, - struct iio_dev *dev_info, - int line_number, - unsigned long type, - const char *name) -{ - int ret; - - dev_info->interrupts[line_number] = iio_allocate_interrupt(); - if (dev_info->interrupts[line_number] == NULL) { - ret = -ENOMEM; - goto error_ret; - } - dev_info->interrupts[line_number]->line_number = line_number; - dev_info->interrupts[line_number]->irq = irq; - dev_info->interrupts[line_number]->dev_info = dev_info; - - /* Possibly only request on demand? - * Can see this may complicate the handling of interrupts. - * However, with this approach we might end up handling lots of - * events no-one cares about.*/ - ret = request_irq(irq, - &iio_interrupt_handler, - type, - name, - dev_info->interrupts[line_number]); - -error_ret: - return ret; -} -EXPORT_SYMBOL(iio_register_interrupt_line); /* This turns up an awful lot */ ssize_t iio_read_const_attr(struct device *dev, @@ -186,54 +130,6 @@ ssize_t iio_read_const_attr(struct device *dev, } EXPORT_SYMBOL(iio_read_const_attr); -/* Before this runs the interrupt generator must have been disabled */ -void iio_unregister_interrupt_line(struct iio_dev *dev_info, int line_number) -{ - /* make sure the interrupt handlers are all done */ - flush_scheduled_work(); - free_irq(dev_info->interrupts[line_number]->irq, - dev_info->interrupts[line_number]); - kfree(dev_info->interrupts[line_number]); -} -EXPORT_SYMBOL(iio_unregister_interrupt_line); - -/* Reference counted add and remove */ -void iio_add_event_to_list(struct iio_event_handler_list *el, - struct list_head *head) -{ - unsigned long flags; - struct iio_interrupt *inter = to_iio_interrupt(head); - - /* take mutex to protect this element */ - mutex_lock(&el->exist_lock); - if (el->refcount == 0) { - /* Take the event list spin lock */ - spin_lock_irqsave(&inter->ev_list_lock, flags); - list_add(&el->list, head); - spin_unlock_irqrestore(&inter->ev_list_lock, flags); - } - el->refcount++; - mutex_unlock(&el->exist_lock); -} -EXPORT_SYMBOL(iio_add_event_to_list); - -void iio_remove_event_from_list(struct iio_event_handler_list *el, - struct list_head *head) -{ - unsigned long flags; - struct iio_interrupt *inter = to_iio_interrupt(head); - - mutex_lock(&el->exist_lock); - el->refcount--; - if (el->refcount == 0) { - /* Take the event list spin lock */ - spin_lock_irqsave(&inter->ev_list_lock, flags); - list_del_init(&el->list); - spin_unlock_irqrestore(&inter->ev_list_lock, flags); - } - mutex_unlock(&el->exist_lock); -} -EXPORT_SYMBOL(iio_remove_event_from_list); static ssize_t iio_event_chrdev_read(struct file *filep, char __user *buf, @@ -273,18 +169,6 @@ static ssize_t iio_event_chrdev_read(struct file *filep, list_del(&el->list); ev_int->current_events--; mutex_unlock(&ev_int->event_list_lock); - /* - * Possible concurency issue if an update of this event is on its way - * through. May lead to new event being removed whilst the reported - * event was the unescalated event. In typical use case this is not a - * problem as userspace will say read half the buffer due to a 50% - * full event which would make the correct 100% full incorrect anyway. - */ - if (el->shared_pointer) { - spin_lock(&el->shared_pointer->lock); - (el->shared_pointer->ev_p) = NULL; - spin_unlock(&el->shared_pointer->lock); - } kfree(el); return len; @@ -381,7 +265,7 @@ void iio_device_free_chrdev_minor(int val) spin_unlock(&iio_ida_lock); } -int iio_setup_ev_int(struct iio_event_interface *ev_int, +static int iio_setup_ev_int(struct iio_event_interface *ev_int, const char *name, struct module *owner, struct device *dev) @@ -433,7 +317,7 @@ error_device_put: return ret; } -void iio_free_ev_int(struct iio_event_interface *ev_int) +static void iio_free_ev_int(struct iio_event_interface *ev_int) { device_unregister(&ev_int->dev); put_device(&ev_int->dev); @@ -488,24 +372,375 @@ static void __exit iio_exit(void) bus_unregister(&iio_bus_type); } -static int iio_device_register_sysfs(struct iio_dev *dev_info) +static ssize_t iio_read_channel_info(struct device *dev, + struct device_attribute *attr, + char *buf) { - int ret = 0; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + int val, val2; + int ret = indio_dev->read_raw(indio_dev, this_attr->c, + &val, &val2, this_attr->address); - ret = sysfs_create_group(&dev_info->dev.kobj, dev_info->attrs); - if (ret) { - dev_err(dev_info->dev.parent, - "Failed to register sysfs hooks\n"); + if (ret < 0) + return ret; + + if (ret == IIO_VAL_INT) + return sprintf(buf, "%d\n", val); + else if (ret == IIO_VAL_INT_PLUS_MICRO) { + if (val2 < 0) + return sprintf(buf, "-%d.%06u\n", val, -val2); + else + return sprintf(buf, "%d.%06u\n", val, val2); + } else + return 0; +} + +static ssize_t iio_write_channel_info(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + int ret, integer = 0, micro = 0, micro_mult = 100000; + bool integer_part = true, negative = false; + + /* Assumes decimal - precision based on number of digits */ + if (!indio_dev->write_raw) + return -EINVAL; + if (buf[0] == '-') { + negative = true; + buf++; + } + while (*buf) { + if ('0' <= *buf && *buf <= '9') { + if (integer_part) + integer = integer*10 + *buf - '0'; + else { + micro += micro_mult*(*buf - '0'); + if (micro_mult == 1) + break; + micro_mult /= 10; + } + } else if (*buf == '\n') { + if (*(buf + 1) == '\0') + break; + else + return -EINVAL; + } else if (*buf == '.') { + integer_part = false; + } else { + return -EINVAL; + } + buf++; + } + if (negative) { + if (integer) + integer = -integer; + else + micro = -micro; + } + + ret = indio_dev->write_raw(indio_dev, this_attr->c, + integer, micro, this_attr->address); + if (ret) + return ret; + + 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; +} + +int __iio_device_attr_init(struct device_attribute *dev_attr, + const char *postfix, + struct iio_chan_spec const *chan, + ssize_t (*readfunc)(struct device *dev, + struct device_attribute *attr, + char *buf), + ssize_t (*writefunc)(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len), + bool generic) +{ + 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); + + if (name_format == NULL) { + ret = -ENOMEM; + goto error_free_full_postfix; + } + dev_attr->attr.name = kasprintf(GFP_KERNEL, + name_format, + chan->channel, + chan->channel2); + if (dev_attr->attr.name == NULL) { + ret = -ENOMEM; + goto error_free_name_format; + } + + if (readfunc) { + dev_attr->attr.mode |= S_IRUGO; + dev_attr->show = readfunc; + } + + if (writefunc) { + dev_attr->attr.mode |= S_IWUSR; + dev_attr->store = writefunc; + } + kfree(name_format); + kfree(full_postfix); + + return 0; + +error_free_name_format: + kfree(name_format); +error_free_full_postfix: + kfree(full_postfix); +error_ret: + return ret; +} + +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, + char *buf), + ssize_t (*writefunc)(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len), + int mask, + bool generic, + struct device *dev, + struct list_head *attr_list) +{ + int ret; + struct iio_dev_attr *iio_attr, *t; + + iio_attr = kzalloc(sizeof *iio_attr, GFP_KERNEL); + if (iio_attr == NULL) { + ret = -ENOMEM; goto error_ret; } + ret = __iio_device_attr_init(&iio_attr->dev_attr, + postfix, chan, + readfunc, writefunc, generic); + if (ret) + goto error_iio_dev_attr_free; + iio_attr->c = chan; + iio_attr->address = mask; + list_for_each_entry(t, attr_list, l) + if (strcmp(t->dev_attr.attr.name, + iio_attr->dev_attr.attr.name) == 0) { + if (!generic) + dev_err(dev, "tried to double register : %s\n", + t->dev_attr.attr.name); + 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; + +error_device_attr_deinit: + __iio_device_attr_deinit(&iio_attr->dev_attr); +error_iio_dev_attr_free: + kfree(iio_attr); +error_ret: + return ret; +} + +static int iio_device_add_channel_sysfs(struct iio_dev *dev_info, + struct iio_chan_spec const *chan) +{ + int ret, i; + + 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, + NULL, + 0, + 0, + &dev_info->dev, + &dev_info->channel_attr_list); + if (ret) + goto error_ret; + + for_each_set_bit(i, &chan->info_mask, sizeof(long)*8) { + ret = __iio_add_chan_devattr(iio_chan_info_postfix[i/2], + NULL, chan, + &iio_read_channel_info, + &iio_write_channel_info, + (1 << i), + !(i%2), + &dev_info->dev, + &dev_info->channel_attr_list); + if (ret == -EBUSY && (i%2 == 0)) { + ret = 0; + continue; + } + if (ret < 0) + goto error_ret; + } error_ret: return 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); +} + +static int iio_device_register_sysfs(struct iio_dev *dev_info) +{ + int i, ret = 0; + struct iio_dev_attr *p, *n; + + if (dev_info->attrs) { + ret = sysfs_create_group(&dev_info->dev.kobj, dev_info->attrs); + if (ret) { + dev_err(dev_info->dev.parent, + "Failed to register sysfs hooks\n"); + goto error_ret; + } + } + + /* + * 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 + ->channels[i]); + if (ret < 0) + goto error_clear_attrs; + } + + return 0; +error_clear_attrs: + 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->attrs) + sysfs_remove_group(&dev_info->dev.kobj, dev_info->attrs); +error_ret: + return ret; + +} + static void iio_device_unregister_sysfs(struct iio_dev *dev_info) { - sysfs_remove_group(&dev_info->dev.kobj, dev_info->attrs); + + struct iio_dev_attr *p, *n; + 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->attrs) + sysfs_remove_group(&dev_info->dev.kobj, dev_info->attrs); } /* Return a negative errno on failure */ @@ -552,32 +787,232 @@ static void iio_device_unregister_id(struct iio_dev *dev_info) iio_free_ida_val(&iio_ida, dev_info->id); } +static const char * const iio_ev_type_text[] = { + [IIO_EV_TYPE_THRESH] = "thresh", + [IIO_EV_TYPE_MAG] = "mag", + [IIO_EV_TYPE_ROC] = "roc" +}; + +static const char * const iio_ev_dir_text[] = { + [IIO_EV_DIR_EITHER] = "either", + [IIO_EV_DIR_RISING] = "rising", + [IIO_EV_DIR_FALLING] = "falling" +}; + +static ssize_t iio_ev_state_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + int ret; + unsigned long val; + ret = strict_strtoul(buf, 10, &val); + if (ret || val < 0 || val > 1) + return -EINVAL; + + ret = indio_dev->write_event_config(indio_dev, this_attr->address, + val); + return (ret < 0) ? ret : len; +} + +static ssize_t iio_ev_state_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + int val = indio_dev->read_event_config(indio_dev, this_attr->address); + + if (val < 0) + return val; + else + return sprintf(buf, "%d\n", val); +} + +static ssize_t iio_ev_value_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + int val, ret; + + ret = indio_dev->read_event_value(indio_dev, + this_attr->address, &val); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", val); +} + +static ssize_t iio_ev_value_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + unsigned long val; + int ret; + + ret = strict_strtoul(buf, 10, &val); + if (ret) + return ret; + + ret = indio_dev->write_event_value(indio_dev, this_attr->address, + val); + if (ret < 0) + return ret; + + return len; +} + +static int iio_device_add_event_sysfs(struct iio_dev *dev_info, + struct iio_chan_spec const *chan) +{ + + int ret = 0, i, mask; + 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]); + 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: + 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"); + } + 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_attr_list); + kfree(postfix); + if (ret) + goto error_ret; + + 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]); + if (postfix == NULL) { + ret = -ENOMEM; + goto error_ret; + } + ret = __iio_add_chan_devattr(postfix, NULL, 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); + kfree(postfix); + if (ret) + goto error_ret; + + } + +error_ret: + return ret; +} + +static inline void __iio_remove_all_event_sysfs(struct iio_dev *dev_info, + const char *groupname, + int num) +{ + struct iio_dev_attr *p, *n; + list_for_each_entry_safe(p, n, + &dev_info->event_interfaces[num]. + 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) { + int j; int ret; - /*p for adding, q for removing */ struct attribute **attrp, **attrq; if (dev_info->event_conf_attrs && dev_info->event_conf_attrs[i].attrs) { attrp = dev_info->event_conf_attrs[i].attrs; while (*attrp) { - ret = sysfs_add_file_to_group(&dev_info->dev.kobj, + ret = sysfs_add_file_to_group(&dev_info + ->event_interfaces[0] + .dev.kobj, *attrp, - dev_info - ->event_attrs[i].name); + NULL); if (ret) goto error_ret; attrp++; } } + INIT_LIST_HEAD(&dev_info->event_interfaces[0].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; + } + } return 0; +error_clear_attrs: + __iio_remove_all_event_sysfs(dev_info, + NULL, + i); error_ret: attrq = dev_info->event_conf_attrs[i].attrs; while (attrq != attrp) { - sysfs_remove_file_from_group(&dev_info->dev.kobj, - *attrq, - dev_info->event_attrs[i].name); + sysfs_remove_file_from_group(&dev_info + ->event_interfaces[0] + .dev.kobj, + *attrq, + NULL); attrq++; } @@ -588,15 +1023,18 @@ static inline int __iio_remove_event_config_attrs(struct iio_dev *dev_info, int i) { struct attribute **attrq; - + __iio_remove_all_event_sysfs(dev_info, + NULL, + i); if (dev_info->event_conf_attrs && dev_info->event_conf_attrs[i].attrs) { attrq = dev_info->event_conf_attrs[i].attrs; while (*attrq) { - sysfs_remove_file_from_group(&dev_info->dev.kobj, + sysfs_remove_file_from_group(&dev_info + ->event_interfaces[0] + .dev.kobj, *attrq, - dev_info - ->event_attrs[i].name); + NULL); attrq++; } } @@ -620,14 +1058,6 @@ static int iio_device_register_eventset(struct iio_dev *dev_info) goto error_ret; } - dev_info->interrupts = kzalloc(sizeof(struct iio_interrupt *) - *dev_info->num_interrupt_lines, - GFP_KERNEL); - if (dev_info->interrupts == NULL) { - ret = -ENOMEM; - goto error_free_event_interfaces; - } - for (i = 0; i < dev_info->num_interrupt_lines; i++) { dev_info->event_interfaces[i].owner = dev_info->driver_module; @@ -650,10 +1080,12 @@ static int iio_device_register_eventset(struct iio_dev *dev_info) dev_set_drvdata(&dev_info->event_interfaces[i].dev, (void *)dev_info); - ret = sysfs_create_group(&dev_info - ->event_interfaces[i] - .dev.kobj, - &dev_info->event_attrs[i]); + + if (dev_info->event_attrs != NULL) + ret = sysfs_create_group(&dev_info + ->event_interfaces[i] + .dev.kobj, + &dev_info->event_attrs[i]); if (ret) { dev_err(&dev_info->dev, @@ -676,14 +1108,13 @@ error_unregister_config_attrs: i = dev_info->num_interrupt_lines - 1; error_remove_sysfs_interfaces: for (j = 0; j < i; j++) - sysfs_remove_group(&dev_info + if (dev_info->event_attrs != NULL) + sysfs_remove_group(&dev_info ->event_interfaces[j].dev.kobj, &dev_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->interrupts); -error_free_event_interfaces: kfree(dev_info->event_interfaces); error_ret: @@ -696,14 +1127,16 @@ static void iio_device_unregister_eventset(struct iio_dev *dev_info) if (dev_info->num_interrupt_lines == 0) return; - for (i = 0; i < dev_info->num_interrupt_lines; i++) - sysfs_remove_group(&dev_info - ->event_interfaces[i].dev.kobj, - &dev_info->event_attrs[i]); + for (i = 0; i < dev_info->num_interrupt_lines; i++) { + __iio_remove_event_config_attrs(dev_info, i); + if (dev_info->event_attrs != NULL) + sysfs_remove_group(&dev_info + ->event_interfaces[i].dev.kobj, + &dev_info->event_attrs[i]); + } for (i = 0; i < dev_info->num_interrupt_lines; i++) iio_free_ev_int(&dev_info->event_interfaces[i]); - kfree(dev_info->interrupts); kfree(dev_info->event_interfaces); }