/*
 * Copyright (C) 2008-2015 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.
 */

#define LOG_TAG "CrosECSensor"

#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/input.h>
#include <math.h>
#include <poll.h>
#include <pthread.h>
#include <stdlib.h>

#include <utils/Atomic.h>
#include <utils/Log.h>

#include <hardware/sensors.h>

#include "cros_ec_sensors.h"
#include "sensors.h"

/*****************************************************************************/

/*****************************************************************************/

#define UNSET_FIELD -1
/*
 * TODO(gwendal): We should guess the fifo size, but
 * we need to issue an ioctl instead of just reading IIO sysfs.
 * EC will trigger an interrupt at 2/3 of its FIFO.
 */
#define CROS_EC_FIFO_SIZE (2048 * 2 / 3)

/* Name of iio devices, as reported by cros_ec_dev.c */
const char *cros_ec_sensor_names[] = {
    [CROS_EC_ACCEL] = "cros-ec-accel",
    [CROS_EC_GYRO] = "cros-ec-gyro",
    [CROS_EC_MAG] = "cros-ec-mag",
    [CROS_EC_PROX] = "cros-ec-prox-unused", // Prevent a match.
    [CROS_EC_LIGHT] = "cros-ec-light",
    [CROS_EC_ACTIVITY] = "cros-ec-activity",
    [CROS_EC_RING] = "cros-ec-ring",
};

/* Name of iio data names, as reported by IIO */
const char *cros_ec_iio_axis_names[] = {
    [CROS_EC_ACCEL] = "in_accel",
    [CROS_EC_GYRO] = "in_anglvel",
};

/*
 * cros_ec_activity is shared between sensors interface and
 * activity interface.
 * Activity has a separate module is not implemented yet
 */

/* Activities that belongs to the sensor interface */
const char *cros_ec_gesture_name[] = {
    [CROS_EC_SIGMO] = "in_activity_still_change_falling_en",
};

const int cros_ec_gesture_id[] = {
    [CROS_EC_SIGMO] = MOTIONSENSE_ACTIVITY_SIG_MOTION,
};


/*
 * Template for sensor_t structure return to motionservice.
 *
 * Some parameters (handle, range, resolution) are retreived
 * from IIO.
 */
