X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=control.c;h=7d942fdfab48557a412089f649544edb0816f30e;hb=b24d6584ce3c44cdb5ba9ebe6aa525ff7ecd477c;hp=811270debb43ffec2de3d4555752f884a39dab81;hpb=816bba686f5c70100cf825b3894e808f58e30ab8;p=android-x86%2Fhardware-intel-libsensors.git diff --git a/control.c b/control.c index 811270d..7d942fd 100644 --- a/control.c +++ b/control.c @@ -2,14 +2,21 @@ * Copyright (C) 2014 Intel Corporation. */ +#include +#include #include +#include +#include #include -#include +#include #include #include #include "control.h" #include "enumeration.h" #include "utils.h" +#include "transform.h" +#include "calibration.h" +#include "description.h" /* Currently active sensors count, per device */ static int poll_sensors_per_dev[MAX_DEVICES]; /* poll-mode sensors */ @@ -19,37 +26,77 @@ 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 */ +/* We use pthread condition variables to get worker threads out of sleep */ +static pthread_cond_t thread_release_cond [MAX_SENSORS]; +static pthread_mutex_t thread_release_mutex [MAX_SENSORS]; + +/* + * 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 + * - THREAD_REPORT_TAG_BASE + sensor handle if the fd is the receiving end of a + * pipe used by a sysfs data acquisition thread + * */ +#define THREAD_REPORT_TAG_BASE 0x00010000 + +/* When polling try to compensate for the iio overhead in + * order to try to get a frequency closer to the advertised one + */ +#define OVERHEAD_THRESHOLD 0.97 +#define ENABLE_BUFFER_RETRIES 10 +#define ENABLE_BUFFER_RETRY_DELAY_MS 10 static int enable_buffer(int dev_num, int enabled) { char sysfs_path[PATH_MAX]; + int ret, retries, millisec; + struct timespec req = {0}; + + retries = ENABLE_BUFFER_RETRIES; + millisec = ENABLE_BUFFER_RETRY_DELAY_MS; + req.tv_sec = 0; + req.tv_nsec = millisec * 1000000L; sprintf(sysfs_path, ENABLE_PATH, dev_num); - /* Low level, non-multiplexed, enable/disable routine */ - return sysfs_write_int(sysfs_path, enabled); + while (retries--) { + /* Low level, non-multiplexed, enable/disable routine */ + ret = sysfs_write_int(sysfs_path, enabled); + if (ret > 0) + break; + + ALOGE("Failed enabling buffer, retrying"); + nanosleep(&req, (struct timespec *)NULL); + } + + if (ret < 0) { + ALOGE("Could not enable buffer\n"); + return -EIO; + } + + return 0; } -static int setup_trigger(int dev_num, const char* trigger_val) +static void setup_trigger (int s, const char* trigger_val) { char sysfs_path[PATH_MAX]; - sprintf(sysfs_path, TRIGGER_PATH, dev_num); + sprintf(sysfs_path, TRIGGER_PATH, sensor_info[s].dev_num); - return sysfs_write_str(sysfs_path, trigger_val); + if (trigger_val[0] != '\n') + ALOGI("Setting S%d (%s) trigger to %s\n", s, + sensor_info[s].friendly_name, trigger_val); + + sysfs_write_str(sysfs_path, trigger_val); + + sensor_info[s].selected_trigger = trigger_val; } -static void refresh_sensor_report_maps(int dev_num) +void build_sensor_report_maps(int dev_num) { /* * Read sysfs files from a iio device's scan_element directory, and @@ -69,21 +116,19 @@ static void refresh_sensor_report_maps(int dev_num) 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 known_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; + known_channels = 0; /* For each sensor that is linked to this device */ for (s=0; s= MAX_SENSORS) { + ALOGE("Index out of bounds!: %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++; + known_channels++; + } + + /* Stop sampling - if we are recovering from hal restart */ + enable_buffer(dev_num, 0); + setup_trigger(s, "\n"); + + /* Turn on channels we're aware of */ + for (c=0;c 1) return 0; /* The sensor was, and remains, in use */ + + switch (sensor_type) { + case SENSOR_TYPE_MAGNETIC_FIELD: + compass_read_data(&sensor_info[s]); + break; + + case SENSOR_TYPE_GYROSCOPE: + case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED: + gyro_cal_init(&sensor_info[s]); + break; + } } else { if (sensor_info[s].enable_count == 0) return -1; /* Spurious disable call */ - ALOGI("Disabling sensor %d (iio device %d)\n", s, dev_num); + ALOGI("Disabling sensor %d (iio device %d: %s)\n", s, dev_num, + sensor_info[s].friendly_name); 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 disabled, lower report available flag */ sensor_info[s].report_pending = 0; - memset(sensor_info[s].report_buffer, 0, MAX_SENSOR_REPORT_SIZE); + + if (sensor_type == SENSOR_TYPE_MAGNETIC_FIELD) + compass_store_data(&sensor_info[s]); } + + /* If uncalibrated type and pair is already active don't adjust counters */ + if (sensor_type == SENSOR_TYPE_GYROSCOPE_UNCALIBRATED && + sensor_info[sensor_info[s].pair_idx].enable_count != 0) + return 0; + /* We changed the state of a sensor - adjust per iio device counters */ /* If this is a regular event-driven sensor */ @@ -252,78 +317,277 @@ int adjust_counters (int s, int enabled) } +static int get_field_count (int s) +{ + int catalog_index = sensor_info[s].catalog_index; + int sensor_type = sensor_catalog[catalog_index].type; + + 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_UNCALIBRATED: + case SENSOR_TYPE_GYROSCOPE: /* radians/s */ + return 3; + + 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 */ + return 1; + + case SENSOR_TYPE_ROTATION_VECTOR: + return 4; + + default: + ALOGE("Unknown sensor type!\n"); + return 0; /* Drop sample */ + } +} + + +static void time_add(struct timespec *out, struct timespec *in, int64_t ns) +{ + int64_t target_ts = 1000000000LL * in->tv_sec + in->tv_nsec + ns; + + out->tv_sec = target_ts / 1000000000; + out->tv_nsec = target_ts % 1000000000; +} + + +static void* acquisition_routine (void* param) +{ + /* + * Data acquisition routine run in a dedicated thread, covering a single + * sensor. This loop will periodically retrieve sampling data through + * sysfs, then package it as a sample and transfer it to our master poll + * loop through a report fd. Checks for a cancellation signal quite + * frequently, as the thread may be disposed of at any time. Note that + * Bionic does not provide pthread_cancel / pthread_testcancel... + */ + + int s = (int) (size_t) param; + int num_fields; + struct sensors_event_t data = {0}; + int c; + int ret; + struct timespec entry_time; + struct timespec target_time; + int64_t period; + + ALOGI("Entering data acquisition thread S%d (%s): rate(%f), minDelay(%ld), maxDelay(%ld)\n", + s, sensor_info[s].friendly_name, sensor_info[s].sampling_rate, + sensor_desc[s].minDelay, sensor_desc[s].maxDelay); + + if (s < 0 || s >= sensor_count) { + ALOGE("Invalid sensor handle!\n"); + return NULL; + } + + if (!sensor_info[s].sampling_rate) { + ALOGE("Zero rate in acquisition routine for sensor %d\n", s); + return NULL; + } + + num_fields = get_field_count(s); + + /* + * Each condition variable is associated to a mutex that has to be + * locked by the thread that's waiting on it. We use these condition + * variables to get the acquisition threads out of sleep quickly after + * the sampling rate is adjusted, or the sensor is disabled. + */ + pthread_mutex_lock(&thread_release_mutex[s]); + + while (1) { + /* Pinpoint the moment we start sampling */ + clock_gettime(CLOCK_REALTIME, &entry_time); + + ALOGV("Acquiring sample data for sensor %d through sysfs\n", s); + + /* Read values through sysfs */ + for (c=0; c= MAX_DEVICES) - return -1; + /* There's an incoming report on the specified iio device char dev fd */ - /* There's an incoming report on the specified fd */ + if (dev_num < 0 || dev_num >= MAX_DEVICES) { + ALOGE("Event reported on unexpected iio device %d\n", dev_num); + return -1; + } - for (s=0; sversion = sizeof(sensors_event_t); + data->sensor = s; + data->type = sensor_type; + data->timestamp = sensor_info[s].report_ts; - return (val + offset) * scale; -} + ALOGV("Sample on sensor %d (type %d):\n", s, sensor_type); + current_sample = sensor_info[s].report_buffer; -static void propagate_sensor_report(int s, struct sensors_event_t* data) -{ - /* There's a sensor report pending for this sensor ; transmit it */ + /* If this is a poll sensor */ + if (!sensor_info[s].num_channels) { + /* Use the data provided by the acquisition thread */ + ALOGV("Reporting data from worker thread for S%d\n", s); + memcpy(data->data, current_sample, num_fields * sizeof(float)); + return 1; + } - 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; + /* Convert the data into the expected Android-level format */ + for (c=0; cdata[c] = sensor_info[s].ops.transform + (s, c, current_sample); - data->version = sizeof(sensors_event_t); - data->sensor = s; - data->type = sensor_type; - data->timestamp = get_timestamp(); + ALOGV("\tfield %d: %f\n", c, data->data[c]); + current_sample += sensor_info[s].channel[c].size; + } - 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; + /* + * The finalize routine, in addition to its late sample processing duty, + * has the final say on whether or not the sample gets sent to Android. + */ + return sensor_info[s].ops.finalize(s, data); +} - 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; +static void synthetize_duplicate_samples (void) +{ + /* + * Some sensor types (ex: gyroscope) are defined as continuously firing + * by Android, despite the fact that we can be dealing with iio drivers + * that only report events for new samples. For these we generate + * reports periodically, duplicating the last data we got from the + * driver. This is not necessary for polling sensors. + */ - case SENSOR_TYPE_DEVICE_PRIVATE_BASE: /* hidden for now */ - num_fields = 0; - break; + int s; + int64_t current_ts; + int64_t target_ts; + int64_t period; - default: - ALOGE("Unknown sensor type!\n"); - num_fields = 0; - break; - } + for (s=0; sdata[c] = transform_sample(sensor_type, c, val); + /* We also need a valid sampling rate to be configured */ + if (!sensor_info[s].sampling_rate) + continue; - ALOGV("\tfield %d: %f\n", c, data->data[c]); - } - return; - } + period = (int64_t) (1000000000.0/ sensor_info[s].sampling_rate); - /* Convert the data into the expected Android-level format */ + current_ts = get_timestamp(); + target_ts = sensor_info[s].report_ts + period; - current_sample = sensor_info[s].report_buffer; + if (target_ts <= current_ts) { + /* Mark the sensor for event generation */ + set_report_ts(s, current_ts); + sensor_info[s].report_pending = 1; + } + } +} - for (c=0; cdata[c] = transform_sample(sensor_type, c, val); + len = read(sensor_info[s].thread_data_fd[0], + sensor_info[s].report_buffer, + expected_len); - ALOGV("\tfield %d: %f\n", c, data->data[c]); - current_sample += sample_size; + if (len == expected_len) { + set_report_ts(s, get_timestamp()); + sensor_info[s].report_pending = 1; } } -static int get_poll_time (void) +static int get_poll_wait_timeout (void) { - if (!active_poll_sensors) - return -1; /* Infinite wait */ + /* + * Compute an appropriate timeout value, in ms, for the epoll_wait + * call that's going to await for iio device reports and incoming + * reports from our sensor sysfs data reader threads. + */ + + int s; + int64_t target_ts = INT64_MAX; + int64_t ms_to_wait; + int64_t period; + + /* + * Check if have have to deal with "terse" drivers that only send events + * when there is motion, despite the fact that the associated Android + * sensor type is continuous rather than on-change. In that case we have + * to duplicate events. Check deadline for the nearest upcoming event. + */ + for (s=0; sbias_x; + data[uncal_start + i].data[1] = data[returned_events + i].data[1] + gyro_data->bias_y; + data[uncal_start + i].data[2] = data[returned_events + i].data[2] + gyro_data->bias_z; + + data[uncal_start + i].uncalibrated_gyro.bias[0] = gyro_data->bias_x; + data[uncal_start + i].uncalibrated_gyro.bias[1] = gyro_data->bias_y; + data[uncal_start + i].uncalibrated_gyro.bias[2] = gyro_data->bias_z; + } + event_count <<= 1; + } + sensor_info[sensor_info[s].pair_idx].report_pending = 0; + returned_events += event_count; + /* + * If the sample was deemed invalid or unreportable, + * e.g. had the same value as the previously reported + * value for a 'on change' sensor, silently drop it. + */ } - /* Keep a minimum time interval between poll operations */ - delta = (get_timestamp() - last_poll_exit_ts)/1000; + if (returned_events) + return returned_events; - if (delta > 0 && delta < POLL_MIN_INTERVAL) - usleep(POLL_MIN_INTERVAL - delta); +await_event: ALOGV("Awaiting sensor data\n"); - nfds = epoll_wait(poll_fd, ev, MAX_DEVICES, get_poll_time()); + nfds = epoll_wait(poll_fd, ev, MAX_DEVICES, get_poll_wait_timeout()); - last_poll_exit_ts = get_timestamp(); + if (nfds == -1) { + ALOGE("epoll_wait returned -1 (%s)\n", strerror(errno)); + goto await_event; + } ALOGV("%d fds signalled\n", nfds); - /* For each of the devices for which a report is available */ + /* For each of the signalled sources */ for (i=0; i max_supported_rate) { + new_sampling_rate = max_supported_rate; + } + + sensor_info[s].sampling_rate = new_sampling_rate; + + /* If we're dealing with a poll-mode sensor */ + if (!sensor_info[s].num_channels) { + /* Interrupt current sleep so the new sampling gets used */ + pthread_cond_signal(&thread_release_cond[s]); + return 0; + } + + sprintf(sysfs_path, SENSOR_SAMPLING_PATH, dev_num, prefix); + + if (sysfs_read_float(sysfs_path, &cur_sampling_rate) != -1) { + per_sensor_sampling_rate = 1; + per_device_sampling_rate = 0; + } else { + per_sensor_sampling_rate = 0; + + sprintf(sysfs_path, DEVICE_SAMPLING_PATH, dev_num); + + if (sysfs_read_float(sysfs_path, &cur_sampling_rate) != -1) + per_device_sampling_rate = 1; + else + per_device_sampling_rate = 0; + } + + if (!per_sensor_sampling_rate && !per_device_sampling_rate) { + ALOGE("No way to adjust sampling rate on sensor %d\n", s); + return -ENOSYS; + } + + /* Coordinate with others active sensors on the same device, if any */ + if (per_device_sampling_rate) + for (n=0; n new_sampling_rate) + new_sampling_rate= sensor_info[n].sampling_rate; + + /* 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 */ + if (new_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 > new_sampling_rate) { + new_sampling_rate = sr; + break; + } + + /* Skip digits */ + while (cursor[0] && !isspace(cursor[0])) + cursor++; + + /* Skip spaces */ + while (cursor[0] && isspace(cursor[0])) + cursor++; + } + } + + + if (max_supported_rate && + new_sampling_rate > max_supported_rate) { + new_sampling_rate = max_supported_rate; + } + + + /* If the desired rate is already active we're all set */ + if (new_sampling_rate == cur_sampling_rate) + return 0; + + ALOGI("Sensor %d sampling rate set to %g\n", s, new_sampling_rate); + + if (trig_sensors_per_dev[dev_num]) + enable_buffer(dev_num, 0); + + sysfs_write_float(sysfs_path, new_sampling_rate); + + /* Switch back to continuous sampling for accelerometer based games */ + if (is_fast_accelerometer(s) && sensor_info[s].selected_trigger != + sensor_info[s].init_trigger_name) + setup_trigger(s, sensor_info[s].init_trigger_name); + + if (trig_sensors_per_dev[dev_num]) + enable_buffer(dev_num, 1); + + return 0; } @@ -650,6 +1275,7 @@ int allocate_control_data (void) if (poll_fd == -1) { ALOGE("Can't create epoll instance for iio sensors!\n"); + return -1; } return poll_fd;