/*
- * Copyright (C) 2014-2015 Intel Corporation.
- */
+// Copyright (c) 2015 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
#include <stdlib.h>
#include <ctype.h>
#include "calibration.h"
#include "description.h"
#include "filtering.h"
-
-/* Couple of temporary defines until we get a suitable linux/iio/events.h include */
-
-struct iio_event_data {
- __u64 id;
- __s64 timestamp;
-};
-
-#define IIO_GET_EVENT_FD_IOCTL _IOR('i', 0x90, int)
+#ifndef __NO_EVENTS__
+#include <linux/iio/events.h>
+#include <linux/iio/types.h>
+#endif
+#include <errno.h>
/* Currently active sensors count, per device */
static int poll_sensors_per_dev[MAX_DEVICES]; /* poll-mode sensors */
static int active_poll_sensors; /* Number of enabled poll-mode sensors */
+static int flush_event_fd[2]; /* Pipe used for flush signaling */
+
/* We use pthread condition variables to get worker threads out of sleep */
static pthread_condattr_t thread_cond_attr [MAX_SENSORS];
static pthread_cond_t thread_release_cond [MAX_SENSORS];
static pthread_mutex_t thread_release_mutex [MAX_SENSORS];
+#define FLUSH_REPORT_TAG 900
/*
* We associate tags to each of our poll set entries. These tags have the following values:
* - a iio device number if the fd is a iio character device fd
known_channels++;
}
+ sensor_update_max_range(s);
+
/* Stop sampling - if we are recovering from hal restart */
enable_buffer(dev_num, 0);
setup_trigger(s, "\n");
ALOGI("Enabling sensor %d (iio device %d: %s)\n", s, dev_num, sensor[s].friendly_name);
switch (sensor[s].type) {
+ case SENSOR_TYPE_ACCELEROMETER:
+ accel_cal_init(s);
+ break;
+
case SENSOR_TYPE_MAGNETIC_FIELD:
- compass_read_data(&sensor[s]);
+ compass_read_data(s);
break;
case SENSOR_TYPE_GYROSCOPE:
- gyro_cal_init(&sensor[s]);
+ gyro_cal_init(s);
break;
}
} else {
/* Sensor disabled, lower report available flag */
sensor[s].report_pending = 0;
- if (sensor[s].type == SENSOR_TYPE_MAGNETIC_FIELD)
- compass_store_data(&sensor[s]);
+ /* Save calibration data to persistent storage */
+ switch (sensor[s].type) {
+ case SENSOR_TYPE_ACCELEROMETER:
+ accel_cal_store(s);
+ break;
+
+ case SENSOR_TYPE_MAGNETIC_FIELD:
+ compass_store_data(s);
+ break;
- if (sensor[s].type == SENSOR_TYPE_GYROSCOPE)
- gyro_store_data(&sensor[s]);
+ case SENSOR_TYPE_GYROSCOPE:
+ gyro_store_data(s);
+ break;
+ }
}
/* We changed the state of a sensor: adjust device ref counts */
static int get_field_count (int s, size_t *field_size)
{
*field_size = sizeof(float);
+
switch (sensor[s].type) {
case SENSOR_TYPE_ACCELEROMETER: /* m/s^2 */
case SENSOR_TYPE_MAGNETIC_FIELD: /* micro-tesla */
case SENSOR_TYPE_GYROSCOPE: /* radians/s */
return 3;
+ case SENSOR_TYPE_INTERNAL_INTENSITY:
+ case SENSOR_TYPE_INTERNAL_ILLUMINANCE:
case SENSOR_TYPE_LIGHT: /* SI lux units */
case SENSOR_TYPE_AMBIENT_TEMPERATURE: /* °C */
case SENSOR_TYPE_TEMPERATURE: /* °C */
/* Initialize data fields that will be shared by all sensor reports */
data.version = sizeof(sensors_event_t);
data.sensor = s;
- data.type = sensor[s].type;
+ data.type = sensor_desc[s].type;
num_fields = get_field_count(s, &field_size);
ret = write(sensor[s].thread_data_fd[1], &data, sizeof(sensors_event_t));
if (ret != sizeof(sensors_event_t))
- ALOGE("S%d write failure: wrote %d, got %d\n", s, sizeof(sensors_event_t), ret);
+ ALOGE("S%d write failure: wrote %zd, got %d\n", s, sizeof(sensors_event_t), ret);
}
/* Check and honor termination requests */
return arbitrated_rate;
}
+extern float sensor_get_max_freq (int s);
+
+static float select_closest_available_rate(int s, float requested_rate)
+{
+ float sr;
+ int j;
+ float selected_rate = 0;
+ float max_rate_from_prop = sensor_get_max_freq(s);
+
+ if (!sensor[s].avail_freqs_count)
+ return requested_rate;
+
+ for (j = 0; j < sensor[s].avail_freqs_count; j++) {
+
+ sr = sensor[s].avail_freqs[j];
+
+ /* If this matches the selected rate, we're happy. Have some tolerance for rounding errors and avoid needless jumps to higher rates */
+ if ((fabs(requested_rate - sr) <= 0.01) && (sr <= max_rate_from_prop)) {
+ return sr;
+ }
+
+ /* Select rate if it's less than max freq */
+ if ((sr > selected_rate) && (sr <= max_rate_from_prop)) {
+ selected_rate = sr;
+ }
+
+ /*
+ * If we reached a higher value than the desired rate, adjust selected rate so it matches the first higher available one and
+ * stop parsing - this makes the assumption that rates are sorted by increasing value in the allowed frequencies string.
+ */
+ if (sr > requested_rate) {
+ return selected_rate;
+ }
+ }
+
+ /* Check for wrong values */
+ if (selected_rate < 0.1) {
+ return requested_rate;
+ } else {
+ return selected_rate;
+ }
+}
static int sensor_set_rate (int s, float requested_rate)
{
/* Set the rate at which a specific sensor should report events. See Android sensors.h for indication on sensor trigger modes */
char sysfs_path[PATH_MAX];
- char avail_sysfs_path[PATH_MAX];
int dev_num = sensor[s].dev_num;
int i = sensor[s].catalog_index;
const char *prefix = sensor_catalog[i].tag;
int per_sensor_sampling_rate;
int per_device_sampling_rate;
- char freqs_buf[100];
- char* cursor;
int n;
float sr;
float group_max_sampling_rate;
float cur_sampling_rate; /* Currently used sampling rate */
float arb_sampling_rate; /* Granted sampling rate after arbitration */
+ char hrtimer_sampling_path[PATH_MAX];
+ char trigger_path[PATH_MAX];
ALOGV("Sampling rate %g requested on sensor %d (%s)\n", requested_rate, s, sensor[s].friendly_name);
return -ENOSYS;
}
- /* Check if we have contraints on allowed sampling rates */
-
- sprintf(avail_sysfs_path, DEVICE_AVAIL_FREQ_PATH, dev_num);
-
- if (sysfs_read_str(avail_sysfs_path, freqs_buf, sizeof(freqs_buf)) > 0) {
- cursor = freqs_buf;
-
- /* Decode allowed sampling rates string, ex: "10 20 50 100" */
-
- /* While we're not at the end of the string */
- while (*cursor && cursor[0]) {
-
- /* Decode a single value */
- sr = strtod(cursor, NULL);
-
- /* If this matches the selected rate, we're happy. Have some tolerance for rounding errors and avoid needless jumps to higher rates */
- if (fabs(arb_sampling_rate - sr) <= 0.001) {
- arb_sampling_rate = sr;
- break;
- }
-
- /*
- * If we reached a higher value than the desired rate, adjust selected rate so it matches the first higher available one and
- * stop parsing - this makes the assumption that rates are sorted by increasing value in the allowed frequencies string.
- */
- if (sr > arb_sampling_rate) {
- arb_sampling_rate = sr;
- break;
- }
-
- /* Skip digits */
- while (cursor[0] && !isspace(cursor[0]))
- cursor++;
-
- /* Skip spaces */
- while (cursor[0] && isspace(cursor[0]))
- cursor++;
- }
+ if (sensor[s].hrtimer_trigger_name[0] != '\0') {
+ snprintf(trigger_path, PATH_MAX, "%s%s%d/", IIO_DEVICES, "trigger", sensor[s].trigger_nr);
+ snprintf(hrtimer_sampling_path, PATH_MAX, "%s%s", trigger_path, "sampling_frequency");
+ /* Enforce frequency update when software trigger
+ * frequency and current sampling rate are different */
+ if (sysfs_read_float(hrtimer_sampling_path, &sr) != -1 && sr != cur_sampling_rate)
+ cur_sampling_rate = -1;
+ } else {
+ arb_sampling_rate = select_closest_available_rate(s, arb_sampling_rate);
}
- if (sensor[s].max_supported_rate &&
- arb_sampling_rate > sensor[s].max_supported_rate) {
- arb_sampling_rate = sensor[s].max_supported_rate;
- }
+ /* Record the rate that was agreed upon with the sensor taken in isolation ; this avoid uncontrolled ripple effects between colocated sensor rates */
+ sensor[s].semi_arbitrated_rate = arb_sampling_rate;
/* Coordinate with others active sensors on the same device, if any */
if (per_device_sampling_rate)
for (n=0; n<sensor_count; n++)
- if (n != s && sensor[n].dev_num == dev_num && sensor[n].num_channels && is_enabled(n) && sensor[n].sampling_rate > arb_sampling_rate) {
+ if (n != s && sensor[n].dev_num == dev_num && sensor[n].num_channels && is_enabled(n) &&
+ sensor[n].semi_arbitrated_rate > arb_sampling_rate) {
ALOGV("Sampling rate shared between %s and %s, using %g instead of %g\n", sensor[s].friendly_name, sensor[n].friendly_name,
- sensor[n].sampling_rate, arb_sampling_rate);
- arb_sampling_rate = sensor[n].sampling_rate;
+ sensor[n].semi_arbitrated_rate, arb_sampling_rate);
+ arb_sampling_rate = sensor[n].semi_arbitrated_rate;
}
sensor[s].sampling_rate = arb_sampling_rate;
ALOGI("Sensor %d (%s) sampling rate set to %g\n", s, sensor[s].friendly_name, arb_sampling_rate);
+ if (sensor[s].hrtimer_trigger_name[0] != '\0')
+ sysfs_write_float(hrtimer_sampling_path, ceilf(arb_sampling_rate));
+
if (trig_sensors_per_dev[dev_num])
enable_buffer(dev_num, 0);
- sysfs_write_float(sysfs_path, arb_sampling_rate);
+ if (sensor[s].hrtimer_trigger_name[0] != '\0') {
+ sysfs_write_float(sysfs_path, select_closest_available_rate(s, arb_sampling_rate));
+ } else {
+ sysfs_write_float(sysfs_path, arb_sampling_rate);
+ }
/* Check if it makes sense to use an alternate trigger */
tentative_switch_trigger(s);
if (trig_sensors_per_dev[dev_num]) {
/* Start sampling */
- setup_trigger(s, sensor[s].init_trigger_name);
+ if (sensor[s].hrtimer_trigger_name[0] != '\0')
+ setup_trigger(s, sensor[s].hrtimer_trigger_name);
+ else
+ setup_trigger(s, sensor[s].init_trigger_name);
+
enable_buffer(dev_num, 1);
}
} else if (sensor[s].mode == MODE_POLL) {
}
/* Note: poll-mode fds are not readable */
+#ifdef __NO_EVENTS__
+ }
+#else
} else if (sensor[s].mode == MODE_EVENT) {
event_fd = events_fd[dev_num];
device_fd[dev_num] = -1;
}
}
+#endif
}
/* Ensure that on-change sensors send at least one event after enable */
int s;
for (s=0; s<MAX_SENSORS; s++)
- if (sensor[s].dev_num == dev_num && is_enabled(s) && sensor[s].mode != MODE_POLL)
- sensor[s].report_ts = ts;
+ if (sensor[s].dev_num == dev_num && is_enabled(s) && sensor[s].mode != MODE_POLL) {
+ if (sensor[s].quirks & QUIRK_SPOTTY)
+ set_report_ts(s, ts);
+ else
+ sensor[s].report_ts = ts;
+ }
}
sensor[s].report_pending = DATA_TRIGGER;
sensor[s].report_initialized = 1;
- ts_offset += sr_offset;
}
/* Tentatively switch to an any-motion trigger if conditions are met */
return 0;
}
- ALOGV("Driver timestamp on iio device %d: ts=%lld\n", dev_num, ts);
+ ALOGV("Driver timestamp on iio device %d: ts=%jd\n", dev_num, ts);
boot_to_rt_delta = get_timestamp_boot() - get_timestamp_realtime();
stamp_reports(dev_num, ts + boot_to_rt_delta);
+
return 0;
}
+#ifndef __NO_EVENTS__
static int integrate_device_report_from_event(int dev_num, int fd)
{
int len, s;
int64_t ts;
struct iio_event_data event;
+ int64_t boot_to_rt_delta = get_timestamp_boot() - get_timestamp_realtime();
/* There's an incoming report on the specified iio device char dev fd */
if (fd == -1) {
return -1;
}
- ts = event.timestamp;
+ ts = event.timestamp + boot_to_rt_delta;
- ALOGV("Read event %lld from fd %d of iio device %d\n", event.id, fd, dev_num);
+ ALOGV("Read event %lld from fd %d of iio device %d - ts %jd\n", event.id, fd, dev_num, ts);
/* Map device report to sensor reports */
for (s = 0; s < MAX_SENSORS; s++)
if (sensor[s].dev_num == dev_num &&
is_enabled(s)) {
+ sensor[s].event_id = event.id;
sensor[s].report_ts = ts;
sensor[s].report_pending = 1;
sensor[s].report_initialized = 1;
}
return 0;
}
+#endif
static int integrate_device_report(int dev_num)
{
return -1;
}
+#ifndef __NO_EVENTS__
if (events_fd[dev_num] != -1) {
ret = integrate_device_report_from_event(dev_num, events_fd[dev_num]);
if (ret < 0)
return ret;
}
+#endif
if (device_fd[dev_num] != -1)
ret = integrate_device_report_from_dev(dev_num, device_fd[dev_num]);
memcpy(data, &sensor[s].sample, sizeof(sensors_event_t));
data->sensor = s;
- data->type = sensor[s].type;
+ data->type = sensor_desc[s].type; /* sensor_desc[s].type can differ from sensor[s].type ; internal types are remapped */
return 1;
}
data->version = sizeof(sensors_event_t);
data->sensor = s;
- data->type = sensor[s].type;
+ data->type = sensor_desc[s].type; /* sensor_desc[s].type can differ from sensor[s].type ; internal types are remapped */
data->timestamp = sensor[s].report_ts;
+#ifndef __NO_EVENTS__
if (sensor[s].mode == MODE_EVENT) {
ALOGV("Reporting event\n");
/* Android requires events to return 1.0 */
- data->data[0] = 1.0;
+ int dir = IIO_EVENT_CODE_EXTRACT_DIR(sensor[s].event_id);
+ switch (sensor[s].type) {
+ case SENSOR_TYPE_PROXIMITY:
+ if (dir == IIO_EV_DIR_FALLING)
+ data->data[0] = 0.0;
+ else
+ data->data[0] = 1.0;
+ break;
+ default:
+ data->data[0] = 1.0;
+ break;
+
+ }
data->data[1] = 0.0;
data->data[2] = 0.0;
return 1;
}
+#endif
/* Convert the data into the expected Android-level format */
/* Get report from acquisition thread */
integrate_thread_report(ev[i].data.u32);
break;
+ case FLUSH_REPORT_TAG:
+ {
+ char flush_event_content;
+ read(flush_event_fd[0], &flush_event_content, sizeof(flush_event_content));
+ break;
+ }
default:
ALOGW("Unexpected event source!\n");
float requested_sampling_rate;
if (ns <= 0) {
- ALOGE("Invalid delay requested on sensor %d: %lld\n", s, ns);
+ ALOGE("Invalid delay requested on sensor %d: %jd\n", s, ns);
return -EINVAL;
}
int sensor_flush (int s)
{
+ char flush_event_content = 0;
/* If one shot or not enabled return -EINVAL */
if (sensor_desc[s].flags & SENSOR_FLAG_ONE_SHOT_MODE || !is_enabled(s))
return -EINVAL;
sensor[s].meta_data_pending++;
+ write(flush_event_fd[1], &flush_event_content, sizeof(flush_event_content));
return 0;
}
int allocate_control_data (void)
{
- int i;
+ int i, ret;
+ struct epoll_event ev = {0};
for (i=0; i<MAX_DEVICES; i++) {
device_fd[i] = -1;
return -1;
}
+ ret = pipe(flush_event_fd);
+ if (ret) {
+ ALOGE("Cannot create flush_event_fd");
+ return -1;
+ }
+
+ ev.events = EPOLLIN;
+ ev.data.u32 = FLUSH_REPORT_TAG;
+ ret = epoll_ctl(poll_fd, EPOLL_CTL_ADD, flush_event_fd[0] , &ev);
+ if (ret == -1) {
+ ALOGE("Failed adding %d to poll set (%s)\n",
+ flush_event_fd[0], strerror(errno));
+ return -1;
+ }
+
return poll_fd;
}