static const struct sensor_t sSensorListTemplate[] = {
    [CROS_EC_ACCEL] = {
        .name =              "CrosEC Accelerometer",
        .vendor =            "Google",
        .version =           1,
        .handle =            UNSET_FIELD,
        .type =              SENSOR_TYPE_ACCELEROMETER,
        .maxRange =          UNSET_FIELD,
        .resolution =        UNSET_FIELD,
        .power =             0.18f,    /* Based on BMI160 */
        .minDelay =          5000,
        .fifoReservedEventCount =0,
        .fifoMaxEventCount = CROS_EC_FIFO_SIZE,
        .stringType =        SENSOR_STRING_TYPE_ACCELEROMETER,
        .requiredPermission =0,
        /*
         * BMI160 has a problem at 6.25Hz or less, FIFO not readable.
         * Works at 12.5Hz, so set maxDelay at 80ms
         */
        .maxDelay =          80000,
        .flags =             SENSOR_FLAG_CONTINUOUS_MODE,
        .reserved =          { 0 }
    },
    [CROS_EC_GYRO] = {
        .name =              "CrosEC Gyroscope",
        .vendor =            "Google",
        .version =           1,
        .handle =            UNSET_FIELD,
        .type =              SENSOR_TYPE_GYROSCOPE,
        .maxRange =          UNSET_FIELD,
        .resolution =        UNSET_FIELD,
        .power =             0.85f,
        .minDelay =          5000,
        .fifoReservedEventCount =0,
        .fifoMaxEventCount = CROS_EC_FIFO_SIZE,
        .stringType =        SENSOR_STRING_TYPE_GYROSCOPE,
        .requiredPermission =0,
        .maxDelay =          80000,
        .flags =             SENSOR_FLAG_CONTINUOUS_MODE,
        .reserved =          { 0 }
    },
    [CROS_EC_MAG] = {
        .name =              "CrosEC Compass",
        .vendor =            "Google",
        .version =           1,
        .handle =            UNSET_FIELD,
        .type =              SENSOR_TYPE_MAGNETIC_FIELD,
        .maxRange =          UNSET_FIELD,
        .resolution =        UNSET_FIELD,
        .power =             5.0f,  /* Based on BMM150 */
        /*
         * BMI150 uses repetition to reduce output noise.
         * Set ODR at no more than 25Hz.
         */
        .minDelay =          40000,
        .fifoReservedEventCount =0,
        .fifoMaxEventCount = CROS_EC_FIFO_SIZE,
        .stringType =        SENSOR_STRING_TYPE_MAGNETIC_FIELD,
        .requiredPermission =0,
        .maxDelay =          200000,
        .flags =             SENSOR_FLAG_CONTINUOUS_MODE,
        .reserved =          { 0 }
    },
    [CROS_EC_PROX] = {
        .name =              "CrosEC Proximity",
        .vendor =            "Google",
        .version =           1,
        .handle =            UNSET_FIELD,
        .type =              SENSOR_TYPE_PROXIMITY,
        .maxRange =          UNSET_FIELD,
        .resolution =        UNSET_FIELD,
        .power =             0.12f,  /* Based on Si1141 */
        .minDelay =          20000,
        .fifoReservedEventCount =0,
        .fifoMaxEventCount = CROS_EC_FIFO_SIZE,
        .stringType =        SENSOR_STRING_TYPE_PROXIMITY,
        .requiredPermission =0,
        /* Forced mode, can be long =10s */
        .maxDelay =          10000000,
        /* WAKE UP required by API */
        .flags =             SENSOR_FLAG_ON_CHANGE_MODE | SENSOR_FLAG_WAKE_UP,
        .reserved =          { 0 }
    },
    [CROS_EC_LIGHT] = {
        .name =              "CrosEC Light",
        .vendor =            "Google",
        .version =           1,
        .handle =            UNSET_FIELD,
        .type =              SENSOR_TYPE_LIGHT,
        .maxRange =          UNSET_FIELD,
        .resolution =        UNSET_FIELD,
        .power =             0.12f,  /* Based on Si1141 */
        .minDelay =          20000,
        .fifoReservedEventCount =0,
        .fifoMaxEventCount = CROS_EC_FIFO_SIZE,
        .stringType =        SENSOR_STRING_TYPE_LIGHT,
        .requiredPermission =0,
        /* Forced mode, can be long =9s */
        .maxDelay =          10000000,
        .flags =             SENSOR_FLAG_ON_CHANGE_MODE,
        .reserved =          { 0 }
    },
};

static const struct sensor_t sGestureListTemplate[] = {
    [CROS_EC_SIGMO] = {
        .name =              "CrosEC Significant Motion",
        .vendor =            "Google",
        .version =           1,
        .handle =            UNSET_FIELD,
        .type =              SENSOR_TYPE_SIGNIFICANT_MOTION,
        .maxRange =          1.0f,
        .resolution =        1.0f,
        .power =             0.18f,    /* Based on BMI160 */
        .minDelay =          -1,
        .fifoReservedEventCount =0,
        .fifoMaxEventCount = 0,
        .stringType =        SENSOR_STRING_TYPE_SIGNIFICANT_MOTION,
        .requiredPermission =0,
        .maxDelay =          0,
        .flags =             SENSOR_FLAG_ONE_SHOT_MODE | SENSOR_FLAG_WAKE_UP,
        .reserved =          { 0 }
    },
};

/* We only support the sensors in the lid */
static const char *cros_ec_location = "lid";

static int Stotal_sensor_count_ = 0;
static int Stotal_max_sensor_handle_ = 0;
static int Stotal_max_gesture_handle_ = 0;

static struct sensor_t *Ssensor_list_ = NULL;

struct cros_ec_sensor_info *Ssensor_info_ = NULL;
struct cros_ec_gesture_info *Sgesture_info_ = NULL;

static int cros_ec_open_sensors(const struct hw_module_t *module,
                                const char *id,
                                struct hw_device_t **device);
/*
 * cros_ec_get_sensors_list: entry point that returns the list
 * of sensors.
 *
 * At first invocation, build the list from Ssensor_info_,
 * then keep returning the same list.
 *
 * The expected design is a hardcoded list of sensors.
 * Therefore we don't have access to
 */
