From c7fb43e0d50125c36e340ecac1403d9be8660d08 Mon Sep 17 00:00:00 2001 From: Elad Alfassa Date: Sat, 21 Mar 2015 16:55:07 +0200 Subject: [PATCH] main: Add IIO buffering Light Sensor driver Such as the one on the Thinkpad Twist --- src/Makefile.am | 1 + src/drivers.h | 1 + src/drv-iio-buffer-light.c | 208 +++++++++++++++++++++++++++++++++++++ src/iio-sensor-proxy.c | 1 + 4 files changed, 211 insertions(+) create mode 100644 src/drv-iio-buffer-light.c diff --git a/src/Makefile.am b/src/Makefile.am index f2fb7cb..705493b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -11,6 +11,7 @@ iio_sensor_proxy_SOURCES = \ drv-fake-light.c \ drv-iio-poll-light.c \ drv-hwmon-light.c \ + drv-iio-buffer-light.c \ iio-buffer-utils.h \ iio-buffer-utils.c diff --git a/src/drivers.h b/src/drivers.h index 93133ff..7503419 100644 --- a/src/drivers.h +++ b/src/drivers.h @@ -63,3 +63,4 @@ extern SensorDriver input_accel; extern SensorDriver fake_light; extern SensorDriver iio_poll_light; extern SensorDriver hwmon_light; +extern SensorDriver iio_buffer_light; diff --git a/src/drv-iio-buffer-light.c b/src/drv-iio-buffer-light.c new file mode 100644 index 0000000..5f9fd90 --- /dev/null +++ b/src/drv-iio-buffer-light.c @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2014 Bastien Nocera + * Copyright (c) 2015 Elad Alfassa + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include "drivers.h" +#include "iio-buffer-utils.h" + +#include +#include +#include +#include + +typedef struct { + guint timeout_id; + ReadingsUpdateFunc callback_func; + gpointer user_data; + + GUdevDevice *dev; + const char *dev_path; + int device_id; + BufferDrvData *buffer_data; +} DrvData; + +static DrvData *drv_data = NULL; + +static int +process_scan (IIOSensorData data, DrvData *or_data) +{ + int i; + int level = 0; + gboolean present_level; + LightReadings readings; + + if (data.read_size < 0) { + g_warning ("Couldn't read from device: %s", g_strerror (errno)); + return 0; + } + + /* Rather than read everything: + * for (i = 0; i < data.read_size / or_data->scan_size; i++)... + * Just read the last one */ + i = (data.read_size / or_data->buffer_data->scan_size) - 1; + if (i < 0) { + g_debug ("Not enough data to read (read_size: %d scan_size: %d)", (int) data.read_size, or_data->buffer_data->scan_size); + return 0; + } + + process_scan_1(data.data + or_data->buffer_data->scan_size*i, or_data->buffer_data, "in_intensity_both", &level, &present_level); + + g_debug ("Read from IIO: %d", level); + readings.level = level; + readings.uses_lux = TRUE; + + //FIXME report errors + or_data->callback_func (&iio_buffer_light, (gpointer) &readings, or_data->user_data); + + return 1; +} + +static void +prepare_output (DrvData *or_data, + const char *dev_dir_name, + const char *trigger_name) +{ + IIOSensorData data; + + int fp, buf_len = 127; + + data.data = g_malloc(or_data->buffer_data->scan_size * buf_len); + + /* Attempt to open non blocking to access dev */ + fp = open (or_data->dev_path, O_RDONLY | O_NONBLOCK); + if (fp == -1) { /* If it isn't there make the node */ + g_warning ("Failed to open %s : %s", or_data->dev_path, strerror(errno)); + goto bail; + } + + /* Actually read the data */ + data.read_size = read (fp, data.data, buf_len * or_data->buffer_data->scan_size); + if (data.read_size == -1 && errno == EAGAIN) { + g_debug ("No new data available"); + } else { + process_scan(data, or_data); + } + + close(fp); + +bail: + g_free(data.data); +} + +static char * +get_trigger_name (GUdevDevice *device) +{ + GList *devices, *l; + GUdevClient *client; + gboolean has_trigger = FALSE; + char *trigger_name; + const gchar * const subsystems[] = { "iio", NULL }; + + client = g_udev_client_new (subsystems); + devices = g_udev_client_query_by_subsystem (client, "iio"); + + /* Find the associated trigger */ + trigger_name = g_strdup_printf ("als-dev%s", g_udev_device_get_number (device)); + for (l = devices; l != NULL; l = l->next) { + GUdevDevice *dev = l->data; + + if (g_strcmp0 (trigger_name, g_udev_device_get_sysfs_attr (dev, "name")) == 0) { + g_debug ("Found associated trigger at %s", g_udev_device_get_sysfs_path (dev)); + has_trigger = TRUE; + break; + } + } + + g_list_free_full (devices, g_object_unref); + g_clear_object (&client); + + if (has_trigger) + return trigger_name; + + g_warning ("Could not find trigger name associated with %s", + g_udev_device_get_sysfs_path (device)); + g_free (trigger_name); + return NULL; +} + +static gboolean +read_light (gpointer user_data) +{ + DrvData *data = user_data; + + prepare_output (data, data->buffer_data->dev_dir_name, data->buffer_data->trigger_name); + + return G_SOURCE_CONTINUE; +} + +static gboolean +iio_buffer_light_discover (GUdevDevice *device) +{ + if (g_strcmp0 (g_udev_device_get_subsystem (device), "iio") != 0) + return FALSE; + + if (g_strcmp0 ("als", g_udev_device_get_sysfs_attr (device, "name")) != 0) + return FALSE; + + g_debug ("Found als at %s", g_udev_device_get_sysfs_path (device)); + return TRUE; +} + +static gboolean +iio_buffer_light_open (GUdevDevice *device, + ReadingsUpdateFunc callback_func, + gpointer user_data) +{ + char *trigger_name; + + drv_data = g_new0 (DrvData, 1); + + /* Get the trigger name, and build the channels from that */ + trigger_name = get_trigger_name (device); + if (!trigger_name) { + g_clear_pointer (&drv_data, g_free); + return FALSE; + } + drv_data->buffer_data = buffer_drv_data_new (device, trigger_name); + g_free (trigger_name); + + if (!drv_data->buffer_data) { + g_clear_pointer (&drv_data, g_free); + return FALSE; + } + + drv_data->dev = g_object_ref (device); + drv_data->dev_path = g_udev_device_get_device_file (device); + + drv_data->callback_func = callback_func; + drv_data->user_data = user_data; + + drv_data->timeout_id = g_timeout_add (700, read_light, drv_data); + g_source_set_name_by_id (drv_data->timeout_id, "read_light"); + + return TRUE; +} + +static void +iio_buffer_light_close (void) +{ + g_source_remove (drv_data->timeout_id); + g_clear_pointer (&drv_data->buffer_data, buffer_drv_data_free); + g_clear_object (&drv_data->dev); + g_clear_pointer (&drv_data, g_free); +} + +SensorDriver iio_buffer_light = { + .name = "IIO Buffer Light sensor", + .type = DRIVER_TYPE_LIGHT, + .specific_type = DRIVER_TYPE_LIGHT_IIO, + + .discover = iio_buffer_light_discover, + .open = iio_buffer_light_open, + .close = iio_buffer_light_close, +}; diff --git a/src/iio-sensor-proxy.c b/src/iio-sensor-proxy.c index ddc9dfa..60ddfcd 100644 --- a/src/iio-sensor-proxy.c +++ b/src/iio-sensor-proxy.c @@ -87,6 +87,7 @@ static const SensorDriver * const drivers[] = { &iio_poll_accel, &input_accel, &iio_poll_light, + &iio_buffer_light, &hwmon_light, &fake_light };