OSDN Git Service

STPK-1429 Initial version of iio-sensors-hal component
authorPatrick Porlan <patrick.porlan@intel.com>
Fri, 21 Mar 2014 16:50:01 +0000 (17:50 +0100)
committersuyyala <sridhar.uyyala@intel.com>
Mon, 28 Apr 2014 02:40:39 +0000 (19:40 -0700)
This alternative sensors HAL should eventually be usable with all
sensors exposed using the iio interface. In the current form, it
has only been tested on a European T100 computer, which uses an
Invensense 6500 sensor complex, and only the accelerometer has
been taken care of.

The major reason for writing this new HAL module is the need for
flexibility to handle cases such as several sensors being exposed
through a single iio device. On the T100 the ALS and magnetometer
expose a polling interface (no buffer, no trigger, unreadable
character device). This kind of sensor is supported too.

Issue: STPK-1429

Change-Id: Ib4ebfec4d21559c929f3bbb7b4f2da452a50bd52
Signed-off-by: Patrick Porlan <patrick.porlan@intel.com>
Android.mk [new file with mode: 0644]
common.h [new file with mode: 0644]
control.c [new file with mode: 0644]
control.h [new file with mode: 0644]
description.c [new file with mode: 0644]
description.h [new file with mode: 0644]
entry.c [new file with mode: 0644]
enumeration.c [new file with mode: 0644]
enumeration.h [new file with mode: 0644]
utils.c [new file with mode: 0644]
utils.h [new file with mode: 0644]

