From: Grégor Boirie <gregor.boirie@xxxxxxxxxx> Add a sysfs file entry for each interrupt trigger instance allowing userspace to : * poll for interrupt events ; * retrieve number of interrupts that occurred since trigger was * initialized Signed-off-by: Gregor Boirie <gregor.boirie@xxxxxxxxxx> --- .../ABI/testing/sysfs-bus-iio-trig-interrupt | 22 ++++++ drivers/iio/trigger/iio-trig-interrupt.c | 88 ++++++++++++++++------ 2 files changed, 88 insertions(+), 22 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-trig-interrupt diff --git a/Documentation/ABI/testing/sysfs-bus-iio-trig-interrupt b/Documentation/ABI/testing/sysfs-bus-iio-trig-interrupt new file mode 100644 index 0000000..cb246d2 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-trig-interrupt @@ -0,0 +1,22 @@ +What: /sys/bus/iio/devices/triggerX/name +KernelVersion: 3.11 +Contact: linux-iio@xxxxxxxxxxxxxxx +Description: + The name attribute holds a description string for the current + trigger. In order to associate the trigger with an IIO device + one should write this name string to + /sys/bus/iio/devices/iio:deviceY/trigger/current_trigger. + +What: /sys/bus/iio/devices/triggerX/count +KernelVersion: 4.5 +Contact: linux-iio@xxxxxxxxxxxxxxx +Description: + The count attribute is a unsigned int counter holding the number + of times the attached interrupt occurred since trigger was + initialized. + You can poll(2) on that file and poll(2) will return whenever + the interrupt was triggered. If you use poll(2), set the events + POLLPRI and POLLERR. If you use select(2), set the file + descriptor in exceptfds. After poll(2) returns, either lseek(2) + to the beginning of the sysfs file and read the new value or + close the file and re-open it to read the value. diff --git a/drivers/iio/trigger/iio-trig-interrupt.c b/drivers/iio/trigger/iio-trig-interrupt.c index 3c4e18f..1bf3986 100644 --- a/drivers/iio/trigger/iio-trig-interrupt.c +++ b/drivers/iio/trigger/iio-trig-interrupt.c @@ -17,14 +17,53 @@ #include <linux/iio/iio.h> #include <linux/iio/trigger.h> - struct iio_interrupt_trigger_info { + struct kernfs_node *poll; + atomic_t count; unsigned int irq; }; +/* + * If interested by counter value, userspace should read often enough since + * counter may wrap. Userspace will miss interrupt events when counter wraps + * twice or more between 2 consecutive reads. + */ +ssize_t iio_interrupt_trigger_show_count(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_trigger *trig = to_iio_trigger(dev); + struct iio_interrupt_trigger_info *info = iio_trigger_get_drvdata(trig); + unsigned int count = atomic_read(&info->count); + + return snprintf(buf, PAGE_SIZE, "%u\n", count); +} + +static DEVICE_ATTR(count, S_IRUGO, iio_interrupt_trigger_show_count, NULL); + +static struct attribute *iio_interrupt_trigger_attrs[] = { + &dev_attr_count.attr, + NULL, +}; + +static const struct attribute_group iio_interrupt_trigger_attr_group = { + .attrs = iio_interrupt_trigger_attrs, +}; + +static const struct attribute_group *iio_interrupt_trigger_attr_groups[] = { + &iio_interrupt_trigger_attr_group, + NULL +}; + static irqreturn_t iio_interrupt_trigger_poll(int irq, void *private) { - iio_trigger_poll(private); + struct iio_trigger *trig = private; + struct iio_interrupt_trigger_info *info = iio_trigger_get_drvdata(trig); + + atomic_inc(&info->count); + sysfs_notify_dirent(info->poll); + iio_trigger_poll(trig); + return IRQ_HANDLED; } @@ -36,17 +75,12 @@ static int iio_interrupt_trigger_probe(struct platform_device *pdev) { struct iio_interrupt_trigger_info *trig_info; struct iio_trigger *trig; - unsigned long irqflags; struct resource *irq_res; int irq, ret = 0; irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - - if (irq_res == NULL) + if (!irq_res) return -ENODEV; - - irqflags = (irq_res->flags & IRQF_TRIGGER_MASK) | IRQF_SHARED; - irq = irq_res->start; trig = iio_trigger_alloc("irqtrig%d", irq); @@ -60,27 +94,36 @@ static int iio_interrupt_trigger_probe(struct platform_device *pdev) ret = -ENOMEM; goto error_put_trigger; } - iio_trigger_set_drvdata(trig, trig_info); + + atomic_set(&trig_info->count, 0); trig_info->irq = irq; + iio_trigger_set_drvdata(trig, trig_info); trig->ops = &iio_interrupt_trigger_ops; - ret = request_irq(irq, iio_interrupt_trigger_poll, - irqflags, trig->name, trig); - if (ret) { - dev_err(&pdev->dev, - "request IRQ-%d failed", irq); + trig->dev.groups = iio_interrupt_trigger_attr_groups; + ret = iio_trigger_register(trig); + if (ret) goto error_free_trig_info; + + /* Create a sysfs entry which the userspace may poll for irq events. */ + trig_info->poll = sysfs_get_dirent(trig->dev.kobj.sd, "count"); + if (!trig_info->poll) { + ret = -ENOENT; + goto error_unregister_trig; } - ret = iio_trigger_register(trig); - if (ret) - goto error_release_irq; - platform_set_drvdata(pdev, trig); + ret = request_irq(irq, iio_interrupt_trigger_poll, + (irq_res->flags & IRQF_TRIGGER_MASK) | IRQF_SHARED, + trig->name, trig); + if (!ret) { + platform_set_drvdata(pdev, trig); + return 0; + } - return 0; + sysfs_put(trig_info->poll); /* First clean up the partly allocated trigger */ -error_release_irq: - free_irq(irq, trig); +error_unregister_trig: + iio_trigger_unregister(trig); error_free_trig_info: kfree(trig_info); error_put_trigger: @@ -96,8 +139,9 @@ static int iio_interrupt_trigger_remove(struct platform_device *pdev) trig = platform_get_drvdata(pdev); trig_info = iio_trigger_get_drvdata(trig); - iio_trigger_unregister(trig); free_irq(trig_info->irq, trig); + sysfs_put(trig_info->poll); + iio_trigger_unregister(trig); kfree(trig_info); iio_trigger_put(trig); -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html