/*
* 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);
}