static int cros_ec_get_sensors_list(struct sensors_module_t*,
        struct sensor_t const** list)
{
    ALOGD("counting sensors: count %d: sensor_list_ %p\n",
            Stotal_sensor_count_, Ssensor_list_);

    if (Stotal_sensor_count_ != 0) {
        *list = Ssensor_list_;
        return Stotal_sensor_count_;
    }

    for (int i = 0 ; i < Stotal_max_sensor_handle_ ; i++) {
        if (Ssensor_info_[i].device_name == NULL)
            continue;

        Stotal_sensor_count_++;
        Ssensor_list_ = (sensor_t*)realloc(Ssensor_list_,
                Stotal_sensor_count_ * sizeof(sensor_t));
        if (Ssensor_list_ == NULL) {
            ALOGI("Unable to allocate Ssensor_list_\n");
            return 0;
        }
        sensor_t *sensor_data;
        sensor_data = &Ssensor_info_[i].sensor_data;
        memcpy(&Ssensor_list_[Stotal_sensor_count_ - 1], sensor_data,
               sizeof(sensor_t));
    }

    for (int i = 0 ; i < Stotal_max_gesture_handle_ ; i++) {
        if (Sgesture_info_[i].device_name == NULL)
            continue;

        Stotal_sensor_count_++;
        Ssensor_list_ = (sensor_t*)realloc(Ssensor_list_,
                Stotal_sensor_count_ * sizeof(sensor_t));
        if (Ssensor_list_ == NULL) {
            ALOGI("Unable to allocate Ssensor_list_\n");
            return 0;
        }
        sensor_t *sensor_data;
        sensor_data = &Sgesture_info_[i].sensor_data;
        memcpy(&Ssensor_list_[Stotal_sensor_count_ - 1], sensor_data,
               sizeof(sensor_t));
    }
    *list = Ssensor_list_;
    return Stotal_sensor_count_;
}

/*
 * cros_ec_get_gesture_names: Build list of gestures from IIO
 *
 * Looking into the cros_ec_activity sensors, looks for events
 * the sensorserivces are managing.
 *
 * We assume only one cros_ec activity sensor.
 */
static int cros_ec_get_gesture_names(const char *sensor_name)
{
    char path_device[IIO_MAX_DEVICE_NAME_LENGTH];
    strcpy(path_device, IIO_DIR);
    strcat(path_device, sensor_name);
    strcat(path_device, "/events");
    DIR *events_dir;
    ALOGD("looking at %s:", path_device);
    events_dir = opendir(path_device);
    if (events_dir == NULL)
        return -ENODEV;
    const struct dirent *ent_event;
    while (ent_event = readdir(events_dir), ent_event != NULL) {
        int gesture;
        for (gesture = 0; gesture < CROS_EC_MAX_GESTURE; gesture++) {
            if (!strcmp(ent_event->d_name, cros_ec_gesture_name[gesture]))
                break;
        }
        if (gesture == CROS_EC_MAX_GESTURE)
            continue;
        int gesture_id = cros_ec_gesture_id[gesture];
        if (Stotal_max_gesture_handle_ <= gesture_id) {
            Sgesture_info_ = (cros_ec_gesture_info*)realloc(Sgesture_info_,
                    (gesture_id + 1) * sizeof(cros_ec_gesture_info));
            if (Sgesture_info_ == NULL)
                return -ENOMEM;
            memset(&Sgesture_info_[Stotal_max_gesture_handle_], 0,
                    (gesture_id + 1 - Stotal_max_gesture_handle_) *
                    sizeof(cros_ec_gesture_info));
            Stotal_max_gesture_handle_ = gesture_id + 1;
        }
        cros_ec_gesture_info *gesture_info = &Sgesture_info_[gesture_id];
        gesture_info->device_name = strdup(sensor_name);
        gesture_info->enable_entry = cros_ec_gesture_name[gesture];

        sensor_t *sensor_data;
        sensor_data = &gesture_info->sensor_data;
        memcpy(sensor_data, &sGestureListTemplate[gesture], sizeof(sensor_t));
        sensor_data->handle = CROS_EC_MAX_PHYSICAL_SENSOR + gesture_id;

        ALOGD("new gesture '%s' on device '%s' : handle: %d\n",
              gesture_info->enable_entry, gesture_info->device_name, gesture_id);
    }
    closedir(events_dir);
    return 0;
}

