From 58a34aa03efa2aa181fb8ff190dfa1d8b50d46e5 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Thu, 2 Jan 2014 22:37:45 +0100 Subject: Piranha Sensors Signed-off-by: Paul Kocialkowski --- BoardConfigCommon.mk | 3 - p51xx-common.mk | 3 + sensors/Android.mk | 73 +++++ sensors/bh1721.c | 230 ++++++++++++++++ sensors/bma250.c | 260 ++++++++++++++++++ sensors/geomagneticd/geomagneticd.c | 521 ++++++++++++++++++++++++++++++++++++ sensors/geomagneticd/geomagneticd.h | 59 ++++ sensors/geomagneticd/input.c | 335 +++++++++++++++++++++++ sensors/input.c | 335 +++++++++++++++++++++++ sensors/orientationd/bma250.c | 85 ++++++ sensors/orientationd/input.c | 335 +++++++++++++++++++++++ sensors/orientationd/orientationd.c | 327 ++++++++++++++++++++++ sensors/orientationd/orientationd.h | 86 ++++++ sensors/orientationd/yas530.c | 85 ++++++ sensors/piranha_sensors.c | 283 ++++++++++++++++++++ sensors/piranha_sensors.h | 102 +++++++ sensors/yas530.c | 260 ++++++++++++++++++ sensors/yas_orientation.c | 297 ++++++++++++++++++++ 18 files changed, 3676 insertions(+), 3 deletions(-) create mode 100644 sensors/Android.mk create mode 100644 sensors/bh1721.c create mode 100644 sensors/bma250.c create mode 100644 sensors/geomagneticd/geomagneticd.c create mode 100644 sensors/geomagneticd/geomagneticd.h create mode 100644 sensors/geomagneticd/input.c create mode 100644 sensors/input.c create mode 100644 sensors/orientationd/bma250.c create mode 100644 sensors/orientationd/input.c create mode 100644 sensors/orientationd/orientationd.c create mode 100644 sensors/orientationd/orientationd.h create mode 100644 sensors/orientationd/yas530.c create mode 100644 sensors/piranha_sensors.c create mode 100644 sensors/piranha_sensors.h create mode 100644 sensors/yas530.c create mode 100644 sensors/yas_orientation.c diff --git a/BoardConfigCommon.mk b/BoardConfigCommon.mk index fa0b663..ad0747a 100644 --- a/BoardConfigCommon.mk +++ b/BoardConfigCommon.mk @@ -82,9 +82,6 @@ BOARD_HAVE_BLUETOOTH_BCM := true BOARD_BLUETOOTH_BDROID_BUILDCFG_INCLUDE_DIR := device/samsung/p5100/bluetooth BOARD_BLUEDROID_VENDOR_CONF := device/samsung/p5100/bluetooth/vnd_espresso10.txt -# Sensors -BOARD_USE_LEGACY_SENSORS_FUSION := false - # Security BOARD_USES_SECURE_SERVICES := true diff --git a/p51xx-common.mk b/p51xx-common.mk index 495f724..7a23233 100755 --- a/p51xx-common.mk +++ b/p51xx-common.mk @@ -74,6 +74,9 @@ PRODUCT_COPY_FILES += \ PRODUCT_PACKAGES += \ audio.primary.piranha \ camera.piranha \ + sensors.piranha \ + geomagneticd \ + orientationd \ GalaxyTab2Settings \ hwcomposer.piranha \ lights.piranha \ diff --git a/sensors/Android.mk b/sensors/Android.mk new file mode 100644 index 0000000..4390aad --- /dev/null +++ b/sensors/Android.mk @@ -0,0 +1,73 @@ +# Copyright (C) 2013 Paul Kocialkowski +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +LOCAL_PATH := $(call my-dir) +PIRANHA_SENSORS_PATH := $(LOCAL_PATH) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + piranha_sensors.c \ + input.c \ + bma250.c \ + bh1721.c \ + yas530.c \ + yas_orientation.c + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH) + +LOCAL_SHARED_LIBRARIES := libutils libcutils liblog libhardware +LOCAL_PRELINK_MODULE := false + +LOCAL_MODULE := sensors.piranha +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) + +LOCAL_PATH := $(PIRANHA_SENSORS_PATH)/geomagneticd + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + geomagneticd.c \ + input.c + +LOCAL_SHARED_LIBRARIES := libutils libcutils liblog +LOCAL_PRELINK_MODULE := false + +LOCAL_MODULE := geomagneticd +LOCAL_MODULE_TAGS := optional + +include $(BUILD_EXECUTABLE) + +LOCAL_PATH := $(PIRANHA_SENSORS_PATH)/orientationd + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + orientationd.c \ + input.c \ + bma250.c \ + yas530.c + +LOCAL_SHARED_LIBRARIES := libutils libcutils liblog +LOCAL_PRELINK_MODULE := false + +LOCAL_MODULE := orientationd +LOCAL_MODULE_TAGS := optional + +include $(BUILD_EXECUTABLE) diff --git a/sensors/bh1721.c b/sensors/bh1721.c new file mode 100644 index 0000000..34d4594 --- /dev/null +++ b/sensors/bh1721.c @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2013 Paul Kocialkowski + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define LOG_TAG "piranha_sensors" +#include + +#include "piranha_sensors.h" + +struct bh1721_data { + char path_enable[PATH_MAX]; + char path_delay[PATH_MAX]; +}; + +int bh1721_init(struct piranha_sensors_handlers *handlers, + struct piranha_sensors_device *device) +{ + struct bh1721_data *data = NULL; + char path[PATH_MAX] = { 0 }; + int input_fd = -1; + int rc; + + ALOGD("%s(%p, %p)", __func__, handlers, device); + + if (handlers == NULL) + return -EINVAL; + + data = (struct bh1721_data *) calloc(1, sizeof(struct bh1721_data)); + + input_fd = input_open("light_sensor"); + if (input_fd < 0) { + ALOGE("%s: Unable to open input", __func__); + goto error; + } + + rc = sysfs_path_prefix("light_sensor", (char *) &path); + if (rc < 0 || path[0] == '\0') { + ALOGE("%s: Unable to open sysfs", __func__); + goto error; + } + + snprintf(data->path_enable, PATH_MAX, "%s/enable", path); + snprintf(data->path_delay, PATH_MAX, "%s/poll_delay", path); + + handlers->poll_fd = input_fd; + handlers->data = (void *) data; + + return 0; + +error: + if (data != NULL) + free(data); + + if (input_fd >= 0) + close(input_fd); + + handlers->poll_fd = -1; + handlers->data = NULL; + + return -1; +} + +int bh1721_deinit(struct piranha_sensors_handlers *handlers) +{ + ALOGD("%s(%p)", __func__, handlers); + + if (handlers == NULL) + return -EINVAL; + + if (handlers->poll_fd >= 0) + close(handlers->poll_fd); + handlers->poll_fd = -1; + + if (handlers->data != NULL) + free(handlers->data); + handlers->data = NULL; + + return 0; +} + +int bh1721_activate(struct piranha_sensors_handlers *handlers) +{ + struct bh1721_data *data; + int rc; + + ALOGD("%s(%p)", __func__, handlers); + + if (handlers == NULL || handlers->data == NULL) + return -EINVAL; + + data = (struct bh1721_data *) handlers->data; + + rc = sysfs_value_write(data->path_enable, 1); + if (rc < 0) { + ALOGE("%s: Unable to write sysfs value", __func__); + return -1; + } + + handlers->activated = 1; + + return 0; +} + +int bh1721_deactivate(struct piranha_sensors_handlers *handlers) +{ + struct bh1721_data *data; + int rc; + + ALOGD("%s(%p)", __func__, handlers); + + if (handlers == NULL || handlers->data == NULL) + return -EINVAL; + + data = (struct bh1721_data *) handlers->data; + + rc = sysfs_value_write(data->path_enable, 0); + if (rc < 0) { + ALOGE("%s: Unable to write sysfs value", __func__); + return -1; + } + + handlers->activated = 1; + + return 0; +} + +int bh1721_set_delay(struct piranha_sensors_handlers *handlers, long int delay) +{ + struct bh1721_data *data; + int rc; + + ALOGD("%s(%p, %ld)", __func__, handlers, delay); + + if (handlers == NULL || handlers->data == NULL) + return -EINVAL; + + data = (struct bh1721_data *) handlers->data; + + rc = sysfs_value_write(data->path_delay, (int) delay); + if (rc < 0) { + ALOGE("%s: Unable to write sysfs value", __func__); + return -1; + } + + return 0; +} + +float bh1721_convert(int value) +{ + return value * 0.712f; +} + +int bh1721_get_data(struct piranha_sensors_handlers *handlers, + struct sensors_event_t *event) +{ + struct input_event input_event; + int input_fd; + int rc; + +// ALOGD("%s(%p, %p)", __func__, handlers, event); + + if (handlers == NULL || event == NULL) + return -EINVAL; + + input_fd = handlers->poll_fd; + if (input_fd < 0) + return -EINVAL; + + memset(event, 0, sizeof(struct sensors_event_t)); + event->version = sizeof(struct sensors_event_t); + event->sensor = handlers->handle; + event->type = handlers->handle; + + do { + rc = read(input_fd, &input_event, sizeof(input_event)); + if (rc < (int) sizeof(input_event)) + break; + + if (input_event.type == EV_REL) { + if (input_event.code == REL_MISC) + event->light = bh1721_convert(input_event.value); + } else if (input_event.type == EV_SYN) { + if (input_event.code == SYN_REPORT) + event->timestamp = input_timestamp(&input_event); + } + } while (input_event.type != EV_SYN); + + return 0; +} + +struct piranha_sensors_handlers bh1721 = { + .name = "BH1721", + .handle = SENSOR_TYPE_LIGHT, + .init = bh1721_init, + .deinit = bh1721_deinit, + .activate = bh1721_activate, + .deactivate = bh1721_deactivate, + .set_delay = bh1721_set_delay, + .get_data = bh1721_get_data, + .activated = 0, + .needed = 0, + .poll_fd = -1, + .data = NULL, +}; diff --git a/sensors/bma250.c b/sensors/bma250.c new file mode 100644 index 0000000..91d7fc0 --- /dev/null +++ b/sensors/bma250.c @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2013 Paul Kocialkowski + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define LOG_TAG "piranha_sensors" +#include + +#include "piranha_sensors.h" + +struct bma250_data { + char path_enable[PATH_MAX]; + char path_delay[PATH_MAX]; + + sensors_vec_t acceleration; +}; + +int bma250_init(struct piranha_sensors_handlers *handlers, + struct piranha_sensors_device *device) +{ + struct bma250_data *data = NULL; + char path[PATH_MAX] = { 0 }; + int input_fd = -1; + int rc; + + ALOGD("%s(%p, %p)", __func__, handlers, device); + + if (handlers == NULL) + return -EINVAL; + + data = (struct bma250_data *) calloc(1, sizeof(struct bma250_data)); + + input_fd = input_open("accelerometer"); + if (input_fd < 0) { + ALOGE("%s: Unable to open input", __func__); + goto error; + } + + rc = sysfs_path_prefix("accelerometer", (char *) &path); + if (rc < 0 || path[0] == '\0') { + ALOGE("%s: Unable to open sysfs", __func__); + goto error; + } + + snprintf(data->path_enable, PATH_MAX, "%s/enable", path); + snprintf(data->path_delay, PATH_MAX, "%s/delay", path); + + handlers->poll_fd = input_fd; + handlers->data = (void *) data; + + return 0; + +error: + if (data != NULL) + free(data); + + if (input_fd >= 0) + close(input_fd); + + handlers->poll_fd = -1; + handlers->data = NULL; + + return -1; +} + +int bma250_deinit(struct piranha_sensors_handlers *handlers) +{ + ALOGD("%s(%p)", __func__, handlers); + + if (handlers == NULL) + return -EINVAL; + + if (handlers->poll_fd >= 0) + close(handlers->poll_fd); + handlers->poll_fd = -1; + + if (handlers->data != NULL) + free(handlers->data); + handlers->data = NULL; + + return 0; +} + +int bma250_activate(struct piranha_sensors_handlers *handlers) +{ + struct bma250_data *data; + int rc; + + ALOGD("%s(%p)", __func__, handlers); + + if (handlers == NULL || handlers->data == NULL) + return -EINVAL; + + data = (struct bma250_data *) handlers->data; + + rc = sysfs_value_write(data->path_enable, 1); + if (rc < 0) { + ALOGE("%s: Unable to write sysfs value", __func__); + return -1; + } + + handlers->activated = 1; + + return 0; +} + +int bma250_deactivate(struct piranha_sensors_handlers *handlers) +{ + struct bma250_data *data; + int rc; + + ALOGD("%s(%p)", __func__, handlers); + + if (handlers == NULL || handlers->data == NULL) + return -EINVAL; + + data = (struct bma250_data *) handlers->data; + + rc = sysfs_value_write(data->path_enable, 0); + if (rc < 0) { + ALOGE("%s: Unable to write sysfs value", __func__); + return -1; + } + + handlers->activated = 1; + + return 0; +} + +int bma250_set_delay(struct piranha_sensors_handlers *handlers, long int delay) +{ + struct bma250_data *data; + int d; + int rc; + + ALOGD("%s(%p, %ld)", __func__, handlers, delay); + + if (handlers == NULL || handlers->data == NULL) + return -EINVAL; + + data = (struct bma250_data *) handlers->data; + + if (delay < 10000000) + d = 10; + else + d = delay / 1000000; + + rc = sysfs_value_write(data->path_delay, d); + if (rc < 0) { + ALOGE("%s: Unable to write sysfs value", __func__); + return -1; + } + + return 0; +} + +float bma250_convert(int value) +{ + return value * (GRAVITY_EARTH / 256.0f); +} + +int bma250_get_data(struct piranha_sensors_handlers *handlers, + struct sensors_event_t *event) +{ + struct bma250_data *data; + struct input_event input_event; + int input_fd; + int rc; + +// ALOGD("%s(%p, %p)", __func__, handlers, event); + + if (handlers == NULL || handlers->data == NULL || event == NULL) + return -EINVAL; + + data = (struct bma250_data *) handlers->data; + + input_fd = handlers->poll_fd; + if (input_fd < 0) + return -EINVAL; + + memset(event, 0, sizeof(struct sensors_event_t)); + event->version = sizeof(struct sensors_event_t); + event->sensor = handlers->handle; + event->type = handlers->handle; + + event->acceleration.x = data->acceleration.x; + event->acceleration.y = data->acceleration.y; + event->acceleration.z = data->acceleration.z; + + do { + rc = read(input_fd, &input_event, sizeof(input_event)); + if (rc < (int) sizeof(input_event)) + break; + + if (input_event.type == EV_ABS) { + switch (input_event.code) { + case ABS_X: + event->acceleration.x = bma250_convert(input_event.value); + break; + case ABS_Y: + event->acceleration.y = bma250_convert(input_event.value); + break; + case ABS_Z: + event->acceleration.z = bma250_convert(input_event.value); + break; + default: + continue; + } + } else if (input_event.type == EV_SYN) { + if (input_event.code == SYN_REPORT) + event->timestamp = input_timestamp(&input_event); + } + } while (input_event.type != EV_SYN); + + data->acceleration.x = event->acceleration.x; + data->acceleration.y = event->acceleration.y; + data->acceleration.z = event->acceleration.z; + + return 0; +} + +struct piranha_sensors_handlers bma250 = { + .name = "BMA250", + .handle = SENSOR_TYPE_ACCELEROMETER, + .init = bma250_init, + .deinit = bma250_deinit, + .activate = bma250_activate, + .deactivate = bma250_deactivate, + .set_delay = bma250_set_delay, + .get_data = bma250_get_data, + .activated = 0, + .needed = 0, + .poll_fd = -1, + .data = NULL, +}; diff --git a/sensors/geomagneticd/geomagneticd.c b/sensors/geomagneticd/geomagneticd.c new file mode 100644 index 0000000..61c4795 --- /dev/null +++ b/sensors/geomagneticd/geomagneticd.c @@ -0,0 +1,521 @@ +/* + * Copyright (C) 2013 Paul Kocialkowski + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#define LOG_TAG "geomagneticd" +#include + +#include "geomagneticd.h" + +// This geomagnetic daemon is in charge of finding the correct calibration +// offsets to apply to the YAS530 magnetic field sensor. +// This is done by finding raw data extrema (minimum and maximum) for each axis +// and calculating the offset so that these values are -45uT and 45uT. + +/* + * Config + */ + +int geomagneticd_config_read(struct geomagneticd_data *data) +{ + char buffer[100] = { 0 }; + int config_fd = -1; + int rc; + + if (data == NULL) + return -EINVAL; + + config_fd = open(GEOMAGNETICD_CONFIG_PATH, O_RDONLY); + if (config_fd < 0) { + ALOGE("%s: Unable to open config", __func__); + goto error; + } + + rc = read(config_fd, buffer, sizeof(buffer)); + if (rc <= 0) { + ALOGE("%s: Unable to read config", __func__); + goto error; + } + + rc = sscanf(buffer, "%d,%d,%d,%d,%d,%d,%d", + &data->hard_offsets[0], &data->hard_offsets[1], &data->hard_offsets[2], + &data->calib_offsets[0], &data->calib_offsets[1], &data->calib_offsets[2], + &data->accuracy); + if (rc != 7) { + ALOGE("%s: Unable to parse config", __func__); + goto error; + } + + rc = 0; + goto complete; + +error: + rc = -1; + +complete: + if (config_fd >= 0) + close(config_fd); + + return rc; +} + +int geomagneticd_config_write(struct geomagneticd_data *data) +{ + char buffer[100] = { 0 }; + int config_fd = -1; + int rc; + + if (data == NULL) + return -EINVAL; + + config_fd = open(GEOMAGNETICD_CONFIG_BACKUP_PATH, O_WRONLY | O_TRUNC | O_CREAT, 0644); + if (config_fd < 0) { + ALOGE("%s: Unable to open config", __func__); + goto error; + } + + sprintf(buffer, "%d,%d,%d,%d,%d,%d,%d", + data->hard_offsets[0], data->hard_offsets[1], data->hard_offsets[2], + data->calib_offsets[0], data->calib_offsets[1], data->calib_offsets[2], + data->accuracy); + + rc = write(config_fd, buffer, strlen(buffer) + 1); + if (rc < (int) strlen(buffer) + 1) { + ALOGE("%s: Unable to write config", __func__); + goto error; + } + + rename(GEOMAGNETICD_CONFIG_BACKUP_PATH, GEOMAGNETICD_CONFIG_PATH); + + rc = 0; + goto complete; + +error: + rc = -1; + +complete: + if (config_fd >= 0) + close(config_fd); + + return rc; +} + +/* + * Offsets + */ + +int geomagneticd_offsets_read(struct geomagneticd_data *data) +{ + char buffer[100] = { 0 }; + int offsets_fd = -1; + int rc; + + if (data == NULL) + return -EINVAL; + + offsets_fd = open(data->path_offsets, O_RDONLY); + if (offsets_fd < 0) { + ALOGE("%s: Unable to open offsets", __func__); + goto error; + } + + rc = read(offsets_fd, buffer, sizeof(buffer)); + if (rc <= 0) { + ALOGE("%s: Unable to read offsets", __func__); + goto error; + } + + rc = sscanf(buffer, "%d %d %d %d %d %d %d", + &data->hard_offsets[0], &data->hard_offsets[1], &data->hard_offsets[2], + &data->calib_offsets[0], &data->calib_offsets[1], &data->calib_offsets[2], + &data->accuracy); + if (rc != 7) { + ALOGE("%s: Unable to parse offsets", __func__); + goto error; + } + + rc = 0; + goto complete; + +error: + rc = -1; + +complete: + if (offsets_fd >= 0) + close(offsets_fd); + + return rc; +} + +int geomagneticd_offsets_write(struct geomagneticd_data *data) +{ + char buffer[100] = { 0 }; + int offsets_fd = -1; + int rc; + + if (data == NULL) + return -EINVAL; + + offsets_fd = open(data->path_offsets, O_WRONLY); + if (offsets_fd < 0) { + ALOGE("%s: Unable to open offsets", __func__); + goto error; + } + + sprintf(buffer, "%d %d %d %d %d %d %d\n", + data->hard_offsets[0], data->hard_offsets[1], data->hard_offsets[2], + data->calib_offsets[0], data->calib_offsets[1], data->calib_offsets[2], + data->accuracy); + + rc = write(offsets_fd, buffer, strlen(buffer) + 1); + if (rc < (int) strlen(buffer) + 1) { + ALOGE("%s: Unable to write offsets", __func__); + goto error; + } + + rc = 0; + goto complete; + +error: + rc = -1; + +complete: + if (offsets_fd >= 0) + close(offsets_fd); + + return rc; +} + +int geomagneticd_offsets_init(struct geomagneticd_data *data) +{ + int count; + int i; + + if (data == NULL) + return -EINVAL; + + count = sizeof(data->hard_offsets) / sizeof(int); + + // 0x7f is an invalid value for hard offsets + for (i = 0; i < count; i++) + data->hard_offsets[i] = 0x7f; + + count = sizeof(data->calib_offsets) / sizeof(int); + + // 0x0x7fffffff is an invalid value for calib offsets + for (i = 0; i < count; i++) + data->calib_offsets[i] = 0x7fffffff; + + return 0; +} + +int geomagneticd_offsets_check(struct geomagneticd_data *data) +{ + int count; + int i; + + if (data == NULL) + return -EINVAL; + + count = sizeof(data->hard_offsets) / sizeof(int); + + // 0x7f is an invalid value for hard offsets + for (i = 0; i < count; i++) + if (data->hard_offsets[i] == 0x7f) + return 0; + + count = sizeof(data->calib_offsets) / sizeof(int); + + // 0x0x7fffffff is an invalid value for calib offsets + for (i = 0; i < count; i++) + if (data->calib_offsets[i] == 0x7fffffff) + return 0; + + return 1; +} + +/* + * Geomagneticd + */ + +int geomagneticd_magnetic_extrema_init(struct geomagneticd_data *data) +{ + int count; + int i; + + if (data == NULL) + return -EINVAL; + + count = sizeof(data->calib_offsets) / sizeof(int); + + // Approximate the previous extrema from the calib offsets + for (i = 0; i < count; i++) { + data->magnetic_extrema[0][i] = -(data->calib_offsets[i] + 45) * 1000 + 5000; + data->magnetic_extrema[1][i] = (data->calib_offsets[i] + 45) * 1000 - 5000; + } + + return 0; +} + +int geomagneticd_magnetic_extrema(struct geomagneticd_data *data, int index, + int value) +{ + if (data == NULL || index < 0 || index >= 3) + return -EINVAL; + + if (value == 0) + return 0; + + // Update the extrema from the current value if needed + if (value < data->magnetic_extrema[0][index] || data->magnetic_extrema[0][index] == 0) + data->magnetic_extrema[0][index] = value; + if (value > data->magnetic_extrema[1][index] || data->magnetic_extrema[1][index] == 0) + data->magnetic_extrema[1][index] = value; + + return 0; +} + +int geomagneticd_calib_offsets(struct geomagneticd_data *data) +{ + int calib_offsets[3]; + int offsets[2]; + int update; + int count; + int rc; + int i; + + if (data == NULL) + return -EINVAL; + + // Calculating the offset is only meaningful when enough values were + // obtained. There is no need to calculate it too often either. + if (data->count % 10 != 0) + return 0; + + update = 0; + + count = sizeof(data->calib_offsets) / sizeof(int); + + // Calculate the calib offset for each axis to have values in [-45;45] uT + for (i = 0; i < count; i++) { + offsets[0] = data->magnetic_extrema[0][i] + 45 * 1000; + offsets[1] = data->magnetic_extrema[1][i] - 45 * 1000; + calib_offsets[i] = (offsets[0] + offsets[1]) / 2; + + if (calib_offsets[i] != data->calib_offsets[i]) { + data->calib_offsets[i] = calib_offsets[i]; + update = 1; + } + } + + if (update) { + data->accuracy = 1; + + rc = geomagneticd_offsets_write(data); + if (rc < 0) { + ALOGE("%s: Unable to write offsets", __func__); + return -1; + } + + rc = geomagneticd_config_write(data); + if (rc < 0) { + ALOGE("%s: Unable to write config", __func__); + return -1; + } + } + + return 0; +} + +int geomagneticd_poll(struct geomagneticd_data *data) +{ + struct input_event input_event; + struct pollfd poll_fd; + int rc; + + if (data == NULL) + return -EINVAL; + + if (data->input_fd < 0) + return -1; + + memset(&poll_fd, 0, sizeof(poll_fd)); + poll_fd.fd = data->input_fd; + poll_fd.events = POLLIN; + + while (1) { + rc = poll(&poll_fd, 1, -1); + if (rc < 0) { + ALOGE("%s: poll failure", __func__); + goto error; + } + + if (!(poll_fd.revents & POLLIN)) + continue; + + poll_fd.revents = 0; + + rc = read(data->input_fd, &input_event, sizeof(input_event)); + if (rc < (int) sizeof(input_event)) { + ALOGE("%s: Unable to read input event", __func__); + continue; + } + + // Update the extrema from the current value + if(input_event.type == EV_ABS) { + switch (input_event.code) { + case ABS_X: + geomagneticd_magnetic_extrema(data, 0, input_event.value); + break; + case ABS_Y: + geomagneticd_magnetic_extrema(data, 1, input_event.value); + break; + case ABS_Z: + geomagneticd_magnetic_extrema(data, 2, input_event.value); + break; + } + } + + if (input_event.type == EV_SYN) { + // Sometimes, the hard offsets cannot be read at startup + // so we need to do it now + if (!geomagneticd_offsets_check(data)) { + rc = geomagneticd_offsets_read(data); + if (rc < 0) { + ALOGE("%s: Unable to read offsets", __func__); + continue; + } + + // Most likely, the calib offset will be invalid + if (geomagneticd_offsets_check(data)) { + data->accuracy = 1; + geomagneticd_magnetic_extrema_init(data); + } + + rc = geomagneticd_config_write(data); + if (rc < 0) { + ALOGE("%s: Unable to write config", __func__); + continue; + } + } + + data->count++; + + rc = geomagneticd_calib_offsets(data); + if (rc < 0) { + ALOGE("%s: Unable to calib offsets", __func__); + continue; + } + } + } + + rc = 0; + goto complete; + +error: + rc = -1; + +complete: + return rc; +} + +int main(int argc, char *argv[]) +{ + struct geomagneticd_data *geomagneticd_data = NULL; + char path[PATH_MAX] = { 0 }; + int input_fd = -1; + int rc; + + geomagneticd_data = (struct geomagneticd_data *) + calloc(1, sizeof(struct geomagneticd_data)); + + input_fd = input_open("geomagnetic_raw"); + if (input_fd < 0) { + ALOGE("%s: Unable to open input", __func__); + goto error; + } + + rc = sysfs_path_prefix("geomagnetic_raw", (char *) &path); + if (rc < 0 || path[0] == '\0') { + ALOGE("%s: Unable to open sysfs", __func__); + goto error; + } + + snprintf(geomagneticd_data->path_offsets, PATH_MAX, "%s/offsets", path); + + geomagneticd_data->input_fd = input_fd; + + geomagneticd_offsets_init(geomagneticd_data); + + // Attempt to read the offsets from the config + rc = geomagneticd_config_read(geomagneticd_data); + if (rc < 0 || !geomagneticd_offsets_check(geomagneticd_data)) { + // Read the offsets from the driver + rc = geomagneticd_offsets_read(geomagneticd_data); + if (rc < 0) { + ALOGE("%s: Unable to read offsets", __func__); + goto error; + } + + // Most likely, the calib offset will be invalid and the hard + // offset may be invalid as well + if (geomagneticd_offsets_check(geomagneticd_data)) { + geomagneticd_data->accuracy = 1; + geomagneticd_magnetic_extrema_init(geomagneticd_data); + } + } else { + // Get the magnetic extrema from the config's offsets + geomagneticd_magnetic_extrema_init(geomagneticd_data); + + rc = geomagneticd_offsets_write(geomagneticd_data); + if (rc < 0) { + ALOGE("%s: Unable to write offsets", __func__); + goto error; + } + } + + rc = geomagneticd_poll(geomagneticd_data); + if (rc < 0) + goto error; + + rc = 0; + goto complete; + +error: + while (1) + sleep(3600); + + rc = 1; + +complete: + if (input_fd >= 0) + close(input_fd); + + if (geomagneticd_data != NULL) + free(geomagneticd_data); + + return rc; +} diff --git a/sensors/geomagneticd/geomagneticd.h b/sensors/geomagneticd/geomagneticd.h new file mode 100644 index 0000000..cd57eb9 --- /dev/null +++ b/sensors/geomagneticd/geomagneticd.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2013 Paul Kocialkowski + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include +#include + +#ifndef _GEOMAGNETICD_H_ +#define _GEOMAGNETICD_H_ + +#define GEOMAGNETICD_CONFIG_PATH "/data/system/yas.cfg" +#define GEOMAGNETICD_CONFIG_BACKUP_PATH "/data/system/yas-backup.cfg" + +struct geomagneticd_data { + int magnetic_extrema[2][3]; + int hard_offsets[3]; + int calib_offsets[3]; + int accuracy; + + int input_fd; + char path_offsets[PATH_MAX]; + + int count; +}; + +/* + * Input + */ + +void input_event_set(struct input_event *event, int type, int code, int value); +long int timestamp(struct timeval *time); +long int input_timestamp(struct input_event *event); +int uinput_rel_create(const char *name); +void uinput_destroy(int uinput_fd); +int input_open(char *name); +int sysfs_path_prefix(char *name, char *path_prefix); +int sysfs_value_read(char *path); +int sysfs_value_write(char *path, int value); +int sysfs_string_read(char *path, char *buffer, size_t length); +int sysfs_string_write(char *path, char *buffer, size_t length); + +#endif diff --git a/sensors/geomagneticd/input.c b/sensors/geomagneticd/input.c new file mode 100644 index 0000000..7e9ca4a --- /dev/null +++ b/sensors/geomagneticd/input.c @@ -0,0 +1,335 @@ +/* + * Copyright (C) 2013 Paul Kocialkowski + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOG_TAG "geomagneticd" +#include + +#include "geomagneticd.h" + +void input_event_set(struct input_event *event, int type, int code, int value) +{ + if (event == NULL) + return; + + memset(event, 0, sizeof(struct input_event)); + + event->type = type, + event->code = code; + event->value = value; + + gettimeofday(&event->time, NULL); +} + +long int timestamp(struct timeval *time) +{ + if (time == NULL) + return -1; + + return time->tv_sec * 1000000000LL + time->tv_usec * 1000; +} + +long int input_timestamp(struct input_event *event) +{ + if (event == NULL) + return -1; + + return timestamp(&event->time); +} + +int uinput_rel_create(const char *name) +{ + struct uinput_user_dev uinput_dev; + int uinput_fd; + int rc; + + if (name == NULL) + return -1; + + uinput_fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK); + if (uinput_fd < 0) { + ALOGE("%s: Unable to open uinput device", __func__); + goto error; + } + + memset(&uinput_dev, 0, sizeof(uinput_dev)); + + strncpy(uinput_dev.name, name, sizeof(uinput_dev.name)); + uinput_dev.id.bustype = BUS_I2C; + uinput_dev.id.vendor = 0; + uinput_dev.id.product = 0; + uinput_dev.id.version = 0; + + rc = 0; + rc |= ioctl(uinput_fd, UI_SET_EVBIT, EV_REL); + rc |= ioctl(uinput_fd, UI_SET_RELBIT, REL_X); + rc |= ioctl(uinput_fd, UI_SET_RELBIT, REL_Y); + rc |= ioctl(uinput_fd, UI_SET_RELBIT, REL_Z); + rc |= ioctl(uinput_fd, UI_SET_EVBIT, EV_SYN); + + if (rc < 0) { + ALOGE("%s: Unable to set uinput bits", __func__); + goto error; + } + + rc = write(uinput_fd, &uinput_dev, sizeof(uinput_dev)); + if (rc < 0) { + ALOGE("%s: Unable to write uinput device", __func__); + goto error; + } + + rc = ioctl(uinput_fd, UI_DEV_CREATE); + if (rc < 0) { + ALOGE("%s: Unable to create uinput device", __func__); + goto error; + } + + usleep(3000); + + return uinput_fd; + +error: + if (uinput_fd >= 0) + close(uinput_fd); + + return -1; +} + +void uinput_destroy(int uinput_fd) +{ + if (uinput_fd < 0) + return; + + ioctl(uinput_fd, UI_DEV_DESTROY); +} + +int input_open(char *name) +{ + DIR *d; + struct dirent *di; + + char input_name[80] = { 0 }; + char path[PATH_MAX]; + char *c; + int fd; + int rc; + + if (name == NULL) + return -EINVAL; + + d = opendir("/dev/input"); + if (d == NULL) + return -1; + + while ((di = readdir(d))) { + if (di == NULL || strcmp(di->d_name, ".") == 0 || strcmp(di->d_name, "..") == 0) + continue; + + snprintf(path, PATH_MAX, "/dev/input/%s", di->d_name); + fd = open(path, O_RDWR | O_NONBLOCK); + if (fd < 0) + continue; + + rc = ioctl(fd, EVIOCGNAME(sizeof(input_name) - 1), &input_name); + if (rc < 0) + continue; + + c = strstr((char *) &input_name, "\n"); + if (c != NULL) + *c = '\0'; + + if (strcmp(input_name, name) == 0) + return fd; + else + close(fd); + } + + return -1; +} + +int sysfs_path_prefix(char *name, char *path_prefix) +{ + DIR *d; + struct dirent *di; + + char input_name[80] = { 0 }; + char path[PATH_MAX]; + char *c; + int fd; + + if (name == NULL || path_prefix == NULL) + return -EINVAL; + + d = opendir("/sys/class/input"); + if (d == NULL) + return -1; + + while ((di = readdir(d))) { + if (di == NULL || strcmp(di->d_name, ".") == 0 || strcmp(di->d_name, "..") == 0) + continue; + + snprintf(path, PATH_MAX, "/sys/class/input/%s/name", di->d_name); + + fd = open(path, O_RDONLY); + if (fd < 0) + continue; + + read(fd, &input_name, sizeof(input_name)); + close(fd); + + c = strstr((char *) &input_name, "\n"); + if (c != NULL) + *c = '\0'; + + if (strcmp(input_name, name) == 0) { + snprintf(path_prefix, PATH_MAX, "/sys/class/input/%s", di->d_name); + return 0; + } + } + + return -1; +} + +int sysfs_value_read(char *path) +{ + char buffer[100]; + int value; + int fd = -1; + int rc; + + if (path == NULL) + return -1; + + fd = open(path, O_RDONLY); + if (fd < 0) + goto error; + + rc = read(fd, &buffer, sizeof(buffer)); + if (rc <= 0) + goto error; + + value = atoi(buffer); + goto complete; + +error: + value = -1; + +complete: + if (fd >= 0) + close(fd); + + return value; +} + +int sysfs_value_write(char *path, int value) +{ + char buffer[100]; + int fd = -1; + int rc; + + if (path == NULL) + return -1; + + fd = open(path, O_WRONLY); + if (fd < 0) + goto error; + + snprintf((char *) &buffer, sizeof(buffer), "%d\n", value); + + rc = write(fd, buffer, strlen(buffer)); + if (rc < (int) strlen(buffer)) + goto error; + + rc = 0; + goto complete; + +error: + rc = -1; + +complete: + if (fd >= 0) + close(fd); + + return rc; +} + +int sysfs_string_read(char *path, char *buffer, size_t length) +{ + int fd = -1; + int rc; + + if (path == NULL || buffer == NULL || length == 0) + return -1; + + fd = open(path, O_RDONLY); + if (fd < 0) + goto error; + + rc = read(fd, buffer, length); + if (rc <= 0) + goto error; + + rc = 0; + goto complete; + +error: + rc = -1; + +complete: + if (fd >= 0) + close(fd); + + return rc; +} + +int sysfs_string_write(char *path, char *buffer, size_t length) +{ + int fd = -1; + int rc; + + if (path == NULL || buffer == NULL || length == 0) + return -1; + + fd = open(path, O_WRONLY); + if (fd < 0) + goto error; + + rc = write(fd, buffer, length); + if (rc <= 0) + goto error; + + rc = 0; + goto complete; + +error: + rc = -1; + +complete: + if (fd >= 0) + close(fd); + + return rc; +} diff --git a/sensors/input.c b/sensors/input.c new file mode 100644 index 0000000..c48f17f --- /dev/null +++ b/sensors/input.c @@ -0,0 +1,335 @@ +/* + * Copyright (C) 2013 Paul Kocialkowski + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOG_TAG "piranha_sensors" +#include + +#include "piranha_sensors.h" + +void input_event_set(struct input_event *event, int type, int code, int value) +{ + if (event == NULL) + return; + + memset(event, 0, sizeof(struct input_event)); + + event->type = type, + event->code = code; + event->value = value; + + gettimeofday(&event->time, NULL); +} + +long int timestamp(struct timeval *time) +{ + if (time == NULL) + return -1; + + return time->tv_sec * 1000000000LL + time->tv_usec * 1000; +} + +long int input_timestamp(struct input_event *event) +{ + if (event == NULL) + return -1; + + return timestamp(&event->time); +} + +int uinput_rel_create(const char *name) +{ + struct uinput_user_dev uinput_dev; + int uinput_fd; + int rc; + + if (name == NULL) + return -1; + + uinput_fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK); + if (uinput_fd < 0) { + ALOGE("%s: Unable to open uinput device", __func__); + goto error; + } + + memset(&uinput_dev, 0, sizeof(uinput_dev)); + + strncpy(uinput_dev.name, name, sizeof(uinput_dev.name)); + uinput_dev.id.bustype = BUS_I2C; + uinput_dev.id.vendor = 0; + uinput_dev.id.product = 0; + uinput_dev.id.version = 0; + + rc = 0; + rc |= ioctl(uinput_fd, UI_SET_EVBIT, EV_REL); + rc |= ioctl(uinput_fd, UI_SET_RELBIT, REL_X); + rc |= ioctl(uinput_fd, UI_SET_RELBIT, REL_Y); + rc |= ioctl(uinput_fd, UI_SET_RELBIT, REL_Z); + rc |= ioctl(uinput_fd, UI_SET_EVBIT, EV_SYN); + + if (rc < 0) { + ALOGE("%s: Unable to set uinput bits", __func__); + goto error; + } + + rc = write(uinput_fd, &uinput_dev, sizeof(uinput_dev)); + if (rc < 0) { + ALOGE("%s: Unable to write uinput device", __func__); + goto error; + } + + rc = ioctl(uinput_fd, UI_DEV_CREATE); + if (rc < 0) { + ALOGE("%s: Unable to create uinput device", __func__); + goto error; + } + + usleep(3000); + + return uinput_fd; + +error: + if (uinput_fd >= 0) + close(uinput_fd); + + return -1; +} + +void uinput_destroy(int uinput_fd) +{ + if (uinput_fd < 0) + return; + + ioctl(uinput_fd, UI_DEV_DESTROY); +} + +int input_open(char *name) +{ + DIR *d; + struct dirent *di; + + char input_name[80] = { 0 }; + char path[PATH_MAX]; + char *c; + int fd; + int rc; + + if (name == NULL) + return -EINVAL; + + d = opendir("/dev/input"); + if (d == NULL) + return -1; + + while ((di = readdir(d))) { + if (di == NULL || strcmp(di->d_name, ".") == 0 || strcmp(di->d_name, "..") == 0) + continue; + + snprintf(path, PATH_MAX, "/dev/input/%s", di->d_name); + fd = open(path, O_RDONLY | O_NONBLOCK); + if (fd < 0) + continue; + + rc = ioctl(fd, EVIOCGNAME(sizeof(input_name) - 1), &input_name); + if (rc < 0) + continue; + + c = strstr((char *) &input_name, "\n"); + if (c != NULL) + *c = '\0'; + + if (strcmp(input_name, name) == 0) + return fd; + else + close(fd); + } + + return -1; +} + +int sysfs_path_prefix(char *name, char *path_prefix) +{ + DIR *d; + struct dirent *di; + + char input_name[80] = { 0 }; + char path[PATH_MAX]; + char *c; + int fd; + + if (name == NULL || path_prefix == NULL) + return -EINVAL; + + d = opendir("/sys/class/input"); + if (d == NULL) + return -1; + + while ((di = readdir(d))) { + if (di == NULL || strcmp(di->d_name, ".") == 0 || strcmp(di->d_name, "..") == 0) + continue; + + snprintf(path, PATH_MAX, "/sys/class/input/%s/name", di->d_name); + + fd = open(path, O_RDONLY); + if (fd < 0) + continue; + + read(fd, &input_name, sizeof(input_name)); + close(fd); + + c = strstr((char *) &input_name, "\n"); + if (c != NULL) + *c = '\0'; + + if (strcmp(input_name, name) == 0) { + snprintf(path_prefix, PATH_MAX, "/sys/class/input/%s", di->d_name); + return 0; + } + } + + return -1; +} + +int sysfs_value_read(char *path) +{ + char buffer[100]; + int value; + int fd = -1; + int rc; + + if (path == NULL) + return -1; + + fd = open(path, O_RDONLY); + if (fd < 0) + goto error; + + rc = read(fd, &buffer, sizeof(buffer)); + if (rc <= 0) + goto error; + + value = atoi(buffer); + goto complete; + +error: + value = -1; + +complete: + if (fd >= 0) + close(fd); + + return value; +} + +int sysfs_value_write(char *path, int value) +{ + char buffer[100]; + int fd = -1; + int rc; + + if (path == NULL) + return -1; + + fd = open(path, O_WRONLY); + if (fd < 0) + goto error; + + snprintf((char *) &buffer, sizeof(buffer), "%d\n", value); + + rc = write(fd, buffer, strlen(buffer)); + if (rc < (int) strlen(buffer)) + goto error; + + rc = 0; + goto complete; + +error: + rc = -1; + +complete: + if (fd >= 0) + close(fd); + + return rc; +} + +int sysfs_string_read(char *path, char *buffer, size_t length) +{ + int fd = -1; + int rc; + + if (path == NULL || buffer == NULL || length == 0) + return -1; + + fd = open(path, O_RDONLY); + if (fd < 0) + goto error; + + rc = read(fd, buffer, length); + if (rc <= 0) + goto error; + + rc = 0; + goto complete; + +error: + rc = -1; + +complete: + if (fd >= 0) + close(fd); + + return rc; +} + +int sysfs_string_write(char *path, char *buffer, size_t length) +{ + int fd = -1; + int rc; + + if (path == NULL || buffer == NULL || length == 0) + return -1; + + fd = open(path, O_WRONLY); + if (fd < 0) + goto error; + + rc = write(fd, buffer, length); + if (rc <= 0) + goto error; + + rc = 0; + goto complete; + +error: + rc = -1; + +complete: + if (fd >= 0) + close(fd); + + return rc; +} diff --git a/sensors/orientationd/bma250.c b/sensors/orientationd/bma250.c new file mode 100644 index 0000000..88819ea --- /dev/null +++ b/sensors/orientationd/bma250.c @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2013 Paul Kocialkowski + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define LOG_TAG "orientationd" +#include + +#include "orientationd.h" + +float bma250_convert(int value) +{ + return value * (GRAVITY_EARTH / 256.0f); +} + +int bma250_get_data(struct orientationd_handlers *handlers, + struct orientationd_data *data) +{ + struct input_event input_event; + int input_fd; + int rc; + + if (handlers == NULL || data == NULL) + return -EINVAL; + + input_fd = handlers->poll_fd; + if (input_fd < 0) + return -1; + + do { + rc = read(input_fd, &input_event, sizeof(input_event)); + if (rc < (int) sizeof(input_event)) + break; + + if (input_event.type == EV_ABS) { + switch (input_event.code) { + case ABS_X: + data->acceleration.x = bma250_convert(input_event.value); + break; + case ABS_Y: + data->acceleration.y = bma250_convert(input_event.value); + break; + case ABS_Z: + data->acceleration.z = bma250_convert(input_event.value); + break; + default: + continue; + } + } + } while (input_event.type != EV_SYN); + + return 0; +} + +struct orientationd_handlers bma250 = { + .input_name = "accelerometer", + .handle = SENSOR_TYPE_ACCELEROMETER, + .poll_fd = -1, + .get_data = bma250_get_data, +}; diff --git a/sensors/orientationd/input.c b/sensors/orientationd/input.c new file mode 100644 index 0000000..1afa2b8 --- /dev/null +++ b/sensors/orientationd/input.c @@ -0,0 +1,335 @@ +/* + * Copyright (C) 2013 Paul Kocialkowski + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOG_TAG "orientationd" +#include + +#include "orientationd.h" + +void input_event_set(struct input_event *event, int type, int code, int value) +{ + if (event == NULL) + return; + + memset(event, 0, sizeof(struct input_event)); + + event->type = type, + event->code = code; + event->value = value; + + gettimeofday(&event->time, NULL); +} + +long int timestamp(struct timeval *time) +{ + if (time == NULL) + return -1; + + return time->tv_sec * 1000000000LL + time->tv_usec * 1000; +} + +long int input_timestamp(struct input_event *event) +{ + if (event == NULL) + return -1; + + return timestamp(&event->time); +} + +int uinput_rel_create(const char *name) +{ + struct uinput_user_dev uinput_dev; + int uinput_fd; + int rc; + + if (name == NULL) + return -1; + + uinput_fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK); + if (uinput_fd < 0) { + ALOGE("%s: Unable to open uinput device", __func__); + goto error; + } + + memset(&uinput_dev, 0, sizeof(uinput_dev)); + + strncpy(uinput_dev.name, name, sizeof(uinput_dev.name)); + uinput_dev.id.bustype = BUS_I2C; + uinput_dev.id.vendor = 0; + uinput_dev.id.product = 0; + uinput_dev.id.version = 0; + + rc = 0; + rc |= ioctl(uinput_fd, UI_SET_EVBIT, EV_REL); + rc |= ioctl(uinput_fd, UI_SET_RELBIT, REL_X); + rc |= ioctl(uinput_fd, UI_SET_RELBIT, REL_Y); + rc |= ioctl(uinput_fd, UI_SET_RELBIT, REL_Z); + rc |= ioctl(uinput_fd, UI_SET_EVBIT, EV_SYN); + + if (rc < 0) { + ALOGE("%s: Unable to set uinput bits", __func__); + goto error; + } + + rc = write(uinput_fd, &uinput_dev, sizeof(uinput_dev)); + if (rc < 0) { + ALOGE("%s: Unable to write uinput device", __func__); + goto error; + } + + rc = ioctl(uinput_fd, UI_DEV_CREATE); + if (rc < 0) { + ALOGE("%s: Unable to create uinput device", __func__); + goto error; + } + + usleep(3000); + + return uinput_fd; + +error: + if (uinput_fd >= 0) + close(uinput_fd); + + return -1; +} + +void uinput_destroy(int uinput_fd) +{ + if (uinput_fd < 0) + return; + + ioctl(uinput_fd, UI_DEV_DESTROY); +} + +int input_open(char *name) +{ + DIR *d; + struct dirent *di; + + char input_name[80] = { 0 }; + char path[PATH_MAX]; + char *c; + int fd; + int rc; + + if (name == NULL) + return -EINVAL; + + d = opendir("/dev/input"); + if (d == NULL) + return -1; + + while ((di = readdir(d))) { + if (di == NULL || strcmp(di->d_name, ".") == 0 || strcmp(di->d_name, "..") == 0) + continue; + + snprintf(path, PATH_MAX, "/dev/input/%s", di->d_name); + fd = open(path, O_RDWR | O_NONBLOCK); + if (fd < 0) + continue; + + rc = ioctl(fd, EVIOCGNAME(sizeof(input_name) - 1), &input_name); + if (rc < 0) + continue; + + c = strstr((char *) &input_name, "\n"); + if (c != NULL) + *c = '\0'; + + if (strcmp(input_name, name) == 0) + return fd; + else + close(fd); + } + + return -1; +} + +int sysfs_path_prefix(char *name, char *path_prefix) +{ + DIR *d; + struct dirent *di; + + char input_name[80] = { 0 }; + char path[PATH_MAX]; + char *c; + int fd; + + if (name == NULL || path_prefix == NULL) + return -EINVAL; + + d = opendir("/sys/class/input"); + if (d == NULL) + return -1; + + while ((di = readdir(d))) { + if (di == NULL || strcmp(di->d_name, ".") == 0 || strcmp(di->d_name, "..") == 0) + continue; + + snprintf(path, PATH_MAX, "/sys/class/input/%s/name", di->d_name); + + fd = open(path, O_RDONLY); + if (fd < 0) + continue; + + read(fd, &input_name, sizeof(input_name)); + close(fd); + + c = strstr((char *) &input_name, "\n"); + if (c != NULL) + *c = '\0'; + + if (strcmp(input_name, name) == 0) { + snprintf(path_prefix, PATH_MAX, "/sys/class/input/%s", di->d_name); + return 0; + } + } + + return -1; +} + +int sysfs_value_read(char *path) +{ + char buffer[100]; + int value; + int fd = -1; + int rc; + + if (path == NULL) + return -1; + + fd = open(path, O_RDONLY); + if (fd < 0) + goto error; + + rc = read(fd, &buffer, sizeof(buffer)); + if (rc <= 0) + goto error; + + value = atoi(buffer); + goto complete; + +error: + value = -1; + +complete: + if (fd >= 0) + close(fd); + + return value; +} + +int sysfs_value_write(char *path, int value) +{ + char buffer[100]; + int fd = -1; + int rc; + + if (path == NULL) + return -1; + + fd = open(path, O_WRONLY); + if (fd < 0) + goto error; + + snprintf((char *) &buffer, sizeof(buffer), "%d\n", value); + + rc = write(fd, buffer, strlen(buffer)); + if (rc < (int) strlen(buffer)) + goto error; + + rc = 0; + goto complete; + +error: + rc = -1; + +complete: + if (fd >= 0) + close(fd); + + return rc; +} + +int sysfs_string_read(char *path, char *buffer, size_t length) +{ + int fd = -1; + int rc; + + if (path == NULL || buffer == NULL || length == 0) + return -1; + + fd = open(path, O_RDONLY); + if (fd < 0) + goto error; + + rc = read(fd, buffer, length); + if (rc <= 0) + goto error; + + rc = 0; + goto complete; + +error: + rc = -1; + +complete: + if (fd >= 0) + close(fd); + + return rc; +} + +int sysfs_string_write(char *path, char *buffer, size_t length) +{ + int fd = -1; + int rc; + + if (path == NULL || buffer == NULL || length == 0) + return -1; + + fd = open(path, O_WRONLY); + if (fd < 0) + goto error; + + rc = write(fd, buffer, length); + if (rc <= 0) + goto error; + + rc = 0; + goto complete; + +error: + rc = -1; + +complete: + if (fd >= 0) + close(fd); + + return rc; +} diff --git a/sensors/orientationd/orientationd.c b/sensors/orientationd/orientationd.c new file mode 100644 index 0000000..8ad84c5 --- /dev/null +++ b/sensors/orientationd/orientationd.c @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2013 Paul Kocialkowski + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define LOG_TAG "orientationd" +#include + +#include "orientationd.h" + +struct orientationd_handlers *orientationd_handlers[] = { + &bma250, + &yas530, +}; + +int orientationd_handlers_count = sizeof(orientationd_handlers) / + sizeof(struct orientationd_handlers *); + +static float rad2deg(float v) +{ + return (v * 180.0f / 3.1415926535f); +} + +static float vector_scalar(sensors_vec_t *v, sensors_vec_t *d) +{ + return v->x * d->x + v->y * d->y + v->z * d->z; +} + +static float vector_length(sensors_vec_t *v) +{ + return sqrtf(vector_scalar(v, v)); +} + +int orientation_calculate(struct orientationd_data *data) +{ + sensors_vec_t *a, *m, *o; + float azimuth, pitch, roll; + float la, sinp, cosp, sinr, cosr, x, y; + + if (data == NULL) + return -EINVAL; + + a = &data->acceleration; + m = &data->magnetic; + o = &data->orientation; + + la = vector_length(a); + pitch = asinf(-(a->y) / la); + roll = asinf((a->x) / la); + + sinp = sinf(pitch); + cosp = cosf(pitch); + sinr = sinf(roll); + cosr = cosf(roll); + + y = -(m->x) * cosr + m->z * sinr; + x = m->x * sinp * sinr + m->y * cosp + m->z * sinp * cosr; + azimuth = atan2f(y, x); + + o->azimuth = rad2deg(azimuth); + o->pitch = rad2deg(pitch); + o->roll = rad2deg(roll); + + if (o->azimuth < 0) + o->azimuth += 360.0f; + + return 0; +} + +void *orientationd_thread(void *thread_data) +{ + struct orientationd_data *data; + struct input_event event; + struct timeval time; + long int before, after; + int diff; + int input_fd; + int rc; + + if (thread_data == NULL) + return NULL; + + data = (struct orientationd_data *) thread_data; + + input_fd = data->input_fd; + if (input_fd < 0) + return NULL; + + while (data->thread_continue) { + pthread_mutex_lock(&data->mutex); + if (!data->thread_continue) + break; + + while (data->activated) { + gettimeofday(&time, NULL); + before = timestamp(&time); + + rc = orientation_calculate(data); + if (rc < 0) { + ALOGE("%s: Unable to calculate orientation", __func__); + goto next; + } + + input_event_set(&event, EV_ABS, ABS_X, (int) (data->orientation.azimuth * 1000)); + write(input_fd, &event, sizeof(event)); + input_event_set(&event, EV_ABS, ABS_Y, (int) (data->orientation.pitch * 1000)); + write(input_fd, &event, sizeof(event)); + input_event_set(&event, EV_ABS, ABS_Z, (int) (data->orientation.roll * 1000)); + write(input_fd, &event, sizeof(event)); + input_event_set(&event, EV_SYN, 0, 0); + write(input_fd, &event, sizeof(event)); + +next: + gettimeofday(&time, NULL); + after = timestamp(&time); + + diff = (int) (data->delay * 1000000 - (after - before)) / 1000; + if (diff <= 0) + continue; + + usleep(diff); + } + } + return NULL; +} + +int orientation_get_data(struct orientationd_data *data) +{ + struct input_event input_event; + int input_fd; + int activated; + unsigned int delay; + int rc; + + if (data == NULL) + return -EINVAL; + + input_fd = data->input_fd; + if (input_fd < 0) + return -1; + + do { + rc = read(input_fd, &input_event, sizeof(input_event)); + if (rc < (int) sizeof(input_event)) + break; + + if (input_event.type == EV_ABS) { + switch (input_event.code) { + case ABS_THROTTLE: + data->activated = input_event.value & (1 << 16) ? 1 : 0; + data->delay = input_event.value & ~(1 << 16); + + pthread_mutex_unlock(&data->mutex); + break; + default: + continue; + } + } + } while (input_event.type != EV_SYN); + + return 0; +} + +int orientationd_poll(struct orientationd_data *data) +{ + int count; + int i, j; + int rc; + + if (data == NULL) + return -EINVAL; + + ALOGD("Starting orientationd poll"); + + while (1) { + if (data->activated) + count = data->poll_fds_count; + else + count = 1; + + rc = poll(data->poll_fds, count, -1); + if (rc < 0) { + ALOGE("%s: poll failure", __func__); + goto error; + } + + for (i = 0; i < count; i++) { + if (data->poll_fds[i].revents & POLLIN) { + data->poll_fds[i].revents = 0; + + if (data->poll_fds[i].fd == data->input_fd) { + orientation_get_data(data); + continue; + } + + for (j = 0; j < data->handlers_count; j++) + if (data->handlers[j] != NULL && data->handlers[j]->poll_fd == data->poll_fds[i].fd && data->handlers[j]->get_data != NULL) + data->handlers[j]->get_data(data->handlers[j], data); + } + } + } + + rc = 0; + goto complete; + +error: + rc = -1; + +complete: + return rc; +} + +int main(int argc, char *argv[]) +{ + struct orientationd_data *orientationd_data = NULL; + pthread_attr_t thread_attr; + int input_fd = -1; + int poll_fd; + int p, i; + int rc; + + orientationd_data = (struct orientationd_data *) + calloc(1, sizeof(struct orientationd_data)); + orientationd_data->handlers = orientationd_handlers; + orientationd_data->handlers_count = orientationd_handlers_count; + orientationd_data->activated = 0; + orientationd_data->poll_fds = (struct pollfd *) + calloc(1, (orientationd_handlers_count + 1) * sizeof(struct pollfd)); + + p = 0; + + input_fd = input_open("orientation"); + if (input_fd < 0) { + ALOGE("%s: Unable to open input", __func__); + goto error; + } + + orientationd_data->input_fd = input_fd; + + orientationd_data->poll_fds[p].fd = input_fd; + orientationd_data->poll_fds[p].events = POLLIN; + p++; + + for (i = 0; i < orientationd_handlers_count; i++) { + if (orientationd_handlers[i] == NULL || orientationd_handlers[i]->input_name == NULL) + continue; + + poll_fd = input_open(orientationd_handlers[i]->input_name); + if (poll_fd < 0) { + ALOGE("%s: Unable to open input %s", __func__, orientationd_handlers[i]->input_name); + continue; + } + + orientationd_handlers[i]->poll_fd = poll_fd; + orientationd_data->poll_fds[p].fd = poll_fd; + orientationd_data->poll_fds[p].events = POLLIN; + p++; + } + + orientationd_data->poll_fds_count = p; + + orientationd_data->thread_continue = 1; + + pthread_mutex_init(&orientationd_data->mutex, NULL); + pthread_mutex_lock(&orientationd_data->mutex); + + pthread_attr_init(&thread_attr); + pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED); + + rc = pthread_create(&orientationd_data->thread, &thread_attr, orientationd_thread, (void *) orientationd_data); + if (rc < 0) { + ALOGE("%s: Unable to create orientationd thread", __func__); + goto error; + } + + rc = orientationd_poll(orientationd_data); + if (rc < 0) + goto error; + + rc = 0; + goto complete; + +error: + while (1) + sleep(3600); + + rc = 1; + +complete: + if (input_fd >= 0) + close(input_fd); + + if (orientationd_data != NULL) { + orientationd_data->thread_continue = 0; + pthread_mutex_destroy(&orientationd_data->mutex); + + if (orientationd_data->poll_fds != NULL) + free(orientationd_data->poll_fds); + + free(orientationd_data); + } + + return rc; +} diff --git a/sensors/orientationd/orientationd.h b/sensors/orientationd/orientationd.h new file mode 100644 index 0000000..7e169e4 --- /dev/null +++ b/sensors/orientationd/orientationd.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2013 Paul Kocialkowski + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include +#include + +#ifndef _ORIENTATIOND_H_ +#define _ORIENTATIOND_H_ + +struct orientationd_data; + +struct orientationd_handlers { + char *input_name; + int handle; + int poll_fd; + + int (*get_data)(struct orientationd_handlers *handlers, + struct orientationd_data *data); +}; + +struct orientationd_data { + struct orientationd_handlers **handlers; + int handlers_count; + + struct pollfd *poll_fds; + int poll_fds_count; + + sensors_vec_t orientation; + sensors_vec_t acceleration; + sensors_vec_t magnetic; + + unsigned int delay; + int input_fd; + + int activated; + + pthread_t thread; + pthread_mutex_t mutex; + int thread_continue; +}; + +extern struct orientationd_handlers *orientationd_handlers[]; +extern int orientationd_handlers_count; + +/* + * Input + */ + +void input_event_set(struct input_event *event, int type, int code, int value); +long int timestamp(struct timeval *time); +long int input_timestamp(struct input_event *event); +int uinput_rel_create(const char *name); +void uinput_destroy(int uinput_fd); +int input_open(char *name); +int sysfs_path_prefix(char *name, char *path_prefix); +int sysfs_value_read(char *path); +int sysfs_value_write(char *path, int value); +int sysfs_string_read(char *path, char *buffer, size_t length); +int sysfs_string_write(char *path, char *buffer, size_t length); + +/* + * Sensors + */ + +extern struct orientationd_handlers bma250; +extern struct orientationd_handlers yas530; + +#endif diff --git a/sensors/orientationd/yas530.c b/sensors/orientationd/yas530.c new file mode 100644 index 0000000..7942c50 --- /dev/null +++ b/sensors/orientationd/yas530.c @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2013 Paul Kocialkowski + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define LOG_TAG "orientationd" +#include + +#include "orientationd.h" + +float yas530_convert(int value) +{ + return value / 1000.0f; +} + +int yas530_get_data(struct orientationd_handlers *handlers, + struct orientationd_data *data) +{ + struct input_event input_event; + int input_fd; + int rc; + + if (handlers == NULL || data == NULL) + return -EINVAL; + + input_fd = handlers->poll_fd; + if (input_fd < 0) + return -1; + + do { + rc = read(input_fd, &input_event, sizeof(input_event)); + if (rc < (int) sizeof(input_event)) + break; + + if (input_event.type == EV_ABS) { + switch (input_event.code) { + case ABS_X: + data->magnetic.x = yas530_convert(input_event.value); + break; + case ABS_Y: + data->magnetic.y = yas530_convert(input_event.value); + break; + case ABS_Z: + data->magnetic.z = yas530_convert(input_event.value); + break; + default: + continue; + } + } + } while (input_event.type != EV_SYN); + + return 0; +} + +struct orientationd_handlers yas530 = { + .input_name = "geomagnetic", + .handle = SENSOR_TYPE_MAGNETIC_FIELD, + .poll_fd = -1, + .get_data = yas530_get_data, +}; diff --git a/sensors/piranha_sensors.c b/sensors/piranha_sensors.c new file mode 100644 index 0000000..126f0f0 --- /dev/null +++ b/sensors/piranha_sensors.c @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2013 Paul Kocialkowski + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOG_TAG "piranha_sensors" +#include + +#include "piranha_sensors.h" + +/* + * Sensors list + */ + +struct sensor_t piranha_sensors[] = { + { "BMA250 Acceleration Sensor", "Bosch", 1, SENSOR_TYPE_ACCELEROMETER, + SENSOR_TYPE_ACCELEROMETER, 2 * GRAVITY_EARTH, GRAVITY_EARTH / 256.0f, 0.13f, 10000, {}, }, + { "YAS530 Magnetic Sensor", "Yamaha", 1, SENSOR_TYPE_MAGNETIC_FIELD, + SENSOR_TYPE_MAGNETIC_FIELD, 800.0f, 0.3f, 4.0f, 10000, {}, }, + { "YAS Orientation Sensor", "Yamaha", 1, SENSOR_TYPE_ORIENTATION, + SENSOR_TYPE_ORIENTATION, 360.0f, 0.1f, 0.0f, 10000, {}, }, + { "BH1721 Light Sensor", "ROHM", 1, SENSOR_TYPE_LIGHT, + SENSOR_TYPE_LIGHT, 0.0f, 0.0f, 0.0f, 0, {}, }, +}; + +int piranha_sensors_count = sizeof(piranha_sensors) / sizeof(struct sensor_t); + +struct piranha_sensors_handlers *piranha_sensors_handlers[] = { + &bma250, + &yas530, + &yas_orientation, + &bh1721, +}; + +int piranha_sensors_handlers_count = sizeof(piranha_sensors_handlers) / + sizeof(struct piranha_sensors_handlers *); + +/* + * Piranha Sensors + */ + +int piranha_sensors_activate(struct sensors_poll_device_t *dev, int handle, + int enabled) +{ + struct piranha_sensors_device *device; + int i; + + ALOGD("%s(%p, %d, %d)", __func__, dev, handle, enabled); + + if (dev == NULL) + return -EINVAL; + + device = (struct piranha_sensors_device *) dev; + + if (device->handlers == NULL || device->handlers_count <= 0) + return -EINVAL; + + for (i = 0; i < device->handlers_count; i++) { + if (device->handlers[i] == NULL) + continue; + + if (device->handlers[i]->handle == handle) { + if (enabled && device->handlers[i]->activate != NULL) { + device->handlers[i]->needed |= PIRANHA_SENSORS_NEEDED_API; + if (device->handlers[i]->needed == PIRANHA_SENSORS_NEEDED_API) + return device->handlers[i]->activate(device->handlers[i]); + else + return 0; + } else if (!enabled && device->handlers[i]->deactivate != NULL) { + device->handlers[i]->needed &= ~PIRANHA_SENSORS_NEEDED_API; + if (device->handlers[i]->needed == 0) + return device->handlers[i]->deactivate(device->handlers[i]); + else + return 0; + } + } + } + + return -1; +} + +int piranha_sensors_set_delay(struct sensors_poll_device_t *dev, int handle, + int64_t ns) +{ + struct piranha_sensors_device *device; + int i; + + ALOGD("%s(%p, %d, %ld)", __func__, dev, handle, (long int) ns); + + if (dev == NULL) + return -EINVAL; + + device = (struct piranha_sensors_device *) dev; + + if (device->handlers == NULL || device->handlers_count <= 0) + return -EINVAL; + + for (i = 0; i < device->handlers_count; i++) { + if (device->handlers[i] == NULL) + continue; + + if (device->handlers[i]->handle == handle && device->handlers[i]->set_delay != NULL) + return device->handlers[i]->set_delay(device->handlers[i], (long int) ns); + } + + return 0; +} + +int piranha_sensors_poll(struct sensors_poll_device_t *dev, + struct sensors_event_t* data, int count) +{ + struct piranha_sensors_device *device; + int i, j; + int c, n; + int poll_rc, rc; + +// ALOGD("%s(%p, %p, %d)", __func__, dev, data, count); + + if (dev == NULL) + return -EINVAL; + + device = (struct piranha_sensors_device *) dev; + + if (device->handlers == NULL || device->handlers_count <= 0 || + device->poll_fds == NULL || device->poll_fds_count <= 0) + return -EINVAL; + + n = 0; + + do { + poll_rc = poll(device->poll_fds, device->poll_fds_count, n > 0 ? 0 : -1); + if (poll_rc < 0) + return -1; + + for (i = 0; i < device->poll_fds_count; i++) { + if (!(device->poll_fds[i].revents & POLLIN)) + continue; + + for (j = 0; j < device->handlers_count; j++) { + if (device->handlers[j] == NULL || device->handlers[j]->poll_fd != device->poll_fds[i].fd || device->handlers[j]->get_data == NULL) + continue; + + rc = device->handlers[j]->get_data(device->handlers[j], &data[n]); + if (rc < 0) { + device->poll_fds[i].revents = 0; + poll_rc = -1; + } else { + n++; + count--; + } + } + } + } while ((poll_rc > 0 || n < 1) && count > 0); + + return n; +} + +/* + * Interface + */ + +int piranha_sensors_close(hw_device_t *device) +{ + struct piranha_sensors_device *piranha_sensors_device; + int i; + + ALOGD("%s(%p)", __func__, device); + + if (device == NULL) + return -EINVAL; + + piranha_sensors_device = (struct piranha_sensors_device *) device; + + if (piranha_sensors_device->poll_fds != NULL) + free(piranha_sensors_device->poll_fds); + + for (i = 0; i < piranha_sensors_device->handlers_count; i++) { + if (piranha_sensors_device->handlers[i] == NULL || piranha_sensors_device->handlers[i]->deinit == NULL) + continue; + + piranha_sensors_device->handlers[i]->deinit(piranha_sensors_device->handlers[i]); + } + + free(device); + + return 0; +} + +int piranha_sensors_open(const struct hw_module_t* module, const char *id, + struct hw_device_t** device) +{ + struct piranha_sensors_device *piranha_sensors_device; + int p, i; + + ALOGD("%s(%p, %s, %p)", __func__, module, id, device); + + if (module == NULL || device == NULL) + return -EINVAL; + + piranha_sensors_device = (struct piranha_sensors_device *) + calloc(1, sizeof(struct piranha_sensors_device)); + piranha_sensors_device->device.common.tag = HARDWARE_DEVICE_TAG; + piranha_sensors_device->device.common.version = 0; + piranha_sensors_device->device.common.module = (struct hw_module_t *) module; + piranha_sensors_device->device.common.close = piranha_sensors_close; + piranha_sensors_device->device.activate = piranha_sensors_activate; + piranha_sensors_device->device.setDelay = piranha_sensors_set_delay; + piranha_sensors_device->device.poll = piranha_sensors_poll; + piranha_sensors_device->handlers = piranha_sensors_handlers; + piranha_sensors_device->handlers_count = piranha_sensors_handlers_count; + piranha_sensors_device->poll_fds = (struct pollfd *) + calloc(1, piranha_sensors_handlers_count * sizeof(struct pollfd)); + + p = 0; + for (i = 0; i < piranha_sensors_handlers_count; i++) { + if (piranha_sensors_handlers[i] == NULL || piranha_sensors_handlers[i]->init == NULL) + continue; + + piranha_sensors_handlers[i]->init(piranha_sensors_handlers[i], piranha_sensors_device); + if (piranha_sensors_handlers[i]->poll_fd >= 0) { + piranha_sensors_device->poll_fds[p].fd = piranha_sensors_handlers[i]->poll_fd; + piranha_sensors_device->poll_fds[p].events = POLLIN; + p++; + } + } + + piranha_sensors_device->poll_fds_count = p; + + *device = &(piranha_sensors_device->device.common); + + return 0; +} + +int piranha_sensors_get_sensors_list(struct sensors_module_t* module, + const struct sensor_t **sensors_p) +{ + ALOGD("%s(%p, %p)", __func__, module, sensors_p); + + if (sensors_p == NULL) + return -EINVAL; + + *sensors_p = piranha_sensors; + return piranha_sensors_count; +} + +struct hw_module_methods_t piranha_sensors_module_methods = { + .open = piranha_sensors_open, +}; + +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 = "Piranha Sensors", + .author = "Paul Kocialkowski", + .methods = &piranha_sensors_module_methods, + }, + .get_sensors_list = piranha_sensors_get_sensors_list, +}; diff --git a/sensors/piranha_sensors.h b/sensors/piranha_sensors.h new file mode 100644 index 0000000..247fe48 --- /dev/null +++ b/sensors/piranha_sensors.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2013 Paul Kocialkowski + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include +#include + +#ifndef _PIRANHA_SENSORS_H_ +#define _PIRANHA_SENSORS_H_ + +#define PIRANHA_SENSORS_NEEDED_API (1 << 0) +#define PIRANHA_SENSORS_NEEDED_ORIENTATION (1 << 1) + +struct piranha_sensors_device; + +struct piranha_sensors_handlers { + char *name; + int handle; + + int (*init)(struct piranha_sensors_handlers *handlers, + struct piranha_sensors_device *device); + int (*deinit)(struct piranha_sensors_handlers *handlers); + int (*activate)(struct piranha_sensors_handlers *handlers); + int (*deactivate)(struct piranha_sensors_handlers *handlers); + int (*set_delay)(struct piranha_sensors_handlers *handlers, + long int delay); + int (*get_data)(struct piranha_sensors_handlers *handlers, + struct sensors_event_t *event); + + int activated; + int needed; + int poll_fd; + + void *data; +}; + +struct piranha_sensors_device { + struct sensors_poll_device_t device; + + struct piranha_sensors_handlers **handlers; + int handlers_count; + + struct pollfd *poll_fds; + int poll_fds_count; +}; + +extern struct piranha_sensors_handlers *piranha_sensors_handlers[]; +extern int piranha_sensors_handlers_count; + +int piranha_sensors_activate(struct sensors_poll_device_t *dev, int handle, + int enabled); +int piranha_sensors_set_delay(struct sensors_poll_device_t *dev, int handle, + int64_t ns); +int piranha_sensors_poll(struct sensors_poll_device_t *dev, + struct sensors_event_t* data, int count); + +/* + * Input + */ + +void input_event_set(struct input_event *event, int type, int code, int value); +long int timestamp(struct timeval *time); +long int input_timestamp(struct input_event *event); +int uinput_rel_create(const char *name); +void uinput_destroy(int uinput_fd); +int input_open(char *name); +int sysfs_path_prefix(char *name, char *path_prefix); +int sysfs_value_read(char *path); +int sysfs_value_write(char *path, int value); +int sysfs_string_read(char *path, char *buffer, size_t length); +int sysfs_string_write(char *path, char *buffer, size_t length); + +/* + * Sensors + */ + +int orientation_fill(struct piranha_sensors_handlers *handlers, + sensors_vec_t *acceleration, sensors_vec_t *magnetic); + +extern struct piranha_sensors_handlers bma250; +extern struct piranha_sensors_handlers yas530; +extern struct piranha_sensors_handlers yas_orientation; +extern struct piranha_sensors_handlers bh1721; + +#endif diff --git a/sensors/yas530.c b/sensors/yas530.c new file mode 100644 index 0000000..81ff60d --- /dev/null +++ b/sensors/yas530.c @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2013 Paul Kocialkowski + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define LOG_TAG "piranha_sensors" +#include + +#include "piranha_sensors.h" + +struct yas530_data { + char path_enable[PATH_MAX]; + char path_delay[PATH_MAX]; + + sensors_vec_t magnetic; +}; + +int yas530_init(struct piranha_sensors_handlers *handlers, + struct piranha_sensors_device *device) +{ + struct yas530_data *data = NULL; + char path[PATH_MAX] = { 0 }; + int input_fd = -1; + int rc; + + ALOGD("%s(%p, %p)", __func__, handlers, device); + + if (handlers == NULL) + return -EINVAL; + + data = (struct yas530_data *) calloc(1, sizeof(struct yas530_data)); + + input_fd = input_open("geomagnetic"); + if (input_fd < 0) { + ALOGE("%s: Unable to open input", __func__); + goto error; + } + + rc = sysfs_path_prefix("geomagnetic", (char *) &path); + if (rc < 0 || path[0] == '\0') { + ALOGE("%s: Unable to open sysfs", __func__); + goto error; + } + + snprintf(data->path_enable, PATH_MAX, "%s/enable", path); + snprintf(data->path_delay, PATH_MAX, "%s/delay", path); + + handlers->poll_fd = input_fd; + handlers->data = (void *) data; + + return 0; + +error: + if (data != NULL) + free(data); + + if (input_fd >= 0) + close(input_fd); + + handlers->poll_fd = -1; + handlers->data = NULL; + + return -1; +} + +int yas530_deinit(struct piranha_sensors_handlers *handlers) +{ + ALOGD("%s(%p)", __func__, handlers); + + if (handlers == NULL) + return -EINVAL; + + if (handlers->poll_fd >= 0) + close(handlers->poll_fd); + handlers->poll_fd = -1; + + if (handlers->data != NULL) + free(handlers->data); + handlers->data = NULL; + + return 0; +} + +int yas530_activate(struct piranha_sensors_handlers *handlers) +{ + struct yas530_data *data; + int rc; + + ALOGD("%s(%p)", __func__, handlers); + + if (handlers == NULL || handlers->data == NULL) + return -EINVAL; + + data = (struct yas530_data *) handlers->data; + + rc = sysfs_value_write(data->path_enable, 1); + if (rc < 0) { + ALOGE("%s: Unable to write sysfs value", __func__); + return -1; + } + + handlers->activated = 1; + + return 0; +} + +int yas530_deactivate(struct piranha_sensors_handlers *handlers) +{ + struct yas530_data *data; + int rc; + + ALOGD("%s(%p)", __func__, handlers); + + if (handlers == NULL || handlers->data == NULL) + return -EINVAL; + + data = (struct yas530_data *) handlers->data; + + rc = sysfs_value_write(data->path_enable, 0); + if (rc < 0) { + ALOGE("%s: Unable to write sysfs value", __func__); + return -1; + } + + handlers->activated = 1; + + return 0; +} + +int yas530_set_delay(struct piranha_sensors_handlers *handlers, long int delay) +{ + struct yas530_data *data; + int d; + int rc; + + ALOGD("%s(%p, %ld)", __func__, handlers, delay); + + if (handlers == NULL || handlers->data == NULL) + return -EINVAL; + + data = (struct yas530_data *) handlers->data; + + if (delay < 10000000) + d = 10; + else + d = delay / 1000000; + + rc = sysfs_value_write(data->path_delay, d); + if (rc < 0) { + ALOGE("%s: Unable to write sysfs value", __func__); + return -1; + } + + return 0; +} + +float yas530_convert(int value) +{ + return value / 1000.0f; +} + +int yas530_get_data(struct piranha_sensors_handlers *handlers, + struct sensors_event_t *event) +{ + struct yas530_data *data; + struct input_event input_event; + int input_fd; + int rc; + +// ALOGD("%s(%p, %p)", __func__, handlers, event); + + if (handlers == NULL || handlers->data == NULL || event == NULL) + return -EINVAL; + + data = (struct yas530_data *) handlers->data; + + input_fd = handlers->poll_fd; + if (input_fd < 0) + return -EINVAL; + + memset(event, 0, sizeof(struct sensors_event_t)); + event->version = sizeof(struct sensors_event_t); + event->sensor = handlers->handle; + event->type = handlers->handle; + + event->magnetic.x = data->magnetic.x; + event->magnetic.y = data->magnetic.y; + event->magnetic.z = data->magnetic.z; + + do { + rc = read(input_fd, &input_event, sizeof(input_event)); + if (rc < (int) sizeof(input_event)) + break; + + if (input_event.type == EV_ABS) { + switch (input_event.code) { + case ABS_X: + event->magnetic.x = yas530_convert(input_event.value); + break; + case ABS_Y: + event->magnetic.y = yas530_convert(input_event.value); + break; + case ABS_Z: + event->magnetic.z = yas530_convert(input_event.value); + break; + default: + continue; + } + } else if (input_event.type == EV_SYN) { + if (input_event.code == SYN_REPORT) + event->timestamp = input_timestamp(&input_event); + } + } while (input_event.type != EV_SYN); + + data->magnetic.x = event->magnetic.x; + data->magnetic.y = event->magnetic.y; + data->magnetic.z = event->magnetic.z; + + return 0; +} + +struct piranha_sensors_handlers yas530 = { + .name = "YAS530", + .handle = SENSOR_TYPE_MAGNETIC_FIELD, + .init = yas530_init, + .deinit = yas530_deinit, + .activate = yas530_activate, + .deactivate = yas530_deactivate, + .set_delay = yas530_set_delay, + .get_data = yas530_get_data, + .activated = 0, + .needed = 0, + .poll_fd = -1, + .data = NULL, +}; diff --git a/sensors/yas_orientation.c b/sensors/yas_orientation.c new file mode 100644 index 0000000..3668d8b --- /dev/null +++ b/sensors/yas_orientation.c @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2013 Paul Kocialkowski + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define LOG_TAG "piranha_sensors" +#include + +#include "piranha_sensors.h" + +struct yas_orientation_data { + struct piranha_sensors_handlers *acceleration_sensor; + struct piranha_sensors_handlers *magnetic_sensor; + + char path_enable[PATH_MAX]; + char path_delay[PATH_MAX]; +}; + +int yas_orientation_init(struct piranha_sensors_handlers *handlers, + struct piranha_sensors_device *device) +{ + struct yas_orientation_data *data = NULL; + char path[PATH_MAX] = { 0 }; + int input_fd = -1; + int rc; + int i; + + ALOGD("%s(%p, %p)", __func__, handlers, device); + + if (handlers == NULL) + return -EINVAL; + + data = (struct yas_orientation_data *) calloc(1, sizeof(struct yas_orientation_data)); + + for (i = 0; i < device->handlers_count; i++) { + if (device->handlers[i] == NULL) + continue; + + if (device->handlers[i]->handle == SENSOR_TYPE_ACCELEROMETER) + data->acceleration_sensor = device->handlers[i]; + else if (device->handlers[i]->handle == SENSOR_TYPE_MAGNETIC_FIELD) + data->magnetic_sensor = device->handlers[i]; + } + + if (data->acceleration_sensor == NULL || data->magnetic_sensor == NULL) { + ALOGE("%s: Missing sensors for orientation", __func__); + goto error; + } + + input_fd = input_open("orientation"); + if (input_fd < 0) { + ALOGE("%s: Unable to open input", __func__); + goto error; + } + + rc = sysfs_path_prefix("orientation", (char *) &path); + if (rc < 0 || path[0] == '\0') { + ALOGE("%s: Unable to open sysfs", __func__); + goto error; + } + + snprintf(data->path_enable, PATH_MAX, "%s/enable", path); + snprintf(data->path_delay, PATH_MAX, "%s/delay", path); + + handlers->poll_fd = input_fd; + handlers->data = (void *) data; + + return 0; + +error: + if (data != NULL) + free(data); + + if (input_fd >= 0) + close(input_fd); + + handlers->poll_fd = -1; + handlers->data = NULL; + + return -1; +} + +int yas_orientation_deinit(struct piranha_sensors_handlers *handlers) +{ + ALOGD("%s(%p)", __func__, handlers); + + if (handlers == NULL) + return -EINVAL; + + if (handlers->poll_fd >= 0) + close(handlers->poll_fd); + handlers->poll_fd = -1; + + if (handlers->data != NULL) + free(handlers->data); + handlers->data = NULL; + + return 0; +} + +int yas_orientation_activate(struct piranha_sensors_handlers *handlers) +{ + struct yas_orientation_data *data; + int rc; + + ALOGD("%s(%p)", __func__, handlers); + + if (handlers == NULL || handlers->data == NULL) + return -EINVAL; + + data = (struct yas_orientation_data *) handlers->data; + + if (data->acceleration_sensor == NULL || data->magnetic_sensor == NULL) + return -1; + + data->acceleration_sensor->needed |= PIRANHA_SENSORS_NEEDED_ORIENTATION; + if (data->acceleration_sensor->needed == PIRANHA_SENSORS_NEEDED_ORIENTATION) + data->acceleration_sensor->activate(data->acceleration_sensor); + + data->magnetic_sensor->needed |= PIRANHA_SENSORS_NEEDED_ORIENTATION; + if (data->magnetic_sensor->needed == PIRANHA_SENSORS_NEEDED_ORIENTATION) + data->magnetic_sensor->activate(data->magnetic_sensor); + + rc = sysfs_value_write(data->path_enable, 1); + if (rc < 0) { + ALOGE("%s: Unable to write sysfs value", __func__); + return -1; + } + + handlers->activated = 1; + + return 0; +} + +int yas_orientation_deactivate(struct piranha_sensors_handlers *handlers) +{ + struct yas_orientation_data *data; + int rc; + + ALOGD("%s(%p)", __func__, handlers); + + if (handlers == NULL || handlers->data == NULL) + return -EINVAL; + + data = (struct yas_orientation_data *) handlers->data; + + if (data->acceleration_sensor == NULL || data->magnetic_sensor == NULL) + return -1; + + data->acceleration_sensor->needed &= ~(PIRANHA_SENSORS_NEEDED_ORIENTATION); + if (data->acceleration_sensor->needed == 0) + data->acceleration_sensor->deactivate(data->acceleration_sensor); + + data->magnetic_sensor->needed &= ~(PIRANHA_SENSORS_NEEDED_ORIENTATION); + if (data->magnetic_sensor->needed == 0) + data->magnetic_sensor->deactivate(data->magnetic_sensor); + + rc = sysfs_value_write(data->path_enable, 0); + if (rc < 0) { + ALOGE("%s: Unable to write sysfs value", __func__); + return -1; + } + + handlers->activated = 1; + + return 0; +} + +int yas_orientation_set_delay(struct piranha_sensors_handlers *handlers, long int delay) +{ + struct yas_orientation_data *data; + int d; + int rc; + + ALOGD("%s(%p, %ld)", __func__, handlers, delay); + + if (handlers == NULL || handlers->data == NULL) + return -EINVAL; + + data = (struct yas_orientation_data *) handlers->data; + + if (data->acceleration_sensor == NULL || data->magnetic_sensor == NULL) + return -1; + + if (data->acceleration_sensor->needed == PIRANHA_SENSORS_NEEDED_ORIENTATION) + data->acceleration_sensor->set_delay(data->acceleration_sensor, delay); + + if (data->magnetic_sensor->needed == PIRANHA_SENSORS_NEEDED_ORIENTATION) + data->magnetic_sensor->set_delay(data->magnetic_sensor, delay); + + if (delay < 10000000) + d = 10; + else + d = delay / 1000000; + + rc = sysfs_value_write(data->path_delay, d); + if (rc < 0) { + ALOGE("%s: Unable to write sysfs value", __func__); + return -1; + } + + return 0; +} + +float yas_orientation_convert(int value) +{ + return value / 1000.0f; +} + +int yas_orientation_get_data(struct piranha_sensors_handlers *handlers, + struct sensors_event_t *event) +{ + struct input_event input_event; + int input_fd; + int rc; + +// ALOGD("%s(%p, %p)", __func__, handlers, event); + + if (handlers == NULL || event == NULL) + return -EINVAL; + + input_fd = handlers->poll_fd; + if (input_fd < 0) + return -EINVAL; + + memset(event, 0, sizeof(struct sensors_event_t)); + event->version = sizeof(struct sensors_event_t); + event->sensor = handlers->handle; + event->type = handlers->handle; + + do { + rc = read(input_fd, &input_event, sizeof(input_event)); + if (rc < (int) sizeof(input_event)) + break; + + if (input_event.type == EV_ABS) { + switch (input_event.code) { + case ABS_X: + event->orientation.azimuth = yas_orientation_convert(input_event.value); + break; + case ABS_Y: + event->orientation.pitch = yas_orientation_convert(input_event.value); + break; + case ABS_Z: + event->orientation.roll = yas_orientation_convert(input_event.value); + break; + default: + continue; + } + } else if (input_event.type == EV_SYN) { + if (input_event.code == SYN_REPORT) + event->timestamp = input_timestamp(&input_event); + } + } while (input_event.type != EV_SYN); + + return 0; +} + +struct piranha_sensors_handlers yas_orientation = { + .name = "YAS Orientation", + .handle = SENSOR_TYPE_ORIENTATION, + .init = yas_orientation_init, + .deinit = yas_orientation_deinit, + .activate = yas_orientation_activate, + .deactivate = yas_orientation_deactivate, + .set_delay = yas_orientation_set_delay, + .get_data = yas_orientation_get_data, + .activated = 0, + .needed = 0, + .poll_fd = -1, + .data = NULL, +}; -- cgit v1.1