On 19/02/16 19:18, Gregor Boirie wrote: > From: Grégor Boirie <gregor.boirie@xxxxxxxxxx> > Hi Gregor. You certainly have some unusual requirements - or perhaps you are simply the first person to show up with them here! > Add a sysfs file entry for each interrupt trigger instance allowing > userspace to : > * poll for interrupt events ; Why? > * retrieve number of interrupts that occurred since trigger was > * initialized Again why? This is interesting stuff, but I'd like to fully understand the question of what you are doing with it before we go too far into the code. > > 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); > > -- 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