/*
 * cros_ec_calibrate_3d_sensor: calibrate Accel or Gyro.
 *
 * In factory, calibration data is in VPD.
 * It is available from user space by reading /sys/firmware/vpd/ro/<Key>.
 * Key names are similar to iio: <type>_<axis>_calibbias,
 * when type is in_accel or in_anglvel and axis is x,y, or z.
 */
static int cros_ec_calibrate_3d_sensor(int sensor_type, const char *device_name)
{
    const char vpd_path[] = "/sys/firmware/vpd/ro";
    char calib_value[MAX_AXIS][20];
    char calib_key[MAX_AXIS][IIO_MAX_NAME_LENGTH];
    bool calib_data_valid = true;

    for (int i = X ; i < MAX_AXIS; i++) {
        snprintf(calib_key[i], sizeof(calib_key[i]), "%s_%c_calibbias",
                cros_ec_iio_axis_names[sensor_type], 'x' + i);
    }
    for (int i = X ; i < MAX_AXIS; i++) {
        if (cros_ec_sysfs_get_attr(vpd_path, calib_key[i], calib_value[i])) {
            ALOGI("Calibration key %s missing.\n", calib_key[i]);
            calib_data_valid = false;
            break;
        }
    }
    if (calib_data_valid && sensor_type == CROS_EC_ACCEL) {
        for (int i = X ; i < MAX_AXIS; i++) {
            /*
             * Workaround for invalid calibration values obveserved on several
             * devices (b/26927000). If the value seems bogus, ignore the whole
             * calibration.
             * If one calibration axis is greater than 2 m/s^2, ignore.
             */
            int value = atoi(calib_value[i]);
            if (abs(value) > (2 * 1024 * 100 / 981)) {
                ALOGE("Calibration data invalid on axis %d: %d\n", i, value);
                calib_data_valid = false;
                break;
            }
        }
    }

    for (int i = X ; i < MAX_AXIS; i++) {
        const char *value = (calib_data_valid ? calib_value[i] : "0");
        if (cros_ec_sysfs_set_input_attr(device_name, calib_key[i],
                    value, strlen(value))) {
            ALOGE("Writing bias %s to %s for device %s failed.\n",
                    calib_key[i], value, device_name);
        }
    }
    return 0;
}

/*
 * cros_ec_get_sensors_names: Build list of sensors from IIO
 *
 * Scanning /sys/iio/devices, finds all the sensors managed by the EC.
 *
 * Fill Ssensor_info_ global structure.
 * ring_device_name: name of iio ring buffer. We
 *   will open /dev/<ring_device_name> later
 * ring_trigger_name: Name of hardware trigger for setting the
 *   ring buffer producer side.
 */
