C++程序  |  444行  |  13.15 KB

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