diff --git a/Android.mk b/Android.mk
new file mode 100644 (file)
index 0000000..3a8df41
--- /dev/null
@@ -0,0 +1,29 @@
+#
+# Copyright (C) 2014 Intel Corporation.
+#
+
+# IIO sensors HAL module implementation, compiled as hw/iio-sensors-hal.so
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+src_path := .
+src_files := $(src_path)/entry.c \
+            $(src_path)/enumeration.c \
+            $(src_path)/control.c \
+            $(src_path)/description.c \
+            $(src_path)/utils.c \
+
+LOCAL_C_INCLUDES += $(LOCAL_PATH) vendor/intel/hardware/iio-sensors-hal
+LOCAL_MODULE := iio-sensors-hal
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := -DLOG_TAG=\"Sensors\" -fvisibility=hidden
+LOCAL_LDFLAGS := -Wl,--gc-sections
+LOCAL_SHARED_LIBRARIES := liblog libcutils libdl
+LOCAL_PRELINK_MODULE := false
+LOCAL_SRC_FILES := $(src_files)
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/common.h b/common.h
new file mode 100644 (file)
index 0000000..7be8d2e
--- /dev/null
+++ b/common.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2014 Intel Corporation.
+ */
+
+#ifndef __COMMON_H__
+#define __COMMON_H__
+
+#define MAX_DEVICES    8       /* Check iio devices 0 to MAX_DEVICES-1 */
+#define MAX_SENSORS    10      /* We can handle as many sensors */
+#define MAX_CHANNELS   4       /* We can handle as many channels per sensor */
+
+#define DEV_FILE_PATH  "/dev/iio:device%d"
+
+#define BASE_PATH      "/sys/bus/iio/devices/iio:device%d/"
+
+#define CHANNEL_PATH           BASE_PATH "scan_elements/"
+#define ENABLE_PATH            BASE_PATH "buffer/enable"
+#define NAME_PATH              BASE_PATH "name"
+#define TRIGGER_PATH           BASE_PATH "trigger/current_trigger"
+#define COMMON_OFFSET_PATH     BASE_PATH "in_%s_offset"
+#define COMMON_SCALE_PATH      BASE_PATH "in_%s_scale"
+
+#define MAX_TYPE_SPEC_LEN 32   /* Channel type spec len; ex: "le:u10/16>>0" */
+#define MAX_SENSOR_REPORT_SIZE 32      /* Sensor report buffer size */
+
+#define MAX_NAME_SIZE          32
+
+#define ARRAY_SIZE(x) sizeof(x)/sizeof(x[0])
+
+struct channel_descriptor_t
+{
+       /* sysfs entries located under scan_elements */
+       const char *en_path;    /* Enabled sysfs file name ; ex: "in_temp_en" */
+       const char *type_path;  /* _type sysfs file name  */
+       const char *index_path; /* _index sysfs file name */
+
+       /* sysfs entries located in /sys/bus/iio/devices/iio:deviceX */
+       const char *raw_path;   /* _raw sysfs file name  */
+       const char *input_path; /* _input sysfs file name */
+};
+
+struct sensor_catalog_entry_t
+{
+       const char *tag; /* Prefix such as "accel", "gyro", "temp"... */
+       const int type;  /* Sensor type ; ex: SENSOR_TYPE_ACCELEROMETER */
+       const int num_channels; /* Expected iio channels for this sensor */
+       struct channel_descriptor_t channel[MAX_CHANNELS];
+};
+
+struct datum_info_t
+{
+       char sign;
+       char endianness;
+       short realbits;
+       short storagebits;
+       short shift;
+};
+
+struct channel_info_t
+{
+       int offset; /* Offset in bytes within the iio character device report */
+       int size;                       /* Field size in bytes */
+       char type_spec[MAX_TYPE_SPEC_LEN]; /* From driver; ex: le:u10/16>>0 */
+       struct datum_info_t type_info;     /* Decoded contents of type spec */
+};
+
+struct sensor_info_t
+{
+       char friendly_name[MAX_NAME_SIZE];      /* ex: Accelerometer */
+       char internal_name[MAX_NAME_SIZE];      /* ex: accel_3d */
+       char vendor_name[MAX_NAME_SIZE];        /* ex: Intel */
+
+       float max_range;
+       float resolution;
+       float power;
+
+       float offset;   /* (cooked = raw + offset) * scale */
+       float scale;
+
+       int dev_num;    /* Associated iio dev num, ex: 3 for /dev/iio:device3 */
+       int enable_count;
+
+       int catalog_index;/* Associated entry within the sensor_catalog array */
+
+       int num_channels; /* Actual channel count ; 0 for poll mode sensors */
+
+       /*
+        * The array below indicates where to gather report data for this
+        * sensor inside the reports that we read from the iio character device.
+        * It is updated whenever channels are enabled or disabled on the same
+        * device. Channel size indicates the size in bytes of fields, and
+        * should be zero for disabled channels. The type field indicates how a
+        * specific channel data item is structured.
+        */
+       struct channel_info_t channel[MAX_CHANNELS];
+
+       /*
+        * This flag is set if we acquired data from the sensor but did not
+        * forward it to upper layers (i.e. Android) yet. If so, report_buffer
+        * contains that data.
+        */
+       int report_pending;
+
+       unsigned char report_buffer[MAX_SENSOR_REPORT_SIZE];
+
+       /* Note: we may have to explicitely serialize access to some fields */
+};
+
+/* Reference a few commonly used variables... */
+extern int                             sensor_count;
+extern struct sensor_info_t            sensor_info[MAX_SENSORS];
+extern struct sensor_catalog_entry_t   sensor_catalog[];
+extern int                             sensors_per_device[MAX_DEVICES];
+
+#endif
diff --git a/control.c b/control.c
new file mode 100644 (file)
index 0000000..2fe4751
--- /dev/null
+++ b/control.c
@@ -0,0 +1,661 @@
+/*
+ * Copyright (C) 2014 Intel Corporation.
+ */
+
+#include <fcntl.h>
+#include <sys/epoll.h>
+#include <math.h>
+#include <utils/Log.h>
+#include <hardware/sensors.h>
+#include "control.h"
+#include "enumeration.h"
+#include "utils.h"
+
+/* Currently active sensors count, per device */
+static int poll_sensors_per_dev[MAX_DEVICES];  /* poll-mode sensors */
+static int trig_sensors_per_dev[MAX_DEVICES];  /* trigger, event based */
+
+static int device_fd[MAX_DEVICES];   /* fd on the /dev/iio:deviceX file */
+
+static int poll_fd; /* epoll instance covering all enabled sensors */
+
+/* Timestamp for the moment when we last exited a poll operation */
+static int64_t last_poll_exit_ts;
+
+/* Cap the time between poll operations to this, to counter runaway polls */
+#define POLL_MIN_INTERVAL 10000 /* uS */
+
+static int active_poll_sensors; /* Number of enabled poll-mode sensors */
+
+
+static int enable_buffer(int dev_num, int enabled)
+{
+       char sysfs_path[PATH_MAX];
+
+       sprintf(sysfs_path, ENABLE_PATH, dev_num);
+
+       /* Low level, non-multiplexed, enable/disable routine */
+       return sysfs_write_int(sysfs_path, enabled);
+}
+
+
+static int setup_trigger(int dev_num, const char* trigger_val)
+{
+       char sysfs_path[PATH_MAX];
+
+       sprintf(sysfs_path, TRIGGER_PATH, dev_num);
+
+       return sysfs_write_str(sysfs_path, trigger_val);
+}
+
+
+static void refresh_sensor_report_maps(int dev_num)
+{
+       /*
+        * Read sysfs files from a iio device's scan_element directory, and
+        * build a couple of tables from that data. These tables will tell, for
+        * each sensor, where to gather relevant data in a device report, i.e.
+        * the structure that we read from the /dev/iio:deviceX file in order to
+        * sensor report, itself being the data that we return to Android when a
+        * sensor poll completes. The mapping should be straightforward in the
+        * case where we have a single sensor active per iio device but, this is
+        * not the general case. In general several sensors can be handled
+        * through a single iio device, and the _en, _index and _type syfs
+        * entries all concur to paint a picture of what the structure of the
+        * device report is.
+        */
+
+       int s;
+       int c;
+       int n;
+       int i;
+       int ch_enabled;
+       int ch_index;
+       char* ch_spec;
+       char spec_buf[MAX_TYPE_SPEC_LEN];
+       struct datum_info_t* ch_info;
+       int size;
+       char sysfs_path[PATH_MAX];
+       int active_channels;
+       int offset;
+       int channel_count;
+       int channel_size_from_index[MAX_SENSORS * MAX_CHANNELS] = { 0 };
+       int sensor_handle_from_index[MAX_SENSORS * MAX_CHANNELS] = { 0 };
+       int channel_number_from_index[MAX_SENSORS * MAX_CHANNELS] = { 0 };
+
+       active_channels = 0;
+
+       /* For each sensor that is linked to this device */
+       for (s=0; s<sensor_count; s++) {
+               if (sensor_info[s].dev_num != dev_num)
+                       continue;
+
+               i = sensor_info[s].catalog_index;
+
+               /* Read channel status through syfs attributes */
+               for (c=0; c<sensor_info[s].num_channels; c++) {
+
+                       /* Read _en file */
+                       sprintf(sysfs_path, CHANNEL_PATH "%s",
+                               sensor_info[s].dev_num,
+                               sensor_catalog[i].channel[c].en_path);
+
+                       n = sysfs_read_int(sysfs_path, &ch_enabled);
+
+                       if (n == -1) {
+                               ALOGW(  "Failed to read _en flag: %s\n",
+                               sysfs_path);
+                               continue;
+                       }
+
+                       if (!ch_enabled != 1) {
+                               sensor_info[s].channel[c].size = 0;
+                       }
+
+                       /* Read _type file */
+                       sprintf(sysfs_path, CHANNEL_PATH "%s",
+                               sensor_info[s].dev_num,
+                               sensor_catalog[i].channel[c].type_path);
+
+                       n = sysfs_read_str(sysfs_path, spec_buf, 
+                                               sizeof(spec_buf));
+
+                       if (n == -1) {
+                                       ALOGW(  "Failed to read type: %s\n",
+                                       sysfs_path);
+                                       continue;
+                               }
+
+                       ch_spec = sensor_info[s].channel[c].type_spec;
+
+                       memcpy(ch_spec, spec_buf, sizeof(spec_buf));
+
+                       ch_info = &sensor_info[s].channel[c].type_info;
+
+                       size = decode_type_spec(ch_spec, ch_info);
+
+                       /* Read _index file */
+                       sprintf(sysfs_path, CHANNEL_PATH "%s",
+                               sensor_info[s].dev_num,
+                               sensor_catalog[i].channel[c].index_path);
+
+                       n = sysfs_read_int(sysfs_path, &ch_index);
+
+                       if (n == -1) {
+                                       ALOGW(  "Failed to read index: %s\n",
+                                               sysfs_path);
+                                       continue;
+                               }
+
+                       /* Record what this index is about */
+
+                       sensor_handle_from_index [ch_index] = s;
+                       channel_number_from_index[ch_index] = c;
+                       channel_size_from_index  [ch_index] = size;
+
+                       active_channels++;
+               }
+       }
+
+       ALOGI("Found %d enabled channels for iio device %d\n", active_channels,
+               dev_num);
+
+       /*
+        * Now that we know which channels are enabled, their sizes and their
+        * ordering, update channels offsets within device report. Note: there
+        * is a possibility that several sensors share the same index, with
+        * their data fields being isolated by masking and shifting as specified
+        * through the real bits and shift values in type attributes. This case
+        * is not currently supported. Also, the code below assumes no hole in
+        * the sequence of indices, so it is dependent on discovery of all
+        * sensors.
+        */
+        offset = 0;
+        for (i=0; i<MAX_SENSORS * MAX_CHANNELS; i++) {
+               s =     sensor_handle_from_index[i];
+               c =     channel_number_from_index[i];
+               size =  channel_size_from_index[i];
+
+               if (!size)
+                       continue;
+
+               ALOGI("S%d C%d : offset %d, size %d, type %s\n",
+                     s, c, offset, size, sensor_info[s].channel[c].type_spec);
+
+               sensor_info[s].channel[c].offset        = offset;
+               sensor_info[s].channel[c].size          = size;
+
+               offset += size;
+        }
+}
+
+
+int adjust_counters (int s, int enabled)
+{
+       /*
+        * Adjust counters based on sensor enable action. Return values are:
+        * -1 if there's an inconsistency: abort action in this case
+        *  0 if the operation was completed and we're all set
+        *  1 if we toggled the state of the sensor and there's work left
+        */
+
+       int dev_num = sensor_info[s].dev_num;
+
+       /* Refcount per sensor, in terms of enable count */
+       if (enabled) {
+               ALOGI("Enabling sensor %d (iio device %d: %s)\n",
+                       s, dev_num, sensor_info[s].internal_name);
+
+               sensor_info[s].enable_count++;
+
+               if (sensor_info[s].enable_count != 1)
+                       return 0; /* The sensor was, and remains, in use */
+       } else {
+               if (sensor_info[s].enable_count == 0)
+                       return -1; /* Spurious disable call */
+
+               ALOGI("Disabling sensor %d (iio device %d)\n", s, dev_num);
+
+               sensor_info[s].enable_count--;
+
+               if (sensor_info[s].enable_count > 0)
+                       return 0; /* The sensor was, and remains, in use */
+
+               /* Sensor disabled, clear up pending data */
+
+               sensor_info[s].report_pending = 0;
+               memset(sensor_info[s].report_buffer, 0, MAX_SENSOR_REPORT_SIZE);
+       }
+
+       /* We changed the state of a sensor - adjust per iio device counters */
+
+       /* If this is a regular event-driven sensor */
+       if (sensor_info[s].num_channels) {
+
+                       if (enabled)
+                               trig_sensors_per_dev[dev_num]++;
+                       else
+                               trig_sensors_per_dev[dev_num]--;
+
+                       return 1;
+               }
+
+       if (enabled) {
+               active_poll_sensors++;
+               poll_sensors_per_dev[dev_num]++;
+               return 1;
+       }
+
+       active_poll_sensors--;
+       poll_sensors_per_dev[dev_num]--;
+       return 1;
+}
+
+
+int sensor_activate(int s, int enabled)
+{
+       char sysfs_path[PATH_MAX];
+       char device_name[PATH_MAX];
+       char trigger_name[MAX_NAME_SIZE + 16];
+       int c;
+       struct epoll_event ev = {0};
+       int dev_fd;
+       int ret;
+       int dev_num = sensor_info[s].dev_num;
+       int i = sensor_info[s].catalog_index;
+       int is_poll_sensor = !sensor_info[s].num_channels;
+
+       ret = adjust_counters(s, enabled);
+
+       /* If the operation was neutral in terms of state, we're done */
+       if (ret <= 0)
+               return ret;
+
+       if (!is_poll_sensor) {
+               /* Changes have to be made while the buffer is turned off */
+               enable_buffer(dev_num, 0);
+
+               /* Configure trigger */
+               switch (trig_sensors_per_dev[dev_num]) {
+                       case 0:
+                               setup_trigger(dev_num, "none");
+                               break;
+
+                       case 1:
+                               sprintf(trigger_name, "%s-dev%d",
+                                       sensor_info[s].internal_name, dev_num);
+
+                               setup_trigger(dev_num, trigger_name);
+                               break;
+
+                       default:
+                               /* The trigger is already set */
+                               break;
+               }
+
+               /*
+                * Turn channels associated to this sensor on or off, and update
+                * the channels maps for all sensors associated to this device.
+                */
+               for (c=0;c<sensor_info[s].num_channels; c++) {
+                       sprintf(sysfs_path, CHANNEL_PATH "%s",
+                               sensor_info[s].dev_num,
+                               sensor_catalog[i].channel[c].en_path);
+
+                       sysfs_write_int(sysfs_path, enabled);
+               }
+
+               /* If there's at least one sensor left */
+               if (trig_sensors_per_dev[dev_num]) {
+                       refresh_sensor_report_maps(dev_num);
+                       enable_buffer(dev_num, 1);
+               }
+       }
+
+       /*
+        * Make sure we have a fd on the character device ; conversely, close
+        * the fd if no one is using associated sensor anymore. The assumption
+        * here is that the underlying driver will power on the relevant
+        * hardware block while someone hold a fd on the device.
+        */
+       dev_fd = device_fd[dev_num];
+
+       switch (poll_sensors_per_dev[dev_num] + trig_sensors_per_dev[dev_num]) {
+
+               case 0:
+                       if (dev_fd != -1) {
+                               /*
+                                * Stop watching this fd. This should be a no-op
+                                * in case this fd was not in the poll set.
+                                */
+                               epoll_ctl(poll_fd, EPOLL_CTL_DEL, dev_fd, NULL);
+
+                               close(dev_fd);
+                               device_fd[dev_num] = -1;
+                       }
+                       return 0;
+
+               case 1:
+                       /* First enabled sensor on this iio device */
+                       sprintf(device_name, DEV_FILE_PATH, dev_num);
+                       dev_fd = open(device_name, O_RDONLY | O_NONBLOCK);
+
+                       if (dev_fd == -1) {
+                               ALOGE("Could not open fd on %s (%s)\n",
+                                     device_name, strerror(errno));
+                               return -1;
+                       }
+
+                       ALOGV("Opened %s: fd=%d\n", device_name, dev_fd);
+
+                       device_fd[dev_num] = dev_fd;
+                       break;
+
+               default:
+                       break;
+       }
+
+       if (!is_poll_sensor && trig_sensors_per_dev[dev_num] == 1) {
+
+               /* Add this iio device fd to the set of watched fds */
+               ev.events = EPOLLIN;
+               ev.data.u32 = dev_num;
+
+               ret = epoll_ctl(poll_fd, EPOLL_CTL_ADD, dev_fd, &ev);
+
+               if (ret == -1) {
+                       ALOGE("Failed adding %d to poll set (%s)\n", dev_fd,
+                             strerror(errno));
+                       return -1;
+               }
+
+               /* Note: poll-mode fds are not readable */
+       }
+
+       return 0;
+}
+
+
+static int integrate_device_report(int dev_num)
+{
+       int len;
+       int s,c;
+       unsigned char buf[MAX_SENSOR_REPORT_SIZE * MAX_SENSORS] = { 0 };
+       int dr_offset;
+       int sr_offset;
+       unsigned char *target;
+       unsigned char *source;
+       int size;
+       int expected_size = 0;
+
+       if (dev_num < 0 || dev_num >= MAX_DEVICES)
+               return -1;
+
+       /* There's an incoming report on the specified fd */
+
+       for (s=0; s<MAX_SENSORS; s++)
+               if (sensor_info[s].enable_count &&
+                   sensor_info[s].dev_num == dev_num)
+                       for (c=0; c<sensor_info[s].num_channels; c++)
+                               expected_size += sensor_info[s].channel[c].size;
+
+       if (expected_size == 0)
+               return 0;
+
+       len = read(device_fd[dev_num], buf, expected_size);
+
+       if (len == -1) {
+               ALOGE("Could not read report from iio device %d (%s)\n",
+                     dev_num, strerror(errno));
+               return -1;
+       }
+
+       ALOGV("Read %d bytes from iio device %d\n", len, dev_num);
+
+       dr_offset = 0;
+
+       for (s=0; s<MAX_SENSORS; s++)
+               if (sensor_info[s].enable_count &&
+                   sensor_info[s].dev_num == dev_num) {
+                       sr_offset = 0;
+
+                       /* Copy data from device to sensor report buffer */
+                       for (c=0; c<sensor_info[s].num_channels; c++) {
+
+                               target = sensor_info[s].report_buffer +
+                                       sr_offset;
+
+                               source = buf + sensor_info[s].channel[c].offset;
+
+                               size = sensor_info[s].channel[c].size;
+
+                               memcpy(target, source, size);
+
+                               sr_offset += size;
+                       }
+
+                       ALOGV("Sensor %d report available (%d bytes)\n",
+                               s, sr_offset);
+
+                       sensor_info[s].report_pending = 1;
+               }
+
+       return 0;
+}
+
+
+static float acquire_immediate_value(int s, int c)
+{
+       char sysfs_path[PATH_MAX];
+       float val;
+       int ret;
+       int dev_num = sensor_info[s].dev_num;
+       int i = sensor_info[s].catalog_index;
+       const char* raw_path = sensor_catalog[i].channel[c].raw_path;
+       const char* input_path = sensor_catalog[i].channel[c].input_path;
+       float scale = sensor_info[s].scale;
+       float offset = sensor_info[s].offset;
+
+       /* Acquire a sample value for sensor s / channel c through sysfs */
+
+       if (input_path[0]) {
+               sprintf(sysfs_path, BASE_PATH "%s", dev_num, input_path);
+               ret = sysfs_read_float(sysfs_path, &val);
+
+               if (!ret) {
+                       return val;
+               }
+       };
+
+       if (!raw_path[0])
+               return 0;
+
+       sprintf(sysfs_path, BASE_PATH "%s", dev_num, raw_path);
+       ret = sysfs_read_float(sysfs_path, &val);
+
+       if (ret == -1)
+               return 0;
+
+       return val;
+}
+
+
+static void propagate_sensor_report(int s, struct sensors_event_t* data)
+{
+       /* There's a sensor report pending for this sensor ; transmit it */
+
+       int catalog_index = sensor_info[s].catalog_index;
+       int sensor_type = sensor_catalog[catalog_index].type;
+       int num_fields;
+       int c;
+       unsigned char* current_sample;
+       int sample_size;
+       struct datum_info_t* sample_type;
+       int64_t s64;
+       float val;
+
+       memset(data, 0, sizeof(sensors_event_t));
+
+       data->version = sizeof(sensors_event_t);
+       data->sensor = s;
+       data->type = sensor_type;
+       data->timestamp = get_timestamp();
+
+       switch (sensor_type) {
+               case SENSOR_TYPE_ACCELEROMETER:         /* m/s^2        */
+               case SENSOR_TYPE_MAGNETIC_FIELD:        /* micro-tesla  */
+               case SENSOR_TYPE_ORIENTATION:           /* degrees      */
+               case SENSOR_TYPE_GYROSCOPE:             /* radians/s    */
+                       num_fields = 3;
+                       break;
+
+               case SENSOR_TYPE_LIGHT:                 /* SI lux units */
+               case SENSOR_TYPE_AMBIENT_TEMPERATURE:   /* Â°C          */
+               case SENSOR_TYPE_TEMPERATURE:           /* Â°C          */
+               case SENSOR_TYPE_PROXIMITY:             /* centimeters  */
+               case SENSOR_TYPE_PRESSURE:              /* hecto-pascal */
+               case SENSOR_TYPE_RELATIVE_HUMIDITY:     /* percent */
+                       num_fields = 1;
+                       break;
+
+               case SENSOR_TYPE_ROTATION_VECTOR:
+                       num_fields = 4;
+                       break;
+
+               case SENSOR_TYPE_DEVICE_PRIVATE_BASE:   /* hidden for now */
+                       num_fields = 0;
+                       break;
+
+               default:
+                       ALOGE("Unknown sensor type!\n");
+                       num_fields = 0;
+                       break;
+       }
+
+       ALOGV("Sample on sensor %d (type %d):\n", s, sensor_type);
+
+       /* If we're dealing with a poll-mode sensor */
+       if (!sensor_info[s].num_channels) {
+
+               /* Read values through sysfs rather than from a report buffer */
+               for (c=0; c<num_fields; c++) {
+                       val = acquire_immediate_value(s, c);
+
+                       data->data[c] = transform_sample(sensor_type, c, val);
+
+                       ALOGV("\tfield %d: %f\n", c, data->data[c]);
+               }
+               return;
+       }
+
+       /* Convert the data into the expected Android-level format */
+
+       current_sample = sensor_info[s].report_buffer;
+
+       for (c=0; c<num_fields; c++) {
+               sample_size     =  sensor_info[s].channel[c].size;
+               sample_type     = &sensor_info[s].channel[c].type_info;
+
+               s64 = sample_as_int64(current_sample, sample_type);
+
+               val = (sensor_info[s].offset + s64) * sensor_info[s].scale;
+
+               data->data[c] = transform_sample(sensor_type, c, val);
+
+               ALOGV("\tfield %d: %f\n", c, data->data[c]);
+               current_sample += sample_size;
+       }
+}
+
+
+static int get_poll_time (void)
+{
+       if (!active_poll_sensors)
+               return -1;      /* Infinite wait */
+
+       return 100;     /* ms ... this needs to be dynamic */
+}
+
+
+int sensor_poll(struct sensors_event_t* data, int count)
+{
+       int s;
+       int i;
+       int nfds;
+       int delta;
+       struct epoll_event ev[MAX_DEVICES];
+
+       /* Get one or more events from our collection of sensors */
+
+return_first_available_sensor_report:
+
+       /* If there's at least one available report */
+       for (s=0; s<sensor_count; s++)
+               if (sensor_info[s].report_pending) {
+
+                       /* Return that up */
+                       propagate_sensor_report(s, data);
+                       sensor_info[s].report_pending = 0;
+                       ALOGV("Report on sensor %d\n", s);
+                       return 1;
+               }
+
+       /* Keep a minimum time interval between poll operations */
+       delta = (get_timestamp() - last_poll_exit_ts)/1000;
+
+       if (delta > 0 && delta < POLL_MIN_INTERVAL)
+               usleep(POLL_MIN_INTERVAL - delta);
+
+       ALOGV("Awaiting sensor data\n");
+
+       nfds = epoll_wait(poll_fd, ev, MAX_DEVICES, get_poll_time());
+
+       last_poll_exit_ts = get_timestamp();
+
+       ALOGV("%d fds signalled\n", nfds);
+
+       /* For each of the devices for which a report is available */
+       for (i=0; i<nfds; i++)
+               if (ev[i].events == EPOLLIN)
+                       /* Read report */
+                       integrate_device_report(ev[i].data.u32);
+
+       /* It's a good time to invalidate poll-mode sensor values */
+       for (s=0; s<sensor_count; s++)
+               if (!sensor_info[s].num_channels && sensor_info[s].enable_count)
+                       sensor_info[s].report_pending = 1;
+
+       goto return_first_available_sensor_report;
+}
+
+
+int sensor_set_delay(int handle, int64_t ns)
+{
+       /* Set the rate at which a specific sensor should report events */
+       /* Continuous reports: accelerometer, gyroscope */
+       /* On change with minimum delay between events: ALS, proximity */
+       /* See sensors.h for indication on sensor trigger modes */
+       return -1;
+}
+
+
+int allocate_control_data (void)
+{
+       int i;
+
+       for (i=0; i<MAX_DEVICES; i++)
+               device_fd[i] = -1;
+
+       poll_fd = epoll_create(MAX_DEVICES);
+
+       if (poll_fd == -1) {
+               ALOGE("Can't create epoll instance for iio sensors!\n");
+       }
+
+       return poll_fd;
+}
+
+
+void delete_control_data (void)
+{
+}
diff --git a/control.h b/control.h
new file mode 100644 (file)
index 0000000..b69dd49
--- /dev/null
+++ b/control.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) 2014 Intel Corporation.
+ */
+
+#ifndef __CONTROL_H__
+#define __CONTROL_H__
+
+int    sensor_activate         (int handle, int enabled);
+int    sensor_set_delay        (int handle, int64_t ns);
+int    sensor_poll             (sensors_event_t* data, int count);
+
+int    allocate_control_data   (void);
+void   delete_control_data     (void);
+
+#endif
diff --git a/description.c b/description.c
new file mode 100644 (file)
index 0000000..183c11a
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2014 Intel Corporation.
+ */
+
+#include <utils/Log.h>
+#include <hardware/sensors.h>
+#include "enumeration.h"
+
+/*
+ * This information should be provided on a sensor basis through a configuration
+ * file, or we should build a catalog of known sensors.
+ */
+
+#define IIO_SENSOR_HAL_VERSION 1
+
+
+char* sensor_get_name (int s)
+{
+       if (sensor_info[s].friendly_name[0] == '\0') {
+
+               /* If we got a iio device name from sysfs, use it */
+               if (sensor_info[s].internal_name[0])
+                       sprintf(sensor_info[s].friendly_name, "S%d-%s", s,
+                               sensor_info[s].internal_name);
+               else
+                       sprintf(sensor_info[s].friendly_name, "S%d", s);
+       }
+
+       return sensor_info[s].friendly_name;
+}
+
+
+char* sensor_get_vendor (int s)
+{
+       if (sensor_info[s].vendor_name[0])
+               return sensor_info[s].vendor_name;
+
+       return "<unknown>";
+}
+
+
+int sensor_get_version (int handle)
+{
+       return IIO_SENSOR_HAL_VERSION;
+}
+
+
+float sensor_get_max_range (int s)
+{
+       int catalog_index;
+       int sensor_type;
+
+       if (sensor_info[s].max_range != 0.0)
+               return sensor_info[s].max_range;
+
+       /* Try returning a sensible value given the sensor type */
+
+       /* We should cap returned samples accordingly... */
+
+       catalog_index = sensor_info[s].catalog_index;
+       sensor_type = sensor_catalog[catalog_index].type;
+
+       switch (sensor_type) {
+               case SENSOR_TYPE_ACCELEROMETER:         /* m/s^2        */
+                       return 50;
+
+               case SENSOR_TYPE_MAGNETIC_FIELD:        /* micro-tesla  */
+                       return 500;
+
+               case SENSOR_TYPE_ORIENTATION:           /* degrees      */
+                       return 360;
+
+               case SENSOR_TYPE_GYROSCOPE:             /* radians/s    */
+                       return 10;
+
+               case SENSOR_TYPE_LIGHT:                 /* SI lux units */
+                       return 50000;
+
+               case SENSOR_TYPE_AMBIENT_TEMPERATURE:   /* Â°C          */
+               case SENSOR_TYPE_TEMPERATURE:           /* Â°C          */
+               case SENSOR_TYPE_PROXIMITY:             /* centimeters  */
+               case SENSOR_TYPE_PRESSURE:              /* hecto-pascal */
+               case SENSOR_TYPE_RELATIVE_HUMIDITY:     /* percent */
+                       return 100;
+
+               default:
+                       return 0.0;
+               }
+}
+
+
+float sensor_get_resolution (int s)
+{
+       return sensor_info[s].resolution;
+}
+
+
+float sensor_get_power (int s)
+{
+       /* mA used while sensor is in use ; not sure about volts :) */
+       return sensor_info[s].power;
+}
diff --git a/description.h b/description.h
new file mode 100644 (file)
index 0000000..418b0de
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2014 Intel Corporation.
+ */
+
+#ifndef __DESCRIPTION_H__
+#define __DESCRIPTION_H__
+
+#include "common.h"
+
+char*  sensor_get_name         (int handle);
+char*  sensor_get_vendor       (int handle);
+int    sensor_get_version      (int handle);
+float  sensor_get_max_range    (int handle);
+float  sensor_get_resolution   (int handle);
+float  sensor_get_power        (int handle);
+
+#endif
diff --git a/entry.c b/entry.c
new file mode 100644 (file)
index 0000000..0a107b6
--- /dev/null
+++ b/entry.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2014 Intel Corporation.
+ */
+
+#include <hardware/sensors.h>
+#include <utils/Log.h>
+#include "enumeration.h"
+#include "control.h"
+
+/* This is the IIO Sensors HAL module entry points file */
+
+static int init_count;
+
+static int activate(struct sensors_poll_device_t* dev, int handle, int enabled)
+{
+       if (init_count == 0 || handle < 0 || handle >= sensor_count)
+               return -EINVAL;
+
+       return sensor_activate(handle, enabled);
+}
+
+
+static int set_delay(struct sensors_poll_device_t* dev, int handle, int64_t ns)
+{
+       if (init_count == 0 || handle < 0 || handle >= sensor_count)
+               return -EINVAL;
+
+       return sensor_set_delay(handle, ns);
+}
+
+
+static int poll(struct sensors_poll_device_t* dev, sensors_event_t* data,
+               int count)
+{
+       if (init_count == 0 || !data || count < 1)
+               return -EINVAL;
+
+       return sensor_poll(data, count);
+}
+
+
+static int close_module(hw_device_t *device)
+{
+       if (init_count == 0)
+               return -EINVAL;
+
+       init_count--;
+
+       if (init_count == 0) {
+               ALOGI("Closing IIO sensors HAL module\n");
+               delete_enumeration_data();
+               delete_control_data();
+       }
+
+       return 0;
+}
+
+
+static int initialize_module(const struct hw_module_t *module, const char *id,
+                               struct hw_device_t** device)
+{
+       static struct sensors_poll_device_t poll_device;
+
+       if (strcmp(id, SENSORS_HARDWARE_POLL))
+                return -EINVAL;
+
+       poll_device.common.tag          = HARDWARE_DEVICE_TAG;
+       poll_device.common.version      = 0;
+       poll_device.common.module       = (struct hw_module_t*) module;
+       poll_device.common.close        = close_module;
+
+       poll_device.activate            = activate;
+       poll_device.setDelay            = set_delay;
+       poll_device.poll                = poll;
+
+       *device = &poll_device.common;
+
+        if (init_count == 0) {
+               ALOGI("Initializing IIO sensors HAL module\n");
+               allocate_control_data();
+               enumerate_sensors();
+       }
+
+       init_count++;
+       return 0;
+}
+
+
+static struct hw_module_methods_t module_methods = {
+       .open = initialize_module
+};
+
+
+/* Externally visible module descriptor */
+struct sensors_module_t __attribute__ ((visibility ("default")))
+       HAL_MODULE_INFO_SYM = {
+               .common = {
+                       .tag = HARDWARE_MODULE_TAG,
+                       .version_major = 1,
+                       .version_minor = 0,
+                       .id = SENSORS_HARDWARE_MODULE_ID,
+                       .name = "IIO sensors HAL",
+                       .author = "Intel",
+                       .methods = &module_methods,
+               },
+               .get_sensors_list = get_sensors_list
+};
+
diff --git a/enumeration.c b/enumeration.c
new file mode 100644 (file)
index 0000000..271dba3
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2014 Intel Corporation.
+ */
+
+#include <dirent.h>
+#include <utils/Log.h>
+#include <hardware/sensors.h>
+#include "enumeration.h"
+#include "description.h"
+#include "utils.h"
+
+/*
+ * This table maps syfs entries in scan_elements directories to sensor types,
+ * and will also be used to determine other sysfs names as well as the iio
+ * device number associated to a specific sensor.
+ */
+
+struct sensor_catalog_entry_t sensor_catalog[] = {
+       DECLARE_SENSOR3("accel",      SENSOR_TYPE_ACCELEROMETER,  "x", "y", "z")
+       DECLARE_SENSOR3("anglvel",    SENSOR_TYPE_GYROSCOPE,      "x", "y", "z")
+       DECLARE_SENSOR3("magn",       SENSOR_TYPE_MAGNETIC_FIELD, "x", "y", "z")
+       DECLARE_SENSOR0("illuminance",SENSOR_TYPE_LIGHT                        )
+       DECLARE_SENSOR3("incli",      SENSOR_TYPE_ORIENTATION,    "x", "y", "z")
+       DECLARE_SENSOR4("rot",        SENSOR_TYPE_ROTATION_VECTOR,
+                                        "quat_x", "quat_y", "quat_z", "quat_w")
+       DECLARE_SENSOR0("temp",       SENSOR_TYPE_TEMPERATURE                  )
+       DECLARE_SENSOR0("timestamp",  SENSOR_TYPE_DEVICE_PRIVATE_BASE          )
+};
+
+#define CATALOG_SIZE   ARRAY_SIZE(sensor_catalog)
+
+
+/* We equate sensor handles to indices in these tables */
+
+struct sensor_t      sensor_desc[MAX_SENSORS]; /* Android-level descriptors */
+struct sensor_info_t sensor_info[MAX_SENSORS]; /* Internal descriptors      */
+
+int sensors_per_device[MAX_DEVICES];   /* Sensors can share a iio device  */
+int sensor_count;                      /* Detected sensors */
+
+
+static void add_sensor (int dev_num, int catalog_index, int use_polling)
+{
+       int s;
+       int sensor_type;
+       char sysfs_path[PATH_MAX];
+       const char* prefix;
+        float scale;
+
+       if (sensor_count == MAX_SENSORS) {
+               ALOGE("Too many sensors!\n");
+               return;
+       }
+
+       sensor_type = sensor_catalog[catalog_index].type;
+
+       /*
+        * At this point we could check that the expected sysfs attributes are
+        * present ; that would enable having multiple catalog entries with the
+        * same sensor type, accomodating different sets of sysfs attributes.
+        */
+
+       s = sensor_count;
+
+       sensor_info[s].dev_num          = dev_num;
+       sensor_info[s].catalog_index    = catalog_index;
+
+        if (use_polling)
+                sensor_info[s].num_channels = 0;
+        else
+                sensor_info[s].num_channels =
+                                sensor_catalog[catalog_index].num_channels;
+
+       prefix = sensor_catalog[catalog_index].tag;
+
+       /* Read name attribute, if available */
+       sprintf(sysfs_path, NAME_PATH, dev_num);
+       sysfs_read_str(sysfs_path, sensor_info[s].internal_name, MAX_NAME_SIZE);
+
+       /* See if we have general offsets and scale values for this sensor */
+
+       sprintf(sysfs_path, COMMON_OFFSET_PATH, dev_num, prefix);
+       sysfs_read_float(sysfs_path, &sensor_info[s].offset);
+
+       sprintf(sysfs_path, COMMON_SCALE_PATH, dev_num, prefix);
+       if (!sysfs_read_float(sysfs_path, &scale))
+                sensor_info[s].scale = scale;
+
+       /* Initialize Android-visible descriptor */
+       sensor_desc[s].name             = sensor_get_name(s);
+       sensor_desc[s].vendor           = sensor_get_vendor(s);
+       sensor_desc[s].version          = sensor_get_version(s);
+       sensor_desc[s].handle           = s;
+       sensor_desc[s].type             = sensor_type;
+       sensor_desc[s].maxRange         = sensor_get_max_range(s);
+       sensor_desc[s].resolution       = sensor_get_resolution(s);
+       sensor_desc[s].power            = sensor_get_power(s);
+
+       if (sensor_info[s].internal_name[0] == '\0') {
+               /*
+                * In case the kernel-mode driver doesn't expose a name for
+                * the iio device, use (null)-dev%d as the trigger name...
+                * This can be considered a kernel-mode iio driver bug.
+                */
+               ALOGW("Using null trigger on sensor %d (dev %d)\n", s, dev_num);
+               strcpy(sensor_info[s].internal_name, "(null)");
+       }
+
+       sensor_count++;
+
+       sensors_per_device[dev_num]++;
+}
+
+
+static void discover_poll_sensors (int dev_num, char map[CATALOG_SIZE])
+{
+       char base_dir[PATH_MAX];
+       DIR *dir;
+       char sysfs_dir[PATH_MAX];
+       struct sensor *sensor;
+       struct dirent *d;
+       unsigned int i;
+        int c;
+
+       memset(map, 0, CATALOG_SIZE);
+
+       snprintf(base_dir, sizeof(base_dir), BASE_PATH, dev_num);
+
+       dir = opendir(base_dir);
+       if (!dir) {
+               return;
+       }
+
+       /* Enumerate entries in this iio device's base folder */
+
+       while ((d = readdir(dir))) {
+               if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+                       continue;
+
+                /* If the name matches a catalog entry, flag it */
+                for (i = 0; i<CATALOG_SIZE; i++)
+                        for (c=0; c<sensor_catalog[i].num_channels; c++)
+                                if (!strcmp(d->d_name,
+                                   sensor_catalog[i].channel[c].raw_path) ||
+                                    !strcmp(d->d_name,
+                                   sensor_catalog[i].channel[c].input_path)) {
+                               map[i] = 1;
+                                break;
+                        }
+       }
+
+       closedir(dir);
+}
+
+
+static void discover_trig_sensors (int dev_num, char map[CATALOG_SIZE])
+{
+       char scan_elem_dir[PATH_MAX];
+       DIR *dir;
+       char sysfs_dir[PATH_MAX];
+       struct sensor *sensor;
+       struct dirent *d;
+       unsigned int i;
+
+        memset(map, 0, CATALOG_SIZE);
+
+       /* Enumerate entries in this iio device's scan_elements folder */
+
+       snprintf(scan_elem_dir, sizeof(scan_elem_dir), CHANNEL_PATH, dev_num);
+
+       dir = opendir(scan_elem_dir);
+       if (!dir) {
+               return;
+       }
+
+       while ((d = readdir(dir))) {
+               if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+                       continue;
+
+               /* Compare en entry to known ones and create matching sensors */
+
+                for (i = 0; i<CATALOG_SIZE; i++)
+                       if (!strcmp(d->d_name,
+                                   sensor_catalog[i].channel[0].en_path)) {
+                               map[i] = 1;
+                                break;
+                        }
+       }
+
+       closedir(dir);
+}
+
+
+void enumerate_sensors (void)
+{
+       /*
+        * Discover supported sensors and allocate control structures for them.
+        * Multiple sensors can potentially rely on a single iio device (each
+        * using their own channels). We can't have multiple sensors of the same
+        * type on the same device. In case of detection as both a poll-mode
+        * and trigger-based sensor, use the trigger usage mode.
+        */
+       char poll_sensors[CATALOG_SIZE];
+       char trig_sensors[CATALOG_SIZE];
+       int dev_num;
+       unsigned int i;
+
+       for (dev_num=0; dev_num<MAX_DEVICES; dev_num++) {
+               discover_poll_sensors(dev_num, poll_sensors);
+               discover_trig_sensors(dev_num, trig_sensors);
+
+               for (i=0; i<CATALOG_SIZE; i++)
+                       if (trig_sensors[i])
+                               add_sensor(dev_num, i, 0);
+                       else
+                               if (poll_sensors[i])
+                                       add_sensor(dev_num, i, 1);
+       }
+
+       ALOGI("Discovered %d sensors\n", sensor_count);
+}
+
+
+void delete_enumeration_data (void)
+{
+       /* Reset sensor count */
+       sensor_count = 0;
+}
+
+
+int get_sensors_list(  struct sensors_module_t* module,
+                       struct sensor_t const** list)
+{
+       *list = sensor_desc;
+       return sensor_count;
+}
+
diff --git a/enumeration.h b/enumeration.h
new file mode 100644 (file)
index 0000000..16af2fe
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2014 Intel Corporation.
+ */
+
+#ifndef __ENUMERATION_H__
+#define __ENUMERATION_H__
+
+#include "common.h"
+
+/*
+ * Macros associating iio sysfs entries to to sensor types ; see
+ * linux/kernel/drivers/iio/industrialio-core.c and
+ * hardware/libhardware/include/hardware/sensor.h
+ */
+
+#define DECLARE_CHANNEL(tag, spacer, name)             \
+               {                                       \
+                       "in_"tag spacer name"_en",      \
+                       "in_"tag spacer name"_type",    \
+                       "in_"tag spacer name"_index",   \
+                       "in_"tag spacer name"_raw",     \
+                       "in_"tag spacer name"_input",   \
+                },
+
+#define DECLARE_NAMED_CHANNEL(tag, name)       DECLARE_CHANNEL(tag, "_", name)
+
+#define DECLARE_GENERIC_CHANNEL(tag)           DECLARE_CHANNEL(tag, "", "")
+
+
+#define DECLARE_SENSOR0(tag, type)                     \
+{                                                      \
+       tag, type, 1,                                   \
+       {                                               \
+               DECLARE_GENERIC_CHANNEL(tag)            \
+       }                                               \
+},
+
+#define DECLARE_SENSOR1(tag, type, ch1)                        \
+{                                                      \
+       tag, type, 1,                                   \
+       {                                               \
+               DECLARE_NAMED_CHANNEL(tag, ch1)         \
+       }                                               \
+},
+
+#define DECLARE_SENSOR2(tag, type, ch1, ch2)           \
+{                                                      \
+       tag, type, 2,                                   \
+       {                                               \
+               DECLARE_NAMED_CHANNEL(tag, ch1)         \
+               DECLARE_NAMED_CHANNEL(tag, ch2)         \
+       }                                               \
+},
+
+#define DECLARE_SENSOR3(tag, type, ch1, ch2, ch3)      \
+{                                                      \
+       tag, type, 3,                                   \
+       {                                               \
+               DECLARE_NAMED_CHANNEL(tag, ch1)         \
+               DECLARE_NAMED_CHANNEL(tag, ch2)         \
+               DECLARE_NAMED_CHANNEL(tag, ch3)         \
+       }                                               \
+},
+
+#define DECLARE_SENSOR4(tag, type, ch1, ch2, ch3, ch4) \
+{                                                      \
+       tag, type, 4,                                   \
+       {                                               \
+               DECLARE_NAMED_CHANNEL(tag, ch1)         \
+               DECLARE_NAMED_CHANNEL(tag, ch2)         \
+               DECLARE_NAMED_CHANNEL(tag, ch3)         \
+               DECLARE_NAMED_CHANNEL(tag, ch4)         \
+       }                                               \
+},
+
+int    get_sensors_list        (struct sensors_module_t* module,
+                                struct sensor_t const** list);
+
+void   enumerate_sensors       (void);
+void   delete_enumeration_data (void);
+
+#endif
diff --git a/utils.c b/utils.c
new file mode 100644 (file)
index 0000000..fcde830
--- /dev/null
+++ b/utils.c
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2014 Intel Corporation.
+ */
+
+#include <stdlib.h>
+#include <fcntl.h>
+#include <utils/Log.h>
+#include <hardware/sensors.h>
+#include "utils.h"
+
+
+/* Note:
+ *
+ * Some of these calls are going to fail, because not all sensors expose all
+ * possible sysfs attributes. As an optimization we may want to cache which
+ * ones are valid and immediately return in error for inexistent entries.
+ */
+
+int sysfs_write_int(const char path[PATH_MAX], int value)
+{
+       int ret;
+       int fd;
+       int len;
+       char buf[20];
+
+       if (!path[0]) {
+               return -1;
+       }
+
+       fd = open(path, O_WRONLY);
+
+       if (fd == -1) {
+               ALOGV("Cannot open %s (%s)\n", path, strerror(errno));
+               return -1;
+       }
+
+       len = sprintf(buf, "%d", value) + 1;
+
+       ret = write(fd, buf, len);
+
+       if (ret != len) {
+               ALOGW("Cannot write %s (%d bytes) to %s (%s)\n", buf, len, path,
+                     strerror(errno));
+       }
+
+       close(fd);
+
+       return ret;
+}
+
+
+int sysfs_read_int(const char path[PATH_MAX], int *value)
+{
+       int fd;
+       int len;
+       char buf[20] = {0};
+
+       if (!path[0] || !value) {
+               return -1;
+       }
+
+       fd = open(path, O_RDONLY);
+
+       if (fd == -1) {
+               ALOGV("Cannot open %s (%s)\n", path, strerror(errno));
+               return -1;
+       }
+
+       len = read(fd, buf, sizeof(buf));
+
+       close(fd);
+
+       if (len <= 0) {
+               ALOGW("Cannot read integer from %s (%s)\n", path,
+                     strerror(errno));
+               return -1;
+       }
+
+       *value = atoi(buf);
+
+       ALOGV("Read %d from %s\n", *value, path);
+
+       return 0;
+}
+
+
+int sysfs_write_str(const char path[PATH_MAX], const char *str)
+{
+       int ret;
+       int fd;
+       int len;
+
+       if (!path[0] || !str || !str[0]) {
+               return -1;
+       }
+
+       fd = open(path, O_WRONLY);
+
+       if (fd == -1) {
+               ALOGV("Cannot open %s (%s)\n", path, strerror(errno));
+               return -1;
+       }
+
+       len = strlen(str);
+
+       ret = write(fd, str, len);
+
+       if (ret != len) {
+               ALOGW("Cannot write %s (%d bytes) to %s (%s)\n", str, len, path,
+                     strerror(errno));
+       }
+       else
+               ALOGV("Wrote %s to %s\n", str, path);
+
+       close(fd);
+
+       return ret;
+}
+
+
+int sysfs_read_str(const char path[PATH_MAX], char *buf, int buf_len)
+{
+       int fd;
+       int len;
+
+       if (!path[0] || !buf || buf_len < 1)
+               return -1;
+
+       fd = open(path, O_RDONLY);
+
+       if (fd == -1) {
+               ALOGV("Cannot open %s (%s)\n", path, strerror(errno));
+               return -1;
+       }
+
+       len = read(fd, buf, buf_len);
+
+       close(fd);
+
+       if (len == -1) {
+               ALOGW("Cannot read string from %s (%s)\n", path,
+                     strerror(errno));
+               return -1;
+       }
+
+       buf[len == 0 ? 0 : len-1] = '\0';
+
+       ALOGV("Read %s from %s\n", buf, path);
+
+       return len;
+}
+
+
+int sysfs_read_float(const char path[PATH_MAX], float *value)
+{
+       int fd;
+       int len;
+       char buf[20] = {0};
+
+       if (!path[0] || !value) {
+               return -1;
+       }
+
+       fd = open(path, O_RDONLY);
+
+       if (fd == -1) {
+               ALOGV("Cannot open %s (%s)\n", path, strerror(errno));
+               return -1;
+       }
+
+       len = read(fd, buf, sizeof(buf));
+
+       close(fd);
+
+       if (len <= 0) {
+               ALOGW("Cannot read float from %s (%s)\n", path,
+                     strerror(errno));
+               return -1;
+       }
+
+       *value = (float) strtod(buf, NULL);
+
+       ALOGV("Read %f from %s\n", *value, path);
+
+       return 0;
+}
+
+
+int decode_type_spec(  const char type_buf[MAX_TYPE_SPEC_LEN],
+                       struct datum_info_t *type_info)
+{
+       /* Return size in bytes for this type specification, or -1 in error */
+       char sign;
+       char endianness;
+       unsigned int realbits, storagebits, shift;
+       int tokens;
+
+       /* Valid specs: "le:u10/16>>0", "le:s16/32>>0" or "le:s32/32>>0" */
+
+       tokens = sscanf(type_buf, "%ce:%c%u/%u>>%u",
+                       &endianness, &sign, &realbits, &storagebits, &shift);
+
+       if     (tokens != 5 ||
+               (endianness != 'b' && endianness != 'l') ||
+               (sign != 'u' && sign != 's') ||
+               realbits > storagebits ||
+               (storagebits != 16 && storagebits != 32 && storagebits != 64)) {
+                       ALOGE("Invalid iio channel type spec: %s\n", type_buf);
+                       return -1;
+               }
+
+       type_info->endianness   =               endianness;
+       type_info->sign         =               sign;
+       type_info->realbits     =       (short) realbits;
+       type_info->storagebits  =       (short) storagebits;
+       type_info->shift        =       (short) shift;
+
+       return storagebits / 8;
+}
+
+
+int64_t sample_as_int64(unsigned char* sample, struct datum_info_t* type)
+{
+       uint16_t u16;
+       uint32_t u32;
+       uint64_t u64;
+       int i;
+
+       switch (type->storagebits) {
+               case 64:
+                       u64 = 0;
+
+                       if (type->endianness == 'b')
+                               for (i=0; i<8; i++)
+                                       u64 = (u64 << 8) | sample[i];
+                       else
+                               for (i=7; i>=0; i--)
+                                       u64 = (u64 << 8) | sample[i];
+
+                       if (type->sign == 'u')
+                               return (int64_t) (u64 >> type->shift);
+
+                       return ((int64_t) u64) >> type->shift;
+
+               case 32:
+                       if (type->endianness == 'b')
+                               u32 = (sample[0] << 24) | (sample[1] << 16) |
+                                       (sample[2] << 8) | sample[3];
+                       else
+                               u32 = (sample[3] << 24) | (sample[2] << 16) |
+                                       (sample[1] << 8) | sample[0];
+
+                       if (type->sign == 'u')
+                               return u32 >> type->shift;
+
+                       return ((int32_t) u32) >> type->shift;
+
+               case 16:
+                       if (type->endianness == 'b')
+                               u16 = (sample[0] << 8) | sample[1];
+                       else
+                               u16 = (sample[1] << 8) | sample[0];
+
+                       if (type->sign == 'u')
+                               return u16 >> type->shift;
+
+                       return  ((int16_t) u16) >> type->shift;
+       }
+
+       ALOGE("Unhandled sample storage size\n");
+       return 0;
+}
+
+
+float transform_sample (int sensor_type, int channel, float val)
+{
+       /* Last opportunity to alter sample data before it goes to Android */
+       switch (sensor_type) {
+               case SENSOR_TYPE_ACCELEROMETER:
+                       /*
+                        * Invert x axis orientation from SI units - see
+                        * /hardware/libhardware/include/hardware/sensors.h
+                        * for a discussion of what Android expects
+                        */
+                       if (channel == 0)
+                               return -val;
+                       break;
+
+               case SENSOR_TYPE_GYROSCOPE:
+                       /* Limit drift */
+                       if (val > -0.05 && val < 0.05)
+                               return 0;
+                       break;
+       }
+
+       return val;
+}
+
+
+int64_t get_timestamp(void)
+{
+       struct timespec ts = {0};
+
+       clock_gettime(CLOCK_MONOTONIC, &ts);
+
+       return 1000000000LL * ts.tv_sec + ts.tv_nsec;
+}
+
+
diff --git a/utils.h b/utils.h
new file mode 100644 (file)
index 0000000..6f827d3
--- /dev/null
+++ b/utils.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2014 Intel Corporation.
+ */
+
+#ifndef __UTILS_H__
+#define __UTILS_H__
+
+#include "common.h"
+
+int    sysfs_read_int  (const char path[PATH_MAX], int *value);
+int    sysfs_write_int (const char path[PATH_MAX], int value);
+
+int    sysfs_read_str  (const char path[PATH_MAX], char *buf, int buf_len);
+int    sysfs_write_str (const char path[PATH_MAX], const char *buf);
+
+int    sysfs_read_float(const char path[PATH_MAX], float *value);
+
+int    decode_type_spec(const char type_buf[MAX_TYPE_SPEC_LEN],
+                        struct datum_info_t *type_info);
+
+int64_t sample_as_int64        (unsigned char* sample, struct datum_info_t* type);
+
+float  transform_sample(int sensor_type, int channel, float val);
+
+int64_t        get_timestamp   (void);
+
+#endif
+
+
+