| /* |
| * Copyright (C) 2008-2014 The Android Open Source Project |
| * |
| * 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 <ctype.h> |
| #include <dirent.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <inttypes.h> |
| #include <math.h> |
| #include <poll.h> |
| #include <pthread.h> |
| #include <stdlib.h> |
| #include <sys/select.h> |
| #include <unistd.h> |
| |
| #define LOG_TAG "CrosECSensor" |
| #include <cutils/log.h> |
| #include <cutils/properties.h> |
| #include <utils/Timers.h> |
| |
| #include "cros_ec_sensors.h" |
| |
| |
| /*****************************************************************************/ |
| static int max(int a, int b) { |
| return (a > b) ? a : b; |
| } |
| static int min(int a, int b) { |
| return (a < b) ? a : b; |
| } |
| |
| /* |
| * Constructor. |
| * |
| * Setup and open the ring buffer. |
| */ |
| CrosECSensor::CrosECSensor( |
| struct cros_ec_sensor_info *sensor_info, |
| size_t sensor_nb, |
| struct cros_ec_gesture_info *gesture_info, |
| size_t gesture_nb, |
| const char *ring_device_name, |
| const char *trigger_name) |
| : mSensorInfo(sensor_info), |
| mSensorNb(sensor_nb), |
| mGestureInfo(gesture_info), |
| mGestureNb(gesture_nb) |
| { |
| char ring_buffer_name[IIO_MAX_NAME_LENGTH] = "/dev/"; |
| |
| strcat(ring_buffer_name, ring_device_name); |
| mDataFd = open(ring_buffer_name, O_RDONLY); |
| if (mDataFd < 0) { |
| ALOGE("open file '%s' failed: %s\n", |
| ring_buffer_name, strerror(errno)); |
| } |
| |
| strcpy(mRingPath, ring_device_name); |
| |
| /* Be sure the buffer is disbabled before altering parameters */ |
| if (cros_ec_sysfs_set_input_attr_by_int(mRingPath, "buffer/enable", 0) < 0) { |
| ALOGE("disable IIO buffer failed: %s\n", strerror(errno)); |
| return; |
| } |
| if (cros_ec_sysfs_set_input_attr(mRingPath, "trigger/current_trigger", |
| trigger_name, strlen(trigger_name))) { |
| ALOGE("Unable to set trigger name: %s\n", strerror(errno)); |
| return; |
| } |
| if (cros_ec_sysfs_set_input_attr_by_int(mRingPath, "buffer/length", |
| IIO_MAX_BUFF_SIZE) < 0) { |
| ALOGE("set IIO buffer length (%d) failed: %s\n", |
| IIO_MAX_BUFF_SIZE, strerror(errno)); |
| } |
| if (cros_ec_sysfs_set_input_attr_by_int(mRingPath, "buffer/enable", 1) < 0) { |
| ALOGE("enable IIO buffer failed: %s\n", |
| strerror(errno)); |
| return; |
| } |
| } |
| |
| CrosECSensor::~CrosECSensor() { |
| /* Silence all the sensors, so that we can stop the buffer */ |
| for (size_t i = 0 ; i < mSensorNb ; i++) { |
| if (mSensorInfo[i].device_name == NULL) |
| continue; |
| activate(i, 0); |
| } |
| for (size_t i = 0 ; i < mGestureNb ; i++) { |
| if (mGestureInfo[i].device_name == NULL) |
| continue; |
| activate(i + CROS_EC_MAX_PHYSICAL_SENSOR, 0); |
| } |
| if (cros_ec_sysfs_set_input_attr_by_int(mRingPath, "buffer/enable", 0) < 0) { |
| ALOGE("disable IIO buffer failed: %s\n", strerror(errno)); |
| return; |
| } |
| close(mDataFd); |
| } |
| |
| /* |
| * getFd: retrieve the ring file descriptor. |
| * |
| * Needed for CrosECSensor creator to listen to the buffer. |
| */ |
| int CrosECSensor::getFd(void) |
| { |
| return mDataFd; |
| } |
| |
| /* |
| * flush: Flush entry point. |
| * |
| * Issue the flush for a particular sensor to the EC via iio. |
| */ |
| int CrosECSensor::flush(int handle) |
| { |
| if (handle >= CROS_EC_MAX_PHYSICAL_SENSOR) { |
| struct cros_ec_gesture_info* info = &mGestureInfo[handle - CROS_EC_MAX_PHYSICAL_SENSOR]; |
| if (info->sensor_data.flags & SENSOR_FLAG_ONE_SHOT_MODE) |
| return -EINVAL; |
| /* not expected, current gestures are all one-shot. */ |
| return -EINVAL; |
| } else { |
| struct cros_ec_sensor_info *info = &mSensorInfo[handle]; |
| |
| if (!info->enabled) |
| return -EINVAL; |
| |
| return cros_ec_sysfs_set_input_attr_by_int(info->device_name, "flush", 1); |
| } |
| } |
| |
| /* |
| * activate: Activate entry point. |
| * |
| * When enabled set the sensor frequency. If not enabled, set |
| * the sensor in suspend mode by setting the frequency to 0. |
| */ |
| int CrosECSensor::activate(int handle, int enabled) |
| { |
| int err; |
| if (handle < CROS_EC_MAX_PHYSICAL_SENSOR) { |
| struct cros_ec_sensor_info *info = &mSensorInfo[handle]; |
| /* |
| * Frequency is in mHz, sampling period in ns, use 10^(9 + 3) |
| * coefficient. |
| */ |
| long frequency = enabled ? 1e12 / info->sampling_period_ns : 0; |
| |
| err = cros_ec_sysfs_set_input_attr_by_int(info->device_name, |
| "frequency", frequency); |
| if (err) |
| return err; |
| |
| long ec_period = nanoseconds_to_milliseconds(info->max_report_latency_ns); |
| |
| if (enabled) |
| ec_period = min(CROS_EC_MAX_SAMPLING_PERIOD, ec_period); |
| else |
| ec_period = 0; |
| |
| /* Sampling is encoded on a 16bit so, so the maximal period is ~65s. */ |
| err = cros_ec_sysfs_set_input_attr_by_int( |
| info->device_name, "sampling_frequency", ec_period); |
| if (!err) |
| info->enabled = enabled; |
| } else { |
| struct cros_ec_gesture_info* info = &mGestureInfo[handle - CROS_EC_MAX_PHYSICAL_SENSOR]; |
| char attr[PATH_MAX] = "events/"; |
| strcat(attr, info->enable_entry); |
| err = cros_ec_sysfs_set_input_attr_by_int(info->device_name, attr, enabled); |
| if (!err) |
| info->enabled = enabled; |
| } |
| |
| return err; |
| } |
| |
| /* |
| * batch: Batch entry point. |
| * |
| * Set the EC sampling frequency. Check boundaries to prevent polling too fast. |
| */ |
| int CrosECSensor::batch(int handle, |
| int64_t sampling_period_ns, |
| int64_t max_report_latency_ns) |
| { |
| if (handle < CROS_EC_MAX_PHYSICAL_SENSOR) { |
| struct cros_ec_sensor_info *info = &mSensorInfo[handle]; |
| |
| info->max_report_latency_ns = max_report_latency_ns; |
| |
| if (nanoseconds_to_microseconds(sampling_period_ns) > |
| info->sensor_data.maxDelay) |
| info->sampling_period_ns = microseconds_to_nanoseconds(info->sensor_data.maxDelay); |
| else if (nanoseconds_to_microseconds(sampling_period_ns) < |
| info->sensor_data.minDelay) |
| info->sampling_period_ns = microseconds_to_nanoseconds(info->sensor_data.minDelay); |
| else |
| info->sampling_period_ns = sampling_period_ns; |
| |
| /* |
| * Note that the sensor hub limit minimal sampling frequency at few ms. |
| * Which is good, because HAL shold not ask for polling sensor at |
| * more than the sampling period, set in sensor_t. |
| */ |
| if (info->max_report_latency_ns < max(sampling_period_ns, info->sampling_period_ns)) { |
| /* |
| * We have to report an event as soon as available. |
| * Set polling frequency as low as sampling frequency |
| */ |
| info->max_report_latency_ns = max(sampling_period_ns, info->sampling_period_ns); |
| } |
| |
| |
| /* Call activate to change the paramters if necessary */ |
| return activate(handle, info->enabled); |
| } else { |
| return 0; |
| } |
| } |
| |
| /* |
| * readEvents: Read events from the iio ring buffer. |
| * |
| * data: where to put the events. |
| * count: maximal number of events to read from iio. |
| * If iio indicates no more events are available, return. |
| */ |
| int CrosECSensor::readEvents(sensors_event_t* data, int count) |
| { |
| int rc; |
| |
| if (count < 1) { |
| return -EINVAL; |
| } |
| |
| /* |
| * Do a single read to collects all pending events. |
| * up to what poll caller can handle. |
| */ |
| rc = read(mDataFd, mEvents, sizeof(cros_ec_event) * count); |
| if (rc < 0) { |
| ALOGE("rc %d while reading ring\n", rc); |
| return rc; |
| } |
| if (rc % sizeof(cros_ec_event) != 0) { |
| ALOGE("Incomplete event while reading ring: %d\n", rc); |
| return -EINVAL; |
| } |
| |
| int nb_events = rc / sizeof(cros_ec_event); |
| int data_events = 0; |
| for (int i = 0; i < nb_events; i++) { |
| rc = processEvent(data, &mEvents[i]); |
| if (rc == 0) { |
| data++; |
| data_events++; |
| } |
| } |
| |
| return data_events; |
| } |
| |
| /* |
| * processEvent: |
| * |
| * Internal function to translate an event from the iio ring |
| * buffer into a sensors_event_t. |
| * |
| * Support flush meta event and regular events. |
| */ |
| int CrosECSensor::processEvent(sensors_event_t* data, const cros_ec_event *event) |
| { |
| if (event->flags & CROS_EC_EVENT_FLUSH_FLAG) { |
| data->version = META_DATA_VERSION; |
| data->sensor = 0; |
| data->type = SENSOR_TYPE_META_DATA; |
| data->reserved0 = 0; |
| data->timestamp = 0; |
| data->meta_data.what = META_DATA_FLUSH_COMPLETE; |
| data->meta_data.sensor = event->sensor_id; |
| return 0; |
| } |
| |
| if (event->sensor_id >= mSensorNb) { |
| return -EINVAL; |
| } |
| struct cros_ec_sensor_info *info = &mSensorInfo[event->sensor_id]; |
| |
| if (info->type == CROS_EC_ACTIVITY) { |
| ALOGI("Activity: %d - state: %d\n", event->activity, event->state); |
| if (event->activity >= mGestureNb) |
| return -ENOKEY; |
| |
| struct cros_ec_gesture_info *gesture = &mGestureInfo[event->activity]; |
| if (!gesture->enabled) |
| return -ENOKEY; |
| |
| data->version = sizeof(sensors_event_t); |
| data->sensor = CROS_EC_MAX_PHYSICAL_SENSOR + event->activity; |
| data->type = gesture->sensor_data.type; |
| |
| /* |
| * bootime Timestamp coming from the kernel are not reliable when |
| * the system resume: very early, the sleep delay has not yet been added. |
| * Use the current time, not the kernel timestamp. |
| * chrome-os-partner:46724 |
| */ |
| data->timestamp = systemTime(SYSTEM_TIME_BOOTTIME); |
| data->data[0] = (float)event->state; |
| |
| if (gesture->sensor_data.flags & SENSOR_FLAG_ONE_SHOT_MODE) |
| gesture->enabled = 0; |
| } else { |
| |
| /* |
| * The sensor hub can send data even if the sensor is not set up. |
| * workaround it unitl b/23238991 is fixed. |
| */ |
| if (!info->enabled) |
| return -ENOKEY; |
| |
| data->version = sizeof(sensors_event_t); |
| data->sensor = event->sensor_id; |
| data->type = info->sensor_data.type; |
| data->timestamp = event->timestamp; |
| data->acceleration.status = SENSOR_STATUS_ACCURACY_LOW; |
| |
| /* |
| * Even for sensor with one axis (light, proxmity), be sure to write |
| * the other vectors. EC 0s them out. |
| */ |
| float d; |
| for (int i = X ; i < MAX_AXIS; i++) { |
| switch (info->sensor_data.type) { |
| case SENSOR_TYPE_ACCELEROMETER: |
| case SENSOR_TYPE_GYROSCOPE: |
| case SENSOR_TYPE_MAGNETIC_FIELD: |
| d = event->vector[i]; |
| break; |
| case SENSOR_TYPE_LIGHT: |
| case SENSOR_TYPE_PROXIMITY: |
| d = (uint16_t)event->vector[i]; |
| break; |
| default: |
| return -EINVAL; |
| } |
| data->acceleration.v[i] = |
| d * mSensorInfo[event->sensor_id].sensor_data.resolution; |
| } |
| } |
| return 0; |
| } |
| |
| |
| /* |
| * cros_ec_sysfs_get_attr: Helper function to read sysfs attributes. |
| * |
| * path: the path of the device. |
| * attr: attribute to read (path/attr) |
| * output: where to put the string read. |
| */ |
| int cros_ec_sysfs_get_attr(const char *path, const char *attr, char *output) |
| { |
| char name[IIO_MAX_DEVICE_NAME_LENGTH + 10]; |
| strcpy(name, path); |
| strcat(name, "/"); |
| strcat(name, attr); |
| int fd = open(name, O_RDONLY); |
| if (fd < 0) { |
| ALOGE("Unable to read %s\n", name); |
| return -errno; |
| } |
| int size = read(fd, output, IIO_MAX_NAME_LENGTH); |
| close(fd); |
| if (size == 0) |
| return -EINVAL; |
| if (output[size - 1] == '\n') |
| output[size - 1] = 0; |
| else |
| output[size] = 0; |
| return 0; |
| } |
| |
| /* |
| * cros_ec_sysfs_set_input_attr: Helper function to write a sysfs attribute. |
| */ |
| int cros_ec_sysfs_set_input_attr(const char *path, const char *attr, |
| const char *value, size_t len) |
| { |
| char fname[PATH_MAX]; |
| int fd; |
| int rc; |
| |
| snprintf(fname, sizeof(fname), "%s%s/%s", IIO_DIR, path, attr); |
| fname[sizeof(fname) - 1] = '\0'; |
| |
| fd = open(fname, O_WRONLY); |
| if (fd < 0) { |
| ALOGE("%s: fname = %s, fd = %d, failed: %s\n", __func__, |
| fname, fd, strerror(errno)); |
| return -EACCES; |
| } |
| |
| rc = write(fd, value, (size_t)len); |
| if (rc < 0) { |
| ALOGE("%s: write failed: fd = %d, rc = %d, strerr = %s\n", __func__, |
| fd, rc, strerror(errno)); |
| ALOGE("fname = %s, value = %s\n", fname, value); |
| } |
| |
| close(fd); |
| |
| return rc < 0 ? rc : 0; |
| } |
| |
| int cros_ec_sysfs_set_input_attr_by_int(const char *path, |
| const char *attr, int value) |
| { |
| char buf[INT32_CHAR_LEN]; |
| |
| size_t n = snprintf(buf, sizeof(buf), "%d", value); |
| if (n > sizeof(buf)) { |
| return -1; |
| } |
| |
| return cros_ec_sysfs_set_input_attr(path, attr, buf, n); |
| } |