From c26533a08018cf9e76e7c12c54cba49b064ca181 Mon Sep 17 00:00:00 2001 From: Yunmi Ha Date: Wed, 8 Sep 2021 14:29:48 +0900 Subject: [PATCH] Upgrade sensorhub modules including pedometer sensor Change-Id: I459f25679c366c387f7ac78680b4bf3837e737a0 Signed-off-by: Yunmi Ha --- CMakeLists.txt | 1 - packaging/hal-backend-sensor-emulator.spec | 1 - src/hal-backend-sensor.cpp | 2 + src/sensor_common.h | 18 +- src/sensorhub/CMakeLists.txt | 8 - src/sensorhub/libsensorhub-hal.i586.so | Bin 112764 -> 0 bytes src/sensorhub/libsensorhub-hal.x86_64.so | Bin 114200 -> 0 bytes src/sensorhub/pedometer.cpp | 494 +++++++++++++++++++++ src/sensorhub/sensorhub.cpp | 233 ++++++++++ src/sensorhub/sensorhub.h | 59 +++ src/sensorhub/sensorhub_controller.cpp | 435 ++++++++++++++++++ src/sensorhub/sensorhub_controller.h | 55 +++ src/sensorhub/sensorhub_manager.cpp | 109 +++++ src/sensorhub/sensorhub_manager.h | 68 +++ src/sensorhub/sensorhub_sensor.cpp | 171 +++++++ src/sensorhub/sensorhub_sensor.h | 91 ++++ 16 files changed, 1731 insertions(+), 14 deletions(-) delete mode 100644 src/sensorhub/CMakeLists.txt delete mode 100755 src/sensorhub/libsensorhub-hal.i586.so delete mode 100755 src/sensorhub/libsensorhub-hal.x86_64.so create mode 100644 src/sensorhub/pedometer.cpp create mode 100644 src/sensorhub/sensorhub.cpp create mode 100644 src/sensorhub/sensorhub.h create mode 100644 src/sensorhub/sensorhub_controller.cpp create mode 100644 src/sensorhub/sensorhub_controller.h create mode 100644 src/sensorhub/sensorhub_manager.cpp create mode 100644 src/sensorhub/sensorhub_manager.h create mode 100644 src/sensorhub/sensorhub_sensor.cpp create mode 100644 src/sensorhub/sensorhub_sensor.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ec97ed..0b410e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -155,7 +155,6 @@ ENDIF() IF("${SENSORHUB}" STREQUAL "ON") FILE(GLOB_RECURSE SRCS ${SRCS} src/sensorhub/*.cpp) ADD_DEFINITIONS(-DENABLE_SENSORHUB) -ADD_SUBDIRECTORY(src/sensorhub) ENDIF() MESSAGE("Sources: ${SRCS}") diff --git a/packaging/hal-backend-sensor-emulator.spec b/packaging/hal-backend-sensor-emulator.spec index 353d666..cad49b1 100644 --- a/packaging/hal-backend-sensor-emulator.spec +++ b/packaging/hal-backend-sensor-emulator.spec @@ -52,5 +52,4 @@ install -m 0644 %SOURCE2 %{buildroot}%{_hal_libdir}/udev/rules.d %{_hal_libdir}/udev/rules.d/99-sensor.rules %{_hal_libdir}/udev/rules.d/99-sensorhub.rules %{_hal_libdir}/*.so* -%{_hal_libdir}/sensorhub/*.so* %{_hal_licensedir}/%{name}/LICENSE.APLv2 diff --git a/src/hal-backend-sensor.cpp b/src/hal-backend-sensor.cpp index e9ed041..e53472f 100644 --- a/src/hal-backend-sensor.cpp +++ b/src/hal-backend-sensor.cpp @@ -29,6 +29,7 @@ #include "pressure/pressure_device.h" #include "proxi/proxi_device.h" #include "ultraviolet/uv_device.h" +#include "sensorhub/sensorhub.h" static std::vector devs; @@ -57,6 +58,7 @@ static int sensor_emulator_create(sensor_device_t **devices) { create_sensor("Light"); create_sensor("Proximity"); create_sensor("HRM"); + create_sensor("Sensorhub"); *devices = &devs[0]; return devs.size(); diff --git a/src/sensor_common.h b/src/sensor_common.h index b549c96..9c56c2f 100644 --- a/src/sensor_common.h +++ b/src/sensor_common.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * Copyright (c) 2017 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,14 +15,23 @@ * */ -#ifndef _SENSOR_COMMON_H_ -#define _SENSOR_COMMON_H_ +#ifndef __SENSOR_COMMON_H__ +#define __SENSOR_COMMON_H__ +#define SENSOR_LIB_SHIFT 16 #define SENSOR_EVENT_SHIFT 16 #define RAW_DATA_EVENT 0x0001 +#define SENSOR_DEVICE_ID(type, index) ((type) << SENSOR_LIB_SHIFT | (index)) +#define SENSOR_EVENT_TYPE(type) ((type) << SENSOR_EVENT_SHIFT | RAW_DATA_EVENT) +#define SHORT_TO_UINT64(value, shift) (((unsigned long long)(value & 0xFFFF)) << shift) +#define CONTEXT_SENSOR_ID 0xFFFF0001 + +#define NS_TO_US(x) ((x) / 1000) + #define UNKNOWN_NAME "UNKNOWN" + enum sensorhub_enable_bit { SENSORHUB_ACCELEROMETER_ENABLE_BIT = 0, SENSORHUB_GYROSCOPE_ENABLE_BIT, @@ -53,4 +62,5 @@ enum sensorhub_enable_bit { SENSORHUB_ENABLE_BIT_MAX, }; -#endif /* _SENSOR_COMMON_H_ */ + +#endif /* __SENSOR_COMMON_H__ */ diff --git a/src/sensorhub/CMakeLists.txt b/src/sensorhub/CMakeLists.txt deleted file mode 100644 index a53a731..0000000 --- a/src/sensorhub/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -IF("${BUILD_ARCH}" STREQUAL "x86") - INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/libsensorhub-hal.i586.so DESTINATION ${HAL_LIBDIR}/sensorhub/ COMPONENT RuntimeLibraries) -ENDIF() - -IF("${BUILD_ARCH}" STREQUAL "x86_64") - INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/libsensorhub-hal.x86_64.so DESTINATION ${HAL_LIBDIR}/sensorhub/ COMPONENT RuntimeLibraries) -ENDIF() - diff --git a/src/sensorhub/libsensorhub-hal.i586.so b/src/sensorhub/libsensorhub-hal.i586.so deleted file mode 100755 index eb249e6d8087d9f506e1365e327ba00a522f6103..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 112764 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%0D!woFAd+}@r8k0ZFft^Ake5*XYoWo@BHtbS;( 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 +//#include +#include +#include + +#include "sensorhub_sensor.h" +#include "sensorhub_manager.h" + +#define PEDOMETER_NAME "SENSOR_PEDOMETER" +#define PAYLOAD_SFACTOR 84 + +#define GENDER_MALE 1 +#define GENDER_FEMALE 2 + +#define DEFAULT_HEIGHT 170 +#define DEFAULT_WEIGHT 65 +#define DEFAULT_GENDER GENDER_MALE +#define DEFAULT_AGE 30 + +#define GET_UINT8(data, cur) ((unsigned char)data[cur++]) +#define GET_UINT16(data, cur) get_uint(data, cur, 2) +#define GET_UINT24(data, cur) get_uint(data, cur, 3) +#define GET_UINT32(data, cur) get_uint(data, cur, 4) + +static const sensor_info_t sensor_info = { + id: SENSOR_DEVICE_ID(SHUB_LIB_PEDOMETER, 0x1), + name: PEDOMETER_NAME, + type: SENSOR_DEVICE_HUMAN_PEDOMETER, + event_type: SENSOR_EVENT_TYPE(SENSOR_DEVICE_HUMAN_PEDOMETER), + model_name: "Human Pedometer Sensor", + vendor: "Samsung Electronics", + min_range: 0, + max_range: 16777216, /* 2^24 */ + resolution: 1, + min_interval: 0, + max_batch_count: 0, + wakeup_supported: true +}; + +enum value_index { + IDX_STEPS = 0, + IDX_WALK_STEPS, + IDX_RUN_STEPS, + IDX_DISTANCE, + IDX_CALORIES, + IDX_SPEED, + IDX_FREQUENCY, + IDX_STATE, + NUM_VALUES, + IDX_WALK_UP = NUM_VALUES, + IDX_WALK_DOWN, + IDX_RUN_UP, + IDX_RUN_DOWN, + IDX_STATE_EX, +}; + +/* SSP's step state protocol */ +enum ssp_step_state { + SSP_STEP_STATE_UNKNOWN = -1, + SSP_STEP_STATE_STOP, + SSP_STEP_STATE_MARK, + SSP_STEP_STATE_STROLL, + SSP_STEP_STATE_WALK, + SSP_STEP_STATE_RUN, + SSP_STEP_STATE_RUSH, + SSP_STEP_STATE_WALK_UP, + SSP_STEP_STATE_WALK_DOWN, + SSP_STEP_STATE_RUN_UP, + SSP_STEP_STATE_RUN_DOWN +}; + +/* SensorFW's step status protocol */ +enum step_state { + STEP_STATE_UNKNOWN = -1, + STEP_STATE_STOP, + STEP_STATE_WALK, + STEP_STATE_RUN, +}; + +static int convert_step_status(int sh_stat) +{ + switch (sh_stat) { + case SSP_STEP_STATE_STOP: + case SSP_STEP_STATE_MARK: + return STEP_STATE_STOP; + + case SSP_STEP_STATE_STROLL: + case SSP_STEP_STATE_WALK: + case SSP_STEP_STATE_WALK_UP: + case SSP_STEP_STATE_WALK_DOWN: + return STEP_STATE_WALK; + + case SSP_STEP_STATE_RUN: + case SSP_STEP_STATE_RUSH: + case SSP_STEP_STATE_RUN_UP: + case SSP_STEP_STATE_RUN_DOWN: + return STEP_STATE_RUN; + + default: + return STEP_STATE_UNKNOWN; + } +} + +static void get_date_and_time(int *base_date, int *base_time) +{ + struct timeval now; + struct tm tm_now; + time_t now_sec; + + gettimeofday(&now, NULL); + now_sec = now.tv_sec; + gmtime_r(&now_sec, &tm_now); + + *base_time = tm_now.tm_hour * 3600 + tm_now.tm_min * 60 + tm_now.tm_sec; + *base_date = now.tv_sec - *base_time; +} + +static int add_date_to_ssp_time(int ssp_time, int base_date, int base_time) +{ + // SensorHub Patch 0301: if time stamp represents future, set it to current time + int diff = base_time - ssp_time; + if (diff < 0 && diff > -1500) + ssp_time = base_time; + + if (ssp_time - base_time > 43200) + return ssp_time + base_date - 86400; + + return ssp_time + base_date; +} + +/* Big Endian */ +static unsigned int get_uint(const char *data, int &cursor, int bytes = 1) +{ + unsigned int sum = 0; + for (int i = 0; i < bytes; ++i) { + sum = (sum << 8) + (unsigned char)data[cursor++]; + } + return sum; +} + +class sh_pedometer_sensor : public sensorhub_sensor { +public: + sh_pedometer_sensor(); + + bool enable(void); + bool disable(void); + + int parse(const char *data, int data_len); + int get_data(sensor_data_t **data, int *length); + + bool set_attribute_int(int32_t attribute, int32_t value); + bool set_attribute_str(int32_t key, char *value, int len); + + bool flush(void); + +private: + int m_height; + int m_weight; + int m_gender; + int m_age; + bool m_is_sfactor; + sensor_data_t *m_data; + + int parse_basic(const char *data, int data_len, int cursor); + int parse_extended(const char *data, int data_len, int cursor); + int parse_batch(const char *data, int data_len, int cursor); + int parse_scale_factor(const char *data, int data_len, int cursor); +}; + +sh_pedometer_sensor::sh_pedometer_sensor() +: sensorhub_sensor(&sensor_info) +, m_height(DEFAULT_HEIGHT) +, m_weight(DEFAULT_WEIGHT) +, m_gender(DEFAULT_GENDER) +, m_age(DEFAULT_AGE) +, m_is_sfactor(false) +, m_data(NULL) +{ +} + +bool sh_pedometer_sensor::enable(void) +{ + char cmd[] = {SHUB_INST_LIB_ADD, SHUB_LIB_PEDOMETER, 0, 0, 0}; + cmd[2] = (char)m_height; + cmd[3] = (char)m_weight; + cmd[4] = (char)m_gender; + + if (!m_controller) { + _E("Controller is not set : %s", PEDOMETER_NAME); + return false; + } + + if (m_controller->send_sensorhub_data(cmd, sizeof(cmd)) < 0) + _E("sensor enable fail"); + + return sensorhub_sensor::enable(); +} + +bool sh_pedometer_sensor::disable(void) +{ + return sensorhub_sensor::enable(false, SHUB_LIB_PEDOMETER); +} + +int sh_pedometer_sensor::parse(const char *data, int data_len) +{ + unsigned char mode; + int cursor = SHUB_IDX_LIBTYPE + 1; + + free(m_data); + m_data = NULL; + m_is_sfactor = false; + + /* Pedometer packet type ext: "1, 3, 3, 1, ..." */ + if (data[SHUB_IDX_TYPE] == SHUB_TYPE_LIB_EXT) + ++cursor; + + if (cursor >= data_len) + return -EINVAL; + + mode = (unsigned char)data[cursor] & 0xE0; + + if (mode == 0x80) { + _D("Extended pedometer packet"); + return parse_extended(data, data_len, cursor); + } else if (mode == 0xc0) { + _D("Batch pedometer packet"); + return parse_batch(data, data_len, cursor); + } else if (mode == 0xe0) { + _D("Scale factor"); + return parse_scale_factor(data, data_len, cursor); + } else { + _D("Normal pedometer packet"); + return parse_basic(data, data_len, cursor); + } +} + +int sh_pedometer_sensor::parse_basic(const char *data, int data_len, int cursor) +{ + if (data_len - cursor < 15) + return -EINVAL; + + sensor_pedometer_data_t *pedometer_data = (sensor_pedometer_data_t*)malloc(sizeof(sensor_pedometer_data_t)); + retv_if(pedometer_data == NULL, -ENOMEM); + + pedometer_data->values[IDX_WALK_STEPS] = (GET_UINT8(data, cursor) & 0x7F); + pedometer_data->values[IDX_RUN_STEPS] = GET_UINT8(data, cursor); + + ++cursor; // Up & down step count + + pedometer_data->values[IDX_STEPS] = GET_UINT16(data, cursor); + pedometer_data->values[IDX_DISTANCE] = GET_UINT16(data, cursor) / 100.0; + pedometer_data->values[IDX_SPEED] = GET_UINT8(data, cursor) / 10.0; + pedometer_data->values[IDX_STATE_EX] = data[cursor++]; + pedometer_data->values[IDX_STATE] = convert_step_status((int)pedometer_data->values[IDX_STATE_EX]); + pedometer_data->values[IDX_CALORIES] = GET_UINT8(data, cursor) / 100.0; + pedometer_data->values[IDX_FREQUENCY] = GET_UINT8(data, cursor) / 10.0; + + pedometer_data->values[IDX_WALK_UP] = GET_UINT8(data, cursor); + pedometer_data->values[IDX_WALK_DOWN] = GET_UINT8(data, cursor); + pedometer_data->values[IDX_RUN_UP] = GET_UINT8(data, cursor); + pedometer_data->values[IDX_RUN_DOWN] = GET_UINT8(data, cursor); + + pedometer_data->diffs_count = 0; + pedometer_data->accuracy = SENSOR_ACCURACY_GOOD; + pedometer_data->timestamp = util::get_timestamp(); + pedometer_data->value_count = NUM_VALUES; + + m_data = (sensor_data_t *)pedometer_data; + + return cursor; +} + +int sh_pedometer_sensor::parse_extended(const char *data, int data_len, int cursor) +{ + ++cursor; + + if (data_len - cursor < 32) + return -EINVAL; + + sensor_pedometer_data_t *pedometer_data = (sensor_pedometer_data_t*)malloc(sizeof(sensor_pedometer_data_t)); + retv_if(pedometer_data == NULL, -ENOMEM); + + pedometer_data->values[IDX_WALK_STEPS] = GET_UINT24(data, cursor); + pedometer_data->values[IDX_RUN_STEPS] = GET_UINT24(data, cursor); + + cursor += 3; // Up & down step count + + pedometer_data->values[IDX_STEPS] = GET_UINT24(data, cursor); + pedometer_data->values[IDX_DISTANCE] = GET_UINT24(data, cursor) / 100.0; + pedometer_data->values[IDX_SPEED] = GET_UINT8(data, cursor) / 10.0; + pedometer_data->values[IDX_STATE_EX] = data[cursor++]; + pedometer_data->values[IDX_STATE] = convert_step_status((int)pedometer_data->values[IDX_STATE_EX]); + pedometer_data->values[IDX_CALORIES] = GET_UINT16(data, cursor) / 10.0; + pedometer_data->values[IDX_FREQUENCY] = GET_UINT8(data, cursor) / 10.0; + + pedometer_data->values[IDX_WALK_UP] = GET_UINT24(data, cursor); + pedometer_data->values[IDX_WALK_DOWN] = GET_UINT24(data, cursor); + pedometer_data->values[IDX_RUN_UP] = GET_UINT24(data, cursor); + pedometer_data->values[IDX_RUN_DOWN] = GET_UINT24(data, cursor); + + pedometer_data->diffs_count = 0; + pedometer_data->accuracy = SENSOR_ACCURACY_GOOD; + pedometer_data->timestamp = util::get_timestamp(); + pedometer_data->value_count = NUM_VALUES; + + m_data = (sensor_data_t *)pedometer_data; + + return cursor; +} + +int sh_pedometer_sensor::parse_batch(const char *data, int data_len, int cursor) +{ + static const int BATCH_UNIT_SIZE = 20; + int cnt; + int base_date; + int base_time; + + ++cursor; + + if (!(cursor < data_len)) + return -EINVAL; + + cnt = GET_UINT8(data, cursor); + if (cnt == 0 || cnt > SENSOR_PEDOMETER_DATA_DIFFS_SIZE || cursor + BATCH_UNIT_SIZE * cnt > data_len) + return -EINVAL; + + sensor_pedometer_data_t *pedometer_data = (sensor_pedometer_data_t*)malloc(sizeof(sensor_pedometer_data_t)); + retv_if(pedometer_data == NULL, -ENOMEM); + + pedometer_data->values[IDX_STATE] = STEP_STATE_UNKNOWN; + pedometer_data->values[IDX_STATE_EX] = SSP_STEP_STATE_UNKNOWN; + pedometer_data->values[IDX_FREQUENCY] = -1; + pedometer_data->diffs_count = cnt; + + get_date_and_time(&base_date, &base_time); + + pedometer_data->values[IDX_STEPS] = 0; + pedometer_data->values[IDX_WALK_STEPS] = 0; + pedometer_data->values[IDX_RUN_STEPS] = 0; + pedometer_data->values[IDX_DISTANCE] = 0; + pedometer_data->values[IDX_CALORIES] = 0; + + pedometer_data->values[IDX_WALK_UP] = 0; + pedometer_data->values[IDX_WALK_DOWN] = 0; + pedometer_data->values[IDX_RUN_UP] = 0; + pedometer_data->values[IDX_RUN_DOWN] = 0; + + for (cnt = 0; cnt < pedometer_data->diffs_count; ++cnt) { + pedometer_data->diffs[cnt].timestamp = add_date_to_ssp_time(GET_UINT32(data, cursor) / 1000, base_date, base_time); + pedometer_data->diffs[cnt].distance = GET_UINT16(data, cursor) / 100.0; + pedometer_data->diffs[cnt].calories = GET_UINT16(data, cursor) / 100.0; + pedometer_data->diffs[cnt].walk_steps = GET_UINT8(data, cursor); + pedometer_data->diffs[cnt].run_steps = GET_UINT16(data, cursor); + + pedometer_data->diffs[cnt].walk_up_steps = GET_UINT8(data, cursor); + pedometer_data->diffs[cnt].walk_down_steps = GET_UINT8(data, cursor); + pedometer_data->diffs[cnt].run_up_steps = GET_UINT16(data, cursor); + pedometer_data->diffs[cnt].run_down_steps = GET_UINT16(data, cursor); + + pedometer_data->diffs[cnt].speed = GET_UINT8(data, cursor) / 10.0; + pedometer_data->diffs[cnt].steps = GET_UINT16(data, cursor); + + pedometer_data->values[IDX_STEPS] += pedometer_data->diffs[cnt].steps; + pedometer_data->values[IDX_WALK_STEPS] += pedometer_data->diffs[cnt].walk_steps; + pedometer_data->values[IDX_RUN_STEPS] += pedometer_data->diffs[cnt].run_steps; + pedometer_data->values[IDX_DISTANCE] += pedometer_data->diffs[cnt].distance; + pedometer_data->values[IDX_CALORIES] += pedometer_data->diffs[cnt].calories; + + pedometer_data->values[IDX_WALK_UP] += pedometer_data->diffs[cnt].walk_up_steps; + pedometer_data->values[IDX_WALK_DOWN] += pedometer_data->diffs[cnt].walk_down_steps; + pedometer_data->values[IDX_RUN_UP] += pedometer_data->diffs[cnt].run_up_steps; + pedometer_data->values[IDX_RUN_DOWN] += pedometer_data->diffs[cnt].run_down_steps; + } + + pedometer_data->values[IDX_SPEED] = pedometer_data->diffs[pedometer_data->diffs_count - 1].speed; + pedometer_data->accuracy = SENSOR_ACCURACY_GOOD; + pedometer_data->timestamp = util::get_timestamp(); + pedometer_data->value_count = NUM_VALUES; + + m_data = (sensor_data_t *)pedometer_data; + + return cursor; +} + +int sh_pedometer_sensor::parse_scale_factor(const char *data, int data_len, int cursor) +{ + /* Packet Format: 1, 1, 3, 0xe0, uint32_t * 20 */ + retv_if(data_len < PAYLOAD_SFACTOR, -EINVAL); + + /* Sensorhub Pedometer may emit its scale factor, an array 20 integer values. + Because this scale factor cannot be bundled in sensor_pedometer_data_t format, + as an walkaround, it is stored in sensorhub_data_t format instead. + sensord needs to hook this scale factor packet. + (Apps do not need to receive this scale factor.) */ + sensorhub_data_t *sfactor_data = (sensorhub_data_t*)malloc(sizeof(sensorhub_data_t)); + retv_if(sfactor_data == NULL, -ENOMEM); + + sfactor_data->value_count = PAYLOAD_SFACTOR; + memcpy(sfactor_data->values, data, PAYLOAD_SFACTOR); + + m_data = (sensor_data_t *)sfactor_data; + m_is_sfactor = true; + + return PAYLOAD_SFACTOR; +} + +int sh_pedometer_sensor::get_data(sensor_data_t **data, int *length) +{ + *data = m_data; + *length = m_is_sfactor ? sizeof(sensorhub_data_t) : sizeof(sensor_pedometer_data_t); + retv_if(*data == NULL, -EINVAL); + m_data = NULL; + return 0; +} + +bool sh_pedometer_sensor::set_attribute_int(int32_t attribute, int32_t value) +{ + char cmd[3] = {SHUB_INST_LIB_PUTVALUE, 0, 0}; + + if (!m_controller) { + _E("Controller is not set : %s", PEDOMETER_NAME); + return false; + } + + switch (attribute) { + case SENSOR_ATTR_PEDOMETER_HEIGHT: + cmd[1] = SHUB_PROP_USR_INFO_HEIGHT; + cmd[2] = m_height = value; + break; + case SENSOR_ATTR_PEDOMETER_WEIGHT: + cmd[1] = SHUB_PROP_USR_INFO_WEIGHT; + cmd[2] = m_weight = value; + break; + case SENSOR_ATTR_PEDOMETER_GENDER: + cmd[1] = SHUB_PROP_USR_INFO_GENDER; + cmd[2] = m_gender = value; + break; + case SENSOR_ATTR_PEDOMETER_AGE: + cmd[1] = SHUB_PROP_USR_INFO_AGE; + cmd[2] = m_age = value; + break; + case SENSOR_ATTR_PEDOMETER_FLUSH: + return flush(); + default : + _E("Invalid attribute: %d, value: %d", attribute, value); + return false; +} + + if (m_controller->send_sensorhub_data(cmd, sizeof(cmd)) < 0) + return false; + + sensorhub_sensor::set_attribute_int(attribute, value); + return true; +} + +bool sh_pedometer_sensor::set_attribute_str(int32_t attribute, char *value, int len) +{ + /* TODO: handle scale factor */ + return false; +} + +bool sh_pedometer_sensor::flush(void) +{ + char cmd[] = {SHUB_INST_LIB_GETVALUE, SHUB_LIB_PEDOMETER, SHUB_EXT_CURRENT_INFO, 0}; + + if (!m_controller) { + _E("Controller is not set : %s", PEDOMETER_NAME); + return false; + } + + if (m_controller->send_sensorhub_data(cmd, sizeof(cmd)) < 0) + return false; + + return true; +} + +REGISTER_SENSORHUB_LIB(sensor_info, sh_pedometer_sensor) diff --git a/src/sensorhub/sensorhub.cpp b/src/sensorhub/sensorhub.cpp new file mode 100644 index 0000000..130e46f --- /dev/null +++ b/src/sensorhub/sensorhub.cpp @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include + +#include "sensorhub.h" +#include "sensor_common.h" +#include "sensorhub_controller.h" +#include "sensorhub_manager.h" + +sensorhub_device::sensorhub_device() +{ + controller = new(std::nothrow) sensorhub_controller; + if (!controller) { + _E("Failed to allocated memory"); + throw; + } + + manager = &sensorhub_manager::get_instance(); + if (!manager) { + _E("Failed to allocated memory"); + throw; + } + manager->set_controller(controller); + + _I("sensorhub_device is created!"); +} + +sensorhub_device::~sensorhub_device() +{ + delete controller; + _I("sensorhub_device is destroyed!"); +} + +int sensorhub_device::get_poll_fd(void) +{ + return controller->get_poll_fd(); +} + +int sensorhub_device::get_sensors(const sensor_info_t **sensors) +{ + int size; + + retvm_if(sensors == NULL || sensors == nullptr, -EINVAL, "sensorhub_device:NULL interface"); + + size = manager->get_sensor_infos(sensors); + + return size; +} + +bool sensorhub_device::enable(uint32_t id) +{ + controller->enable(); + sensorhub_sensor *sensor = manager->get_sensor(id); + + if (!sensor) { + _E("Failed to enable sensor(0x%x)", id); + return false; + } + + return sensor->enable(); +} + +bool sensorhub_device::disable(uint32_t id) +{ + controller->disable(); + sensorhub_sensor *sensor = manager->get_sensor(id); + + if (!sensor) { + _E("Failed to disable sensor(0x%x)", id); + return false; + } + + return sensor->disable(); +} + +bool sensorhub_device::set_interval(uint32_t id, unsigned long val) +{ + sensorhub_sensor *sensor = manager->get_sensor(id); + + if (!sensor) { + _E("Failed to set interval to sensor(0x%x)", id); + return false; + } + + return sensor->set_interval(val); +} + +bool sensorhub_device::set_batch_latency(uint32_t id, unsigned long val) +{ + sensorhub_sensor *sensor = manager->get_sensor(id); + + if (!sensor) { + _E("Failed to set batch latency to sensor(0x%x)", id); + return false; + } + + return sensor->set_batch_latency(val); +} + +bool sensorhub_device::set_attribute_int(uint32_t id, int32_t attribute, int32_t value) +{ + sensorhub_sensor *sensor = manager->get_sensor(id); + + if (!sensor) { + _E("Failed to set attribute to sensor(0x%x)", id); + return false; + } + + return sensor->set_attribute_int(attribute, value); +} + +bool sensorhub_device::set_attribute_str(uint32_t id, int32_t attribute, char *value, int len) +{ + sensorhub_sensor *sensor = manager->get_sensor(id); + + if (!sensor) { + _E("Failed to set attribute to sensor(0x%x)", id); + return false; + } + + return sensor->set_attribute_str(attribute, value, len); +} + +int sensorhub_device::read_fd(uint32_t **ids) +{ + sensorhub_data_t data; + + // step 1 + if (!controller->read_fd(data)) + return 0; + + // step 2 + const char *hub_data = data.values; + int data_len = data.value_count; + + // step 3 + event_ids.clear(); + + /* Sensorhub Sensors */ + while (data_len > 0) { + _D("Remaining data length: %d", data_len); + int parsed = parse(hub_data, data_len); + if (parsed <= 0) { + _I("Parsing failed"); + break; + } + + data_len -= parsed; + hub_data += parsed; + } + + // step 4 + int size = event_ids.size(); + + if (event_ids.empty()) + return 0; + + *ids = &event_ids[0]; + + return size; +} + +int sensorhub_device::get_data(uint32_t id, sensor_data_t **data, int *length) +{ + int remains = 1; + + sensorhub_sensor *sensor = manager->get_sensor(id); + if (!sensor) { + _E("Failed to get data from sensor(0x%x)", id); + return -EINVAL; + } + + remains = sensor->get_data(data, length); + + return remains; +} + +bool sensorhub_device::flush(uint32_t id) +{ + sensorhub_sensor *sensor = manager->get_sensor(id); + if (!sensor) { + _E("Failed to get data from sensor(0x%x)", id); + return false; + } + + return sensor->flush(); +} + +int sensorhub_device::parse(const char *hub_data, int data_len) +{ + return parse_data(hub_data, data_len); +} + +int sensorhub_device::parse_data(const char *hub_data, int data_len) +{ + int size = 0; + char libtype; + + libtype = hub_data[SHUB_IDX_LIBTYPE]; + if (!manager->get_sensors(libtype, active_sensors)) { + _E("Unknown Sensorhub lib type: %d", libtype); + return -EINVAL; + } + + for (auto const &sensor : active_sensors) { + size = sensor->parse(hub_data, data_len); + if (size <= 0) + break; + + event_ids.push_back(sensor->get_id()); + } + + active_sensors.clear(); + + return size; +} diff --git a/src/sensorhub/sensorhub.h b/src/sensorhub/sensorhub.h new file mode 100644 index 0000000..98c5784 --- /dev/null +++ b/src/sensorhub/sensorhub.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * 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. + * + */ + +#ifndef _SENSORHUB_DEVICE_H_ +#define _SENSORHUB_DEVICE_H_ + +#include +#include + +#include "sensorhub_controller.h" +#include "sensorhub_manager.h" + +class sensorhub_device : public sensor_device { +public: + sensorhub_device(); + virtual ~sensorhub_device(); + + int get_poll_fd(void); + int get_sensors(const sensor_info_t **sensors); + + bool enable(uint32_t id); + bool disable(uint32_t id); + + bool set_interval(uint32_t id, unsigned long val); + bool set_batch_latency(uint32_t id, unsigned long val); + bool set_attribute_int(uint32_t id, int32_t attribute, int32_t value); + bool set_attribute_str(uint32_t id, int32_t attribute, char *value, int len); + + int read_fd(uint32_t **ids); + int get_data(uint32_t id, sensor_data_t **data, int *length); + + bool flush(uint32_t id); + + std::vector active_sensors; + +private: + sensorhub_manager *manager; + sensorhub_controller *controller; + std::vector event_ids; + + int parse(const char *hub_data, int data_len); + int parse_data(const char *hub_data, int data_len); +}; + +#endif /* _SENSORHUB_DEVICE_H_ */ diff --git a/src/sensorhub/sensorhub_controller.cpp b/src/sensorhub/sensorhub_controller.cpp new file mode 100644 index 0000000..2df8fc0 --- /dev/null +++ b/src/sensorhub/sensorhub_controller.cpp @@ -0,0 +1,435 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "sensorhub_controller.h" + +#define EVENT_TYPE_SENSORHUB_DATA REL_RX +#define EVENT_TYPE_LARGE_SENSORHUB_DATA REL_RY +#define EVENT_TYPE_SENSORHUB_NOTI REL_RZ + +#define SSP_INPUT_NODE_NAME "ssp_context" +#define SSPSENSORHUB_DEVICE "/dev/ssp_sensorhub" +#define SENSORHUB_IOCTL_MAGIC 'S' +#define IOCTL_READ_LARGE_SENSORHUB_DATA _IOR(SENSORHUB_IOCTL_MAGIC, 3, unsigned int) + +#define INJECTION_NODE_PATH "/run/sensord/sensorhub" +#define INJECTION_ENABLE 1 +#define SENSOR_ATTR_SENSORHUB_INJECT_VALUE CONVERT_TYPE_ATTR(SENSOR_DEVICE_CONTEXT, 0x1) + +sensorhub_controller::sensorhub_controller() +: m_enabled(false) +, m_poll_node(-1) +, m_data_node(-1) +, m_injection_mode(false) +{ + /* initialize sensor_data for sensorhub */ + m_pending_data.accuracy = SENSOR_ACCURACY_UNDEFINED; + m_pending_data.timestamp = 0; + m_pending_data.value_count = 0; + memset(m_pending_data.values, 0, sizeof(m_pending_data.values)); + + m_data.accuracy = SENSOR_ACCURACY_UNDEFINED; + m_data.timestamp = 0; + m_data.value_count = 0; + memset(m_data.values, 0, sizeof(m_data.values)); + + /* initialize polling node */ + m_poll_node = open_input_node(SSP_INPUT_NODE_NAME); + if (m_poll_node < 0) + throw ENXIO; + + /* initialize sensorhub input node */ + if ((m_data_node = open(SSPSENSORHUB_DEVICE, O_RDWR)) < 0) { + _E("Open sensorhub device failed(%d)", m_data_node); + throw ENXIO; + } + + _I("m_data_node = %s", SSPSENSORHUB_DEVICE); +} + +sensorhub_controller::~sensorhub_controller() +{ + if (m_poll_node >= 0) + close(m_poll_node); + + if (m_data_node >= 0) + close(m_data_node); +} + +int sensorhub_controller::get_poll_fd(void) +{ + return m_poll_node; +} + +bool sensorhub_controller::enable(void) +{ + if (m_enabled == 0) + _I("Enable Sensorhub"); + + m_enabled++; + return true; +} + +bool sensorhub_controller::disable(void) +{ + if (m_enabled == 0) { + _E("sensorhub is already disabled"); + return false; + } + + if (m_enabled == 1) + _I("Disable Sensorhub"); + + m_enabled--; + return true; +} + +int sensorhub_controller::open_input_node(const char* input_node) +{ + size_t length; + int fd = -1; + const char *dirname = "/dev/input"; + char devname[PATH_MAX] = {0}; + char *filename; + DIR *dir; + struct dirent *entry; + + _I("======================start open_input_node============================="); + + dir = opendir(dirname); + + if (dir == NULL) + return -EINVAL; + + strncpy(devname, dirname, strlen(dirname)+1); + + filename = devname + strlen(devname); + *filename++ = '/'; + + while (true) { + entry = readdir(dir); + if (!entry) break; + + if (entry->d_name[0] == '.' && + (entry->d_name[1] == '\0' || + (entry->d_name[1] == '.' && entry->d_name[2] == '\0'))) + continue; + + length = strlen(entry->d_name); + strncpy(filename, entry->d_name, length+1); + + /* Need to be terminated with a null character */ + filename[length] = '\0'; + + fd = open(devname, O_RDONLY); + + if (fd < 0) + continue; + + char name[80]; + if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) + name[0] = '\0'; + + if (!strcmp(name, input_node)) { + _I("m_poll_node = %s", devname); + break; + } + + close(fd); + fd = -1; + } + + closedir(dir); + + if (fd < 0) + _E("couldn't find '%s' input device", input_node); + + return fd; +} + +bool sensorhub_controller::read_fd(sensorhub_data_t &data) +{ + const int INPUT_MAX_BEFORE_SYN = 10; + struct input_event sensorhub_input; + int read_input_cnt = 0; + int sensorhub_len = 0; + bool syn = false; + + fd_set readfds, exceptfds; + + FD_ZERO(&readfds); + FD_ZERO(&exceptfds); + + FD_SET(m_poll_node, &readfds); + FD_SET(m_poll_node, &exceptfds); + + while ((syn == false) && (read_input_cnt < INPUT_MAX_BEFORE_SYN)) { + int input_len = read(m_poll_node, &sensorhub_input, sizeof(sensorhub_input)); + + if (input_len != sizeof(sensorhub_input)) { + _E("sensorhub node read fail, read_len = %d", input_len); + return false; + } + + ++read_input_cnt; + + if (sensorhub_input.type == EV_REL) { + float value = sensorhub_input.value; + + if (sensorhub_input.code == EVENT_TYPE_SENSORHUB_DATA) { + _D("EVENT_TYPE_SENSORHUB_DATA, value_count=%g", value); + m_pending_data.value_count = value; + sensorhub_len = read_sensorhub_data(); + + if (sensorhub_len == 0) + _E("No library data"); + else if (sensorhub_len < 0) + _E("read_sensorhub_data() err(%d)", sensorhub_len); + + } else if (sensorhub_input.code == EVENT_TYPE_LARGE_SENSORHUB_DATA) { + _D("EVENT_TYPE_LARGE_SENSORHUB_DATA, value_count=%g", value); + m_pending_data.value_count = value; + sensorhub_len = read_large_sensorhub_data(); + + if (sensorhub_len == 0) + _E("No large library data"); + else if (sensorhub_len < 0) + _E("read_large_sensorhub_data() err(%d)", sensorhub_len); + + } + } else if (sensorhub_input.type == EV_SYN) { + syn = true; + + if (!m_enabled || (sensorhub_len <= 0)) + break; + + m_data = m_pending_data; + data = m_data; + _D("sensorhub event is received!"); + } else { + _E("Unknown event (type=%d, code=%d)", sensorhub_input.type, sensorhub_input.code); + } + } + + if (syn == false) { + _E("EV_SYN didn't come until %d inputs had come", read_input_cnt); + return false; + } + + return true; +} + +int sensorhub_controller::read_sensorhub_data(void) +{ + int ret = 0; + + if (m_data_node < 0) { + _E("Invalid sensorhub fd(%d)", m_data_node); + return -ENODEV; + } + +read: + ret = read(m_data_node, m_pending_data.values, m_pending_data.value_count); + + if (ret > 0) { + m_pending_data.value_count = ret; + print_sensorhub_data(__FUNCTION__, m_pending_data.values, m_pending_data.value_count); + } else if (ret < 0) { + if (errno == EINTR) { + _E("EINTR! retry read"); + goto read; + } + + _ERRNO(errno, _E, "Failed to read data"); + return -errno; + } + + return ret; +} + +int sensorhub_controller::read_large_sensorhub_data(void) +{ + int ret = 0; + + if (m_data_node < 0) { + _E("Invalid sensorhub fd(%d)", m_data_node); + return -ENODEV; + } + +ioctl: + ret = ioctl(m_data_node, IOCTL_READ_LARGE_SENSORHUB_DATA, m_pending_data.values); + + if (ret > 0) { + m_pending_data.value_count = ret; + print_sensorhub_data(__FUNCTION__, m_pending_data.values, m_pending_data.value_count); + } else if (ret < 0) { + if (errno == EINTR) { + _I("EINTR! retry ioctl"); + goto ioctl; + } + + _ERRNO(errno, _E, "Failed to ioctl"); + return -errno; + } + + return ret; +} + +int sensorhub_controller::send_sensorhub_data(const char *data, int data_len) +{ + int ret; + + if (data_len <= 0) { + _E("Invalid data_len(%d)", data_len); + return -EINVAL; + } + + if (m_data_node < 0) { + _E("Invalid sensorhub fd(%d)", m_data_node); + return -ENODEV; + } + + if (m_injection_mode) + return -EPERM; + + print_sensorhub_data(__FUNCTION__, data, data_len); + +write: + ret = write(m_data_node, data, data_len); + if (ret < 0) { + if (errno == EINTR) { + _I("EINTR! retry to write"); + goto write; + } + _ERRNO(errno, _E, "Failed to write data"); + } + + return ret < 0 ? -errno : ret; +} + +char *sensorhub_controller::base64_encode(const unsigned char *data, size_t input_length) +{ + const char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/'}; + const int mod_table[] = {0, 2, 1}; + int output_length = ((input_length - 1) / 3) * 4 + 5; + + char *encoded_data = (char *)malloc(output_length); + if (encoded_data == NULL) return NULL; + + for (size_t i = 0, j = 0; i < input_length;) { + uint32_t octet_a = i < input_length ? (unsigned char)data[i++] : 0; + uint32_t octet_b = i < input_length ? (unsigned char)data[i++] : 0; + uint32_t octet_c = i < input_length ? (unsigned char)data[i++] : 0; + + uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c; + + encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F]; + encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F]; + encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F]; + encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F]; + } + + for (int i = 0; i < mod_table[input_length % 3]; i++) + encoded_data[output_length - 2 - i] = '='; + + encoded_data[output_length - 1] = '\0'; + + return encoded_data; +} + +int sensorhub_controller::print_sensorhub_data(const char* name, const char *data, int length) +{ + const int LOG_SIZE_LIMIT = 100; + + char buf[6]; + char *log_str; + + if ((length > LOG_SIZE_LIMIT) || (length <= 0)) { + _W("log size(%d) is exceptional!", length); + if (data[0] == 1 && data[1] == 1 && data[2] == 15) { + char *encoded_data = base64_encode((unsigned char *)data, length); + _W("%s", encoded_data); + free(encoded_data); + } + return -EINVAL; + } + + int log_size = strlen(name) + 2 + sizeof(buf) * length + 1; + + log_str = new(std::nothrow) char[log_size]; + retvm_if(!log_str, -ENOMEM, "Failed to allocate memory"); + + memset(log_str, 0, log_size); + + for (int i = 0; i < length; i++ ) { + if (i == 0) { + strncat(log_str, name, strlen(name)+1); + strncat(log_str, ": ", 2); + } else { + strncat(log_str, ", ", 2); + } + + snprintf(buf, sizeof(buf), "%d", (signed char)data[i]); + strncat(log_str, buf, sizeof(buf)); + } + + _I("%s", log_str); + delete[] log_str; + + return length; +} + + +bool sensorhub_controller::set_attribute(int32_t attribute, int32_t value) +{ + bool injection_mode = false; + std::string path = SSPSENSORHUB_DEVICE; + if (attribute != SENSOR_ATTR_SENSORHUB_INJECT_VALUE) + return false; + + if (value == INJECTION_ENABLE) { + injection_mode = true; + path = INJECTION_NODE_PATH; + } + + m_injection_mode = injection_mode; + m_data_node = open(path.c_str(), O_RDWR); + if (m_data_node < 0) { + _E("Open sensorhub device failed(%s)", path.c_str()); + return false; + } + + return true; +} diff --git a/src/sensorhub/sensorhub_controller.h b/src/sensorhub/sensorhub_controller.h new file mode 100644 index 0000000..cf07033 --- /dev/null +++ b/src/sensorhub/sensorhub_controller.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * 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. + * + */ + +#ifndef _SENSORHUB_CONTROLLER_H_ +#define _SENSORHUB_CONTROLLER_H_ + +#include + +class sensorhub_controller { +public: + sensorhub_controller(); + virtual ~sensorhub_controller(); + + int open_input_node(const char* input_node); + int get_poll_fd(void); + + bool enable(void); + bool disable(void); + + bool read_fd(sensorhub_data_t &data); + + int read_sensorhub_data(void); + int read_large_sensorhub_data(void); + int send_sensorhub_data(const char *data, int data_len); + + bool set_attribute(int32_t attribute, int32_t value); + +private: + int m_enabled; + int m_poll_node; + int m_data_node; + bool m_injection_mode; + + sensorhub_data_t m_pending_data; + sensorhub_data_t m_data; + + int print_sensorhub_data(const char* name, const char *data, int length); + char *base64_encode(const unsigned char *data, size_t input_length); +}; + +#endif /* _SENSORHUB_CONTROLLER_H_ */ diff --git a/src/sensorhub/sensorhub_manager.cpp b/src/sensorhub/sensorhub_manager.cpp new file mode 100644 index 0000000..faa8981 --- /dev/null +++ b/src/sensorhub/sensorhub_manager.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include "sensorhub_controller.h" +#include "sensorhub_manager.h" +#include "sensorhub_sensor.h" + +sensorhub_manager::sensorhub_manager() +{ +} + +sensorhub_manager::~sensorhub_manager() +{ + for (auto &it : m_id_sensors) + delete it.second; + + m_id_sensors.clear(); + m_infos.clear(); +} + +sensorhub_manager& sensorhub_manager::get_instance() { + static sensorhub_manager instance; + return instance; +} + +bool sensorhub_manager::add_sensor(sensor_info_t info, sensorhub_sensor *sensor) +{ + m_infos.push_back(info); + m_id_sensors[info.id] = sensor; + + return true; +} + +void sensorhub_manager::set_controller(sensorhub_controller *controller) +{ + for (auto const &it : m_id_sensors) { + sensorhub_sensor *sensor = it.second; + sensor->set_controller(controller); + } +} + +sensorhub_sensor *sensorhub_manager::get_sensor(uint32_t id) +{ + if (m_id_sensors.find(id) == m_id_sensors.end()) + return NULL; + + return m_id_sensors[id]; +} + +bool sensorhub_manager::get_sensors(char libtype, std::vector &sensors) +{ + char type; + sensorhub_sensor *sensor; + + for (auto const &it : m_id_sensors) { + sensor = it.second; + if (!sensor) + continue; + + type = (char)(sensor->get_id() >> SENSOR_LIB_SHIFT); + + if (type == libtype) + sensors.push_back(sensor); + } + + if (sensors.size() == 0) + return false; + + return true; +} + +int sensorhub_manager::get_sensor_infos(const sensor_info_t **sensors) +{ + int size; + + if (m_infos.empty()) { + *sensors = 0; + return 0; + } + + size = m_infos.size(); + *sensors = &m_infos[0]; + + return size; +} + +void sensorhub_manager::restore_sensors() +{ + for (auto &it : m_id_sensors) { + it.second->restore(); + it.second->restore_attributes(); + } +} diff --git a/src/sensorhub/sensorhub_manager.h b/src/sensorhub/sensorhub_manager.h new file mode 100644 index 0000000..a4bc6f7 --- /dev/null +++ b/src/sensorhub/sensorhub_manager.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * 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. + * + */ + +#ifndef _SENSORHUB_MANAGER_H_ +#define _SENSORHUB_MANAGER_H_ + +#include +#include +#include + +#include "sensorhub_controller.h" +#include "sensorhub_sensor.h" + + +#define REGISTER_SENSORHUB_LIB(info, sensor_class) \ + static sensor_initializer initializer_##sensor_class((info)); \ + +class sensorhub_manager { +public: + static sensorhub_manager& get_instance(); + virtual ~sensorhub_manager(); + + sensorhub_sensor *get_sensor(uint32_t id); + bool get_sensors(char libtype, std::vector &sensors); + int get_sensor_infos(const sensor_info_t **sensors); + + void set_controller(sensorhub_controller *controller); + bool add_sensor(sensor_info_t info, sensorhub_sensor *sensor); + + void restore_sensors(void); + +private: + sensorhub_manager(); + + std::map m_id_sensors; + std::vector m_infos; +}; + +template +class sensor_initializer { +public: + sensor_initializer(sensor_info_t info) + { + T *sensor = new(std::nothrow) T(); + if (!sensor) { + _E("Failed to allocate memory"); + return; + } + sensorhub_manager::get_instance().add_sensor(info, sensor); + } + ~sensor_initializer() {} +}; + +#endif /* _SENSORHUB_MANAGER_H_ */ diff --git a/src/sensorhub/sensorhub_sensor.cpp b/src/sensorhub/sensorhub_sensor.cpp new file mode 100644 index 0000000..2f4b694 --- /dev/null +++ b/src/sensorhub/sensorhub_sensor.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include "sensorhub_controller.h" +#include "sensorhub_sensor.h" + +sensorhub_sensor::sensorhub_sensor(const sensor_info_t *sensor_info) +: m_sensor_info(sensor_info) +, m_enabled(false) +, m_restore_mode(false) +{ +} + +sensorhub_sensor::~sensorhub_sensor() +{ + _I("%s is destroyed!", m_sensor_info->name); +} + +int32_t sensorhub_sensor::get_id(void) +{ + return m_sensor_info->id; +} + +void sensorhub_sensor::set_controller(sensorhub_controller *controller) +{ + m_controller = controller; +} + +bool sensorhub_sensor::enable(void) +{ + m_enabled.store(true); + return true; +} + +bool sensorhub_sensor::disable(void) +{ + m_enabled.store(false); + return true; +} + +bool sensorhub_sensor::is_enabled(void) +{ + return m_enabled.load(); +} + +bool sensorhub_sensor::is_restore_mode(void) +{ + return m_restore_mode.load(); +} + +bool sensorhub_sensor::set_interval(unsigned long val) +{ + return false; +} + +bool sensorhub_sensor::set_batch_latency(unsigned long val) +{ + return false; +} + +bool sensorhub_sensor::set_attribute_int(int32_t attribute, int32_t value) +{ + if (is_restore_mode()) + return false; + + attributes_int.push_back(std::make_pair(attribute, value)); + return true; +} + +bool sensorhub_sensor::set_attribute_str(int32_t attribute, char *value, int len) +{ + attr_info info; + + if (is_restore_mode()) + return false; + + info.value = value; + info.len = len; + + attributes_str.push_back(std::make_pair(attribute, &info)); + return true; +} + +bool sensorhub_sensor::flush(void) +{ + return false; +} + +bool sensorhub_sensor::enable(bool enbl, const char lib_id) +{ + struct cmd { + uint8_t inst; + uint8_t type; + uint8_t reserved[2]; + } cmd = {enbl ? SHUB_INST_LIB_ADD : SHUB_INST_LIB_REMOVE, (uint8_t)lib_id, {0, 0}}; + + if (!m_controller) { + _E("Controller is not set : %d", lib_id); + return false; + } + + if (m_controller->send_sensorhub_data(reinterpret_cast(&cmd), sizeof(struct cmd))) { + m_enabled.store(enbl); + if (!enbl) { + attributes_int.clear(); + attributes_str.clear(); + } + return true; + } + + return false; +} + +void sensorhub_sensor::restore(void) +{ + if (!is_enabled()) + return; + + _I("Restart %s", m_sensor_info->name); + enable(); +} + +bool sensorhub_sensor::restore_attributes(void) +{ + if (!is_enabled()) + return false; + + _I("Restart attributes %s", m_sensor_info->name); + + m_restore_mode.store(true); + + for (auto it = attributes_int.begin(); it != attributes_int.end(); ++it) { + int attr = it->first; + int val = it->second; + if (!set_attribute_int(attr, val)) { + _E("Failed to set_attribute_int(%d, %d) for %s", + attr, val, m_sensor_info->name); + return false; + } + } + + for (auto it = attributes_str.begin(); it != attributes_str.end(); ++it) { + int attr = it->first; + char *val = it->second->value; + int len = it->second->len; + if (!set_attribute_str(attr, val, len)) { + _E("Failed to set_attribute_str(%d, %s, %d) for %s", + attr, val, len, m_sensor_info->name); + return false; + } + } + + m_restore_mode.store(false); + return true; +} diff --git a/src/sensorhub/sensorhub_sensor.h b/src/sensorhub/sensorhub_sensor.h new file mode 100644 index 0000000..cde7742 --- /dev/null +++ b/src/sensorhub/sensorhub_sensor.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * 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. + * + */ + +#ifndef _SENSORHUB_SENSOR_H_ +#define _SENSORHUB_SENSOR_H_ + +#include +#include +#include "sensorhub_controller.h" + +#define SHUB_INST_LIB_GETVALUE (uint8_t)0x35 +#define SHUB_INST_LIB_PUTVALUE (uint8_t)0x36 +#define SHUB_INST_LIB_ADD (uint8_t)0x37 +#define SHUB_INST_LIB_REMOVE (uint8_t)0x38 + +#define SHUB_PROP_USR_INFO_AGE 16 +#define SHUB_PROP_USR_INFO_HEIGHT 18 +#define SHUB_PROP_USR_INFO_WEIGHT 19 +#define SHUB_PROP_USR_INFO_GENDER 20 + +#define SHUB_IDX_TYPE 1 +#define SHUB_IDX_LIBTYPE 2 +#define SHUB_TYPE_LIB_EXT 3 +#define SHUB_EXT_CURRENT_INFO 1 + +#define SHUB_LIB_PEDOMETER 3 +#define SHUB_LIB_HRM_LED_GREEN_BATCH 28 +#define SHUB_LIB_HRM_BATCH 46 + +struct attr_info { + char *value; + int len; +}; + +typedef std::vector> sensor_attribute_int_vec; +typedef std::vector> sensor_attribute_str_vec; + +class sensorhub_sensor { +public: + sensorhub_sensor(const sensor_info_t *sensor_info); + virtual ~sensorhub_sensor(); + + int32_t get_id(void); + + virtual bool enable(void); + virtual bool disable(void); + virtual int parse(const char *hub_data, int data_len) = 0; + virtual int get_data(sensor_data_t **data, int *length) = 0; + + void set_controller(sensorhub_controller *controller); + + virtual bool set_interval(unsigned long val); + virtual bool set_batch_latency(unsigned long val); + virtual bool set_attribute_int(int32_t attribute, int32_t value); + virtual bool set_attribute_str(int32_t key, char *value, int len); + + virtual bool flush(void); + virtual void restore(void); + virtual bool restore_attributes(void); + + sensor_attribute_int_vec attributes_int; + sensor_attribute_str_vec attributes_str; + +protected: + const sensor_info_t *m_sensor_info{nullptr}; + sensorhub_controller *m_controller{nullptr}; + + bool enable(bool enbl, const char lib_id); + bool is_enabled(void); + bool is_restore_mode(void); + +private: + std::atomic_bool m_enabled{false}; + std::atomic_bool m_restore_mode{false}; +}; + +#endif /* _SENSORHUB_SENSOR_H_ */ -- 2.34.1