static int cros_ec_get_sensors_names(char **ring_device_name,
                                     char **ring_trigger_name)
{
    /*
     * If Ssensor_info_ is valid, we don't want to open
     * the same device twice.
     */
    if (Stotal_max_sensor_handle_ != 0)
        return -EINVAL;

    *ring_device_name = NULL;
    *ring_trigger_name = NULL;

    DIR *iio_dir;
    iio_dir = opendir(IIO_DIR);
    if (iio_dir == NULL) {
        return -ENODEV;
    }
    const struct dirent *ent_device;
    while (ent_device = readdir(iio_dir), ent_device != NULL) {
        /* Find the iio directory with the sensor definition */
        if (ent_device->d_type != DT_LNK)
            continue;
        char path_device[IIO_MAX_DEVICE_NAME_LENGTH];
        strcpy(path_device, IIO_DIR);
        strcat(path_device, ent_device->d_name);

        char dev_name[IIO_MAX_NAME_LENGTH + 1];
        if (cros_ec_sysfs_get_attr(path_device, "name", dev_name))
            continue;

        for (int i = CROS_EC_ACCEL; i < CROS_EC_RING; ++i) {
            /* We assume only one sensor hub per device.
             * Otherwise we need to look at the symlink and connect the 2:
             * iio:device0 ->
             *  ../../../devices/7000c400.i2c/i2c-1/1-001e/cros-ec-dev.0/
             *  cros-ec-accel.0/iio:device0
             * and
             * ...
             * iio:device1 ->
             *  ../../../devices/7000c400.i2c/i2c-1/1-001e/cros-ec-dev.0/
             *  cros-ec-ring.0/iio:device1
             */
            if (!strcmp(cros_ec_sensor_names[i], dev_name)) {
                /*
                 * First check if the device belongs to the lid.
                 * (base is keyboard)
                 */
                char loc[IIO_MAX_NAME_LENGTH + 1];
                if (cros_ec_sysfs_get_attr(path_device, "location", loc))
                    continue;
                if (strcmp(cros_ec_location, loc))
                    continue;

                char dev_id[40];
                if (cros_ec_sysfs_get_attr(path_device, "id", dev_id))
                    continue;
                int sensor_id = atoi(dev_id);
                if (Stotal_max_sensor_handle_ <= sensor_id) {
                    Ssensor_info_ = (cros_ec_sensor_info*)realloc(Ssensor_info_,
                            (sensor_id + 1) * sizeof(cros_ec_sensor_info));
                    if (Ssensor_info_ == NULL) {
                        closedir(iio_dir);
                        return -ENOMEM;
                    }
                    memset(&Ssensor_info_[Stotal_max_sensor_handle_], 0,
                            (sensor_id + 1 - Stotal_max_sensor_handle_) *
                            sizeof(cros_ec_sensor_info));
                    Stotal_max_sensor_handle_ = sensor_id + 1;
                }

                struct cros_ec_sensor_info *sensor_info = &Ssensor_info_[sensor_id];
                sensor_info->type = static_cast<enum cros_ec_sensor_device>(i);

                if (i == CROS_EC_ACTIVITY) {
                    cros_ec_get_gesture_names(ent_device->d_name);
                } else {
                    sensor_info->device_name = strdup(ent_device->d_name);
                    char dev_scale[40];
                    if (cros_ec_sysfs_get_attr(path_device, "scale", dev_scale)) {
                        ALOGE("Unable to read scale\n");
                        continue;
                    }
                    double scale = atof(dev_scale);

                    sensor_t *sensor_data = &sensor_info->sensor_data;
                    memcpy(sensor_data, &sSensorListTemplate[i], sizeof(sensor_t));
                    sensor_data->handle = sensor_id;

                    if (sensor_data->type == SENSOR_TYPE_MAGNETIC_FIELD)
                        /* iio units are in Gauss, not micro Telsa */
                        scale *= 100;
                    if (sensor_data->type == SENSOR_TYPE_PROXIMITY) {
                        /*
                         * Proximity does not detect anything beyond 3m.
                         */
                        sensor_data->resolution = 1;
                        sensor_data->maxRange = 300;
                    } else {
                        sensor_data->resolution = scale;
                        sensor_data->maxRange = scale * (1 << 15);
                    }

                    if (sensor_data->type == SENSOR_TYPE_ACCELEROMETER ||
                        sensor_data->type == SENSOR_TYPE_GYROSCOPE) {
                        /* There is an assumption by the calibration code that there is
                         * only one type of sensors per device.
                         * If it needs to change, we will add "location" sysfs key
                         * to find the proper calibration data.
                         */
                        cros_ec_calibrate_3d_sensor(i, sensor_info->device_name);
                    }

                    ALOGD("new dev '%s' handle: %d\n",
                            sensor_info->device_name, sensor_id);
                }
                break;
            }
        }

        if (!strcmp(cros_ec_sensor_names[CROS_EC_RING], dev_name)) {
            *ring_device_name = strdup(ent_device->d_name);
        }

        char trigger_name[80];
        strcpy(trigger_name, cros_ec_sensor_names[CROS_EC_RING]);
        strcat(trigger_name, "-trigger");
        if (!strncmp(trigger_name, dev_name, strlen(trigger_name))) {
            *ring_trigger_name = strdup(dev_name);
            ALOGD("new trigger '%s' \n", *ring_trigger_name);
            continue;
        }
    }
    closedir(iio_dir);

    if (*ring_device_name == NULL || *ring_trigger_name == NULL)
        return -ENODEV;

    return Stotal_max_sensor_handle_ ? 0 : -ENODEV;
}

static struct hw_module_methods_t cros_ec_sensors_methods = {
    .open = cros_ec_open_sensors,
};

struct sensors_module_t HAL_MODULE_INFO_SYM = {
    .common ={
        .tag =HARDWARE_MODULE_TAG,
        .version_major =1,
        .version_minor =0,
        .id =SENSORS_HARDWARE_MODULE_ID,
        .name ="CrosEC sensor hub module",
        .author ="Google",
        .methods =&cros_ec_sensors_methods,
        .dso =NULL,
        .reserved ={ 0 },
    },
    .get_sensors_list =cros_ec_get_sensors_list,
    .set_operation_mode =NULL,
};

/*****************************************************************************/
cros_ec_sensors_poll_context_t::cros_ec_sensors_poll_context_t(
        const struct hw_module_t *module,
        const char *ring_device_name,
        const char *ring_trigger_name)
{
    memset(&device, 0, sizeof(sensors_poll_device_1_t));

    device.common.tag      = HARDWARE_DEVICE_TAG;
    device.common.version  = SENSORS_DEVICE_API_VERSION_1_3;
    device.common.module   = const_cast<hw_module_t *>(module);
    device.common.close    = wrapper_close;
    device.activate        = wrapper_activate;
    device.setDelay        = wrapper_setDelay;
    device.poll            = wrapper_poll;

    // Batch processing
    device.batch           = wrapper_batch;
    device.flush           = wrapper_flush;

    /*
     * One more time, assume only one sensor hub in the system.
     * Find the iio:deviceX with name "cros_ec_ring"
     * Open /dev/iio:deviceX, enable buffer.
     */
    mSensor = new CrosECSensor(
        Ssensor_info_, Stotal_max_sensor_handle_,
        Sgesture_info_, Stotal_max_gesture_handle_,
        ring_device_name, ring_trigger_name);

    mPollFds[crosEcRingFd].fd = mSensor->getFd();
    mPollFds[crosEcRingFd].events = POLLIN;
    mPollFds[crosEcRingFd].revents = 0;

    int wakeFds[2];
    int result = pipe(wakeFds);
    ALOGE_IF(result < 0, "error creating wake pipe (%s)", strerror(errno));
    fcntl(wakeFds[0], F_SETFL, O_NONBLOCK);
    fcntl(wakeFds[1], F_SETFL, O_NONBLOCK);
    mWritePipeFd = wakeFds[1];

    mPollFds[crosEcWakeFd].fd = wakeFds[0];
    mPollFds[crosEcWakeFd].events = POLLIN;
    mPollFds[crosEcWakeFd].revents = 0;
}

cros_ec_sensors_poll_context_t::~cros_ec_sensors_poll_context_t() {
    delete mSensor;
    close(mPollFds[crosEcWakeFd].fd);
    close(mWritePipeFd);
}

int cros_ec_sensors_poll_context_t::activate(int handle, int enabled) {
    int err = mSensor->activate(handle, enabled);

    if (enabled && !err) {
        const char wakeMessage(WAKE_MESSAGE);
        int result = write(mWritePipeFd, &wakeMessage, 1);
        ALOGE_IF(result<0, "error sending wake message (%s)", strerror(errno));
    }
    return err;
}

int cros_ec_sensors_poll_context_t::setDelay(int /* handle */,
                                             int64_t /* ns */) {
    /* No supported */
    return 0;
}

int cros_ec_sensors_poll_context_t::pollEvents(sensors_event_t* data, int count)
{
    int nbEvents = 0;
    int n = 0;
    do {
        // see if we have some leftover from the last poll()
        if (mPollFds[crosEcRingFd].revents & POLLIN) {
            int nb = mSensor->readEvents(data, count);
            if (nb < count) {
                // no more data for this sensor
                mPollFds[crosEcRingFd].revents = 0;
            }
            count -= nb;
            nbEvents += nb;
            data += nb;
        }

        if (count) {
            // we still have some room, so try to see if we can get
            // some events immediately or just wait if we don't have
            // anything to return
            do {
                TEMP_FAILURE_RETRY(n = poll(mPollFds, numFds,
                                            nbEvents ? 0 : -1));
            } while (n < 0 && errno == EINTR);
            if (n < 0) {
                ALOGE("poll() failed (%s)", strerror(errno));
                return -errno;
            }
            if (mPollFds[crosEcWakeFd].revents & POLLIN) {
                char msg(WAKE_MESSAGE);
                int result = read(mPollFds[crosEcWakeFd].fd, &msg, 1);
                ALOGE_IF(result < 0,
                         "error reading from wake pipe (%s)", strerror(errno));
                ALOGE_IF(msg != WAKE_MESSAGE,
                         "unknown message on wake queue (0x%02x)", int(msg));
                mPollFds[crosEcWakeFd].revents = 0;
            }
        }
        // if we have events and space, go read them
    } while (n && count);
    return nbEvents;
}

