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