diff options
Diffstat (limited to 'drivers/staging/iio/industrialio-trigger.c')
-rw-r--r-- | drivers/staging/iio/industrialio-trigger.c | 299 |
1 files changed, 187 insertions, 112 deletions
diff --git a/drivers/staging/iio/industrialio-trigger.c b/drivers/staging/iio/industrialio-trigger.c index 57dd9232cf0d..615902333fb0 100644 --- a/drivers/staging/iio/industrialio-trigger.c +++ b/drivers/staging/iio/industrialio-trigger.c @@ -39,6 +39,19 @@ static LIST_HEAD(iio_trigger_list); static DEFINE_MUTEX(iio_trigger_list_lock); /** + * iio_trigger_read_name() - retrieve useful identifying name + **/ +static ssize_t iio_trigger_read_name(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_trigger *trig = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", trig->name); +} + +static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL); + +/** * iio_trigger_register_sysfs() - create a device for this trigger * @trig_info: the trigger * @@ -46,20 +59,16 @@ static DEFINE_MUTEX(iio_trigger_list_lock); **/ static int iio_trigger_register_sysfs(struct iio_trigger *trig_info) { - int ret = 0; - - if (trig_info->control_attrs) - ret = sysfs_create_group(&trig_info->dev.kobj, - trig_info->control_attrs); - - return ret; + return sysfs_add_file_to_group(&trig_info->dev.kobj, + &dev_attr_name.attr, + NULL); } static void iio_trigger_unregister_sysfs(struct iio_trigger *trig_info) { - if (trig_info->control_attrs) - sysfs_remove_group(&trig_info->dev.kobj, - trig_info->control_attrs); + sysfs_remove_file_from_group(&trig_info->dev.kobj, + &dev_attr_name.attr, + NULL); } @@ -134,14 +143,8 @@ EXPORT_SYMBOL(iio_trigger_register); void iio_trigger_unregister(struct iio_trigger *trig_info) { - struct iio_trigger *cursor; - mutex_lock(&iio_trigger_list_lock); - list_for_each_entry(cursor, &iio_trigger_list, list) - if (cursor == trig_info) { - list_del(&cursor->list); - break; - } + list_del(&trig_info->list); mutex_unlock(&iio_trigger_list_lock); iio_trigger_unregister_sysfs(trig_info); @@ -151,47 +154,55 @@ void iio_trigger_unregister(struct iio_trigger *trig_info) } EXPORT_SYMBOL(iio_trigger_unregister); -struct iio_trigger *iio_trigger_find_by_name(const char *name, size_t len) +static struct iio_trigger *iio_trigger_find_by_name(const char *name, + size_t len) { - struct iio_trigger *trig; - bool found = false; - - if (len && name[len - 1] == '\n') - len--; + struct iio_trigger *trig = NULL, *iter; mutex_lock(&iio_trigger_list_lock); - list_for_each_entry(trig, &iio_trigger_list, list) { - if (strncmp(trig->name, name, len) == 0) { - found = true; + list_for_each_entry(iter, &iio_trigger_list, list) + if (sysfs_streq(iter->name, name)) { + trig = iter; break; } - } mutex_unlock(&iio_trigger_list_lock); - return found ? trig : NULL; + return trig; } -EXPORT_SYMBOL(iio_trigger_find_by_name); void iio_trigger_poll(struct iio_trigger *trig, s64 time) { - struct iio_poll_func *pf_cursor; - - list_for_each_entry(pf_cursor, &trig->pollfunc_list, list) { - if (pf_cursor->poll_func_immediate) { - pf_cursor->poll_func_immediate(pf_cursor->private_data); - trig->use_count++; - } - } - list_for_each_entry(pf_cursor, &trig->pollfunc_list, list) { - if (pf_cursor->poll_func_main) { - pf_cursor->poll_func_main(pf_cursor->private_data, - time); - trig->use_count++; - } + int i; + if (!trig->use_count) { + for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++) + if (trig->subirqs[i].enabled) { + trig->use_count++; + generic_handle_irq(trig->subirq_base + i); + } } } EXPORT_SYMBOL(iio_trigger_poll); +irqreturn_t iio_trigger_generic_data_rdy_poll(int irq, void *private) +{ + iio_trigger_poll(private, iio_get_time_ns()); + return IRQ_HANDLED; +} +EXPORT_SYMBOL(iio_trigger_generic_data_rdy_poll); + +void iio_trigger_poll_chained(struct iio_trigger *trig, s64 time) +{ + int i; + if (!trig->use_count) { + for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++) + if (trig->subirqs[i].enabled) { + trig->use_count++; + handle_nested_irq(trig->subirq_base + i); + } + } +} +EXPORT_SYMBOL(iio_trigger_poll_chained); + void iio_trigger_notify_done(struct iio_trigger *trig) { trig->use_count--; @@ -203,18 +214,6 @@ void iio_trigger_notify_done(struct iio_trigger *trig) } EXPORT_SYMBOL(iio_trigger_notify_done); -/** - * iio_trigger_read_name() - retrieve useful identifying name - **/ -ssize_t iio_trigger_read_name(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct iio_trigger *trig = dev_get_drvdata(dev); - return sprintf(buf, "%s\n", trig->name); -} -EXPORT_SYMBOL(iio_trigger_read_name); - /* Trigger Consumer related functions */ /* Complexity in here. With certain triggers (datardy) an acknowledgement @@ -228,18 +227,16 @@ int iio_trigger_attach_poll_func(struct iio_trigger *trig, struct iio_poll_func *pf) { int ret = 0; - unsigned long flags; - - spin_lock_irqsave(&trig->pollfunc_list_lock, flags); - list_add_tail(&pf->list, &trig->pollfunc_list); - spin_unlock_irqrestore(&trig->pollfunc_list_lock, flags); - - if (trig->set_trigger_state) + bool notinuse + = bitmap_empty(trig->pool, CONFIG_IIO_CONSUMERS_PER_TRIGGER); + + pf->irq = iio_trigger_get_irq(trig); + ret = request_threaded_irq(pf->irq, pf->h, pf->thread, + pf->type, pf->name, + pf); + if (trig->set_trigger_state && notinuse) ret = trig->set_trigger_state(trig, true); - if (ret) { - printk(KERN_ERR "set trigger state failed\n"); - list_del(&pf->list); - } + return ret; } EXPORT_SYMBOL(iio_trigger_attach_poll_func); @@ -247,41 +244,68 @@ EXPORT_SYMBOL(iio_trigger_attach_poll_func); int iio_trigger_dettach_poll_func(struct iio_trigger *trig, struct iio_poll_func *pf) { - struct iio_poll_func *pf_cursor; - unsigned long flags; - int ret = -EINVAL; - - spin_lock_irqsave(&trig->pollfunc_list_lock, flags); - list_for_each_entry(pf_cursor, &trig->pollfunc_list, list) - if (pf_cursor == pf) { - ret = 0; - break; - } - if (!ret) { - if (list_is_singular(&trig->pollfunc_list) - && trig->set_trigger_state) { - spin_unlock_irqrestore(&trig->pollfunc_list_lock, - flags); - /* May sleep hence cannot hold the spin lock */ - ret = trig->set_trigger_state(trig, false); - if (ret) - goto error_ret; - spin_lock_irqsave(&trig->pollfunc_list_lock, flags); - } - /* - * Now we can delete safe in the knowledge that, if this is - * the last pollfunc then we have disabled the trigger anyway - * and so nothing should be able to call the pollfunc. - */ - list_del(&pf_cursor->list); + int ret = 0; + bool no_other_users + = (bitmap_weight(trig->pool, + CONFIG_IIO_CONSUMERS_PER_TRIGGER) + == 1); + if (trig->set_trigger_state && no_other_users) { + ret = trig->set_trigger_state(trig, false); + if (ret) + goto error_ret; } - spin_unlock_irqrestore(&trig->pollfunc_list_lock, flags); + iio_trigger_put_irq(trig, pf->irq); + free_irq(pf->irq, pf); error_ret: return ret; } EXPORT_SYMBOL(iio_trigger_dettach_poll_func); +irqreturn_t iio_pollfunc_store_time(int irq, void *p) +{ + struct iio_poll_func *pf = p; + pf->timestamp = iio_get_time_ns(); + return IRQ_WAKE_THREAD; +} +EXPORT_SYMBOL(iio_pollfunc_store_time); + +struct iio_poll_func +*iio_alloc_pollfunc(irqreturn_t (*h)(int irq, void *p), + irqreturn_t (*thread)(int irq, void *p), + int type, + void *private, + const char *fmt, + ...) +{ + va_list vargs; + struct iio_poll_func *pf; + + pf = kmalloc(sizeof *pf, GFP_KERNEL); + if (pf == NULL) + return NULL; + va_start(vargs, fmt); + pf->name = kvasprintf(GFP_KERNEL, fmt, vargs); + va_end(vargs); + if (pf->name == NULL) { + kfree(pf); + return NULL; + } + pf->h = h; + pf->thread = thread; + pf->type = type; + + return pf; +} +EXPORT_SYMBOL_GPL(iio_alloc_pollfunc); + +void iio_dealloc_pollfunc(struct iio_poll_func *pf) +{ + kfree(pf->name); + kfree(pf); +} +EXPORT_SYMBOL_GPL(iio_dealloc_pollfunc); + /** * iio_trigger_read_currrent() - trigger consumer sysfs query which trigger * @@ -348,6 +372,23 @@ static const struct attribute_group iio_trigger_consumer_attr_group = { static void iio_trig_release(struct device *device) { struct iio_trigger *trig = to_iio_trigger(device); + int i; + + if (trig->subirq_base) { + for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++) { + irq_modify_status(trig->subirq_base + i, + IRQ_NOAUTOEN, + IRQ_NOREQUEST | IRQ_NOPROBE); + irq_set_chip(trig->subirq_base + i, + NULL); + irq_set_handler(trig->subirq_base + i, + NULL); + } + + irq_free_descs(trig->subirq_base, + CONFIG_IIO_CONSUMERS_PER_TRIGGER); + } + kfree(trig->name); kfree(trig); iio_put(); } @@ -356,18 +397,66 @@ static struct device_type iio_trig_type = { .release = iio_trig_release, }; -struct iio_trigger *iio_allocate_trigger(void) +static void iio_trig_subirqmask(struct irq_data *d) +{ + struct irq_chip *chip = irq_data_get_irq_chip(d); + struct iio_trigger *trig + = container_of(chip, + struct iio_trigger, subirq_chip); + trig->subirqs[d->irq - trig->subirq_base].enabled = false; +} + +static void iio_trig_subirqunmask(struct irq_data *d) +{ + struct irq_chip *chip = irq_data_get_irq_chip(d); + struct iio_trigger *trig + = container_of(chip, + struct iio_trigger, subirq_chip); + trig->subirqs[d->irq - trig->subirq_base].enabled = true; +} + +struct iio_trigger *iio_allocate_trigger(const char *fmt, ...) { + va_list vargs; struct iio_trigger *trig; trig = kzalloc(sizeof *trig, GFP_KERNEL); if (trig) { + int i; trig->dev.type = &iio_trig_type; trig->dev.bus = &iio_bus_type; device_initialize(&trig->dev); dev_set_drvdata(&trig->dev, (void *)trig); - spin_lock_init(&trig->pollfunc_list_lock); - INIT_LIST_HEAD(&trig->list); - INIT_LIST_HEAD(&trig->pollfunc_list); + + mutex_init(&trig->pool_lock); + trig->subirq_base + = irq_alloc_descs(-1, 0, + CONFIG_IIO_CONSUMERS_PER_TRIGGER, + 0); + if (trig->subirq_base < 0) { + kfree(trig); + return NULL; + } + va_start(vargs, fmt); + trig->name = kvasprintf(GFP_KERNEL, fmt, vargs); + va_end(vargs); + if (trig->name == NULL) { + irq_free_descs(trig->subirq_base, + CONFIG_IIO_CONSUMERS_PER_TRIGGER); + kfree(trig); + return NULL; + } + trig->subirq_chip.name = trig->name; + trig->subirq_chip.irq_mask = &iio_trig_subirqmask; + trig->subirq_chip.irq_unmask = &iio_trig_subirqunmask; + for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++) { + irq_set_chip(trig->subirq_base + i, + &trig->subirq_chip); + irq_set_handler(trig->subirq_base + i, + &handle_simple_irq); + irq_modify_status(trig->subirq_base + i, + IRQ_NOREQUEST | IRQ_NOAUTOEN, + IRQ_NOPROBE); + } iio_get(); } return trig; @@ -398,20 +487,6 @@ int iio_device_unregister_trigger_consumer(struct iio_dev *dev_info) } EXPORT_SYMBOL(iio_device_unregister_trigger_consumer); -int iio_alloc_pollfunc(struct iio_dev *indio_dev, - void (*immediate)(struct iio_dev *indio_dev), - void (*main)(struct iio_dev *private_data, s64 time)) -{ - indio_dev->pollfunc = kzalloc(sizeof(*indio_dev->pollfunc), GFP_KERNEL); - if (indio_dev->pollfunc == NULL) - return -ENOMEM; - indio_dev->pollfunc->poll_func_immediate = immediate; - indio_dev->pollfunc->poll_func_main = main; - indio_dev->pollfunc->private_data = indio_dev; - return 0; -} -EXPORT_SYMBOL(iio_alloc_pollfunc); - int iio_triggered_ring_postenable(struct iio_dev *indio_dev) { return indio_dev->trig |