int cros_ec_sensors_poll_context_t::batch(int handle, int /* flags */,
        int64_t sampling_period_ns,
        int64_t max_report_latency_ns)
{
    return mSensor->batch(handle, sampling_period_ns,
                          max_report_latency_ns);
}

int cros_ec_sensors_poll_context_t::flush(int handle)
{
    return mSensor->flush(handle);
}


/*****************************************************************************/

int cros_ec_sensors_poll_context_t::wrapper_close(struct hw_device_t *dev)
{
    cros_ec_sensors_poll_context_t *ctx = reinterpret_cast<cros_ec_sensors_poll_context_t *>(dev);
    if (ctx) {
        delete ctx;
    }
    if (Stotal_max_sensor_handle_ != 0) {
        free(Ssensor_info_);
        Stotal_max_sensor_handle_ = 0;
        free(Sgesture_info_);
    }
    return 0;
}

int cros_ec_sensors_poll_context_t::wrapper_activate(struct sensors_poll_device_t *dev,
        int handle, int enabled)
{
    cros_ec_sensors_poll_context_t *ctx = reinterpret_cast<cros_ec_sensors_poll_context_t *>(dev);
    return ctx->activate(handle, enabled);
}

int cros_ec_sensors_poll_context_t::wrapper_setDelay(struct sensors_poll_device_t *dev,
        int handle, int64_t ns)
{
    cros_ec_sensors_poll_context_t *ctx = reinterpret_cast<cros_ec_sensors_poll_context_t *>(dev);
    return ctx->setDelay(handle, ns);
}

int cros_ec_sensors_poll_context_t::wrapper_poll(struct sensors_poll_device_t *dev,
        sensors_event_t* data, int count)
{
    cros_ec_sensors_poll_context_t *ctx = reinterpret_cast<cros_ec_sensors_poll_context_t *>(dev);
    return ctx->pollEvents(data, count);
}

int cros_ec_sensors_poll_context_t::wrapper_batch(struct sensors_poll_device_1 *dev,
        int handle, int flags, int64_t period_ns, int64_t timeout)
{
    cros_ec_sensors_poll_context_t *ctx = reinterpret_cast<cros_ec_sensors_poll_context_t *>(dev);
    return ctx->batch(handle, flags, period_ns, timeout);
}

int cros_ec_sensors_poll_context_t::wrapper_flush(struct sensors_poll_device_1 *dev,
        int handle)
{
    cros_ec_sensors_poll_context_t *ctx = reinterpret_cast<cros_ec_sensors_poll_context_t *>(dev);
    return ctx->flush(handle);
}

/*****************************************************************************/

/*
 * cros_ec_open_sensors: open entry point.
 *
 * Call by sensor service via helper function:  sensors_open()
 *
 * Create a device the service will use for event polling.
 * Assume one open/one close.
 *
 * Later, sensorservice will use device with an handle to access
 * a particular sensor.
 */
static int cros_ec_open_sensors(
        const struct hw_module_t* module, const char*,
        struct hw_device_t** device)
{
    char *ring_device_name = NULL, *ring_trigger_name = NULL;
    int err;
    err = cros_ec_get_sensors_names(&ring_device_name, &ring_trigger_name);
    if (err == 0) {
        cros_ec_sensors_poll_context_t *dev = new cros_ec_sensors_poll_context_t(
                module, ring_device_name, ring_trigger_name);

        *device = &dev->device.common;
    }
    free(ring_device_name);
    free(ring_trigger_name);
    return err;
}