Re: [PATCH] iio: acpi_als: Add trigger support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Sat, Dec 5, 2020 at 10:27 AM Jonathan Cameron <jic23@xxxxxxxxxx> wrote:
>
> On Fri,  4 Dec 2020 12:37:55 -0800
> Gwendal Grignou <gwendal@xxxxxxxxxxxx> wrote:
>
> > Add timestamp channel: use standard procedure to collect timestamp.
> > As some firmware do not notify on illuminance changes, add a
> > trigger to periodically query light.
> > We can either use the device trigger, or a software trigger like sysfs
> > or hrtimer.
> >
> > This change is not backward compatible. To get samples from bios that
> > supports notification, we need to register the hardware trigger first:
> >
> > echo acpi-als-dev${X} > iio\:device${X}/trigger/current_trigger
>
> That's a problem as we can't break backwards compatibility.
> Note that you can set a default trigger for a device with
>
> indio_dev->trig = iio_trigger_get(trig);
> If we do that then it can still be overridden but at least
> we don't break backwards compatibility.
Done in v2, I now understand the code better.
> We also have an immutable variant if we really don't want
> it to be changed later but I don't think that is true here.
>
> >
> > Check iio_info reports the sensor as buffer capable:
> > iio:device0: acpi-als (buffer capable)
> > Check we can get data on demand:
> > echo 1 > iio_sysfs_trigger/add_trigger
> > cat trigger2/name > iio\:device0/trigger/current_trigger
> > for i in iio\:device0/scan_elements/*_en iio\:device0/buffer/enable ; do
> >   echo 1 > $i
> > done
> > od -x /dev/iio\:device0&
> > echo 1 > trigger2/trigger_now
> >
> > Signed-off-by: Gwendal Grignou <gwendal@xxxxxxxxxxxx>
> > ---
> >  drivers/iio/light/acpi-als.c | 86 +++++++++++++++++++++++++-----------
> >  1 file changed, 59 insertions(+), 27 deletions(-)
> >
> > diff --git a/drivers/iio/light/acpi-als.c b/drivers/iio/light/acpi-als.c
> > index 1eafd0b24e182..2619e4b073a59 100644
> > --- a/drivers/iio/light/acpi-als.c
> > +++ b/drivers/iio/light/acpi-als.c
> > @@ -16,11 +16,15 @@
> >  #include <linux/module.h>
> >  #include <linux/acpi.h>
> >  #include <linux/err.h>
> > +#include <linux/irq.h>
> >  #include <linux/mutex.h>
> >
> >  #include <linux/iio/iio.h>
> >  #include <linux/iio/buffer.h>
> >  #include <linux/iio/kfifo_buf.h>
> > +#include <linux/iio/trigger.h>
> > +#include <linux/iio/triggered_buffer.h>
> > +#include <linux/iio/trigger_consumer.h>
> >
> >  #define ACPI_ALS_CLASS                       "als"
> >  #define ACPI_ALS_DEVICE_NAME         "acpi-als"
> > @@ -45,22 +49,22 @@ static const struct iio_chan_spec acpi_als_channels[] = {
> >               .info_mask_separate     = BIT(IIO_CHAN_INFO_RAW) |
> >                                         BIT(IIO_CHAN_INFO_PROCESSED),
> >       },
> > +     IIO_CHAN_SOFT_TIMESTAMP(1),
> >  };
> >
> >  /*
> >   * The event buffer contains timestamp and all the data from
> >   * the ACPI0008 block. There are multiple, but so far we only
> > - * support _ALI (illuminance). Once someone adds new channels
> > - * to acpi_als_channels[], the evt_buffer below will grow
> > - * automatically.
> > + * support _ALI (illuminance):
> > + * One channel, paddind and timestamp.
> >   */
> > -#define ACPI_ALS_EVT_NR_SOURCES              ARRAY_SIZE(acpi_als_channels)
> >  #define ACPI_ALS_EVT_BUFFER_SIZE             \
> > -     (sizeof(s64) + (ACPI_ALS_EVT_NR_SOURCES * sizeof(s32)))
> > +     (sizeof(s32) + sizeof(s32) + sizeof(s64))
> >
> >  struct acpi_als {
> >       struct acpi_device      *device;
> >       struct mutex            lock;
> > +     struct iio_trigger      *trig;
> >
> >       s32                     evt_buffer[ACPI_ALS_EVT_BUFFER_SIZE];
> This gets used in iio_push_to_buffers_with_timestamp and that
> has a few not well documented quirks (on list to fix)
> In particular it relies on being able to put the timestamp in
> the right place and side effect of that is that the buffer
> passed to that function must be 8 byte aligned.
> Note I've been working through fixing drivers for this but clearly
> still some to go.
>
> You can probably do that here with __aligned(8)
Done in v2.
>
> As a side note, isn't the buffer way too big?  I think should be
> ACPI_ALS_EVT_BUFFER_SIZE / sizeof(u32)
Done in v2.
>
> >  };
> > @@ -104,33 +108,20 @@ static void acpi_als_notify(struct acpi_device *device, u32 event)
> >  {
> >       struct iio_dev *indio_dev = acpi_driver_data(device);
> >       struct acpi_als *als = iio_priv(indio_dev);
> > -     s32 *buffer = als->evt_buffer;
> > -     s64 time_ns = iio_get_time_ns(indio_dev);
> > -     s32 val;
> > -     int ret;
> > -
> > -     mutex_lock(&als->lock);
> >
> > -     memset(buffer, 0, ACPI_ALS_EVT_BUFFER_SIZE);
> > +     if (!iio_buffer_enabled(indio_dev) ||
> > +         !iio_trigger_using_own(indio_dev))
> > +             return;
> >
> >       switch (event) {
> >       case ACPI_ALS_NOTIFY_ILLUMINANCE:
> > -             ret = acpi_als_read_value(als, ACPI_ALS_ILLUMINANCE, &val);
> > -             if (ret < 0)
> > -                     goto out;
> > -             *buffer++ = val;
> > +             iio_trigger_poll_chained(als->trig);
>
> One issue with this path is it won't have set the poll function timestamp.
> It's a long term problem that there is no way for a device to know with
> certainty if the top half of it's trigger handler was ever called.
>
> I have been thinking about adding accessors to pf->timestamp to set
> and clear it which could also set a flag to say it had been set.
> Hence the get would return an error if not and we could grab a local
> timestamp if that happens as it would be the best available.
Looking at other devices that support sw/hw riggers (for instance
light/rpr0521.c), pf->timestamp == 0 indicates that the top half was
not called.
>
> Anyhow, I would assume you will get all 0 timestamps currently.
>
> >               break;
> >       default:
> >               /* Unhandled event */
> >               dev_dbg(&device->dev, "Unhandled ACPI ALS event (%08x)!\n",
> >                       event);
> > -             goto out;
> >       }
> > -
> > -     iio_push_to_buffers_with_timestamp(indio_dev, als->evt_buffer, time_ns);
> > -
> > -out:
> > -     mutex_unlock(&als->lock);
> >  }
> >
> >  static int acpi_als_read_raw(struct iio_dev *indio_dev,
> > @@ -161,11 +152,37 @@ static const struct iio_info acpi_als_info = {
> >       .read_raw               = acpi_als_read_raw,
> >  };
> >
> > +static irqreturn_t acpi_als_trigger_handler(int irq, void *p)
> > +{
> > +     struct iio_poll_func *pf = p;
> > +     struct iio_dev *indio_dev = pf->indio_dev;
> > +     struct acpi_als *als = iio_priv(indio_dev);
> > +     s32 *buffer = als->evt_buffer;
> > +     s32 val;
> > +     int ret;
> > +
> > +     mutex_lock(&als->lock);
> > +
> > +     memset(buffer, 0, ACPI_ALS_EVT_BUFFER_SIZE);
>
> This shouldn't be needed. The buffer was originally kzalloc'd
> I believe and we write all the values that might contain stale
> data.
Done
>
> > +     ret = acpi_als_read_value(als, ACPI_ALS_ILLUMINANCE, &val);
> > +     if (ret < 0)
> > +             goto out;
> > +     *buffer++ = val;
> > +
> > +     iio_push_to_buffers_with_timestamp(indio_dev, als->evt_buffer,
> > +                                        pf->timestamp);
>
> Use buffer for evt_buffer here for consistency.
Done
>
> > +out:
> > +     mutex_unlock(&als->lock);
> > +     iio_trigger_notify_done(indio_dev->trig);
> > +
> > +     return IRQ_HANDLED;
> > +}
> > +
> >  static int acpi_als_add(struct acpi_device *device)
> >  {
> >       struct acpi_als *als;
> >       struct iio_dev *indio_dev;
> > -     struct iio_buffer *buffer;
> > +     int ret;
> >
> >       indio_dev = devm_iio_device_alloc(&device->dev, sizeof(*als));
> >       if (!indio_dev)
> > @@ -180,15 +197,30 @@ static int acpi_als_add(struct acpi_device *device)
> >       indio_dev->name = ACPI_ALS_DEVICE_NAME;
> >       indio_dev->dev.parent = &device->dev;
> >       indio_dev->info = &acpi_als_info;
> > -     indio_dev->modes = INDIO_BUFFER_SOFTWARE;
> > +     indio_dev->modes = INDIO_DIRECT_MODE;
> >       indio_dev->channels = acpi_als_channels;
> >       indio_dev->num_channels = ARRAY_SIZE(acpi_als_channels);
> >
> > -     buffer = devm_iio_kfifo_allocate(&device->dev);
> > -     if (!buffer)
> > +     als->trig = devm_iio_trigger_alloc(&device->dev,
> > +                                        "%s-dev%d",
> > +                                        indio_dev->name,
> > +                                        indio_dev->id);
> > +     if (!als->trig)
> >               return -ENOMEM;
> >
> > -     iio_device_attach_buffer(indio_dev, buffer);
> > +     als->trig->dev.parent = &device->dev;
>
> Hmm. we should probably push this boilerplate into
> devm_iio_trigger_alloc() like we have done for iio devices.
Done in a separate patch. I only clean up obvious calls to set
trig->dev.parent.
Looking at the code, some triggers parents are set to the request
device in more convoluted ways or the grand parent.
>
> > +     iio_trigger_set_drvdata(als->trig, indio_dev);
> > +     ret = iio_trigger_register(als->trig);
> > +     if (ret)
> > +             return ret;
> > +
> > +     ret = devm_iio_triggered_buffer_setup(&device->dev,
> > +                                           indio_dev,
> > +                                           iio_pollfunc_store_time,
> > +                                           acpi_als_trigger_handler,
> > +                                           NULL);
> > +     if (ret)
> > +             return ret;
> >
> >       return devm_iio_device_register(&device->dev, indio_dev);
> >  }
>



[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux