From b673165a70e258a0c9c9a3a0b8e4852d91de07dc Mon Sep 17 00:00:00 2001 From: Vibhor Gaur Date: Thu, 16 Oct 2014 16:24:01 +0530 Subject: [PATCH 01/16] Updating test file for gravity and orientation sensor -Updating error check conditions for start and stop handle. -Removing redundant bool error_state variable. -Removing redundant else condition after parameter count check. -Removing redundant if condition assosciated with error_state. -Updating print logs in callback by printing timestamp before other values. Change-Id: I6a6144b6fb2806a7186580971ba4ff9ab97c7ab9 --- test/src/gravity.c | 50 +++++++++++++++++++++++++------------------------- test/src/orientation.c | 50 +++++++++++++++++++++++++------------------------- 2 files changed, 50 insertions(+), 50 deletions(-) diff --git a/test/src/gravity.c b/test/src/gravity.c index 84b01e6..48081ce 100755 --- a/test/src/gravity.c +++ b/test/src/gravity.c @@ -30,7 +30,7 @@ static GMainLoop *mainloop; void callback(unsigned int event_type, sensor_event_data_t *event, void *user_data) { sensor_data_t *data = (sensor_data_t *)event->event_data; - printf("Gravity [%6.6f] [%6.6f] [%6.6f] [%lld]\n\n", data->values[0], data->values[1], data->values[2], data->timestamp); + printf("Gravity [%lld] [%6.6f] [%6.6f] [%6.6f]\n\n", data->timestamp, data->values[0], data->values[1], data->values[2]); } void printformat() @@ -47,8 +47,7 @@ void printformat() int main(int argc,char **argv) { - int result, handle; - bool error_state = FALSE; + int result, handle, start_handle, stop_handle; unsigned int event; mainloop = g_main_loop_new(NULL, FALSE); @@ -59,48 +58,49 @@ int main(int argc,char **argv) if (argc != 2 && argc != 3) { printformat(); - error_state = TRUE; + free(event_condition); + return 0; } else { if (strcmp(argv[1], "RAW_DATA_REPORT_ON_TIME") == 0) event = GRAVITY_EVENT_RAW_DATA_REPORT_ON_TIME; else { printformat(); - error_state = TRUE; + free(event_condition); + return 0; } if (argc == 3) event_condition->cond_value1 = atof(argv[2]); } - if (!error_state) { - handle = sf_connect(type); - result = sf_register_event(handle, event, event_condition, callback, NULL); + handle = sf_connect(type); + result = sf_register_event(handle, event, event_condition, callback, NULL); - if (result < 0) - printf("Can't register gravity virtual sensor\n"); + if (result < 0) + printf("Can't register gravity virtual sensor\n"); - if (!(sf_start(handle,0) < 0)) { - printf("Success start \n"); - } - else { - printf("Error\n\n\n\n"); - sf_unregister_event(handle, event); - sf_disconnect(handle); - return -1; - } - - g_main_loop_run(mainloop); - g_main_loop_unref(mainloop); + start_handle = sf_start(handle, 0); + if (start_handle < 0) { + printf("Error\n\n\n\n"); sf_unregister_event(handle, event); + sf_disconnect(handle); + return -1; + } - if (!(sf_stop(handle) < 0)) - printf("Success stop \n"); + g_main_loop_run(mainloop); + g_main_loop_unref(mainloop); - sf_disconnect(handle); + sf_unregister_event(handle, event); + stop_handle = sf_stop(handle); + + if (stop_handle < 0) { + printf("Error\n\n"); + return -1; } + sf_disconnect(handle); free(event_condition); return 0; diff --git a/test/src/orientation.c b/test/src/orientation.c index 83dd667..ca9b083 100755 --- a/test/src/orientation.c +++ b/test/src/orientation.c @@ -30,7 +30,7 @@ static GMainLoop *mainloop; void callback(unsigned int event_type, sensor_event_data_t *event, void *user_data) { sensor_data_t *data = (sensor_data_t *)event->event_data; - printf("Orientation [%6.6f] [%6.6f] [%6.6f] [%lld]\n\n", data->values[0], data->values[1], data->values[2], data->timestamp); + printf("Orientation [%lld] [%6.6f] [%6.6f] [%6.6f]\n\n", data->timestamp, data->values[0], data->values[1], data->values[2]); } void printformat() @@ -47,8 +47,7 @@ void printformat() int main(int argc,char **argv) { - int result, handle; - bool error_state = FALSE; + int result, handle, start_handle, stop_handle; unsigned int event; mainloop = g_main_loop_new(NULL, FALSE); @@ -59,47 +58,48 @@ int main(int argc,char **argv) if (argc != 2 && argc != 3) { printformat(); - error_state = TRUE; + free(event_condition); + return 0; } else { if (strcmp(argv[1], "RAW_DATA_REPORT_ON_TIME") == 0) event = ORIENTATION_EVENT_RAW_DATA_REPORT_ON_TIME; else { printformat(); - error_state = TRUE; + free(event_condition); + return 0; } if (argc == 3) event_condition->cond_value1 = atof(argv[2]); } - if (!error_state) { - handle = sf_connect(type); - result = sf_register_event(handle, event, event_condition, callback, NULL); + handle = sf_connect(type); + result = sf_register_event(handle, event, event_condition, callback, NULL); - if (result < 0) - printf("Can't register orientation virtual sensor\n"); + if (result < 0) + printf("Can't register orientation virtual sensor\n"); - if (!(sf_start(handle,0) < 0)) { - printf("Success start \n"); - } - else { - printf("Error\n\n\n\n"); - sf_unregister_event(handle, event); - sf_disconnect(handle); - return -1; - } - - g_main_loop_run(mainloop); - g_main_loop_unref(mainloop); + start_handle = sf_start(handle, 0); + if (start_handle < 0) { + printf("Error\n\n\n\n"); sf_unregister_event(handle, event); + sf_disconnect(handle); + return -1; + } - if (!(sf_stop(handle) < 0)) - printf("Success stop \n"); + g_main_loop_run(mainloop); + g_main_loop_unref(mainloop); - sf_disconnect(handle); + sf_unregister_event(handle, event); + stop_handle = sf_stop(handle); + + if (stop_handle < 0) { + printf("Error\n\n"); + return -1; } + sf_disconnect(handle); free(event_condition); -- 2.7.4 From 11f43f43ed75e122c59dd5802dec127c3a7de0be Mon Sep 17 00:00:00 2001 From: Ramasamy Date: Fri, 17 Oct 2014 13:00:29 +0530 Subject: [PATCH 02/16] Cleanup of sensor fusion library Cleanup and removal of unused code from sensor fusion library. Change-Id: If738e42f5fd14af57dfb11aa2cd77c7c73718c30 --- src/sensor_fusion/euler_angles.cpp | 4 +- src/sensor_fusion/euler_angles.h | 6 +- src/sensor_fusion/matrix.cpp | 4 +- src/sensor_fusion/matrix.h | 6 +- src/sensor_fusion/orientation_filter.cpp | 107 ++++++++------------- src/sensor_fusion/orientation_filter.h | 15 +-- src/sensor_fusion/quaternion.cpp | 19 +--- src/sensor_fusion/quaternion.h | 8 +- src/sensor_fusion/rotation_matrix.cpp | 4 +- src/sensor_fusion/rotation_matrix.h | 6 +- src/sensor_fusion/sensor_data.cpp | 4 +- src/sensor_fusion/sensor_data.h | 6 +- src/sensor_fusion/standalone/gravity_sensor.h | 6 +- .../standalone/linear_acceleration_sensor.h | 6 +- src/sensor_fusion/standalone/orientation_sensor.h | 6 +- .../test/quaternion_test/quaternion_main.cpp | 5 - src/sensor_fusion/vector.cpp | 2 +- src/sensor_fusion/vector.h | 6 +- 18 files changed, 86 insertions(+), 134 deletions(-) diff --git a/src/sensor_fusion/euler_angles.cpp b/src/sensor_fusion/euler_angles.cpp index c668318..1ecce95 100644 --- a/src/sensor_fusion/euler_angles.cpp +++ b/src/sensor_fusion/euler_angles.cpp @@ -16,7 +16,7 @@ * limitations under the License. * */ -#if defined (_EULER_ANGLES_H) && defined (_VECTOR_H) +#if defined (_EULER_ANGLES_H_) && defined (_VECTOR_H_) #include @@ -103,4 +103,4 @@ euler_angles deg2rad(const euler_angles e) return (e.m_ang * (T) DEG2RAD); } -#endif //_EULER_ANGLES_H +#endif //_EULER_ANGLES_H_ diff --git a/src/sensor_fusion/euler_angles.h b/src/sensor_fusion/euler_angles.h index 69fe0dd..35d8abb 100644 --- a/src/sensor_fusion/euler_angles.h +++ b/src/sensor_fusion/euler_angles.h @@ -17,8 +17,8 @@ * */ -#ifndef _EULER_ANGLES_H -#define _EULER_ANGLES_H +#ifndef _EULER_ANGLES_H_ +#define _EULER_ANGLES_H_ #include "vector.h" #include "quaternion.h" @@ -43,4 +43,4 @@ public: #include "euler_angles.cpp" -#endif //_EULER_ANGLES_H +#endif //_EULER_ANGLES_H_ diff --git a/src/sensor_fusion/matrix.cpp b/src/sensor_fusion/matrix.cpp index 9ce7487..b742fdf 100755 --- a/src/sensor_fusion/matrix.cpp +++ b/src/sensor_fusion/matrix.cpp @@ -17,7 +17,7 @@ * */ -#ifdef _MATRIX_H +#ifdef _MATRIX_H_ template matrix::matrix(void) @@ -287,4 +287,4 @@ matrix mul(const matrix m1, const matrix m2) return m3; } -#endif //_MATRIX_H +#endif //_MATRIX_H_ diff --git a/src/sensor_fusion/matrix.h b/src/sensor_fusion/matrix.h index d2b2c29..05030a0 100755 --- a/src/sensor_fusion/matrix.h +++ b/src/sensor_fusion/matrix.h @@ -17,8 +17,8 @@ * */ -#ifndef _MATRIX_H -#define _MATRIX_H +#ifndef _MATRIX_H_ +#define _MATRIX_H_ #include #include @@ -67,4 +67,4 @@ public: #include "matrix.cpp" -#endif //_MATRIX_H +#endif //_MATRIX_H_ diff --git a/src/sensor_fusion/orientation_filter.cpp b/src/sensor_fusion/orientation_filter.cpp index 2226cbe..7d0cc5a 100644 --- a/src/sensor_fusion/orientation_filter.cpp +++ b/src/sensor_fusion/orientation_filter.cpp @@ -18,7 +18,7 @@ */ -#ifdef _ORIENTATION_FILTER_H +#ifdef _ORIENTATION_FILTER_H_ #include "orientation_filter.h" @@ -29,27 +29,15 @@ #define US2S (1.0 / 1000000.0) #define SAMPLE_FREQ 100000 -// Gyro Types -// Systron-donner "Horizon" -#define ZIGMA_W (0.05 * DEG2RAD)//deg/s -#define TAU_W 1000//secs -// Crossbow DMU-6X -//#define ZIGMA_W 0.05 * DEG2RAD //deg/s -//#define TAU_W 300 //secs -// FOGs (KVH Autogyro and Crossbow DMU-FOG) -//#define ZIGMA_W 0 //deg/s - +#define ZIGMA_W (0.05 * DEG2RAD) +#define TAU_W 1000 #define QWB_CONST ((2 * (ZIGMA_W * ZIGMA_W)) / TAU_W) #define F_CONST (-1 / TAU_W) -#define ENABLE_LPF false - #define M3X3R 3 #define M3X3C 3 - #define M6X6R 6 #define M6X6C 6 - #define V1x3S 3 #define V1x4S 4 #define V1x6S 6 @@ -88,46 +76,37 @@ orientation_filter::orientation_filter() m_roll_phase_compensation = 1; m_yaw_phase_compensation = 1; m_magnetic_alignment_factor = 1; + + m_gyro.m_time_stamp = 0; } template orientation_filter::~orientation_filter() { - } template -inline void orientation_filter::filter_sensor_data(const sensor_data accel, +inline void orientation_filter::initialize_sensor_data(const sensor_data accel, const sensor_data gyro, const sensor_data magnetic) { - const TYPE iir_b[] = {0.98, 0}; - const TYPE iir_a[] = {1.0000000, 0.02}; - vect acc_data(V1x3S); vect gyr_data(V1x3S); + unsigned long long sample_interval_gyro = SAMPLE_FREQ; - m_filt_accel[0] = m_filt_accel[1]; - m_filt_gyro[0] = m_filt_gyro[1]; - m_filt_magnetic[0] = m_filt_magnetic[1]; + m_accel.m_data = accel.m_data; + m_gyro.m_data = gyro.m_data; + m_magnetic.m_data = magnetic.m_data; - if (ENABLE_LPF) - { - m_filt_accel[1].m_data = accel.m_data * iir_b[0] - m_filt_accel[0].m_data * iir_a[1]; - m_filt_gyro[1].m_data = gyro.m_data * iir_b[0] - m_filt_gyro[0].m_data * iir_a[1]; - m_filt_magnetic[1].m_data = magnetic.m_data * iir_b[0] - m_filt_magnetic[0].m_data * iir_a[1]; - } - else - { - m_filt_accel[1].m_data = accel.m_data; - m_filt_gyro[1].m_data = gyro.m_data; - m_filt_magnetic[1].m_data = magnetic.m_data; - } + if (m_gyro.m_time_stamp != 0 && gyro.m_time_stamp != 0) + sample_interval_gyro = gyro.m_time_stamp - m_gyro.m_time_stamp; - m_filt_accel[1].m_time_stamp = accel.m_time_stamp; - m_filt_gyro[1].m_time_stamp = accel.m_time_stamp; - m_filt_magnetic[1].m_time_stamp = accel.m_time_stamp; + m_gyro_dt = sample_interval_gyro * US2S; - m_filt_gyro[1].m_data = m_filt_gyro[1].m_data - m_bias_correction; + m_accel.m_time_stamp = accel.m_time_stamp; + m_gyro.m_time_stamp = gyro.m_time_stamp; + m_magnetic.m_time_stamp = magnetic.m_time_stamp; + + m_gyro.m_data = m_gyro.m_data - m_bias_correction; } template @@ -139,10 +118,10 @@ inline void orientation_filter::orientation_triad_algorithm() vect acc_e(V1x3S, arr_acc_e); vect mag_e(V1x3S, arr_mag_e); - vect acc_b_x_mag_b = cross(m_filt_accel[1].m_data, m_filt_magnetic[1].m_data); + vect acc_b_x_mag_b = cross(m_accel.m_data, m_magnetic.m_data); vect acc_e_x_mag_e = cross(acc_e, mag_e); - vect cross1 = cross(acc_b_x_mag_b, m_filt_accel[1].m_data); + vect cross1 = cross(acc_b_x_mag_b, m_accel.m_data); vect cross2 = cross(acc_e_x_mag_e, acc_e); matrix mat_b(M3X3R, M3X3C); @@ -150,7 +129,7 @@ inline void orientation_filter::orientation_triad_algorithm() for(int i = 0; i < M3X3R; i++) { - mat_b.m_mat[i][0] = m_filt_accel[1].m_data.m_vec[i]; + mat_b.m_mat[i][0] = m_accel.m_data.m_vec[i]; mat_b.m_mat[i][1] = acc_b_x_mag_b.m_vec[i]; mat_b.m_mat[i][2] = cross1.m_vec[i]; mat_e.m_mat[i][0] = acc_e.m_vec[i]; @@ -170,9 +149,9 @@ inline void orientation_filter::compute_covariance() TYPE var_gyr_x, var_gyr_y, var_gyr_z; TYPE var_roll, var_pitch, var_yaw; - insert_end(m_var_gyr_x, m_filt_gyro[1].m_data.m_vec[0]); - insert_end(m_var_gyr_y, m_filt_gyro[1].m_data.m_vec[1]); - insert_end(m_var_gyr_z, m_filt_gyro[1].m_data.m_vec[2]); + insert_end(m_var_gyr_x, m_gyro.m_data.m_vec[0]); + insert_end(m_var_gyr_y, m_gyro.m_data.m_vec[1]); + insert_end(m_var_gyr_z, m_gyro.m_data.m_vec[2]); insert_end(m_var_roll, m_orientation.m_ang.m_vec[0]); insert_end(m_var_pitch, m_orientation.m_ang.m_vec[1]); insert_end(m_var_yaw, m_orientation.m_ang.m_vec[2]); @@ -202,20 +181,13 @@ inline void orientation_filter::time_update() quaternion quat_diff, quat_error; euler_angles euler_error; euler_angles orientation; - unsigned long long sample_interval_gyro = SAMPLE_FREQ; - TYPE dt = 0; - - if (m_filt_gyro[1].m_time_stamp != 0 && m_filt_gyro[0].m_time_stamp != 0) - sample_interval_gyro = m_filt_gyro[1].m_time_stamp - m_filt_gyro[0].m_time_stamp; - - dt = sample_interval_gyro * US2S; - m_tran_mat.m_mat[0][1] = m_filt_gyro[1].m_data.m_vec[2]; - m_tran_mat.m_mat[0][2] = -m_filt_gyro[1].m_data.m_vec[1]; - m_tran_mat.m_mat[1][0] = -m_filt_gyro[1].m_data.m_vec[2]; - m_tran_mat.m_mat[1][2] = m_filt_gyro[1].m_data.m_vec[0]; - m_tran_mat.m_mat[2][0] = m_filt_gyro[1].m_data.m_vec[1]; - m_tran_mat.m_mat[2][1] = -m_filt_gyro[1].m_data.m_vec[0]; + m_tran_mat.m_mat[0][1] = m_gyro.m_data.m_vec[2]; + m_tran_mat.m_mat[0][2] = -m_gyro.m_data.m_vec[1]; + m_tran_mat.m_mat[1][0] = -m_gyro.m_data.m_vec[2]; + m_tran_mat.m_mat[1][2] = m_gyro.m_data.m_vec[0]; + m_tran_mat.m_mat[2][0] = m_gyro.m_data.m_vec[1]; + m_tran_mat.m_mat[2][1] = -m_gyro.m_data.m_vec[0]; m_tran_mat.m_mat[3][3] = (TYPE) F_CONST; m_tran_mat.m_mat[4][4] = (TYPE) F_CONST; m_tran_mat.m_mat[5][5] = (TYPE) F_CONST; @@ -232,12 +204,12 @@ inline void orientation_filter::time_update() if(!is_initialized(m_quat_driv.m_quat)) m_quat_driv = m_quat_aid; - quaternion quat_rot_inc(0, m_filt_gyro[1].m_data.m_vec[0], m_filt_gyro[1].m_data.m_vec[1], - m_filt_gyro[1].m_data.m_vec[2]); + quaternion quat_rot_inc(0, m_gyro.m_data.m_vec[0], m_gyro.m_data.m_vec[1], + m_gyro.m_data.m_vec[2]); quat_diff = (m_quat_driv * quat_rot_inc) * (TYPE) 0.5; - m_quat_driv = m_quat_driv + (quat_diff * (TYPE) dt * (TYPE) PI); + m_quat_driv = m_quat_driv + (quat_diff * (TYPE) m_gyro_dt * (TYPE) PI); m_quat_driv.quat_normalize(); @@ -289,7 +261,8 @@ inline void orientation_filter::measurement_update() else iden = 0; - m_pred_cov.m_mat[i][j] = (iden - (gain.m_mat[i][j] * m_measure_mat.m_mat[j][i])) * m_pred_cov.m_mat[i][j]; + m_pred_cov.m_mat[i][j] = (iden - (gain.m_mat[i][j] * m_measure_mat.m_mat[j][i])) * + m_pred_cov.m_mat[i][j]; } } @@ -307,11 +280,11 @@ euler_angles orientation_filter::get_orientation(const sensor_data cor_euler_ang; - filter_sensor_data(accel, gyro, magnetic); + initialize_sensor_data(accel, gyro, magnetic); - normalize(m_filt_accel[1]); - m_filt_gyro[1].m_data = m_filt_gyro[1].m_data * (TYPE) PI; - normalize(m_filt_magnetic[1]); + normalize(m_accel); + m_gyro.m_data = m_gyro.m_data * (TYPE) PI; + normalize(m_magnetic); orientation_triad_algorithm(); @@ -333,4 +306,4 @@ rotation_matrix orientation_filter::get_rotation_matrix(const sensor return m_rot_matrix; } -#endif //_ORIENTATION_FILTER_H +#endif //_ORIENTATION_FILTER_H_ diff --git a/src/sensor_fusion/orientation_filter.h b/src/sensor_fusion/orientation_filter.h index 3f97569..02ece6f 100644 --- a/src/sensor_fusion/orientation_filter.h +++ b/src/sensor_fusion/orientation_filter.h @@ -17,8 +17,8 @@ * */ -#ifndef _ORIENTATION_FILTER_H -#define _ORIENTATION_FILTER_H +#ifndef _ORIENTATION_FILTER_H_ +#define _ORIENTATION_FILTER_H_ #include "matrix.h" #include "vector.h" @@ -30,9 +30,9 @@ template class orientation_filter { public: - sensor_data m_filt_accel[2]; - sensor_data m_filt_gyro[2]; - sensor_data m_filt_magnetic[2]; + sensor_data m_accel; + sensor_data m_gyro; + sensor_data m_magnetic; vect m_var_gyr_x; vect m_var_gyr_y; vect m_var_gyr_z; @@ -52,6 +52,7 @@ public: quaternion m_quat_driv; rotation_matrix m_rot_matrix; euler_angles m_orientation; + TYPE m_gyro_dt; int m_pitch_phase_compensation; int m_roll_phase_compensation; @@ -61,7 +62,7 @@ public: orientation_filter(); ~orientation_filter(); - inline void filter_sensor_data(const sensor_data accel, + inline void initialize_sensor_data(const sensor_data accel, const sensor_data gyro, const sensor_data magnetic); inline void orientation_triad_algorithm(); inline void compute_covariance(); @@ -76,4 +77,4 @@ public: #include "orientation_filter.cpp" -#endif /* _ORIENTATION_FILTER_H */ +#endif /* _ORIENTATION_FILTER_H_ */ diff --git a/src/sensor_fusion/quaternion.cpp b/src/sensor_fusion/quaternion.cpp index 5a9148a..7272012 100755 --- a/src/sensor_fusion/quaternion.cpp +++ b/src/sensor_fusion/quaternion.cpp @@ -17,7 +17,7 @@ * */ -#if defined (_QUATERNION_H) && defined (_VECTOR_H) +#if defined (_QUATERNION_H_) && defined (_VECTOR_H_) #include @@ -117,19 +117,4 @@ quaternion operator +(const quaternion q1, const quaternion q2) return (q1.m_quat + q2.m_quat); } -template -quaternion quat_conj(const quaternion q) -{ - T w, x, y, z; - - w = q.m_quat.m_vec[0]; - x = q.m_quat.m_vec[1]; - y = q.m_quat.m_vec[2]; - z = q.m_quat.m_vec[3]; - - quaternion q1(w, -x, -y, -z); - - return q1; -} - -#endif //_QUATERNION_H +#endif //_QUATERNION_H_ diff --git a/src/sensor_fusion/quaternion.h b/src/sensor_fusion/quaternion.h index 90ce4e8..44eaa58 100755 --- a/src/sensor_fusion/quaternion.h +++ b/src/sensor_fusion/quaternion.h @@ -17,8 +17,8 @@ * */ -#ifndef _QUATERNION_H -#define _QUATERNION_H +#ifndef _QUATERNION_H_ +#define _QUATERNION_H_ #include "vector.h" @@ -42,10 +42,8 @@ public: const quaternion q2); template friend quaternion operator +(const quaternion q1, const quaternion q2); - - template friend quaternion quat_conj(const quaternion q); }; #include "quaternion.cpp" -#endif //_QUATERNION_H +#endif //_QUATERNION_H_ diff --git a/src/sensor_fusion/rotation_matrix.cpp b/src/sensor_fusion/rotation_matrix.cpp index 3a1ef86..f29e669 100644 --- a/src/sensor_fusion/rotation_matrix.cpp +++ b/src/sensor_fusion/rotation_matrix.cpp @@ -17,7 +17,7 @@ * */ -#if defined (_ROTATION_MATRIX_H) && defined (_MATRIX_H) +#if defined (_ROTATION_MATRIX_H_) && defined (_MATRIX_H_) #define QUAT_LEN 4 #define ROT_MAT_ROWS 3 @@ -143,4 +143,4 @@ quaternion rot_mat2quat(rotation_matrix rm) return q; } -#endif /* _ROTATION_MATRIX_H */ +#endif /* _ROTATION_MATRIX_H_ */ diff --git a/src/sensor_fusion/rotation_matrix.h b/src/sensor_fusion/rotation_matrix.h index 7ded3e2..bb4aa78 100644 --- a/src/sensor_fusion/rotation_matrix.h +++ b/src/sensor_fusion/rotation_matrix.h @@ -17,8 +17,8 @@ * */ -#ifndef _ROTATION_MATRIX_H -#define _ROTATION_MATRIX_H +#ifndef _ROTATION_MATRIX_H_ +#define _ROTATION_MATRIX_H_ #include "matrix.h" #include "quaternion.h" @@ -42,4 +42,4 @@ public: #include "rotation_matrix.cpp" -#endif /* _ROTATION_MATRIX_H */ +#endif /* _ROTATION_MATRIX_H_ */ diff --git a/src/sensor_fusion/sensor_data.cpp b/src/sensor_fusion/sensor_data.cpp index 72b9395..98bed4c 100644 --- a/src/sensor_fusion/sensor_data.cpp +++ b/src/sensor_fusion/sensor_data.cpp @@ -17,7 +17,7 @@ * */ -#if defined (_SENSOR_DATA_H) && defined (_VECTOR_H) +#if defined (_SENSOR_DATA_H_) && defined (_VECTOR_H_) #include "math.h" @@ -104,5 +104,5 @@ sensor_data scale_data(sensor_data data, T scaling_factor) return s; } -#endif /* _SENSOR_DATA_H */ +#endif /* _SENSOR_DATA_H_ */ diff --git a/src/sensor_fusion/sensor_data.h b/src/sensor_fusion/sensor_data.h index 4c84bd9..c95e5b4 100644 --- a/src/sensor_fusion/sensor_data.h +++ b/src/sensor_fusion/sensor_data.h @@ -17,8 +17,8 @@ * */ -#ifndef _SENSOR_DATA_H -#define _SENSOR_DATA_H +#ifndef _SENSOR_DATA_H_ +#define _SENSOR_DATA_H_ #include "vector.h" @@ -48,4 +48,4 @@ public: #include "sensor_data.cpp" -#endif /* _SENSOR_DATA_H */ +#endif /* _SENSOR_DATA_H_ */ diff --git a/src/sensor_fusion/standalone/gravity_sensor.h b/src/sensor_fusion/standalone/gravity_sensor.h index 780d280..7d3fa6c 100644 --- a/src/sensor_fusion/standalone/gravity_sensor.h +++ b/src/sensor_fusion/standalone/gravity_sensor.h @@ -17,8 +17,8 @@ * */ -#ifndef _GRAVITY_SENSOR_H -#define _GRAVITY_SENSOR_H +#ifndef _GRAVITY_SENSOR_H_ +#define _GRAVITY_SENSOR_H_ #include "orientation_sensor.h" @@ -33,4 +33,4 @@ public: #include "gravity_sensor.cpp" -#endif /* _GRAVITY_SENSOR_H */ +#endif /* _GRAVITY_SENSOR_H_ */ diff --git a/src/sensor_fusion/standalone/linear_acceleration_sensor.h b/src/sensor_fusion/standalone/linear_acceleration_sensor.h index df8a811..1942f3b 100644 --- a/src/sensor_fusion/standalone/linear_acceleration_sensor.h +++ b/src/sensor_fusion/standalone/linear_acceleration_sensor.h @@ -17,8 +17,8 @@ * */ -#ifndef _LINEAR_ACCELERATION_SENSOR_H -#define _LINEAR_ACCELERATION_SENSOR_H +#ifndef _LINEAR_ACCELERATION_SENSOR_H_ +#define _LINEAR_ACCELERATION_SENSOR_H_ #include "gravity_sensor.h" @@ -33,4 +33,4 @@ public: #include "linear_acceleration_sensor.cpp" -#endif /* _LINEAR_ACCELERATION_SENSOR_H */ +#endif /* _LINEAR_ACCELERATION_SENSOR_H_ */ diff --git a/src/sensor_fusion/standalone/orientation_sensor.h b/src/sensor_fusion/standalone/orientation_sensor.h index e670309..a02e2af 100644 --- a/src/sensor_fusion/standalone/orientation_sensor.h +++ b/src/sensor_fusion/standalone/orientation_sensor.h @@ -17,8 +17,8 @@ * */ -#ifndef _ORIENTATION_SENSOR_H -#define _ORIENTATION_SENSOR_H +#ifndef _ORIENTATION_SENSOR_H_ +#define _ORIENTATION_SENSOR_H_ #include "../orientation_filter.h" @@ -35,4 +35,4 @@ public: #include "orientation_sensor.cpp" -#endif /* _ORIENTATION_SENSOR_H */ +#endif /* _ORIENTATION_SENSOR_H_ */ diff --git a/src/sensor_fusion/standalone/test/quaternion_test/quaternion_main.cpp b/src/sensor_fusion/standalone/test/quaternion_test/quaternion_main.cpp index d2ec86b..330a1ad 100644 --- a/src/sensor_fusion/standalone/test/quaternion_test/quaternion_main.cpp +++ b/src/sensor_fusion/standalone/test/quaternion_test/quaternion_main.cpp @@ -63,10 +63,5 @@ int main() cout << "input\t" << q1.m_quat << "\n"; q1.quat_normalize(); cout << "output\t" << q1.m_quat << "\n\n"; - - cout << "Quaternion Conjugate\n"; - quaternion q9 = quat_conj(q1); - cout << "input\t" << q1.m_quat << "\n"; - cout << "output\t" << q9.m_quat << "\n\n"; } diff --git a/src/sensor_fusion/vector.cpp b/src/sensor_fusion/vector.cpp index bf2ee65..a51c72d 100644 --- a/src/sensor_fusion/vector.cpp +++ b/src/sensor_fusion/vector.cpp @@ -17,7 +17,7 @@ * */ -#if defined (_VECTOR_H) && defined (_MATRIX_H) +#if defined (_VECTOR_H_) && defined (_MATRIX_H_) template vect::vect(void) diff --git a/src/sensor_fusion/vector.h b/src/sensor_fusion/vector.h index 4c3eaf1..5578f3c 100644 --- a/src/sensor_fusion/vector.h +++ b/src/sensor_fusion/vector.h @@ -17,8 +17,8 @@ * */ -#ifndef _VECTOR_H -#define _VECTOR_H +#ifndef _VECTOR_H_ +#define _VECTOR_H_ #include "matrix.h" @@ -71,4 +71,4 @@ public: #include "vector.cpp" -#endif /* _VECTOR_H */ +#endif /* _VECTOR_H_ */ -- 2.7.4 From b5232f2a97fa86f74e71b76db9869f46517e3c0b Mon Sep 17 00:00:00 2001 From: Ramasamy Date: Mon, 20 Oct 2014 13:59:10 +0530 Subject: [PATCH 03/16] Adding/Updating images for documentation Adding updated sensor fusion documentation images for: - orientation estimation - gravity and linear accelertion computation - gravity projection diagram Change-Id: I16140e327fdd18c18679b6ef3dc2a5889983bfc0 --- ...lock_diagram_gravity_and_linear_acceleration.png | Bin 21896 -> 21658 bytes .../block_diagram_orientation_estimation.png | Bin 42305 -> 63358 bytes .../projection_diagram_gravity_computation.png | Bin 0 -> 36547 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100755 src/sensor_fusion/design/documentation/projection_diagram_gravity_computation.png diff --git a/src/sensor_fusion/design/documentation/block_diagram_gravity_and_linear_acceleration.png b/src/sensor_fusion/design/documentation/block_diagram_gravity_and_linear_acceleration.png index 958e640bc819f9b5f7b8ef5b21a8034379336107..62024e25074ec891087be6a823ab2966f23284ca 100755 GIT binary patch literal 21658 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 literal 21896 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

Y=tAF-A&Q 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 diff --git a/src/sensor_fusion/design/documentation/projection_diagram_gravity_computation.png b/src/sensor_fusion/design/documentation/projection_diagram_gravity_computation.png new file mode 100755 index 0000000000000000000000000000000000000000..e22590d28fdf4733693e98923649492374431a06 GIT binary patch literal 36547 zcmb5VbySq!+dWK)APv&e2o@rZbP6ai)X*U%GBnaD-7O$BbPXjE(w$Nw49L)>bV*76 z&V0VldjEf2i{)Y*?>Og*eeJzZgqo@X(LK6*7#J8tN{VtC7#LWw;D-he3w&a$d9exp z!u+72AcIjk`T+e4j+L~EGzLa>9Kn_89q>E8qoUpi3=HCK^bcmgLx}|jMy#%qoV1pQ z@opB0&;9kR+iK{WH{R6xvqk;|)kr~iyQ)&8@y>p^x(#;)stC&e+Ac`@b(KeqmYr8} zAM3+F{_60zhY+nFwSj$8&eD&A{ZlcDB5+u+`Yh=7{hXGSl_l9+ z)0}-~WBg)lXJ>e4=eOtB**{S^wqQ~j#o$~7K1*=CqAZj`29G5n6u|*LWhH{D;IRbD z(IL`FW$+^SwFFp#Ny)8CU^2W(?a%CPC%uPR6_+){`v`5RNdND$3c@XgpSE+Rd?z$`hFl${-cN}z>uIX(&zl>f;AHCH$2iFjc9WWh|Uj)(Qz zbB>Ux;(OXU6y(C_M|zlY6@BDZ3M13#*T@$R7dUYBh;pnrY+=;0^ATlXCk6YF7+G

~+W+q#$Uh;H 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 Date: Tue, 21 Oct 2014 15:11:03 +0530 Subject: [PATCH 04/16] Restructuring and Updating sensor fusion documentation - restructuring documentation folder structure. - Updating documentation for new diagrams. Change-Id: Ib34df41528deb8630db87d1051d90960289d7df1 --- ...ock_diagram_gravity_and_linear_acceleration.png | Bin .../block_diagram_orientation_estimation.png | Bin .../documentation/diagram/device_orientation.png | Bin 0 -> 35185 bytes .../{ => diagram}/kalman_filter_stages.png | Bin .../diagram/orientation_effect_on_gravity.png | Bin 0 -> 30634 bytes .../projection_diagram_gravity_computation.png | Bin .../{ => equation}/state_equation.png | Bin .../orientation_effect_on_gravity.png | Bin 29060 -> 0 bytes .../design/documentation/sensor_fusion.htm | 48 +++++++++++++++++---- 9 files changed, 40 insertions(+), 8 deletions(-) rename src/sensor_fusion/design/documentation/{ => diagram}/block_diagram_gravity_and_linear_acceleration.png (100%) rename src/sensor_fusion/design/documentation/{ => diagram}/block_diagram_orientation_estimation.png (100%) create mode 100755 src/sensor_fusion/design/documentation/diagram/device_orientation.png rename src/sensor_fusion/design/documentation/{ => diagram}/kalman_filter_stages.png (100%) create mode 100755 src/sensor_fusion/design/documentation/diagram/orientation_effect_on_gravity.png rename src/sensor_fusion/design/documentation/{ => diagram}/projection_diagram_gravity_computation.png (100%) rename src/sensor_fusion/design/documentation/{ => equation}/state_equation.png (100%) delete mode 100755 src/sensor_fusion/design/documentation/orientation_effect_on_gravity.png diff --git a/src/sensor_fusion/design/documentation/block_diagram_gravity_and_linear_acceleration.png b/src/sensor_fusion/design/documentation/diagram/block_diagram_gravity_and_linear_acceleration.png similarity index 100% rename from src/sensor_fusion/design/documentation/block_diagram_gravity_and_linear_acceleration.png rename to src/sensor_fusion/design/documentation/diagram/block_diagram_gravity_and_linear_acceleration.png diff --git a/src/sensor_fusion/design/documentation/block_diagram_orientation_estimation.png b/src/sensor_fusion/design/documentation/diagram/block_diagram_orientation_estimation.png similarity index 100% rename from src/sensor_fusion/design/documentation/block_diagram_orientation_estimation.png rename to src/sensor_fusion/design/documentation/diagram/block_diagram_orientation_estimation.png diff --git a/src/sensor_fusion/design/documentation/diagram/device_orientation.png b/src/sensor_fusion/design/documentation/diagram/device_orientation.png new file mode 100755 index 0000000000000000000000000000000000000000..6bc621a8f90b5ed1b6155913e6af375a6c36ab4b GIT binary patch literal 35185 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⪙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ΠaOX0X%%+Cen+|o}2;#0$g0z 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 &O^fX2g)QVZp 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 DOVI*v diff --git a/src/sensor_fusion/design/documentation/sensor_fusion.htm b/src/sensor_fusion/design/documentation/sensor_fusion.htm index f7552b4..4575215 100755 --- a/src/sensor_fusion/design/documentation/sensor_fusion.htm +++ b/src/sensor_fusion/design/documentation/sensor_fusion.htm @@ -6,7 +6,7 @@

Sensor Fusion is the process of combining the accelerometer, gyroscope, geo-magnetic sensor, GPS data and barometer in order to generate accurate virtual sensor outputs such as Orientation, Gravity, Linear -Acceleration, Altitude, Pedometer, etc. Sensor Fusion is used for extracting +Acceleration, Altitude, etc. Sensor Fusion is used for extracting individual virtual sensor components from composite sensor data and/or combining multiple sensor data to create new sensor component data while compensating for individual sensor errors. Ideally the following errors would @@ -64,8 +64,25 @@ to determine the heading of a device.

Orientation Estimation

-

+

+ +
+
+ +
Fig. 1. Device Orientation in Euler Angles.
+
+
+ + +
+
+ +
Fig. 2. Quaternion based Sensor Fusion Approach for Orientation +Estimation.
+
+
+

Driving System

@@ -92,11 +109,16 @@ Kalman filtering.

Kalman Filtering

-

+
+
+ +
Fig. 3. Kalman Filter Stages.
+
+

To ensure linear Kalman filtering the following error state equation is used:

-

+

,where the first term is the quaternion representation of the Euler angles error and the next three terms are the error in knowledge of bias errors.

@@ -109,8 +131,13 @@ gravity measurements in 3D space are derived from the orientation (pitch, roll and yaw) measurements that is output from the Kalman filter. The process to compute gravity and linear acceleration is shown in figure below. -

+
+
+ +
Fig. 4. Computation of Gravity and Linear Acceleration.
+
+

Gravity virtual sensor data provides the measure of the direction in which the EarthÂ’s gravitational force is observed in the deviceÂ’s frame of reference. The @@ -123,7 +150,12 @@ gravitational effect on the devices reference axis.

GRy = EarthÂ’s Gravity * sin(roll)

GRz = EarthÂ’s Gravity * cos(pitch) * cos(roll)

-

+
+
+ +
Fig. 5. Effect of Device Orientation on Gravity.
+
+

When the device tilt values (pitch,roll) are changed from (0,0) to (0,Π/2), phone is rotated around x-axis, the y-axis gets aligned to earth's gravitational field -- 2.7.4 From bb157ef19dadbd4f2b96aff0cfbf3ef2fed1b245 Mon Sep 17 00:00:00 2001 From: Ramasamy Date: Mon, 27 Oct 2014 17:00:56 +0530 Subject: [PATCH 05/16] Adding sensor fusion pre-processing equations Adding sensor fusion equations relating to post processing of sensor related data. Change-Id: I53ee77d6d982dc4e26540717280c29d8c74e9cb7 --- .../design/documentation/equation/equation_1.png | Bin 0 -> 2776 bytes .../design/documentation/equation/equation_2.png | Bin 0 -> 2930 bytes .../design/documentation/equation/equation_3.png | Bin 0 -> 4853 bytes .../design/documentation/equation/equation_4.png | Bin 0 -> 5034 bytes .../design/documentation/equation/equation_5.png | Bin 0 -> 5563 bytes .../design/documentation/sensor_fusion.htm | 29 +++++++++++++++++++++ 6 files changed, 29 insertions(+) create mode 100755 src/sensor_fusion/design/documentation/equation/equation_1.png create mode 100755 src/sensor_fusion/design/documentation/equation/equation_2.png create mode 100755 src/sensor_fusion/design/documentation/equation/equation_3.png create mode 100755 src/sensor_fusion/design/documentation/equation/equation_4.png create mode 100755 src/sensor_fusion/design/documentation/equation/equation_5.png diff --git a/src/sensor_fusion/design/documentation/equation/equation_1.png b/src/sensor_fusion/design/documentation/equation/equation_1.png new file mode 100755 index 0000000000000000000000000000000000000000..49a8487251e0e377e474b7e30e34da4c32c7dad9 GIT binary patch literal 2776 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 Z9i0#jrV2^mm!SU~G?BXZDpejn`w!NKbp8MU literal 0 HcmV?d00001 diff --git a/src/sensor_fusion/design/documentation/equation/equation_3.png b/src/sensor_fusion/design/documentation/equation/equation_3.png new file mode 100755 index 0000000000000000000000000000000000000000..c41a98d9161f00978e89f1394c8435d67e7989e6 GIT binary patch literal 4853 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 JT&ZFg{y)x?Jk$UH literal 0 HcmV?d00001 diff --git a/src/sensor_fusion/design/documentation/equation/equation_4.png b/src/sensor_fusion/design/documentation/equation/equation_4.png new file mode 100755 index 0000000000000000000000000000000000000000..cecdabb7d998d8fa4a9e3a2faa349782089edf59 GIT binary patch literal 5034 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 D@K>Gj literal 0 HcmV?d00001 diff --git a/src/sensor_fusion/design/documentation/equation/equation_5.png b/src/sensor_fusion/design/documentation/equation/equation_5.png new file mode 100755 index 0000000000000000000000000000000000000000..1063ecb7599c5d13529a1a76913997fbed3d4f23 GIT binary patch literal 5563 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 literal 0 HcmV?d00001 diff --git a/src/sensor_fusion/design/documentation/sensor_fusion.htm b/src/sensor_fusion/design/documentation/sensor_fusion.htm index 4575215..0ccfb0e 100755 --- a/src/sensor_fusion/design/documentation/sensor_fusion.htm +++ b/src/sensor_fusion/design/documentation/sensor_fusion.htm @@ -83,6 +83,35 @@ Estimation. +

+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+

Driving System

-- 2.7.4 From bdb0cfd3f2d7caa735bda226ac59494b9a413d70 Mon Sep 17 00:00:00 2001 From: Ramasamy Date: Thu, 30 Oct 2014 10:25:27 +0530 Subject: [PATCH 06/16] Updating orientation sensor based on porting to SDK - Updated orientation sensor class and implementation based on porting and testing of tizen SDK. Change-Id: I6eac7359fcf36af0b669922572dc7ae777aff1e3 --- src/orientation/orientation_sensor.cpp | 51 +++++++++++++++++++--------------- src/orientation/orientation_sensor.h | 6 ++++ 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/src/orientation/orientation_sensor.cpp b/src/orientation/orientation_sensor.cpp index 14d0349..f4fbaa1 100755 --- a/src/orientation/orientation_sensor.cpp +++ b/src/orientation/orientation_sensor.cpp @@ -60,13 +60,11 @@ int roll_phase_compensation = 1; int yaw_phase_compensation = -1; int magnetic_alignment_factor = 1; -void pre_process_data(sensor_data &data_out, sensor_data_t &data_in, float *bias, int *sign, float scale) +void pre_process_data(sensor_data &data_out, const float *data_in, float *bias, int *sign, float scale) { - data_out.m_data.m_vec[0] = sign[0] * (data_in.values[0] - bias[0]) / scale; - data_out.m_data.m_vec[1] = sign[1] * (data_in.values[1] - bias[1]) / scale; - data_out.m_data.m_vec[2] = sign[2] * (data_in.values[2] - bias[2]) / scale; - - data_out.m_time_stamp = data_in.timestamp; + data_out.m_data.m_vec[0] = sign[0] * (data_in[0] - bias[0]) / scale; + data_out.m_data.m_vec[1] = sign[1] * (data_in[1] - bias[1]) / scale; + data_out.m_data.m_vec[2] = sign[2] * (data_in[2] - bias[2]) / scale; } orientation_sensor::orientation_sensor() @@ -105,7 +103,6 @@ bool orientation_sensor::init(void) return true; } - sensor_type_t orientation_sensor::get_type(void) { return ORIENTATION_SENSOR; @@ -116,10 +113,13 @@ bool orientation_sensor::on_start(void) AUTOLOCK(m_mutex); m_accel_sensor->add_client(ACCELEROMETER_EVENT_RAW_DATA_REPORT_ON_TIME); + m_accel_sensor->add_interval((int)this, (m_interval/MS_TO_US), true); m_accel_sensor->start(); m_gyro_sensor->add_client(GYROSCOPE_EVENT_RAW_DATA_REPORT_ON_TIME); + m_gyro_sensor->add_interval((int)this, (m_interval/MS_TO_US), true); m_gyro_sensor->start(); m_magnetic_sensor->add_client(GEOMAGNETIC_EVENT_RAW_DATA_REPORT_ON_TIME); + m_magnetic_sensor->add_interval((int)this, (m_interval/MS_TO_US), true); m_magnetic_sensor->start(); activate(); @@ -128,11 +128,16 @@ bool orientation_sensor::on_start(void) bool orientation_sensor::on_stop(void) { + AUTOLOCK(m_mutex); + m_accel_sensor->delete_client(ACCELEROMETER_EVENT_RAW_DATA_REPORT_ON_TIME); + m_accel_sensor->delete_interval((int)this, true); m_accel_sensor->stop(); m_gyro_sensor->delete_client(GYROSCOPE_EVENT_RAW_DATA_REPORT_ON_TIME); + m_gyro_sensor->delete_interval((int)this, true); m_gyro_sensor->stop(); m_magnetic_sensor->delete_client(GEOMAGNETIC_EVENT_RAW_DATA_REPORT_ON_TIME); + m_magnetic_sensor->delete_interval((int)this, true); m_magnetic_sensor->stop(); deactivate(); @@ -166,16 +171,9 @@ void orientation_sensor::synthesize(const sensor_event_t &event, vector accel; - sensor_data gyro; - sensor_data magnetic; - - sensor_data_t accel_data; - sensor_data_t gyro_data; - sensor_data_t magnetic_data; - sensor_event_t orientation_event; euler_angles euler_orientation; + float raw_data[3]; if (event.event_type == ACCELEROMETER_EVENT_RAW_DATA_REPORT_ON_TIME) { diff_time = event.data.timestamp - m_timestamp; @@ -183,7 +181,9 @@ void orientation_sensor::synthesize(const sensor_event_t &event, vectorget_sensor_data(GYROSCOPE_EVENT_RAW_DATA_REPORT_ON_TIME, gyro_data); m_magnetic_sensor->get_sensor_data(GEOMAGNETIC_EVENT_RAW_DATA_REPORT_ON_TIME, magnetic_data); - pre_process_data(accel, accel_data, bias_accel, sign_accel, scale_accel); - pre_process_data(gyro, gyro_data, bias_gyro, sign_gyro, scale_gyro); - pre_process_data(magnetic, magnetic_data, bias_magnetic, sign_magnetic, scale_magnetic); + pre_process_data(accel, accel_data.values, bias_accel, sign_accel, scale_accel); + pre_process_data(gyro, gyro_data.values, bias_gyro, sign_gyro, scale_gyro); + pre_process_data(magnetic, magnetic_data.values, bias_magnetic, sign_magnetic, scale_magnetic); + accel.m_time_stamp = accel_data.timestamp; + gyro.m_time_stamp = gyro_data.timestamp; + magnetic.m_time_stamp = magnetic_data.timestamp; m_orientation.m_pitch_phase_compensation = pitch_phase_compensation; m_orientation.m_roll_phase_compensation = roll_phase_compensation; diff --git a/src/orientation/orientation_sensor.h b/src/orientation/orientation_sensor.h index 9f289f0..f295ccc 100755 --- a/src/orientation/orientation_sensor.h +++ b/src/orientation/orientation_sensor.h @@ -47,6 +47,12 @@ private: sensor_base *m_gyro_sensor; sensor_base *m_magnetic_sensor; + sensor_data m_accel; + sensor_data m_gyro; + sensor_data m_magnetic; + + cmutex m_value_mutex; + orientation_filter m_orientation; unsigned int m_enable_orientation; -- 2.7.4 From 561758cb3c973ac15aefaa18dfc0dbc776180f77 Mon Sep 17 00:00:00 2001 From: Amit Dharmapurikar Date: Wed, 29 Oct 2014 17:10:34 +0530 Subject: [PATCH 07/16] Synchronizing sensord code with IIO driver support for accelerometer plugin The sensord code improvements are as follows: - sensor HAL plugin design has been changed to include common plugin code in sensor_hal parent class instead of individual sensor HAL plugins - the new plugin design allows easy modification for IIO or input based driver interface - sensor client library API has been changed - policy for loading plugins is based on configuration as well as the plugin directory path - code cleanup and bug fixes Contributors: Jae Hyun Jo Hyun-sung Kibak Yoon Amit Vithalrao Dharmapurikar Change-Id: I87776b07b6202af1c82ec4cdbff85df0553af8cb Signed-off-by: Amit Dharmapurikar --- packaging/sensord.spec | 18 +- src/accel/CMakeLists.txt | 2 +- src/accel/accel_sensor.cpp | 272 +---- src/accel/accel_sensor.h | 35 +- src/accel/accel_sensor_hal.cpp | 614 +++++------- src/accel/accel_sensor_hal.h | 82 +- src/libsensord/CMakeLists.txt | 20 +- src/libsensord/client.cpp | 1056 +++++++++++++------- src/libsensord/client_common.cpp | 185 ++-- src/libsensord/client_common.h | 26 +- src/libsensord/command_channel.cpp | 358 +++---- src/libsensord/command_channel.h | 13 +- src/libsensord/creg_event_info.h | 31 +- src/libsensord/csensor_event_listener.cpp | 672 +++++++++---- src/libsensord/csensor_event_listener.h | 126 +-- src/libsensord/csensor_handle_info.cpp | 61 +- src/libsensord/csensor_handle_info.h | 27 +- src/libsensord/poller.cpp | 15 +- src/libsensord/poller.h | 5 +- src/libsensord/sensor_accel.h | 56 +- src/libsensord/sensor_context.h | 11 +- src/libsensord/sensor_geomag.h | 23 +- src/libsensord/sensor_gravity.h | 17 +- src/libsensord/sensor_gyro.h | 17 +- src/libsensord/sensor_info_manager.cpp | 101 ++ src/libsensord/sensor_info_manager.h | 57 ++ src/libsensord/sensor_internal.h | 339 +++++++ .../{sensor.h => sensor_internal_deprecated.h} | 110 +- src/libsensord/sensor_light.h | 19 +- src/libsensord/sensor_linear_accel.h | 15 +- src/libsensord/sensor_motion.h | 19 +- src/libsensord/sensor_orientation.h | 16 +- src/libsensord/sensor_proxi.h | 9 +- src/server/CMakeLists.txt | 1 + src/server/command_worker.cpp | 653 ++++++++---- src/server/command_worker.h | 39 +- src/server/dbus_util.cpp | 132 +++ src/server/dbus_util.h | 32 + src/server/main.cpp | 11 +- src/server/server.cpp | 26 +- src/server/server.h | 11 +- src/shared/CMakeLists.txt | 4 +- src/shared/cbase_lock.cpp | 38 +- src/shared/cbase_lock.h | 33 +- src/shared/cclient_info_manager.cpp | 204 ++-- src/shared/cclient_info_manager.h | 66 +- src/shared/cclient_sensor_record.cpp | 137 ++- src/shared/cclient_sensor_record.h | 48 +- src/shared/cconfig.cpp | 182 ++-- src/shared/cconfig.h | 54 +- src/shared/cinterval_info_list.cpp | 28 +- src/shared/cinterval_info_list.h | 15 +- src/shared/cmutex.cpp | 7 +- src/shared/cmutex.h | 9 +- src/shared/common.cpp | 132 +-- src/shared/common.h | 22 +- src/shared/cpacket.cpp | 91 +- src/shared/cpacket.h | 32 +- src/shared/crw_lock.cpp | 9 + src/shared/crw_lock.h | 7 +- src/shared/csensor_event_dispatcher.cpp | 175 ++-- src/shared/csensor_event_dispatcher.h | 25 +- src/shared/csensor_event_queue.cpp | 30 +- src/shared/csensor_event_queue.h | 79 +- src/shared/csensor_usage.cpp | 26 +- src/shared/csensor_usage.h | 10 +- src/shared/csocket.cpp | 95 +- src/shared/csocket.h | 27 +- src/shared/fusion_util.cpp | 56 ++ src/shared/fusion_util.h | 34 + src/shared/iio_common.cpp | 14 +- src/shared/iio_common.h | 3 +- src/shared/physical_sensor.cpp | 5 + src/shared/physical_sensor.h | 2 +- src/shared/sensor_base.cpp | 113 ++- src/shared/sensor_base.h | 46 +- src/shared/sensor_common.h | 161 ++- src/shared/sensor_hal.cpp | 413 +++++++- src/shared/sensor_hal.h | 71 +- src/shared/sensor_info.cpp | 311 ++++++ src/shared/sensor_info.h | 95 ++ src/shared/sensor_plugin_loader.cpp | 336 +++++-- src/shared/sensor_plugin_loader.h | 53 +- src/shared/sensord-server.pc.in | 0 src/shared/sf_common.h | 100 +- src/shared/virtual_sensor.cpp | 4 + src/shared/virtual_sensor.h | 5 +- src/shared/worker_thread.cpp | 14 +- src/shared/worker_thread.h | 3 +- 89 files changed, 5568 insertions(+), 3188 deletions(-) create mode 100755 src/libsensord/sensor_info_manager.cpp create mode 100755 src/libsensord/sensor_info_manager.h create mode 100755 src/libsensord/sensor_internal.h rename src/libsensord/{sensor.h => sensor_internal_deprecated.h} (53%) mode change 100755 => 100644 src/libsensord/sensor_orientation.h create mode 100755 src/server/dbus_util.cpp create mode 100755 src/server/dbus_util.h create mode 100644 src/shared/fusion_util.cpp create mode 100755 src/shared/fusion_util.h create mode 100755 src/shared/sensor_info.cpp create mode 100755 src/shared/sensor_info.h mode change 100755 => 100644 src/shared/sensord-server.pc.in diff --git a/packaging/sensord.spec b/packaging/sensord.spec index d760af3..30202f7 100755 --- a/packaging/sensord.spec +++ b/packaging/sensord.spec @@ -9,15 +9,15 @@ Source1: sensord.service Source2: sensord.socket %define accel_state ON -%define gyro_state ON -%define proxi_state ON -%define light_state ON -%define geo_state ON -%define pressure_state ON -%define temperature_state ON -%define orientation_state ON -%define gravity_state ON -%define linear_accel_state ON +%define gyro_state OFF +%define proxi_state OFF +%define light_state OFF +%define geo_state OFF +%define pressure_state OFF +%define temperature_state OFF +%define orientation_state OFF +%define gravity_state OFF +%define linear_accel_state OFF %define build_test_suite OFF diff --git a/src/accel/CMakeLists.txt b/src/accel/CMakeLists.txt index b4dcd9a..56e9119 100755 --- a/src/accel/CMakeLists.txt +++ b/src/accel/CMakeLists.txt @@ -15,7 +15,7 @@ include_directories(${CMAKE_SOURCE_DIR}/src/libsensord) include(FindPkgConfig) pkg_check_modules(rpkgs REQUIRED vconf) -add_definitions(${rpkgs_CFLAGS} -DUSE_ONLY_ONE_MODULE) +add_definitions(${rpkgs_CFLAGS} -DUSE_ONLY_ONE_MODULE -DUSE_LCD_TYPE_CHECK) set(PROJECT_MAJOR_VERSION "0") set(PROJECT_MINOR_VERSION "0") diff --git a/src/accel/accel_sensor.cpp b/src/accel/accel_sensor.cpp index 21d256d..7323b35 100755 --- a/src/accel/accel_sensor.cpp +++ b/src/accel/accel_sensor.cpp @@ -19,6 +19,7 @@ #include #include + #include #include #include @@ -34,32 +35,6 @@ using std::mem_fun; #define SENSOR_NAME "ACCELEROMETER_SENSOR" -#define ROTATION_CHECK_INTERVAL 200 -#define ROTATION_RULE_CNT 4 -#define TILT_MIN 30 - -#define ROTATION_0 0 -#define ROTATION_90 90 -#define ROTATION_180 180 -#define ROTATION_270 270 -#define ROTATION_360 360 - -#define DEGREE_90 90 -#define DEGREE_180 180 -#define DEGREE_360 360 - -struct rotation_rule { - int tilt; - int angle; -}; - -struct rotation_rule rot_rule[ROTATION_RULE_CNT] = { - {40, 80}, - {50, 70}, - {60, 65}, - {90, 60}, -}; - accel_sensor::accel_sensor() : m_sensor_hal(NULL) , m_interval(POLL_1HZ_MS) @@ -67,20 +42,18 @@ accel_sensor::accel_sensor() m_name = string(SENSOR_NAME); vector supported_events = { - ACCELEROMETER_EVENT_ROTATION_CHECK, ACCELEROMETER_EVENT_RAW_DATA_REPORT_ON_TIME, - ACCELEROMETER_EVENT_ORIENTATION_DATA_REPORT_ON_TIME, }; for_each(supported_events.begin(), supported_events.end(), - bind1st(mem_fun(&sensor_base::register_supported_event), this)); + bind1st(mem_fun(&sensor_base::register_supported_event), this)); physical_sensor::set_poller(accel_sensor::working, this); } accel_sensor::~accel_sensor() { - INFO("accel_sensor is destroyed!"); + INFO("accel_sensor is destroyed!\n"); } bool accel_sensor::init() @@ -95,14 +68,15 @@ bool accel_sensor::init() sensor_properties_t properties; if (m_sensor_hal->get_properties(properties) == false) { - ERR("sensor->get_properties() is failed!"); + ERR("sensor->get_properties() is failed!\n"); return false; } - m_raw_data_unit = properties.sensor_resolution / GRAVITY * G_TO_MG; + m_raw_data_unit = properties.resolution / GRAVITY * G_TO_MG; + + INFO("m_raw_data_unit accel : [%f]\n", m_raw_data_unit); - INFO("m_raw_data_unit accel : [%f]", m_raw_data_unit); - INFO("%s is created!", sensor_base::get_name()); + INFO("%s is created!\n", sensor_base::get_name()); return true; } @@ -113,160 +87,66 @@ sensor_type_t accel_sensor::get_type(void) bool accel_sensor::working(void *inst) { - accel_sensor *sensor = (accel_sensor *)inst; + accel_sensor *sensor = (accel_sensor*)inst; return sensor->process_event(); } bool accel_sensor::process_event(void) { - sensor_data_t raw_data; + sensor_event_t base_event; if (!m_sensor_hal->is_data_ready(true)) return true; - m_sensor_hal->get_sensor_data(raw_data); + m_sensor_hal->get_sensor_data(base_event.data); AUTOLOCK(m_mutex); AUTOLOCK(m_client_info_mutex); if (get_client_cnt(ACCELEROMETER_EVENT_RAW_DATA_REPORT_ON_TIME)) { - sensor_event_t base_event; - - copy_sensor_data(&raw_data, &(base_event.data)); - + base_event.sensor_id = get_id(); base_event.event_type = ACCELEROMETER_EVENT_RAW_DATA_REPORT_ON_TIME; raw_to_base(base_event.data); push(base_event); } - if (get_client_cnt(ACCELEROMETER_EVENT_ROTATION_CHECK)) { - if (is_rotation_time()) { - sensor_data_t base_data; - float x, y, z; - int rotation; - - copy_sensor_data(&raw_data, &base_data); - raw_to_base(base_data); - - x = base_data.values[0]; - y = base_data.values[1]; - z = base_data.values[2]; - - rotation = get_rotation_event(x, y, z); - - if (rotation != -1) { - AUTOLOCK(m_value_mutex); - - sensor_event_t rotation_event; - rotation_event.event_type = ACCELEROMETER_EVENT_ROTATION_CHECK; - rotation_event.data.timestamp = raw_data.timestamp; - rotation_event.data.values_num = 1; - rotation_event.data.values[0] = rotation; - push(rotation_event); - - INFO("Rotation event occurred, rotation value = %d", rotation); - } - } - } - - if (get_client_cnt(ACCELEROMETER_EVENT_ORIENTATION_DATA_REPORT_ON_TIME)) { - sensor_event_t orientation_event; - - copy_sensor_data(&raw_data, &(orientation_event.data)); - - orientation_event.event_type = ACCELEROMETER_EVENT_ORIENTATION_DATA_REPORT_ON_TIME; - raw_to_orientation(raw_data, orientation_event.data); - push(orientation_event); - } - return true; } bool accel_sensor::on_start(void) { - AUTOLOCK(m_mutex); - if (!m_sensor_hal->enable()) { - ERR("m_sensor_hal start fail"); + ERR("m_sensor_hal start fail\n"); return false; } - reset_rotation(); - return start_poll(); } bool accel_sensor::on_stop(void) { - AUTOLOCK(m_mutex); - if (!m_sensor_hal->disable()) { - ERR("m_sensor_hal stop fail"); + ERR("m_sensor_hal stop fail\n"); return false; } return stop_poll(); } -bool accel_sensor::add_client(unsigned int event_type) -{ - AUTOLOCK(m_mutex); - - if (!sensor_base::add_client(event_type)) - return false; - - switch (event_type) { - case ACCELEROMETER_EVENT_ROTATION_CHECK: - if (get_client_cnt(ACCELEROMETER_EVENT_ROTATION_CHECK) == 1) - reset_rotation(); - break; - default: - break; - } - - return true; -} - -long accel_sensor::set_command(const unsigned int cmd, long value) -{ - if (m_sensor_hal->set_command(cmd, value) < 0) { - ERR("m_sensor_hal set_cmd fail"); - return -1; - } - - return 0; -} - -bool accel_sensor::get_properties(const unsigned int type, sensor_properties_t &properties) +bool accel_sensor::get_properties(sensor_properties_t &properties) { return m_sensor_hal->get_properties(properties); } -int accel_sensor::get_sensor_data(const unsigned int type, sensor_data_t &data) +int accel_sensor::get_sensor_data(unsigned int type, sensor_data_t &data) { - if (type == ACCELEROMETER_ROTATION_DATA_SET) { - AUTOLOCK(m_value_mutex); - - data.data_accuracy = SENSOR_ACCURACY_NORMAL; - data.data_unit_idx = SENSOR_UNDEFINED_UNIT; - data.values_num = 1; - data.values[0] = m_rotation; - data.timestamp = m_rotation_time; - return 0; - } - if (m_sensor_hal->get_sensor_data(data) < 0) { ERR("Failed to get sensor data"); return -1; } - if (type == ACCELEROMETER_BASE_DATA_SET) + if (type == ACCELEROMETER_BASE_DATA_SET) { raw_to_base(data); - else if (type == ACCELEROMETER_ORIENTATION_DATA_SET) { - sensor_data_t raw; - - copy_sensor_data(&data, &raw); - raw_to_orientation(raw, data); } else { ERR("Does not support type: 0x%x", type); return -1; @@ -275,131 +155,25 @@ int accel_sensor::get_sensor_data(const unsigned int type, sensor_data_t &data) return 0; } -int accel_sensor::get_rotation_event(float x, float y, float z) -{ - int cur_rotation = ROTATION_UNKNOWN; - - double atan_value; - int acc_theta, acc_pitch; - double realg; - bool is_stable = false; - bool rotation_on = false; - int tilt, angle; - int i; - - atan_value = atan2(x, y); - acc_theta = (int)(atan_value * (RADIAN_VALUE) + DEGREE_360) % DEGREE_360; - realg = (double)sqrt((x * x) + (y * y) + (z * z)); - acc_pitch = ROTATION_90 - abs((int) (asin(z / realg) * RADIAN_VALUE)); - - for (i = 0; i < ROTATION_RULE_CNT; ++i) { - tilt = rot_rule[i].tilt; - - if ((acc_pitch >= TILT_MIN) && (acc_pitch <= tilt)) { - if ((m_rotation == ROTATION_EVENT_0) || (m_rotation == ROTATION_EVENT_180)) - angle = rot_rule[i].angle; - else - angle = ROTATION_90 - rot_rule[i].angle; - - if ((acc_theta >= ROTATION_360 - angle && acc_theta <= ROTATION_360 - 1) || - (acc_theta >= ROTATION_0 && acc_theta <= ROTATION_0 + angle)) { - cur_rotation = ROTATION_EVENT_0; - } else if (acc_theta >= ROTATION_0 + angle && acc_theta <= ROTATION_180 - angle) { - cur_rotation = ROTATION_EVENT_90; - } else if (acc_theta >= ROTATION_180 - angle && acc_theta <= ROTATION_180 + angle) { - cur_rotation = ROTATION_EVENT_180; - } else if (acc_theta >= ROTATION_180 + angle && acc_theta <= ROTATION_360 - angle) { - cur_rotation = ROTATION_EVENT_270; - } - break; - } - } - - m_windowing[m_curr_window_count++] = cur_rotation; - - if (m_curr_window_count == MAX_WINDOW_NUM) - m_curr_window_count = 0; - - for (i = 0; i < MAX_WINDOW_NUM ; i++) { - if (m_windowing[i] == cur_rotation) - is_stable = true; - else { - is_stable = false; - break; - } - } - - rotation_on = (m_rotation != cur_rotation); - - if (rotation_on && is_stable) { - m_rotation = cur_rotation; - m_rotation_time = get_timestamp(); - return m_rotation; - } - - return -1; -} - -void accel_sensor::reset_rotation(void) -{ - int i; - - for (i = 0 ; i < MAX_WINDOW_NUM ; i++) - m_windowing[i] = 0; - - m_curr_window_count = 0; - m_rotation = ROTATION_UNKNOWN; - m_rotation_time = 0; - m_rotation_check_remained_time = ROTATION_CHECK_INTERVAL; -} - -bool accel_sensor::is_rotation_time(void) -{ - AUTOLOCK(m_mutex); - m_rotation_check_remained_time -= m_interval; - - if (m_rotation_check_remained_time <= 0) { - m_rotation_check_remained_time = ROTATION_CHECK_INTERVAL; - return true; - } - - return false; -} - bool accel_sensor::set_interval(unsigned long interval) { AUTOLOCK(m_mutex); m_interval = interval; + INFO("Polling interval is set to %dms", interval); + return m_sensor_hal->set_interval(interval); } void accel_sensor::raw_to_base(sensor_data_t &data) { - data.data_unit_idx = SENSOR_UNIT_METRE_PER_SECOND_SQUARED; - data.values_num = 3; + data.value_count = 3; data.values[0] = RAW_DATA_TO_METRE_PER_SECOND_SQUARED_UNIT(data.values[0] * m_raw_data_unit); data.values[1] = RAW_DATA_TO_METRE_PER_SECOND_SQUARED_UNIT(data.values[1] * m_raw_data_unit); data.values[2] = RAW_DATA_TO_METRE_PER_SECOND_SQUARED_UNIT(data.values[2] * m_raw_data_unit); } -void accel_sensor::raw_to_orientation(sensor_data_t &raw, sensor_data_t &orientation) -{ - orientation.timestamp = raw.time_stamp; - orientation.data_accuracy = raw.data_accuracy; - orientation.data_unit_idx = SENSOR_UNIT_DEGREE; - orientation.values_num = 3; - orientation.values[0] = fmodf((atan2(raw.values[0], raw.values[1]) * RADIAN_VALUE + DEGREE_360), DEGREE_360); - orientation.values[1] = fmodf((atan2(raw.values[1], raw.values[2]) * RADIAN_VALUE), DEGREE_180); - orientation.values[2] = fmodf((atan2(raw.values[0], raw.values[2]) * RADIAN_VALUE), DEGREE_180); - - if (orientation.values[2] > DEGREE_90) - orientation.values[2] = DEGREE_180 - orientation.values[2]; - else if (orientation.values[2] < -DEGREE_90) - orientation.values[2] = -DEGREE_180 - orientation.values[2]; -} - extern "C" void *create(void) { accel_sensor *inst; @@ -407,14 +181,14 @@ extern "C" void *create(void) try { inst = new accel_sensor(); } catch (int err) { - ERR("Failed to create accel_sensor class, errno : %d, errstr : %s", err, strerror(err)); + ERR("accel_sensor class create fail , errno : %d , errstr : %s\n", err, strerror(err)); return NULL; } - return (void *)inst; + return (void*)inst; } extern "C" void destroy(void *inst) { - delete (accel_sensor *)inst; + delete (accel_sensor*)inst;; } diff --git a/src/accel/accel_sensor.h b/src/accel/accel_sensor.h index dedf8c2..5d946ba 100755 --- a/src/accel/accel_sensor.h +++ b/src/accel/accel_sensor.h @@ -21,52 +21,35 @@ #define _ACCEL_SENSOR_H_ #include + #include #include -class accel_sensor : public physical_sensor -{ +class accel_sensor : public physical_sensor { public: accel_sensor(); virtual ~accel_sensor(); - virtual bool init(); + bool init(); virtual sensor_type_t get_type(void); static bool working(void *inst); - - virtual bool on_start(void); - virtual bool on_stop(void); - - bool add_client(unsigned int event_type); - virtual bool set_interval(unsigned long interval); - virtual long set_command(const unsigned int cmd, long value); - virtual bool get_properties(const unsigned int type, sensor_properties_t &properties); - int get_sensor_data(const unsigned int type, sensor_data_t &data); + virtual bool get_properties(sensor_properties_t &properties); + virtual int get_sensor_data(unsigned int type, sensor_data_t &data); private: sensor_hal *m_sensor_hal; cmutex m_value_mutex; float m_raw_data_unit; - int m_rotation; - unsigned long long m_rotation_time; unsigned long m_interval; - int m_curr_window_count; - static const int RADIAN_VALUE = 57.29747; - static const int MAX_WINDOW_NUM = 2; - long m_windowing[MAX_WINDOW_NUM]; - - int m_rotation_check_remained_time; - - int get_rotation_event(float x, float y, float z); - void reset_rotation(void); - bool is_rotation_time(void); + virtual bool on_start(void); + virtual bool on_stop(void); void raw_to_base(sensor_data_t &data); - void raw_to_orientation(sensor_data_t &raw, sensor_data_t &orientation); bool process_event(void); }; -#endif /*_ACCEL_SENSOR_H_*/ + +#endif diff --git a/src/accel/accel_sensor_hal.cpp b/src/accel/accel_sensor_hal.cpp index 63b499b..6b155b7 100755 --- a/src/accel/accel_sensor_hal.cpp +++ b/src/accel/accel_sensor_hal.cpp @@ -1,5 +1,5 @@ /* - * sensord + * accel_sensor_hal * * Copyright (c) 2014 Samsung Electronics Co., Ltd. * @@ -16,94 +16,160 @@ * limitations under the License. * */ - -#include #include #include -#include + #include #include + #include #include -#include - using std::ifstream; using config::CConfig; -#define INITIAL_VALUE -1 -#define INITIAL_TIME 0 #define GRAVITY 9.80665 #define G_TO_MG 1000 #define RAW_DATA_TO_G_UNIT(X) (((float)(X))/((float)G_TO_MG)) #define RAW_DATA_TO_METRE_PER_SECOND_SQUARED_UNIT(X) (GRAVITY * (RAW_DATA_TO_G_UNIT(X))) -#define MIN_RANGE(RES) (-((2 << (RES))/2)) -#define MAX_RANGE(RES) (((2 << (RES))/2)-1) - -#define SEC_MSEC 1000 -#define MSEC_TO_FREQ(VAL) (int)((SEC_MSEC) / (VAL)) +#define MIN_RANGE(RES) (-((1 << (RES))/2)) +#define MAX_RANGE(RES) (((1 << (RES))/2)-1) #define SENSOR_TYPE_ACCEL "ACCEL" #define ELEMENT_NAME "NAME" #define ELEMENT_VENDOR "VENDOR" #define ELEMENT_RAW_DATA_UNIT "RAW_DATA_UNIT" #define ELEMENT_RESOLUTION "RESOLUTION" + #define ATTR_VALUE "value" -#define ENABLE 1 -#define DISABLE 0 +#define INPUT_NAME "accelerometer_sensor" +#define ACCEL_SENSORHUB_POLL_NODE_NAME "accel_poll_delay" + +#define SCAN_EL_DIR "scan_elements/" +#define ACCEL_RINGBUF_LEN 32 +#define SEC_MSEC 1000 +#define MSEC_TO_FREQ(VAL) ((SEC_MSEC) / (VAL)) +#define NSEC_TO_MUSEC(VAL) ((VAL) / 1000) accel_sensor_hal::accel_sensor_hal() -: m_x(INITIAL_VALUE) -, m_y(INITIAL_VALUE) -, m_z(INITIAL_VALUE) +: m_x(-1) +, m_y(-1) +, m_z(-1) +, m_node_handle(-1) , m_polling_interval(POLL_1HZ_MS) -, m_fired_time(INITIAL_TIME) -, m_sensorhub_supported(false) +, m_fired_time(0) { - if (!check_hw_node()) { - ERR("check_hw_node() fail"); + const string sensorhub_interval_node_name = "accel_poll_delay"; + CConfig &config = CConfig::get_instance(); + + node_path_info_query query; + node_path_info info; + int input_method = IIO_METHOD; + + if (!get_model_properties(SENSOR_TYPE_ACCEL, m_model_id, input_method)) { + ERR("Failed to find model_properties"); throw ENXIO; } - CConfig &config = CConfig::get_instance(); + query.input_method = input_method; + query.sensorhub_controlled = m_sensorhub_controlled = is_sensorhub_controlled(sensorhub_interval_node_name); + query.sensor_type = SENSOR_TYPE_ACCEL; + query.input_event_key = "accelerometer_sensor"; + query.iio_enable_node_name = "accel_enable"; + query.sensorhub_interval_node_name = sensorhub_interval_node_name; + + if (!get_node_path_info(query, info)) { + ERR("Failed to get node info"); + throw ENXIO; + } + + show_node_path_info(info); + + m_data_node = info.data_node_path; + m_interval_node = info.interval_node_path; + m_accel_dir = info.base_dir; + m_trigger_path = info.trigger_node_path; + m_buffer_enable_node_path = info.buffer_enable_node_path; + m_buffer_length_node_path = info.buffer_length_node_path; + m_available_freq_node_path = info.available_freq_node_path; + m_available_scale_node_path = info.available_scale_node_path; if (!config.get(SENSOR_TYPE_ACCEL, m_model_id, ELEMENT_VENDOR, m_vendor)) { - ERR("[VENDOR] is empty"); + ERR("[VENDOR] is empty\n"); throw ENXIO; } INFO("m_vendor = %s", m_vendor.c_str()); if (!config.get(SENSOR_TYPE_ACCEL, m_model_id, ELEMENT_NAME, m_chip_name)) { - ERR("[NAME] is empty"); + ERR("[NAME] is empty\n"); throw ENXIO; } - INFO("m_chip_name = %s", m_chip_name.c_str()); + INFO("m_chip_name = %s\n",m_chip_name.c_str()); + + if (input_method == IIO_METHOD) { + m_trigger_name = m_model_id + "-trigger"; + if (!verify_iio_trigger(m_trigger_name)) { + ERR("Failed verify trigger"); + throw ENXIO; + } + string scan_dir = m_accel_dir + "scan_elements/"; + if (!get_generic_channel_names(scan_dir, string("_type"), m_generic_channel_names)) + ERR ("Failed to find any input channels"); + else + { + INFO ("generic channel names:"); + for (vector ::iterator it = m_generic_channel_names.begin(); + it != m_generic_channel_names.end(); ++it) { + INFO ("%s", it->c_str()); + } + } + } long resolution; if (!config.get(SENSOR_TYPE_ACCEL, m_model_id, ELEMENT_RESOLUTION, resolution)) { - ERR("[RESOLUTION] is empty"); + ERR("[RESOLUTION] is empty\n"); throw ENXIO; } m_resolution = (int)resolution; - INFO("m_resolution = %d", m_resolution); + + INFO("m_resolution = %d\n",m_resolution); double raw_data_unit; if (!config.get(SENSOR_TYPE_ACCEL, m_model_id, ELEMENT_RAW_DATA_UNIT, raw_data_unit)) { - ERR("[RAW_DATA_UNIT] is empty"); + ERR("[RAW_DATA_UNIT] is empty\n"); throw ENXIO; } m_raw_data_unit = (float)(raw_data_unit); - INFO("m_raw_data_unit = %f", m_raw_data_unit); - INFO("accel_sensor_hal is created!"); + m_node_handle = open(m_data_node.c_str(), O_RDONLY | O_NONBLOCK); + if (m_node_handle < 0) { + ERR("accel handle open fail for accel processor, error:%s\n", strerror(errno)); + throw ENXIO; + } + + if (setup_channels() == true) + INFO("IIO channel setup successful"); + else { + ERR("IIO channel setup failed"); + throw ENXIO; + } + +// int clockId = CLOCK_MONOTONIC; +// if (ioctl(m_node_handle, EVIOCSCLOCKID, &clockId) != 0) { +// ERR("Fail to set monotonic timestamp for %s", m_data_node.c_str()); +// throw ENXIO; +// } + + INFO("m_raw_data_unit = %f\n", m_raw_data_unit); + INFO("accel_sensor is created!\n"); } accel_sensor_hal::~accel_sensor_hal() @@ -111,10 +177,11 @@ accel_sensor_hal::~accel_sensor_hal() enable_resource(false); if (m_data != NULL) delete []m_data; - if (m_fp_buffer > 0) - close(m_fp_buffer); - INFO("accel_sensor_hal is destroyed!"); + close(m_node_handle); + m_node_handle = -1; + + INFO("accel_sensor is destroyed!\n"); } string accel_sensor_hal::get_model_id(void) @@ -127,57 +194,150 @@ sensor_type_t accel_sensor_hal::get_type(void) return ACCELEROMETER_SENSOR; } -long accel_sensor_hal::set_command(const unsigned int cmd, long value) +bool accel_sensor_hal::add_accel_channels_to_array(void) { - AUTOLOCK(m_mutex); - switch (cmd) { - case ACCELEROMETER_PROPERTY_SET_CALIBRATION : - ERR("Accel sensor calibration not supported\n"); - return -1; - case ACCELEROMETER_PROPERTY_CHECK_CALIBRATION_STATUS : - ERR("Accel sensor calibration check not supported\n"); - return -1; - default: - ERR("Invalid property_cmd\n"); - break; + int i = 0; + m_channels = (struct channel_parameters*) malloc(sizeof(struct channel_parameters) * m_generic_channel_names.size()); + for (vector ::iterator it = m_generic_channel_names.begin(); + it != m_generic_channel_names.end(); ++it) { + if (add_channel_to_array(m_accel_dir.c_str(), it->c_str() , &m_channels[i++]) < 0) { + ERR("Failed to add channel %s to channel array", it->c_str()); + return false; + } } - return -1; + return true; } -bool accel_sensor_hal::enable_resource(bool enable) +bool accel_sensor_hal::setup_channels(void) { - string temp; - int enable_val; + int freq, i; + double sf; - if(enable) - { - enable_val = ENABLE; - setup_trigger(INPUT_TRIG_NAME, enable); + enable_resource(true); + + if (!add_accel_channels_to_array()) { + ERR("Failed to add channels to array!"); + return false; } - else - { - enable_val = DISABLE; - setup_trigger("NULL", enable); + + INFO("Sorting channels by index"); + sort_channels_by_index(m_channels, m_generic_channel_names.size()); + INFO("Sorting channels by index completed"); + + m_scan_size = get_channel_array_size(m_channels, m_generic_channel_names.size()); + if (m_scan_size == 0) { + ERR("Channel array size is zero"); + return false; } - temp = m_accel_dir + string(SCAN_EL_DIR) + string(CHANNEL_NAME_X) + string(ENABLE_SUFFIX); - if (update_sysfs_num(temp.c_str(), enable_val) < 0) + m_data = new (std::nothrow) char[m_scan_size * ACCEL_RINGBUF_LEN]; + if (m_data == NULL) { + ERR("Couldn't create data buffer\n"); return false; + } - temp = m_accel_dir + string(SCAN_EL_DIR) + string(CHANNEL_NAME_Y) + string(ENABLE_SUFFIX); - if (update_sysfs_num(temp.c_str(), enable_val) < 0) + FILE *fp = NULL; + fp = fopen(m_available_freq_node_path.c_str(), "r"); + if (!fp) { + ERR("Fail to open available frequencies file:%s\n", m_available_freq_node_path.c_str()); return false; + } - temp = m_accel_dir + string(SCAN_EL_DIR) + string(CHANNEL_NAME_Z) + string(ENABLE_SUFFIX); - if (update_sysfs_num(temp.c_str(), enable_val) < 0) + for (i = 0; i < MAX_FREQ_COUNT; i++) + m_sample_freq[i] = 0; + + i = 0; + + while (fscanf(fp, "%d", &freq) > 0) + m_sample_freq[i++] = freq; + + m_sample_freq_count = i; + + fp = fopen(m_available_scale_node_path.c_str(), "r"); + if (!fp) { + ERR("Fail to open available scale factors file:%s\n", m_available_scale_node_path.c_str()); return false; + } - temp = m_accel_dir + string(SCAN_EL_DIR) + string(CHANNEL_NAME_TIME) + string(ENABLE_SUFFIX); - if (update_sysfs_num(temp.c_str(), enable_val) < 0) + for (i = 0; i < MAX_SCALING_COUNT; i++) + m_scale_factor[i] = 0; + + i = 0; + + while (fscanf(fp, "%lf", &sf) > 0) + m_scale_factor[i++] = sf; + + m_scale_factor_count = i; + + return true; +} + +void accel_sensor_hal::decode_data(void) +{ + AUTOLOCK(m_value_mutex); + + m_x = convert_bytes_to_int(*(unsigned short int *)(m_data + m_channels[0].buf_index), &m_channels[0]); + m_y = convert_bytes_to_int(*(unsigned short int *)(m_data + m_channels[1].buf_index), &m_channels[1]); + m_z = convert_bytes_to_int(*(unsigned short int *)(m_data + m_channels[2].buf_index), &m_channels[2]); + + long long int val = *(long long int *)(m_data + m_channels[3].buf_index); + if ((val >> m_channels[3].valid_bits) & 1) + val = (val & m_channels[3].mask) | ~m_channels[3].mask; + + m_fired_time = (unsigned long long int)(NSEC_TO_MUSEC(val)); + DBG("m_x = %d, m_y = %d, m_z = %d, time = %lluus", m_x, m_y, m_z, m_fired_time); +} +bool accel_sensor_hal::setup_trigger(const char* trig_name, bool verify) +{ + int ret = 0; + + ret = update_sysfs_string(m_trigger_path.c_str(), trig_name); + if (ret < 0) { + ERR("failed to write to current_trigger,%s,%s\n", m_trigger_path.c_str(), trig_name); + return false; + } + INFO("current_trigger setup successfully\n"); + return true; +} + +bool accel_sensor_hal::setup_buffer(int enable) +{ + int ret; + ret = update_sysfs_num(m_buffer_length_node_path.c_str(), ACCEL_RINGBUF_LEN, true); + if (ret < 0) { + ERR("failed to write to buffer/length\n"); return false; + } + INFO("buffer/length setup successfully\n"); - setup_buffer(enable_val); + ret = update_sysfs_num(m_buffer_enable_node_path.c_str(), enable, true); + if (ret < 0) { + ERR("failed to write to buffer/enable\n"); + return false; + } + if (enable) + INFO("buffer enabled\n"); + else + INFO("buffer disabled\n"); + return true; +} + +bool accel_sensor_hal::enable_resource(bool enable) +{ + string temp; + if(enable) + setup_trigger(m_trigger_name.c_str(), enable); + else + setup_trigger("NULL", enable); + + for (vector ::iterator it = m_generic_channel_names.begin(); + it != m_generic_channel_names.end(); ++it) { + temp = m_accel_dir + string(SCAN_EL_DIR) + *it + string("_en"); + if (update_sysfs_num(temp.c_str(), enable) < 0) + return false; + } + setup_buffer(enable); return true; } @@ -186,7 +346,8 @@ bool accel_sensor_hal::enable(void) AUTOLOCK(m_mutex); if (!enable_resource(true)) - return false; + return false; + set_interval(m_polling_interval); m_fired_time = 0; @@ -198,7 +359,9 @@ bool accel_sensor_hal::disable(void) { AUTOLOCK(m_mutex); - enable_resource(false); + if (!enable_resource(false)) + return false; + INFO("Accel sensor real stopping"); return true; } @@ -207,20 +370,16 @@ bool accel_sensor_hal::set_interval(unsigned long ms_interval) { int freq, i; - freq = MSEC_TO_FREQ(ms_interval); + freq = (int)(MSEC_TO_FREQ(ms_interval)); - for (i=0; i < m_sample_freq_count; i++) - { - if (freq == m_sample_freq[i]) - { - if (update_sysfs_num(m_freq_resource.c_str(), freq, true) == 0) - { + for (i=0; i < m_sample_freq_count; i++) { + if (freq == m_sample_freq[i]) { + if (update_sysfs_num(m_interval_node.c_str(), freq, true) == 0) { INFO("Interval is changed from %lums to %lums]", m_polling_interval, ms_interval); m_polling_interval = ms_interval; return true; } - else - { + else { ERR("Failed to set data %lu\n", ms_interval); return false; } @@ -239,21 +398,19 @@ bool accel_sensor_hal::update_value(bool wait) ssize_t read_size; const int TIMEOUT = 1000; - pfd.fd = m_fp_buffer; + pfd.fd = m_node_handle; pfd.events = POLLIN; if (wait) poll(&pfd, 1, TIMEOUT); else poll(&pfd, 1, 0); - read_size = read(m_fp_buffer, m_data, ACCEL_RINGBUF_LEN * m_scan_size); - if (read_size <= 0) - { + read_size = read(m_node_handle, m_data, ACCEL_RINGBUF_LEN * m_scan_size); + if (read_size <= 0) { ERR("Accel:No data available\n"); return false; } - else - { + else { for (i = 0; i < (read_size / m_scan_size); i++) decode_data(); } @@ -269,23 +426,11 @@ bool accel_sensor_hal::is_data_ready(bool wait) int accel_sensor_hal::get_sensor_data(sensor_data_t &data) { - const int chance = 3; - int retry = 0; - - while ((m_fired_time == 0) && (retry++ < chance)) { - INFO("Try usleep for getting a valid BASE DATA value"); - usleep(m_polling_interval * MS_TO_SEC); - } - - if (m_fired_time == 0) { - ERR("get_sensor_data failed"); - return -1; - } + AUTOLOCK(m_value_mutex); - data.data_accuracy = SENSOR_ACCURACY_GOOD; - data.data_unit_idx = SENSOR_UNIT_VENDOR_UNIT; - data.timestamp = m_fired_time ; - data.values_num = 3; + data.accuracy = SENSOR_ACCURACY_GOOD; + data.timestamp = m_fired_time; + data.value_count = 3; data.values[0] = m_x; data.values[1] = m_y; data.values[2] = m_z; @@ -295,279 +440,32 @@ int accel_sensor_hal::get_sensor_data(sensor_data_t &data) bool accel_sensor_hal::get_properties(sensor_properties_t &properties) { - properties.sensor_unit_idx = SENSOR_UNIT_METRE_PER_SECOND_SQUARED; - properties.sensor_min_range = MIN_RANGE(m_resolution) * RAW_DATA_TO_METRE_PER_SECOND_SQUARED_UNIT(m_raw_data_unit); - properties.sensor_max_range = MAX_RANGE(m_resolution) * RAW_DATA_TO_METRE_PER_SECOND_SQUARED_UNIT(m_raw_data_unit); - snprintf(properties.sensor_name, sizeof(properties.sensor_name), "%s", m_chip_name.c_str()); - snprintf(properties.sensor_vendor, sizeof(properties.sensor_vendor), "%s", m_vendor.c_str()); - properties.sensor_resolution = RAW_DATA_TO_METRE_PER_SECOND_SQUARED_UNIT(m_raw_data_unit); - return true; -} - -bool accel_sensor_hal::is_sensorhub_supported(void) -{ - return false; -} - -bool accel_sensor_hal::check_hw_node(void) -{ - string name_node; - string hw_name; - string file_name; - string temp; - DIR *main_dir = NULL; - struct dirent *dir_entry = NULL; - bool find_node = false; - bool find_trigger = false; - - INFO("======================start check_hw_node============================="); - - m_sensorhub_supported = is_sensorhub_supported(); - main_dir = opendir(IIO_DIR); - - if (!main_dir) { - ERR("Could not open IIO directory\n"); - return false; - } - - m_channels = (struct channel_parameters*) malloc(sizeof(struct channel_parameters) * NO_OF_CHANNELS); - - while (!(find_node && find_trigger)) - { - dir_entry = readdir(main_dir); - if(dir_entry == NULL) - break; - - if ((strncasecmp(dir_entry->d_name , ".", 1 ) != 0) && (strncasecmp(dir_entry->d_name , "..", 2 ) != 0) && (dir_entry->d_ino != 0)) - { - file_name = string(IIO_DIR) + string(dir_entry->d_name) + string(NAME_NODE); - ifstream infile(file_name.c_str()); - - if (!infile) - continue; - - infile >> hw_name; - - if (strncmp(dir_entry->d_name, "iio:device", 10) == 0) - { - if (hw_name == string(INPUT_DEV_NAME)) - { - m_accel_dir = string(IIO_DIR) + string(dir_entry->d_name) + string("/"); - m_buffer_access = string("/dev/") + string(dir_entry->d_name); - m_name = m_model_id = hw_name; - find_node = true; - DBG("m_accel_dir:%s\n", m_accel_dir.c_str()); - DBG("m_buffer_access:%s\n", m_buffer_access.c_str()); - DBG("m_name:%s\n", m_name.c_str()); - } - } - if (strncmp(dir_entry->d_name, "trigger", 7) == 0) - { - if (hw_name == string(INPUT_TRIG_NAME)) - { - m_accel_trig_dir = string(IIO_DIR) + string(dir_entry->d_name) + string("/"); - find_trigger = true; - DBG("m_accel_trig_dir:%s\n", m_accel_trig_dir.c_str()); - } - } - if (find_node && find_trigger) - break; - } - } - - closedir(main_dir); - - if (find_node && find_trigger) - { - if (setup_channels() == true) - INFO("IIO channel setup successful"); - else - { - ERR("IIO channel setup failed"); - return false; - } - } - return (find_node && find_trigger); -} - -bool accel_sensor_hal::add_accel_channels_to_array(void) -{ - if (add_channel_to_array(m_accel_dir.c_str(), CHANNEL_NAME_X, &m_channels[0]) < 0) - { - ERR("Failed to add channel x to channel array"); - return false; - } - if (add_channel_to_array(m_accel_dir.c_str(), CHANNEL_NAME_Y, &m_channels[1]) < 0) - { - ERR("Failed to add channel y to channel array"); - return false; - } - if (add_channel_to_array(m_accel_dir.c_str(), CHANNEL_NAME_Z, &m_channels[2]) < 0) - { - ERR("Failed to add channel z to channel array"); - return false; - } - if (add_channel_to_array(m_accel_dir.c_str(), CHANNEL_NAME_TIME, &m_channels[3]) < 0) - { - ERR("Failed to add channel time_stamp to channel array"); - return false; - } - return true; -} - -bool accel_sensor_hal::setup_channels(void) -{ - int freq, i; - double sf; - string temp; - - enable_resource(true); - - if (!add_accel_channels_to_array()) - return false; - - sort_channels_by_index(m_channels, NO_OF_CHANNELS); - - m_scan_size = get_channel_array_size(m_channels, NO_OF_CHANNELS); - if (m_scan_size == 0) - { - ERR("Channel array size is zero"); - return false; - } - - m_data = new (std::nothrow) char[m_scan_size * ACCEL_RINGBUF_LEN]; - if (m_data == NULL) - { - ERR("Couldn't create data buffer\n"); - return false; - } - - m_fp_buffer = open(m_buffer_access.c_str(), O_RDONLY | O_NONBLOCK); - if (m_fp_buffer == -1) - { - ERR("Failed to open ring buffer(%s)\n", m_buffer_access.c_str()); - return false; - } - - m_freq_resource = m_accel_dir + string(ACCEL_FREQ); - temp = m_accel_dir + string(ACCEL_FREQ_AVLBL); - FILE *fp = NULL; - fp = fopen(temp.c_str(), "r"); - if (!fp) - { - ERR("Fail to open available frequencies file:%s\n", temp.c_str()); - return false; - } - - for (i = 0; i < MAX_FREQ_COUNT; i++) - m_sample_freq[i] = 0; - - i = 0; - - while (fscanf(fp, "%d", &freq) > 0) - m_sample_freq[i++] = freq; - - m_sample_freq_count = i; - - temp = m_accel_dir + string(ACCEL_SCALE_AVLBL); - fp = fopen(temp.c_str(), "r"); - if (!fp) - { - ERR("Fail to open available scale factors file:%s\n", temp.c_str()); - return false; - } - - for (i = 0; i < MAX_SCALING_COUNT; i++) - m_scale_factor[i] = 0; - - i = 0; - - while (fscanf(fp, "%lf", &sf) > 0) - m_scale_factor[i++] = sf; - - m_scale_factor_count = i; - - return true; -} - -void accel_sensor_hal::decode_data(void) -{ - AUTOLOCK(m_value_mutex); - - m_x = convert_bytes_to_int(*(unsigned short int *)(m_data + m_channels[0].buf_index), &m_channels[0]); - m_y = convert_bytes_to_int(*(unsigned short int *)(m_data + m_channels[1].buf_index), &m_channels[1]); - m_z = convert_bytes_to_int(*(unsigned short int *)(m_data + m_channels[2].buf_index), &m_channels[2]); - - long long int val = *(long long int *)(m_data + m_channels[3].buf_index); - if ((val >> m_channels[3].valid_bits) & 1) - val = (val & m_channels[3].mask) | ~m_channels[3].mask; - - m_fired_time = (unsigned long long int)(val); - INFO("m_x = %d, m_y = %d, m_z = %d, time = %lluus", m_x, m_y, m_z, m_fired_time); -} - -bool accel_sensor_hal::setup_trigger(char* trig_name, bool verify) -{ - string temp; - int ret = 0; - - temp = m_accel_dir + string("trigger/current_trigger"); - ret = update_sysfs_string(temp.c_str(), trig_name, verify); - if (ret < 0) - { - ERR("failed to write to current_trigger\n"); - return false; - } - INFO("current_trigger setup successfully\n"); - return true; -} - -bool accel_sensor_hal::setup_buffer(int enable) -{ - string temp; - int ret; - temp = m_accel_dir + string(BUFFER_LEN); - INFO("Buffer Length Setup: %s", temp.c_str()); - ret = update_sysfs_num(temp.c_str(), ACCEL_RINGBUF_LEN, true); - if (ret < 0) - { - ERR("failed to write to buffer/length\n"); - return false; - } - INFO("buffer/length setup successfully\n"); - - temp = m_accel_dir + string(BUFFER_EN); - INFO("Buffer Enable: %s", temp.c_str()); - ret = update_sysfs_num(temp.c_str(), enable, true); - if (ret < 0) - { - ERR("failed to write to buffer/enable\n"); - return false; - } - if (enable) - INFO("buffer enabled\n"); - else - INFO("buffer disabled\n"); - + properties.name = m_chip_name; + properties.vendor = m_vendor; + properties.min_range = MIN_RANGE(m_resolution)* RAW_DATA_TO_METRE_PER_SECOND_SQUARED_UNIT(m_raw_data_unit); + properties.max_range = MAX_RANGE(m_resolution)* RAW_DATA_TO_METRE_PER_SECOND_SQUARED_UNIT(m_raw_data_unit); + properties.min_interval = 1; + properties.resolution = RAW_DATA_TO_METRE_PER_SECOND_SQUARED_UNIT(m_raw_data_unit); + properties.fifo_count = 0; + properties.max_batch_count = 0; return true; } extern "C" void *create(void) { accel_sensor_hal *inst; - INFO("creating accel_sensor_hal instance"); try { inst = new accel_sensor_hal(); } catch (int err) { - ERR("Failed to create accel_sensor_hal class, errno : %d, errstr : %s", err, strerror(err)); + ERR("accel_sensor class create fail , errno : %d , errstr : %s\n", err, strerror(err)); return NULL; } - return (void *)inst; + return (void*)inst; } extern "C" void destroy(void *inst) { - delete (accel_sensor_hal *)inst; + delete (accel_sensor_hal*)inst; } diff --git a/src/accel/accel_sensor_hal.h b/src/accel/accel_sensor_hal.h index d4918f3..b2e86e1 100755 --- a/src/accel/accel_sensor_hal.h +++ b/src/accel/accel_sensor_hal.h @@ -1,5 +1,5 @@ /* - * sensord + * accel_sensor_hal * * Copyright (c) 2014 Samsung Electronics Co., Ltd. * @@ -20,36 +20,12 @@ #ifndef _ACCEL_SENSOR_HAL_H_ #define _ACCEL_SENSOR_HAL_H_ -#include -#include -#include - -#define INPUT_DEV_NAME "lsm330dlc-accel" -#define INPUT_TRIG_NAME "lsm330dlc-accel-trigger" - -#define IIO_DIR "/sys/bus/iio/devices/" -#define ACCEL_FREQ "sampling_frequency" -#define ACCEL_FREQ_AVLBL "sampling_frequency_available" -#define ACCEL_SCALE_AVLBL "in_accel_scale_available" -#define ACCEL_X_SCALE "in_accel_x_scale" -#define ACCEL_Y_SCALE "in_accel_y_scale" -#define ACCEL_Z_SCALE "in_accel_z_scale" - -#define NO_OF_CHANNELS 4 #define MAX_FREQ_COUNT 16 #define MAX_SCALING_COUNT 16 -#define CHANNEL_NAME_X "in_accel_x" -#define CHANNEL_NAME_Y "in_accel_y" -#define CHANNEL_NAME_Z "in_accel_z" -#define CHANNEL_NAME_TIME "in_timestamp" -#define ENABLE_SUFFIX "_en" -#define NAME_NODE "/name" -#define BUFFER_EN "buffer/enable" -#define BUFFER_LEN "buffer/length" -#define SCAN_EL_DIR "scan_elements/" - -#define ACCEL_RINGBUF_LEN 32 +#include +#include +#include using std::string; @@ -62,56 +38,60 @@ public: sensor_type_t get_type(void); bool enable(void); bool disable(void); - bool set_interval(unsigned long ms_interval); + bool set_interval(unsigned long val); bool is_data_ready(bool wait); virtual int get_sensor_data(sensor_data_t &data); bool get_properties(sensor_properties_t &properties); - bool check_hw_node(void); - long set_command(const unsigned int cmd, long value); +// bool check_hw_node(void); private: int m_x; int m_y; int m_z; + int m_node_handle; unsigned long m_polling_interval; unsigned long long m_fired_time; - bool m_sensorhub_supported; + + int m_scale_factor_count; + int m_sample_freq_count; + int m_sample_freq[MAX_FREQ_COUNT]; + double m_scale_factor[MAX_SCALING_COUNT]; + char *m_data; + int m_scan_size; + struct channel_parameters *m_channels; + + string m_trigger_name; + string m_trigger_path; + string m_buffer_enable_node_path; + string m_buffer_length_node_path; + string m_available_freq_node_path; + string m_available_scale_node_path; + string m_accel_dir; + vector m_generic_channel_names; string m_model_id; - string m_name; string m_vendor; string m_chip_name; - string m_polling_resource; - - string m_accel_dir; - string m_accel_trig_dir; - string m_buffer_access; - string m_freq_resource; - int m_resolution; float m_raw_data_unit; - int m_scale_factor_count; - int m_sample_freq_count; - int m_sample_freq[MAX_FREQ_COUNT]; - double m_scale_factor[MAX_SCALING_COUNT]; + string m_data_node; + string m_interval_node; - int m_fp_buffer; - char *m_data; - int m_scan_size; - struct channel_parameters *m_channels; + bool m_sensorhub_controlled; cmutex m_value_mutex; bool update_value(bool wait); - bool is_sensorhub_supported(void); + bool calibration(int cmd); + bool setup_trigger(const char* trig_name, bool verify); + bool setup_buffer(int enable); bool enable_resource(bool enable); bool add_accel_channels_to_array(void); bool setup_channels(void); - bool setup_buffer(int enable); bool setup_trigger(char* trig_name, bool verify); void decode_data(void); }; -#endif /*_ACCEL_SENSOR_HAL_H_*/ +#endif /*_ACCEL_SENSOR_HAL_CLASS_H_*/ diff --git a/src/libsensord/CMakeLists.txt b/src/libsensord/CMakeLists.txt index 5de0c38..bb2b159 100755 --- a/src/libsensord/CMakeLists.txt +++ b/src/libsensord/CMakeLists.txt @@ -16,19 +16,19 @@ add_definitions(${rpkgs_CFLAGS}) #add_definitions(-Wall -O3 -omit-frame-pointer -lm) #add_definitions(-Wall -g -lma -DUSE_FILE_DEBUG) #add_definitions(-D_DEBUG) -add_definitions(-Wall -g -lma -DUSE_DLOG_LOG -std=c++0x) +add_definitions(-Wall -g -lma -fPIC -DUSE_DLOG_LOG -std=c++0x) #add_definitions(-fvisibility=hidden -lm -DUSE_DLOG_LOG) #add_definitions(-fvisibility=hidden -lm) FIND_PROGRAM(UNAME NAMES uname) EXEC_PROGRAM("${UNAME}" ARGS "-m" OUTPUT_VARIABLE "ARCH") IF("${ARCH}" STREQUAL "arm") -#ADD_DEFINITIONS("-DTARGET -DHWREV_CHECK -DUSE_MPU3050_GYRO") +# ADD_DEFINITIONS("-DTARGET -DHWREV_CHECK -DUSE_MPU3050_GYRO") ADD_DEFINITIONS("-DTARGET -DHWREV_CHECK") - MESSAGE("add -DTARGET") + MESSAGE("add -DTARGET") ELSE("${ARCH}" STREQUAL "arm") - ADD_DEFINITIONS("-DSIMULATOR") - MESSAGE("add -DSIMULATOR") + ADD_DEFINITIONS("-DSIMULATOR") + MESSAGE("add -DSIMULATOR") ENDIF("${ARCH}" STREQUAL "arm") include_directories(${CMAKE_CURRENT_SOURCE_DIR}) @@ -36,6 +36,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}) add_library(${PROJECT_NAME} SHARED client.cpp csensor_event_listener.cpp + sensor_info_manager.cpp csensor_handle_info.cpp client_common.cpp command_channel.cpp @@ -51,21 +52,24 @@ configure_file(${PROJECT_NAME}.pc.in ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME} #install(DIRECTORY include/ DESTINATION include/ FILES_MATCHING PATTERN "*.h") install(TARGETS ${PROJECT_NAME} DESTINATION lib COMPONENT RuntimeLibraries) -install(FILES sensor.h DESTINATION include/sensor/) +install(FILES sensor_internal_deprecated.h DESTINATION include/sensor/) +install(FILES sensor_internal.h DESTINATION include/sensor/) install(FILES poller.h DESTINATION include/sensor/) install(FILES creg_event_info.h DESTINATION include/sensor/) install(FILES csensor_event_listener.h DESTINATION include/sensor/) +install(FILES sensor_info_manager.h DESTINATION include/sensor/) install(FILES csensor_handle_info.h DESTINATION include/sensor/) install(FILES client_common.h DESTINATION include/sensor/) install(FILES sensor_accel.h DESTINATION include/sensor/) install(FILES sensor_geomag.h DESTINATION include/sensor/) install(FILES sensor_light.h DESTINATION include/sensor/) install(FILES sensor_proxi.h DESTINATION include/sensor/) +install(FILES sensor_motion.h DESTINATION include/sensor/) install(FILES sensor_gyro.h DESTINATION include/sensor/) install(FILES sensor_pressure.h DESTINATION include/sensor/) +install(FILES sensor_context.h DESTINATION include/sensor/) install(FILES sensor_gravity.h DESTINATION include/sensor/) install(FILES sensor_linear_accel.h DESTINATION include/sensor/) install(FILES sensor_orientation.h DESTINATION include/sensor/) -install(FILES sensor_context.h DESTINATION include/sensor/) -install(FILES sensor_motion.h DESTINATION include/sensor/) +install(FILES sensor_temperature.h DESTINATION include/sensor/) install(FILES ${PROJECT_NAME}.pc DESTINATION lib/pkgconfig) diff --git a/src/libsensord/client.cpp b/src/libsensord/client.cpp index f978c66..4e967cb 100755 --- a/src/libsensord/client.cpp +++ b/src/libsensord/client.cpp @@ -18,40 +18,45 @@ */ #include -#include +#include +#include #include #include #include #include #include +#include +#include -#ifndef EXTAPI -#define EXTAPI __attribute__((visibility("default"))) +#ifndef API +#define API __attribute__((visibility("default"))) #endif static const int OP_SUCCESS = 0; static const int OP_ERROR = -1; -static const int CMD_ERROR = -2; static csensor_event_listener &event_listener = csensor_event_listener::get_instance(); static cmutex lock; -static bool g_power_save_state = false; +static int g_power_save_state = 0; -static bool get_power_save_state(void); +static int get_power_save_state(void); static void power_save_state_cb(keynode_t *node, void *data); static void clean_up(void); static void good_bye(void); -static int change_sensor_rep(sensor_type_t sensor, sensor_rep &prev_rep, sensor_rep &cur_rep); +static bool change_sensor_rep(sensor_id_t sensor_id, sensor_rep &prev_rep, sensor_rep &cur_rep); +static void restore_session(void); +static bool register_event(int handle, unsigned int event_type, unsigned int interval, int max_batch_latency, int cb_type, void* cb, void *user_data); void init_client(void) { + event_listener.set_hup_observer(restore_session); atexit(good_bye); } static void good_bye(void) { - _D("Good bye! %s", get_client_name()); + _D("Good bye! %s\n", get_client_name()); clean_up(); } @@ -67,7 +72,7 @@ static void set_power_save_state_cb(void) if (g_power_save_state_cb_cnt == 1) { _D("Power save callback is registered"); g_power_save_state = get_power_save_state(); - _D("power_save_state = [%s]", g_power_save_state ? "on" : "off"); + _D("power_save_state = [%d]", g_power_save_state); vconf_notify_key_changed(VCONFKEY_PM_STATE, power_save_state_cb, NULL); } } @@ -88,10 +93,10 @@ static void unset_power_save_state_cb(void) static void clean_up(void) { handle_vector handles; - handle_vector::iterator it_handle; event_listener.get_all_handles(handles); - it_handle = handles.begin(); + + auto it_handle = handles.begin(); while (it_handle != handles.end()) { sf_disconnect(*it_handle); @@ -99,25 +104,28 @@ static void clean_up(void) } } -static bool get_power_save_state (void) + +static int get_power_save_state (void) { + int state = 0; int pm_state, ps_state; vconf_get_int(VCONFKEY_PM_STATE, &pm_state); - if ((pm_state == VCONFKEY_PM_STATE_LCDOFF)) - return true; + if (pm_state == VCONFKEY_PM_STATE_LCDOFF) + state |= SENSOR_OPTION_ON_IN_SCREEN_OFF; - return false; + return state; } static void power_save_state_cb(keynode_t *node, void *data) { - bool cur_power_save_state; - sensor_type_vector sensors; + int cur_power_save_state; + sensor_id_vector sensors; sensor_rep prev_rep, cur_rep; AUTOLOCK(lock); + cur_power_save_state = get_power_save_state(); if (cur_power_save_state == g_power_save_state) { @@ -126,24 +134,94 @@ static void power_save_state_cb(keynode_t *node, void *data) } g_power_save_state = cur_power_save_state; - _D("power_save_state %s noti to %s", g_power_save_state ? "on" : "off", get_client_name()); + _D("power_save_state: %d noti to %s", g_power_save_state, get_client_name()); event_listener.get_listening_sensors(sensors); - sensor_type_vector::iterator it_sensor; - it_sensor = sensors.begin(); + + auto it_sensor = sensors.begin(); while (it_sensor != sensors.end()) { event_listener.get_sensor_rep(*it_sensor, prev_rep); + event_listener.operate_sensor(*it_sensor, cur_power_save_state); + event_listener.get_sensor_rep(*it_sensor, cur_rep); + change_sensor_rep(*it_sensor, prev_rep, cur_rep); + + ++it_sensor; + } +} + + +static void restore_session(void) +{ + AUTOLOCK(lock); + + _I("Trying to restore session for %s", get_client_name()); + + command_channel *cmd_channel; + int client_id; + + event_listener.close_command_channel(); + event_listener.set_client_id(CLIENT_ID_INVALID); + + sensor_id_vector sensors; + + event_listener.get_listening_sensors(sensors); - if (cur_power_save_state) - event_listener.pause_sensor(*it_sensor); - else - event_listener.resume_sensor(*it_sensor); + bool first_connection = true; + + auto it_sensor = sensors.begin(); + + while (it_sensor != sensors.end()) { + cmd_channel = new(std::nothrow) command_channel(); + retm_if (!cmd_channel, "Failed to allocate memory"); + + if (!cmd_channel->create_channel()) { + _E("%s failed to create command channel for %s", get_client_name(), get_sensor_name(*it_sensor)); + delete cmd_channel; + goto FAILED; + } + + event_listener.add_command_channel(*it_sensor, cmd_channel); + + if (first_connection) { + first_connection = false; + if (!cmd_channel->cmd_get_id(client_id)) { + _E("Failed to get client id"); + goto FAILED; + } + + event_listener.set_client_id(client_id); + event_listener.start_event_listener(); + } + + cmd_channel->set_client_id(client_id); + + if (!cmd_channel->cmd_hello(*it_sensor)) { + _E("Sending cmd_hello(%s, %d) failed for %s", get_sensor_name(*it_sensor), client_id, get_client_name()); + goto FAILED; + } + + sensor_rep prev_rep, cur_rep; + prev_rep.active = false; + prev_rep.option = SENSOR_OPTION_DEFAULT; + prev_rep.interval = 0; event_listener.get_sensor_rep(*it_sensor, cur_rep); - change_sensor_rep(*it_sensor, prev_rep, cur_rep); + if (!change_sensor_rep(*it_sensor, prev_rep, cur_rep)) { + _E("Failed to change rep(%s) for %s", get_sensor_name(*it_sensor), get_client_name()); + goto FAILED; + } + ++it_sensor; } + + _I("Succeeded to restore session for %s", get_client_name()); + + return; + +FAILED: + event_listener.clear(); + _E("Failed to restore session for %s", get_client_name()); } static bool get_events_diff(event_type_vector &a_vec, event_type_vector &b_vec, event_type_vector &add_vec, event_type_vector &del_vec) @@ -157,27 +235,28 @@ static bool get_events_diff(event_type_vector &a_vec, event_type_vector &b_vec, return !(add_vec.empty() && del_vec.empty()); } -static int change_sensor_rep(sensor_type_t sensor, sensor_rep &prev_rep, sensor_rep &cur_rep) + +static bool change_sensor_rep(sensor_id_t sensor_id, sensor_rep &prev_rep, sensor_rep &cur_rep) { int client_id; command_channel *cmd_channel; event_type_vector add_event_types, del_event_types; - if (!event_listener.get_command_channel(sensor, &cmd_channel)) { - ERR("client %s failed to get command channel for %s", get_client_name(), get_sensor_name(sensor)); - return OP_ERROR; + if (!event_listener.get_command_channel(sensor_id, &cmd_channel)) { + ERR("client %s failed to get command channel for %s", get_client_name(), get_sensor_name(sensor_id)); + return false; } client_id = event_listener.get_client_id(); - retvm_if ((client_id < 0), OP_ERROR, "Invalid client id : %d, %s, %s", client_id, get_sensor_name(sensor), get_client_name()); + retvm_if ((client_id < 0), false, "Invalid client id : %d, %s, %s", client_id, get_sensor_name(sensor_id), get_client_name()); get_events_diff(prev_rep.event_types, cur_rep.event_types, add_event_types, del_event_types); if (cur_rep.active) { if (prev_rep.option != cur_rep.option) { if (!cmd_channel->cmd_set_option(cur_rep.option)) { - ERR("Sending cmd_set_option(%d, %s, %d) failed for %s", client_id, get_sensor_name(sensor), cur_rep.option, get_client_name()); - return CMD_ERROR; + ERR("Sending cmd_set_option(%d, %s, %d) failed for %s", client_id, get_sensor_name(sensor_id), cur_rep.option, get_client_name()); + return false; } } @@ -185,54 +264,322 @@ static int change_sensor_rep(sensor_type_t sensor, sensor_rep &prev_rep, sensor_ unsigned int min_interval; if (cur_rep.interval == 0) - min_interval = POLL_MAX_HZ_MS; + min_interval= POLL_MAX_HZ_MS; else min_interval = cur_rep.interval; if (!cmd_channel->cmd_set_interval(min_interval)) { - ERR("Sending cmd_set_interval(%d, %s, %d) failed for %s", client_id, get_sensor_name(sensor), min_interval, get_client_name()); - return CMD_ERROR; + ERR("Sending cmd_set_interval(%d, %s, %d) failed for %s", client_id, get_sensor_name(sensor_id), min_interval, get_client_name()); + return false; } } if (!add_event_types.empty()) { if (!cmd_channel->cmd_register_events(add_event_types)) { ERR("Sending cmd_register_events(%d, add_event_types) failed for %s", client_id, get_client_name()); - return CMD_ERROR; + return false; } } + } if (prev_rep.active && !del_event_types.empty()) { if (!cmd_channel->cmd_unregister_events(del_event_types)) { ERR("Sending cmd_unregister_events(%d, del_event_types) failed for %s", client_id, get_client_name()); - return CMD_ERROR; + return false; } } if (prev_rep.active != cur_rep.active) { if (cur_rep.active) { if (!cmd_channel->cmd_start()) { - ERR("Sending cmd_start(%d, %s) failed for %s", client_id, get_sensor_name(sensor), get_client_name()); - return CMD_ERROR; + ERR("Sending cmd_start(%d, %s) failed for %s", client_id, get_sensor_name(sensor_id), get_client_name()); + return false; } } else { if (!cmd_channel->cmd_unset_interval()) { - ERR("Sending cmd_unset_interval(%d, %s) failed for %s", client_id, get_sensor_name(sensor), get_client_name()); - return CMD_ERROR; + ERR("Sending cmd_unset_interval(%d, %s) failed for %s", client_id, get_sensor_name(sensor_id), get_client_name()); + return false; } if (!cmd_channel->cmd_stop()) { - ERR("Sending cmd_stop(%d, %s) failed for %s", client_id, get_sensor_name(sensor), get_client_name()); - return CMD_ERROR; + ERR("Sending cmd_stop(%d, %s) failed for %s", client_id, get_sensor_name(sensor_id), get_client_name()); + return false; } } } - return OP_SUCCESS; + return true; +} + +API int sf_connect(sensor_type_t sensor_type) +{ + sensor_t sensor; + + sensor = sensord_get_sensor(sensor_type); + + return sensord_connect(sensor); +} + +API int sf_disconnect(int handle) +{ + return sensord_disconnect(handle) ? OP_SUCCESS : OP_ERROR; +} + +API int sf_start(int handle, int option) +{ + return sensord_start(handle, option) ? OP_SUCCESS : OP_ERROR; +} + +API int sf_stop(int handle) +{ + return sensord_stop(handle) ? OP_SUCCESS : OP_ERROR; +} + +API int sf_register_event(int handle, unsigned int event_type, event_condition_t *event_condition, sensor_callback_func_t cb, void *user_data) +{ + unsigned int interval = BASE_GATHERING_INTERVAL; + + if (event_condition != NULL) { + if ((event_condition->cond_op == CONDITION_EQUAL) && (event_condition->cond_value1 > 0)) + interval = event_condition->cond_value1; + } + + return register_event(handle, event_type, interval, 0, SENSOR_LEGACY_CB, (void*) cb, user_data) ? OP_SUCCESS : OP_ERROR; +} + +API int sf_unregister_event(int handle, unsigned int event_type) +{ + return sensord_unregister_event(handle, event_type) ? OP_SUCCESS : OP_ERROR; +} + +API int sf_change_event_condition(int handle, unsigned int event_type, event_condition_t *event_condition) +{ + unsigned int interval = BASE_GATHERING_INTERVAL; + + if (event_condition != NULL) { + if ((event_condition->cond_op == CONDITION_EQUAL) && (event_condition->cond_value1 > 0)) + interval = event_condition->cond_value1; + } + + return sensord_change_event_interval(handle, event_type, interval) ? OP_SUCCESS : OP_ERROR; +} + +API int sf_change_sensor_option(int handle, int option) +{ + return sensord_set_option(handle, option) ? OP_SUCCESS : OP_ERROR; +} + + +API int sf_send_sensorhub_data(int handle, const char* data, int data_len) +{ + return sensord_send_sensorhub_data(handle, data, data_len) ? OP_SUCCESS : OP_ERROR; +} + +API int sf_get_data(int handle, unsigned int data_id, sensor_data_t* sensor_data) +{ + return sensord_get_data(handle, data_id, sensor_data) ? OP_SUCCESS : OP_ERROR; +} + +static bool get_sensor_list(void) +{ + static cmutex l; + static bool init = false; + + AUTOLOCK(l); + + if (!init) { + command_channel cmd_channel; + + if (!cmd_channel.create_channel()) { + ERR("%s failed to create command channel", get_client_name()); + return false; + } + + if (!cmd_channel.cmd_get_sensor_list()) + return false; + + init = true; + } + + return true; } -EXTAPI int sf_connect(sensor_type_t sensor) +API bool sensord_get_sensor_list(sensor_type_t type, sensor_t **list, int *sensor_count) +{ + retvm_if (!get_sensor_list(), false, "Fail to get sensor list from server"); + + vector sensor_infos = sensor_info_manager::get_instance().get_infos(type); + *list = (sensor_t *) malloc(sizeof(sensor_info *) * sensor_infos.size()); + retvm_if(!*list, false, "Failed to allocate memory"); + + for (int i = 0; i < sensor_infos.size(); ++i) + *(*list + i) = sensor_info_to_sensor(sensor_infos[i]); + + *sensor_count = sensor_infos.size(); + + return true; +} + +API sensor_t sensord_get_sensor(sensor_type_t type) +{ + retvm_if (!get_sensor_list(), false, "Fail to get sensor list from server"); + + const sensor_info *info; + + info = sensor_info_manager::get_instance().get_info(type); + + return sensor_info_to_sensor(info); +} + +API bool sensord_get_type(sensor_t sensor, sensor_type_t *type) +{ + sensor_info* info = sensor_to_sensor_info(sensor); + + retvm_if (!sensor_info_manager::get_instance().is_valid(info) || !type, + NULL, "Invalid param: sensor (%p), type(%p)", sensor, type); + + *type = info->get_type(); + + return true; +} + +API const char* sensord_get_name(sensor_t sensor) +{ + sensor_info* info = sensor_to_sensor_info(sensor); + + retvm_if (!sensor_info_manager::get_instance().is_valid(info), + NULL, "Invalid param: sensor (%p)", sensor); + + return info->get_name(); +} + +API const char* sensord_get_vendor(sensor_t sensor) +{ + sensor_info* info = sensor_to_sensor_info(sensor); + + retvm_if (!sensor_info_manager::get_instance().is_valid(info), + NULL, "Invalid param: sensor (%p)", sensor); + + return info->get_vendor(); +} + +API bool sensord_get_privilege(sensor_t sensor, sensor_privilege_t *privilege) +{ + sensor_info* info = sensor_to_sensor_info(sensor); + + retvm_if (!sensor_info_manager::get_instance().is_valid(info) || !privilege, + false, "Invalid param: sensor (%p), privilege(%p)", sensor, privilege); + + *privilege = info->get_privilege(); + + return true; +} + +API bool sensord_get_min_range(sensor_t sensor, float *min_range) +{ + sensor_info* info = sensor_to_sensor_info(sensor); + + retvm_if (!sensor_info_manager::get_instance().is_valid(info) || !min_range, + false, "Invalid param: sensor (%p), min_range(%p)", sensor, min_range); + + *min_range = info->get_min_range(); + + return true; +} + +API bool sensord_get_max_range(sensor_t sensor, float *max_range) +{ + sensor_info* info = sensor_to_sensor_info(sensor); + + retvm_if (!sensor_info_manager::get_instance().is_valid(info) || !max_range, + false, "Invalid param: sensor (%p), max_range(%p)", sensor, max_range); + + *max_range = info->get_max_range(); + + return true; +} + +API bool sensord_get_resolution(sensor_t sensor, float *resolution) +{ + sensor_info* info = sensor_to_sensor_info(sensor); + + retvm_if (!sensor_info_manager::get_instance().is_valid(info) || !resolution, + false, "Invalid param: sensor (%p), resolution(%p)", sensor, resolution); + + *resolution = info->get_resolution(); + + return true; +} + +API bool sensord_get_min_interval(sensor_t sensor, int *min_interval) +{ + sensor_info* info = sensor_to_sensor_info(sensor); + + retvm_if (!sensor_info_manager::get_instance().is_valid(info) || !min_interval, + false, "Invalid param: sensor (%p), min_interval(%p)", sensor, min_interval); + + *min_interval = info->get_min_interval(); + + return true; +} + +API bool sensord_get_fifo_count(sensor_t sensor, int *fifo_count) +{ + sensor_info* info = sensor_to_sensor_info(sensor); + + retvm_if (!sensor_info_manager::get_instance().is_valid(info) || !fifo_count, + false, "Invalid param: sensor (%p), fifo_count(%p)", sensor, fifo_count); + + *fifo_count = info->get_fifo_count(); + + return true; +} + +API bool sensord_get_max_batch_count(sensor_t sensor, int *max_batch_count) +{ + sensor_info* info = sensor_to_sensor_info(sensor); + + retvm_if (!sensor_info_manager::get_instance().is_valid(info) || !max_batch_count, + false, "Invalid param: sensor (%p), max_batch_count(%p)", sensor, max_batch_count); + + *max_batch_count = info->get_max_batch_count(); + + return true; +} + +API bool sensord_get_supported_event_types(sensor_t sensor, unsigned int **event_types, int *count) +{ + sensor_info* info = sensor_to_sensor_info(sensor); + + retvm_if (!sensor_info_manager::get_instance().is_valid(info) || !event_types || !count, + false, "Invalid param: sensor (%p), event_types(%p), count(%)", sensor, event_types, count); + + vector event_vec; + + info->get_supported_events(event_vec); + *event_types = (unsigned int *) malloc(sizeof(unsigned int) * event_vec.size()); + retvm_if(!*event_types, false, "Failed to allocate memory"); + + copy(event_vec.begin(), event_vec.end(), *event_types); + *count = event_vec.size(); + + return true; +} + +API bool sensord_is_supported_event_type(sensor_t sensor, unsigned int event_type, bool *supported) +{ + sensor_info* info = sensor_to_sensor_info(sensor); + + retvm_if (!sensor_info_manager::get_instance().is_valid(info) || !event_type || !supported, + false, "Invalid param: sensor (%p), event_type(%p), supported(%)", sensor, event_type, supported); + + *supported = info->is_supported_event(event_type); + + return true; +} + +API int sensord_connect(sensor_t sensor) { command_channel *cmd_channel = NULL; int handle; @@ -240,43 +587,50 @@ EXTAPI int sf_connect(sensor_type_t sensor) bool sensor_registered; bool first_connection = false; + sensor_info* info = sensor_to_sensor_info(sensor); + + retvm_if (!sensor_info_manager::get_instance().is_valid(info), + OP_ERROR, "Invalid param: sensor (%p)", sensor); + + sensor_id_t sensor_id = info->get_id(); + AUTOLOCK(lock); - sensor_registered = event_listener.is_sensor_registered(sensor); - handle = event_listener.create_handle(sensor); + sensor_registered = event_listener.is_sensor_registered(sensor_id); + handle = event_listener.create_handle(sensor_id); if (handle == MAX_HANDLE) { - ERR("Maximum number of handles reached, sensor: %s in client %s", get_sensor_name(sensor), get_client_name()); + ERR("Maximum number of handles reached, sensor: %s in client %s", get_sensor_name(sensor_id), get_client_name()); return OP_ERROR; } if (!sensor_registered) { - cmd_channel = new command_channel(); + cmd_channel = new(std::nothrow) command_channel(); + retvm_if (!cmd_channel, OP_ERROR, "Failed to allocate memory"); if (!cmd_channel->create_channel()) { - ERR("%s failed to create command channel for %s", get_client_name(), get_sensor_name(sensor)); + ERR("%s failed to create command channel for %s", get_client_name(), get_sensor_name(sensor_id)); event_listener.delete_handle(handle); delete cmd_channel; return OP_ERROR; } - event_listener.set_command_channel(sensor, cmd_channel); + event_listener.add_command_channel(sensor_id, cmd_channel); } - if (!event_listener.get_command_channel(sensor, &cmd_channel)) { - ERR("%s failed to get command channel for %s", get_client_name(), get_sensor_name(sensor)); + if (!event_listener.get_command_channel(sensor_id, &cmd_channel)) { + ERR("%s failed to get command channel for %s", get_client_name(), get_sensor_name(sensor_id)); event_listener.delete_handle(handle); return OP_ERROR; } if (!event_listener.has_client_id()) { first_connection = true; - - if (!cmd_channel->cmd_get_id(client_id)) { - ERR("Sending cmd_get_id() failed for %s", get_sensor_name(sensor)); - event_listener.close_command_channel(sensor); + if(!cmd_channel->cmd_get_id(client_id)) { + ERR("Sending cmd_get_id() failed for %s", get_sensor_name(sensor_id)); + event_listener.close_command_channel(sensor_id); event_listener.delete_handle(handle); - return CMD_ERROR; + return OP_ERROR; } event_listener.set_client_id(client_id); @@ -288,19 +642,20 @@ EXTAPI int sf_connect(sensor_type_t sensor) client_id = event_listener.get_client_id(); cmd_channel->set_client_id(client_id); - INFO("%s[%d] connects with %s[%d]", get_client_name(), client_id, get_sensor_name(sensor), handle); + INFO("%s[%d] connects with %s[%d]", get_client_name(), client_id, get_sensor_name(sensor_id), handle); + event_listener.set_sensor_params(handle, SENSOR_STATE_STOPPED, SENSOR_OPTION_DEFAULT); if (!sensor_registered) { - if (!cmd_channel->cmd_hello(sensor)) { - ERR("Sending cmd_hello(%s, %d) failed for %s", get_sensor_name(sensor), client_id, get_client_name()); - event_listener.close_command_channel(sensor); + if(!cmd_channel->cmd_hello(sensor_id)) { + ERR("Sending cmd_hello(%s, %d) failed for %s", get_sensor_name(sensor_id), client_id, get_client_name()); + event_listener.close_command_channel(sensor_id); event_listener.delete_handle(handle); - - if (first_connection) + if (first_connection) { + event_listener.set_client_id(CLIENT_ID_INVALID); event_listener.stop_event_listener(); - - return CMD_ERROR; + } + return OP_ERROR; } } @@ -308,46 +663,49 @@ EXTAPI int sf_connect(sensor_type_t sensor) return handle; } -EXTAPI int sf_disconnect(int handle) +API bool sensord_disconnect(int handle) { command_channel *cmd_channel; - sensor_type_t sensor; + sensor_id_t sensor_id; int client_id; int sensor_state; AUTOLOCK(lock); - if (!event_listener.get_sensor_state(handle, sensor_state) || - !event_listener.get_sensor_type(handle, sensor)) { + if (!event_listener.get_sensor_state(handle, sensor_state)|| + !event_listener.get_sensor_id(handle, sensor_id)) { ERR("client %s failed to get handle information", get_client_name()); - return OP_ERROR; + return false; } - if (!event_listener.get_command_channel(sensor, &cmd_channel)) { - ERR("client %s failed to get command channel for %s", get_client_name(), get_sensor_name(sensor)); - return OP_ERROR; + if (!event_listener.get_command_channel(sensor_id, &cmd_channel)) { + ERR("client %s failed to get command channel for %s", get_client_name(), get_sensor_name(sensor_id)); + return false; } client_id = event_listener.get_client_id(); - retvm_if ((client_id < 0), OP_ERROR, "Invalid client id : %d, handle: %d, %s, %s", client_id, handle, get_sensor_name(sensor), get_client_name()); - INFO("%s disconnects with %s[%d]", get_client_name(), get_sensor_name(sensor), handle); + retvm_if ((client_id < 0), false, "Invalid client id : %d, handle: %d, %s, %s", client_id, handle, get_sensor_name(sensor_id), get_client_name()); + + INFO("%s disconnects with %s[%d]", get_client_name(), get_sensor_name(sensor_id), handle); if (sensor_state != SENSOR_STATE_STOPPED) { - WARN("Before disconnecting, sensor %s[%d] is forced to stop in client %s", - get_sensor_name(sensor), handle, get_client_name()); - sf_stop(handle); + WARN("%s[%d] for %s is not stopped before disconnecting.", + get_sensor_name(sensor_id), handle, get_client_name()); + sensord_stop(handle); } if (!event_listener.delete_handle(handle)) - return OP_ERROR; + return false; - if (!event_listener.is_sensor_registered(sensor)) { - if (!cmd_channel->cmd_byebye()) { - ERR("Sending cmd_byebye(%d, %s) failed for %s", client_id, get_sensor_name(sensor), get_client_name()); - return CMD_ERROR; - } + if (!event_listener.is_active()) + event_listener.set_client_id(CLIENT_ID_INVALID); - event_listener.close_command_channel(sensor); + if (!event_listener.is_sensor_registered(sensor_id)) { + if(!cmd_channel->cmd_byebye()) { + ERR("Sending cmd_byebye(%d, %s) failed for %s", client_id, get_sensor_name(sensor_id), get_client_name()); + return false; + } + event_listener.close_command_channel(sensor_id); } if (!event_listener.is_active()) { @@ -357,451 +715,373 @@ EXTAPI int sf_disconnect(int handle) unset_power_save_state_cb(); - return OP_SUCCESS; + return true; } -EXTAPI int sf_start(int handle, int option) + +static bool register_event(int handle, unsigned int event_type, unsigned int interval, int max_batch_latency, int cb_type, void* cb, void *user_data) { - sensor_type_t sensor; + sensor_id_t sensor_id; sensor_rep prev_rep, cur_rep; + bool ret; + + retvm_if (!cb, false, "callback is NULL"); + AUTOLOCK(lock); - if (!event_listener.get_sensor_type(handle, sensor)) { + if (!event_listener.get_sensor_id(handle, sensor_id)) { ERR("client %s failed to get handle information", get_client_name()); - return OP_ERROR; + return false; } - retvm_if ((option < 0) || (option >= SENSOR_OPTION_END), OP_ERROR, "Invalid option value : %d, handle: %d, %s, %s", - option, handle, get_sensor_name(sensor), get_client_name()); - INFO("%s starts %s[%d], with option: %d%s", get_client_name(), get_sensor_name(sensor), - handle, option, g_power_save_state ? " in power save state" : ""); + if (interval == 0) + interval = 1; - if (g_power_save_state && (option != SENSOR_OPTION_ALWAYS_ON)) { - event_listener.set_sensor_params(handle, SENSOR_STATE_PAUSED, option); - return OP_SUCCESS; - } + INFO("%s registers event %s[0x%x] for sensor %s[%d] with interval: %d, cb: 0x%x, user_data: 0x%x", get_client_name(), get_event_name(event_type), + event_type, get_sensor_name(sensor_id), handle, interval, cb, user_data); - event_listener.get_sensor_rep(sensor, prev_rep); - event_listener.set_sensor_params(handle, SENSOR_STATE_STARTED, option); - event_listener.get_sensor_rep(sensor, cur_rep); + event_listener.get_sensor_rep(sensor_id, prev_rep); + event_listener.register_event(handle, event_type, interval, cb_type, cb, user_data); + event_listener.get_sensor_rep(sensor_id, cur_rep); + ret = change_sensor_rep(sensor_id, prev_rep, cur_rep); - return change_sensor_rep(sensor, prev_rep, cur_rep); + if (!ret) + event_listener.unregister_event(handle, event_type); + + return ret; } -EXTAPI int sf_stop(int handle) +API bool sensord_register_event(int handle, unsigned int event_type, unsigned int interval, unsigned int max_batch_latency, sensor_cb_t cb, void *user_data) { - sensor_type_t sensor; - int sensor_state; - sensor_rep prev_rep, cur_rep; - - AUTOLOCK(lock); - - if (!event_listener.get_sensor_state(handle, sensor_state) || - !event_listener.get_sensor_type(handle, sensor)) { - ERR("client %s failed to get handle information", get_client_name()); - return OP_ERROR; - } - - retvm_if ((sensor_state == SENSOR_STATE_STOPPED), OP_SUCCESS, "%s already stopped with %s[%d]", - get_client_name(), get_sensor_name(sensor), handle); - - INFO("%s stops sensor %s[%d]", get_client_name(), get_sensor_name(sensor), handle); - - event_listener.get_sensor_rep(sensor, prev_rep); - event_listener.set_sensor_state(handle, SENSOR_STATE_STOPPED); - event_listener.get_sensor_rep(sensor, cur_rep); - return change_sensor_rep(sensor, prev_rep, cur_rep); + return register_event(handle, event_type, interval, max_batch_latency, SENSOR_EVENT_CB, (void *)cb, user_data); } -EXTAPI int sf_register_event(int handle, unsigned int event_type, event_condition_t *event_condition, sensor_callback_func_t cb, void *cb_data ) +API bool sensord_register_hub_event(int handle, unsigned int event_type, unsigned int interval, unsigned int max_batch_latency, sensorhub_cb_t cb, void *user_data) { - sensor_type_t sensor; - unsigned int interval = BASE_GATHERING_INTERVAL; - sensor_rep prev_rep, cur_rep; - retvm_if ((cb == NULL), OP_ERROR, "callback is NULL"); - - AUTOLOCK(lock); - - if (!event_listener.get_sensor_type(handle, sensor)) { - ERR("client %s failed to get handle information", get_client_name()); - return OP_ERROR; - } - - if (event_condition != NULL) { - if ((event_condition->cond_op == CONDITION_EQUAL) && (event_condition->cond_value1 > 0)) - interval = event_condition->cond_value1; - } - - INFO("%s registers event %s[0x%x] for sensor %s[%d] with interval: %d, cb: 0x%x, cb_data: 0x%x", get_client_name(), get_event_name(event_type), - event_type, get_sensor_name(sensor), handle, interval, cb, cb_data); - - event_listener.get_sensor_rep(sensor, prev_rep); - event_listener.register_event(handle, event_type, interval, cb, cb_data); - event_listener.get_sensor_rep(sensor, cur_rep); - return change_sensor_rep(sensor, prev_rep, cur_rep); + return register_event(handle, event_type, interval, max_batch_latency, SENSORHUB_EVENT_CB, (void *)cb, user_data); } -EXTAPI int sf_unregister_event(int handle, unsigned int event_type) + +API bool sensord_unregister_event(int handle, unsigned int event_type) { - sensor_type_t sensor; + sensor_id_t sensor_id; sensor_rep prev_rep, cur_rep; + bool ret; + unsigned int prev_interval; + int prev_cb_type; + void *prev_cb; + void *prev_user_data; AUTOLOCK(lock); - if (!event_listener.get_sensor_type(handle, sensor)) { + if (!event_listener.get_sensor_id(handle, sensor_id)) { ERR("client %s failed to get handle information", get_client_name()); - return OP_ERROR; + return false; } INFO("%s unregisters event %s[0x%x] for sensor %s[%d]", get_client_name(), get_event_name(event_type), - event_type, get_sensor_name(sensor), handle); + event_type, get_sensor_name(sensor_id), handle); - event_listener.get_sensor_rep(sensor, prev_rep); + event_listener.get_sensor_rep(sensor_id, prev_rep); + event_listener.get_event_info(handle, event_type, prev_interval, prev_cb_type, prev_cb, prev_user_data); if (!event_listener.unregister_event(handle, event_type)) { ERR("%s try to unregister non registered event %s[0x%x] for sensor %s[%d]", - get_client_name(), get_event_name(event_type), event_type, get_sensor_name(sensor), handle); - return OP_ERROR; + get_client_name(),get_event_name(event_type), event_type, get_sensor_name(sensor_id), handle); + return false; } - event_listener.get_sensor_rep(sensor, cur_rep); - return change_sensor_rep(sensor, prev_rep, cur_rep); + event_listener.get_sensor_rep(sensor_id, cur_rep); + ret = change_sensor_rep(sensor_id, prev_rep, cur_rep); + + if (!ret) + event_listener.register_event(handle, event_type, prev_interval, prev_cb_type, prev_cb, prev_user_data); + + return ret; + } -EXTAPI int sf_change_event_condition(int handle, unsigned int event_type, event_condition_t *event_condition) + +API bool sensord_register_accuracy_cb(int handle, sensor_accuracy_changed_cb_t cb, void *user_data) { - sensor_type_t sensor; - sensor_rep prev_rep, cur_rep; - unsigned int interval = BASE_GATHERING_INTERVAL; + sensor_id_t sensor_id; + + retvm_if (!cb, false, "callback is NULL"); + AUTOLOCK(lock); - if (!event_listener.get_sensor_type(handle, sensor)) { + if (!event_listener.get_sensor_id(handle, sensor_id)) { ERR("client %s failed to get handle information", get_client_name()); - return OP_ERROR; + return false; } - if (event_condition != NULL) { - if ((event_condition->cond_op == CONDITION_EQUAL) && (event_condition->cond_value1 > 0)) - interval = event_condition->cond_value1; - } - INFO("%s changes interval of event %s[0x%x] for %s[%d] to interval %d", get_client_name(), get_event_name(event_type), - event_type, get_sensor_name(sensor), handle, interval); - event_listener.get_sensor_rep(sensor, prev_rep); - event_listener.set_event_interval(handle, event_type, interval); - event_listener.get_sensor_rep(sensor, cur_rep); - return change_sensor_rep(sensor, prev_rep, cur_rep); + INFO("%s registers accuracy_changed_cb for sensor %s[%d] with cb: 0x%x, user_data: 0x%x", + get_client_name(), get_sensor_name(sensor_id), handle, cb, user_data); + + event_listener.register_accuracy_cb(handle, cb , user_data); + + return true; + } -int sf_change_sensor_option(int handle, int option) +API bool sensord_unregister_accuracy_cb(int handle) { - sensor_type_t sensor; - sensor_rep prev_rep, cur_rep; - int sensor_state; + sensor_id_t sensor_id; + AUTOLOCK(lock); - if (!event_listener.get_sensor_state(handle, sensor_state) || - !event_listener.get_sensor_type(handle, sensor)) { + if (!event_listener.get_sensor_id(handle, sensor_id)) { ERR("client %s failed to get handle information", get_client_name()); - return OP_ERROR; + return false; } - retvm_if ((option < 0) || (option >= SENSOR_OPTION_END), OP_ERROR, "Invalid option value : %d, handle: %d, %s, %s", - option, handle, get_sensor_name(sensor), get_client_name()); - event_listener.get_sensor_rep(sensor, prev_rep); - if (g_power_save_state) { - if ((option == SENSOR_OPTION_ALWAYS_ON) && (sensor_state == SENSOR_STATE_PAUSED)) - event_listener.set_sensor_state(handle, SENSOR_STATE_STARTED); - else if ((option == SENSOR_OPTION_DEFAULT) && (sensor_state == SENSOR_STATE_STARTED)) - event_listener.set_sensor_state(handle, SENSOR_STATE_PAUSED); - } + INFO("%s unregisters accuracy_changed_cb for sensor %s[%d]", + get_client_name(), get_sensor_name(sensor_id), handle); - event_listener.set_sensor_option(handle, option); - event_listener.get_sensor_rep(sensor, cur_rep); - return change_sensor_rep(sensor, prev_rep, cur_rep); + event_listener.unregister_accuracy_cb(handle); + + return true; } -EXTAPI int sf_send_sensorhub_data(int handle, const char *data, int data_len) + +API bool sensord_start(int handle, int option) { - sensor_type_t sensor; - command_channel *cmd_channel; - int client_id; + sensor_id_t sensor_id; + sensor_rep prev_rep, cur_rep; + bool ret; + int prev_state, prev_option; + AUTOLOCK(lock); - if (!event_listener.get_sensor_type(handle, sensor)) { + if (!event_listener.get_sensor_id(handle, sensor_id)) { ERR("client %s failed to get handle information", get_client_name()); - return OP_ERROR; + return false; } - retvm_if (sensor != CONTEXT_SENSOR, OP_ERROR, "%s use this API wrongly, only for CONTEXT_SENSOR not for %s", - get_client_name(), get_sensor_name(sensor)); + retvm_if ((option < 0) || (option >= SENSOR_OPTION_END), false, "Invalid option value : %d, handle: %d, %s, %s", + option, handle, get_sensor_name(sensor_id), get_client_name()); - if (!event_listener.get_command_channel(sensor, &cmd_channel)) { - ERR("client %s failed to get command channel for %s", get_client_name(), get_sensor_name(sensor)); - return OP_ERROR; + INFO("%s starts %s[%d], with option: %d, power save state: %d", get_client_name(), get_sensor_name(sensor_id), + handle, option, g_power_save_state); + + if (g_power_save_state && !(g_power_save_state & option)) { + event_listener.set_sensor_params(handle, SENSOR_STATE_PAUSED, option); + return true; } - retvm_if((data_len < 0) || (data == NULL), OP_ERROR, "Invalid data_len: %d, data: 0x%x, handle: %d, %s, %s", - data_len, data, handle, get_sensor_name(sensor), get_client_name()); - client_id = event_listener.get_client_id(); - retvm_if ((client_id < 0), OP_ERROR, "Invalid client id : %d, handle: %d, %s, %s", client_id, handle, get_sensor_name(sensor), get_client_name()); - retvm_if (!event_listener.is_sensor_active(sensor), OP_ERROR, "%s with client_id:%d is not active state for %s with handle: %d", - get_sensor_name(sensor), client_id, get_client_name(), handle); + event_listener.get_sensor_rep(sensor_id, prev_rep); + event_listener.get_sensor_params(handle, prev_state, prev_option); + event_listener.set_sensor_params(handle, SENSOR_STATE_STARTED, option); + event_listener.get_sensor_rep(sensor_id, cur_rep); - if (!cmd_channel->cmd_send_sensorhub_data(data_len, data)) { - ERR("Sending cmd_send_sensorhub_data(%d, %d, 0x%x) failed for %s", - client_id, data_len, data, get_client_name); - return CMD_ERROR; - } + ret = change_sensor_rep(sensor_id, prev_rep, cur_rep); + + if (!ret) + event_listener.set_sensor_params(handle, prev_state, prev_option); - return OP_SUCCESS; + return ret; } -EXTAPI int sf_is_sensor_event_available (sensor_type_t sensor, unsigned int event) +API bool sensord_stop(int handle) { - command_channel *cmd_channel; - int handle; - int client_id; - handle = sf_connect(sensor); - - if (handle < 0) { - return OP_ERROR; - } - - if (event != 0) { - AUTOLOCK(lock); - INFO("%s checks if event %s[0x%x] is registered", get_client_name(), get_event_name(event), event); + sensor_id_t sensor_id; + int sensor_state; + bool ret; + int prev_state, prev_option; - if (!event_listener.get_command_channel(sensor, &cmd_channel)) { - ERR("client %s failed to get command channel for %s", get_client_name(), get_sensor_name(sensor)); - return OP_ERROR; - } + sensor_rep prev_rep, cur_rep; - client_id = event_listener.get_client_id(); - retvm_if ((client_id < 0), OP_ERROR, "Invalid client id : %d, handle: %d, %s, %s", client_id, handle, get_sensor_name(sensor), get_client_name()); + AUTOLOCK(lock); - if (!cmd_channel->cmd_check_event(event)) { - INFO("Sensor event %s is not supported in sensor %s", get_event_name(event), get_sensor_name(sensor)); - return CMD_ERROR; - } + if (!event_listener.get_sensor_state(handle, sensor_state)|| + !event_listener.get_sensor_id(handle, sensor_id)) { + ERR("client %s failed to get handle information", get_client_name()); + return false; } - sf_disconnect(handle); - return OP_SUCCESS; -} + retvm_if ((sensor_state == SENSOR_STATE_STOPPED), true, "%s already stopped with %s[%d]", + get_client_name(), get_sensor_name(sensor_id), handle); -static int server_get_properties(int handle, unsigned int type, void *properties) -{ - command_channel *cmd_channel; - sensor_type_t sensor; - int client_id; - AUTOLOCK(lock); - if (!event_listener.get_sensor_type(handle, sensor)) { - ERR("client %s failed to get handle information", get_client_name()); - return OP_ERROR; - } + INFO("%s stops sensor %s[%d]", get_client_name(), get_sensor_name(sensor_id), handle); - if (!event_listener.get_command_channel(sensor, &cmd_channel)) { - ERR("client %s failed to get command channel for %s", get_client_name(), get_sensor_name(sensor)); - return OP_ERROR; - } + event_listener.get_sensor_rep(sensor_id, prev_rep); + event_listener.get_sensor_params(handle, prev_state, prev_option); + event_listener.set_sensor_state(handle, SENSOR_STATE_STOPPED); + event_listener.get_sensor_rep(sensor_id, cur_rep); - client_id = event_listener.get_client_id(); - retvm_if ((client_id < 0), OP_ERROR, "Invalid client id : %d, handle: %d, %s, %s", client_id, handle, get_sensor_name(sensor), get_client_name()); - INFO("%s gets property with %s[%d], property_id: %d", get_client_name(), get_sensor_name(sensor), handle, type); + ret = change_sensor_rep(sensor_id, prev_rep, cur_rep); - if (!cmd_channel->cmd_get_properties(type, properties)) { - ERR("Sending cmd_get_properties(%d, %s, %d, 0x%x) failed for %s", client_id, get_sensor_name(sensor), type, properties, get_client_name()); - return CMD_ERROR; - } + if (!ret) + event_listener.set_sensor_params(handle, prev_state, prev_option); - return OP_SUCCESS; + return ret; } -static int server_set_property(int handle, unsigned int property_id, long value) + +API bool sensord_change_event_interval(int handle, unsigned int event_type, unsigned int interval) { - command_channel *cmd_channel; - sensor_type_t sensor; - int client_id; + sensor_id_t sensor_id; + sensor_rep prev_rep, cur_rep; + bool ret; + unsigned int prev_interval; + int prev_cb_type; + void *prev_cb; + void *prev_user_data; + AUTOLOCK(lock); - if (!event_listener.get_sensor_type(handle, sensor)) { + if (!event_listener.get_sensor_id(handle, sensor_id)) { ERR("client %s failed to get handle information", get_client_name()); - return OP_ERROR; + return false; } - if (!event_listener.get_command_channel(sensor, &cmd_channel)) { - ERR("client %s failed to get command channel for %s", get_client_name(), get_sensor_name(sensor)); - return OP_ERROR; - } + INFO("%s changes interval of event %s[0x%x] for %s[%d] to interval %d", get_client_name(), get_event_name(event_type), + event_type, get_sensor_name(sensor_id), handle, interval); - client_id = event_listener.get_client_id(); - retvm_if ((client_id < 0), OP_ERROR, "Invalid client id : %d, handle: %d, %s, %s", client_id, handle, get_sensor_name(sensor), get_client_name()); - INFO("%s gets property with %s[%d], property_id: %d", get_client_name(), get_sensor_name(sensor), handle, property_id); + event_listener.get_sensor_rep(sensor_id, prev_rep); - if (!cmd_channel->cmd_set_command(property_id, value)) { - ERR("Cmd_set_value(%d, %s, %d, %d) failed for %s", client_id, get_sensor_name(sensor), property_id, value, get_client_name()); - return CMD_ERROR; - } + event_listener.get_event_info(handle, event_type, prev_interval, prev_cb_type, prev_cb, prev_user_data); - return OP_SUCCESS; -} + if (!event_listener.set_event_interval(handle, event_type, interval)) + return false; -EXTAPI int sf_get_data_properties(unsigned int data_id, sensor_data_properties_t *return_data_properties) -{ - int handle; - int state = -1; - retvm_if ((!return_data_properties ), -1, "Invalid return properties pointer : %p from %s", - return_data_properties, get_client_name()); - handle = sf_connect((sensor_type_t)(data_id >> 16)); + event_listener.get_sensor_rep(sensor_id, cur_rep); - if (handle < 0) { - ERR("Sensor connect fail !! for : %x", (data_id >> 16)); - return OP_ERROR; - } else { - state = server_get_properties(handle, data_id, return_data_properties ); + ret = change_sensor_rep(sensor_id, prev_rep, cur_rep); - if (state < 0) - ERR("server_get_properties fail, state : %d", state); + if (!ret) + event_listener.set_event_interval(handle, event_type, prev_interval); - sf_disconnect(handle); - } + return ret; - return state; } -static unsigned int get_sensor_property_level(sensor_type_t sensor) +API bool sensord_change_event_max_batch_latency(int handle, unsigned int max_batch_latency) { - return (sensor << SENSOR_TYPE_SHIFT) | 0x0001; + return false; } -EXTAPI int sf_get_properties(sensor_type_t sensor, sensor_properties_t *return_properties) -{ - int handle; - int state = -1; - retvm_if ((!return_properties ), -1, "Invalid return properties pointer : %p from %s", - return_properties, get_client_name()); - handle = sf_connect(sensor); - if (handle < 0) { - ERR("Sensor connect fail !! for : %x", sensor); - return OP_ERROR; - } else { - state = server_get_properties(handle, get_sensor_property_level(sensor), return_properties ); +API bool sensord_set_option(int handle, int option) +{ + sensor_id_t sensor_id; + sensor_rep prev_rep, cur_rep; + int sensor_state; + bool ret; + int prev_state, prev_option; - if (state < 0) - ERR("server_get_properties fail, state : %d", state); + AUTOLOCK(lock); - sf_disconnect(handle); + if (!event_listener.get_sensor_state(handle, sensor_state)|| + !event_listener.get_sensor_id(handle, sensor_id)) { + ERR("client %s failed to get handle information", get_client_name()); + return false; } - return state; -} - -EXTAPI int sf_set_property(sensor_type_t sensor, unsigned int property_id, long value) -{ - int handle; - int state = -1; - handle = sf_connect(sensor); + retvm_if ((option < 0) || (option >= SENSOR_OPTION_END), false, "Invalid option value : %d, handle: %d, %s, %s", + option, handle, get_sensor_name(sensor_id), get_client_name()); - if (handle < 0) { - ERR("Sensor connect fail !! for : %x", sensor); - return OP_ERROR; - } else { - state = server_set_property(handle, property_id, value ); - if (state < 0) - ERR("server_set_property fail, state : %d", state); + event_listener.get_sensor_rep(sensor_id, prev_rep); + event_listener.get_sensor_params(handle, prev_state, prev_option); - sf_disconnect(handle); + if (g_power_save_state) { + if ((option & g_power_save_state) && (sensor_state == SENSOR_STATE_PAUSED)) + event_listener.set_sensor_state(handle, SENSOR_STATE_STARTED); + else if (!(option & g_power_save_state) && (sensor_state == SENSOR_STATE_STARTED)) + event_listener.set_sensor_state(handle, SENSOR_STATE_PAUSED); } + event_listener.set_sensor_option(handle, option); + + event_listener.get_sensor_rep(sensor_id, cur_rep); + ret = change_sensor_rep(sensor_id, prev_rep, cur_rep); + + if (!ret) + event_listener.set_sensor_option(handle, prev_option); + + return ret; - return state; } -EXTAPI int sf_get_data(int handle, unsigned int data_id, sensor_data_t *sensor_data) +bool sensord_send_sensorhub_data(int handle, const char *data, int data_len) { - sensor_type_t sensor; + sensor_id_t sensor_id; command_channel *cmd_channel; - int sensor_state; int client_id; - retvm_if ((!sensor_data), OP_ERROR, "sf_get_data fail, invalid get_values pointer %p", sensor_data); + AUTOLOCK(lock); - if (!event_listener.get_sensor_state(handle, sensor_state) || - !event_listener.get_sensor_type(handle, sensor)) { + if (!event_listener.get_sensor_id(handle, sensor_id)) { ERR("client %s failed to get handle information", get_client_name()); - return OP_ERROR; + return false; } - if (!event_listener.get_command_channel(sensor, &cmd_channel)) { - ERR("client %s failed to get command channel for %s", get_client_name(), get_sensor_name(sensor)); - return OP_ERROR; + retvm_if (sensor_id != CONTEXT_SENSOR, false, "%s use this API wrongly, only for CONTEXT_SENSOR not for %s", + get_client_name(), get_sensor_name(sensor_id)); + + if (!event_listener.get_command_channel(sensor_id, &cmd_channel)) { + ERR("client %s failed to get command channel for %s", get_client_name(), get_sensor_name(sensor_id)); + return false; } + retvm_if((data_len < 0) || (data == NULL), false, "Invalid data_len: %d, data: 0x%x, handle: %d, %s, %s", + data_len, data, handle, get_sensor_name(sensor_id), get_client_name()); + client_id = event_listener.get_client_id(); - retvm_if ((client_id < 0), OP_ERROR, "Invalid client id : %d, handle: %d, %s, %s", client_id, handle, get_sensor_name(sensor), get_client_name()); + retvm_if ((client_id < 0), false, "Invalid client id : %d, handle: %d, %s, %s", client_id, handle, get_sensor_name(sensor_id), get_client_name()); - if (sensor_state != SENSOR_STATE_STARTED) { - ERR("Sensor %s is not started for client %s with handle: %d, sensor_state: %d", get_sensor_name(sensor), get_client_name(), handle, sensor_state); - return OP_ERROR; - } + retvm_if (!event_listener.is_sensor_active(sensor_id), false, "%s with client_id:%d is not active state for %s with handle: %d", + get_sensor_name(sensor_id), client_id, get_client_name(), handle); - if (!cmd_channel->cmd_get_data(data_id, sensor_data)) { - ERR("Cmd_get_struct(%d, %d, 0x%x) failed for %s", client_id, data_id, sensor_data, get_client_name()); - return CMD_ERROR; + if (!cmd_channel->cmd_send_sensorhub_data(data, data_len)) { + ERR("Sending cmd_send_sensorhub_data(%d, %d, 0x%x) failed for %s", + client_id, data_len, data, get_client_name); + return false; } - return OP_SUCCESS; + return true; + } -EXTAPI int sf_check_rotation(unsigned long *curr_state) +API bool sensord_get_data(int handle, unsigned int data_id, sensor_data_t* sensor_data) { - int state = -1; - int handle = 0; - sensor_data_t sensor_data; - retvm_if (curr_state == NULL, -1, "sf_check_rotation fail, invalid curr_state"); - *curr_state = ROTATION_UNKNOWN; - - handle = sf_connect(ACCELEROMETER_SENSOR); + sensor_id_t sensor_id; + command_channel *cmd_channel; + int sensor_state; + int client_id; - if (handle < 0) { - ERR("sensor attach fail"); - return OP_ERROR; - } + retvm_if ((!sensor_data), false, "sensor_data is NULL"); - state = sf_start(handle, 1); + AUTOLOCK(lock); - if (state < 0) { - ERR("sf_start fail"); - return OP_ERROR; + if (!event_listener.get_sensor_state(handle, sensor_state)|| + !event_listener.get_sensor_id(handle, sensor_id)) { + ERR("client %s failed to get handle information", get_client_name()); + return false; } - state = sf_get_data(handle, ACCELEROMETER_ROTATION_DATA_SET, &sensor_data); - - if (state < 0) { - ERR("sf_get_data fail"); - return OP_ERROR; + if (!event_listener.get_command_channel(sensor_id, &cmd_channel)) { + ERR("client %s failed to get command channel for %s", get_client_name(), get_sensor_name(sensor_id)); + return false; } - state = sf_stop(handle); - if (state < 0) { - ERR("sf_stop fail"); - return OP_ERROR; - } + client_id = event_listener.get_client_id(); + retvm_if ((client_id < 0), false, "Invalid client id : %d, handle: %d, %s, %s", client_id, handle, get_sensor_name(sensor_id), get_client_name()); - state = sf_disconnect(handle); + if (sensor_state != SENSOR_STATE_STARTED) { + ERR("Sensor %s is not started for client %s with handle: %d, sensor_state: %d", get_sensor_name(sensor_id), get_client_name(), handle, sensor_state); + return false; + } - if (state < 0) { - ERR("sf_disconnect fail"); - return OP_ERROR; + if(!cmd_channel->cmd_get_data(data_id, sensor_data)) { + ERR("Cmd_get_struct(%d, %d, 0x%x) failed for %s", client_id, data_id, sensor_data, get_client_name()); + return false; } - *curr_state = sensor_data.values[0]; + return true; - INFO("%s gets %s by checking rotation", get_client_name(), get_rotate_name(*curr_state)); - return OP_SUCCESS; } diff --git a/src/libsensord/client_common.cpp b/src/libsensord/client_common.cpp index 5ed83f3..8acb613 100755 --- a/src/libsensord/client_common.cpp +++ b/src/libsensord/client_common.cpp @@ -16,15 +16,13 @@ * limitations under the License. * */ - #include #include #include #include #include -#include - -using std::map; +#include +using std::unordered_map; #define FILL_LOG_ELEMENT(ID, TYPE, CNT, PRINT_PER_CNT) {ID, TYPE, {#TYPE, CNT, PRINT_PER_CNT} } @@ -34,60 +32,74 @@ log_element g_log_elements[] = { FILL_LOG_ELEMENT(LOG_ID_SENSOR_TYPE, GEOMAGNETIC_SENSOR, 0, 1), FILL_LOG_ELEMENT(LOG_ID_SENSOR_TYPE, LIGHT_SENSOR, 0, 1), FILL_LOG_ELEMENT(LOG_ID_SENSOR_TYPE, PROXIMITY_SENSOR, 0, 1), + FILL_LOG_ELEMENT(LOG_ID_SENSOR_TYPE, THERMOMETER_SENSOR, 0, 1), FILL_LOG_ELEMENT(LOG_ID_SENSOR_TYPE, GYROSCOPE_SENSOR, 0, 1), + FILL_LOG_ELEMENT(LOG_ID_SENSOR_TYPE, PRESSURE_SENSOR, 0, 1), FILL_LOG_ELEMENT(LOG_ID_SENSOR_TYPE, MOTION_SENSOR, 0, 1), + FILL_LOG_ELEMENT(LOG_ID_SENSOR_TYPE, CONTEXT_SENSOR, 0, 1), FILL_LOG_ELEMENT(LOG_ID_SENSOR_TYPE, GRAVITY_SENSOR, 0, 1), FILL_LOG_ELEMENT(LOG_ID_SENSOR_TYPE, LINEAR_ACCEL_SENSOR, 0, 1), FILL_LOG_ELEMENT(LOG_ID_SENSOR_TYPE, ORIENTATION_SENSOR, 0, 1), + FILL_LOG_ELEMENT(LOG_ID_SENSOR_TYPE, TEMPERATURE_SENSOR, 0, 1), - FILL_LOG_ELEMENT(LOG_ID_EVENT, ACCELEROMETER_EVENT_ROTATION_CHECK, 0, 1), - FILL_LOG_ELEMENT(LOG_ID_EVENT, ACCELEROMETER_EVENT_CALIBRATION_NEEDED, 0, 1), - FILL_LOG_ELEMENT(LOG_ID_EVENT, ACCELEROMETER_EVENT_SET_WAKEUP, 0, 1), FILL_LOG_ELEMENT(LOG_ID_EVENT, GEOMAGNETIC_EVENT_CALIBRATION_NEEDED, 0, 1), - FILL_LOG_ELEMENT(LOG_ID_EVENT, PROXIMITY_EVENT_CHANGE_STATE, 0, 1), + FILL_LOG_ELEMENT(LOG_ID_EVENT, PROXIMITY_EVENT_CHANGE_STATE, 0,1), FILL_LOG_ELEMENT(LOG_ID_EVENT, LIGHT_EVENT_CHANGE_LEVEL, 0, 1), FILL_LOG_ELEMENT(LOG_ID_EVENT, MOTION_ENGINE_EVENT_SNAP, 0, 1), FILL_LOG_ELEMENT(LOG_ID_EVENT, MOTION_ENGINE_EVENT_SHAKE, 0, 1), FILL_LOG_ELEMENT(LOG_ID_EVENT, MOTION_ENGINE_EVENT_DOUBLETAP, 0, 1), - FILL_LOG_ELEMENT(LOG_ID_EVENT, MOTION_ENGINE_EVENT_DIRECT_CALL, 0, 1), + FILL_LOG_ELEMENT(LOG_ID_EVENT, MOTION_ENGINE_EVENT_PANNING, 0, 25), + FILL_LOG_ELEMENT(LOG_ID_EVENT, MOTION_ENGINE_EVENT_TOP_TO_BOTTOM, 0, 1), + FILL_LOG_ELEMENT(LOG_ID_EVENT, MOTION_ENGINE_EVENT_DIRECT_CALL, 0,1), + FILL_LOG_ELEMENT(LOG_ID_EVENT, MOTION_ENGINE_EVENT_SMART_RELAY, 0, 1), + FILL_LOG_ELEMENT(LOG_ID_EVENT, MOTION_ENGINE_EVENT_TILT_TO_UNLOCK, 0,1 ), + FILL_LOG_ELEMENT(LOG_ID_EVENT, MOTION_ENGINE_EVENT_SMART_ALERT, 0, 1), + FILL_LOG_ELEMENT(LOG_ID_EVENT, MOTION_ENGINE_EVENT_TILT, 0, 25), + FILL_LOG_ELEMENT(LOG_ID_EVENT, MOTION_ENGINE_EVENT_PANNING_BROWSE, 0, 25), + FILL_LOG_ELEMENT(LOG_ID_EVENT, MOTION_ENGINE_EVENT_NO_MOVE, 0, 1), + FILL_LOG_ELEMENT(LOG_ID_EVENT, MOTION_ENGINE_EVENT_SHAKE_ALWAYS_ON, 0, 1), - FILL_LOG_ELEMENT(LOG_ID_EVENT, ACCELEROMETER_EVENT_RAW_DATA_REPORT_ON_TIME, 0, 10), - FILL_LOG_ELEMENT(LOG_ID_EVENT, GEOMAGNETIC_EVENT_RAW_DATA_REPORT_ON_TIME, 0, 10), FILL_LOG_ELEMENT(LOG_ID_EVENT, PROXIMITY_EVENT_STATE_REPORT_ON_TIME, 0, 10), + FILL_LOG_ELEMENT(LOG_ID_EVENT, PROXIMITY_EVENT_DISTANCE_DATA_REPORT_ON_TIME, 0, 10), + FILL_LOG_ELEMENT(LOG_ID_EVENT, CONTEXT_EVENT_REPORT, 0, 1), + +#ifdef _RAWDATA_DEBUG + FILL_LOG_ELEMENT(LOG_ID_EVENT, ACCELEROMETER_EVENT_RAW_DATA_REPORT_ON_TIME, 0, 10), FILL_LOG_ELEMENT(LOG_ID_EVENT, GYROSCOPE_EVENT_RAW_DATA_REPORT_ON_TIME, 0, 10), + FILL_LOG_ELEMENT(LOG_ID_EVENT, GEOMAGNETIC_EVENT_RAW_DATA_REPORT_ON_TIME, 0, 10), + FILL_LOG_ELEMENT(LOG_ID_EVENT, PRESSURE_EVENT_RAW_DATA_REPORT_ON_TIME, 0, 10), FILL_LOG_ELEMENT(LOG_ID_EVENT, LIGHT_EVENT_LEVEL_DATA_REPORT_ON_TIME, 0, 10), FILL_LOG_ELEMENT(LOG_ID_EVENT, LIGHT_EVENT_LUX_DATA_REPORT_ON_TIME, 0, 10), - FILL_LOG_ELEMENT(LOG_ID_EVENT, GEOMAGNETIC_EVENT_ATTITUDE_DATA_REPORT_ON_TIME, 0, 10), - FILL_LOG_ELEMENT(LOG_ID_EVENT, ACCELEROMETER_EVENT_ORIENTATION_DATA_REPORT_ON_TIME, 0, 10), - FILL_LOG_ELEMENT(LOG_ID_EVENT, PROXIMITY_EVENT_DISTANCE_DATA_REPORT_ON_TIME, 0, 10), FILL_LOG_ELEMENT(LOG_ID_EVENT, GRAVITY_EVENT_RAW_DATA_REPORT_ON_TIME, 0, 10), FILL_LOG_ELEMENT(LOG_ID_EVENT, LINEAR_ACCEL_EVENT_RAW_DATA_REPORT_ON_TIME, 0, 10), FILL_LOG_ELEMENT(LOG_ID_EVENT, ORIENTATION_EVENT_RAW_DATA_REPORT_ON_TIME, 0, 10), + FILL_LOG_ELEMENT(LOG_ID_EVENT, PRESSURE_EVENT_RAW_DATA_REPORT_ON_TIME, 0, 10), + FILL_LOG_ELEMENT(LOG_ID_EVENT, TEMPERATURE_EVENT_RAW_DATA_REPORT_ON_TIME, 0, 10), +#endif FILL_LOG_ELEMENT(LOG_ID_DATA, ACCELEROMETER_BASE_DATA_SET, 0, 25), - FILL_LOG_ELEMENT(LOG_ID_DATA, ACCELEROMETER_ORIENTATION_DATA_SET, 0, 25), - FILL_LOG_ELEMENT(LOG_ID_DATA, ACCELEROMETER_ROTATION_DATA_SET, 0, 25), FILL_LOG_ELEMENT(LOG_ID_DATA, GYRO_BASE_DATA_SET, 0, 25), FILL_LOG_ELEMENT(LOG_ID_DATA, PROXIMITY_BASE_DATA_SET, 0, 25), FILL_LOG_ELEMENT(LOG_ID_DATA, PROXIMITY_DISTANCE_DATA_SET, 0, 25), + FILL_LOG_ELEMENT(LOG_ID_DATA, PRESSURE_BASE_DATA_SET, 0, 25), FILL_LOG_ELEMENT(LOG_ID_DATA, GEOMAGNETIC_BASE_DATA_SET, 0, 25), - FILL_LOG_ELEMENT(LOG_ID_DATA, GEOMAGNETIC_RAW_DATA_SET, 0, 25), - FILL_LOG_ELEMENT(LOG_ID_DATA, GEOMAGNETIC_ATTITUDE_DATA_SET, 0, 25), FILL_LOG_ELEMENT(LOG_ID_DATA, LIGHT_BASE_DATA_SET, 0, 25), FILL_LOG_ELEMENT(LOG_ID_DATA, LIGHT_LUX_DATA_SET, 0, 25), - - FILL_LOG_ELEMENT(LOG_ID_ROTATE, ROTATION_EVENT_0, 0, 1), - FILL_LOG_ELEMENT(LOG_ID_ROTATE, ROTATION_EVENT_90, 0, 1), - FILL_LOG_ELEMENT(LOG_ID_ROTATE, ROTATION_EVENT_180, 0, 1), - FILL_LOG_ELEMENT(LOG_ID_ROTATE, ROTATION_EVENT_270, 0, 1), + FILL_LOG_ELEMENT(LOG_ID_DATA, CONTEXT_BASE_DATA_SET, 0, 25), + FILL_LOG_ELEMENT(LOG_ID_DATA, GRAVITY_BASE_DATA_SET, 0, 25), + FILL_LOG_ELEMENT(LOG_ID_DATA, LINEAR_ACCEL_BASE_DATA_SET, 0, 25), + FILL_LOG_ELEMENT(LOG_ID_DATA, ORIENTATION_BASE_DATA_SET, 0, 25), + FILL_LOG_ELEMENT(LOG_ID_DATA, PRESSURE_BASE_DATA_SET, 0, 25), + FILL_LOG_ELEMENT(LOG_ID_DATA, TEMPERATURE_BASE_DATA_SET, 0, 25), }; -typedef map log_map; +typedef unordered_map log_map; log_map g_log_maps[LOG_ID_END]; extern void init_client(void); static void init_log_maps(void); + class initiator { public: @@ -101,18 +113,21 @@ public: static void init_log_maps(void) { int cnt; + cnt = sizeof(g_log_elements) / sizeof(g_log_elements[0]); for (int i = 0; i < cnt; ++i) { g_log_maps[g_log_elements[i].id][g_log_elements[i].type] = &g_log_elements[i].log_attr; } + } -const char *get_log_element_name(log_id id, unsigned int type) + +const char* get_log_element_name(log_id id, unsigned int type) { - const char *p_unknown = "UNKNOWN"; - log_map::iterator iter; - iter = g_log_maps[id].find(type); + const char* p_unknown = "UNKNOWN"; + + auto iter = g_log_maps[id].find(type); if (iter == g_log_maps[id].end()) { INFO("Unknown type value: 0x%x", type); @@ -122,30 +137,31 @@ const char *get_log_element_name(log_id id, unsigned int type) return iter->second->name; } -const char *get_sensor_name(sensor_type_t sensor_type) +const char* get_sensor_name(sensor_id_t sensor_id) { + const int SENSOR_TYPE_MASK = 0x0000FFFF; + + sensor_type_t sensor_type = (sensor_type_t) (sensor_id & SENSOR_TYPE_MASK); + return get_log_element_name(LOG_ID_SENSOR_TYPE, sensor_type); } -const char *get_event_name(unsigned int event_type) +const char* get_event_name(unsigned int event_type) { return get_log_element_name(LOG_ID_EVENT, event_type); } -const char *get_data_name(unsigned int data_id) -{ - return get_log_element_name(LOG_ID_DATA, data_id); -} -const char *get_rotate_name(unsigned int rotate_type) +const char* get_data_name(unsigned int data_id) { - return get_log_element_name(LOG_ID_ROTATE, rotate_type); + return get_log_element_name(LOG_ID_DATA, data_id); } bool is_one_shot_event(unsigned int event_type) { switch (event_type) { - case ACCELEROMETER_EVENT_SET_WAKEUP: + case MOTION_ENGINE_EVENT_SMART_ALERT: + case MOTION_ENGINE_EVENT_SMART_RELAY: return true; break; } @@ -157,17 +173,16 @@ bool is_ontime_event(unsigned int event_type) { switch (event_type ) { case ACCELEROMETER_EVENT_RAW_DATA_REPORT_ON_TIME: - case ACCELEROMETER_EVENT_ORIENTATION_DATA_REPORT_ON_TIME: - case GEOMAGNETIC_EVENT_RAW_DATA_REPORT_ON_TIME: - case GEOMAGNETIC_EVENT_ATTITUDE_DATA_REPORT_ON_TIME: + case PROXIMITY_EVENT_STATE_REPORT_ON_TIME: case GYROSCOPE_EVENT_RAW_DATA_REPORT_ON_TIME: case LIGHT_EVENT_LEVEL_DATA_REPORT_ON_TIME: + case GEOMAGNETIC_EVENT_RAW_DATA_REPORT_ON_TIME: case LIGHT_EVENT_LUX_DATA_REPORT_ON_TIME: - case PROXIMITY_EVENT_STATE_REPORT_ON_TIME: case PROXIMITY_EVENT_DISTANCE_DATA_REPORT_ON_TIME: case GRAVITY_EVENT_RAW_DATA_REPORT_ON_TIME: case LINEAR_ACCEL_EVENT_RAW_DATA_REPORT_ON_TIME: case ORIENTATION_EVENT_RAW_DATA_REPORT_ON_TIME: + case PRESSURE_EVENT_RAW_DATA_REPORT_ON_TIME: return true; break; } @@ -177,21 +192,32 @@ bool is_ontime_event(unsigned int event_type) bool is_panning_event(unsigned int event_type) { + switch (event_type) { + case MOTION_ENGINE_EVENT_PANNING: + case MOTION_ENGINE_EVENT_TILT: + case MOTION_ENGINE_EVENT_PANNING_BROWSE: + return true; + break; + } + return false; } bool is_single_state_event(unsigned int event_type) { switch (event_type) { - case ACCELEROMETER_EVENT_SET_WAKEUP: - case ACCELEROMETER_EVENT_ROTATION_CHECK: case GEOMAGNETIC_EVENT_CALIBRATION_NEEDED: case LIGHT_EVENT_CHANGE_LEVEL: case PROXIMITY_EVENT_CHANGE_STATE: case MOTION_ENGINE_EVENT_SNAP: case MOTION_ENGINE_EVENT_SHAKE: + case MOTION_ENGINE_EVENT_SHAKE_ALWAYS_ON: case MOTION_ENGINE_EVENT_DOUBLETAP: + case MOTION_ENGINE_EVENT_TOP_TO_BOTTOM: case MOTION_ENGINE_EVENT_DIRECT_CALL: + case MOTION_ENGINE_EVENT_SMART_RELAY: + case MOTION_ENGINE_EVENT_TILT_TO_UNLOCK: + case MOTION_ENGINE_EVENT_NO_MOVE: return true; break; } @@ -202,11 +228,14 @@ bool is_single_state_event(unsigned int event_type) unsigned int get_calibration_event_type(unsigned int event_type) { sensor_type_t sensor; + sensor = (sensor_type_t)(event_type >> SENSOR_TYPE_SHIFT); switch (sensor) { case GEOMAGNETIC_SENSOR: return GEOMAGNETIC_EVENT_CALIBRATION_NEEDED; + case ORIENTATION_SENSOR: + return ORIENTATION_EVENT_CALIBRATION_NEEDED; default: return 0; } @@ -216,42 +245,20 @@ unsigned long long get_timestamp(void) { struct timespec t; clock_gettime(CLOCK_MONOTONIC, &t); - return ((unsigned long long)(t.tv_sec) * NS_TO_SEC + t.tv_nsec) / MS_TO_SEC; -} - -void sensor_event_to_data(sensor_event_t &event, sensor_data_t &data) -{ - data.data_accuracy = event.data.data_accuracy; - data.data_unit_idx = event.data.data_unit_idx; - data.timestamp = event.data.timestamp; - data.values_num = event.data.values_num; - memcpy(data.values, event.data.values, sizeof(event.data.values)); + return ((unsigned long long)(t.tv_sec)*1000000000LL + t.tv_nsec) / 1000; } -void sensorhub_event_to_hub_data(sensorhub_event_t &event, sensorhub_data_t &data) -{ - data.version = event.data.version; - data.sensorhub = event.data.sensorhub; - data.type = event.data.type; - data.hub_data_size = event.data.hub_data_size; - data.timestamp = event.data.timestamp; - memcpy(data.hub_data, event.data.hub_data, event.data.hub_data_size); - memcpy(data.data, event.data.data, sizeof(event.data.data)); -} - -void print_event_occurrence_log(csensor_handle_info &sensor_handle_info, creg_event_info &event_info, - sensor_event_data_t &sensor_event_data) +void print_event_occurrence_log(csensor_handle_info &sensor_handle_info, const creg_event_info *event_info) { log_attr *log_attr; - log_map::iterator iter; - iter = g_log_maps[LOG_ID_EVENT].find(event_info.m_event_type); - if (iter == g_log_maps[LOG_ID_EVENT].end()) { - ERR("wrong event_type: 0x%x, handle %d", event_info.m_event_type, sensor_handle_info.m_handle); + auto iter = g_log_maps[LOG_ID_EVENT].find(event_info->type); + + if (iter == g_log_maps[LOG_ID_EVENT].end()) return; - } log_attr = iter->second; + log_attr->cnt++; if ((log_attr->cnt != 1) && ((log_attr->cnt % log_attr->print_per_cnt) != 0)) { @@ -259,13 +266,35 @@ void print_event_occurrence_log(csensor_handle_info &sensor_handle_info, creg_ev } INFO("%s receives %s with %s[%d][state: %d, option: %d count: %d]", get_client_name(), log_attr->name, - get_sensor_name(sensor_handle_info.m_sensor_type), sensor_handle_info.m_handle, sensor_handle_info.m_sensor_state, - sensor_handle_info.m_sensor_option, log_attr->cnt); + get_sensor_name(sensor_handle_info.m_sensor_id), sensor_handle_info.m_handle, sensor_handle_info.m_sensor_state, + sensor_handle_info.m_sensor_option, log_attr->cnt); - if (event_info.m_event_type == ACCELEROMETER_EVENT_ROTATION_CHECK) { - INFO("%s", get_rotate_name(*(unsigned int *)(sensor_event_data.event_data))); - } + INFO("0x%x(cb_event_type = %s, &user_data, client_data = 0x%x)\n", event_info->m_cb, + log_attr->name, event_info->m_user_data); +} + +/* + * To prevent user mistakenly freeing sensor_info using sensor_t + */ +static const int SENSOR_TO_SENSOR_INFO = 4; +static const int SENSOR_INFO_TO_SENSOR = -SENSOR_TO_SENSOR_INFO; + +sensor_info *sensor_to_sensor_info(sensor_t sensor) +{ + if (!sensor) + return NULL; + + sensor_info* info = (sensor_info *)((char *)sensor + SENSOR_TO_SENSOR_INFO); + + return info; +} + +sensor_t sensor_info_to_sensor(const sensor_info *info) +{ + if (!info) + return NULL; + + sensor_t sensor = (sensor_t)((char *)info + SENSOR_INFO_TO_SENSOR); - INFO("0x%x(cb_event_type = %s, &cb_data, client_data = 0x%x)", event_info.m_event_callback, - log_attr->name, event_info.m_cb_data); + return sensor; } diff --git a/src/libsensord/client_common.h b/src/libsensord/client_common.h index dc8c78b..5d6a752 100755 --- a/src/libsensord/client_common.h +++ b/src/libsensord/client_common.h @@ -20,12 +20,14 @@ #ifndef CLIENT_COMMON_H_ #define CLIENT_COMMON_H_ -#include +/*header for each sensor type*/ +#include #include #include #include #define BASE_GATHERING_INTERVAL 100 + #define CLIENT_NAME_SIZE NAME_MAX+10 enum log_id { @@ -34,7 +36,6 @@ enum log_id { LOG_ID_EVENT, LOG_ID_DATA, LOG_ID_PROPERTY, - LOG_ID_ROTATE, LOG_ID_END, }; @@ -50,6 +51,7 @@ struct log_element { struct log_attr log_attr; }; + typedef struct { int handle; unsigned int event_type; @@ -60,23 +62,21 @@ typedef struct { creg_event_info event_info; } log_info; -bool is_sensorhub_event(unsigned int event_type); bool is_one_shot_event(unsigned int event_type); bool is_ontime_event(unsigned int event_type); bool is_panning_event(unsigned int event_type); bool is_single_state_event(unsigned int event_type); unsigned int get_calibration_event_type(unsigned int event_type); unsigned long long get_timestamp(void); -void sensor_event_to_data(sensor_event_t &event, sensor_data_t &data); -void sensorhub_event_to_hub_data(sensorhub_event_t &event, sensorhub_data_t &data); -const char *get_log_element_name(log_id id, unsigned int type); -const char *get_sensor_name(sensor_type_t sensor_type); -const char *get_event_name(unsigned int event_type); -const char *get_data_name(unsigned int data_id); -const char *get_rotate_name(unsigned int rotate_type); -void print_event_occurrence_log(csensor_handle_info &sensor_handle_info, - creg_event_info &event_info, - sensor_event_data_t &event_data); +const char* get_log_element_name(log_id id, unsigned int type); +const char* get_sensor_name(sensor_id_t sensor_id); +const char* get_event_name(unsigned int event_type); +const char* get_data_name(unsigned int data_id); +void print_event_occurrence_log(csensor_handle_info &sensor_handle_info, const creg_event_info *event_info); + +class sensor_info; +sensor_info *sensor_to_sensor_info(sensor_t sensor); +sensor_t sensor_info_to_sensor(const sensor_info *info); #endif /* CLIENT_COMMON_H_ */ diff --git a/src/libsensord/command_channel.cpp b/src/libsensord/command_channel.cpp index c9c6681..21238a2 100755 --- a/src/libsensord/command_channel.cpp +++ b/src/libsensord/command_channel.cpp @@ -20,13 +20,15 @@ #include #include #include +#include +#include command_channel::command_channel() : m_client_id(CLIENT_ID_INVALID) -, m_sensor_type(UNKNOWN_SENSOR) +, m_sensor_id(UNKNOWN_SENSOR) { -} +} command_channel::~command_channel() { if (m_command_socket.is_valid()) @@ -40,12 +42,13 @@ bool command_channel::command_handler(cpacket *packet, void **return_payload) return false; } - if (packet->size() < 0) { + if (packet->size() == 0) { ERR("Packet is not valid for client %s", get_client_name()); return false; } if (m_command_socket.send(packet->packet(), packet->size()) <= 0) { + m_command_socket.close(); ERR("Failed to send command in client %s", get_client_name()); return false; } @@ -53,19 +56,23 @@ bool command_channel::command_handler(cpacket *packet, void **return_payload) packet_header header; if (m_command_socket.recv(&header, sizeof(header)) <= 0) { + m_command_socket.close(); ERR("Failed to receive header for reply packet in client %s", get_client_name()); return false; } - char *buffer = new char[header.size]; + char *buffer = new(std::nothrow) char[header.size]; + retvm_if(!buffer, false, "Failed to allocate memory"); if (m_command_socket.recv(buffer, header.size) <= 0) { + m_command_socket.close(); ERR("Failed to receive reply packet in client %s", get_client_name()); delete[] buffer; return false; } *return_payload = buffer; + return true; } @@ -80,6 +87,7 @@ bool command_channel::create_channel(void) } m_command_socket.set_connection_mode(); + return true; } @@ -94,9 +102,11 @@ bool command_channel::cmd_get_id(int &client_id) cmd_get_id_t *cmd_get_id; cmd_get_id_done_t *cmd_get_id_done; - packet = new cpacket(sizeof(cmd_get_id_t)); + packet = new(std::nothrow) cpacket(sizeof(cmd_get_id_t)); + retvm_if(!packet, false, "Failed to allocate memory"); + packet->set_cmd(CMD_GET_ID); - packet->set_payload_size(sizeof(cmd_get_id_t)); + cmd_get_id = (cmd_get_id_t *)packet->data(); cmd_get_id->pid = getpid(); @@ -120,19 +130,67 @@ bool command_channel::cmd_get_id(int &client_id) delete[] (char *)cmd_get_id_done; delete packet; + return true; } -bool command_channel::cmd_hello(sensor_type_t sensor) +bool command_channel::cmd_get_sensor_list(void) +{ + cpacket packet; + cmd_get_sensor_list_done_t *cmd_get_sensor_list_done; + + packet.set_payload_size(sizeof(cmd_get_sensor_list_t)); + packet.set_cmd(CMD_GET_SENSOR_LIST); + + INFO("%s send cmd_get_sensor_list", get_client_name()); + + if (!command_handler(&packet, (void **)&cmd_get_sensor_list_done)) { + ERR("Client %s failed to send/receive command", get_client_name()); + return false; + } + + int sensor_cnt; + const size_t *size_field; + const char *raw_data_field; + + sensor_cnt = cmd_get_sensor_list_done->sensor_cnt; + size_field = (const size_t *)cmd_get_sensor_list_done->data; + raw_data_field = (const char *)size_field + (sizeof(size_t) * sensor_cnt); + + sensor_info *info; + + int idx = 0; + for (int i = 0; i < sensor_cnt; ++i) { + info = new(std::nothrow) sensor_info; + + if (!info) { + ERR("Failed to allocate memory"); + delete[] (char *)cmd_get_sensor_list_done; + return false; + } + + info->set_raw_data(raw_data_field + idx, size_field[i]); + sensor_info_manager::get_instance().add_info(info); + idx += size_field[i]; + } + + delete[] (char *)cmd_get_sensor_list_done; + return true; +} + + +bool command_channel::cmd_hello(sensor_id_t sensor) { cpacket *packet; cmd_hello_t *cmd_hello; cmd_done_t *cmd_done; - packet = new cpacket(sizeof(cmd_hello_t)); + packet = new(std::nothrow) cpacket(sizeof(cmd_hello_t)); + retvm_if(!packet, false, "Failed to allocate memory"); + packet->set_cmd(CMD_HELLO); - packet->set_payload_size(sizeof(cmd_hello_t)); - cmd_hello = (cmd_hello_t *)packet->data(); + + cmd_hello = (cmd_hello_t*)packet->data(); cmd_hello->client_id = m_client_id; cmd_hello->sensor = sensor; @@ -149,6 +207,7 @@ bool command_channel::cmd_hello(sensor_type_t sensor) if (cmd_done->value < 0) { ERR("client %s got error[%d] from server with sensor [%s]", get_client_name(), cmd_done->value, get_sensor_name(sensor)); + delete[] (char *)cmd_done; delete packet; return false; @@ -156,7 +215,9 @@ bool command_channel::cmd_hello(sensor_type_t sensor) delete[] (char *)cmd_done; delete packet; - m_sensor_type = sensor; + + m_sensor_id = sensor; + return true; } @@ -165,23 +226,25 @@ bool command_channel::cmd_byebye(void) cpacket *packet; cmd_done_t *cmd_done; - packet = new cpacket(sizeof(cmd_byebye_t)); + packet = new(std::nothrow) cpacket(sizeof(cmd_byebye_t)); + retvm_if(!packet, false, "Failed to allocate memory"); + packet->set_cmd(CMD_BYEBYE); - packet->set_payload_size(sizeof(cmd_byebye_t)); INFO("%s send cmd_byebye(client_id=%d, %s)", - get_client_name(), m_client_id, get_sensor_name(m_sensor_type)); + get_client_name(), m_client_id, get_sensor_name(m_sensor_id)); if (!command_handler(packet, (void **)&cmd_done)) { ERR("Client %s failed to send/receive command for sensor[%s] with client_id [%d]", - get_client_name(), get_sensor_name(m_sensor_type), m_client_id); + get_client_name(), get_sensor_name(m_sensor_id), m_client_id); delete packet; return false; } if (cmd_done->value < 0) { ERR("Client %s got error[%d] from server for sensor[%s] with client_id [%d]", - get_client_name(), cmd_done->value, get_sensor_name(m_sensor_type), m_client_id); + get_client_name(), cmd_done->value, get_sensor_name(m_sensor_id), m_client_id); + delete[] (char *)cmd_done; delete packet; return false; @@ -193,8 +256,10 @@ bool command_channel::cmd_byebye(void) if (m_command_socket.is_valid()) m_command_socket.close(); + m_client_id = CLIENT_ID_INVALID; - m_sensor_type = UNKNOWN_SENSOR; + m_sensor_id = UNKNOWN_SENSOR; + return true; } @@ -203,23 +268,25 @@ bool command_channel::cmd_start(void) cpacket *packet; cmd_done_t *cmd_done; - packet = new cpacket(sizeof(cmd_start_t)); + packet = new(std::nothrow) cpacket(sizeof(cmd_start_t)); + retvm_if(!packet, false, "Failed to allocate memory"); + packet->set_cmd(CMD_START); - packet->set_payload_size(sizeof(cmd_start_t)); INFO("%s send cmd_start(client_id=%d, %s)", - get_client_name(), m_client_id, get_sensor_name(m_sensor_type)); + get_client_name(), m_client_id, get_sensor_name(m_sensor_id)); if (!command_handler(packet, (void **)&cmd_done)) { ERR("Client %s failed to send/receive command for sensor[%s] with client_id [%d]", - get_client_name(), get_sensor_name(m_sensor_type), m_client_id); + get_client_name(), get_sensor_name(m_sensor_id), m_client_id); delete packet; return false; } if (cmd_done->value < 0) { ERR("Client %s got error[%d] from server for sensor[%s] with client_id [%d]", - get_client_name(), cmd_done->value, get_sensor_name(m_sensor_type), m_client_id); + get_client_name(), cmd_done->value, get_sensor_name(m_sensor_id), m_client_id); + delete[] (char *)cmd_done; delete packet; return false; @@ -227,6 +294,7 @@ bool command_channel::cmd_start(void) delete[] (char *)cmd_done; delete packet; + return true; } @@ -235,23 +303,25 @@ bool command_channel::cmd_stop(void) cpacket *packet; cmd_done_t *cmd_done; - packet = new cpacket(sizeof(cmd_stop_t)); + packet = new(std::nothrow) cpacket(sizeof(cmd_stop_t)); + retvm_if(!packet, false, "Failed to allocate memory"); + packet->set_cmd(CMD_STOP); - packet->set_payload_size(sizeof(cmd_stop_t)); INFO("%s send cmd_stop(client_id=%d, %s)", - get_client_name(), m_client_id, get_sensor_name(m_sensor_type)); + get_client_name(), m_client_id, get_sensor_name(m_sensor_id)); if (!command_handler(packet, (void **)&cmd_done)) { ERR("Client %s failed to send/receive command for sensor[%s] with client_id [%d]", - get_client_name(), get_sensor_name(m_sensor_type), m_client_id); + get_client_name(), get_sensor_name(m_sensor_id), m_client_id); delete packet; return false; } if (cmd_done->value < 0) { ERR("Client %s got error[%d] from server for sensor[%s] with client_id [%d]", - get_client_name(), cmd_done->value, get_sensor_name(m_sensor_type), m_client_id); + get_client_name(), cmd_done->value, get_sensor_name(m_sensor_id), m_client_id); + delete[] (char *)cmd_done; delete packet; return false; @@ -259,6 +329,7 @@ bool command_channel::cmd_stop(void) delete[] (char *)cmd_done; delete packet; + return true; } @@ -268,25 +339,28 @@ bool command_channel::cmd_set_option(int option) cmd_set_option_t *cmd_set_option; cmd_done_t *cmd_done; - packet = new cpacket(sizeof(cmd_set_option_t)); + packet = new(std::nothrow) cpacket(sizeof(cmd_set_option_t)); + retvm_if(!packet, false, "Failed to allocate memory"); + packet->set_cmd(CMD_SET_OPTION); - packet->set_payload_size(sizeof(cmd_set_option_t)); - cmd_set_option = (cmd_set_option_t *)packet->data(); + + cmd_set_option = (cmd_set_option_t*)packet->data(); cmd_set_option->option = option; INFO("%s send cmd_set_option(client_id=%d, %s, option=%d)", - get_client_name(), m_client_id, get_sensor_name(m_sensor_type), option); + get_client_name(), m_client_id, get_sensor_name(m_sensor_id), option); if (!command_handler(packet, (void **)&cmd_done)) { - ERR("Client %s failed to send/receive command for sensor[%s] with client_id [%d], option[%]", - get_client_name(), get_sensor_name(m_sensor_type), m_client_id, option); + ERR("Client %s failed to send/receive command for sensor[%s] with client_id [%d], option[%d]", + get_client_name(), get_sensor_name(m_sensor_id), m_client_id, option); delete packet; return false; } if (cmd_done->value < 0) { - ERR("Client %s got error[%d] from server for sensor[%s] with client_id [%d], option[%]", - get_client_name(), cmd_done->value, get_sensor_name(m_sensor_type), m_client_id, option); + ERR("Client %s got error[%d] from server for sensor[%s] with client_id [%d], option[%d]", + get_client_name(), cmd_done->value, get_sensor_name(m_sensor_id), m_client_id, option); + delete[] (char *)cmd_done; delete packet; return false; @@ -294,6 +368,7 @@ bool command_channel::cmd_set_option(int option) delete[] (char *)cmd_done; delete packet; + return true; } @@ -303,10 +378,12 @@ bool command_channel::cmd_register_event(unsigned int event_type) cmd_reg_t *cmd_reg; cmd_done_t *cmd_done; - packet = new cpacket(sizeof(cmd_reg_t)); + packet = new(std::nothrow) cpacket(sizeof(cmd_reg_t)); + retvm_if(!packet, false, "Failed to allocate memory"); + packet->set_cmd(CMD_REG); - packet->set_payload_size(sizeof(cmd_reg_t)); - cmd_reg = (cmd_reg_t *)packet->data(); + + cmd_reg = (cmd_reg_t*)packet->data(); cmd_reg->event_type = event_type; INFO("%s send cmd_register_event(client_id=%d, %s)", @@ -322,6 +399,7 @@ bool command_channel::cmd_register_event(unsigned int event_type) if (cmd_done->value < 0) { ERR("Client %s got error[%d] from server with client_id [%d], event_type[%s]", get_client_name(), cmd_done->value, m_client_id, get_event_name(event_type)); + delete[] (char *)cmd_done; delete packet; return false; @@ -329,16 +407,17 @@ bool command_channel::cmd_register_event(unsigned int event_type) delete[] (char *)cmd_done; delete packet; + return true; } + bool command_channel::cmd_register_events(event_type_vector &event_vec) { - event_type_vector::iterator it_event; - it_event = event_vec.begin(); + auto it_event = event_vec.begin(); while (it_event != event_vec.end()) { - if (!cmd_register_event(*it_event)) + if(!cmd_register_event(*it_event)) return false; ++it_event; @@ -353,10 +432,12 @@ bool command_channel::cmd_unregister_event(unsigned int event_type) cmd_unreg_t *cmd_unreg; cmd_done_t *cmd_done; - packet = new cpacket(sizeof(cmd_unreg_t)); + packet = new(std::nothrow) cpacket(sizeof(cmd_unreg_t)); + retvm_if(!packet, false, "Failed to allocate memory"); + packet->set_cmd(CMD_UNREG); - packet->set_payload_size(sizeof(cmd_unreg_t)); - cmd_unreg = (cmd_unreg_t *)packet->data(); + + cmd_unreg = (cmd_unreg_t*)packet->data(); cmd_unreg->event_type = event_type; INFO("%s send cmd_unregister_event(client_id=%d, %s)", @@ -372,6 +453,7 @@ bool command_channel::cmd_unregister_event(unsigned int event_type) if (cmd_done->value < 0) { ERR("Client %s got error[%d] from server with client_id [%d], event_type[%s]", get_client_name(), cmd_done->value, m_client_id, get_event_name(event_type)); + delete[] (char *)cmd_done; delete packet; return false; @@ -379,16 +461,17 @@ bool command_channel::cmd_unregister_event(unsigned int event_type) delete[] (char *)cmd_done; delete packet; + return true; } + bool command_channel::cmd_unregister_events(event_type_vector &event_vec) { - event_type_vector::iterator it_event; - it_event = event_vec.begin(); + auto it_event = event_vec.begin(); while (it_event != event_vec.end()) { - if (!cmd_unregister_event(*it_event)) + if(!cmd_unregister_event(*it_event)) return false; ++it_event; @@ -397,64 +480,34 @@ bool command_channel::cmd_unregister_events(event_type_vector &event_vec) return true; } -bool command_channel::cmd_check_event(unsigned int event_type) -{ - cpacket *packet; - cmd_check_event_t *cmd_check_event; - cmd_done_t *cmd_done; - - packet = new cpacket(sizeof(cmd_check_event_t)); - packet->set_cmd(CMD_CHECK_EVENT); - packet->set_payload_size(sizeof(cmd_check_event_t)); - cmd_check_event = (cmd_check_event_t *)packet->data(); - cmd_check_event->event_type = event_type; - - INFO("%s send cmd_check_event(client_id=%d, %s)", - get_client_name(), m_client_id, get_event_name(event_type)); - - if (!command_handler(packet, (void **)&cmd_done)) { - ERR("Client %s failed to send/receive command with client_id [%d], event_type[%s]", - get_client_name(), m_client_id, get_event_name(event_type)); - delete packet; - return false; - } - - if (cmd_done->value < 0) { - delete[] (char *)cmd_done; - delete packet; - return false; - } - - delete[] (char *)cmd_done; - delete packet; - return true; -} - bool command_channel::cmd_set_interval(unsigned int interval) { cpacket *packet; cmd_set_interval_t *cmd_set_interval; cmd_done_t *cmd_done; - packet = new cpacket(sizeof(cmd_set_interval_t)); + packet = new(std::nothrow) cpacket(sizeof(cmd_set_interval_t)); + retvm_if(!packet, false, "Failed to allocate memory"); + packet->set_cmd(CMD_SET_INTERVAL); - packet->set_payload_size(sizeof(cmd_set_interval_t)); - cmd_set_interval = (cmd_set_interval_t *)packet->data(); + + cmd_set_interval = (cmd_set_interval_t*)packet->data(); cmd_set_interval->interval = interval; INFO("%s send cmd_set_interval(client_id=%d, %s, interval=%d)", - get_client_name(), m_client_id, get_sensor_name(m_sensor_type), interval); + get_client_name(), m_client_id, get_sensor_name(m_sensor_id), interval); if (!command_handler(packet, (void **)&cmd_done)) { ERR("%s failed to send/receive command for sensor[%s] with client_id [%d], interval[%d]", - get_client_name(), get_sensor_name(m_sensor_type), m_client_id, interval); + get_client_name(), get_sensor_name(m_sensor_id), m_client_id, interval); delete packet; return false; } if (cmd_done->value < 0) { ERR("%s got error[%d] from server for sensor[%s] with client_id [%d], interval[%d]", - get_client_name(), cmd_done->value, get_sensor_name(m_sensor_type), m_client_id, interval); + get_client_name(), cmd_done->value, get_sensor_name(m_sensor_id), m_client_id, interval); + delete[] (char *)cmd_done; delete packet; return false; @@ -462,6 +515,7 @@ bool command_channel::cmd_set_interval(unsigned int interval) delete[] (char *)cmd_done; delete packet; + return true; } @@ -469,23 +523,26 @@ bool command_channel::cmd_unset_interval(void) { cpacket *packet; cmd_done_t *cmd_done; - packet = new cpacket(sizeof(cmd_unset_interval_t)); + + packet = new(std::nothrow) cpacket(sizeof(cmd_unset_interval_t)); + retvm_if(!packet, false, "Failed to allocate memory"); + packet->set_cmd(CMD_UNSET_INTERVAL); - packet->set_payload_size(sizeof(cmd_unset_interval_t)); INFO("%s send cmd_unset_interval(client_id=%d, %s)", - get_client_name(), m_client_id, get_sensor_name(m_sensor_type)); + get_client_name(), m_client_id, get_sensor_name(m_sensor_id)); if (!command_handler(packet, (void **)&cmd_done)) { ERR("Client %s failed to send/receive command for sensor[%s] with client_id [%d]", - get_client_name(), get_sensor_name(m_sensor_type), m_client_id); + get_client_name(), get_sensor_name(m_sensor_id), m_client_id); delete packet; return false; } if (cmd_done->value < 0) { ERR("Client %s got error[%d] from server for sensor[%s] with client_id [%d]", - get_client_name(), cmd_done->value, get_sensor_name(m_sensor_type), m_client_id); + get_client_name(), cmd_done->value, get_sensor_name(m_sensor_id), m_client_id); + delete[] (char *)cmd_done; delete packet; return false; @@ -493,67 +550,7 @@ bool command_channel::cmd_unset_interval(void) delete[] (char *)cmd_done; delete packet; - return true; -} - -static bool is_sensor_property(unsigned int type) -{ - return ((type & 0xFFFF) == 1); -} - -bool command_channel::cmd_get_properties(unsigned int type, void *properties) -{ - cpacket *packet; - cmd_get_properties_t *cmd_get_properties; - cmd_properties_done_t *cmd_properties_done; - packet = new cpacket(sizeof(cmd_get_properties_t)); - packet->set_cmd(CMD_GET_PROPERTIES); - packet->set_payload_size(sizeof(cmd_get_properties_t)); - cmd_get_properties = (cmd_get_properties_t *)packet->data(); - cmd_get_properties->type = type; - - INFO("%s send cmd_get_properties(client_id=%d, %s, %s)", - get_client_name(), m_client_id, get_sensor_name(m_sensor_type), get_data_name(type)); - - if (!command_handler(packet, (void **)&cmd_properties_done)) { - ERR("Client %s failed to send/receive command for sensor[%s] with client_id [%d], data_id[%s]", - get_client_name(), get_sensor_name(m_sensor_type), m_client_id, get_data_name(type)); - delete packet; - return false; - } - - if (cmd_properties_done->state < 0) { - ERR("Client %s got error[%d] from server for sensor[%s] with client_id [%d], data_id[%s]", - get_client_name(), cmd_properties_done->state, get_sensor_name(m_sensor_type), m_client_id, get_data_name(type)); - delete[] (char *)cmd_properties_done; - delete packet; - return false; - } - - sensor_properties_t *ret_properties; - ret_properties = &cmd_properties_done->properties; - - if (is_sensor_property(type)) { - sensor_properties_t *sensor_properties; - sensor_properties = (sensor_properties_t *)properties; - sensor_properties->sensor_unit_idx = ret_properties->sensor_unit_idx; - sensor_properties->sensor_min_range = ret_properties->sensor_min_range; - sensor_properties->sensor_max_range = ret_properties->sensor_max_range; - sensor_properties->sensor_resolution = ret_properties->sensor_resolution; - strncpy(sensor_properties->sensor_name, ret_properties->sensor_name, strlen(ret_properties->sensor_name)); - strncpy(sensor_properties->sensor_vendor, ret_properties->sensor_vendor, strlen(ret_properties->sensor_vendor)); - } else { - sensor_data_properties_t *data_properies; - data_properies = (sensor_data_properties_t *)properties; - data_properies->sensor_unit_idx = ret_properties->sensor_unit_idx ; - data_properies->sensor_min_range = ret_properties->sensor_min_range; - data_properies->sensor_max_range = ret_properties->sensor_max_range; - data_properies->sensor_resolution = ret_properties->sensor_resolution; - } - - delete[] (char *)cmd_properties_done; - delete packet; return true; } @@ -563,26 +560,30 @@ bool command_channel::cmd_set_command(unsigned int cmd, long value) cmd_set_command_t *cmd_set_command; cmd_done_t *cmd_done; - packet = new cpacket(sizeof(cmd_set_command_t)); + packet = new(std::nothrow) cpacket(sizeof(cmd_set_command_t)); + retvm_if(!packet, false, "Failed to allocate memory"); + packet->set_cmd(CMD_SET_COMMAND); - packet->set_payload_size(sizeof(cmd_set_command_t)); - cmd_set_command = (cmd_set_command_t *)packet->data(); + + cmd_set_command = (cmd_set_command_t*)packet->data(); cmd_set_command->cmd = cmd; cmd_set_command->value = value; + INFO("%s send cmd_set_command(client_id=%d, %s, 0x%x, %ld)", - get_client_name(), m_client_id, get_sensor_name(m_sensor_type), cmd, value); + get_client_name(), m_client_id, get_sensor_name(m_sensor_id), cmd, value); if (!command_handler(packet, (void **)&cmd_done)) { ERR("Client %s failed to send/receive command for sensor[%s] with client_id [%d], property[0x%x], value[%d]", - get_client_name(), get_sensor_name(m_sensor_type), m_client_id, cmd, value); + get_client_name(), get_sensor_name(m_sensor_id), m_client_id, cmd, value); delete packet; return false; } if (cmd_done->value < 0) { ERR("Client %s got error[%d] from server for sensor[%s] with property[0x%x], value[%d]", - get_client_name(), cmd_done->value, get_sensor_name(m_sensor_type), cmd, value); + get_client_name(), cmd_done->value, get_sensor_name(m_sensor_id), cmd, value); + delete[] (char *)cmd_done; delete packet; return false; @@ -590,19 +591,22 @@ bool command_channel::cmd_set_command(unsigned int cmd, long value) delete[] (char *)cmd_done; delete packet; + return true; } -bool command_channel::cmd_get_data(unsigned int type, sensor_data_t *sensor_data) +bool command_channel::cmd_get_data(unsigned int type, sensor_data_t* sensor_data) { cpacket *packet; cmd_get_data_t *cmd_get_data; cmd_get_data_done_t *cmd_get_data_done; - packet = new cpacket(sizeof(cmd_get_data_done_t)); + packet = new(std::nothrow) cpacket(sizeof(cmd_get_data_done_t)); + retvm_if(!packet, false, "Failed to allocate memory"); + packet->set_cmd(CMD_GET_DATA); - packet->set_payload_size(sizeof(cmd_get_data_t)); - cmd_get_data = (cmd_get_data_t *)packet->data(); + + cmd_get_data = (cmd_get_data_t*)packet->data(); cmd_get_data->type = type; if (!command_handler(packet, (void **)&cmd_get_data_done)) { @@ -612,14 +616,12 @@ bool command_channel::cmd_get_data(unsigned int type, sensor_data_t *sensor_data return false; } - if (cmd_get_data_done->state < 0) { + if (cmd_get_data_done->state < 0 ) { ERR("Client %s got error[%d] from server with client_id [%d], data_id[%s]", get_client_name(), cmd_get_data_done->state, m_client_id, get_data_name(type)); - sensor_data->data_accuracy = SENSOR_ACCURACY_UNDEFINED; - sensor_data->data_unit_idx = SENSOR_UNDEFINED_UNIT; + sensor_data->accuracy = SENSOR_ACCURACY_UNDEFINED; sensor_data->timestamp = 0; - sensor_data->values_num = 0; - + sensor_data->value_count = 0; delete[] (char *)cmd_get_data_done; delete packet; return false; @@ -627,30 +629,36 @@ bool command_channel::cmd_get_data(unsigned int type, sensor_data_t *sensor_data sensor_data_t *base_data; base_data = &cmd_get_data_done->base_data; + sensor_data->timestamp = base_data->timestamp; - sensor_data->data_accuracy = base_data->data_accuracy; - sensor_data->data_unit_idx = base_data->data_unit_idx; - sensor_data->values_num = base_data->values_num; + sensor_data->accuracy = base_data->accuracy; + sensor_data->value_count = base_data->value_count; + memcpy(sensor_data->values, base_data->values, - sizeof(sensor_data->values[0]) * base_data->values_num); + sizeof(sensor_data->values[0]) * base_data->value_count); delete[] (char *)cmd_get_data_done; delete packet; + return true; } -bool command_channel::cmd_send_sensorhub_data(int data_len, const char *buffer) +bool command_channel::cmd_send_sensorhub_data(const char* buffer, int data_len) { cpacket *packet; cmd_send_sensorhub_data_t *cmd_send_sensorhub_data; cmd_done_t *cmd_done; - packet = new cpacket(sizeof(cmd_send_sensorhub_data_t) + data_len); + packet = new(std::nothrow) cpacket(sizeof(cmd_send_sensorhub_data_t) + data_len); + retvm_if(!packet, false, "Failed to allocate memory"); + packet->set_cmd(CMD_SEND_SENSORHUB_DATA); - cmd_send_sensorhub_data = (cmd_send_sensorhub_data_t *)packet->data(); + + cmd_send_sensorhub_data = (cmd_send_sensorhub_data_t*)packet->data(); cmd_send_sensorhub_data->data_len = data_len; memcpy(cmd_send_sensorhub_data->data, buffer, data_len); + INFO("%s send cmd_send_sensorhub_data(client_id=%d, data_len = %d, buffer = 0x%x)", get_client_name(), m_client_id, data_len, buffer); @@ -664,6 +672,7 @@ bool command_channel::cmd_send_sensorhub_data(int data_len, const char *buffer) if (cmd_done->value < 0) { ERR("%s got error[%d] from server with client_id [%d]", get_client_name(), cmd_done->value, m_client_id); + delete[] (char *)cmd_done; delete packet; return false; @@ -671,5 +680,8 @@ bool command_channel::cmd_send_sensorhub_data(int data_len, const char *buffer) delete[] (char *)cmd_done; delete packet; + return true; + + } diff --git a/src/libsensord/command_channel.h b/src/libsensord/command_channel.h index 13d84e1..2688c15 100755 --- a/src/libsensord/command_channel.h +++ b/src/libsensord/command_channel.h @@ -20,7 +20,7 @@ #ifndef _COMMAND_CHANNEL_H_ #define _COMMAND_CHANNEL_H_ #include -#include +#include #include #include #include @@ -38,7 +38,8 @@ public: void set_client_id(int client_id); bool cmd_get_id(int &client_id); - bool cmd_hello(sensor_type_t sensor); + bool cmd_get_sensor_list(void); + bool cmd_hello(sensor_id_t sensor); bool cmd_byebye(void); bool cmd_start(void); bool cmd_stop(void); @@ -47,17 +48,15 @@ public: bool cmd_register_events(event_type_vector &event_vec); bool cmd_unregister_event(unsigned int event_type); bool cmd_unregister_events(event_type_vector &event_vec); - bool cmd_check_event(unsigned int event_type); bool cmd_set_interval(unsigned int interval); bool cmd_unset_interval(void); - bool cmd_get_properties(unsigned int type, void *properties); bool cmd_set_command(unsigned int cmd, long value); - bool cmd_get_data(unsigned int type, sensor_data_t *values); - bool cmd_send_sensorhub_data(int data_len, const char *buffer); + bool cmd_get_data(unsigned int type, sensor_data_t* values); + bool cmd_send_sensorhub_data(const char* buffer, int data_len); private: csocket m_command_socket; int m_client_id; - sensor_type_t m_sensor_type; + sensor_id_t m_sensor_id; bool command_handler(cpacket *packet, void **return_payload); }; diff --git a/src/libsensord/creg_event_info.h b/src/libsensord/creg_event_info.h index 9ccf473..82f23bd 100755 --- a/src/libsensord/creg_event_info.h +++ b/src/libsensord/creg_event_info.h @@ -20,27 +20,34 @@ #ifndef CREG_EVENT_INFO_H_ #define CREG_EVENT_INFO_H_ -#include +#include #include -class creg_event_info -{ +typedef enum { + SENSOR_EVENT_CB, + SENSORHUB_EVENT_CB, + SENSOR_LEGACY_CB, +} event_cb_type_t; + +class creg_event_info { public: unsigned long long m_id; int m_handle; - unsigned int m_event_type; - unsigned int m_event_interval; - sensor_callback_func_t m_event_callback; - void *m_cb_data; + unsigned int type; + unsigned int m_interval; + int m_cb_type; + void *m_cb; + void *m_user_data; unsigned long long m_previous_event_time; bool m_fired; - creg_event_info(): m_id(0), m_handle(-1), - m_event_type(0), m_event_interval(POLL_1HZ_MS), - m_event_callback(NULL), m_cb_data(NULL), - m_previous_event_time(0), m_fired(false) {} + creg_event_info():m_id(0), m_handle(-1), + type(0), m_interval(POLL_1HZ_MS), + m_cb_type(SENSOR_EVENT_CB), m_cb(NULL), m_user_data(NULL), + m_previous_event_time(0), m_fired(false){} - ~creg_event_info() {} + ~creg_event_info(){} }; + #endif /* CREG_EVENT_INFO_H_ */ diff --git a/src/libsensord/csensor_event_listener.cpp b/src/libsensord/csensor_event_listener.cpp index ae0fd4f..e093f21 100755 --- a/src/libsensord/csensor_event_listener.cpp +++ b/src/libsensord/csensor_event_listener.cpp @@ -20,6 +20,8 @@ #include #include #include +#include + #include #include @@ -30,6 +32,7 @@ csensor_event_listener::csensor_event_listener() : m_client_id(CLIENT_ID_INVALID) , m_thread_state(THREAD_STATE_TERMINATE) , m_poller(NULL) +, m_hup_observer(NULL) { } @@ -38,7 +41,15 @@ csensor_event_listener::~csensor_event_listener() stop_event_listener(); } -int csensor_event_listener::create_handle(const sensor_type_t sensor) + +csensor_event_listener& csensor_event_listener::get_instance(void) +{ + static csensor_event_listener inst; + return inst; +} + + +int csensor_event_listener::create_handle(sensor_id_t sensor) { csensor_handle_info handle_info; int handle = 0; @@ -53,20 +64,24 @@ int csensor_event_listener::create_handle(const sensor_type_t sensor) return MAX_HANDLE_REACHED; } - handle_info.m_sensor_type = sensor; + handle_info.m_sensor_id = sensor; handle_info.m_sensor_state = SENSOR_STATE_STOPPED; handle_info.m_sensor_option = SENSOR_OPTION_DEFAULT; handle_info.m_handle = handle; - m_sensor_handle_infos.insert(pair (handle, handle_info)); + handle_info.m_accuracy = -1; + handle_info.m_accuracy_cb = NULL; + handle_info.m_accuracy_user_data = NULL; + + m_sensor_handle_infos.insert(pair (handle, handle_info)); + return handle; } -bool csensor_event_listener::delete_handle(const int handle) +bool csensor_event_listener::delete_handle(int handle) { - sensor_handle_info_map::iterator it_handle; - AUTOLOCK(m_handle_info_lock); - it_handle = m_sensor_handle_infos.find(handle); + + auto it_handle = m_sensor_handle_infos.find(handle); if (it_handle == m_sensor_handle_infos.end()) { ERR("Handle[%d] is not found for client %s", handle, get_client_name()); @@ -77,6 +92,7 @@ bool csensor_event_listener::delete_handle(const int handle) return true; } + bool csensor_event_listener::is_active() { AUTOLOCK(m_handle_info_lock); @@ -84,41 +100,39 @@ bool csensor_event_listener::is_active() return !m_sensor_handle_infos.empty(); } -bool csensor_event_listener::start_handle(const int handle) +bool csensor_event_listener::start_handle(int handle) { return set_sensor_state(handle, SENSOR_STATE_STARTED); } -bool csensor_event_listener::stop_handle(const int handle) +bool csensor_event_listener::stop_handle(int handle) { return set_sensor_state(handle, SENSOR_STATE_STOPPED); } -bool csensor_event_listener::register_event(const int handle, const unsigned int event_type, - const unsigned int interval, const sensor_callback_func_t callback, void *cb_data) +bool csensor_event_listener::register_event(int handle, unsigned int event_type, + unsigned int interval, int cb_type, void *cb, void* user_data) { - sensor_handle_info_map::iterator it_handle; - AUTOLOCK(m_handle_info_lock); - it_handle = m_sensor_handle_infos.find(handle); + + auto it_handle = m_sensor_handle_infos.find(handle); if (it_handle == m_sensor_handle_infos.end()) { ERR("Handle[%d] is not found for client %s", handle, get_client_name()); return false; } - if (!it_handle->second.add_reg_event_info(event_type, interval, callback, cb_data)) + if (!it_handle->second.add_reg_event_info(event_type, interval, cb_type, cb, user_data)) return false; return true; } -bool csensor_event_listener::unregister_event(const int handle, const unsigned int event_type) +bool csensor_event_listener::unregister_event(int handle, unsigned int event_type) { - sensor_handle_info_map::iterator it_handle; - AUTOLOCK(m_handle_info_lock); - it_handle = m_sensor_handle_infos.find(handle); + + auto it_handle = m_sensor_handle_infos.find(handle); if (it_handle == m_sensor_handle_infos.end()) { ERR("Handle[%d] is not found for client %s", handle, get_client_name()); @@ -131,13 +145,12 @@ bool csensor_event_listener::unregister_event(const int handle, const unsigned i return true; } -bool csensor_event_listener::change_event_interval(const int handle, const unsigned int event_type, - const unsigned int interval) +bool csensor_event_listener::change_event_interval(int handle, unsigned int event_type, + unsigned int interval) { - sensor_handle_info_map::iterator it_handle; - AUTOLOCK(m_handle_info_lock); - it_handle = m_sensor_handle_infos.find(handle); + + auto it_handle = m_sensor_handle_infos.find(handle); if (it_handle == m_sensor_handle_infos.end()) { ERR("Handle[%d] is not found for client %s", handle, get_client_name()); @@ -150,12 +163,47 @@ bool csensor_event_listener::change_event_interval(const int handle, const unsig return true; } -bool csensor_event_listener::set_sensor_params(const int handle, int sensor_state, int sensor_option) +bool csensor_event_listener::register_accuracy_cb(int handle, sensor_accuracy_changed_cb_t cb, void* user_data) +{ + AUTOLOCK(m_handle_info_lock); + + auto it_handle = m_sensor_handle_infos.find(handle); + + if (it_handle == m_sensor_handle_infos.end()) { + ERR("Handle[%d] is not found for client %s", handle, get_client_name()); + return false; + } + + it_handle->second.m_accuracy = -1; + it_handle->second.m_accuracy_cb = cb; + it_handle->second.m_accuracy_user_data = user_data; + + return true; +} + +bool csensor_event_listener::unregister_accuracy_cb(int handle) { - sensor_handle_info_map::iterator it_handle; + AUTOLOCK(m_handle_info_lock); + + auto it_handle = m_sensor_handle_infos.find(handle); + + if (it_handle == m_sensor_handle_infos.end()) { + ERR("Handle[%d] is not found for client %s", handle, get_client_name()); + return false; + } + + it_handle->second.m_accuracy = -1; + it_handle->second.m_accuracy_cb = NULL; + it_handle->second.m_accuracy_user_data = NULL; + + return true; +} +bool csensor_event_listener::set_sensor_params(int handle, int sensor_state, int sensor_option) +{ AUTOLOCK(m_handle_info_lock); - it_handle = m_sensor_handle_infos.find(handle); + + auto it_handle = m_sensor_handle_infos.find(handle); if (it_handle == m_sensor_handle_infos.end()) { ERR("Handle[%d] is not found for client %s", handle, get_client_name()); @@ -168,12 +216,28 @@ bool csensor_event_listener::set_sensor_params(const int handle, int sensor_stat return true; } -bool csensor_event_listener::set_sensor_state(const int handle, const int sensor_state) +bool csensor_event_listener::get_sensor_params(int handle, int &sensor_state, int &sensor_option) { - sensor_handle_info_map::iterator it_handle; + AUTOLOCK(m_handle_info_lock); + + auto it_handle = m_sensor_handle_infos.find(handle); + if (it_handle == m_sensor_handle_infos.end()) { + ERR("Handle[%d] is not found for client %s", handle, get_client_name()); + return false; + } + + sensor_state = it_handle->second.m_sensor_state; + sensor_option = it_handle->second.m_sensor_option; + + return true; +} + +bool csensor_event_listener::set_sensor_state(int handle, int sensor_state) +{ AUTOLOCK(m_handle_info_lock); - it_handle = m_sensor_handle_infos.find(handle); + + auto it_handle = m_sensor_handle_infos.find(handle); if (it_handle == m_sensor_handle_infos.end()) { ERR("Handle[%d] is not found for client %s", handle, get_client_name()); @@ -185,12 +249,11 @@ bool csensor_event_listener::set_sensor_state(const int handle, const int sensor return true; } -bool csensor_event_listener::set_sensor_option(const int handle, const int sensor_option) +bool csensor_event_listener::set_sensor_option(int handle, int sensor_option) { - sensor_handle_info_map::iterator it_handle; - AUTOLOCK(m_handle_info_lock); - it_handle = m_sensor_handle_infos.find(handle); + + auto it_handle = m_sensor_handle_infos.find(handle); if (it_handle == m_sensor_handle_infos.end()) { ERR("Handle[%d] is not found for client %s", handle, get_client_name()); @@ -202,12 +265,11 @@ bool csensor_event_listener::set_sensor_option(const int handle, const int senso return true; } -bool csensor_event_listener::set_event_interval(const int handle, const unsigned int event_type, const unsigned int interval) +bool csensor_event_listener::set_event_interval(int handle, unsigned int event_type, unsigned int interval) { - sensor_handle_info_map::iterator it_handle; - AUTOLOCK(m_handle_info_lock); - it_handle = m_sensor_handle_infos.find(handle); + + auto it_handle = m_sensor_handle_infos.find(handle); if (it_handle == m_sensor_handle_infos.end()) { ERR("Handle[%d] is not found for client %s", handle, get_client_name()); @@ -220,23 +282,52 @@ bool csensor_event_listener::set_event_interval(const int handle, const unsigned return true; } -void csensor_event_listener::get_listening_sensors(sensor_type_vector &sensors) + +bool csensor_event_listener::get_event_info(int handle, unsigned int event_type, unsigned int &interval, int &cb_type, void* &cb, void* &user_data) { - sensor_handle_info_map::iterator it_handle; + AUTOLOCK(m_handle_info_lock); + auto it_handle = m_sensor_handle_infos.find(handle); + + if (it_handle == m_sensor_handle_infos.end()) { + ERR("Handle[%d] is not found for client %s", handle, get_client_name()); + return false; + } + + const creg_event_info *event_info; + + event_info = it_handle->second.get_reg_event_info(event_type); + + if (!event_info) + return NULL; + + + interval = event_info->m_interval; + cb_type = event_info->m_cb_type; + cb = event_info->m_cb; + user_data = event_info->m_user_data; + + return true; +} + + +void csensor_event_listener::get_listening_sensors(sensor_id_vector &sensors) +{ AUTOLOCK(m_handle_info_lock); - it_handle = m_sensor_handle_infos.begin(); + + auto it_handle = m_sensor_handle_infos.begin(); while (it_handle != m_sensor_handle_infos.end()) { - sensors.push_back(it_handle->second.m_sensor_type); + sensors.push_back(it_handle->second.m_sensor_id); ++it_handle; } sort(sensors.begin(), sensors.end()); - unique(sensors.begin(), sensors.end()); + unique(sensors.begin(),sensors.end()); } -void csensor_event_listener::get_sensor_rep(sensor_type_t sensor, sensor_rep &rep) + +void csensor_event_listener::get_sensor_rep(sensor_id_t sensor, sensor_rep& rep) { AUTOLOCK(m_handle_info_lock); @@ -244,61 +335,53 @@ void csensor_event_listener::get_sensor_rep(sensor_type_t sensor, sensor_rep &re rep.option = get_active_option(sensor); rep.interval = get_active_min_interval(sensor); get_active_event_types(sensor, rep.event_types); + } -void csensor_event_listener::pause_sensor(const sensor_type_t sensor) +void csensor_event_listener::operate_sensor(sensor_id_t sensor, int power_save_state) { - sensor_handle_info_map::iterator it_handle; AUTOLOCK(m_handle_info_lock); - it_handle = m_sensor_handle_infos.begin(); + + auto it_handle = m_sensor_handle_infos.begin(); while (it_handle != m_sensor_handle_infos.end()) { - if ((it_handle->second.m_sensor_type == sensor) && - (it_handle->second.m_sensor_state == SENSOR_STATE_STARTED) && - (it_handle->second.m_sensor_option != SENSOR_OPTION_ALWAYS_ON)) { + if (it_handle->second.m_sensor_id == sensor) { + if ((it_handle->second.m_sensor_state == SENSOR_STATE_STARTED) && + power_save_state && + !(it_handle->second.m_sensor_option & power_save_state)) { + it_handle->second.m_sensor_state = SENSOR_STATE_PAUSED; INFO("%s's %s[%d] is paused", get_client_name(), get_sensor_name(sensor), it_handle->first); - } - ++it_handle; - } -} + } else if ((it_handle->second.m_sensor_state == SENSOR_STATE_PAUSED) && + (!power_save_state || (it_handle->second.m_sensor_option & power_save_state))) { -void csensor_event_listener::resume_sensor(const sensor_type_t sensor) -{ - sensor_handle_info_map::iterator it_handle; - AUTOLOCK(m_handle_info_lock); - it_handle = m_sensor_handle_infos.begin(); - - while (it_handle != m_sensor_handle_infos.end()) { - if ((it_handle->second.m_sensor_type == sensor) && - (it_handle->second.m_sensor_state == SENSOR_STATE_PAUSED)) { it_handle->second.m_sensor_state = SENSOR_STATE_STARTED; INFO("%s's %s[%d] is resumed", get_client_name(), get_sensor_name(sensor), it_handle->first); + } } ++it_handle; } } -bool csensor_event_listener::set_command_channel(sensor_type_t sensor, command_channel *cmd_channel) +bool csensor_event_listener::add_command_channel(sensor_id_t sensor, command_channel *cmd_channel) { - sensor_command_channel_map::iterator it_channel; - it_channel = m_command_channels.find(sensor); + auto it_channel = m_command_channels.find(sensor); if (it_channel != m_command_channels.end()) { ERR("%s alreay has command_channel for %s", get_client_name(), get_sensor_name(sensor)); return false; } - m_command_channels.insert(pair (sensor, cmd_channel)); + m_command_channels.insert(pair (sensor, cmd_channel)); + return true; -} -bool csensor_event_listener::get_command_channel(sensor_type_t sensor, command_channel **cmd_channel) +} +bool csensor_event_listener::get_command_channel(sensor_id_t sensor, command_channel **cmd_channel) { - sensor_command_channel_map::iterator it_channel; - it_channel = m_command_channels.find(sensor); + auto it_channel = m_command_channels.find(sensor); if (it_channel == m_command_channels.end()) { ERR("%s doesn't have command_channel for %s", get_client_name(), get_sensor_name(sensor)); @@ -306,24 +389,42 @@ bool csensor_event_listener::get_command_channel(sensor_type_t sensor, command_c } *cmd_channel = it_channel->second; + + return true; +} + + +bool csensor_event_listener::close_command_channel(void) +{ + auto it_channel = m_command_channels.begin(); + + if (it_channel != m_command_channels.end()) { + delete it_channel->second; + ++it_channel; + } + + m_command_channels.clear(); + return true; } -bool csensor_event_listener::close_command_channel(sensor_type_t sensor) +bool csensor_event_listener::close_command_channel(sensor_id_t sensor_id) { - sensor_command_channel_map::iterator it_channel; - it_channel = m_command_channels.find(sensor); + auto it_channel = m_command_channels.find(sensor_id); if (it_channel == m_command_channels.end()) { - ERR("%s doesn't have command_channel for %s", get_client_name(), get_sensor_name(sensor)); + ERR("%s doesn't have command_channel for %s", get_client_name(), get_sensor_name(sensor_id)); return false; } delete it_channel->second; + m_command_channels.erase(it_channel); + return true; } + bool csensor_event_listener::has_client_id(void) { return (m_client_id != CLIENT_ID_INVALID); @@ -334,23 +435,23 @@ int csensor_event_listener::get_client_id(void) return m_client_id; } -void csensor_event_listener::set_client_id(const int client_id) +void csensor_event_listener::set_client_id(int client_id) { m_client_id = client_id; } -unsigned int csensor_event_listener::get_active_min_interval(const sensor_type_t sensor) +unsigned int csensor_event_listener::get_active_min_interval(sensor_id_t sensor) { unsigned int min_interval = POLL_MAX_HZ_MS; bool active_sensor_found = false; unsigned int interval; AUTOLOCK(m_handle_info_lock); - sensor_handle_info_map::iterator it_handle; - it_handle = m_sensor_handle_infos.begin(); + + auto it_handle = m_sensor_handle_infos.begin(); while (it_handle != m_sensor_handle_infos.end()) { - if ((it_handle->second.m_sensor_type == sensor) && + if ((it_handle->second.m_sensor_id == sensor) && (it_handle->second.m_sensor_state == SENSOR_STATE_STARTED)) { active_sensor_found = true; interval = it_handle->second.get_min_interval(); @@ -364,19 +465,21 @@ unsigned int csensor_event_listener::get_active_min_interval(const sensor_type_t DBG("Active sensor[0x%x] is not found for client %s", sensor, get_client_name()); return (active_sensor_found) ? min_interval : 0; + } -unsigned int csensor_event_listener::get_active_option(const sensor_type_t sensor) +unsigned int csensor_event_listener::get_active_option(sensor_id_t sensor) { int active_option = SENSOR_OPTION_DEFAULT; bool active_sensor_found = false; int option; + AUTOLOCK(m_handle_info_lock); - sensor_handle_info_map::iterator it_handle; - it_handle = m_sensor_handle_infos.begin(); + + auto it_handle = m_sensor_handle_infos.begin(); while (it_handle != m_sensor_handle_infos.end()) { - if ((it_handle->second.m_sensor_type == sensor) && + if ((it_handle->second.m_sensor_id == sensor) && (it_handle->second.m_sensor_state == SENSOR_STATE_STARTED)) { active_sensor_found = true; option = it_handle->second.m_sensor_option; @@ -392,28 +495,27 @@ unsigned int csensor_event_listener::get_active_option(const sensor_type_t senso return active_option; } -bool csensor_event_listener::get_sensor_type(const int handle, sensor_type_t &sensor) +bool csensor_event_listener::get_sensor_id(int handle, sensor_id_t &sensor) { - sensor_handle_info_map::iterator it_handle; - AUTOLOCK(m_handle_info_lock); - it_handle = m_sensor_handle_infos.find(handle); + + auto it_handle = m_sensor_handle_infos.find(handle); if (it_handle == m_sensor_handle_infos.end()) { ERR("Handle[%d] is not found for client %s", handle, get_client_name()); return false; } - sensor = it_handle->second.m_sensor_type; + sensor = it_handle->second.m_sensor_id; + return true; } -bool csensor_event_listener::get_sensor_state(const int handle, int &sensor_state) +bool csensor_event_listener::get_sensor_state(int handle, int &sensor_state) { - sensor_handle_info_map::iterator it_handle; - AUTOLOCK(m_handle_info_lock); - it_handle = m_sensor_handle_infos.find(handle); + + auto it_handle = m_sensor_handle_infos.find(handle); if (it_handle == m_sensor_handle_infos.end()) { ERR("Handle[%d] is not found for client %s", handle, get_client_name()); @@ -421,20 +523,20 @@ bool csensor_event_listener::get_sensor_state(const int handle, int &sensor_stat } sensor_state = it_handle->second.m_sensor_state; + return true; } -void csensor_event_listener::get_active_event_types(const sensor_type_t sensor, - event_type_vector &active_event_types) +void csensor_event_listener::get_active_event_types(sensor_id_t sensor, event_type_vector &active_event_types) { event_type_vector event_types; AUTOLOCK(m_handle_info_lock); - sensor_handle_info_map::iterator it_handle; - it_handle = m_sensor_handle_infos.begin(); + + auto it_handle = m_sensor_handle_infos.begin(); while (it_handle != m_sensor_handle_infos.end()) { - if ((it_handle->second.m_sensor_type == sensor) && + if ((it_handle->second.m_sensor_id == sensor) && (it_handle->second.m_sensor_state == SENSOR_STATE_STARTED)) it_handle->second.get_reg_event_types(event_types); @@ -445,15 +547,17 @@ void csensor_event_listener::get_active_event_types(const sensor_type_t sensor, return; sort(event_types.begin(), event_types.end()); + unique_copy(event_types.begin(), event_types.end(), back_inserter(active_event_types)); + } + void csensor_event_listener::get_all_handles(handle_vector &handles) { - sensor_handle_info_map::iterator it_handle; - AUTOLOCK(m_handle_info_lock); - it_handle = m_sensor_handle_infos.begin(); + + auto it_handle = m_sensor_handle_infos.begin(); while (it_handle != m_sensor_handle_infos.end()) { handles.push_back(it_handle->first); @@ -461,15 +565,14 @@ void csensor_event_listener::get_all_handles(handle_vector &handles) } } -bool csensor_event_listener::is_sensor_registered(const sensor_type_t sensor) +bool csensor_event_listener::is_sensor_registered(sensor_id_t sensor) { - sensor_handle_info_map::iterator it_handle; - AUTOLOCK(m_handle_info_lock); - it_handle = m_sensor_handle_infos.begin(); + + auto it_handle = m_sensor_handle_infos.begin(); while (it_handle != m_sensor_handle_infos.end()) { - if (it_handle->second.m_sensor_type == sensor) + if (it_handle->second.m_sensor_id == sensor) return true; ++it_handle; @@ -478,16 +581,17 @@ bool csensor_event_listener::is_sensor_registered(const sensor_type_t sensor) return false; } -bool csensor_event_listener::is_sensor_active(const sensor_type_t sensor) + +bool csensor_event_listener::is_sensor_active(sensor_id_t sensor) { - sensor_handle_info_map::iterator it_handle; AUTOLOCK(m_handle_info_lock); - it_handle = m_sensor_handle_infos.begin(); + + auto it_handle = m_sensor_handle_infos.begin(); while (it_handle != m_sensor_handle_infos.end()) { - if ((it_handle->second.m_sensor_type == sensor) && + if ((it_handle->second.m_sensor_id == sensor) && (it_handle->second.m_sensor_state == SENSOR_STATE_STARTED)) - return true; + return true; ++it_handle; } @@ -495,45 +599,94 @@ bool csensor_event_listener::is_sensor_active(const sensor_type_t sensor) return false; } -void csensor_event_listener::handle_events(void *event) +client_callback_info* csensor_event_listener::handle_calibration_cb(csensor_handle_info &handle_info, unsigned event_type, unsigned long long time, int accuracy) +{ + unsigned int cal_event_type = get_calibration_event_type(event_type); + creg_event_info *event_info = NULL; + creg_event_info *cal_event_info = NULL; + client_callback_info* cal_callback_info = NULL; + + if (!cal_event_type) + return NULL; + + cal_event_info = handle_info.get_reg_event_info(cal_event_type); + if ((accuracy == SENSOR_ACCURACY_BAD) && !handle_info.m_bad_accuracy && cal_event_info) { + sensor_event_data_t cal_event_data; + sensor_data_t cal_data; + void *cal_sensor_data; + + cal_event_info->m_previous_event_time = time; + + event_info = handle_info.get_reg_event_info(event_type); + if (!event_info) + return NULL; + + if (event_info->m_cb_type == SENSOR_LEGACY_CB) { + cal_event_data.event_data = (void *)&(accuracy); + cal_event_data.event_data_size = sizeof(accuracy); + cal_sensor_data = &cal_event_data; + } else { + cal_data.accuracy = accuracy; + cal_data.timestamp = time; + cal_data.values[0] = accuracy; + cal_data.value_count = 1; + cal_sensor_data = &cal_data; + } + + cal_callback_info = get_callback_info(handle_info.m_sensor_id, cal_event_info, cal_sensor_data); + + handle_info.m_bad_accuracy = true; + + print_event_occurrence_log(handle_info, cal_event_info); + } + + if ((accuracy != SENSOR_ACCURACY_BAD) && handle_info.m_bad_accuracy) + handle_info.m_bad_accuracy = false; + + return cal_callback_info; +} + + +void csensor_event_listener::handle_events(void* event) { const unsigned int MS_TO_US = 1000; const float MIN_DELIVERY_DIFF_FACTOR = 0.75f; unsigned long long cur_time; - long long diff_time; - creg_event_info event_info; + creg_event_info *event_info = NULL; sensor_event_data_t event_data; - int situation; + sensor_id_t sensor_id; + void *sensor_data; - sensor_data_t sensor_data; - sensorhub_data_t sensorhub_data; sensor_panning_data_t panning_data; int single_state_event_data = 0; - int data_accuracy = SENSOR_ACCURACY_GOOD; + + int accuracy = SENSOR_ACCURACY_GOOD; unsigned int event_type = *((unsigned int *)(event)); bool is_hub_event = is_sensorhub_event(event_type); - client_callback_info *callback_info; + client_callback_info* callback_info = NULL; vector client_callback_infos; - sensor_handle_info_map::iterator it_handle; if (is_hub_event) { sensorhub_event_t *sensor_hub_event = (sensorhub_event_t *)event; - sensorhub_event_to_hub_data(*sensor_hub_event, sensorhub_data); - event_data.event_data = &sensorhub_data; - event_data.event_data_size = sizeof(sensorhub_data); - situation = sensor_hub_event->situation; + sensor_id = sensor_hub_event->sensor_id; + sensor_data = &(sensor_hub_event->data); cur_time = sensor_hub_event->data.timestamp; + + event_data.event_data = &(sensor_hub_event->data); + event_data.event_data_size = sizeof(sensor_hub_event->data); } else { sensor_event_t *sensor_event = (sensor_event_t *)event; - situation = sensor_event->situation; + sensor_id = sensor_event->sensor_id; + sensor_data = &(sensor_event->data); cur_time = sensor_event->data.timestamp; + accuracy = sensor_event->data.accuracy; if (is_single_state_event(event_type)) { single_state_event_data = (int) sensor_event->data.values[0]; - event_data.event_data = (void *) & (single_state_event_data); + event_data.event_data = (void *)&(single_state_event_data); event_data.event_data_size = sizeof(single_state_event_data); } else if (is_panning_event(event_type)) { panning_data.x = (int)sensor_event->data.values[0]; @@ -541,117 +694,170 @@ void csensor_event_listener::handle_events(void *event) event_data.event_data = (void *)&panning_data; event_data.event_data_size = sizeof(panning_data); } else { - sensor_event_to_data(*sensor_event, sensor_data); - event_data.event_data = (void *)&sensor_data; - event_data.event_data_size = sizeof(sensor_data); - data_accuracy = sensor_event->data.data_accuracy; + event_data.event_data = &(sensor_event->data); + event_data.event_data_size = sizeof(sensor_event->data); } } { /* scope for the lock */ AUTOLOCK(m_handle_info_lock); - for (it_handle = m_sensor_handle_infos.begin(); it_handle != m_sensor_handle_infos.end(); ++it_handle) { - csensor_handle_info &sensor_handle_info = it_handle->second; + for (auto it_handle = m_sensor_handle_infos.begin(); it_handle != m_sensor_handle_infos.end(); ++it_handle) { - if ((sensor_handle_info.m_sensor_state != SENSOR_STATE_STARTED) || !sensor_handle_info.get_reg_event_info(event_type, event_info)) - continue; + csensor_handle_info &sensor_handle_info = it_handle->second; - if ((sensor_handle_info.m_sensor_option != SENSOR_OPTION_ALWAYS_ON) && - ((situation == SITUATION_LCD_OFF) || (situation == SITUATION_SURVIVAL_MODE))) + event_info = sensor_handle_info.get_reg_event_info(event_type); + if ((sensor_handle_info.m_sensor_id != sensor_id) || + (sensor_handle_info.m_sensor_state != SENSOR_STATE_STARTED) || + !event_info) continue; - if (event_info.m_fired) + if (event_info->m_fired) continue; - diff_time = cur_time - event_info.m_previous_event_time; + event_info->m_previous_event_time = cur_time; - if ((diff_time >= event_info.m_event_interval * MS_TO_US * MIN_DELIVERY_DIFF_FACTOR) || ((diff_time > 0) && !is_ontime_event(event_type))) { - unsigned int cal_event_type; - creg_event_info cal_event_info; - event_info.m_previous_event_time = cur_time; - cal_event_type = get_calibration_event_type(event_type); + client_callback_info* cal_callback_info = handle_calibration_cb(sensor_handle_info, event_type, cur_time, accuracy); - if (cal_event_type) { - if ((data_accuracy == SENSOR_ACCURACY_BAD) && !sensor_handle_info.bad_accuracy && - sensor_handle_info.get_reg_event_info(cal_event_type, cal_event_info)) { - sensor_event_data_t cal_event_data; - client_callback_info *cal_callback_info; + if (cal_callback_info) + client_callback_infos.push_back(cal_callback_info); - cal_event_info.m_previous_event_time = cur_time; - cal_event_data.event_data = (void *) & (data_accuracy); - cal_event_data.event_data_size = sizeof(data_accuracy); - cal_callback_info = get_callback_info(cal_event_info, cal_event_data); - client_callback_infos.push_back(cal_callback_info); - sensor_handle_info.bad_accuracy = true; + if (event_info->m_cb_type == SENSOR_LEGACY_CB) + callback_info = get_callback_info(sensor_id, event_info, &event_data); + else + callback_info = get_callback_info(sensor_id, event_info, sensor_data); - print_event_occurrence_log(sensor_handle_info, cal_event_info, cal_event_data); - } + if (!callback_info) { + ERR("Failed to get callback_info"); + continue; + } - if ((data_accuracy != SENSOR_ACCURACY_BAD) && sensor_handle_info.bad_accuracy) - sensor_handle_info.bad_accuracy = false; - } + if (sensor_handle_info.m_accuracy != accuracy) { + sensor_handle_info.m_accuracy = accuracy; - callback_info = get_callback_info(event_info, event_data); - client_callback_infos.push_back(callback_info); + callback_info->accuracy_cb = sensor_handle_info.m_accuracy_cb; + callback_info->timestamp = cur_time; + callback_info->accuracy = accuracy; + callback_info->accuracy_user_data = sensor_handle_info.m_accuracy_user_data; + } - if (is_one_shot_event(event_type)) - event_info.m_fired = true; + client_callback_infos.push_back(callback_info); - print_event_occurrence_log(sensor_handle_info, event_info, event_data); - } + if (is_one_shot_event(event_type)) + event_info->m_fired = true; + + print_event_occurrence_log(sensor_handle_info, event_info); } } - vector::iterator it_calback_info; - it_calback_info = client_callback_infos.begin(); + auto it_calback_info = client_callback_infos.begin(); while (it_calback_info != client_callback_infos.end()) { post_callback_to_main_loop(*it_calback_info); ++it_calback_info; } + } -client_callback_info *csensor_event_listener::get_callback_info(creg_event_info &event_info, sensor_event_data_t &event_data) + +client_callback_info* csensor_event_listener::get_callback_info(sensor_id_t sensor_id, const creg_event_info *event_info, void* sensor_data) { - client_callback_info *callback_info; - callback_info = new client_callback_info; - callback_info->event_id = event_info.m_id; - callback_info->handle = event_info.m_handle; - callback_info->callback = event_info.m_event_callback; - callback_info->event_type = event_info.m_event_type; - callback_info->event_data.event_data_size = event_data.event_data_size; - callback_info->event_data.event_data = new char[event_data.event_data_size]; - memcpy(callback_info->event_data.event_data, event_data.event_data, event_data.event_data_size); - callback_info->data = event_info.m_cb_data; + client_callback_info* callback_info; + + callback_info = new(std::nothrow)client_callback_info; + retvm_if (!callback_info, NULL, "Failed to allocate memory"); + + callback_info->sensor = sensor_info_to_sensor(sensor_info_manager::get_instance().get_info(sensor_id)); + callback_info->event_id = event_info->m_id; + callback_info->handle = event_info->m_handle; + callback_info->cb_type = event_info->m_cb_type; + callback_info->cb = event_info->m_cb; + callback_info->event_type = event_info->type; + callback_info->user_data = event_info->m_user_data; + callback_info->accuracy_cb = NULL; + callback_info->timestamp = 0; + callback_info->accuracy = -1; + callback_info->accuracy_user_data = NULL; + + if (event_info->m_cb_type == SENSOR_EVENT_CB) { + callback_info->sensor_data = new(std::nothrow) char[sizeof(sensor_data_t)]; + + if (!callback_info->sensor_data) { + ERR("Failed to allocate memory"); + delete callback_info; + return NULL; + } + + copy_sensor_data((sensor_data_t*) callback_info->sensor_data, (sensor_data_t*) sensor_data); + } else if (event_info->m_cb_type == SENSORHUB_EVENT_CB) { + callback_info->sensor_data = new(std::nothrow) char[sizeof(sensorhub_data_t)]; + + if (!callback_info->sensor_data) { + ERR("Failed to allocate memory"); + delete callback_info; + return NULL; + } + + copy_sensorhub_data((sensorhub_data_t*) callback_info->sensor_data, (sensorhub_data_t*) sensor_data); + } else if(event_info->m_cb_type == SENSOR_LEGACY_CB) { + sensor_event_data_t *dest_sensor_data; + sensor_event_data_t *src_sensor_data = (sensor_event_data_t *)sensor_data; + callback_info->sensor_data = new(std::nothrow) char[sizeof(sensor_event_data_t)]; + + if (!callback_info->sensor_data) { + ERR("Failed to allocate memory"); + delete callback_info; + return NULL; + } + + dest_sensor_data = (sensor_event_data_t *) callback_info->sensor_data; + dest_sensor_data->event_data_size = src_sensor_data->event_data_size; + dest_sensor_data->event_data = new(std::nothrow) char[src_sensor_data->event_data_size]; + + if (!dest_sensor_data->event_data) { + ERR("Failed to allocate memory"); + delete[] (char *)(callback_info->sensor_data); + delete callback_info; + return NULL; + } + + if (is_sensorhub_event(event_info->type)) + copy_sensorhub_data((sensorhub_data_t*)dest_sensor_data->event_data, (sensorhub_data_t*)src_sensor_data->event_data); + else + memcpy(dest_sensor_data->event_data, src_sensor_data->event_data, src_sensor_data->event_data_size); + } + return callback_info; } -void csensor_event_listener::post_callback_to_main_loop(client_callback_info *cb_info) +void csensor_event_listener::post_callback_to_main_loop(client_callback_info* cb_info) { g_idle_add_full(G_PRIORITY_DEFAULT, callback_dispatcher, cb_info, NULL); } + bool csensor_event_listener::is_event_active(int handle, unsigned int event_type, unsigned long long event_id) { - sensor_handle_info_map::iterator it_handle; - creg_event_info event_info; + creg_event_info *event_info; AUTOLOCK(m_handle_info_lock); - it_handle = m_sensor_handle_infos.find(handle); + + auto it_handle = m_sensor_handle_infos.find(handle); if (it_handle == m_sensor_handle_infos.end()) return false; - if (!it_handle->second.get_reg_event_info(event_type, event_info)) + event_info = it_handle->second.get_reg_event_info(event_type); + if (!event_info) return false; - if (event_info.m_id != event_id) + if (event_info->m_id != event_id) return false; return true; } + bool csensor_event_listener::is_valid_callback(client_callback_info *cb_info) { return is_event_active(cb_info->handle, cb_info->event_type, cb_info->event_id); @@ -659,33 +865,49 @@ bool csensor_event_listener::is_valid_callback(client_callback_info *cb_info) gboolean csensor_event_listener::callback_dispatcher(gpointer data) { - client_callback_info *cb_info = (client_callback_info *) data; - - if (csensor_event_listener::get_instance().is_valid_callback(cb_info)) - cb_info->callback(cb_info->event_type, &cb_info->event_data, cb_info->data); - else + client_callback_info *cb_info = (client_callback_info*) data; + + if (csensor_event_listener::get_instance().is_valid_callback(cb_info)) { + if (cb_info->accuracy_cb) + cb_info->accuracy_cb(cb_info->sensor, cb_info->timestamp, cb_info->accuracy, cb_info->accuracy_user_data); + + if (cb_info->cb_type == SENSOR_EVENT_CB) + ((sensor_cb_t) cb_info->cb)(cb_info->sensor, cb_info->event_type, (sensor_data_t *) cb_info->sensor_data, cb_info->user_data); + else if (cb_info->cb_type == SENSORHUB_EVENT_CB) + ((sensorhub_cb_t) cb_info->cb)(cb_info->sensor, cb_info->event_type, (sensorhub_data_t *) cb_info->sensor_data, cb_info->user_data); + else if (cb_info->cb_type == SENSOR_LEGACY_CB) + ((sensor_legacy_cb_t) cb_info->cb)(cb_info->event_type, (sensor_event_data_t *) cb_info->sensor_data, cb_info->user_data); + } else { WARN("Discard invalid callback cb(0x%x)(%s, 0x%x, 0x%x) with id: %llu", - cb_info->callback, get_event_name(cb_info->event_type), &cb_info->event_data, - cb_info->data, cb_info->event_id); + cb_info->cb, get_event_name(cb_info->event_type), cb_info->sensor_data, + cb_info->user_data, cb_info->event_id); + } - delete[] (char *)(cb_info->event_data.event_data); + if (cb_info->cb_type == SENSOR_LEGACY_CB) { + sensor_event_data_t *data = (sensor_event_data_t *) cb_info->sensor_data; + delete[] (char *)data->event_data; + } + + delete[] (char*)(cb_info->sensor_data); delete cb_info; - /* - * To be called only once, it returns false - */ +/* +* To be called only once, it returns false +*/ return false; } -bool csensor_event_listener::sensor_event_poll(void *buffer, int buffer_len) + + +bool csensor_event_listener::sensor_event_poll(void* buffer, int buffer_len, int &event) { ssize_t len; + len = m_event_socket.recv(buffer, buffer_len); if (!len) { - if (!m_poller->poll()) + if(!m_poller->poll(event)) return false; - len = m_event_socket.recv(buffer, buffer_len); if (!len) { @@ -702,15 +924,17 @@ bool csensor_event_listener::sensor_event_poll(void *buffer, int buffer_len) return true; } + + void csensor_event_listener::listen_events(void) { sensorhub_event_t buffer; + int event; do { lock l(m_thread_mutex); - if (m_thread_state == THREAD_STATE_START) { - if (!sensor_event_poll(&buffer, sizeof(buffer))) { + if (!sensor_event_poll(&buffer, sizeof(buffer), event)) { INFO("sensor_event_poll failed"); break; } @@ -719,7 +943,6 @@ void csensor_event_listener::listen_events(void) } else { break; } - } while (true); if (m_poller != NULL) { @@ -728,12 +951,20 @@ void csensor_event_listener::listen_events(void) } close_event_channel(); - set_client_id(CLIENT_ID_INVALID); - lock l(m_thread_mutex); - m_thread_state = THREAD_STATE_TERMINATE; - m_thread_cond.notify_one(); + { /* the scope for the lock */ + lock l(m_thread_mutex); + m_thread_state = THREAD_STATE_TERMINATE; + m_thread_cond.notify_one(); + } + INFO("Event listener thread is terminated."); + + if (has_client_id() && (event & EPOLLHUP)) { + if (m_hup_observer) + m_hup_observer(); + } + } bool csensor_event_listener::create_event_channel(void) @@ -750,6 +981,7 @@ bool csensor_event_listener::create_event_channel(void) } m_event_socket.set_connection_mode(); + client_id = get_client_id(); if (m_event_socket.send(&client_id, sizeof(client_id)) <= 0) { @@ -771,24 +1003,29 @@ bool csensor_event_listener::create_event_channel(void) INFO("Event channel is established for client %s on socket[%d] with client id : %d", get_client_name(), m_event_socket.get_socket_fd(), client_id); + return true; } + void csensor_event_listener::close_event_channel(void) { m_event_socket.close(); } + void csensor_event_listener::stop_event_listener(void) { const int THREAD_TERMINATING_TIMEOUT = 2; + ulock u(m_thread_mutex); if (m_thread_state != THREAD_STATE_TERMINATE) { m_thread_state = THREAD_STATE_STOP; - _D("%s is waiting listener thread[state: %d] to be terminated", get_client_name(), m_thread_state); - if (m_thread_cond.wait_for(u, std::chrono::seconds(THREAD_TERMINATING_TIMEOUT)) == std::cv_status::timeout) + _D("%s is waiting listener thread[state: %d] to be terminated", get_client_name(), m_thread_state); + if (m_thread_cond.wait_for(u, std::chrono::seconds(THREAD_TERMINATING_TIMEOUT)) + == std::cv_status::timeout) _E("Fail to stop listener thread after waiting %d seconds", THREAD_TERMINATING_TIMEOUT); else _D("Listener thread for %s is terminated", get_client_name()); @@ -801,6 +1038,21 @@ void csensor_event_listener::set_thread_state(thread_state state) m_thread_state = state; } +void csensor_event_listener::clear(void) +{ + close_event_channel(); + stop_event_listener(); + close_command_channel(); + m_sensor_handle_infos.clear(); + set_client_id(CLIENT_ID_INVALID); +} + + +void csensor_event_listener::set_hup_observer(hup_observer_t observer) +{ + m_hup_observer = observer; +} + bool csensor_event_listener::start_event_listener(void) { if (!create_event_channel()) { @@ -809,10 +1061,14 @@ bool csensor_event_listener::start_event_listener(void) } m_event_socket.set_transfer_mode(); - m_poller = new poller(m_event_socket.get_socket_fd()); + + m_poller = new(std::nothrow) poller(m_event_socket.get_socket_fd()); + retvm_if (!m_poller, false, "Failed to allocate memory"); set_thread_state(THREAD_STATE_START); + thread listener(&csensor_event_listener::listen_events, this); listener.detach(); + return true; } diff --git a/src/libsensord/csensor_event_listener.h b/src/libsensord/csensor_event_listener.h index 99ce5a3..0f7ac49 100755 --- a/src/libsensord/csensor_event_listener.h +++ b/src/libsensord/csensor_event_listener.h @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include #include @@ -38,7 +38,7 @@ #include #include -using std::map; +using std::unordered_map; using std::vector; using std::string; using std::queue; @@ -48,85 +48,87 @@ using std::unique_lock; using std::condition_variable; typedef vector handle_vector; -typedef vector sensor_type_vector; -typedef map> handle_events_map; -typedef map sensor_handle_info_map; -typedef map sensor_command_channel_map; +typedef vector sensor_id_vector; +typedef unordered_map sensor_handle_info_map; +typedef unordered_map sensor_command_channel_map; typedef struct { unsigned long long event_id; int handle; - sensor_callback_func_t callback; + sensor_t sensor; unsigned int event_type; - sensor_event_data_t event_data; - void *data; + int cb_type; + void *cb; + void *sensor_data; + void *user_data; + sensor_accuracy_changed_cb_t accuracy_cb; + unsigned long long timestamp; + int accuracy; + void *accuracy_user_data; } client_callback_info; -typedef struct sensor_rep { +typedef struct sensor_rep +{ bool active; int option; unsigned int interval; event_type_vector event_types; } sensor_rep; -class csensor_event_listener -{ +typedef void (*hup_observer_t)(void); + +class csensor_event_listener { public: - int create_handle(const sensor_type_t sensor); - bool delete_handle(const int handle); - bool start_handle(const int handle); - bool stop_handle(const int handle); - bool register_event(const int handle, const unsigned int event_type, - const unsigned int interval, - const sensor_callback_func_t callback, void *cb_data); - bool unregister_event(const int handle, const unsigned int event_type); - bool change_event_interval(const int handle, const unsigned int event_type, - const unsigned int interval); - - bool set_sensor_params(const int handle, int sensor_state, int sensor_option); - bool set_sensor_state(const int handle, const int sensor_state); - bool set_sensor_option(const int handle, const int sensor_option); - bool set_event_interval(const int handle, const unsigned int event_type, - const unsigned int interval); - - void get_listening_sensors(sensor_type_vector &sensors); - - void pause_sensor(const sensor_type_t sensor); - void resume_sensor(const sensor_type_t sensor); - - unsigned int get_active_min_interval(const sensor_type_t sensor); - unsigned int get_active_option(const sensor_type_t sensor); - void get_active_event_types(const sensor_type_t sensor, - event_type_vector &active_event_types); - - bool get_sensor_type(const int handle, sensor_type_t &sensor); - bool get_sensor_state(const int handle, int &state); - - void get_sensor_rep(sensor_type_t sensor, sensor_rep &rep); + static csensor_event_listener& get_instance(void); + int create_handle(sensor_id_t sensor_id); + bool delete_handle(int handle); + bool start_handle(int handle); + bool stop_handle(int handle); + bool register_event(int handle, unsigned int event_type, unsigned int interval, int cb_type, void *cb, void* user_data); + bool unregister_event(int handle, unsigned int event_type); + bool change_event_interval(int handle, unsigned int event_type, unsigned int interval); + + bool register_accuracy_cb(int handle, sensor_accuracy_changed_cb_t cb, void* user_data); + bool unregister_accuracy_cb(int handle); + + bool set_sensor_params(int handle, int sensor_state, int sensor_option); + bool get_sensor_params(int handle, int &sensor_state, int &sensor_option); + bool set_sensor_state(int handle, int sensor_state); + bool set_sensor_option(int handle, int sensor_option); + bool set_event_interval(int handle, unsigned int event_type, unsigned int interval); + bool get_event_info(int handle, unsigned int event_type, unsigned int &interval, int &cb_type, void* &cb, void* &user_data); + void operate_sensor(sensor_id_t sensor, int power_save_state); + void get_listening_sensors(sensor_id_vector &sensors); + + unsigned int get_active_min_interval(sensor_id_t sensor_id); + unsigned int get_active_option(sensor_id_t sensor_id); + void get_active_event_types(sensor_id_t sensor_id, event_type_vector &active_event_types); + + bool get_sensor_id(int handle, sensor_id_t &sensor_id); + bool get_sensor_state(int handle, int &state); + + void get_sensor_rep(sensor_id_t sensor_id, sensor_rep& rep); bool has_client_id(void); int get_client_id(void); - void set_client_id(const int client_id); + void set_client_id(int client_id); bool is_active(void); - bool is_sensor_registered(const sensor_type_t sensor); - bool is_sensor_active(const sensor_type_t sensor); + bool is_sensor_registered(sensor_id_t sensor_id); + bool is_sensor_active(sensor_id_t sensor_id); - bool set_command_channel(sensor_type_t sensor, command_channel *cmd_channel); - bool get_command_channel(sensor_type_t sensor, command_channel **cmd_channel); - bool close_command_channel(sensor_type_t sensor); + bool add_command_channel(sensor_id_t sensor_id, command_channel *cmd_channel); + bool get_command_channel(sensor_id_t sensor_id, command_channel **cmd_channel); + bool close_command_channel(void); + bool close_command_channel(sensor_id_t sensor_id); void get_all_handles(handle_vector &handles); - int get_single_event_count(const unsigned int event_type); bool start_event_listener(void); void stop_event_listener(void); + void clear(void); - static csensor_event_listener &get_instance(void) { - static csensor_event_listener inst; - return inst; - } - + void set_hup_observer(hup_observer_t observer); private: enum thread_state { THREAD_STATE_START, @@ -150,22 +152,24 @@ private: mutex m_thread_mutex; condition_variable m_thread_cond; + hup_observer_t m_hup_observer; + csensor_event_listener(); ~csensor_event_listener(); - csensor_event_listener(const csensor_event_listener &) {}; - csensor_event_listener &operator=(const csensor_event_listener &); + csensor_event_listener(const csensor_event_listener&) {}; + csensor_event_listener& operator=(const csensor_event_listener&); bool create_event_channel(void); void close_event_channel(void); - bool sensor_event_poll(void *buffer, int buffer_len); + bool sensor_event_poll(void* buffer, int buffer_len, int &event); void listen_events(void); - void handle_events(void *event); + client_callback_info* handle_calibration_cb(csensor_handle_info &handle_info, unsigned event_type, unsigned long long time, int accuracy); + void handle_events(void* event); - client_callback_info *get_callback_info(creg_event_info &event_info, - sensor_event_data_t &event_data); + client_callback_info* get_callback_info(sensor_id_t sensor_id, const creg_event_info *event_info, void *sensor_data); unsigned long long renew_event_id(void); diff --git a/src/libsensord/csensor_handle_info.cpp b/src/libsensord/csensor_handle_info.cpp index 4b5b5fe..d1c8dbb 100755 --- a/src/libsensord/csensor_handle_info.cpp +++ b/src/libsensord/csensor_handle_info.cpp @@ -26,11 +26,15 @@ unsigned long long csensor_handle_info::m_event_id = 0; csensor_handle_info::csensor_handle_info() : m_handle(0) -, m_sensor_type(UNKNOWN_SENSOR) +, m_sensor_id(UNKNOWN_SENSOR) , m_sensor_state(SENSOR_STATE_UNKNOWN) , m_sensor_option(SENSOR_OPTION_DEFAULT) -, bad_accuracy(false) +, m_bad_accuracy(false) +, m_accuracy(-1) +, m_accuracy_cb(NULL) +, m_accuracy_user_data(NULL) { + } csensor_handle_info::~csensor_handle_info() @@ -38,24 +42,21 @@ csensor_handle_info::~csensor_handle_info() clear_all_events(); } -bool csensor_handle_info::get_reg_event_info(unsigned int event_type, creg_event_info &event_info) +creg_event_info* csensor_handle_info::get_reg_event_info(unsigned int event_type) { - event_info_map::iterator it_event; - it_event = m_reg_event_infos.find(event_type); + auto it_event = m_reg_event_infos.find(event_type); if (it_event == m_reg_event_infos.end()) { DBG("Event %s[0x%x] is not registered for client %s", get_event_name(event_type), event_type, get_client_name()); - return false; + return NULL; } - event_info = it_event->second; - return true; + return &(it_event->second); } void csensor_handle_info::get_reg_event_types(event_type_vector &event_types) { - event_info_map::iterator it_event; - it_event = m_reg_event_infos.begin(); + auto it_event = m_reg_event_infos.begin(); while (it_event != m_reg_event_infos.end()) { event_types.push_back(it_event->first); @@ -63,12 +64,11 @@ void csensor_handle_info::get_reg_event_types(event_type_vector &event_types) } } -bool csensor_handle_info::add_reg_event_info(const unsigned int event_type, const unsigned int interval, - const sensor_callback_func_t callback, void *cb_data) +bool csensor_handle_info::add_reg_event_info(unsigned int event_type, unsigned int interval, int cb_type, void *cb, void *user_data) { - event_info_map::iterator it_event; creg_event_info event_info; - it_event = m_reg_event_infos.find(event_type); + + auto it_event = m_reg_event_infos.find(event_type); if (it_event != m_reg_event_infos.end()) { ERR("Event %s[0x%x] is already registered for client %s", get_event_name(event_type), event_type, get_client_name()); @@ -77,18 +77,20 @@ bool csensor_handle_info::add_reg_event_info(const unsigned int event_type, cons event_info.m_id = renew_event_id(); event_info.m_handle = m_handle; - event_info.m_event_type = event_type; - event_info.m_event_interval = interval; - event_info.m_event_callback = callback; - event_info.m_cb_data = cb_data; - m_reg_event_infos.insert(pair (event_type, event_info)); + event_info.type = event_type; + event_info.m_interval = interval; + event_info.m_cb_type = cb_type; + event_info.m_cb = cb; + event_info.m_user_data = user_data; + + m_reg_event_infos.insert(pair (event_type, event_info)); + return true; } -bool csensor_handle_info::delete_reg_event_info(const unsigned int event_type) +bool csensor_handle_info::delete_reg_event_info(unsigned int event_type) { - event_info_map::iterator it_event; - it_event = m_reg_event_infos.find(event_type); + auto it_event = m_reg_event_infos.find(event_type); if (it_event == m_reg_event_infos.end()) { ERR("Event %s[0x%x] is not registered for client %s", get_event_name(event_type), event_type, get_client_name()); @@ -96,6 +98,7 @@ bool csensor_handle_info::delete_reg_event_info(const unsigned int event_type) } m_reg_event_infos.erase(it_event); + return true; } @@ -104,15 +107,15 @@ void csensor_handle_info::clear_all_events(void) m_reg_event_infos.clear(); } + unsigned long long csensor_handle_info::renew_event_id(void) { return m_event_id++; } -bool csensor_handle_info::change_reg_event_interval(const unsigned int event_type, const unsigned int interval) +bool csensor_handle_info::change_reg_event_interval(unsigned int event_type, unsigned int interval) { - event_info_map::iterator it_event; - it_event = m_reg_event_infos.find(event_type); + auto it_event = m_reg_event_infos.find(event_type); if (it_event == m_reg_event_infos.end()) { ERR("Event %s[0x%x] is not registered for client %s", get_event_name(event_type), event_type, get_client_name()); @@ -120,7 +123,8 @@ bool csensor_handle_info::change_reg_event_interval(const unsigned int event_typ } it_event->second.m_id = renew_event_id(); - it_event->second.m_event_interval = interval; + it_event->second.m_interval = interval; + return true; } @@ -134,11 +138,10 @@ unsigned int csensor_handle_info::get_min_interval(void) return min_interval; } - event_info_map::iterator it_event; - it_event = m_reg_event_infos.begin(); + auto it_event = m_reg_event_infos.begin(); while (it_event != m_reg_event_infos.end()) { - interval = it_event->second.m_event_interval; + interval = it_event->second.m_interval; min_interval = (interval < min_interval) ? interval : min_interval; ++it_event; } diff --git a/src/libsensord/csensor_handle_info.h b/src/libsensord/csensor_handle_info.h index 78d5a33..b6eca9b 100755 --- a/src/libsensord/csensor_handle_info.h +++ b/src/libsensord/csensor_handle_info.h @@ -24,33 +24,33 @@ #include #include #include -#include +#include #include - -using std::map; +using std::unordered_map; using std::vector; -typedef map event_info_map; +typedef unordered_map event_info_map; -class csensor_handle_info -{ +class csensor_handle_info { public: int m_handle; - sensor_type_t m_sensor_type; + sensor_id_t m_sensor_id; int m_sensor_state; int m_sensor_option; - int bad_accuracy; + int m_bad_accuracy; + int m_accuracy; + sensor_accuracy_changed_cb_t m_accuracy_cb; + void *m_accuracy_user_data; csensor_handle_info(); ~csensor_handle_info(); - bool add_reg_event_info(const unsigned int event_type, const unsigned int interval, - const sensor_callback_func_t callback, void *cb_data); - bool delete_reg_event_info(const unsigned int event_type); + bool add_reg_event_info(unsigned int event_type, unsigned int interval, int cb_type, void *cb,void *user_data); + bool delete_reg_event_info(unsigned int event_type); - bool change_reg_event_interval(const unsigned int event_type, const unsigned int interval); + bool change_reg_event_interval(unsigned int event_type, unsigned int interval); - bool get_reg_event_info(const unsigned int event_type, creg_event_info &event_info); + creg_event_info* get_reg_event_info(const unsigned int event_type); void get_reg_event_types(event_type_vector &event_types); unsigned int get_min_interval(void); unsigned int get_reg_event_count(void); @@ -62,4 +62,5 @@ private: static unsigned long long m_event_id; }; + #endif /* CSENSOR_HANDLE_INFO_H_ */ diff --git a/src/libsensord/poller.cpp b/src/libsensord/poller.cpp index 8411dcc..64292e5 100755 --- a/src/libsensord/poller.cpp +++ b/src/libsensord/poller.cpp @@ -16,7 +16,6 @@ * limitations under the License. * */ - #include #include #include @@ -32,6 +31,7 @@ bool poller::create(int fd) m_epfd = epoll_create(1); struct epoll_event event; + event.data.fd = fd; event.events = EPOLLIN | EPOLLERR | EPOLLHUP; @@ -43,9 +43,11 @@ bool poller::create(int fd) return true; } + bool poller::fill_event_queue(void) { - const int EPOLL_MAX_EVENT = 16; + const int EPOLL_MAX_EVENT = 1; + struct epoll_event event_items[EPOLL_MAX_EVENT]; int nr_events = epoll_wait(m_epfd, event_items, EPOLL_MAX_EVENT, -1); @@ -63,14 +65,17 @@ bool poller::fill_event_queue(void) return false; } - for (int i = 0; i < nr_events; i++) + for (int i = 0; i < nr_events; i++) m_event_queue.push(event_items[i].events); return true; } -bool poller::poll(void) + +bool poller::poll(int &event) { + event = 0; + while (true) { if (m_event_queue.empty()) { if (!fill_event_queue()) @@ -78,7 +83,7 @@ bool poller::poll(void) } if (!m_event_queue.empty()) { - int event = m_event_queue.front(); + event = m_event_queue.front(); m_event_queue.pop(); if (event & EPOLLERR) { diff --git a/src/libsensord/poller.h b/src/libsensord/poller.h index b75fe9a..5650518 100755 --- a/src/libsensord/poller.h +++ b/src/libsensord/poller.h @@ -31,13 +31,12 @@ using std::queue; -class poller -{ +class poller { public: poller(int fd); ~poller(); - bool poll(void); + bool poll(int &event); private: int m_epfd; queue m_event_queue; diff --git a/src/libsensord/sensor_accel.h b/src/libsensord/sensor_accel.h index 32622c4..c48bc46 100755 --- a/src/libsensord/sensor_accel.h +++ b/src/libsensord/sensor_accel.h @@ -17,8 +17,8 @@ * */ -#ifndef _SENSOR_ACCEL_H_ -#define _SENSOR_ACCEL_H_ +#ifndef __SENSOR_ACCEL_H__ +#define __SENSOR_ACCEL_H__ //! Pre-defined events for the accelometer sensor //! Sensor Plugin developer can add more event to their own headers @@ -26,7 +26,7 @@ #ifdef __cplusplus extern "C" { -#endif /*__cplusplus*/ +#endif /** * @defgroup SENSOR_ACCEL Accelerometer Sensor @@ -38,53 +38,10 @@ extern "C" enum accelerometer_data_id { ACCELEROMETER_BASE_DATA_SET = (ACCELEROMETER_SENSOR << 16) | 0x0001, - ACCELEROMETER_ORIENTATION_DATA_SET = (ACCELEROMETER_SENSOR << 16) | 0x0002, - ACCELEROMETER_LINEAR_ACCELERATION_DATA_SET = (ACCELEROMETER_SENSOR << 16) | 0x0004, - ACCELEROMETER_GRAVITY_DATA_SET = (ACCELEROMETER_SENSOR << 16) | 0x0008, - ACCELEROMETER_ROTATION_DATA_SET = (ACCELEROMETER_SENSOR << 16) | 0x0010, }; enum accelerometer_event_type { - ACCELEROMETER_EVENT_ROTATION_CHECK = (ACCELEROMETER_SENSOR << 16) | 0x0001, - ACCELEROMETER_EVENT_RAW_DATA_REPORT_ON_TIME = (ACCELEROMETER_SENSOR << 16) | 0x0002, - ACCELEROMETER_EVENT_CALIBRATION_NEEDED = (ACCELEROMETER_SENSOR << 16) | 0x0004, - ACCELEROMETER_EVENT_SET_HORIZON = (ACCELEROMETER_SENSOR << 16) | 0x0008, - ACCELEROMETER_EVENT_SET_WAKEUP = (ACCELEROMETER_SENSOR << 16) | 0x0010, - ACCELEROMETER_EVENT_ORIENTATION_DATA_REPORT_ON_TIME = (ACCELEROMETER_SENSOR << 16) | 0x0020, - ACCELEROMETER_EVENT_LINEAR_ACCELERATION_DATA_REPORT_ON_TIME = (ACCELEROMETER_SENSOR << 16) | 0x0040, - ACCELEROMETER_EVENT_GRAVITY_DATA_REPORT_ON_TIME = (ACCELEROMETER_SENSOR << 16) | 0x0080, -}; - -enum accelerometer_rotate_state { - ROTATION_UNKNOWN = 0, - ROTATION_PORTRAIT_TOP = 1, - ROTATION_LANDSCAPE_LEFT = 2, - ROTATION_PORTRAIT_BTM = 3, - ROTATION_LANDSCAPE_RIGHT = 4, - ROTATION_EVENT_0 = 1, /*CCW base*/ - ROTATION_EVENT_90 = 2, /*CCW base*/ - ROTATION_EVENT_180 = 3, /*CCW base*/ - ROTATION_EVENT_270 = 4, /*CCW base*/ -}; - -enum accelerometer_property_id { - ACCELEROMETER_PROPERTY_UNKNOWN = 0, - ACCELEROMETER_PROPERTY_SET_CALIBRATION, - ACCELEROMETER_PROPERTY_CHECK_CALIBRATION_STATUS, - ACCELEROMETER_PROPERTY_SET_WAKEUP, - ACCELEROMETER_PROPERTY_CHECK_WAKEUP_STATUS, - ACCELEROMETER_PROPERTY_CHECK_WAKEUP_SUPPORTED, - ACCELEROMETER_PROPERTY_GET_WAKEUP, -}; - -enum accelerometer_wakeup_state { - WAKEUP_UNSET = 0, - WAKEUP_SET = 1, -}; - -struct rotation_event { - enum accelerometer_rotate_state rotation; - enum accelerometer_rotate_state rm[2]; + ACCELEROMETER_EVENT_RAW_DATA_REPORT_ON_TIME = (ACCELEROMETER_SENSOR << 16) | 0x0001, }; /** @@ -93,6 +50,7 @@ struct rotation_event { #ifdef __cplusplus } -#endif /*__cplusplus*/ +#endif -#endif /*_SENSOR_ACCEL_H_*/ +#endif +//! End of a file diff --git a/src/libsensord/sensor_context.h b/src/libsensord/sensor_context.h index ff52265..c821596 100755 --- a/src/libsensord/sensor_context.h +++ b/src/libsensord/sensor_context.h @@ -17,8 +17,8 @@ * */ -#ifndef _SENSOR_CONTEXT_H_ -#define _SENSOR_CONTEXT_H_ +#ifndef __SENSOR_CONTEXT_H__ +#define __SENSOR_CONTEXT_H__ //! Pre-defined events for the context sensor //! Sensor Plugin developer can add more event to their own headers @@ -26,7 +26,7 @@ #ifdef __cplusplus extern "C" { -#endif /*__cplusplus*/ +#endif /** * @defgroup SENSOR_CONTEXT Context Sensor @@ -54,6 +54,7 @@ enum context_property_id { #ifdef __cplusplus } -#endif /*__cplusplus*/ +#endif -#endif /*_SENSOR_CONTEXT_H_*/ +#endif +//! End of a file diff --git a/src/libsensord/sensor_geomag.h b/src/libsensord/sensor_geomag.h index 5270762..c1c2de6 100755 --- a/src/libsensord/sensor_geomag.h +++ b/src/libsensord/sensor_geomag.h @@ -17,8 +17,8 @@ * */ -#ifndef _SENSOR_GEOMAG_H_ -#define _SENSOR_GEOMAG_H_ +#ifndef __SENSOR_GEOMAG_H__ +#define __SENSOR_GEOMAG_H__ //! Pre-defined events for the geomagnetic sensor //! Sensor Plugin developer can add more event to their own headers @@ -26,7 +26,7 @@ #ifdef __cplusplus extern "C" { -#endif /*__cplusplus*/ +#endif /** * @defgroup SENSOR_GEOMAG Geomagnetic Sensor @@ -39,19 +39,11 @@ extern "C" enum geomag_data_id { GEOMAGNETIC_BASE_DATA_SET = (GEOMAGNETIC_SENSOR << 16) | 0x0001, GEOMAGNETIC_RAW_DATA_SET = (GEOMAGNETIC_SENSOR << 16) | 0x0001, - GEOMAGNETIC_ATTITUDE_DATA_SET = (GEOMAGNETIC_SENSOR << 16) | 0x0002, }; enum geomag_evet_type { - GEOMAGNETIC_EVENT_CALIBRATION_NEEDED = (GEOMAGNETIC_SENSOR << 16) | 0x0001, - GEOMAGNETIC_EVENT_RAW_DATA_REPORT_ON_TIME = (GEOMAGNETIC_SENSOR << 16) | 0x0002, - GEOMAGNETIC_EVENT_ATTITUDE_DATA_REPORT_ON_TIME = (GEOMAGNETIC_SENSOR << 16) | 0x0004, -}; - -enum geomag_property_id { - GEOMAGNETIC_PROPERTY_UNKNOWN = 0, - GEOMAGNETIC_PROPERTY_SET_ACCEL_CALIBRATION, - GEOMAGNETIC_PROPERTY_CHECK_ACCEL_CALIBRATION, + GEOMAGNETIC_EVENT_RAW_DATA_REPORT_ON_TIME = (GEOMAGNETIC_SENSOR << 16) | 0x0001, + GEOMAGNETIC_EVENT_CALIBRATION_NEEDED = (GEOMAGNETIC_SENSOR << 16) | 0x0002, }; /** @@ -60,6 +52,7 @@ enum geomag_property_id { #ifdef __cplusplus } -#endif /*__cplusplus*/ +#endif -#endif /*_SENSOR_GEOMAG_H_*/ +#endif +//! End of a file diff --git a/src/libsensord/sensor_gravity.h b/src/libsensord/sensor_gravity.h index d80ad41..2509f03 100755 --- a/src/libsensord/sensor_gravity.h +++ b/src/libsensord/sensor_gravity.h @@ -17,8 +17,8 @@ * */ -#ifndef _SENSOR_GRAVITY_H_ -#define _SENSOR_GRAVITY_H_ +#ifndef __SENSOR_GRAVITY_H__ +#define __SENSOR_GRAVITY_H__ //! Pre-defined events for the gravity sensor //! Sensor Plugin developer can add more event to their own headers @@ -26,7 +26,7 @@ #ifdef __cplusplus extern "C" { -#endif /*__cplusplus*/ +#endif /** * @defgroup SENSOR_GRAVITY Gravity Sensor @@ -36,8 +36,12 @@ extern "C" * @{ */ +enum gravity_data_id { + GRAVITY_BASE_DATA_SET = (GRAVITY_SENSOR << 16) | 0x0001, +}; + enum gravity_event_type { - GRAVITY_EVENT_RAW_DATA_REPORT_ON_TIME = (GRAVITY_SENSOR << 16) | 0x0001, + GRAVITY_EVENT_RAW_DATA_REPORT_ON_TIME = (GRAVITY_SENSOR << 16) | 0x0001, }; /** @@ -46,6 +50,7 @@ enum gravity_event_type { #ifdef __cplusplus } -#endif /*__cplusplus*/ +#endif -#endif /*_SENSOR_GRAVITY_H_*/ +#endif +//! End of a file diff --git a/src/libsensord/sensor_gyro.h b/src/libsensord/sensor_gyro.h index 485628f..5a73bb7 100755 --- a/src/libsensord/sensor_gyro.h +++ b/src/libsensord/sensor_gyro.h @@ -17,8 +17,8 @@ * */ -#ifndef _SENSOR_GYRO_H_ -#define _SENSOR_GYRO_H_ +#ifndef __SENSOR_GYRO_H__ +#define __SENSOR_GYRO_H__ //! Pre-defined events for the gyroscope sensor //! Sensor Plugin developer can add more event to their own headers @@ -26,7 +26,7 @@ #ifdef __cplusplus extern "C" { -#endif /*__cplusplus*/ +#endif /** * @defgroup SENSOR_GYRO Gyro Sensor @@ -44,18 +44,13 @@ enum gyro_event_type { GYROSCOPE_EVENT_RAW_DATA_REPORT_ON_TIME = (GYROSCOPE_SENSOR << 16) | 0x0001, }; -enum gyro_property_id { - GYROSCOPE_PROPERTY_UNKNOWN = 0, - GYROSCOPE_PROPERTY_SET_CALIBRATION, - GYROSCOPE_PROPERTY_CHECK_CALIBRATION, -}; - /** * @} */ #ifdef __cplusplus } -#endif /*__cplusplus*/ +#endif -#endif /*_SENSOR_GYRO_H_*/ +#endif +//! End of a file diff --git a/src/libsensord/sensor_info_manager.cpp b/src/libsensord/sensor_info_manager.cpp new file mode 100755 index 0000000..f4c6fe1 --- /dev/null +++ b/src/libsensord/sensor_info_manager.cpp @@ -0,0 +1,101 @@ +/* + * libsensord + * + * Copyright (c) 2014 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 + +using std::pair; +using std::make_pair; + +sensor_info_manager::sensor_info_manager() +{ +} + +sensor_info_manager::~sensor_info_manager() +{ + auto it_info = m_sensor_infos.begin(); + + while (it_info != m_sensor_infos.end()) { + + delete it_info->second; + ++it_info; + } +} + +sensor_info_manager& sensor_info_manager::get_instance(void) +{ + static sensor_info_manager inst; + return inst; +} + +const sensor_info* sensor_info_manager::get_info(sensor_type_t type) +{ + auto it_info = m_sensor_infos.find(type); + + if (it_info == m_sensor_infos.end()) + return NULL; + + return it_info->second; +} + +void sensor_info_manager::add_info(sensor_info* info) +{ + m_sensor_infos.insert(make_pair(info->get_type(), info)); + m_id_to_info_map.insert(make_pair(info->get_id(), info)); + m_info_set.insert(info); +} + +vector sensor_info_manager::get_infos(sensor_type_t type) +{ + vector sensor_infos; + + pair ret; + + if (type == ALL_SENSOR) + ret = std::make_pair(m_sensor_infos.begin(), m_sensor_infos.end()); + else + ret = m_sensor_infos.equal_range(type); + + for (auto it_info = ret.first; it_info != ret.second; ++it_info) { + sensor_infos.push_back(it_info->second); + } + + return sensor_infos; +} + +const sensor_info* sensor_info_manager::get_info(sensor_id_t id) +{ + auto it_info = m_id_to_info_map.find(id); + + if (it_info == m_id_to_info_map.end()) + return NULL; + + return it_info->second; +} + + +bool sensor_info_manager::is_valid(sensor_info* info) +{ + auto it_info = m_info_set.find(info); + + if (it_info == m_info_set.end()) + return false; + + return true; +} diff --git a/src/libsensord/sensor_info_manager.h b/src/libsensord/sensor_info_manager.h new file mode 100755 index 0000000..512905f --- /dev/null +++ b/src/libsensord/sensor_info_manager.h @@ -0,0 +1,57 @@ +/* + * libsensord + * + * Copyright (c) 2014 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 _SENSOR_INFO_MANAGER_H_ +#define _SENSOR_INFO_MANAGER_H_ + +#include +#include +#include +#include +#include + +using std::multimap; +using std::unordered_map; +using std::unordered_set; + +class sensor_info_manager { +public: + static sensor_info_manager& get_instance(void); + const sensor_info* get_info(sensor_type_t type); + vector get_infos(sensor_type_t type); + const sensor_info* get_info(sensor_id_t id); + bool is_valid(sensor_info* info); + void add_info(sensor_info* info); + +private: + typedef multimap sensor_infos; + typedef unordered_map id_to_info_map; + typedef unordered_set info_set; + + sensor_info_manager(); + ~sensor_info_manager(); + + sensor_info_manager(const sensor_info_manager&) {}; + sensor_info_manager& operator=(const sensor_info_manager&); + + sensor_infos m_sensor_infos; + id_to_info_map m_id_to_info_map; + info_set m_info_set; +}; +#endif /* _SENSOR_INFO_MANAGER_H_ */ diff --git a/src/libsensord/sensor_internal.h b/src/libsensord/sensor_internal.h new file mode 100755 index 0000000..8f2efd2 --- /dev/null +++ b/src/libsensord/sensor_internal.h @@ -0,0 +1,339 @@ +/* + * libsensord + * + * Copyright (c) 2014 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 __SENSOR_INTERNAL_H__ +#define __SENSOR_INTERNAL_H__ + +#ifndef DEPRECATED +#define DEPRECATED __attribute__((deprecated)) +#endif + +#include "stdbool.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +#include + +/*header for common sensor type*/ +#include + +/*header for each sensor type*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +typedef void (*sensor_cb_t)(sensor_t sensor, unsigned int event_type, sensor_data_t *data, void *user_data); +typedef void (*sensorhub_cb_t)(sensor_t sensor, unsigned int event_type, sensorhub_data_t *data, void *user_data); +typedef void (*sensor_accuracy_changed_cb_t) (sensor_t sensor, unsigned long long timestamp, int accuracy, void *user_data); + +/** + * @brief Get the list of available sensors of a certain type, use ALL_SENSOR to get all the sensors. + * + * @param[in] type the type of sensors requested. + * @param[out] list the list of sensors matching the asked type, the caller should explicitly free this list. + * @param[out] sensor count the count of sensors contained in the list. + * @return true on success, otherwise false. + */ +bool sensord_get_sensor_list(sensor_type_t type, sensor_t **list, int *sensor_count); + +/** + * @brief Get the default sensor for a given type. + * + * @param[in] type the type of a sensor requested. + * @return the default sensor matching the asked type on success, otherwise NULL. + */ +sensor_t sensord_get_sensor(sensor_type_t type); + +/** + * @brief Get the type of this sensor. + * + * @param[in] sensor a sensor to get type. + * @param[out] type the type of this sensor. + * @return return true on success, otherwise false. + */ +bool sensord_get_type(sensor_t sensor, sensor_type_t *type); + +/** + * @brief Get the name string of this sensor. + * + * @param[in] sensor a sensor to get name. + * @return the name string of this sensor on success, otherwise NULL. + */ +const char* sensord_get_name(sensor_t sensor); + +/** + * @brief Get the vendor string of this sensor. + * + * @param[in] sensor a sensor to get vendor. + * @return the vendor string of this sensor on success, otherwise NULL. + */ +const char* sensord_get_vendor(sensor_t sensor); + +/** + * @brief Get the privilege of this sensor. + * + * @param[in] sensor a sensor to get privilege. + * @param[out] privilege the privilege of this sensor. + * @return true on success, otherwise false. + */ +bool sensord_get_privilege(sensor_t sensor, sensor_privilege_t *privilege); + +/** + * @brief Get the minimum range of this sensor in the sensor's unit. + * + * @param[in] sensor a sensor to get minimum range. + * @param[out] min_range the minimum range of this sensor in the sensor's unit. + * @return true on success, otherwise false. + */ +bool sensord_get_min_range(sensor_t sensor, float *min_range); + +/** + * @brief Get the maximum range of this sensor in the sensor's unit. + * + * @param[in] sensor a sensor to get maximum range. + * @param[out] max_range the maximum range of this sensor in the sensor's unit. + * @return true on success, otherwise false. + */ +bool sensord_get_max_range(sensor_t sensor, float *max_range); + +/** + * @brief Get the resolution of this sensor in the sensor's unit. + * + * @param[in] sensor a sensor to get resolution. + * @param[out] resolution the resolution of this sensor in the sensor's unit. + * @return true on success, otherwise false. + */ +bool sensord_get_resolution(sensor_t sensor, float *resolution); + +/** + * @brief Get the minimum interval allowed between two events in microsecond or zero if this sensor only returns a value when the data it's measuring changes. + * + * @param[in] sensor a sensor to get minimum interval. + * @param[out] min_interval the minimum interval of this sensor. + * @return true on success, otherwise false. + */ +bool sensord_get_min_interval(sensor_t sensor, int *min_interval); + +/** + * @brief Get the number of events reserved for this sensor in the batch mode FIFO. + * + * @param[in] sensor a sensor to get the number of fifo count + * @param[out] fifo_count the number of events reserved for this sensor in the batch mode FIFO + * @return true on success, otherwise false + */ +bool sensord_get_fifo_count(sensor_t sensor, int *fifo_count); + +/** + * @brief Get the maximum number of events of this sensor that could be batched. If this value is zero it indicates that batch mode is not supported for this sensor. + * + * @param[in] sensor a sensor to the maximum number of events that could be batched. + * @param[out] max_batch_count the maximum number of events of this sensor that could be batched. + * @return true on success, otherwise false. + */ +bool sensord_get_max_batch_count(sensor_t sensor, int *max_batch_count); + + +/** + * @brief Get the supported event types of this sensor. + * + * @param[in] sensor a sensor to get the supported event types. + * @param[out] event_types the array containing supported event types of this sensor, the caller should explicitly free this array. + * @param[out] count the count of the supported event types of this sensor. + * @return true on success, otherwise false. + */ +bool sensord_get_supported_event_types(sensor_t sensor, unsigned int **event_types, int *count); + + +/** + * @brief Check a given event type is supporeted by this sensor. + * + * @param[in] sensor a sensor to check a given event type is supporeted. + * @param[out] event_type an event type to be checked whether supported or not. + * @param[out] supported whether a given event is supported or not in this sensor. + * @return true on success, otherwise false. + */ +bool sensord_is_supported_event_type(sensor_t sensor, unsigned int event_type, bool *supported); + +/** + * @brief Connect a given sensor and get a handle of a given sensor. + * + * @param[in] sensor a sensor to connect + * @return a handle of a given sensor on success, otherwise negative value + */ +int sensord_connect(sensor_t sensor); + +/** + * @brief Disconnect a given sensor. + * + * @param[in] handle a handle to disconnect. + * @return true on success, otherwise false. + */ +bool sensord_disconnect(int handle); + +/** + * @brief Register a callback with a connected sensor for a given event_type. This callback will be called when a given event occurs in a connected sensor. + * + * @param[in] handle a handle represensting a connected sensor. + * @param[in] event_type an event type to register + * @param[in] interval The desired interval between two consecutive events in microseconds. This is only a hint to the system so events may be received faster or slower than the specified interval. + * It can be one of SENSOR_INTERVAL_NORMAL, SENSOR_INTERVAL_FASTEST or the interval in microseconds. + * @param[in] max_batch_latency An event in the batch can be delayed by at most max_batch_latency microseconds. If this is set to zero, batch mode is disabled. + * @param[in] cb a callback which is called when a given event occurs + * @param[in] user_data the callback is called with user_data + * @return true on success, otherwise false. + */ +bool sensord_register_event(int handle, unsigned int event_type, unsigned int interval, unsigned int max_batch_latency, sensor_cb_t cb, void *user_data); + +/** + * @brief Register a callback with a connected context sensor for a given event_type. This callback will be called when a given event occurs in a connected context sensor. + * + * @param[in] handle a handle represensting a connected context sensor. + * @param[in] event_type an event type to register + * @param[in] interval The desired interval between two consecutive events in microseconds. This is only a hint to the system so events may be received faster or slower than the specified interval. + * It can be one of SENSOR_INTERVAL_NORMAL, SENSOR_INTERVAL_FASTEST or the interval in microseconds. + * @param[in] max_batch_latency An event in the batch can be delayed by at most max_batch_latency microseconds. If this is set to zero, batch mode is disabled. + * @param[in] cb a callback which is called when a given event occurs + * @param[in] user_data the callback is called with user_data + * @return true on success, otherwise false. + */ +bool sensord_register_hub_event(int handle, unsigned int event_type, unsigned int interval, unsigned int max_batch_latency, sensorhub_cb_t cb, void *user_data); + +/** + * @brief Unregister a event with a connected sensor. After unregistering, that event will not be sent. + * + * @param[in] handle a handle represensting a connected sensor. + * @param[in] event_type an event type to unregister. + * @return true on success, otherwise false. + */ +bool sensord_unregister_event(int handle, unsigned int event_type); + +/** + * @brief Register a callback with a connected sensor. This callback will be called when the accuracy of a sensor has changed. + * + * @param[in] handle a handle represensting a connected sensor. + * @param[in] cb a callback which is called when he accuracy of a sensor has changed. + * @param[in] user_data the callback is called with user_data + * @return true on success, otherwise false. + */ +bool sensord_register_accuracy_cb(int handle, sensor_accuracy_changed_cb_t cb, void *user_data); + +/** + * @brief Unregister a callback with a connected sensor. After unregistering, sensor_accuray_change_cb will not be called. + * + * @param[in] handle a handle represensting a connected sensor. + * @return true on success, otherwise false. + */ +bool sensord_unregister_accuracy_cb(int handle); + +/** + * @brief Start listening events with a connected sensor. + * + * @param[in] handle a handle represensting a connected sensor. + * @param[in] option either one of SENSOR_OPTION_DEFAULT and SENSOR_OPTION_ALWAYS_ON. + * with SENSOR_OPTION_DEFAULT, it stops to listening events when LCD is off or in power save mode. + * with SENSOR_OPTION_ALWAYS_ON, it continues to listening events even when LCD is off or in power save mode. + * @return true on success, otherwise false. + */ + +bool sensord_start(int handle, int option); + +/** + * @brief Stop listening events with a connected sensor. + * + * @param[in] handle a handle represensting a connected sensor. + * @return true on success, otherwise false. + */ +bool sensord_stop(int handle); + +/** + * @brief Change the interval of a specifed event type in a connected sensor. + * + * @param[in] handle a handle represensting a connected sensor. + * @param[in] event_type an event type to change interval. + * @param[in] interval The desired interval between two consecutive events in microseconds. This is only a hint to the system so events may be received faster or slower than the specified interval. + * It can be one of SENSOR_INTERVAL_NORMAL, SENSOR_INTERVAL_FASTEST or the interval in microseconds. + * @return true on success, otherwise false. + */ +bool sensord_change_event_interval(int handle, unsigned int event_type, unsigned int interval); + +/** + * @brief Change the max batch latency of a specifed event type in a connected sensor. + * + * @param[in] handle a handle represensting a connected sensor. + * @param[in] max_batch_latency an event in the batch can be delayed by at most max_batch_latency microseconds. If this is set to zero, batch mode is disabled. + * @return true on success, otherwise false. + */ +bool sensord_change_event_max_batch_latency(int handle, unsigned int max_batch_latency); + +/** + * @brief Change the option of a connected sensor. + * + * @param[in] handle a handle represensting a connected sensor. + * @param[in] option either one of SENSOR_OPTION_DEFAULT and SENSOR_OPTION_ALWAYS_ON. + * with SENSOR_OPTION_DEFAULT, it stops to listening events when LCD is off or in power save mode. + * with SENSOR_OPTION_ALWAYS_ON, it continues to listening events even when LCD is off or in power save mode. + * @return true on success, otherwise false. + */ +bool sensord_set_option(int handle, int option); + +/** + * @brief Send data to sensorhub + * + * @param[in] handle a handle represensting a connected context sensor. + * @param[in] data it holds data to send to sensorhub + * @param[in] data_len the length of data + * @return true on success, otherwise false. + */ +bool sensord_send_sensorhub_data(int handle, const char *data, int data_len); + +/** + * @brief get sensor data from a connected sensor + * + * @param[in] handle a handle represensting a connected context sensor. + * @param[in] data_id it specifies data to get + * @param[out] sensor_data data from connected sensor + * @return true on success, otherwise false. + */ +bool sensord_get_data(int handle, unsigned int data_id, sensor_data_t* sensor_data); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/src/libsensord/sensor.h b/src/libsensord/sensor_internal_deprecated.h similarity index 53% rename from src/libsensord/sensor.h rename to src/libsensord/sensor_internal_deprecated.h index 430c050..79388be 100755 --- a/src/libsensord/sensor.h +++ b/src/libsensord/sensor_internal_deprecated.h @@ -17,13 +17,15 @@ * */ -#ifndef _SENSOR_H_ -#define _SENSOR_H_ +#ifndef __SENSOR_INTERNAL_DEPRECATED__ +#define __SENSOR_INTERNAL_DEPRECATED__ #ifndef DEPRECATED #define DEPRECATED __attribute__((deprecated)) #endif +#include "stdbool.h" + #ifdef __cplusplus extern "C" { @@ -42,11 +44,11 @@ extern "C" #include #include #include -#include +#include #include #include #include -#include +#include typedef struct { condition_op_t cond_op; @@ -59,6 +61,7 @@ typedef struct { } sensor_event_data_t; typedef void (*sensor_callback_func_t)(unsigned int, sensor_event_data_t *, void *); +typedef sensor_callback_func_t sensor_legacy_cb_t; typedef struct { int x; @@ -66,119 +69,69 @@ typedef struct { int z; } sensor_panning_data_t; -typedef struct { - int sensor_unit_idx; - float sensor_min_range; - float sensor_max_range; - float sensor_resolution; -} sensor_data_properties_t; - -/** - * @fn int sf_is_sensor_event_available(sensor_type_t desired_sensor_type, unsigned int desired_event_type) - * @brief This API loads the in parameter available list with the type of sensor available for operation . Some of the supported types are ACCELEROMETER_SENSOR_TYPE, GEOMAGNETIC_SENSOR_TYPE etc. This API will return 0 when it is available and negative value when it does not available. - * @param[in] desired_sensor_type your desired sensor type to check - * @param[in] desired_event_type your desired event type to check, if you want to check only sensor-available , set "0" value - * @return if it succeed, it return zero value(available), otherwise negative value return (not available) - */ -int sf_is_sensor_event_available(sensor_type_t desired_sensor_type, unsigned int desired_event_type); - -/** - * @fn int sf_get_data_properties(unsigned data_id, sensor_dada_properties_t *return_data_properties) - * @brief This API loads the properties of data ID like unit of sensor data, max/min range of sensor data etc to the output parameter sensor_data_properties. - * @param[in] data_id your desired data ID - * @param[out] return_data_properties property information of your desired data ID - * @return if it succeed, it return zero value, otherwise negative value return - */ -int sf_get_data_properties(unsigned int data_id, sensor_data_properties_t *return_data_properties); - -/** - * @fn int sf_get_properties(sensor_type_t sensor, sensor_properties_t *return_properties) - * @brief This API loads the properties of sensor type like unit of sensor data, max/min range of sensor data etc to the output parameter sensor_properties. - * @param[in] sensor your desired sensor type - * @param[out] return_properties property information of your desired sensor - * @return if it succeed, it return zero value, otherwise negative value return - */ -int sf_get_properties(sensor_type_t sensor, sensor_properties_t *return_properties); - -/** - * @fn int sf_set_property(sensor_type_t sensor, unsigned int property_id, long value) - * @brief This API set the property of sensor type like calibration, enable wakeup event, etc - * @param[in] sensor your desired sensor type, property_id your desired property ID, value for property input - * @return if it succeed, it return zero value, otherwise negative value return - */ -int sf_set_property(sensor_type_t sensor, unsigned int property_id, long value); - /** * @fn int sf_connect(sensor_type_t sensor) * @brief This API connects a sensor type to respective sensor. The application calls with the type of the sensor (ex. ACCELEROMETER_SENSOR) and on basis of that server takes decision of which plug-in to be connected. Once sensor connected application can proceed for data processing. This API returns a positive handle which should be used by application to communicate on sensor type. - * @param[in] sensor your desired sensor type - * @return if it succeed, it return handle value( >=0 ), otherwise negative value return + * @param[in] sensor_type your desired sensor type + * @return if it succeed, it return handle value( >=0 ) , otherwise negative value return */ -int sf_connect(sensor_type_t sensor); +int sf_connect(sensor_type_t sensor_type); /** * @fn int sf_disconnect(int handle) * @brief This API disconnects an attached sensor from an application. Application must use the handle retuned after attaching the sensor. After detaching, the corresponding handle will be released. * @param[in] handle received handle value by sf_connect() - * @return if it succeed, it return zero value, otherwise negative value return + * @return if it succeed, it return zero value , otherwise negative value return */ int sf_disconnect(int handle); /** - * @fn int sf_start(int handle, int option) + * @fn int sf_start(int handle , int option) * @brief This API sends a start command to sensor server. This intimates server that the client side is ready to handle data and start processing. The parameter option should be '0' for current usages. * @param[in] handle received handle value by sf_connect() * @param[in] option With SENSOR_OPTION_DEFAULT, it stops to sense when LCD is off, and with SENSOR_OPTION_ALWAYS_ON, it continues to sense even when LCD is off - * @return if it succeed, it return zero value, otherwise negative value return + * @return if it succeed, it return zero value , otherwise negative value return */ -int sf_start(int handle, int option); +int sf_start(int handle , int option); /** * @fn int sf_stop(int handle) * @brief This API sends a stop command to the Sensor server indicating that the data processing is stopped from application side for this time. * @param[in] handle received handle value by sf_connect() - * @return if it succeed, it return zero value, otherwise negative value return + * @return if it succeed, it return zero value , otherwise negative value return */ int sf_stop(int handle); /** - * @fn int sf_register_event(int handle, unsigned int event_type, event_conditon_t *event_condition, sensor_callback_func_t cb, void *cb_data) - * @brief This API registers a user defined callback function with a connected sensor for a particular event. This callback function will be called when there is a change in the state of respective sensor. cb_data will be the parameter used during the callback call. Callback interval can be adjusted using even_contion_t argument. + * @fn int sf_register_event(int handle , unsigned int event_type , event_conditon_t *event_condition , sensor_callback_func_t cb , void *user_data ) + * @brief This API registers a user defined callback function with a connected sensor for a particular event. This callback function will be called when there is a change in the state of respective sensor. user_data will be the parameter used during the callback call. Callback interval can be adjusted using even_contion_t argument. * @param[in] handle received handle value by sf_connect() * @param[in] event_type your desired event_type to register it * @param[in] event_condition input event_condition for special event. if you want to register without event_condition, just use a NULL value * @param[in] cb your define callback function - * @param[in] cb_data your option data that will be send when your define callback function called. if you don't have any option data, just use a NULL value - * @return if it succeed, it return zero value, otherwise negative value return + * @param[in] user_data your option data that will be send when your define callback function called. if you don't have any option data, just use a NULL value + * @return if it succeed, it return zero value , otherwise negative value return */ -int sf_register_event(int handle, unsigned int event_type, event_condition_t *event_condition, sensor_callback_func_t cb, void *cb_data); +int sf_register_event(int handle , unsigned int event_type , event_condition_t *event_condition , sensor_callback_func_t cb , void *user_data ); /** * @fn int sf_unregister_event(int handle, unsigned int event_type) * @brief This API de-registers a user defined callback function with a sensor registered with the specified handle. After unsubscribe, no event will be sent to the application. * @param[in] handle received handle value by sf_connect() * @param[in] event_type your desired event_type that you want to unregister event - * @return if it succeed, it return zero value, otherwise negative value return + * @return if it succeed, it return zero value , otherwise negative value return */ int sf_unregister_event(int handle, unsigned int event_type); /** - * @fn int sf_get_data(int handle, unsigned int data_id, sensor_data_t* values) + * @fn int sf_get_data(int handle , unsigned int data_id , sensor_data_t* values) * @brief This API gets raw data from a sensor with connecting the sensor-server. The type of sensor is supplied and return data is stored in the output parameter values []. * @param[in] handle received handle value by sf_connect() - * @param[in] data_id predefined data_ID as every sensor in own header - sensor_xxx.h, enum xxx_data_id {} + * @param[in] data_id predefined data_ID as every sensor in own header - sensor_xxx.h , enum xxx_data_id {} * @param[out] values return values - * @return if it succeed, it return zero value, otherwise negative value return + * @return if it succeed, it return zero value , otherwise negative value return */ -int sf_get_data(int handle, unsigned int data_id, sensor_data_t *values); - -/** - * @fn int sf_check_rotation( unsigned long *curr_state) - * @brief This API used to get the current rotation state. (i.e. ROTATION_EVENT_0, ROTATION_EVENT_90, ROTATION_EVENT_180 & ROTATION_EVENT_270 ). This API will directly access the sensor without connection process with the sensor-server. Result will be stored in the output parameter state. - * @param[out] curr_state it will return enum accelerometer_rotate_state value - * @return if it succeed, it return zero value, otherwise negative value return - */ -int sf_check_rotation(unsigned long *curr_state); +int sf_get_data(int handle , unsigned int data_id , sensor_data_t* values); /** * @fn int sf_change_event_condition(int handle, unsigned int event_type, event_condition_t *event_condition) @@ -186,7 +139,7 @@ int sf_check_rotation(unsigned long *curr_state); * @param[in] handle received handle value by sf_connect() * @param[in] event_type your desired event_type that you want to unregister event * @param[in] event_condition your desired event condition that you want to change event - * @return if it succeed, it return zero value, otherwise negative value return + * @return if it succeed, it return zero value , otherwise negative value return */ int sf_change_event_condition(int handle, unsigned int event_type, event_condition_t *event_condition); @@ -195,8 +148,9 @@ int sf_change_event_condition(int handle, unsigned int event_type, event_conditi * @brief This API change sensor option . * @param[in] handle received handle value by sf_connect() * @param[in] option your desired option that you want to turn on sensor during LCD OFF - * @return if it succeed, it return zero value, otherwise negative value return + * @return if it succeed, it return zero value , otherwise negative value return */ + int sf_change_sensor_option(int handle, int option); /** @@ -207,14 +161,12 @@ int sf_change_sensor_option(int handle, int option); * @param[in] data_len the length of data * @return if it succeed, it returns zero, otherwise negative value */ -int sf_send_sensorhub_data(int handle, const char *data, int data_len); +int sf_send_sensorhub_data(int handle, const char* data, int data_len); -/** - * @} - */ #ifdef __cplusplus } #endif -#endif /*_SENSOR_H_*/ + +#endif diff --git a/src/libsensord/sensor_light.h b/src/libsensord/sensor_light.h index 86aee82..1ddb87a 100755 --- a/src/libsensord/sensor_light.h +++ b/src/libsensord/sensor_light.h @@ -17,8 +17,8 @@ * */ -#ifndef _SENSOR_LIGHT_H_ -#define _SENSOR_LIGHT_H_ +#ifndef __SENSOR_LIGHT_H__ +#define __SENSOR_LIGHT_H__ //! Pre-defined events for the light sensor //! Sensor Plugin developer can add more event to their own headers @@ -26,7 +26,7 @@ #ifdef __cplusplus extern "C" { -#endif /*__cplusplus*/ +#endif /** * @defgroup SENSOR_LIGHT Light Sensor @@ -37,14 +37,14 @@ extern "C" */ enum light_data_id { - LIGHT_BASE_DATA_SET = (LIGHT_SENSOR << 16) | 0x0001, - LIGHT_LUX_DATA_SET = (LIGHT_SENSOR << 16) | 0x0002, + LIGHT_LUX_DATA_SET = (LIGHT_SENSOR << 16) | 0x0001, + LIGHT_BASE_DATA_SET = (LIGHT_SENSOR << 16) | 0x0002, }; enum light_evet_type { - LIGHT_EVENT_CHANGE_LEVEL = (LIGHT_SENSOR << 16) | 0x0001, + LIGHT_EVENT_LUX_DATA_REPORT_ON_TIME = (LIGHT_SENSOR << 16) | 0x0001, LIGHT_EVENT_LEVEL_DATA_REPORT_ON_TIME = (LIGHT_SENSOR << 16) | 0x0002, - LIGHT_EVENT_LUX_DATA_REPORT_ON_TIME = (LIGHT_SENSOR << 16) | 0x0004, + LIGHT_EVENT_CHANGE_LEVEL = (LIGHT_SENSOR << 16) | 0x0004, }; enum light_property_id { @@ -57,6 +57,7 @@ enum light_property_id { #ifdef __cplusplus } -#endif /*__cplusplus*/ +#endif -#endif /*_SENSOR_LIGHT_H_*/ +#endif +//! End of a file diff --git a/src/libsensord/sensor_linear_accel.h b/src/libsensord/sensor_linear_accel.h index ba48a83..d4369c0 100755 --- a/src/libsensord/sensor_linear_accel.h +++ b/src/libsensord/sensor_linear_accel.h @@ -17,8 +17,8 @@ * */ -#ifndef _SENSOR_LINEAR_ACCEL_H_ -#define _SENSOR_LINEAR_ACCEL_H_ +#ifndef __SENSOR_LINEAR_ACCEL_H__ +#define __SENSOR_LINEAR_ACCEL_H__ //! Pre-defined events for the linear accelerometer sensor //! Sensor Plugin developer can add more event to their own headers @@ -26,7 +26,7 @@ #ifdef __cplusplus extern "C" { -#endif /*__cplusplus*/ +#endif /** * @defgroup SENSOR_LINEAR_ACCEL Linear Accelerometer Sensor @@ -36,6 +36,10 @@ extern "C" * @{ */ +enum linear_accel_data_id { + LINEAR_ACCEL_BASE_DATA_SET = (LINEAR_ACCEL_SENSOR << 16) | 0x0001, +}; + enum linear_accel_event_type { LINEAR_ACCEL_EVENT_RAW_DATA_REPORT_ON_TIME = (LINEAR_ACCEL_SENSOR << 16) | 0x0001, }; @@ -46,6 +50,7 @@ enum linear_accel_event_type { #ifdef __cplusplus } -#endif /*__cplusplus*/ +#endif -#endif /*_SENSOR_LINEAR_ACCEL_H_*/ +#endif +//! End of a file diff --git a/src/libsensord/sensor_motion.h b/src/libsensord/sensor_motion.h index d9a51d8..1fe2db5 100755 --- a/src/libsensord/sensor_motion.h +++ b/src/libsensord/sensor_motion.h @@ -17,8 +17,8 @@ * */ -#ifndef _SENSOR_MOTION_H_ -#define _SENSOR_MOTION_H_ +#ifndef __SENSOR_MOTION_H__ +#define __SENSOR_MOTION_H__ //! Pre-defined events for the motion sensor //! Sensor Plugin developer can add more event to their own headers @@ -26,12 +26,14 @@ #ifdef __cplusplus extern "C" { -#endif /*__cplusplus*/ +#endif enum motion_event_type { MOTION_ENGINE_EVENT_SNAP = (MOTION_SENSOR << 16) | 0x0001, MOTION_ENGINE_EVENT_SHAKE = (MOTION_SENSOR << 16) | 0x0002, MOTION_ENGINE_EVENT_DOUBLETAP = (MOTION_SENSOR << 16) | 0x0004, + MOTION_ENGINE_EVENT_PANNING = (MOTION_SENSOR << 16) | 0x0008, + MOTION_ENGINE_EVENT_TOP_TO_BOTTOM = (MOTION_SENSOR << 16) | 0x0010, MOTION_ENGINE_EVENT_DIRECT_CALL = (MOTION_SENSOR << 16) | 0x0020, MOTION_ENGINE_EVENT_TILT_TO_UNLOCK = (MOTION_SENSOR << 16) | 0x0040, MOTION_ENGINE_EVENT_LOCK_EXECUTE_CAMERA = (MOTION_SENSOR << 16) | 0x0080, @@ -40,6 +42,7 @@ enum motion_event_type { MOTION_ENGINE_EVENT_PANNING_BROWSE = (MOTION_SENSOR << 16) | 0x0400, MOTION_ENGINE_EVENT_NO_MOVE = (MOTION_SENSOR << 16) | 0x0800, MOTION_ENGINE_EVENT_SHAKE_ALWAYS_ON = (MOTION_SENSOR << 16) | 0x1000, + MOTION_ENGINE_EVENT_SMART_RELAY = (MOTION_SENSOR << 16) | 0x2000, }; enum motion_snap_event { @@ -96,6 +99,11 @@ enum motion_direct_call_event_t { MOTION_ENGINE_DIRECT_CALL_DETECTION, }; +enum motion_smart_relay_event_t { + MOTION_ENGINE_SMART_RELAY_NONE, + MOTION_ENGINE_SMART_RELAY_DETECTION, +}; + enum motion_tilt_to_unlock_event_t { MOTION_ENGINE_TILT_TO_UNLOCK_NONE, MOTION_ENGINE_TILT_TO_UNLOCK_DETECTION, @@ -136,6 +144,7 @@ enum motion_property_id { #ifdef __cplusplus } -#endif /*__cplusplus*/ +#endif -#endif /*_SENSOR_MOTION_H_*/ +#endif +//! End of a file diff --git a/src/libsensord/sensor_orientation.h b/src/libsensord/sensor_orientation.h old mode 100755 new mode 100644 index 3c8eb6e..ecc2d2e --- a/src/libsensord/sensor_orientation.h +++ b/src/libsensord/sensor_orientation.h @@ -17,8 +17,8 @@ * */ -#ifndef _SENSOR_ORIENTATION_H_ -#define _SENSOR_ORIENTATION_H_ +#ifndef __SENSOR_ORIENTATION_H__ +#define __SENSOR_ORIENTATION_H__ //! Pre-defined events for the orientation sensor //! Sensor Plugin developer can add more event to their own headers @@ -26,7 +26,7 @@ #ifdef __cplusplus extern "C" { -#endif /*__cplusplus*/ +#endif /** * @defgroup SENSOR_ORIENTATION Orientation Sensor @@ -36,8 +36,13 @@ extern "C" * @{ */ +enum orientation_data_id { + ORIENTATION_BASE_DATA_SET = (ORIENTATION_SENSOR << 16) | 0x0001, +}; + enum orientation_event_type { ORIENTATION_EVENT_RAW_DATA_REPORT_ON_TIME = (ORIENTATION_SENSOR << 16) | 0x0001, + ORIENTATION_EVENT_CALIBRATION_NEEDED = (ORIENTATION_SENSOR << 16) | 0x0002, }; /** @@ -46,6 +51,7 @@ enum orientation_event_type { #ifdef __cplusplus } -#endif /*__cplusplus*/ +#endif -#endif /*_SENSOR_ORIENTATION_H_*/ +#endif +//! End of a file diff --git a/src/libsensord/sensor_proxi.h b/src/libsensord/sensor_proxi.h index ce6b80c..f4aa971 100755 --- a/src/libsensord/sensor_proxi.h +++ b/src/libsensord/sensor_proxi.h @@ -1,7 +1,7 @@ /* * libsensord * - * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * Copyright (c) 2014 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. @@ -23,7 +23,7 @@ #ifdef __cplusplus extern "C" { -#endif /*__cplusplus*/ +#endif /** * @defgroup SENSOR_PROXY Proximity Sensor @@ -59,6 +59,7 @@ enum proxi_property_id { #ifdef __cplusplus } -#endif /*__cplusplus*/ +#endif -#endif /*_SENSOR_PROXI_H_*/ +#endif +//! End of a file diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index 4939cd4..88e83da 100755 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -10,6 +10,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}) include_directories(${CMAKE_SOURCE_DIR}/src/libsensord) SET(SERVER_SRCS + dbus_util.cpp server.cpp command_worker.cpp main.cpp diff --git a/src/server/command_worker.cpp b/src/server/command_worker.cpp index 2ef5562..37a65b2 100755 --- a/src/server/command_worker.cpp +++ b/src/server/command_worker.cpp @@ -19,20 +19,38 @@ #include #include +#include #include #include +#include +#include using std::string; +using std::make_pair; +#define SECURITY_LIB "/usr/lib/libsecurity-server-client.so.1" + +void *command_worker::m_security_handle = NULL; +command_worker::security_server_check_privilege_by_sockfd_t command_worker::security_server_check_privilege_by_sockfd = NULL; command_worker::cmd_handler_t command_worker::m_cmd_handlers[]; +sensor_raw_data_map command_worker::m_sensor_raw_data_map; +cpacket command_worker::m_sensor_list; -command_worker::command_worker(const csocket &socket) +command_worker::command_worker(const csocket& socket) : m_client_id(CLIENT_ID_INVALID) +, m_permission(SENSOR_PERMISSION_NONE) , m_socket(socket) , m_module(NULL) -, m_sensor_type(UNKNOWN_SENSOR) { - init_cmd_handlers(); + static bool init = false; + + if (!init) { + init_security_lib(); + init_cmd_handlers(); + make_sensor_raw_data_map(); + + init = true; + } m_worker.set_context(this); m_worker.set_working(working); @@ -44,52 +62,145 @@ command_worker::~command_worker() m_socket.close(); } + bool command_worker::start(void) { return m_worker.start(); } +void command_worker::init_security_lib(void) +{ + m_security_handle = dlopen(SECURITY_LIB, RTLD_LAZY); + + if (!m_security_handle) { + ERR("dlopen(%s) error, cause: %s", SECURITY_LIB, dlerror()); + return; + } + + security_server_check_privilege_by_sockfd = + (security_server_check_privilege_by_sockfd_t) dlsym(m_security_handle, "security_server_check_privilege_by_sockfd"); + + if (!security_server_check_privilege_by_sockfd) { + ERR("Failed to load symbol"); + dlclose(m_security_handle); + m_security_handle = NULL; + return; + } +} + void command_worker::init_cmd_handlers(void) { - static bool init = false; + m_cmd_handlers[CMD_GET_ID] = &command_worker::cmd_get_id; + m_cmd_handlers[CMD_GET_SENSOR_LIST] = &command_worker::cmd_get_sensor_list; + m_cmd_handlers[CMD_HELLO] = &command_worker::cmd_hello; + m_cmd_handlers[CMD_BYEBYE] = &command_worker::cmd_byebye; + m_cmd_handlers[CMD_START] = &command_worker::cmd_start; + m_cmd_handlers[CMD_STOP] = &command_worker::cmd_stop; + m_cmd_handlers[CMD_REG] = &command_worker::cmd_register_event; + m_cmd_handlers[CMD_UNREG] = &command_worker::cmd_unregister_event; + m_cmd_handlers[CMD_SET_OPTION] = &command_worker::cmd_set_option; + m_cmd_handlers[CMD_SET_INTERVAL] = &command_worker::cmd_set_interval; + m_cmd_handlers[CMD_UNSET_INTERVAL] = &command_worker::cmd_unset_interval; + m_cmd_handlers[CMD_SET_COMMAND] = &command_worker::cmd_set_command; + m_cmd_handlers[CMD_GET_DATA] = &command_worker::cmd_get_data; + m_cmd_handlers[CMD_SEND_SENSORHUB_DATA] = &command_worker::cmd_send_sensorhub_data; +} - if (!init) { - m_cmd_handlers[CMD_GET_ID] = &command_worker::cmd_get_id; - m_cmd_handlers[CMD_HELLO] = &command_worker::cmd_hello; - m_cmd_handlers[CMD_BYEBYE] = &command_worker::cmd_byebye; - m_cmd_handlers[CMD_START] = &command_worker::cmd_start; - m_cmd_handlers[CMD_STOP] = &command_worker::cmd_stop; - m_cmd_handlers[CMD_REG] = &command_worker::cmd_register_event; - m_cmd_handlers[CMD_UNREG] = &command_worker::cmd_unregister_event; - m_cmd_handlers[CMD_CHECK_EVENT] = &command_worker::cmd_check_event; - m_cmd_handlers[CMD_SET_OPTION] = &command_worker::cmd_set_option; - m_cmd_handlers[CMD_SET_INTERVAL] = &command_worker::cmd_set_interval; - m_cmd_handlers[CMD_UNSET_INTERVAL] = &command_worker::cmd_unset_interval; - m_cmd_handlers[CMD_SET_COMMAND] = &command_worker::cmd_set_command; - m_cmd_handlers[CMD_GET_PROPERTIES] = &command_worker::cmd_get_properties; - m_cmd_handlers[CMD_GET_DATA] = &command_worker::cmd_get_data; - m_cmd_handlers[CMD_SEND_SENSORHUB_DATA] = &command_worker::cmd_send_sensorhub_data; - init = true; +void command_worker::get_sensor_list(int permissions, cpacket &sensor_list) +{ + const int PERMISSION_COUNT = sizeof(permissions) * 8; + vector sensor_raw_vec; + size_t total_raw_data_size = 0; + + for (int i = 0; i < PERMISSION_COUNT; ++i) { + int perm = (permissions & (1 << i)); + + if (perm) { + auto range = m_sensor_raw_data_map.equal_range(perm); + + sensor_raw_data_map::iterator it_raw_data; + + for (it_raw_data = range.first; it_raw_data != range.second; ++it_raw_data) { + total_raw_data_size += it_raw_data->second.size(); + sensor_raw_vec.push_back(&(it_raw_data->second)); + } + } + } + + int sensor_cnt; + + sensor_cnt = sensor_raw_vec.size(); + + sensor_list.set_payload_size(sizeof(cmd_get_sensor_list_done_t) + (sizeof(size_t) * sensor_cnt) + total_raw_data_size); + sensor_list.set_cmd(CMD_GET_SENSOR_LIST); + + cmd_get_sensor_list_done_t *cmd_get_sensor_list_done; + + cmd_get_sensor_list_done = (cmd_get_sensor_list_done_t*)sensor_list.data(); + cmd_get_sensor_list_done->sensor_cnt = sensor_cnt; + size_t* size_field = (size_t *) cmd_get_sensor_list_done->data; + + + for (int i = 0; i < sensor_cnt; ++i) + size_field[i] = sensor_raw_vec[i]->size(); + + char* raw_data_field = cmd_get_sensor_list_done->data + (sizeof(size_t) * sensor_cnt); + + int idx = 0; + for (int i = 0; i < sensor_cnt; ++i) { + copy(sensor_raw_vec[i]->begin(), sensor_raw_vec[i]->end(), raw_data_field + idx); + idx += sensor_raw_vec[i]->size(); + } + +} + +void command_worker::make_sensor_raw_data_map(void) +{ + vector sensors; + sensor_info info; + int permission; + + sensors = sensor_plugin_loader::get_instance().get_sensors(ALL_SENSOR); + + auto it_sensor = sensors.begin(); + + while (it_sensor != sensors.end()) { + (*it_sensor)->get_sensor_info(info); + permission = (*it_sensor)->get_permission(); + + sensor_raw_data_map::iterator it_sensor_raw_data; + it_sensor_raw_data = m_sensor_raw_data_map.insert(std::make_pair(permission, raw_data_t())); + + info.get_raw_data(it_sensor_raw_data->second); + info.clear(); + ++it_sensor; } } bool command_worker::working(void *ctx) { - int ret; + bool ret; command_worker *inst = (command_worker *)ctx; + packet_header header; char *payload; if (inst->m_socket.recv(&header, sizeof(header)) <= 0) { - DBG("%s failed to receive header", inst->get_info()); + string info; + inst->get_info(info); + DBG("%s failed to receive header", info); return false; } if (header.size > 0) { - payload = new char[header.size]; + + payload = new(std::nothrow) char[header.size]; + retvm_if(!payload, false, "Failed to allocate memory"); if (inst->m_socket.recv(payload, header.size) <= 0) { - DBG("%s failed to receive data of packet", inst->get_info()); + string info; + inst->get_info(info); + DBG("%s failed to receive data of packet", info); delete[] payload; return false; } @@ -105,37 +216,40 @@ bool command_worker::working(void *ctx) return ret; } + bool command_worker::stopped(void *ctx) { + string info; event_type_vector event_vec; command_worker *inst = (command_worker *)ctx; - INFO("%s is stopped", inst->get_info()); + inst->get_info(info); + INFO("%s is stopped", info.c_str()); if ((inst->m_module) && (inst->m_client_id != CLIENT_ID_INVALID)) { - get_client_info_manager().get_registered_events(inst->m_client_id, inst->m_sensor_type, event_vec); - event_type_vector::iterator it_event; - it_event = event_vec.begin(); + + get_client_info_manager().get_registered_events(inst->m_client_id, inst->m_module->get_id(), event_vec); + + auto it_event = event_vec.begin(); while (it_event != event_vec.end()) { WARN("Does not unregister event[0x%x] before connection broken for [%s]!!", *it_event, inst->m_module->get_name()); - if (!inst->m_module->delete_client(*it_event)) ERR("Unregistering event[0x%x] failed", *it_event); ++it_event; } - if (get_client_info_manager().is_started(inst->m_client_id, inst->m_sensor_type)) { + if (get_client_info_manager().is_started(inst->m_client_id, inst->m_module->get_id())) { WARN("Does not receive cmd_stop before connection broken for [%s]!!", inst->m_module->get_name()); inst->m_module->delete_interval(inst->m_client_id, false); inst->m_module->stop(); } - if (inst->m_sensor_type) { - if (get_client_info_manager().has_sensor_record(inst->m_client_id, inst->m_sensor_type)) { - INFO("Removing sensor[0x%x] record for client_id[%d]", inst->m_sensor_type, inst->m_client_id); - get_client_info_manager().remove_sensor_record(inst->m_client_id, inst->m_sensor_type); + if (inst->m_module->get_id()) { + if (get_client_info_manager().has_sensor_record(inst->m_client_id, inst->m_module->get_id())) { + INFO("Removing sensor[0x%x] record for client_id[%d]", inst->m_module->get_id(), inst->m_client_id); + get_client_info_manager().remove_sensor_record(inst->m_client_id, inst->m_module->get_id()); } } } @@ -144,7 +258,7 @@ bool command_worker::stopped(void *ctx) return true; } -bool command_worker::dispatch_command(int cmd, void *payload) +bool command_worker::dispatch_command(int cmd, void* payload) { int ret = false; @@ -153,7 +267,6 @@ bool command_worker::dispatch_command(int cmd, void *payload) } else { cmd_handler_t cmd_handler; cmd_handler = command_worker::m_cmd_handlers[cmd]; - if (cmd_handler) ret = (this->*cmd_handler)(payload); } @@ -163,12 +276,15 @@ bool command_worker::dispatch_command(int cmd, void *payload) bool command_worker::send_cmd_done(long value) { - cpacket *ret_packet; + cpacket* ret_packet; cmd_done_t *cmd_done; - ret_packet = new cpacket(sizeof(cmd_done_t)); + ret_packet = new(std::nothrow) cpacket(sizeof(cmd_done_t)); + retvm_if(!ret_packet, false, "Failed to allocate memory"); + ret_packet->set_cmd(CMD_DONE); - cmd_done = (cmd_done_t *)ret_packet->data(); + + cmd_done = (cmd_done_t*)ret_packet->data(); cmd_done->value = value; if (m_socket.send(ret_packet->packet(), ret_packet->size()) <= 0) { @@ -179,16 +295,21 @@ bool command_worker::send_cmd_done(long value) delete ret_packet; return true; + } + bool command_worker::send_cmd_get_id_done(int client_id) { - cpacket *ret_packet; + cpacket* ret_packet; cmd_get_id_done_t *cmd_get_id_done; - ret_packet = new cpacket(sizeof(cmd_get_id_done_t)); + ret_packet = new(std::nothrow) cpacket(sizeof(cmd_get_id_done_t)); + retvm_if(!ret_packet, false, "Failed to allocate memory"); + ret_packet->set_cmd(CMD_GET_ID); - cmd_get_id_done = (cmd_get_id_done_t *)ret_packet->data(); + + cmd_get_id_done = (cmd_get_id_done_t*)ret_packet->data(); cmd_get_id_done->client_id = client_id; if (m_socket.send(ret_packet->packet(), ret_packet->size()) <= 0) { @@ -201,19 +322,24 @@ bool command_worker::send_cmd_get_id_done(int client_id) return true; } -bool command_worker::send_cmd_properties_done(int state, sensor_properties_t *properties) +bool command_worker::send_cmd_get_data_done(int state, sensor_data_t *data) { - cpacket *ret_packet; - cmd_properties_done_t *cmd_properties_done; - ret_packet = new cpacket(sizeof(cmd_properties_done_t)); - ret_packet->set_cmd(CMD_GET_PROPERTIES); - cmd_properties_done = (cmd_properties_done_t *)ret_packet->data(); - cmd_properties_done->state = state; - memcpy(&cmd_properties_done->properties, properties , sizeof(sensor_properties_t)); + cpacket* ret_packet; + cmd_get_data_done_t *cmd_get_data_done; + + ret_packet = new(std::nothrow) cpacket(sizeof(cmd_get_data_done_t)); + retvm_if(!ret_packet, false, "Failed to allocate memory"); + + ret_packet->set_cmd(CMD_GET_DATA); + + cmd_get_data_done = (cmd_get_data_done_t*)ret_packet->data(); + cmd_get_data_done->state = state; + + memcpy(&cmd_get_data_done->base_data , data, sizeof(sensor_data_t)); if (m_socket.send(ret_packet->packet(), ret_packet->size()) <= 0) { - ERR("Failed to send a cmd_get_properties"); + ERR("Failed to send a cmd_get_data_done"); delete ret_packet; return false; } @@ -222,37 +348,39 @@ bool command_worker::send_cmd_properties_done(int state, sensor_properties_t *pr return true; } -bool command_worker::send_cmd_get_data_done(int state, sensor_data_t *data) + +bool command_worker::send_cmd_get_sensor_list_done(void) { - cpacket *ret_packet; - cmd_get_data_done_t *cmd_get_data_done; + cpacket sensor_list; - ret_packet = new cpacket(sizeof(cmd_get_data_done_t)); - ret_packet->set_cmd(CMD_GET_DATA); - cmd_get_data_done = (cmd_get_data_done_t *)ret_packet->data(); - cmd_get_data_done->state = state; - memcpy(&cmd_get_data_done->base_data , data, sizeof(sensor_data_t)); + int permission = get_permission(); - if (m_socket.send(ret_packet->packet(), ret_packet->size()) <= 0) { - ERR("Failed to send a cmd_get_data_done"); - delete ret_packet; + INFO("permission = 0x%x", permission); + + get_sensor_list(permission, sensor_list); + + if (m_socket.send(sensor_list.packet(), sensor_list.size()) <= 0) { + ERR("Failed to send a cmd_get_sensor_list_done"); return false; } - delete ret_packet; return true; } bool command_worker::cmd_get_id(void *payload) { - DBG("CMD_GET_ID Handler invoked"); - cmd_get_id_t *cmd; int client_id; - cmd = (cmd_get_id_t *)payload; + DBG("CMD_GET_ID Handler invoked\n"); + cmd = (cmd_get_id_t*)payload; + client_id = get_client_info_manager().create_client_record(); get_client_info_manager().set_client_info(client_id, cmd->pid); + + m_permission = get_permission(); + get_client_info_manager().set_permission(client_id, m_permission); + INFO("New client id [%d] created", client_id); if (!send_cmd_get_id_done(client_id)) @@ -261,30 +389,52 @@ bool command_worker::cmd_get_id(void *payload) return true; } -bool command_worker::cmd_hello(void *payload) + +bool command_worker::cmd_get_sensor_list(void *payload) { - DBG("CMD_HELLO Handler invoked"); + DBG("CMD_GET_SENSOR_LIST Handler invoked\n"); + + if (!send_cmd_get_sensor_list_done()) + ERR("Failed to send cmd_get_sensor_list_done to a client"); + + return true; +} +bool command_worker::cmd_hello(void *payload) +{ cmd_hello_t *cmd; long ret_value = OP_ERROR; - cmd = (cmd_hello_t *)payload; - m_sensor_type = static_cast(cmd->sensor); + DBG("CMD_HELLO Handler invoked\n"); + cmd = (cmd_hello_t*)payload; + m_client_id = cmd->client_id; - DBG("Hello sensor [0x%x], client id [%d]", m_sensor_type, m_client_id); - m_module = (sensor_base *)sensor_plugin_loader::get_instance().get_sensor(m_sensor_type); - if (m_module) { - get_client_info_manager().create_sensor_record(m_client_id, m_sensor_type); - INFO("New sensor record created for sensor [0x%x], sensor name [%s] on client id [%d]", m_sensor_type, m_module->get_name(), m_client_id); - ret_value = OP_SUCCESS; - } else { - ERR("Sensor type[0x%x] is not supported", m_sensor_type); + if (m_permission == SENSOR_PERMISSION_NONE) + get_client_info_manager().get_permission(m_client_id, m_permission); + + m_module = (sensor_base *)sensor_plugin_loader::get_instance().get_sensor(cmd->sensor); + if (!m_module) { + ERR("Sensor type[%d] is not supported", cmd->sensor); if (!get_client_info_manager().has_sensor_record(m_client_id)) get_client_info_manager().remove_client_record(m_client_id); + + ret_value = OP_ERROR; + goto out; } + if (!is_permission_allowed()) { + ERR("Permission denied to connect sensor[0x%x] for client [%d]", m_module->get_id(), m_client_id); + ret_value = OP_ERROR; + goto out; + } + + DBG("Hello sensor [0x%x], client id [%d]", m_module->get_id(), m_client_id); + get_client_info_manager().create_sensor_record(m_client_id, m_module->get_id()); + INFO("New sensor record created for sensor [0x%x], sensor name [%s] on client id [%d]\n", m_module->get_id(), m_module->get_name(), m_client_id); + ret_value = OP_SUCCESS; +out: if (!send_cmd_done(ret_value)) ERR("Failed to send cmd_done to a client"); @@ -293,17 +443,26 @@ bool command_worker::cmd_hello(void *payload) bool command_worker::cmd_byebye(void *payload) { - long ret_value; - DBG("CMD_BYEBYE for client [%d], sensor [0x%x]", m_client_id, m_sensor_type); + long ret_value = OP_ERROR; - if (!get_client_info_manager().remove_sensor_record(m_client_id, m_sensor_type)) { + if (!is_permission_allowed()) { + ERR("Permission denied to stop sensor[0x%x] for client [%d]", m_module? m_module->get_id() : -1, m_client_id); + ret_value = OP_ERROR; + goto out; + } + + DBG("CMD_BYEBYE for client [%d], sensor [0x%x]", m_client_id, m_module->get_id()); + + if (!get_client_info_manager().remove_sensor_record(m_client_id, m_module->get_id())) { ERR("Error removing sensor_record for client [%d]", m_client_id); ret_value = OP_ERROR; - } else { - m_client_id = CLIENT_ID_INVALID; - ret_value = OP_SUCCESS; + goto out; } + m_client_id = CLIENT_ID_INVALID; + ret_value = OP_SUCCESS; + +out: if (!send_cmd_done(ret_value)) ERR("Failed to send cmd_done to a client"); @@ -315,24 +474,32 @@ bool command_worker::cmd_byebye(void *payload) bool command_worker::cmd_start(void *payload) { - long value = OP_SUCCESS; - DBG("START Sensor [0x%x], called from client [%d]", m_sensor_type, m_client_id); - DBG("Invoke Module start for []"); + long ret_value = OP_ERROR; + + if (!is_permission_allowed()) { + ERR("Permission denied to start sensor[0x%x] for client [%d]", m_module? m_module->get_id() : -1, m_client_id); + ret_value = OP_ERROR; + goto out; + } + + DBG("START Sensor [0x%x], called from client [%d]", m_module->get_id(), m_client_id); if (m_module->start()) { - get_client_info_manager().set_start(m_client_id, m_sensor_type, true); - - /* - * Rotation could be changed even LCD is off by pop sync rotation - * and a client listening rotation event with always-on option. - * To reflect the last rotation state, request it to event dispatcher. - */ - get_event_dispathcher().request_last_event(m_client_id, m_sensor_type); + get_client_info_manager().set_start(m_client_id, m_module->get_id(), true); +/* + * Rotation could be changed even LCD is off by pop sync rotation + * and a client listening rotation event with always-on option. + * To reflect the last rotation state, request it to event dispatcher. + */ + get_event_dispathcher().request_last_event(m_client_id, m_module->get_id()); + ret_value = OP_SUCCESS; } else { - value = OP_ERROR; + ERR("Failed to start sensor [0x%x] for client [%d]", m_module->get_id(), m_client_id); + ret_value = OP_ERROR; } - if (!send_cmd_done(value)) +out: + if (!send_cmd_done(ret_value)) ERR("Failed to send cmd_done to a client"); return true; @@ -340,14 +507,26 @@ bool command_worker::cmd_start(void *payload) bool command_worker::cmd_stop(void *payload) { - long ret_val = OP_SUCCESS; - DBG("STOP Sensor [0x%x], called from client [%d]", m_sensor_type, m_client_id); + long ret_value = OP_ERROR; + + if (!is_permission_allowed()) { + ERR("Permission denied to stop sensor[0x%x] for client [%d]", m_module? m_module->get_id() : -1, m_client_id); + ret_value = OP_ERROR; + goto out; + } + + DBG("STOP Sensor [0x%x], called from client [%d]", m_module->get_id(), m_client_id); if (m_module->stop()) { - get_client_info_manager().set_start(m_client_id, m_sensor_type, false); + get_client_info_manager().set_start(m_client_id, m_module->get_id(), false); + ret_value = OP_SUCCESS; + } else { + ERR("Failed to stop sensor [0x%x] for client [%d]", m_module->get_id(), m_client_id); + ret_value = OP_ERROR; } - if (!send_cmd_done(ret_val)) +out: + if (!send_cmd_done(ret_value)) ERR("Failed to send cmd_done to a client"); return true; @@ -356,23 +535,31 @@ bool command_worker::cmd_stop(void *payload) bool command_worker::cmd_register_event(void *payload) { cmd_reg_t *cmd; - long ret_val = OP_ERROR; + long ret_value = OP_ERROR; + + cmd = (cmd_reg_t*)payload; - cmd = (cmd_reg_t *)payload; + if (!is_permission_allowed()) { + ERR("Permission denied to register event [0x%x] for client [%d] to client info manager", + cmd->event_type, m_client_id); + ret_value = OP_ERROR; + goto out; + } - if (!get_client_info_manager().register_event(m_client_id, cmd->event_type)) { + if (!get_client_info_manager().register_event(m_client_id, m_module? m_module->get_id() : -1, cmd->event_type)) { INFO("Failed to register event [0x%x] for client [%d] to client info manager", cmd->event_type, m_client_id); + ret_value = OP_ERROR; goto out; } m_module->add_client(cmd->event_type); - ret_val = OP_SUCCESS; + + ret_value = OP_SUCCESS; DBG("Registering Event [0x%x] is done for client [%d]", cmd->event_type, m_client_id); out: - - if (!send_cmd_done(ret_val)) + if (!send_cmd_done(ret_value)) ERR("Failed to send cmd_done to a client"); return true; @@ -381,44 +568,37 @@ out: bool command_worker::cmd_unregister_event(void *payload) { cmd_unreg_t *cmd; - long ret_val = OP_ERROR; - cmd = (cmd_unreg_t *)payload; + long ret_value = OP_ERROR; + + cmd = (cmd_unreg_t*)payload; - if (!get_client_info_manager().unregister_event(m_client_id, cmd->event_type)) { - ERR("Failed to unregister event [0x%x] for client [%d from client info manager", + if (!is_permission_allowed()) { + ERR("Permission denied to unregister event [0x%x] for client [%d] to client info manager", cmd->event_type, m_client_id); + ret_value = OP_ERROR; + goto out; + } + + if (!get_client_info_manager().unregister_event(m_client_id, m_module->get_id(), cmd->event_type)) { + ERR("Failed to unregister event [0x%x] for client [%d] from client info manager", + cmd->event_type, m_client_id); + ret_value = OP_ERROR; goto out; } if (!m_module->delete_client(cmd->event_type)) { ERR("Failed to unregister event [0x%x] for client [%d]", cmd->event_type, m_client_id); + ret_value = OP_ERROR; goto out; } - ret_val = OP_SUCCESS; + ret_value = OP_SUCCESS; DBG("Unregistering Event [0x%x] is done for client [%d]", cmd->event_type, m_client_id); out: - if (!send_cmd_done(ret_val)) - ERR("Failed to send cmd_done to a client"); - - return true; -} - -bool command_worker::cmd_check_event(void *payload) -{ - cmd_check_event_t *cmd; - long ret_val = OP_ERROR; - cmd = (cmd_check_event_t *)payload; - - if (m_module->is_supported(cmd->event_type)) { - ret_val = OP_SUCCESS; - DBG("Event[0x%x] is supported for client [%d], for sensor [0x%x]", cmd->event_type, m_client_id, (cmd->event_type >> 16)); - } - - if (!send_cmd_done(ret_val)) + if (!send_cmd_done(ret_value)) ERR("Failed to send cmd_done to a client"); return true; @@ -427,25 +607,35 @@ bool command_worker::cmd_check_event(void *payload) bool command_worker::cmd_set_interval(void *payload) { cmd_set_interval_t *cmd; - long ret_val = OP_ERROR; - cmd = (cmd_set_interval_t *)payload; + long ret_value = OP_ERROR; + + cmd = (cmd_set_interval_t*)payload; + + if (!is_permission_allowed()) { + ERR("Permission denied to register interval for client [%d], for sensor [0x%x] with interval [%d] to client info manager", + m_client_id, m_module? m_module->get_id() : -1, cmd->interval); + ret_value = OP_ERROR; + goto out; + } - if (!get_client_info_manager().set_interval(m_client_id, m_sensor_type, cmd->interval)) { + if (!get_client_info_manager().set_interval(m_client_id, m_module->get_id(), cmd->interval)) { ERR("Failed to register interval for client [%d], for sensor [0x%x] with interval [%d] to client info manager", - m_client_id, m_sensor_type, cmd->interval); + m_client_id, m_module->get_id(), cmd->interval); + ret_value = OP_ERROR; goto out; } if (!m_module->add_interval(m_client_id, cmd->interval, false)) { ERR("Failed to set interval for client [%d], for sensor [0x%x] with interval [%d]", - m_client_id, m_sensor_type, cmd->interval); + m_client_id, m_module->get_id(), cmd->interval); + ret_value = OP_ERROR; goto out; } - ret_val = OP_SUCCESS; + ret_value = OP_SUCCESS; out: - if (!send_cmd_done(ret_val)) + if (!send_cmd_done(ret_value)) ERR("Failed to send cmd_done to a client"); return true; @@ -453,23 +643,32 @@ out: bool command_worker::cmd_unset_interval(void *payload) { - long ret_val = OP_ERROR; + long ret_value = OP_ERROR; + + if (!is_permission_allowed()) { + ERR("Permission denied to unregister interval for client [%d], for sensor [0x%x] to client info manager", + m_client_id, m_module? m_module->get_id() : -1); + ret_value = OP_ERROR; + goto out; + } - if (!get_client_info_manager().set_interval(m_client_id, m_sensor_type, 0)) { + if (!get_client_info_manager().set_interval(m_client_id, m_module->get_id(), 0)) { ERR("Failed to unregister interval for client [%d], for sensor [0x%x] to client info manager", - m_client_id, m_sensor_type); + m_client_id, m_module->get_id()); + ret_value = OP_ERROR; goto out; } if (!m_module->delete_interval(m_client_id, false)) { ERR("Failed to delete interval for client [%d]", m_client_id); + ret_value = OP_ERROR; goto out; } - ret_val = OP_SUCCESS; + ret_value = OP_SUCCESS; out: - if (!send_cmd_done(ret_val)) + if (!send_cmd_done(ret_value)) ERR("Failed to send cmd_done to a client"); return true; @@ -478,19 +677,27 @@ out: bool command_worker::cmd_set_option(void *payload) { cmd_set_option_t *cmd; - long ret_val = OP_ERROR; - cmd = (cmd_set_option_t *)payload; + long ret_value = OP_ERROR; - if (!get_client_info_manager().set_option(m_client_id, m_sensor_type, cmd->option)) { - ERR("Failed to register interval for client [%d], for sensor [0x%x] with option [%d] to client info manager", - m_client_id, m_sensor_type, cmd->option); + cmd = (cmd_set_option_t*)payload; + + if (!is_permission_allowed()) { + ERR("Permission denied to set interval for client [%d], for sensor [0x%x] with option [%d] to client info manager", + m_client_id, m_module? m_module->get_id() : -1, cmd->option); + ret_value = OP_ERROR; goto out; } - ret_val = OP_SUCCESS; + if (!get_client_info_manager().set_option(m_client_id, m_module->get_id(), cmd->option)) { + ERR("Failed to set option for client [%d], for sensor [0x%x] with option [%d] to client info manager", + m_client_id, m_module->get_id(), cmd->option); + ret_value = OP_ERROR; + goto out; + } + ret_value = OP_SUCCESS; out: - if (!send_cmd_done(ret_val)) + if (!send_cmd_done(ret_value)) ERR("Failed to send cmd_done to a client"); return true; @@ -498,34 +705,24 @@ out: bool command_worker::cmd_set_command(void *payload) { - DBG("CMD_SET_COMMAND Handler invoked"); - cmd_set_command_t *cmd; - long ret_val = OP_ERROR; - cmd = (cmd_set_command_t *)payload; - ret_val = m_module->set_command(cmd->cmd, cmd->value); - - if (!send_cmd_done(ret_val)) - ERR("Failed to send cmd_done to a client"); + long ret_value = OP_ERROR; - return true; -} + DBG("CMD_SET_COMMAND Handler invoked\n"); -bool command_worker::cmd_get_properties(void *payload) -{ - DBG("CMD_GET_PROPERTIES Handler invoked"); - int state = OP_ERROR; - cmd_get_properties_t *cmd; - sensor_properties_t sensor_properties; + cmd = (cmd_set_command_t*)payload; - cmd = (cmd_get_properties_t *) payload; - memset(&sensor_properties, 0, sizeof(sensor_properties)); - state = m_module->get_properties(cmd->type, sensor_properties); + if (!is_permission_allowed()) { + ERR("Permission denied to set command for client [%d], for sensor [0x%x] with cmd [%d]", + m_client_id, m_module? m_module->get_id() : -1, cmd->cmd); + ret_value = OP_ERROR; + goto out; + } - if (state != 0) - ERR("processor_module get_property fail"); + ret_value = m_module->set_command(cmd->cmd, cmd->value); - if (!send_cmd_properties_done(state, &sensor_properties)) +out: + if (!send_cmd_done(ret_value)) ERR("Failed to send cmd_done to a client"); return true; @@ -533,39 +730,97 @@ bool command_worker::cmd_get_properties(void *payload) bool command_worker::cmd_get_data(void *payload) { - DBG("CMD_GET_VALUE Handler invoked"); + const int GET_DATA_MIN_INTERVAL = 10; cmd_get_data_t *cmd; int state = OP_ERROR; - sensor_data_t base_data; + bool adjusted = false; + + sensor_data_t data; + + DBG("CMD_GET_VALUE Handler invoked\n"); + + cmd = (cmd_get_data_t*)payload; + + if (!is_permission_allowed()) { + ERR("Permission denied to get data for client [%d], for sensor [0x%x]", + m_client_id, m_module? m_module->get_id() : -1); + state = OP_ERROR; + goto out; + } + + state = m_module->get_sensor_data(cmd->type, data); + + // In case of not getting sensor data, wait short time and retry again + // 1. changing interval to be less than 10ms + // 2. In case of first time, wait for INIT_WAIT_TIME + // 3. at another time, wait for WAIT_TIME + // 4. retrying to get data + // 5. repeat 2 ~ 4 operations RETRY_CNT times + // 6. reverting back to original interval + if (!state && !data.timestamp) { + const int RETRY_CNT = 3; + const unsigned long long INIT_WAIT_TIME = 20000; //20ms + const unsigned long WAIT_TIME = 100000; //100ms + int retry = 0; + + unsigned int interval = m_module->get_interval(m_client_id, false); + + if (interval > GET_DATA_MIN_INTERVAL) { + m_module->add_interval(m_client_id, GET_DATA_MIN_INTERVAL, false); + adjusted = true; + } + + while (!state && !data.timestamp && (retry++ < RETRY_CNT)) { + INFO("Wait sensor[0x%x] data updated for client [%d] #%d", m_module->get_id(), m_client_id, retry); + usleep((retry == 1) ? INIT_WAIT_TIME : WAIT_TIME); + state = m_module->get_sensor_data(cmd->type, data); + } + + if (adjusted) + m_module->add_interval(m_client_id, interval, false); + } + + if (!data.timestamp) + state = OP_ERROR; - cmd = (cmd_get_data_t *)payload; - state = m_module->get_sensor_data(cmd->type, base_data); + if (state) { + ERR("Failed to get data for client [%d], for sensor [0x%x]", + m_client_id, m_module->get_id()); + } - if (state != 0) - ERR("processor_module cmd_get_data fail"); +out: + send_cmd_get_data_done(state, &data); - send_cmd_get_data_done(state, &base_data); return true; } bool command_worker::cmd_send_sensorhub_data(void *payload) { - DBG("CMD_SEND_SENSORHUB_DATA Handler invoked"); cmd_send_sensorhub_data_t *cmd; - long ret_val = OP_ERROR; + long ret_value = OP_ERROR; + + DBG("CMD_SEND_SENSORHUB_DATA Handler invoked"); + + cmd = (cmd_send_sensorhub_data_t*)payload; + + if (!is_permission_allowed()) { + ERR("Permission denied to send sensorhub_data for client [%d], for sensor [0x%x]", + m_client_id, m_module? m_module->get_id() : -1); + ret_value = OP_ERROR; + goto out; + } - cmd = (cmd_send_sensorhub_data_t *)payload; - ret_val = m_module->send_sensorhub_data(cmd->data, cmd->data_len); + ret_value = m_module->send_sensorhub_data(cmd->data, cmd->data_len); - if (!send_cmd_done(ret_val)) +out: + if (!send_cmd_done(ret_value)) ERR("Failed to send cmd_done to a client"); return true; } -const char *command_worker::get_info(void) +void command_worker::get_info(string &info) { - static string info; const char *client_info = NULL; const char *sensor_info = NULL; @@ -576,16 +831,34 @@ const char *command_worker::get_info(void) sensor_info = m_module->get_name(); info = string("Command worker for ") + (client_info ? client_info : "Unknown") + "'s " - + (sensor_info ? sensor_info : "Unknown"); - return info.c_str(); + + (sensor_info ? sensor_info : "Unknown"); +} + +int command_worker::get_permission(void) +{ + int permission = SENSOR_PERMISSION_STANDARD; + + return permission; } -cclient_info_manager &command_worker::get_client_info_manager(void) +bool command_worker::is_permission_allowed(void) +{ + if (!m_module) + return false; + + if (m_module->get_permission() & m_permission) + return true; + + return false; +} + + +cclient_info_manager& command_worker::get_client_info_manager(void) { return cclient_info_manager::get_instance(); } -csensor_event_dispatcher &command_worker::get_event_dispathcher(void) +csensor_event_dispatcher& command_worker::get_event_dispathcher(void) { return csensor_event_dispatcher::get_instance(); } diff --git a/src/server/command_worker.h b/src/server/command_worker.h index 9f1d697..06c9c82 100755 --- a/src/server/command_worker.h +++ b/src/server/command_worker.h @@ -24,24 +24,37 @@ #include #include #include +#include -class command_worker -{ +using std::multimap; + +typedef multimap sensor_raw_data_map; + +class command_worker { private: typedef bool (command_worker::*cmd_handler_t)(void *payload); + typedef int (*security_server_check_privilege_by_sockfd_t)(int sockfd, + const char *object, + const char *access_rights); static const int OP_ERROR = -1; static const int OP_SUCCESS = 0; int m_client_id; + int m_permission; csocket m_socket; worker_thread m_worker; sensor_base *m_module; - sensor_type_t m_sensor_type; - + static void *m_security_handle; + static security_server_check_privilege_by_sockfd_t security_server_check_privilege_by_sockfd; static cmd_handler_t m_cmd_handlers[CMD_CNT]; + static cpacket m_sensor_list; + static sensor_raw_data_map m_sensor_raw_data_map; + static void init_security_lib(void); static void init_cmd_handlers(void); + static void make_sensor_raw_data_map(void); + static void get_sensor_list(int permissions, cpacket &sensor_list); static bool working(void *ctx); static bool stopped(void *ctx); @@ -50,10 +63,11 @@ private: bool send_cmd_done(long value); bool send_cmd_get_id_done(int client_id); - bool send_cmd_properties_done(int state, sensor_properties_t *properties); bool send_cmd_get_data_done(int state, sensor_data_t *data); + bool send_cmd_get_sensor_list_done(void); bool cmd_get_id(void *payload); + bool cmd_get_sensor_list(void *payload); bool cmd_hello(void *payload); bool cmd_byebye(void *payload); bool cmd_get_value(void *payload); @@ -61,23 +75,26 @@ private: bool cmd_stop(void *payload); bool cmd_register_event(void *payload); bool cmd_unregister_event(void *payload); - bool cmd_check_event(void *payload); bool cmd_set_interval(void *payload); bool cmd_unset_interval(void *payload); bool cmd_set_option(void *payload); bool cmd_set_command(void *payload); - bool cmd_get_properties(void *payload); bool cmd_get_data(void *payload); bool cmd_send_sensorhub_data(void *payload); - const char *get_info(void); + void get_info(string &info); - static cclient_info_manager &get_client_info_manager(void); - static csensor_event_dispatcher &get_event_dispathcher(void); + int get_permission(void); + bool is_permission_allowed(void); + + static cclient_info_manager& get_client_info_manager(void); + static csensor_event_dispatcher& get_event_dispathcher(void); public: - command_worker(const csocket &socket); + command_worker(const csocket& socket); virtual ~command_worker(); bool start(void); + }; + #endif /* COMMAND_WORKER_H_ */ diff --git a/src/server/dbus_util.cpp b/src/server/dbus_util.cpp new file mode 100755 index 0000000..f3a0ca5 --- /dev/null +++ b/src/server/dbus_util.cpp @@ -0,0 +1,132 @@ +/* + * sensord + * + * Copyright (c) 2014 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 + +#define SENSORD_BUS_NAME "org.tizen.system.sensord" +#define SENSORD_OBJECT_PATH "/Org/Tizen/System/SensorD" +#define SENSORD_INTERFACE_NAME SENSORD_BUS_NAME + +static GDBusNodeInfo *introspection_data = NULL; +static guint owner_id; + +static const gchar introspection_xml[] = +"" +" " +" " +" " +" " +" " +""; + +static void method_call_handler(GDBusConnection *conn, + const gchar *sender, const gchar *object_path, + const gchar *iface_name, const gchar *method_name, + GVariant *parameters, GDBusMethodInvocation *invocation, + gpointer user_data) +{ + int ret = DBUS_INIT; + + if (g_strcmp0(method_name, "check_privilege") == 0) { + _D("check_privilege called"); + ret = DBUS_SUCCESS; + } else { + _D("No matched method call"); + ret = DBUS_FAILED; + } + + g_dbus_method_invocation_return_value(invocation, + g_variant_new("(i)", ret)); + +} + +static const GDBusInterfaceVTable interface_vtable = +{ + method_call_handler, + NULL, + NULL +}; + +static void on_bus_acquired (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + guint registration_id; + + if (!connection) { + _E("connection is null"); + return; + } + + registration_id = g_dbus_connection_register_object(connection, + SENSORD_OBJECT_PATH, + introspection_data->interfaces[0], + &interface_vtable, + NULL, /* user_data */ + NULL, /* user_data_free_func */ + NULL); /* GError** */ + + if (registration_id == 0) + _E("Failed to g_dbus_connection_register_object"); + + _I("Gdbus method call registrated"); +} + +static void on_name_acquired(GDBusConnection *conn, + const gchar *name, gpointer user_data) +{ +} + +static void on_name_lost(GDBusConnection *conn, + const gchar *name, gpointer user_data) +{ + _E("Dbus name is lost!"); +} + +void init_dbus(void) +{ + g_type_init(); + + introspection_data = g_dbus_node_info_new_for_xml(introspection_xml, NULL); + if (introspection_data == NULL) { + _E("Failed to init g_dbus_node_info_new_for_xml"); + return; + } + + owner_id = g_bus_own_name(G_BUS_TYPE_SYSTEM, + SENSORD_BUS_NAME, + (GBusNameOwnerFlags) (G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT + | G_BUS_NAME_OWNER_FLAGS_REPLACE), + on_bus_acquired, + on_name_acquired, + on_name_lost, + NULL, + NULL); +} + +void fini_dbus(void) +{ + if (owner_id != 0) + g_bus_unown_name(owner_id); + + if (introspection_data) + g_dbus_node_info_unref(introspection_data); +} diff --git a/src/server/dbus_util.h b/src/server/dbus_util.h new file mode 100755 index 0000000..96a9635 --- /dev/null +++ b/src/server/dbus_util.h @@ -0,0 +1,32 @@ +/* + * sensord + * + * Copyright (c) 2014 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 _DBUS_UTIL_H_ +#define _DBUS_UTIL_H_ + +enum dbus_ret{ + DBUS_INIT = -1, + DBUS_FAILED = 0, + DBUS_SUCCESS = 1 +}; + +void init_dbus(void); +void fini_dbus(void); + +#endif /* SENSORD_GDBUS_H_ */ diff --git a/src/server/main.cpp b/src/server/main.cpp index f1aeb85..c9be4e0 100755 --- a/src/server/main.cpp +++ b/src/server/main.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include static void sig_term_handler(int signo, siginfo_t *info, void *data) @@ -28,17 +29,16 @@ static void sig_term_handler(int signo, siginfo_t *info, void *data) get_proc_name(info->si_pid, proc_name); - ERR("Received SIGTERM(%d) from %s(%d)", signo, proc_name, info->si_pid); + ERR("Received SIGTERM(%d) from %s(%d)\n", signo, proc_name, info->si_pid); exit(EXIT_SUCCESS); } static void signal_init(void) { struct sigaction sig_act; + memset(&sig_act, 0, sizeof(struct sigaction)); sig_act.sa_handler = SIG_IGN; - sigemptyset(&sig_act.sa_mask); - sigaction(SIGCHLD, &sig_act, NULL); sigaction(SIGPIPE, &sig_act, NULL); @@ -50,13 +50,14 @@ static void signal_init(void) int main(int argc, char *argv[]) { - signal_init(); - INFO("Sensord started"); + signal_init(); + sensor_plugin_loader::get_instance().load_plugins(); server::get_instance().run(); + server::get_instance().stop(); sensor_plugin_loader::get_instance().destroy(); diff --git a/src/server/server.cpp b/src/server/server.cpp index 892b69a..497224a 100755 --- a/src/server/server.cpp +++ b/src/server/server.cpp @@ -28,6 +28,7 @@ using std::thread; server::server() : m_mainloop(NULL) { + } server::~server() @@ -56,6 +57,7 @@ int server::get_systemd_socket(const char *name) void server::accept_client(void) { command_worker *cmd_worker; + INFO("Client acceptor is started"); while (true) { @@ -67,9 +69,15 @@ void server::accept_client(void) } DBG("New client (socket_fd : %d) connected", client_command_socket.get_socket_fd()); - cmd_worker = new command_worker(client_command_socket); - if (!cmd_worker->start()) + cmd_worker = new(std::nothrow) command_worker(client_command_socket); + + if (!cmd_worker) { + ERR("Failed to allocate memory"); + continue; + } + + if(!cmd_worker->start()) delete cmd_worker; } } @@ -88,19 +96,18 @@ void server::run(void) m_client_accep_socket = csocket(sock_fd); } else { ERR("Failed to get systemd socket, create it by myself!"); - if (!m_client_accep_socket.create(SOCK_STREAM)) { ERR("Failed to create command channel"); return; } - if (!m_client_accep_socket.bind(COMMAND_CHANNEL_PATH)) { + if(!m_client_accep_socket.bind(COMMAND_CHANNEL_PATH)) { ERR("Failed to bind command channel"); m_client_accep_socket.close(); return; } - if (!m_client_accep_socket.listen(MAX_PENDING_CONNECTION)) { + if(!m_client_accep_socket.listen(MAX_PENDING_CONNECTION)) { ERR("Failed to listen command channel"); return; } @@ -115,13 +122,20 @@ void server::run(void) g_main_loop_run(m_mainloop); g_main_loop_unref(m_mainloop); + return; } void server::stop(void) { - if (m_mainloop) + if(m_mainloop) g_main_loop_quit(m_mainloop); m_client_accep_socket.close(); } + +server& server::get_instance() +{ + static server inst; + return inst; +} diff --git a/src/server/server.h b/src/server/server.h index b31e02f..4406433 100755 --- a/src/server/server.h +++ b/src/server/server.h @@ -17,8 +17,8 @@ * */ -#ifndef _SERVER_H_ -#define _SERVER_H_ +#ifndef SERVER_H_ +#define SERVER_H_ #include #include @@ -37,10 +37,7 @@ private: public: void run(void); void stop(void); - static server &get_instance() { - static server inst; - return inst; - } + static server& get_instance(); }; -#endif /*_SERVER_H_*/ +#endif diff --git a/src/shared/CMakeLists.txt b/src/shared/CMakeLists.txt index 5ec473e..8ef03d8 100755 --- a/src/shared/CMakeLists.txt +++ b/src/shared/CMakeLists.txt @@ -21,7 +21,7 @@ add_definitions(-DPREFIX="${CMAKE_INSTALL_PREFIX}") add_definitions(-DLOCALEDIR="$ENV{DATAFS}/share/locale") add_definitions(-DFACTORYFS="$ENV{FACTORYFS}") add_definitions(-DDATAFS="$ENV{DATAFS}") -add_definitions(-Wall -std=gnu++0x) +add_definitions(-Wall -fPIC -std=gnu++0x) add_definitions(-DUSE_DLOG_LOG) #add_definitions(-DX1_PROF) #add_definitions(-D_GETTEXT) @@ -59,6 +59,7 @@ add_library(sensord-share SHARED cbase_lock.cpp cmutex.cpp common.cpp + sensor_info.cpp ) target_link_libraries(sensord-server ${rpkgs_LDFLAGS} "-lrt -ldl -pthread" "sensord-share") @@ -87,6 +88,7 @@ install(FILES cbase_lock.h cmutex.h common.h + sensor_info.h iio_common.h DESTINATION include/${PROJECT_NAME} ) diff --git a/src/shared/cbase_lock.cpp b/src/shared/cbase_lock.cpp index 52b1993..c4f6107 100755 --- a/src/shared/cbase_lock.cpp +++ b/src/shared/cbase_lock.cpp @@ -24,6 +24,7 @@ #include #include + cbase_lock::cbase_lock() { m_history_mutex = PTHREAD_MUTEX_INITIALIZER; @@ -34,7 +35,7 @@ cbase_lock::~cbase_lock() pthread_mutex_destroy(&m_history_mutex); } -void cbase_lock::lock(lock_type type, const char *expr, const char *module, const char *func, int line) +void cbase_lock::lock(lock_type type, const char* expr, const char *module, const char *func, int line) { int ret = 0; char m_curent_info[OWNER_INFO_LEN]; @@ -42,6 +43,8 @@ void cbase_lock::lock(lock_type type, const char *expr, const char *module, cons unsigned long long lock_waiting_start_time = 0; unsigned long long lock_acquired_time = 0; unsigned long long waiting_time = 0; + + snprintf(m_curent_info, OWNER_INFO_LEN, "%s:%s(%d)", module, func, line); if (type == LOCK_TYPE_MUTEX) @@ -66,6 +69,7 @@ void cbase_lock::lock(lock_type type, const char *expr, const char *module, cons m_curent_info, expr, this, m_owner_info); pthread_mutex_unlock(&m_history_mutex); + if (type == LOCK_TYPE_MUTEX) lock_impl(); else if (type == LOCK_TYPE_READ) @@ -75,20 +79,33 @@ void cbase_lock::lock(lock_type type, const char *expr, const char *module, cons gettimeofday(&sv, NULL); lock_acquired_time = MICROSECONDS(sv); + waiting_time = lock_acquired_time - lock_waiting_start_time; - pthread_mutex_lock(&m_history_mutex); + pthread_mutex_lock(&m_history_mutex); INFO("%s acquires lock after waiting %lluus, %s(0x%x) was previously owned in %s", m_curent_info, waiting_time, expr, this, m_owner_info); snprintf(m_owner_info, OWNER_INFO_LEN, "%s", m_curent_info); pthread_mutex_unlock(&m_history_mutex); } + +void cbase_lock::lock(lock_type type) +{ + if (type == LOCK_TYPE_MUTEX) + lock_impl(); + else if (type == LOCK_TYPE_READ) + read_lock_impl(); + else if (type == LOCK_TYPE_WRITE) + write_lock_impl(); +} + void cbase_lock::unlock(void) { unlock_impl(); } + int cbase_lock::lock_impl(void) { return 0; @@ -123,3 +140,20 @@ int cbase_lock::unlock_impl(void) { return 0; } + +Autolock::Autolock(cbase_lock &m, lock_type type, const char* expr, const char *module, const char *func, int line) +: m_lock(m) +{ + m_lock.lock(type, expr, module, func, line); +} + +Autolock::Autolock(cbase_lock &m, lock_type type) +: m_lock(m) +{ + m_lock.lock(type); +} + +Autolock::~Autolock() +{ + m_lock.unlock(); +} diff --git a/src/shared/cbase_lock.h b/src/shared/cbase_lock.h index 2bebf66..2b0082a 100755 --- a/src/shared/cbase_lock.h +++ b/src/shared/cbase_lock.h @@ -17,8 +17,8 @@ * */ -#ifndef _CBASE_LOCK_H_ -#define _CBASE_LOCK_H_ +#if !defined(_CBASE_LOCK_CLASS_H_) +#define _CBASE_LOCK_CLASS_H_ #include @@ -28,6 +28,7 @@ enum lock_type { LOCK_TYPE_WRITE, }; +#ifdef _LOCK_DEBUG #define AUTOLOCK(x) Autolock x##_autolock((x),LOCK_TYPE_MUTEX, #x, __MODULE__, __func__, __LINE__) #define AUTOLOCK_R(x) Autolock x##_autolock_r((x),LOCK_TYPE_READ, #x, __MODULE__, __func__, __LINE__) #define AUTOLOCK_W(x) Autolock x##_autolock_w((x),LOCK_TYPE_WRITE, #x, __MODULE__, __func__, __LINE__) @@ -35,6 +36,16 @@ enum lock_type { #define LOCK_R(x) (x).lock(LOCK_TYPE_READ, #x, __MODULE__, __func__, __LINE__) #define LOCK_W(x) (x).lock(LOCK_TYPE_WRITE, #x, __MODULE__, __func__, __LINE__) #define UNLOCK(x) (x).unlock() +#else +#define AUTOLOCK(x) Autolock x##_autolock((x),LOCK_TYPE_MUTEX) +#define AUTOLOCK_R(x) Autolock x##_autolock_r((x),LOCK_TYPE_READ) +#define AUTOLOCK_W(x) Autolock x##_autolock_w((x),LOCK_TYPE_WRITE) +#define LOCK(x) (x).lock() +#define LOCK_R(x) (x).lock(LOCK_TYPE_READ) +#define LOCK_W(x) (x).lock(LOCK_TYPE_WRITE) +#define UNLOCK(x) (x).unlock() +#endif + class cbase_lock { @@ -42,7 +53,8 @@ public: cbase_lock(); virtual ~cbase_lock(); - void lock(lock_type type, const char *expr, const char *module, const char *func, int line); + void lock(lock_type type, const char* expr, const char *module, const char *func, int line); + void lock(lock_type type); void unlock(void); protected: @@ -64,15 +76,12 @@ private: class Autolock { private: - cbase_lock &m_lock; + cbase_lock& m_lock; public: - Autolock(cbase_lock &m, lock_type type, const char *expr, const char *module, const char *func, int line) : m_lock(m) { - m_lock.lock(type, expr, module, func, line); - } - - ~Autolock() { - m_lock.unlock(); - } + Autolock(cbase_lock &m, lock_type type, const char* expr, const char *module, const char *func, int line); + Autolock(cbase_lock &m, lock_type type); + ~Autolock(); }; -#endif /*_CBASE_LOCK_H_*/ +#endif +// End of a file diff --git a/src/shared/cclient_info_manager.cpp b/src/shared/cclient_info_manager.cpp index 19347a0..3302167 100755 --- a/src/shared/cclient_info_manager.cpp +++ b/src/shared/cclient_info_manager.cpp @@ -26,164 +26,157 @@ using std::pair; cclient_info_manager::cclient_info_manager() { } - cclient_info_manager::~cclient_info_manager() { m_clients.clear(); } -unsigned int cclient_info_manager::get_interval(const int client_id, const sensor_type_t sensor) +cclient_info_manager& cclient_info_manager::get_instance() { - AUTOLOCK(m_mutex); - client_id_sensor_record_map::iterator it_record; - it_record = m_clients.find(client_id); - - if (it_record == m_clients.end()) { - ERR("Client[%d] is not found", client_id); - return 0; - } - - return it_record->second.get_interval(sensor); + static cclient_info_manager inst; + return inst; } -bool cclient_info_manager::is_sensor_used(const sensor_type_t sensor, const event_situation mode) + +unsigned int cclient_info_manager::get_interval(int client_id, sensor_id_t sensor_id) { AUTOLOCK(m_mutex); - client_id_sensor_record_map::iterator it_record; - it_record = m_clients.begin(); - while (it_record != m_clients.end()) { - if (it_record->second.is_sensor_used(sensor, mode)) - return true; + auto it_record = m_clients.find(client_id); - ++it_record; + if (it_record == m_clients.end()) { + ERR("Client[%d] is not found", client_id); + return 0; } - return false; + return it_record->second.get_interval(sensor_id); } -bool cclient_info_manager::get_registered_events(const int client_id, const sensor_type_t sensor, event_type_vector &event_vec) +bool cclient_info_manager::get_registered_events(int client_id, sensor_id_t sensor_id, event_type_vector &event_vec) { AUTOLOCK(m_mutex); - client_id_sensor_record_map::iterator it_record; - it_record = m_clients.find(client_id); + + auto it_record = m_clients.find(client_id); if (it_record == m_clients.end()) { ERR("Client[%d] is not found", client_id); return false; } - if (!it_record->second.get_registered_events(sensor, event_vec)) + if(!it_record->second.get_registered_events(sensor_id, event_vec)) return false; return true; } -bool cclient_info_manager::register_event(const int client_id, const unsigned int event_type) +bool cclient_info_manager::register_event(int client_id, sensor_id_t sensor_id, unsigned int event_type) { AUTOLOCK(m_mutex); - client_id_sensor_record_map::iterator it_record; - it_record = m_clients.find(client_id); + + auto it_record = m_clients.find(client_id); if (it_record == m_clients.end()) { ERR("Client[%d] is not found", client_id); return false; } - if (!it_record->second.register_event(event_type)) + if(!it_record->second.register_event(sensor_id, event_type)) return false; return true; } -bool cclient_info_manager::unregister_event(const int client_id, const unsigned int event_type) +bool cclient_info_manager::unregister_event(int client_id, sensor_id_t sensor_id, unsigned int event_type) { AUTOLOCK(m_mutex); - client_id_sensor_record_map::iterator it_record; - it_record = m_clients.find(client_id); + + auto it_record = m_clients.find(client_id); if (it_record == m_clients.end()) { ERR("Client[%d] is not found", client_id); return false; } - if (!it_record->second.unregister_event(event_type)) + if(!it_record->second.unregister_event(sensor_id, event_type)) return false; return true; } -bool cclient_info_manager::set_interval(const int client_id, const sensor_type_t sensor, const unsigned int interval) +bool cclient_info_manager::set_interval(int client_id, sensor_id_t sensor_id, unsigned int interval) { AUTOLOCK(m_mutex); - client_id_sensor_record_map::iterator it_record; - it_record = m_clients.find(client_id); + + auto it_record = m_clients.find(client_id); if (it_record == m_clients.end()) { ERR("Client[%d] is not found", client_id); return false; } - if (!it_record->second.set_interval(sensor, interval)) + if(!it_record->second.set_interval(sensor_id, interval)) return false; return true; } -bool cclient_info_manager::set_option(const int client_id, const sensor_type_t sensor, const int option) +bool cclient_info_manager::set_option(int client_id, sensor_id_t sensor_id, int option) { AUTOLOCK(m_mutex); - client_id_sensor_record_map::iterator it_record; - it_record = m_clients.find(client_id); + + auto it_record = m_clients.find(client_id); if (it_record == m_clients.end()) { ERR("Client[%d] is not found", client_id); return false; } - if (!it_record->second.set_option(sensor, option)) + if(!it_record->second.set_option(sensor_id, option)) return false; return true; } -bool cclient_info_manager::set_start(const int client_id, const sensor_type_t sensor, bool start) +bool cclient_info_manager::set_start(int client_id, sensor_id_t sensor_id, bool start) { AUTOLOCK(m_mutex); - client_id_sensor_record_map::iterator it_record; - it_record = m_clients.find(client_id); + + auto it_record = m_clients.find(client_id); if (it_record == m_clients.end()) { ERR("Client[%d] is not found", client_id); return false; } - if (!it_record->second.set_start(sensor, start)) + if(!it_record->second.set_start(sensor_id, start)) return false; return true; + } -bool cclient_info_manager::is_started(const int client_id, const sensor_type_t sensor) +bool cclient_info_manager::is_started(int client_id, sensor_id_t sensor_id) { AUTOLOCK(m_mutex); - client_id_sensor_record_map::iterator it_record; - it_record = m_clients.find(client_id); + + auto it_record = m_clients.find(client_id); if (it_record == m_clients.end()) { ERR("Client[%d] is not found", client_id); return false; } - return it_record->second.is_started(sensor); + return it_record->second.is_started(sensor_id); } int cclient_info_manager::create_client_record(void) { AUTOLOCK(m_mutex); + int client_id = 0; + cclient_sensor_record client_record; while (m_clients.count(client_id) > 0) @@ -195,16 +188,18 @@ int cclient_info_manager::create_client_record(void) } client_record.set_client_id(client_id); - m_clients.insert(pair (client_id, client_record)); + + m_clients.insert(pair (client_id, client_record)); + return client_id; } -bool cclient_info_manager::remove_client_record(const int client_id) +bool cclient_info_manager::remove_client_record(int client_id) { AUTOLOCK(m_mutex); - client_id_sensor_record_map::iterator it_record; - it_record = m_clients.find(client_id); + + auto it_record = m_clients.find(client_id); if (it_record == m_clients.end()) { ERR("Client[%d] is not found", client_id); @@ -212,7 +207,9 @@ bool cclient_info_manager::remove_client_record(const int client_id) } m_clients.erase(it_record); + INFO("Client record for client[%d] is removed from client info manager", client_id); + return true; } @@ -220,8 +217,8 @@ bool cclient_info_manager::remove_client_record(const int client_id) bool cclient_info_manager::has_client_record(int client_id) { AUTOLOCK(m_mutex); - client_id_sensor_record_map::iterator it_record; - it_record = m_clients.find(client_id); + + auto it_record = m_clients.find(client_id); return (it_record != m_clients.end()); } @@ -230,8 +227,8 @@ bool cclient_info_manager::has_client_record(int client_id) void cclient_info_manager::set_client_info(int client_id, pid_t pid) { AUTOLOCK(m_mutex); - client_id_sensor_record_map::iterator it_record; - it_record = m_clients.find(client_id); + + auto it_record = m_clients.find(client_id); if (it_record == m_clients.end()) { ERR("Client[%d] is not found", client_id); @@ -239,14 +236,15 @@ void cclient_info_manager::set_client_info(int client_id, pid_t pid) } it_record->second.set_client_info(pid); + return; } -const char *cclient_info_manager::get_client_info(int client_id) +const char* cclient_info_manager::get_client_info(int client_id) { AUTOLOCK(m_mutex); - client_id_sensor_record_map::iterator it_record; - it_record = m_clients.find(client_id); + + auto it_record = m_clients.find(client_id); if (it_record == m_clients.end()) { DBG("Client[%d] is not found", client_id); @@ -256,84 +254,115 @@ const char *cclient_info_manager::get_client_info(int client_id) return it_record->second.get_client_info(); } -bool cclient_info_manager::create_sensor_record(int client_id, const sensor_type_t sensor) +bool cclient_info_manager::set_permission(int client_id, int permission) +{ + AUTOLOCK(m_mutex); + + auto it_record = m_clients.find(client_id); + + if (it_record == m_clients.end()) { + DBG("Client[%d] is not found", client_id); + return false; + } + + it_record->second.set_permission(permission); + return true; +} + +bool cclient_info_manager::get_permission(int client_id, int &permission) +{ + AUTOLOCK(m_mutex); + + auto it_record = m_clients.find(client_id); + + if (it_record == m_clients.end()) { + DBG("Client[%d] is not found", client_id); + return false; + } + + permission = it_record->second.get_permission(); + return true; +} + +bool cclient_info_manager::create_sensor_record(int client_id, sensor_id_t sensor_id) { AUTOLOCK(m_mutex); - client_id_sensor_record_map::iterator it_record; - it_record = m_clients.find(client_id); + + auto it_record = m_clients.find(client_id); if (it_record == m_clients.end()) { ERR("Client record[%d] is not registered", client_id); return false; } - it_record->second.add_sensor_usage(sensor); + it_record->second.add_sensor_usage(sensor_id); + return true; } -bool cclient_info_manager::remove_sensor_record(const int client_id, const sensor_type_t sensor) +bool cclient_info_manager::remove_sensor_record(int client_id, sensor_id_t sensor_id) { AUTOLOCK(m_mutex); - client_id_sensor_record_map::iterator it_record; - it_record = m_clients.find(client_id); + + auto it_record = m_clients.find(client_id); if (it_record == m_clients.end()) { ERR("Client[%d] is not found", client_id); return false; } - if (!it_record->second.remove_sensor_usage(sensor)) + if(!it_record->second.remove_sensor_usage(sensor_id)) return false; - if (!it_record->second.has_sensor_usage()) + if(!it_record->second.has_sensor_usage()) remove_client_record(client_id); return true; } -bool cclient_info_manager::has_sensor_record(const int client_id, const sensor_type_t sensor) +bool cclient_info_manager::has_sensor_record(int client_id, sensor_id_t sensor_id) { AUTOLOCK(m_mutex); - client_id_sensor_record_map::iterator it_record; - it_record = m_clients.find(client_id); + + auto it_record = m_clients.find(client_id); if (it_record == m_clients.end()) { DBG("Client[%d] is not found", client_id); return false; } - if (!it_record->second.has_sensor_usage(sensor)) + if(!it_record->second.has_sensor_usage(sensor_id)) return false; return true; } -bool cclient_info_manager::has_sensor_record(const int client_id) +bool cclient_info_manager::has_sensor_record(int client_id) { AUTOLOCK(m_mutex); - client_id_sensor_record_map::iterator it_record; - it_record = m_clients.find(client_id); + + auto it_record = m_clients.find(client_id); if (it_record == m_clients.end()) { DBG("Client[%d] is not found", client_id); return false; } - if (!it_record->second.has_sensor_usage()) + if(!it_record->second.has_sensor_usage()) return false; return true; } -bool cclient_info_manager::get_listener_ids(const unsigned int event_type, const event_situation mode, client_id_vec &id_vec) +bool cclient_info_manager::get_listener_ids(sensor_id_t sensor_id, unsigned int event_type, client_id_vec &id_vec) { AUTOLOCK(m_mutex); - client_id_sensor_record_map::iterator it_record; - it_record = m_clients.begin(); + + auto it_record = m_clients.begin(); while (it_record != m_clients.end()) { - if (it_record->second.is_listening_event(event_type, mode)) + if(it_record->second.is_listening_event(sensor_id, event_type)) id_vec.push_back(it_record->first); ++it_record; @@ -342,11 +371,11 @@ bool cclient_info_manager::get_listener_ids(const unsigned int event_type, const return true; } -bool cclient_info_manager::get_event_socket(const int client_id, csocket &socket) +bool cclient_info_manager::get_event_socket(int client_id, csocket &socket) { AUTOLOCK(m_mutex); - client_id_sensor_record_map::iterator it_record; - it_record = m_clients.find(client_id); + + auto it_record = m_clients.find(client_id); if (it_record == m_clients.end()) { ERR("Client[%d] is not found", client_id); @@ -354,14 +383,16 @@ bool cclient_info_manager::get_event_socket(const int client_id, csocket &socket } it_record->second.get_event_socket(socket); + return true; } -bool cclient_info_manager::set_event_socket(const int client_id, const csocket &socket) +bool cclient_info_manager::set_event_socket(int client_id, const csocket &socket) { + AUTOLOCK(m_mutex); - client_id_sensor_record_map::iterator it_record; - it_record = m_clients.find(client_id); + + auto it_record = m_clients.find(client_id); if (it_record == m_clients.end()) { ERR("Client[%d] is not found", client_id); @@ -369,5 +400,6 @@ bool cclient_info_manager::set_event_socket(const int client_id, const csocket & } it_record->second.set_event_socket(socket); + return true; } diff --git a/src/shared/cclient_info_manager.h b/src/shared/cclient_info_manager.h index 733b95a..ccf67ae 100755 --- a/src/shared/cclient_info_manager.h +++ b/src/shared/cclient_info_manager.h @@ -17,64 +17,60 @@ * */ -#ifndef _CCLIENT_INFO_MANAGER_H_ -#define _CCLIENT_INFO_MANAGER_H_ +#ifndef CCLIENT_INFO_MANAGER_H_ +#define CCLIENT_INFO_MANAGER_H_ #include -#include +#include #include #include +using std::unordered_map; -using std::map; - -typedef map client_id_sensor_record_map; +typedef unordered_map client_id_sensor_record_map; typedef vector client_id_vec; -class cclient_info_manager -{ -public: - static cclient_info_manager &get_instance() { - static cclient_info_manager inst; - return inst; - } +class cclient_info_manager { +public: + static cclient_info_manager& get_instance(); int create_client_record(void); - bool remove_client_record(const int client_id); + bool remove_client_record(int client_id); bool has_client_record(int client_id); void set_client_info(int client_id, pid_t pid); - const char *get_client_info(int client_id); + const char* get_client_info(int client_id); + + bool set_permission(int client_id, int permission); + bool get_permission(int client_id, int &permission); - bool create_sensor_record(int client_id, const sensor_type_t sensor); - bool remove_sensor_record(const int client_id, const sensor_type_t sensor); - bool has_sensor_record(const int client_id, const sensor_type_t sensor); - bool has_sensor_record(const int client_id); + bool create_sensor_record(int client_id, sensor_id_t sensor_id); + bool remove_sensor_record(int client_id, sensor_id_t sensor_id); + bool has_sensor_record(int client_id, sensor_id_t sensor_id); + bool has_sensor_record(int client_id); - bool register_event(const int client_id, const unsigned int event_type); - bool unregister_event(const int client_id, const unsigned int event_type); - bool is_sensor_event_registered(const int client_id, const unsigned int event_type); + bool register_event(int client_id, sensor_id_t sensor_id, unsigned int event_type); + bool unregister_event(int client_id, sensor_id_t sensor_id, unsigned int event_type); - bool set_interval(const int client_id, const sensor_type_t sensor, const unsigned int interval); - unsigned int get_interval(const int client_id, const sensor_type_t sensor); - bool set_option(const int client_id, const sensor_type_t sensor, const int option); + bool set_interval(int client_id, sensor_id_t sensor_id, unsigned int interval); + unsigned int get_interval(int client_id, sensor_id_t sensor_id); + bool set_option(int client_id, sensor_id_t sensor_id, int option); - bool set_start(const int client_id, const sensor_type_t sensor, bool start); - bool is_started(const int client_id, const sensor_type_t sensor); + bool set_start(int client_id, sensor_id_t sensor_id, bool start); + bool is_started(int client_id, sensor_id_t sensor_id); - bool is_sensor_used(const sensor_type_t sensor, const event_situation mode); - bool get_registered_events(const int client_id, const sensor_type_t sensor, event_type_vector &event_vec); + bool get_registered_events(int client_id, sensor_id_t sensor_id, event_type_vector &event_vec); - bool get_listener_ids(const unsigned int event_type, const event_situation mode, client_id_vec &id_vec); - bool get_event_socket(const int client_id, csocket &sock); - bool set_event_socket(const int client_id, const csocket &sock); + bool get_listener_ids(sensor_id_t sensor_id, unsigned int event_type, client_id_vec &id_vec); + bool get_event_socket(int client_id, csocket &sock); + bool set_event_socket(int client_id, const csocket &sock); private: client_id_sensor_record_map m_clients; cmutex m_mutex; cclient_info_manager(); ~cclient_info_manager(); - cclient_info_manager(cclient_info_manager const &) {}; - cclient_info_manager &operator=(cclient_info_manager const &); + cclient_info_manager(cclient_info_manager const&) {}; + cclient_info_manager& operator=(cclient_info_manager const&); }; -#endif /*_CCLIENT_INFO_MANAGER_H_*/ +#endif /* CCLIENT_INFO_MANAGER_H_ */ diff --git a/src/shared/cclient_sensor_record.cpp b/src/shared/cclient_sensor_record.cpp index 9ddee5a..a302e4c 100755 --- a/src/shared/cclient_sensor_record.cpp +++ b/src/shared/cclient_sensor_record.cpp @@ -22,15 +22,12 @@ using std::pair; -static sensor_type_t get_sensor_type(const unsigned int event_type) -{ - return (sensor_type_t) (event_type >> SENSOR_TYPE_SHIFT); -} - cclient_sensor_record::cclient_sensor_record() : m_client_id(0) , m_pid(-1) +, m_permission(SENSOR_PERMISSION_NONE) { + } cclient_sensor_record::~cclient_sensor_record() @@ -39,16 +36,14 @@ cclient_sensor_record::~cclient_sensor_record() close_event_socket(); } -bool cclient_sensor_record::register_event(const unsigned int event_type) +bool cclient_sensor_record::register_event(sensor_id_t sensor_id, unsigned int event_type) { - sensor_usage_map::iterator it_usage; - sensor_type_t sensor = get_sensor_type(event_type); - it_usage = m_sensor_usages.find(sensor); + auto it_usage = m_sensor_usages.find(sensor_id); if (it_usage == m_sensor_usages.end()) { csensor_usage usage; usage.register_event(event_type); - m_sensor_usages.insert(pair(sensor, usage)); + m_sensor_usages.insert(pair(sensor_id, usage)); return true; } @@ -60,14 +55,12 @@ bool cclient_sensor_record::register_event(const unsigned int event_type) return true; } -bool cclient_sensor_record::unregister_event(const unsigned int event_type) +bool cclient_sensor_record::unregister_event(sensor_id_t sensor_id, unsigned int event_type) { - sensor_usage_map::iterator it_usage; - sensor_type_t sensor = get_sensor_type(event_type); - it_usage = m_sensor_usages.find(sensor); + auto it_usage = m_sensor_usages.find(sensor_id); if (it_usage == m_sensor_usages.end()) { - ERR("Sensor[0x%x] is not registered", sensor); + ERR("Sensor[0x%x] is not registered", sensor_id); return false; } @@ -79,15 +72,14 @@ bool cclient_sensor_record::unregister_event(const unsigned int event_type) return true; } -bool cclient_sensor_record::set_interval(const sensor_type_t sensor, const unsigned int interval) +bool cclient_sensor_record::set_interval(sensor_id_t sensor_id, unsigned int interval) { - sensor_usage_map::iterator it_usage; - it_usage = m_sensor_usages.find(sensor); + auto it_usage = m_sensor_usages.find(sensor_id); if (it_usage == m_sensor_usages.end()) { csensor_usage usage; usage.m_interval = interval; - m_sensor_usages.insert(pair(sensor, usage)); + m_sensor_usages.insert(pair(sensor_id, usage)); } else { it_usage->second.m_interval = interval; } @@ -95,15 +87,14 @@ bool cclient_sensor_record::set_interval(const sensor_type_t sensor, const unsig return true; } -bool cclient_sensor_record::set_option(const sensor_type_t sensor, const int option) +bool cclient_sensor_record::set_option(sensor_id_t sensor_id, int option) { - sensor_usage_map::iterator it_usage; - it_usage = m_sensor_usages.find(sensor); + auto it_usage = m_sensor_usages.find(sensor_id); if (it_usage == m_sensor_usages.end()) { csensor_usage usage; usage.m_option = option; - m_sensor_usages.insert(pair(sensor, usage)); + m_sensor_usages.insert(pair(sensor_id, usage)); } else { it_usage->second.m_option = option; } @@ -111,15 +102,15 @@ bool cclient_sensor_record::set_option(const sensor_type_t sensor, const int opt return true; } -bool cclient_sensor_record::set_start(const sensor_type_t sensor, bool start) + +bool cclient_sensor_record::set_start(sensor_id_t sensor_id, bool start) { - sensor_usage_map::iterator it_usage; - it_usage = m_sensor_usages.find(sensor); + auto it_usage = m_sensor_usages.find(sensor_id); if (it_usage == m_sensor_usages.end()) { csensor_usage usage; usage.m_start = start; - m_sensor_usages.insert(pair(sensor, usage)); + m_sensor_usages.insert(pair(sensor_id, usage)); } else { it_usage->second.m_start = start; } @@ -127,10 +118,9 @@ bool cclient_sensor_record::set_start(const sensor_type_t sensor, bool start) return true; } -bool cclient_sensor_record::is_started(const sensor_type_t sensor) +bool cclient_sensor_record::is_started(sensor_id_t sensor_id) { - sensor_usage_map::iterator it_usage; - it_usage = m_sensor_usages.find(sensor); + auto it_usage = m_sensor_usages.find(sensor_id); if (it_usage == m_sensor_usages.end()) return false; @@ -138,78 +128,55 @@ bool cclient_sensor_record::is_started(const sensor_type_t sensor) return it_usage->second.m_start; } -unsigned int cclient_sensor_record::get_interval(const sensor_type_t sensor) + + +unsigned int cclient_sensor_record::get_interval(sensor_id_t sensor_id) { - sensor_usage_map::iterator it_usage; - it_usage = m_sensor_usages.find(sensor); + auto it_usage = m_sensor_usages.find(sensor_id); if (it_usage == m_sensor_usages.end()) { - ERR("Sensor[0x%x] is not found", sensor); + ERR("Sensor[0x%x] is not found", sensor_id); return 0; } return it_usage->second.m_interval; } -bool cclient_sensor_record::is_sensor_used(const sensor_type_t sensor, const event_situation mode) -{ - sensor_usage_map::iterator it_usage; - it_usage = m_sensor_usages.find(sensor); - - if (it_usage == m_sensor_usages.end()) - return false; - - if ((mode == SITUATION_LCD_OFF || mode == SITUATION_SURVIVAL_MODE) && - (it_usage->second.m_option != SENSOR_OPTION_ALWAYS_ON)) - return false; - - return true; -} - -bool cclient_sensor_record::is_listening_event(const unsigned int event_type, const event_situation mode) +bool cclient_sensor_record::is_listening_event(sensor_id_t sensor_id, unsigned int event_type) { - sensor_usage_map::iterator it_usage; - sensor_type_t sensor = get_sensor_type(event_type); - it_usage = m_sensor_usages.find(sensor); + auto it_usage = m_sensor_usages.find(sensor_id); if (it_usage == m_sensor_usages.end()) return false; - if ((mode == SITUATION_LCD_OFF || mode == SITUATION_SURVIVAL_MODE) && - (it_usage->second.m_option != SENSOR_OPTION_ALWAYS_ON)) - return false; - if (it_usage->second.is_event_registered(event_type)) return true; return false; } -bool cclient_sensor_record::add_sensor_usage(const sensor_type_t sensor) +bool cclient_sensor_record::add_sensor_usage(sensor_id_t sensor_id) { - sensor_usage_map::iterator it_usage; - it_usage = m_sensor_usages.find(sensor); + auto it_usage = m_sensor_usages.find(sensor_id); if (it_usage != m_sensor_usages.end()) { - ERR("Sensor[0x%x] is already registered", sensor); + ERR("Sensor[0x%x] is already registered", sensor_id); return false; } csensor_usage usage; - m_sensor_usages.insert(pair (sensor, usage)); + m_sensor_usages.insert(pair (sensor_id, usage)); return true; } -bool cclient_sensor_record::remove_sensor_usage(const sensor_type_t sensor) +bool cclient_sensor_record::remove_sensor_usage(sensor_id_t sensor_id) { - sensor_usage_map::iterator it_usage; - it_usage = m_sensor_usages.find(sensor); + auto it_usage = m_sensor_usages.find(sensor_id); if (it_usage == m_sensor_usages.end()) { - ERR("Sensor[0x%x] is not found", sensor); + ERR("Sensor[0x%x] is not found", sensor_id); return false; } - m_sensor_usages.erase(it_usage); return true; } @@ -222,31 +189,33 @@ bool cclient_sensor_record::has_sensor_usage(void) return true; } -bool cclient_sensor_record::has_sensor_usage(const sensor_type_t sensor) + +bool cclient_sensor_record::has_sensor_usage(sensor_id_t sensor_id) { - sensor_usage_map::iterator it_usage; - it_usage = m_sensor_usages.find(sensor); + auto it_usage = m_sensor_usages.find(sensor_id); if (it_usage == m_sensor_usages.end()) { - DBG("Sensor[0x%x] is not found", sensor); + DBG("Sensor[0x%x] is not found", sensor_id); return false; } return true; } -bool cclient_sensor_record::get_registered_events(const sensor_type_t sensor, event_type_vector &event_vec) + +bool cclient_sensor_record::get_registered_events(sensor_id_t sensor_id, event_type_vector &event_vec) { - sensor_usage_map::iterator it_usage; - it_usage = m_sensor_usages.find(sensor); + auto it_usage = m_sensor_usages.find(sensor_id); if (it_usage == m_sensor_usages.end()) { - DBG("Sensor[0x%x] is not found", sensor); + DBG("Sensor[0x%x] is not found", sensor_id); return false; } copy(it_usage->second.m_reg_events.begin(), it_usage->second.m_reg_events.end(), back_inserter(event_vec)); + return true; + } void cclient_sensor_record::set_client_id(int client_id) @@ -258,17 +227,31 @@ void cclient_sensor_record::set_client_info(pid_t pid) { char client_info[NAME_MAX + 32]; char proc_name[NAME_MAX]; + m_pid = pid; get_proc_name(pid, proc_name); + snprintf(client_info, sizeof(client_info), "%s[pid=%d, id=%d]", proc_name, m_pid, m_client_id); m_client_info.assign(client_info); + } -const char *cclient_sensor_record::get_client_info(void) +const char* cclient_sensor_record::get_client_info(void) { return m_client_info.c_str(); } +void cclient_sensor_record::set_permission(int permission) +{ + m_permission = permission; +} + +int cclient_sensor_record::get_permission(void) +{ + return m_permission; +} + + void cclient_sensor_record::set_event_socket(const csocket &socket) { m_event_socket = socket; @@ -279,8 +262,8 @@ void cclient_sensor_record::get_event_socket(csocket &socket) socket = m_event_socket; } + bool cclient_sensor_record::close_event_socket(void) { return m_event_socket.close(); } - diff --git a/src/shared/cclient_sensor_record.h b/src/shared/cclient_sensor_record.h index 17d1341..c2d9ef7 100755 --- a/src/shared/cclient_sensor_record.h +++ b/src/shared/cclient_sensor_record.h @@ -17,21 +17,20 @@ * */ -#ifndef _CCLIENT_SENSOR_RECORD_H_ -#define _CCLIENT_SENSOR_RECORD_H_ +#ifndef CCLIENT_SENSOR_RECORD_H_ +#define CCLIENT_SENSOR_RECORD_H_ -#include +#include #include #include #include -#include +#include -using std::map; +using std::unordered_map; -typedef map sensor_usage_map; +typedef unordered_map sensor_usage_map; -class cclient_sensor_record -{ +class cclient_sensor_record { public: cclient_sensor_record(); ~cclient_sensor_record(); @@ -39,27 +38,29 @@ public: void set_client_id(int client_id); void set_client_info(pid_t pid); - const char *get_client_info(void); + const char* get_client_info(void); - bool register_event(const unsigned int event_type); - bool unregister_event(const unsigned int event_type); + void set_permission(int permission); + int get_permission(void); - bool set_interval(const sensor_type_t sensor, const unsigned int interval); - unsigned int get_interval(const sensor_type_t sensor); - bool set_option(const sensor_type_t sensor, const int option); + bool register_event(sensor_id_t sensor_id, unsigned int event_type); + bool unregister_event(sensor_id_t sensor_id, unsigned int event_type); - bool set_start(const sensor_type_t sensor, bool start); - bool is_started(const sensor_type_t sensor); + bool set_interval(sensor_id_t sensor_id, unsigned int interval); + unsigned int get_interval(sensor_id_t sensor_id); + bool set_option(sensor_id_t sensor_id, int option); - bool is_sensor_used(const sensor_type_t sensor, const event_situation mode); - bool is_listening_event(const unsigned int event_type, const event_situation mode); + bool set_start(sensor_id_t sensor_id, bool start); + bool is_started(sensor_id_t sensor_id); + + bool is_listening_event(sensor_id_t sensor_id, unsigned int event_type); bool has_sensor_usage(void); - bool has_sensor_usage(const sensor_type_t sensor); + bool has_sensor_usage(sensor_id_t sensor_id); - bool get_registered_events(const sensor_type_t sensor, event_type_vector &event_vec); + bool get_registered_events(sensor_id_t sensor_id, event_type_vector &event_vec); - bool add_sensor_usage(const sensor_type_t sensor); - bool remove_sensor_usage(const sensor_type_t sensor); + bool add_sensor_usage(sensor_id_t sensor_id); + bool remove_sensor_usage(sensor_id_t sensor_id); void set_event_socket(const csocket &socket); void get_event_socket(csocket &socket); @@ -68,9 +69,10 @@ public: private: int m_client_id; pid_t m_pid; + int m_permission; string m_client_info; csocket m_event_socket; sensor_usage_map m_sensor_usages; }; -#endif /*_CCLIENT_SENSOR_RECORD_H_*/ +#endif /* CCLIENT_SENSOR_RECORD_H_ */ diff --git a/src/shared/cconfig.cpp b/src/shared/cconfig.cpp index 389ce24..34a7337 100755 --- a/src/shared/cconfig.cpp +++ b/src/shared/cconfig.cpp @@ -21,10 +21,12 @@ #include "common.h" #include #include -#include #include +#include +#include using namespace config; +using std::ifstream; #define ROOT_ELEMENT "SENSOR" #define TEXT_ELEMENT "text" @@ -35,29 +37,47 @@ CConfig::CConfig() { } -bool CConfig::load_config(const string &config_path) +CConfig& CConfig::get_instance(void) +{ + static bool load_done = false; + static CConfig inst; + + if (!load_done) { + inst.load_config(); + inst.get_device_id(); + if (!inst.m_device_id.empty()) + INFO("Device ID = %s", inst.m_device_id.c_str()); + else + ERR("Failed to get Device ID"); + load_done = true; + } + + return inst; +} + +bool CConfig::load_config(const string& config_path) { xmlDocPtr doc; xmlNodePtr cur; - DBG("CConfig::load_config(\"%s\") is called!", config_path.c_str()); + DBG("CConfig::load_config(\"%s\") is called!\n",config_path.c_str()); + doc = xmlParseFile(config_path.c_str()); if (doc == NULL) { - ERR("There is no %s", config_path.c_str()); + ERR("There is no %s\n",config_path.c_str()); return false; } cur = xmlDocGetRootElement(doc); - - if (cur == NULL) { - ERR("There is no root element in %s", config_path.c_str()); + if(cur == NULL) { + ERR("There is no root element in %s\n",config_path.c_str()); xmlFreeDoc(doc); return false; } - if (xmlStrcmp(cur->name, (const xmlChar *)ROOT_ELEMENT)) { - ERR("Wrong type document: there is no [%s] root element in %s", ROOT_ELEMENT, config_path.c_str()); + if(xmlStrcmp(cur->name, (const xmlChar *)ROOT_ELEMENT)) { + ERR("Wrong type document: there is no [%s] root element in %s\n",ROOT_ELEMENT,config_path.c_str()); xmlFreeDoc(doc); return false; } @@ -66,75 +86,75 @@ bool CConfig::load_config(const string &config_path) xmlNodePtr model_node_ptr; xmlNodePtr element_node_ptr; xmlAttrPtr attr_ptr; - char *prop = NULL; + char* prop = NULL; + model_list_node_ptr = cur->xmlChildrenNode; while (model_list_node_ptr != NULL) { - /* skip garbage element, [text] */ - if (!xmlStrcmp(model_list_node_ptr->name, (const xmlChar *)TEXT_ELEMENT)) { + //skip garbage element, [text] + if (!xmlStrcmp(model_list_node_ptr->name,(const xmlChar *)TEXT_ELEMENT)) { model_list_node_ptr = model_list_node_ptr->next; continue; } - /* insert Model_list to config map */ - m_sensor_config[(const char *)model_list_node_ptr->name]; - DBG("<%s>", (const char *)model_list_node_ptr->name); + //insert Model_list to config map + m_sensor_config[(const char*)model_list_node_ptr->name]; + DBG("<%s>\n",(const char*)model_list_node_ptr->name); model_node_ptr = model_list_node_ptr->xmlChildrenNode; - - while (model_node_ptr != NULL) { - /* skip garbage element, [text] */ - if (!xmlStrcmp(model_node_ptr->name, (const xmlChar *)TEXT_ELEMENT)) { + while (model_node_ptr != NULL){ + //skip garbage element, [text] + if (!xmlStrcmp(model_node_ptr->name,(const xmlChar *)TEXT_ELEMENT)) { model_node_ptr = model_node_ptr->next; continue; } + string model_id; - prop = (char *)xmlGetProp(model_node_ptr, (const xmlChar *)MODEL_ID_ATTR); + prop = (char*)xmlGetProp(model_node_ptr,(const xmlChar*)MODEL_ID_ATTR); model_id = prop; free(prop); - /* insert Model to Model_list */ - m_sensor_config[(const char *)model_list_node_ptr->name][model_id]; - DBG("<%s id=\"%s\">", (const char *)model_list_node_ptr->name, model_id.c_str()); + //insert Model to Model_list + m_sensor_config[(const char*)model_list_node_ptr->name][model_id]; + DBG("<%s id=\"%s\">\n",(const char*)model_list_node_ptr->name,model_id.c_str()); element_node_ptr = model_node_ptr->xmlChildrenNode; - while (element_node_ptr != NULL) { - /* skip garbage element, [text] */ - if (!xmlStrcmp(element_node_ptr->name, (const xmlChar *)TEXT_ELEMENT)) { + //skip garbage element, [text] + if (!xmlStrcmp(element_node_ptr->name,(const xmlChar *)TEXT_ELEMENT)) { element_node_ptr = element_node_ptr->next; continue; } - /* insert Element to Model */ - m_sensor_config[(const char *)model_list_node_ptr->name][model_id][(const char *)element_node_ptr->name]; - DBG("<%s id=\"%s\"><%s>", (const char *)model_list_node_ptr->name, model_id.c_str(), (const char *)element_node_ptr->name); + //insert Element to Model + m_sensor_config[(const char*)model_list_node_ptr->name][model_id][(const char*)element_node_ptr->name]; + DBG("<%s id=\"%s\"><%s>\n",(const char*)model_list_node_ptr->name,model_id.c_str(),(const char*)element_node_ptr->name); attr_ptr = element_node_ptr->properties; - while (attr_ptr != NULL) { - string key, value; - key = (char *)attr_ptr->name; - prop = (char *)xmlGetProp(element_node_ptr, attr_ptr->name); + + string key,value; + key = (char*)attr_ptr->name; + prop = (char*)xmlGetProp(element_node_ptr,attr_ptr->name); value = prop; free(prop); - /* insert attribute to Element */ - m_sensor_config[(const char *)model_list_node_ptr->name][model_id][(const char *)element_node_ptr->name][key] = value; - DBG("<%s id=\"%s\"><%s \"%s\"=\"%s\">", (const char *)model_list_node_ptr->name, model_id.c_str(), (const char *)element_node_ptr->name, key.c_str(), value.c_str()); - + //insert attribute to Element + m_sensor_config[(const char*)model_list_node_ptr->name][model_id][(const char*)element_node_ptr->name][key]=value; + DBG("<%s id=\"%s\"><%s \"%s\"=\"%s\">\n",(const char*)model_list_node_ptr->name,model_id.c_str(),(const char*)element_node_ptr->name,key.c_str(),value.c_str()); attr_ptr = attr_ptr->next; } + element_node_ptr = element_node_ptr->next; } - DBG(""); + DBG("\n"); model_node_ptr = model_node_ptr->next; } - DBG(""); + DBG("\n"); model_list_node_ptr = model_list_node_ptr->next; } @@ -142,49 +162,47 @@ bool CConfig::load_config(const string &config_path) return true; } -bool CConfig::get(const string &sensor_type, const string &model_id, const string &element, const string &attr, string &value) + +bool CConfig::get(const string& sensor_type,const string& model_id, const string& element, const string& attr, string& value) { - Sensor_config::iterator it_model_list; - it_model_list = m_sensor_config.find(sensor_type); + auto it_model_list = m_sensor_config.find(sensor_type); - if (it_model_list == m_sensor_config.end()) { - ERR("There is no <%s> element", sensor_type.c_str()); + if (it_model_list == m_sensor_config.end()) { + ERR("There is no <%s> element\n",sensor_type.c_str()); return false; } - Model_list::iterator it_model; - it_model = it_model_list->second.find(model_id); + auto it_model = it_model_list->second.find(model_id); if (it_model == it_model_list->second.end()) { - ERR("There is no <%s id=\"%s\"> element", sensor_type.c_str(), model_id.c_str()); + ERR("There is no <%s id=\"%s\"> element\n",sensor_type.c_str(),model_id.c_str()); return false; } - Model::iterator it_element; - it_element = it_model->second.find(element); + auto it_element = it_model->second.find(element); if (it_element == it_model->second.end()) { - ERR("There is no <%s id=\"%s\"><%s> element", sensor_type.c_str(), model_id.c_str(), element.c_str()); + DBG("There is no <%s id=\"%s\"><%s> element\n",sensor_type.c_str(),model_id.c_str(),element.c_str()); return false; } - Element::iterator it_attr; - it_attr = it_element->second.find(attr); + auto it_attr = it_element->second.find(attr); if (it_attr == it_element->second.end()) { - DBG("There is no <%s id=\"%s\"><%s \"%s\">", sensor_type.c_str(), model_id.c_str(), element.c_str(), attr.c_str()); + DBG("There is no <%s id=\"%s\"><%s \"%s\">\n",sensor_type.c_str(),model_id.c_str(),element.c_str(),attr.c_str()); return false; } value = it_attr->second; + return true; } -bool CConfig::get(const string &sensor_type, const string &model_id, const string &element, const string &attr, double &value) +bool CConfig::get(const string& sensor_type, const string& model_id, const string& element, const string& attr, double& value) { string str_value; - if (get(sensor_type, model_id, element, attr, str_value) == false) + if (get(sensor_type,model_id,element,attr,str_value) == false) return false; istringstream convert(str_value); @@ -195,11 +213,11 @@ bool CConfig::get(const string &sensor_type, const string &model_id, const strin return true; } -bool CConfig::get(const string &sensor_type, const string &model_id, const string &element, const string &attr, long &value) +bool CConfig::get(const string& sensor_type, const string& model_id, const string& element, const string& attr, long& value) { string str_value; - if (get(sensor_type, model_id, element, attr, str_value) == false) + if (get(sensor_type,model_id,element,attr,str_value) == false) return false; istringstream convert(str_value); @@ -210,7 +228,7 @@ bool CConfig::get(const string &sensor_type, const string &model_id, const strin return true; } -bool CConfig::get(const string &sensor_type, const string &model_id, const string &element, string &value) +bool CConfig::get(const string& sensor_type, const string& model_id, const string& element, string& value) { if (get(sensor_type, model_id, element, m_device_id, value)) return true; @@ -221,7 +239,7 @@ bool CConfig::get(const string &sensor_type, const string &model_id, const strin return false; } -bool CConfig::get(const string &sensor_type, const string &model_id, const string &element, double &value) +bool CConfig::get(const string& sensor_type, const string& model_id, const string& element, double& value) { if (get(sensor_type, model_id, element, m_device_id, value)) return true; @@ -232,7 +250,7 @@ bool CConfig::get(const string &sensor_type, const string &model_id, const strin return false; } -bool CConfig::get(const string &sensor_type, const string &model_id, const string &element, long &value) +bool CConfig::get(const string& sensor_type, const string& model_id, const string& element, long& value) { if (get(sensor_type, model_id, element, m_device_id, value)) return true; @@ -243,16 +261,14 @@ bool CConfig::get(const string &sensor_type, const string &model_id, const strin return false; } -bool CConfig::is_supported(const string &sensor_type, const string &model_id) +bool CConfig::is_supported(const string& sensor_type,const string& model_id) { - Sensor_config::iterator it_model_list; - it_model_list = m_sensor_config.find(sensor_type); + auto it_model_list = m_sensor_config.find(sensor_type); if (it_model_list == m_sensor_config.end()) return false; - Model_list::iterator it_model; - it_model = it_model_list->second.find(model_id); + auto it_model = it_model_list->second.find(model_id); if (it_model == it_model_list->second.end()) return false; @@ -262,14 +278,36 @@ bool CConfig::is_supported(const string &sensor_type, const string &model_id) bool CConfig::get_device_id(void) { - int ret; - char *device = NULL; + const string INFO_INI_PATH = "/etc/info.ini"; + const string START_DELIMETER = "Model="; + const string END_DELIMETER = ";"; + string line; + ifstream in_file; + std::size_t start_pos, end_pos; + bool ret = false; + + in_file.open(INFO_INI_PATH); + + if (!in_file.is_open()) + return false; + + while (!in_file.eof()) { + getline(in_file, line); + start_pos = line.find(START_DELIMETER); + + if (start_pos != std::string::npos) { + start_pos = start_pos + START_DELIMETER.size(); + end_pos = line.find(END_DELIMETER, start_pos); - ret = system_info_get_value_string(SYSTEM_INFO_KEY_MODEL, &device); + if (end_pos != std::string::npos) { + m_device_id = line.substr(start_pos, end_pos - start_pos); + ret = true; + break; + } + } + } - if (device) - m_device_id = device; + in_file.close(); - free(device); - return (ret == SYSTEM_INFO_ERROR_NONE); + return ret; } diff --git a/src/shared/cconfig.h b/src/shared/cconfig.h index 7b14edc..0017e27 100755 --- a/src/shared/cconfig.h +++ b/src/shared/cconfig.h @@ -17,20 +17,20 @@ * */ -#ifndef _CCONFIG_H_ -#define _CCONFIG_H_ +#if !defined(_CCONFIG_CLASS_H_) +#define _CCONFIG_CLASS_H_ #include -#include +#include #include -using std::map; +using std::unordered_map; using std::string; using std::istringstream; #define CONFIG_FILE_PATH "/usr/etc/sensors.xml" -typedef map Element; +typedef unordered_map Element; /* * an Element is a group of attributes * @@ -39,7 +39,7 @@ typedef map Element; * */ -typedef map Model; +typedef unordered_map Model; /* * a Model is a group of elements to consist of specific vendor's one sensor configuration * @@ -51,7 +51,7 @@ typedef map Model; * */ -typedef map Model_list; +typedef unordered_map Model_list; /* * a Model_list is a group of Model * @@ -63,7 +63,7 @@ typedef map Model_list; * */ -typedef map Sensor_config; +typedef unordered_map Sensor_config; /* * a SensorConfig represents sensors.xml * @@ -80,40 +80,24 @@ namespace config { private: CConfig(); - CConfig(CConfig const &) {}; - CConfig &operator=(CConfig const &); - bool load_config(const string &config_path = CONFIG_FILE_PATH); + CConfig(CConfig const&) {}; + CConfig& operator=(CConfig const&); + bool load_config(const string& config_path = CONFIG_FILE_PATH); Sensor_config m_sensor_config; string m_device_id; public: - static CConfig &get_instance(void) { - static bool load_done = false; - static CConfig inst; + static CConfig& get_instance(void); - if (!load_done) { - inst.load_config(); - inst.get_device_id(); + bool get(const string& sensor_type, const string& model_id, const string& element, const string& attr, string& value); + bool get(const string& sensor_type, const string& model_id, const string& element, const string& attr, double& value); + bool get(const string& sensor_type, const string& model_id, const string& element, const string& attr, long& value); - if (!inst.m_device_id.empty()) - INFO("Device ID = %s", inst.m_device_id.c_str()); - else - ERR("Failed to get Device ID"); - - load_done = true; - } - - return inst; - } - bool get(const string &sensor_type, const string &model_id, const string &element, const string &attr, string &value); - bool get(const string &sensor_type, const string &model_id, const string &element, const string &attr, double &value); - bool get(const string &sensor_type, const string &model_id, const string &element, const string &attr, long &value); - - bool get(const string &sensor_type, const string &model_id, const string &element, string &value); - bool get(const string &sensor_type, const string &model_id, const string &element, double &value); - bool get(const string &sensor_type, const string &model_id, const string &element, long &value); + bool get(const string& sensor_type, const string& model_id, const string& element, string& value); + bool get(const string& sensor_type, const string& model_id, const string& element, double& value); + bool get(const string& sensor_type, const string& model_id, const string& element, long& value); bool is_supported(const string &sensor_type, const string &model_id); bool get_device_id(void); }; } -#endif /*_CCONFIG_H_*/ +#endif diff --git a/src/shared/cinterval_info_list.cpp b/src/shared/cinterval_info_list.cpp index a4ba3ee..5adf0b5 100755 --- a/src/shared/cinterval_info_list.cpp +++ b/src/shared/cinterval_info_list.cpp @@ -20,6 +20,14 @@ #include #include + +cinterval_info::cinterval_info(int client_id, bool is_processor, unsigned int interval) +{ + this->client_id = client_id; + this->is_processor = is_processor; + this->interval = interval; +} + bool cinterval_info_list::comp_interval_info(cinterval_info a, cinterval_info b) { return a.interval < b.interval; @@ -27,8 +35,7 @@ bool cinterval_info_list::comp_interval_info(cinterval_info a, cinterval_info b) cinterval_info_iterator cinterval_info_list::find_if(int client_id, bool is_processor) { - cinterval_info_iterator iter; - iter = m_list.begin(); + auto iter = m_list.begin(); while (iter != m_list.end()) { if ((iter->client_id == client_id) && (iter->is_processor == is_processor)) @@ -40,10 +47,10 @@ cinterval_info_iterator cinterval_info_list::find_if(int client_id, bool is_proc return iter; } + bool cinterval_info_list::add_interval(int client_id, unsigned int interval, bool is_processor) { - cinterval_info_iterator iter; - iter = find_if(client_id, is_processor); + auto iter = find_if(client_id, is_processor); if (iter != m_list.end()) *iter = cinterval_info(client_id, is_processor, interval); @@ -55,20 +62,19 @@ bool cinterval_info_list::add_interval(int client_id, unsigned int interval, boo bool cinterval_info_list::delete_interval(int client_id, bool is_processor) { - cinterval_info_iterator iter; - iter = find_if(client_id, is_processor); + auto iter = find_if(client_id, is_processor); if (iter == m_list.end()) return false; m_list.erase(iter); + return true; } unsigned int cinterval_info_list::get_interval(int client_id, bool is_processor) { - cinterval_info_iterator iter; - iter = find_if(client_id, is_processor); + auto iter = find_if(client_id, is_processor); if (iter == m_list.end()) return 0; @@ -78,12 +84,10 @@ unsigned int cinterval_info_list::get_interval(int client_id, bool is_processor) unsigned int cinterval_info_list::get_min(void) { - if (m_list.size() == 0) + if (m_list.empty()) return 0; - cinterval_info_iterator iter; + auto iter = min_element(m_list.begin(), m_list.end(), comp_interval_info); - iter = min_element(m_list.begin(), m_list.end(), comp_interval_info); return iter->interval; } - diff --git a/src/shared/cinterval_info_list.h b/src/shared/cinterval_info_list.h index bfb4240..463de6e 100755 --- a/src/shared/cinterval_info_list.h +++ b/src/shared/cinterval_info_list.h @@ -17,26 +17,19 @@ * */ -#ifndef _CINTERVAL_INFO_LIST_H_ -#define _CINTERVAL_INFO_LIST_H_ +#if !defined(_CINTERVAL_INFO_LIST_CLASS_H_) +#define _CINTERVAL_INFO_LIST_CLASS_H_ #include - using std::list; class cinterval_info { public: - cinterval_info(int client_id, bool is_processor, unsigned int interval) { - this->client_id = client_id; - this->is_processor = is_processor; - this->interval = interval; - } - + cinterval_info(int client_id, bool is_processor, unsigned int interval); int client_id; bool is_processor; unsigned int interval; - }; typedef list::iterator cinterval_info_iterator; @@ -55,4 +48,4 @@ public: unsigned int get_interval(int client_id, bool is_processor = false); unsigned int get_min(void); }; -#endif /*_CINTERVAL_INFO_LIST_H_*/ +#endif diff --git a/src/shared/cmutex.cpp b/src/shared/cmutex.cpp index d4720cf..3ffc9f0 100755 --- a/src/shared/cmutex.cpp +++ b/src/shared/cmutex.cpp @@ -36,10 +36,14 @@ cmutex::~cmutex() void cmutex::lock() { +#ifdef _LOCK_DEBUG cbase_lock::lock(LOCK_TYPE_MUTEX, "mutex", __MODULE__, __func__, __LINE__); +#else + cbase_lock::lock(LOCK_TYPE_MUTEX); +#endif } -void cmutex::lock(const char *expr, const char *module, const char *func, int line) +void cmutex::lock(const char* expr, const char *module, const char *func, int line) { cbase_lock::lock(LOCK_TYPE_MUTEX, expr, module, func, line); } @@ -52,6 +56,7 @@ int cmutex::lock_impl(void) int cmutex::try_lock_impl(void) { return pthread_mutex_trylock(&m_mutex); + } int cmutex::unlock_impl() diff --git a/src/shared/cmutex.h b/src/shared/cmutex.h index e59fba0..bfdd5d0 100755 --- a/src/shared/cmutex.h +++ b/src/shared/cmutex.h @@ -17,8 +17,8 @@ * */ -#ifndef _CMUTEX_H_ -#define _CMUTEX_H_ +#if !defined(_CMUTEX_CLASS_H_) +#define _CMUTEX_CLASS_H_ #include "cbase_lock.h" @@ -29,7 +29,7 @@ public: virtual ~cmutex(); void lock(void); - void lock(const char *expr, const char *module, const char *func, int line); + void lock(const char* expr, const char *module, const char *func, int line); protected: int lock_impl(void); @@ -39,4 +39,5 @@ private: pthread_mutex_t m_mutex; }; -#endif /*_CMUTEXT_H_*/ +#endif +// End of a file diff --git a/src/shared/common.cpp b/src/shared/common.cpp index d02e20d..9c6da31 100755 --- a/src/shared/common.cpp +++ b/src/shared/common.cpp @@ -27,13 +27,14 @@ #include "common.h" #include #include +#include #include #ifndef EXTAPI #define EXTAPI __attribute__((visibility("default"))) #endif -#define SF_SERVER_MSG_LOG_FILE "/var/log/messages" +#define SF_SERVER_MSG_LOG_FILE "/var/log/messages" #define FILE_LENGTH 255 static int sf_debug_file_fd; @@ -45,59 +46,62 @@ EXTAPI void sf_log(int type , int priority , const char *tag , const char *fmt , va_start(ap, fmt); switch (type) { - case SF_LOG_PRINT_FILE: - sf_debug_file_fd = open(SF_SERVER_MSG_LOG_FILE, O_WRONLY | O_CREAT | O_APPEND, 0644); - - if (sf_debug_file_fd != -1) { - vsnprintf(sf_debug_file_buf, 255, fmt , ap ); - write(sf_debug_file_fd, sf_debug_file_buf, strlen(sf_debug_file_buf)); - close(sf_debug_file_fd); - } - - break; - case SF_LOG_SYSLOG: - int syslog_prio; - - switch (priority) { - case SF_LOG_ERR: - syslog_prio = LOG_ERR | LOG_DAEMON; - break; - case SF_LOG_WARN: - syslog_prio = LOG_WARNING | LOG_DAEMON; - break; - case SF_LOG_DBG: - syslog_prio = LOG_DEBUG | LOG_DAEMON; - break; - case SF_LOG_INFO: - syslog_prio = LOG_INFO | LOG_DAEMON; - break; - default: - syslog_prio = priority; + case SF_LOG_PRINT_FILE: + sf_debug_file_fd = open(SF_SERVER_MSG_LOG_FILE, O_WRONLY|O_CREAT|O_APPEND, 0644); + if (sf_debug_file_fd != -1) { + vsnprintf(sf_debug_file_buf,255, fmt , ap ); + write(sf_debug_file_fd, sf_debug_file_buf, strlen(sf_debug_file_buf)); + close(sf_debug_file_fd); + } break; - } - vsyslog(syslog_prio, fmt, ap); - break; - case SF_LOG_DLOG: - - if (tag) { + case SF_LOG_SYSLOG: + int syslog_prio; switch (priority) { - case SF_LOG_ERR: - SLOG_VA(LOG_ERROR, tag ? tag : "NULL" , fmt ? fmt : "NULL" , ap); - break; - case SF_LOG_WARN: - SLOG_VA(LOG_WARN, tag ? tag : "NULL" , fmt ? fmt : "NULL" , ap); - break; - case SF_LOG_DBG: - SLOG_VA(LOG_DEBUG, tag ? tag : "NULL", fmt ? fmt : "NULL" , ap); - break; - case SF_LOG_INFO: - SLOG_VA(LOG_INFO, tag ? tag : "NULL" , fmt ? fmt : "NULL" , ap); - break; + case SF_LOG_ERR: + syslog_prio = LOG_ERR|LOG_DAEMON; + break; + case SF_LOG_WARN: + syslog_prio = LOG_WARNING|LOG_DAEMON; + break; + + case SF_LOG_DBG: + syslog_prio = LOG_DEBUG|LOG_DAEMON; + break; + + case SF_LOG_INFO: + syslog_prio = LOG_INFO|LOG_DAEMON; + break; + + default: + syslog_prio = priority; + break; } - } - break; + vsyslog(syslog_prio, fmt, ap); + break; + + case SF_LOG_DLOG: + if (tag) { + switch (priority) { + case SF_LOG_ERR: + SLOG_VA(LOG_ERROR, tag ? tag : "NULL" , fmt ? fmt : "NULL" , ap); + break; + + case SF_LOG_WARN: + SLOG_VA(LOG_WARN, tag ? tag : "NULL" , fmt ? fmt : "NULL" , ap); + break; + + case SF_LOG_DBG: + SLOG_VA(LOG_DEBUG, tag ? tag : "NULL", fmt ? fmt : "NULL" , ap); + break; + + case SF_LOG_INFO: + SLOG_VA(LOG_INFO, tag ? tag : "NULL" , fmt ? fmt : "NULL" , ap); + break; + } + } + break; } va_end(ap); @@ -108,6 +112,7 @@ bool get_proc_name(pid_t pid, char *process_name) FILE *fp; char buf[NAME_MAX]; char filename[PATH_MAX]; + sprintf(filename, "/proc/%d/stat", pid); fp = fopen(filename, "r"); @@ -119,19 +124,22 @@ bool get_proc_name(pid_t pid, char *process_name) return false; } - strncpy(process_name, buf, NAME_MAX - 1); + strncpy(process_name, buf, NAME_MAX-1); fclose(fp); + return true; } -const char *get_client_name(void) +const char* get_client_name(void) { const int pid_string_size = 10; static pid_t pid = -1; static char client_name[NAME_MAX + pid_string_size]; + char proc_name[NAME_MAX]; - if (pid == -1) { + if (pid == -1) + { pid = getpid(); get_proc_name(pid, proc_name); snprintf(client_name, sizeof(client_name), "%s(%d)", proc_name, pid); @@ -140,16 +148,24 @@ const char *get_client_name(void) return client_name; } + bool is_sensorhub_event(unsigned int event_type) { + if ((event_type >> SENSOR_TYPE_SHIFT) == CONTEXT_SENSOR) + return true; + return false; } -void copy_sensor_data(sensor_data_t *src, sensor_data_t *dest) +void copy_sensor_data(sensor_data_t *dest, sensor_data_t *src) +{ + memcpy(dest, src, offsetof(sensor_data_t, values)); + memcpy(dest->values, src->values, src->value_count * sizeof(src->values[0])); +} + +void copy_sensorhub_data(sensorhub_data_t *dest, sensorhub_data_t *src) { - dest->data_accuracy = src->data_accuracy; - dest->data_unit_idx = src->data_unit_idx; - dest->timestamp = src->timestamp; - dest->values_num = src->values_num; - memcpy(dest->values, src->values, src->values_num * sizeof(src->values[0])); + memcpy(dest, src, offsetof(sensorhub_data_t, hub_data)); + memcpy(dest->hub_data, src->hub_data, src->hub_data_size); + memcpy(dest->data, src->data, sizeof(src->data)); } diff --git a/src/shared/common.h b/src/shared/common.h index a2105ca..ea2a8b9 100755 --- a/src/shared/common.h +++ b/src/shared/common.h @@ -1,7 +1,7 @@ /* * libsensord-share * - * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * Copyright (c) 2014 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. @@ -17,7 +17,7 @@ * */ -#ifndef _COMMON_H_ +#if !defined(_COMMON_H_) #define _COMMON_H_ #ifndef __cplusplus @@ -27,19 +27,20 @@ #ifdef __cplusplus extern "C" { -#endif /*__cplusplus*/ +#endif #include #include -#ifndef PATH_MAX +#if !defined(PATH_MAX) #define PATH_MAX 256 #endif -#ifndef NAME_MAX +#if !defined(NAME_MAX) #define NAME_MAX 256 #endif + #define SENSOR_TYPE_SHIFT 16 enum sf_log_type { @@ -147,6 +148,7 @@ void sf_log(int type , int priority , const char *tag , const char *fmt , ...); #endif + #if defined(_DEBUG) # define warn_if(expr, fmt, arg...) do { \ if(expr) { \ @@ -212,15 +214,19 @@ void sf_log(int type , int priority , const char *tag , const char *fmt , ...); #endif struct sensor_data_t; +struct sensorhub_data_t; typedef struct sensor_data_t sensor_data_t; +typedef struct sensorhub_data_t sensorhub_data_t; const char* get_client_name(void); bool get_proc_name(pid_t pid, char *process_name); bool is_sensorhub_event(unsigned int event_type); -void copy_sensor_data(sensor_data_t *src, sensor_data_t *dest); +void copy_sensor_data(sensor_data_t *dest, sensor_data_t *src); +void copy_sensorhub_data(sensorhub_data_t *dest, sensorhub_data_t *src); #ifdef __cplusplus } -#endif /*__cplusplus*/ +#endif -#endif /*_COMMON_H_*/ +#endif +//! End of a file diff --git a/src/shared/cpacket.cpp b/src/shared/cpacket.cpp index 8bce6e9..aef8070 100755 --- a/src/shared/cpacket.cpp +++ b/src/shared/cpacket.cpp @@ -23,91 +23,82 @@ #include #include #include +#include -cpacket::cpacket(int size) +cpacket::cpacket() { - char *ptr; - ptr = new(std::nothrow) char[size + sizeof(packet_header)]; - - m_packet = (packet_header *)ptr; - memset(m_packet, 0, size + sizeof(packet_header)); - m_packet->size = size; - m_create = NEW; + m_packet = NULL; } -cpacket::cpacket(void *data) +cpacket::cpacket(size_t size) { - m_packet = (packet_header *)data; - m_create = SET; + m_packet = NULL; + set_payload_size(size); } cpacket::~cpacket() { - if (m_create == NEW) { - delete[] (char *)m_packet; - m_packet = NULL; - } -} - -void cpacket::set_payload_size(int size) -{ - if (!m_packet) { - ERR("error m_packet null!!"); - } else { - m_packet->size = size; - } + delete[] (char*)m_packet; } void cpacket::set_cmd(int cmd) { - if (!m_packet) { - ERR("error m_packet null!!"); - } else { - m_packet->cmd = cmd; - } + if (!m_packet) + set_payload_size(0); + + m_packet->cmd = cmd; } int cpacket::cmd(void) { - if (!m_packet) { - ERR("error m_packet null!!"); - return -1; - } else { - return m_packet->cmd; - } + if (!m_packet) + return CMD_NONE; + + return m_packet->cmd; } void *cpacket::data(void) { - if ( !m_packet ) { - ERR("error m_packet null!!"); + if (!m_packet) return NULL; - } return m_packet->data; } void *cpacket::packet(void) { - return (void *)m_packet; + return (void*)m_packet; } -int cpacket::header_size(void) +size_t cpacket::size(void) { - return sizeof(packet_header); + if (!m_packet) + return 0; + + return m_packet->size + sizeof(packet_header); } -int cpacket::size(void) +size_t cpacket::payload_size(void) { - if (!m_packet) { - ERR("error m_packet null!!"); - return -1; - } else { - return m_packet->size + sizeof(packet_header); - } + if (!m_packet) + return 0; + + return m_packet->size; } -int cpacket::payload_size(void) +void cpacket::set_payload_size(size_t size) { - return m_packet->size; + int prev_cmd = CMD_NONE; + + if (m_packet) { + prev_cmd = m_packet->cmd; + delete []m_packet; + } + + m_packet = (packet_header*) new(std::nothrow) char[size + sizeof(packet_header)]; + retm_if (!m_packet, "Failed to allocate memory"); + m_packet->size = size; + + if (prev_cmd != CMD_NONE) + m_packet->cmd = prev_cmd; } diff --git a/src/shared/cpacket.h b/src/shared/cpacket.h index 7eb653a..fa50eaf 100755 --- a/src/shared/cpacket.h +++ b/src/shared/cpacket.h @@ -17,21 +17,21 @@ * */ -#ifndef _CPACKET_H_ -#define _CPACKET_H_ +#if !defined(_CPACKET_CLASS_H_) +#define _CPACKET_CLASS_H_ typedef struct packet_header { int cmd; - int size; - char data[0]; + size_t size; + char data[]; } packet_header; class cpacket { public: - cpacket(int size); - cpacket(void *data); - virtual ~cpacket(); + cpacket(); + explicit cpacket(size_t size); + ~cpacket(); void set_cmd(int cmd); int cmd(void); @@ -39,21 +39,13 @@ public: void *data(void); void *packet(void); - int size(void); - int payload_size(void); - void set_payload_size(int size); - - static int header_size(void); + size_t size(void); + size_t payload_size(void); + void set_payload_size(size_t size); private: - enum { - NEW = 0x01, - SET = 0x02, - }; - packet_header *m_packet; - - int m_create; }; -#endif /*_CPACKET_H_*/ +#endif +// End of a file diff --git a/src/shared/crw_lock.cpp b/src/shared/crw_lock.cpp index 942a034..6470f6c 100755 --- a/src/shared/crw_lock.cpp +++ b/src/shared/crw_lock.cpp @@ -20,6 +20,7 @@ #include "crw_lock.h" #include "common.h" + crw_lock::crw_lock() { m_rw_lock = PTHREAD_RWLOCK_INITIALIZER; @@ -32,12 +33,20 @@ crw_lock::~crw_lock() void crw_lock::read_lock() { +#ifdef _LOCK_DEBUG cbase_lock::lock(LOCK_TYPE_READ, "read lock", __MODULE__, __func__, __LINE__); +#else + cbase_lock::lock(LOCK_TYPE_READ); +#endif } void crw_lock::write_lock() { +#ifdef _LOCK_DEBUG cbase_lock::lock(LOCK_TYPE_WRITE, "write lock", __MODULE__, __func__, __LINE__); +#else + cbase_lock::lock(LOCK_TYPE_WRITE); +#endif } int crw_lock::read_lock_impl(void) diff --git a/src/shared/crw_lock.h b/src/shared/crw_lock.h index 2ec20db..b2a33b3 100755 --- a/src/shared/crw_lock.h +++ b/src/shared/crw_lock.h @@ -17,8 +17,8 @@ * */ -#ifndef _CRW_LOCK_H_ -#define _CRW_LOCK_H_ +#if !defined(_CRW_LOCK_CLASS_H_) +#define _CRW_LOCK_CLASS_H_ #include "cbase_lock.h" @@ -42,4 +42,5 @@ private: pthread_rwlock_t m_rw_lock; }; -#endif /*_CRW_LOCK_H_*/ +#endif +// End of a file diff --git a/src/shared/csensor_event_dispatcher.cpp b/src/shared/csensor_event_dispatcher.cpp index 8df89fa..3b90c34 100755 --- a/src/shared/csensor_event_dispatcher.cpp +++ b/src/shared/csensor_event_dispatcher.cpp @@ -23,35 +23,41 @@ #include #include #include - using std::thread; #define MAX_PENDING_CONNECTION 32 csensor_event_dispatcher::csensor_event_dispatcher() -: m_lcd_on(true) { } csensor_event_dispatcher::~csensor_event_dispatcher() { } + +csensor_event_dispatcher& csensor_event_dispatcher::get_instance() +{ + static csensor_event_dispatcher inst; + return inst; +} + + bool csensor_event_dispatcher::run(void) { - INFO("Starting Event Dispatcher"); + INFO("Starting Event Dispatcher\n"); if (!m_accept_socket.create(SOCK_SEQPACKET)) { - ERR("Listener Socket Creation failed in Server"); + ERR("Listener Socket Creation failed in Server \n"); return false; } - if (!m_accept_socket.bind(EVENT_CHANNEL_PATH)) { - ERR("Listener Socket Binding failed in Server"); + if(!m_accept_socket.bind(EVENT_CHANNEL_PATH)) { + ERR("Listener Socket Binding failed in Server \n"); m_accept_socket.close(); return false; } - if (!m_accept_socket.listen(MAX_PENDING_CONNECTION)) { - ERR("Socket Listen failed in Server"); + if(!m_accept_socket.listen(MAX_PENDING_CONNECTION)) { + ERR("Socket Listen failed in Server \n"); return false; } @@ -68,7 +74,8 @@ void csensor_event_dispatcher::accept_event_channel(csocket client_socket) { int client_id; event_channel_ready_t event_channel_ready; - cclient_info_manager &client_info_manager = get_client_info_manager(); + cclient_info_manager& client_info_manager = get_client_info_manager(); + client_socket.set_connection_mode(); if (client_socket.recv(&client_id, sizeof(client_id)) <= 0) { @@ -77,9 +84,10 @@ void csensor_event_dispatcher::accept_event_channel(csocket client_socket) } client_socket.set_transfer_mode(); + AUTOLOCK(m_mutex); - if (!get_client_info_manager().set_event_socket(client_id, client_socket)) { + if(!get_client_info_manager().set_event_socket(client_id, client_socket)) { ERR("Failed to store event socket[%d] for %s", client_socket.get_socket_fd(), client_info_manager.get_client_info(client_id)); return; @@ -89,7 +97,7 @@ void csensor_event_dispatcher::accept_event_channel(csocket client_socket) event_channel_ready.client_id = client_id; INFO("Event channel is accepted for %s on socket[%d]", - client_info_manager.get_client_info(client_id), client_socket.get_socket_fd()); + client_info_manager.get_client_info(client_id), client_socket.get_socket_fd()); if (client_socket.send(&event_channel_ready, sizeof(event_channel_ready)) <= 0) { ERR("Failed to send event_channel_ready packet to %s on socket fd[%d]", @@ -100,17 +108,18 @@ void csensor_event_dispatcher::accept_event_channel(csocket client_socket) void csensor_event_dispatcher::accept_connections(void) { - INFO("Event channel acceptor is started."); + INFO("Event channel acceptor is started.\n"); while (true) { csocket client_socket; if (!m_accept_socket.accept(client_socket)) { - ERR("Accepting socket failed in Server"); + ERR("Accepting socket failed in Server \n"); continue; } - INFO("New client connected (socket_fd : %d)", client_socket.get_socket_fd()); + INFO("New client connected (socket_fd : %d)\n", client_socket.get_socket_fd()); + thread event_channel_creator(&csensor_event_dispatcher::accept_event_channel, this, client_socket); event_channel_creator.detach(); } @@ -120,47 +129,42 @@ void csensor_event_dispatcher::dispatch_event(void) { const int MAX_EVENT_PER_SENSOR = 16; const int MAX_SENSOR_EVENT = 1 + (sensor_plugin_loader::get_instance().get_virtual_sensors().size() - * MAX_EVENT_PER_SENSOR); + * MAX_EVENT_PER_SENSOR); const int MAX_SYNTH_PER_SENSOR = 5; - INFO("Event Dispatcher started"); - m_lcd_on = is_lcd_on(); - if (vconf_notify_key_changed(VCONFKEY_PM_STATE, situation_watcher, this) != 0) - ERR("Fail to set notify callback for %s", VCONFKEY_PM_STATE); + vector v_sensor_events(MAX_SYNTH_PER_SENSOR); + + INFO("Event Dispatcher started"); while (true) { bool is_hub_event = false; - event_situation situation; + void *seed_event = get_event_queue().pop(); unsigned int event_type = *((unsigned int *)(seed_event)); if (is_sensorhub_event(event_type)) is_hub_event = true; - if (m_lcd_on) - situation = SITUATION_LCD_ON; - else - situation = SITUATION_LCD_OFF; - if (is_hub_event) { sensorhub_event_t *sensorhub_event = (sensorhub_event_t *)seed_event; - sensorhub_event->situation = situation; - send_sensor_events(sensorhub_event, 1, true, situation); + send_sensor_events(sensorhub_event, 1, true); } else { sensor_event_t sensor_events[MAX_SENSOR_EVENT]; unsigned int event_cnt = 0; + sensor_events[event_cnt++] = *((sensor_event_t *)seed_event); virtual_sensors v_sensors = get_active_virtual_sensors(); - virtual_sensors::iterator it_v_sensor; - it_v_sensor = v_sensors.begin(); - vector v_sensor_events; - v_sensor_events.reserve(MAX_SYNTH_PER_SENSOR); + + auto it_v_sensor = v_sensors.begin(); while (it_v_sensor != v_sensors.end()) { int synthesized_cnt; + v_sensor_events.clear(); + (*it_v_sensor)->synthesize(*((sensor_event_t *)seed_event), v_sensor_events); + synthesized_cnt = v_sensor_events.size(); for (int i = 0; i < synthesized_cnt; ++i) @@ -172,13 +176,11 @@ void csensor_event_dispatcher::dispatch_event(void) sort_sensor_events(sensor_events, event_cnt); for (int i = 0; i < event_cnt; ++i) { - sensor_events[i].situation = situation; - if (is_record_event(sensor_events[i].event_type)) put_last_event(sensor_events[i].event_type, sensor_events[i]); } - send_sensor_events(sensor_events, event_cnt, false, situation); + send_sensor_events(sensor_events, event_cnt, false); } if (is_hub_event) @@ -188,11 +190,15 @@ void csensor_event_dispatcher::dispatch_event(void) } } -void csensor_event_dispatcher::send_sensor_events(void *events, int event_cnt, bool is_hub_event, event_situation situation) + +void csensor_event_dispatcher::send_sensor_events(void* events, int event_cnt, bool is_hub_event) { sensor_event_t *sensor_events; sensorhub_event_t *sensor_hub_events; - cclient_info_manager &client_info_manager = get_client_info_manager(); + cclient_info_manager& client_info_manager = get_client_info_manager(); + + const int RESERVED_CLIENT_CNT = 20; + static client_id_vec id_vec(RESERVED_CLIENT_CNT); if (is_hub_event) sensor_hub_events = (sensorhub_event_t *)events; @@ -200,21 +206,27 @@ void csensor_event_dispatcher::send_sensor_events(void *events, int event_cnt, b sensor_events = (sensor_event_t *)events; for (int i = 0; i < event_cnt; ++i) { - client_id_vec id_vec; + sensor_id_t sensor_id; unsigned int event_type; - if (is_hub_event) + if (is_hub_event) { + sensor_id = sensor_hub_events[i].sensor_id; event_type = sensor_hub_events[i].event_type; - else + } else { + sensor_id = sensor_events[i].sensor_id; event_type = sensor_events[i].event_type; + } - client_info_manager.get_listener_ids(event_type, situation, id_vec); - client_id_vec::iterator it_client_id; - it_client_id = id_vec.begin(); + id_vec.clear(); + client_info_manager.get_listener_ids(sensor_id, event_type, id_vec); + + auto it_client_id = id_vec.begin(); while (it_client_id != id_vec.end()) { csocket client_socket; + client_info_manager.get_event_socket(*it_client_id, client_socket); + bool ret; if (is_hub_event) @@ -230,43 +242,21 @@ void csensor_event_dispatcher::send_sensor_events(void *events, int event_cnt, b ++it_client_id; } } -} - -bool csensor_event_dispatcher::is_lcd_on(void) -{ - int lcd_state; - - if (vconf_get_int(VCONFKEY_PM_STATE, &lcd_state) != 0) { - ERR("Can't get the value of VCONFKEY_PM_STATE"); - return true; - } - - if (lcd_state == VCONFKEY_PM_STATE_LCDOFF) - return false; - return true; } -cclient_info_manager &csensor_event_dispatcher::get_client_info_manager(void) +cclient_info_manager& csensor_event_dispatcher::get_client_info_manager(void) { return cclient_info_manager::get_instance(); } -csensor_event_queue &csensor_event_dispatcher::get_event_queue(void) +csensor_event_queue& csensor_event_dispatcher::get_event_queue(void) { return csensor_event_queue::get_instance(); } bool csensor_event_dispatcher::is_record_event(unsigned int event_type) { - switch (event_type) { - case ACCELEROMETER_EVENT_ROTATION_CHECK: - return true; - break; - default: - break; - } - return false; } @@ -279,8 +269,8 @@ void csensor_event_dispatcher::put_last_event(unsigned int event_type, const sen bool csensor_event_dispatcher::get_last_event(unsigned int event_type, sensor_event_t &event) { AUTOLOCK(m_last_events_mutex); - event_type_last_event_map::iterator it_event; - it_event = m_last_events.find(event_type); + + auto it_event = m_last_events.find(event_type); if (it_event == m_last_events.end()) return false; @@ -292,41 +282,42 @@ bool csensor_event_dispatcher::get_last_event(unsigned int event_type, sensor_ev bool csensor_event_dispatcher::has_active_virtual_sensor(virtual_sensor *sensor) { AUTOLOCK(m_active_virtual_sensors_mutex); - virtual_sensors::iterator it_v_sensor; - it_v_sensor = find(m_active_virtual_sensors.begin(), m_active_virtual_sensors.end(), sensor); + + auto it_v_sensor = find(m_active_virtual_sensors.begin(), m_active_virtual_sensors.end(), sensor); + return (it_v_sensor != m_active_virtual_sensors.end()); } + virtual_sensors csensor_event_dispatcher::get_active_virtual_sensors(void) { AUTOLOCK(m_active_virtual_sensors_mutex); - return m_active_virtual_sensors; -} -bool csensor_event_dispatcher::compare_by_timestamp(const sensor_event_t &a, const sensor_event_t &b) -{ - return a.data.timestamp < b.data.timestamp; + return m_active_virtual_sensors; } void csensor_event_dispatcher::sort_sensor_events(sensor_event_t *events, unsigned int cnt) { - std::sort(events, events + cnt, compare_by_timestamp); + std::sort(events, events + cnt, + [](const sensor_event_t& a, const sensor_event_t &b)->bool { + return a.data.timestamp < b.data.timestamp; + } + ); } -void csensor_event_dispatcher::request_last_event(int client_id, const sensor_type_t sensor) + +void csensor_event_dispatcher::request_last_event(int client_id, sensor_id_t sensor_id) { - cclient_info_manager &client_info_manager = get_client_info_manager(); + cclient_info_manager& client_info_manager = get_client_info_manager(); event_type_vector event_vec; csocket client_socket; - if (client_info_manager.get_registered_events(client_id, sensor, event_vec)) { + if (client_info_manager.get_registered_events(client_id, sensor_id, event_vec)) { client_info_manager.get_event_socket(client_id, client_socket); - event_type_vector::iterator it_event; - it_event = event_vec.begin(); + auto it_event = event_vec.begin(); while (it_event != event_vec.end()) { sensor_event_t event; - if (is_record_event(*it_event) && get_last_event(*it_event, event)) { if (client_socket.send(&event, sizeof(event)) > 0) INFO("Send the last event[0x%x] to %s on socket[%d]", event.event_type, @@ -335,13 +326,13 @@ void csensor_event_dispatcher::request_last_event(int client_id, const sensor_ty ERR("Failed to send event[0x%x] to %s on socket[%d]", event.event_type, client_info_manager.get_client_info(client_id), client_socket.get_socket_fd()); } - ++it_event; } } } -bool csensor_event_dispatcher::add_active_virtual_sensor(virtual_sensor *sensor) + +bool csensor_event_dispatcher::add_active_virtual_sensor(virtual_sensor * sensor) { AUTOLOCK(m_active_virtual_sensors_mutex); @@ -351,14 +342,15 @@ bool csensor_event_dispatcher::add_active_virtual_sensor(virtual_sensor *sensor) } m_active_virtual_sensors.push_back(sensor); + return true; } -bool csensor_event_dispatcher::delete_active_virtual_sensor(virtual_sensor *sensor) +bool csensor_event_dispatcher::delete_active_virtual_sensor(virtual_sensor * sensor) { AUTOLOCK(m_active_virtual_sensors_mutex); - virtual_sensors::iterator it_v_sensor; - it_v_sensor = find(m_active_virtual_sensors.begin(), m_active_virtual_sensors.end(), sensor); + + auto it_v_sensor = find(m_active_virtual_sensors.begin(), m_active_virtual_sensors.end(), sensor); if (it_v_sensor == m_active_virtual_sensors.end()) { ERR("Fail to delete non-existent [%s] sensor on active virtual sensors", sensor->get_name()); @@ -366,13 +358,6 @@ bool csensor_event_dispatcher::delete_active_virtual_sensor(virtual_sensor *sens } m_active_virtual_sensors.erase(it_v_sensor); - return true; -} -void csensor_event_dispatcher::situation_watcher(keynode_t *node, void *user_data) -{ - csensor_event_dispatcher *dispatcher = (csensor_event_dispatcher *) user_data; - - if (!strcmp(vconf_keynode_get_name(node), VCONFKEY_PM_STATE)) - dispatcher->m_lcd_on = dispatcher->is_lcd_on(); + return true; } diff --git a/src/shared/csensor_event_dispatcher.h b/src/shared/csensor_event_dispatcher.h index 1e21037..1eaa4f2 100755 --- a/src/shared/csensor_event_dispatcher.h +++ b/src/shared/csensor_event_dispatcher.h @@ -27,7 +27,7 @@ #include #include -typedef map event_type_last_event_map; +typedef unordered_map event_type_last_event_map; typedef list virtual_sensors; class csensor_event_dispatcher @@ -43,18 +43,16 @@ private: csensor_event_dispatcher(); ~csensor_event_dispatcher(); - csensor_event_dispatcher(csensor_event_dispatcher const &) {}; - csensor_event_dispatcher &operator=(csensor_event_dispatcher const &); + csensor_event_dispatcher(csensor_event_dispatcher const&) {}; + csensor_event_dispatcher& operator=(csensor_event_dispatcher const&); void accept_connections(void); void accept_event_channel(csocket client_socket); void dispatch_event(void); - void send_sensor_events(void *events, int event_cnt, bool is_hub_event, event_situation situation); - static void situation_watcher(keynode_t *node, void *user_data); - bool is_lcd_on(void); - static cclient_info_manager &get_client_info_manager(void); - static csensor_event_queue &get_event_queue(void); + void send_sensor_events(void* events, int event_cnt, bool is_hub_event); + static cclient_info_manager& get_client_info_manager(void); + static csensor_event_queue& get_event_queue(void); bool is_record_event(unsigned int event_type); void put_last_event(unsigned int event_type, const sensor_event_t &event); @@ -63,19 +61,14 @@ private: bool has_active_virtual_sensor(virtual_sensor *sensor); virtual_sensors get_active_virtual_sensors(void); - static bool compare_by_timestamp(const sensor_event_t &a, const sensor_event_t &b); void sort_sensor_events(sensor_event_t *events, unsigned int cnt); public: - static csensor_event_dispatcher &get_instance() { - static csensor_event_dispatcher inst; - return inst; - } - + static csensor_event_dispatcher& get_instance(); bool run(void); - void request_last_event(int client_id, const sensor_type_t sensor); + void request_last_event(int client_id, sensor_id_t sensor_id); bool add_active_virtual_sensor(virtual_sensor *sensor); bool delete_active_virtual_sensor(virtual_sensor *sensor); }; -#endif /*_CSENSOR_EVENT_DISPATCHER_H_*/ +#endif diff --git a/src/shared/csensor_event_queue.cpp b/src/shared/csensor_event_queue.cpp index e190005..40059ea 100755 --- a/src/shared/csensor_event_queue.cpp +++ b/src/shared/csensor_event_queue.cpp @@ -24,17 +24,28 @@ csensor_event_queue::csensor_event_queue() { } +csensor_event_queue& csensor_event_queue::get_instance() +{ + static csensor_event_queue inst; + return inst; +} + + void csensor_event_queue::push(sensor_event_t const &event) { - sensor_event_t *new_event = new sensor_event_t; + sensor_event_t *new_event = new(std::nothrow) sensor_event_t; + retm_if(!new_event, "Failed to allocate memory"); *new_event = event; + push_internal(new_event); } void csensor_event_queue::push(sensorhub_event_t const &event) { - sensorhub_event_t *new_event = new sensorhub_event_t; + sensorhub_event_t *new_event = new(std::nothrow) sensorhub_event_t; + retm_if(!new_event, "Failed to allocate memory"); *new_event = event; + push_internal(new_event); } @@ -44,7 +55,15 @@ void csensor_event_queue::push_internal(void *event) bool wake = m_queue.empty(); if (m_queue.size() >= QUEUE_FULL_SIZE) { - ERR("Queue is full"); + ERR("Queue is full, drop it!"); + + unsigned int event_type = *((unsigned int *)(event)); + + if (is_sensorhub_event(event_type)) + delete (sensorhub_event_t *)event; + else + delete (sensor_event_t *)event; + } else m_queue.push(event); @@ -52,14 +71,13 @@ void csensor_event_queue::push_internal(void *event) m_cond_var.notify_one(); } -void *csensor_event_queue::pop(void) +void* csensor_event_queue::pop(void) { ulock u(m_mutex); - while (m_queue.empty()) m_cond_var.wait(u); - void *event = m_queue.front(); + void* event = m_queue.front(); m_queue.pop(); return event; } diff --git a/src/shared/csensor_event_queue.h b/src/shared/csensor_event_queue.h index 85e6ea9..cd8dee6 100755 --- a/src/shared/csensor_event_queue.h +++ b/src/shared/csensor_event_queue.h @@ -17,85 +17,25 @@ * */ -#ifndef _CSENSOR_EVENT_QUEUE_H_ -#define _CSENSOR_EVENT_QUEUE_H_ - +#if !defined(_CSENSOR_EVENT_QUEUE_CLASS_H_) +#define _CSENSOR_EVENT_QUEUE_CLASS_H_ #include #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -using namespace std; using std::queue; -using std::set; using std::mutex; using std::lock_guard; using std::unique_lock; using std::condition_variable; -static set priority_list; - class csensor_event_queue { private: static const unsigned int QUEUE_FULL_SIZE = 1000; - class compare { - public: - bool operator() (void* &v1,void *&v2) { - sensor_event_t *e1 = (sensor_event_t *)v1; - sensor_event_t *e2 = (sensor_event_t *)v2; - bool prioritize_e1 = true; - bool prioritize_e2 = true; - /*Since priority_list is a set it will insert an event only if it is not present in the set*/ - if (e2->event_type == GRAVITY_EVENT_RAW_DATA_REPORT_ON_TIME || e2->event_type == LINEAR_ACCEL_EVENT_RAW_DATA_REPORT_ON_TIME || e2->event_type == ORIENTATION_EVENT_RAW_DATA_REPORT_ON_TIME) { - priority_list.insert(ACCELEROMETER_EVENT_RAW_DATA_REPORT_ON_TIME); - priority_list.insert(GYROSCOPE_EVENT_RAW_DATA_REPORT_ON_TIME); - priority_list.insert(GEOMAGNETIC_EVENT_RAW_DATA_REPORT_ON_TIME); - } - - if (priority_list.empty()) - return (e2->data.timestamp < e1->data.timestamp); - - set::iterator iter_e1 = priority_list.find(e1->event_type); - set::iterator iter_e2 = priority_list.find(e2->event_type); - - if (iter_e1 == priority_list.end()) - prioritize_e1 = false; - - if (iter_e2 == priority_list.end()) - prioritize_e2 = false; - - if (prioritize_e2) { - if (!prioritize_e1) - return true; - else { - if (e2->data.timestamp <= e1->data.timestamp) - return true; - return false; - } - } - else { - if (prioritize_e1) - return false; - else if (e2->data.timestamp <= e1->data.timestamp) - return true; - else - return false; - } - } - }; - - std::priority_queue, compare> m_queue; - + queue m_queue; mutex m_mutex; condition_variable m_cond_var; @@ -103,18 +43,15 @@ private: typedef unique_lock ulock; csensor_event_queue(); - csensor_event_queue(csensor_event_queue const &) {}; - csensor_event_queue &operator=(csensor_event_queue const &); + csensor_event_queue(csensor_event_queue const&) {}; + csensor_event_queue& operator=(csensor_event_queue const&); void push_internal(void *event); public: - static csensor_event_queue &get_instance() { - static csensor_event_queue inst; - return inst; - } + static csensor_event_queue& get_instance(); void push(sensor_event_t const &event); void push(sensorhub_event_t const &event); - void *pop(void); + void* pop(void); }; -#endif /*_CSENSOR_EVENT_QUEUE_H_*/ +#endif diff --git a/src/shared/csensor_usage.cpp b/src/shared/csensor_usage.cpp index 9f35198..f270a36 100755 --- a/src/shared/csensor_usage.cpp +++ b/src/shared/csensor_usage.cpp @@ -17,7 +17,7 @@ * */ -#include +#include #include #include @@ -26,17 +26,16 @@ csensor_usage::csensor_usage() , m_option(SENSOR_OPTION_DEFAULT) , m_start(false) { -} +} csensor_usage::~csensor_usage() { m_reg_events.clear(); } -bool csensor_usage::register_event(const unsigned int event_type) +bool csensor_usage::register_event(unsigned int event_type) { - reg_event_vector::iterator it_event; - it_event = find(m_reg_events.begin(), m_reg_events.end(), event_type); + auto it_event = find(m_reg_events.begin(), m_reg_events.end(), event_type); if (it_event != m_reg_events.end()) { ERR("Event[0x%x] is already registered", event_type); @@ -47,27 +46,26 @@ bool csensor_usage::register_event(const unsigned int event_type) return true; } -bool csensor_usage::unregister_event(const unsigned int event_type) +bool csensor_usage::unregister_event(unsigned int event_type) { - reg_event_vector::iterator it_event; - it_event = find(m_reg_events.begin(), m_reg_events.end(), event_type); + auto it_event = find(m_reg_events.begin(), m_reg_events.end(), event_type); if (it_event == m_reg_events.end()) { - ERR("Event[0x%x] is not found", event_type); + ERR("Event[0x%x] is not found",event_type); return false; } m_reg_events.erase(it_event); + return true; } -bool csensor_usage::is_event_registered(const unsigned int event_type) +bool csensor_usage::is_event_registered(unsigned int event_type) { - reg_event_vector::iterator it_event; - it_event = find (m_reg_events.begin(), m_reg_events.end(), event_type); + auto it_event = find (m_reg_events.begin(), m_reg_events.end(), event_type); - if (it_event == m_reg_events.end()) { - DBG("Event[0x%x] is not registered", event_type); + if (it_event == m_reg_events.end()){ + DBG("Event[0x%x] is not registered",event_type); return false; } diff --git a/src/shared/csensor_usage.h b/src/shared/csensor_usage.h index aed055f..3535962 100755 --- a/src/shared/csensor_usage.h +++ b/src/shared/csensor_usage.h @@ -23,13 +23,11 @@ #include #include #include - using std::vector; typedef vector reg_event_vector; -class csensor_usage -{ +class csensor_usage { public: unsigned int m_interval; int m_option; @@ -39,9 +37,9 @@ public: csensor_usage(); ~csensor_usage(); - bool register_event(const unsigned int event_type); - bool unregister_event(const unsigned int event_type); - bool is_event_registered(const unsigned int event_type); + bool register_event(unsigned int event_type); + bool unregister_event(unsigned int event_type); + bool is_event_registered(unsigned int event_type); }; #endif /* CSENSOR_USAGE_H_ */ diff --git a/src/shared/csocket.cpp b/src/shared/csocket.cpp index 96d50fe..63eaa47 100755 --- a/src/shared/csocket.cpp +++ b/src/shared/csocket.cpp @@ -21,6 +21,7 @@ #include #include + csocket::csocket() : m_sock_fd(-1) , m_sock_type(SOCK_STREAM) @@ -30,6 +31,7 @@ csocket::csocket() memset(&m_addr, 0, sizeof(m_addr)); } + csocket::csocket(int sock_fd) : m_send_flags(MSG_NOSIGNAL) , m_recv_flags(MSG_NOSIGNAL) @@ -39,6 +41,7 @@ csocket::csocket(int sock_fd) memset(&m_addr, 0, sizeof(m_addr)); } + csocket::csocket(const csocket &sock) { if (this == &sock) @@ -52,9 +55,7 @@ csocket::csocket(const csocket &sock) memcpy(&m_addr, &sock.m_addr, sizeof(sockaddr_un)); } -csocket::~csocket() -{ -} +csocket::~csocket() { } bool csocket::create(int sock_type) { @@ -67,6 +68,7 @@ bool csocket::create(int sock_type) } m_sock_type = sock_type; + return true; } @@ -80,18 +82,18 @@ bool csocket::bind (const char *sock_path) return false; } - if ((fsetxattr(m_sock_fd, "security.SMACK64IPOUT", "@", 2, 0)) < 0) { - if (errno != EOPNOTSUPP) { + if((fsetxattr(m_sock_fd, "security.SMACK64IPOUT", "@", 2, 0)) < 0) { + if(errno != EOPNOTSUPP) { close(); - ERR("security.SMACK64IPOUT error = [%d][%s]", errno, strerror(errno) ); + ERR("security.SMACK64IPOUT error = [%d][%s]\n", errno, strerror(errno) ); return false; } } - if ((fsetxattr(m_sock_fd, "security.SMACK64IPIN", "*", 2, 0)) < 0) { - if (errno != EOPNOTSUPP) { + if((fsetxattr(m_sock_fd, "security.SMACK64IPIN", "*", 2, 0)) < 0) { + if(errno != EOPNOTSUPP) { close(); - ERR("security.SMACK64IPIN error = [%d][%s]", errno, strerror(errno) ); + ERR("security.SMACK64IPIN error = [%d][%s]\n", errno, strerror(errno) ); return false; } } @@ -102,6 +104,7 @@ bool csocket::bind (const char *sock_path) m_addr.sun_family = AF_UNIX; strcpy(m_addr.sun_path, sock_path); + length = strlen(m_addr.sun_path) + sizeof(m_addr.sun_family); if (::bind(m_sock_fd, (struct sockaddr *)&m_addr, length) < 0) { @@ -111,7 +114,6 @@ bool csocket::bind (const char *sock_path) } socket_mode = ( S_IRWXU | S_IRWXG | S_IRWXO ); - if (chmod(sock_path, socket_mode) < 0) { ERR("chmod failed for socket(%d), errno : %d , errstr : %s", m_sock_fd, errno, strerror(errno)); close(); @@ -137,10 +139,16 @@ bool csocket::listen(const int max_connections) return true; } -bool csocket::accept(csocket &client_socket) const +bool csocket::accept(csocket& client_socket) const { int addr_length = sizeof(m_addr); - client_socket.m_sock_fd = ::accept(m_sock_fd, (sockaddr *)&m_addr, (socklen_t *)&addr_length); + int err = 0; + + do { + client_socket.m_sock_fd = ::accept(m_sock_fd, (sockaddr *)&m_addr, (socklen_t *)&addr_length); + if (!client_socket.is_valid()) + err = errno; + } while (err == EINTR); if (!client_socket.is_valid()) { ERR("Accept failed for socket(%d), errno : %d , errstr : %s", m_sock_fd, errno, strerror(errno)); @@ -150,7 +158,7 @@ bool csocket::accept(csocket &client_socket) const return true; } -ssize_t csocket::send_for_seqpacket(void const *buffer, size_t size) const +ssize_t csocket::send_for_seqpacket(void const* buffer, size_t size) const { ssize_t err, len; @@ -167,12 +175,12 @@ ssize_t csocket::send_for_seqpacket(void const *buffer, size_t size) const return err == 0 ? len : -err; } -ssize_t csocket::recv_for_seqpacket(void *buffer, size_t size) const +ssize_t csocket::recv_for_seqpacket(void* buffer, size_t size) const { - ssize_t err, len; + ssize_t err, len; do { - len = ::recv(m_sock_fd, buffer, size, m_recv_flags); + len = ::recv(m_sock_fd, buffer, size, m_recv_flags); if (len > 0) { err = 0; @@ -183,7 +191,7 @@ ssize_t csocket::recv_for_seqpacket(void *buffer, size_t size) const } else { err = errno; } - } while (err == EINTR); + } while (err == EINTR); if ((err == EAGAIN) || (err == EWOULDBLOCK)) { DBG("recv(%d, 0x%x, %d, 0x%x) = %d cause = %s(%d)", @@ -196,10 +204,11 @@ ssize_t csocket::recv_for_seqpacket(void *buffer, size_t size) const m_sock_fd, buffer, size, m_recv_flags, len, strerror(errno), errno); } - return err == 0 ? len : -err; + return err == 0 ? len : -err; } -ssize_t csocket::send_for_stream(void const *buffer, size_t size) const + +ssize_t csocket::send_for_stream(void const* buffer, size_t size) const { ssize_t len; ssize_t err = 0; @@ -226,7 +235,7 @@ ssize_t csocket::send_for_stream(void const *buffer, size_t size) const return err == 0 ? total_sent_size : -err; } -ssize_t csocket::recv_for_stream(void *buffer, size_t size) const +ssize_t csocket::recv_for_stream(void* buffer, size_t size) const { ssize_t len; ssize_t err = 0; @@ -257,7 +266,8 @@ ssize_t csocket::recv_for_stream(void *buffer, size_t size) const return err == 0 ? total_recv_size : -err; } -ssize_t csocket::send(void const *buffer, size_t size) const + +ssize_t csocket::send(void const* buffer, size_t size) const { if (m_sock_type == SOCK_STREAM) return send_for_stream(buffer, size); @@ -265,7 +275,7 @@ ssize_t csocket::send(void const *buffer, size_t size) const return send_for_seqpacket(buffer, size); } -ssize_t csocket::recv(void *buffer, size_t size) const +ssize_t csocket::recv(void* buffer, size_t size) const { if (m_sock_type == SOCK_STREAM) return recv_for_stream(buffer, size); @@ -275,7 +285,7 @@ ssize_t csocket::recv(void *buffer, size_t size) const bool csocket::connect(const char *sock_path) { - const int TIMEOUT = 3; + const int TIMEOUT = 5; fd_set write_fds; struct timeval tv; int addr_len; @@ -287,14 +297,15 @@ bool csocket::connect(const char *sock_path) } prev_blocking_mode = is_blocking_mode(); + set_blocking_mode(false); m_addr.sun_family = AF_UNIX; strcpy(m_addr.sun_path, sock_path); addr_len = strlen(m_addr.sun_path) + sizeof(m_addr.sun_family); - if (::connect(m_sock_fd, (sockaddr *) &m_addr, addr_len) < 0) { - ERR("connect error: %s sock_fd: %d for %s", strerror(errno), m_sock_fd, get_client_name()); + if (::connect(m_sock_fd,(sockaddr *) &m_addr, addr_len) < 0) { + ERR("connect error: %s sock_fd: %d\n for %s", strerror(errno), m_sock_fd, get_client_name()); return false; } @@ -304,10 +315,11 @@ bool csocket::connect(const char *sock_path) tv.tv_usec = 0; int ret; + ret = select(m_sock_fd + 1, NULL, &write_fds, NULL, &tv); if (ret == -1) { - ERR("select error: %s sock_fd: %d for %s", strerror(errno), m_sock_fd, get_client_name()); + ERR("select error: %s sock_fd: %d\n for %s", strerror(errno), m_sock_fd, get_client_name()); close(); return false; } else if (!ret) { @@ -348,6 +360,7 @@ bool csocket::connect(const char *sock_path) bool csocket::set_blocking_mode(bool blocking) { int flags; + flags = fcntl(m_sock_fd, F_GETFL); if (flags == -1) { @@ -356,6 +369,7 @@ bool csocket::set_blocking_mode(bool blocking) } flags = blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK); + flags = fcntl(m_sock_fd, F_SETFL, flags); if (flags == -1) { @@ -366,15 +380,17 @@ bool csocket::set_blocking_mode(bool blocking) return true; } + bool csocket::set_sock_type(void) { socklen_t opt_len; int sock_type; + opt_len = sizeof(sock_type); if (getsockopt(m_sock_fd, SOL_SOCKET, SO_TYPE, &sock_type, &opt_len) < 0) { - ERR("getsockopt(SOL_SOCKET, SO_TYPE) failed for %s, m_sock_fd: %d, errno : %d , errstr : %s", get_client_name(), m_sock_fd, errno, strerror(errno)); - return false; + ERR("getsockopt(SOL_SOCKET, SO_TYPE) failed for %s, m_sock_fd: %d, errno : %d , errstr : %s", get_client_name(), m_sock_fd, errno, strerror(errno)); + return false; } m_sock_type = sock_type; @@ -384,12 +400,14 @@ bool csocket::set_sock_type(void) bool csocket::set_connection_mode(void) { struct timeval tv; - const int TIMEOUT = 3; + const int TIMEOUT = 5; + set_blocking_mode(true); + tv.tv_sec = TIMEOUT; tv.tv_usec = 0; - if (setsockopt(m_sock_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) { + if(setsockopt(m_sock_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) { ERR("Set SO_RCVTIMEO failed for %s, m_sock_fd : %d, errno : %d , errstr : %s", get_client_name(), m_sock_fd, errno, strerror(errno)); close(); @@ -398,20 +416,25 @@ bool csocket::set_connection_mode(void) m_send_flags = MSG_NOSIGNAL; m_recv_flags = MSG_NOSIGNAL; + return true; } bool csocket::set_transfer_mode(void) { set_blocking_mode(false); + + m_send_flags = MSG_DONTWAIT | MSG_NOSIGNAL; m_recv_flags = MSG_DONTWAIT | MSG_NOSIGNAL; + return true; } bool csocket::is_blocking_mode(void) { int flags; + flags = fcntl(m_sock_fd, F_GETFL); if (flags == -1) { @@ -422,6 +445,17 @@ bool csocket::is_blocking_mode(void) return !(flags & O_NONBLOCK); } + +bool csocket::is_valid(void) const +{ + return (m_sock_fd >= 0); +} + +int csocket::get_socket_fd(void) const +{ + return m_sock_fd; +} + bool csocket::close(void) { if (m_sock_fd >= 0) { @@ -429,7 +463,6 @@ bool csocket::close(void) ERR("Socket(%d) close failed, errno : %d , errstr : %s", m_sock_fd, errno, strerror(errno)); return false; } - m_sock_fd = -1; } diff --git a/src/shared/csocket.h b/src/shared/csocket.h index 2d9e3ce..b6e8990 100755 --- a/src/shared/csocket.h +++ b/src/shared/csocket.h @@ -28,11 +28,9 @@ #include #include "common.h" #include - using std::string; -class csocket -{ +class csocket { public: csocket(); virtual ~csocket(); @@ -43,38 +41,35 @@ public: bool create(int sock_type); bool bind (const char *sock_path); bool listen(const int max_connections); - bool accept(csocket &client_socket) const; + bool accept(csocket& client_socket) const; //Client bool connect(const char *sock_path); //Data Transfer - ssize_t send(void const *buffer, size_t size) const; - ssize_t recv(void *buffer, size_t size) const; + ssize_t send(void const* buffer, size_t size) const; + ssize_t recv(void* buffer, size_t size) const; bool set_connection_mode(void); bool set_transfer_mode(void); bool is_blocking_mode(void); //check if socket is created - bool is_valid(void) const { - return (m_sock_fd >= 0); - } - int get_socket_fd(void) const { - return m_sock_fd; - } + bool is_valid(void) const; + int get_socket_fd(void) const; bool close(void); + bool is_block_mode(void); private: bool set_blocking_mode(bool blocking); bool set_sock_type(void); - ssize_t send_for_seqpacket(void const *buffer, size_t size) const; - ssize_t send_for_stream(void const *buffer, size_t size) const; - ssize_t recv_for_seqpacket(void *buffer, size_t size) const; - ssize_t recv_for_stream(void *buffer, size_t size) const; + ssize_t send_for_seqpacket(void const* buffer, size_t size) const; + ssize_t send_for_stream(void const* buffer, size_t size) const; + ssize_t recv_for_seqpacket(void* buffer, size_t size) const; + ssize_t recv_for_stream(void* buffer, size_t size) const; int m_sock_fd; int m_sock_type; diff --git a/src/shared/fusion_util.cpp b/src/shared/fusion_util.cpp new file mode 100644 index 0000000..b6d2c14 --- /dev/null +++ b/src/shared/fusion_util.cpp @@ -0,0 +1,56 @@ +/* + * libsensord-share + * + * Copyright (c) 2014 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 + +int quat_to_matrix(const float *quat, float *R) +{ + if(quat == NULL || R == NULL) + return -1; + + float q0 = quat[3]; + float q1 = quat[0]; + float q2 = quat[1]; + float q3 = quat[2]; + + float sq_q1 = 2 * q1 * q1; + float sq_q2 = 2 * q2 * q2; + float sq_q3 = 2 * q3 * q3; + float q1_q2 = 2 * q1 * q2; + float q3_q0 = 2 * q3 * q0; + float q1_q3 = 2 * q1 * q3; + float q2_q0 = 2 * q2 * q0; + float q2_q3 = 2 * q2 * q3; + float q1_q0 = 2 * q1 * q0; + + R[0] = 1 - sq_q2 - sq_q3; + R[1] = q1_q2 - q3_q0; + R[2] = q1_q3 + q2_q0; + R[3] = q1_q2 + q3_q0; + R[4] = 1 - sq_q1 - sq_q3; + R[5] = q2_q3 - q1_q0; + R[6] = q1_q3 - q2_q0; + R[7] = q2_q3 + q1_q0; + R[8] = 1 - sq_q1 - sq_q2; + + return 0; +} + diff --git a/src/shared/fusion_util.h b/src/shared/fusion_util.h new file mode 100755 index 0000000..53310c1 --- /dev/null +++ b/src/shared/fusion_util.h @@ -0,0 +1,34 @@ +/* + * libsensord-share + * + * Copyright (c) 2014 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 _FUSION_UTIL_H_ +#define _FUSION_UTIL_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +int quat_to_matrix(const float *quat, float *R); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/shared/iio_common.cpp b/src/shared/iio_common.cpp index 4fbec08..a127ca0 100644 --- a/src/shared/iio_common.cpp +++ b/src/shared/iio_common.cpp @@ -166,21 +166,15 @@ int update_sysfs_num(const char *filepath, int val, bool verify) return 0; } -int update_sysfs_string(const char *filepath, char *val, bool verify) +int update_sysfs_string(const char *filepath, const char *val) { fstream sysfile; sysfile.open(filepath); - if (!sysfile.is_open()) + if (!sysfile.is_open()) { + ERR("File Open Error, %s", filepath); return -1; - sysfile << val; - if (verify) - { - string test; - sysfile.seekp(0, ios::beg); - sysfile >> test; - if (test != string(val)) - return -1; } + sysfile << val; return 0; } diff --git a/src/shared/iio_common.h b/src/shared/iio_common.h index 0d72947..ecda683 100644 --- a/src/shared/iio_common.h +++ b/src/shared/iio_common.h @@ -28,6 +28,7 @@ using std::string; using std::ifstream; + #define NO_OF_ULL_BYTES 8 #define NO_OF_SHORT_VAL 4 #define CH0_INDEX 0 @@ -80,7 +81,7 @@ int decode_channel_data_type(const char *device_dir, const char *ch_name, struct int add_channel_to_array(const char *device_dir, const char *ch_name, struct channel_parameters *channel); int get_channel_array_size(struct channel_parameters *channels, int num_channels); int update_sysfs_num(const char *filepath, int val, bool verify = false); -int update_sysfs_string(const char *filepath, char *val, bool verify = false); +int update_sysfs_string(const char *filepath, const char *val); int convert_bytes_to_int(int input, struct channel_parameters *info); template diff --git a/src/shared/physical_sensor.cpp b/src/shared/physical_sensor.cpp index a81e33f..4f9ee3c 100755 --- a/src/shared/physical_sensor.cpp +++ b/src/shared/physical_sensor.cpp @@ -20,12 +20,15 @@ #include #include + physical_sensor::physical_sensor() { + } physical_sensor::~physical_sensor() { + } bool physical_sensor::push(sensor_event_t const &event) @@ -46,9 +49,11 @@ void physical_sensor::set_poller(working_func_t func, void *arg) m_sensor_data_poller.set_working(func); } + bool physical_sensor::start_poll(void) { return m_sensor_data_poller.start(); + } bool physical_sensor::stop_poll(void) diff --git a/src/shared/physical_sensor.h b/src/shared/physical_sensor.h index d4438ce..4118ae3 100755 --- a/src/shared/physical_sensor.h +++ b/src/shared/physical_sensor.h @@ -44,4 +44,4 @@ protected: bool stop_poll(void); }; -#endif /*_PHYSICAL_SENSOR_H_*/ +#endif diff --git a/src/shared/sensor_base.cpp b/src/shared/sensor_base.cpp index 17ad75f..3b71b36 100755 --- a/src/shared/sensor_base.cpp +++ b/src/shared/sensor_base.cpp @@ -18,18 +18,23 @@ */ #include + #include #define UNKNOWN_NAME "UNKNOWN_SENSOR" sensor_base::sensor_base() -: m_client(0) +: m_privilege(SENSOR_PRIVILEGE_PUBLIC) +, m_permission(SENSOR_PERMISSION_STANDARD) +, m_client(0) , m_started(false) { + } sensor_base::~sensor_base() { + } bool sensor_base::init() @@ -42,17 +47,44 @@ bool sensor_base::is_virtual() return false; } -bool sensor_base::is_fusion(void) +void sensor_base::set_id(sensor_id_t id) { - return false; + m_id = id; +} + +sensor_id_t sensor_base::get_id(void) +{ + return m_id; +} + +sensor_privilege_t sensor_base::get_privilege(void) +{ + return m_privilege; +} + +int sensor_base::get_permission(void) +{ + return m_permission; } + +void sensor_base::set_privilege(sensor_privilege_t privilege) +{ + m_privilege = privilege; +} + +void sensor_base::set_permission(int permission) +{ + m_permission = permission; +} + + sensor_type_t sensor_base::get_type() { return UNKNOWN_SENSOR; } -const char *sensor_base::get_name() +const char* sensor_base::get_name() { if (m_name.empty()) return UNKNOWN_NAME; @@ -74,6 +106,7 @@ bool sensor_base::start() { AUTOLOCK(m_mutex); AUTOLOCK(m_client_mutex); + ++m_client; if (m_client == 1) { @@ -86,6 +119,7 @@ bool sensor_base::start() } INFO("[%s] sensor started, #client = %d", get_name(), m_client); + return true; } @@ -93,6 +127,7 @@ bool sensor_base::stop(void) { AUTOLOCK(m_mutex); AUTOLOCK(m_client_mutex); + --m_client; if (m_client == 0) { @@ -105,13 +140,16 @@ bool sensor_base::stop(void) } INFO("[%s] sensor stopped, #client = %d", get_name(), m_client); + return true; } + bool sensor_base::is_started(void) { AUTOLOCK(m_mutex); AUTOLOCK(m_client_mutex); + return m_started; } @@ -123,6 +161,7 @@ bool sensor_base::add_client(unsigned int event_type) } AUTOLOCK(m_client_info_mutex); + ++(m_client_info[event_type]); return true; } @@ -135,8 +174,8 @@ bool sensor_base::delete_client(unsigned int event_type) } AUTOLOCK(m_client_info_mutex); - client_info::iterator iter; - iter = m_client_info.find(event_type); + + auto iter = m_client_info.find(event_type); if (iter == m_client_info.end()) return false; @@ -145,24 +184,29 @@ bool sensor_base::delete_client(unsigned int event_type) return false; --(iter->second); + return true; } bool sensor_base::add_interval(int client_id, unsigned int interval, bool is_processor) { - unsigned int cur_min; + unsigned int prev_min, cur_min; + AUTOLOCK(m_interval_info_list_mutex); - cur_min = m_interval_info_list.get_min(); + + prev_min = m_interval_info_list.get_min(); if (!m_interval_info_list.add_interval(client_id, interval, is_processor)) return false; - if ((interval < cur_min) || !cur_min) { + cur_min = m_interval_info_list.get_min(); + + if (cur_min != prev_min) { INFO("Min interval for sensor[0x%x] is changed from %dms to %dms" " by%sclient[%d] adding interval", - get_type(), cur_min, interval, + get_type(), prev_min, cur_min, is_processor ? " processor " : " ", client_id); - set_interval(interval); + set_interval(cur_min); } return true; @@ -172,6 +216,7 @@ bool sensor_base::delete_interval(int client_id, bool is_processor) { unsigned int prev_min, cur_min; AUTOLOCK(m_interval_info_list_mutex); + prev_min = m_interval_info_list.get_min(); if (!m_interval_info_list.delete_interval(client_id, is_processor)) @@ -184,12 +229,14 @@ bool sensor_base::delete_interval(int client_id, bool is_processor) "so set to default %dms", get_type(), is_processor ? " processor " : " ", client_id, POLL_1HZ_MS); + set_interval(POLL_1HZ_MS); } else if (cur_min != prev_min) { INFO("Min interval for sensor[0x%x] is changed from %dms to %dms" " by%sclient[%d] deleting interval", get_type(), prev_min, cur_min, is_processor ? " processor " : " ", client_id); + set_interval(cur_min); } @@ -199,19 +246,39 @@ bool sensor_base::delete_interval(int client_id, bool is_processor) unsigned int sensor_base::get_interval(int client_id, bool is_processor) { AUTOLOCK(m_interval_info_list_mutex); + return m_interval_info_list.get_interval(client_id, is_processor); } -bool sensor_base::get_properties(const unsigned int type, sensor_properties_t &properties) +void sensor_base::get_sensor_info(sensor_info &info) { - ERR("Invalid type: 0x%x", type); - return false; + sensor_properties_t properties; + get_properties(properties); + + info.set_type(get_type()); + info.set_id(get_id()); + info.set_privilege(m_privilege); + info.set_name(properties.name.c_str()); + info.set_vendor(properties.vendor.c_str()); + info.set_min_range(properties.min_range); + info.set_max_range(properties.max_range); + info.set_resolution(properties.resolution); + info.set_min_interval(properties.min_interval); + info.set_fifo_count(properties.fifo_count); + info.set_max_batch_count(properties.max_batch_count); + info.set_supported_events(m_supported_event_info); + + return; +} + +bool sensor_base::get_properties(sensor_properties_t &properties) +{ + return true; } bool sensor_base::is_supported(unsigned int event_type) { - vector::iterator iter; - iter = find(m_supported_event_info.begin(), m_supported_event_info.end(), event_type); + auto iter = find(m_supported_event_info.begin(), m_supported_event_info.end(), event_type); if (iter == m_supported_event_info.end()) return false; @@ -219,17 +286,17 @@ bool sensor_base::is_supported(unsigned int event_type) return true; } -long sensor_base::set_command(const unsigned int cmd, long value) +long sensor_base::set_command(unsigned int cmd, long value) { return -1; } -int sensor_base::send_sensorhub_data(const char *data, int data_len) +int sensor_base::send_sensorhub_data(const char* data, int data_len) { return -1; } -int sensor_base::get_sensor_data(const unsigned int type, sensor_data_t &data) +int sensor_base::get_sensor_data(unsigned int type, sensor_data_t &data) { return -1; } @@ -242,8 +309,8 @@ void sensor_base::register_supported_event(unsigned int event_type) unsigned int sensor_base::get_client_cnt(unsigned int event_type) { AUTOLOCK(m_client_info_mutex); - client_info::iterator iter; - iter = m_client_info.find(event_type); + + auto iter = m_client_info.find(event_type); if (iter == m_client_info.end()) return 0; @@ -260,7 +327,7 @@ unsigned long long sensor_base::get_timestamp(void) { struct timespec t; clock_gettime(CLOCK_MONOTONIC, &t); - return ((unsigned long long)(t.tv_sec) * NS_TO_SEC + t.tv_nsec) / MS_TO_SEC; + return ((unsigned long long)(t.tv_sec)*1000000000LL + t.tv_nsec) / 1000; } unsigned long long sensor_base::get_timestamp(timeval *t) @@ -270,5 +337,5 @@ unsigned long long sensor_base::get_timestamp(timeval *t) return 0; } - return ((unsigned long long)(t->tv_sec) * US_TO_SEC + t->tv_usec); + return ((unsigned long long)(t->tv_sec)*1000000LL +t->tv_usec); } diff --git a/src/shared/sensor_base.h b/src/shared/sensor_base.h index 1d60077..1cc6636 100755 --- a/src/shared/sensor_base.h +++ b/src/shared/sensor_base.h @@ -21,23 +21,26 @@ #define _SENSOR_BASE_H_ #include -#include +#include #include #include #include #include + #include #include + #include #include #include +#include using std::string; using std::mutex; using std::recursive_mutex; using std::lock_guard; using std::list; -using std::map; +using std::unordered_map; using std::vector; using std::unique_lock; using std::condition_variable; @@ -45,20 +48,20 @@ using std::condition_variable; class sensor_base { private: - typedef map client_info; + typedef unordered_map client_info; public: sensor_base(); virtual ~sensor_base(); virtual bool init(void); + void set_id(sensor_id_t id); + sensor_id_t get_id(void); virtual sensor_type_t get_type(void); - virtual const char *get_name(void); + sensor_privilege_t get_privilege(void); + int get_permission(void); + virtual const char* get_name(void); virtual bool is_virtual(void); - virtual bool is_fusion(void); - - virtual bool on_start(void); - virtual bool on_stop(void); bool start(void); bool stop(void); @@ -71,13 +74,15 @@ public: virtual bool delete_interval(int client_id, bool is_processor = false); unsigned int get_interval(int client_id, bool is_processor = false); - virtual bool get_properties(const unsigned int type, sensor_properties_t &properties); + + void get_sensor_info(sensor_info &info); + virtual bool get_properties(sensor_properties_t &properties); bool is_supported(unsigned int event_type); - virtual long set_command(const unsigned int cmd, long value); - virtual int send_sensorhub_data(const char *data, int data_len); + virtual long set_command(unsigned int cmd, long value); + virtual int send_sensorhub_data(const char* data, int data_len); - virtual int get_sensor_data(const unsigned int type, sensor_data_t &data); + virtual int get_sensor_data(unsigned int type, sensor_data_t &data); void register_supported_event(unsigned int event_type); protected: @@ -85,6 +90,10 @@ protected: typedef lock_guard rlock; typedef unique_lock ulock; + sensor_id_t m_id; + sensor_privilege_t m_privilege; + int m_permission; + cinterval_info_list m_interval_info_list; cmutex m_interval_info_list_mutex; @@ -96,17 +105,22 @@ protected: client_info m_client_info; cmutex m_client_info_mutex; - vector m_supported_event_info; + vector m_supported_event_info; bool m_started; string m_name; + void set_privilege(sensor_privilege_t privilege); + void set_permission(int permission); unsigned int get_client_cnt(unsigned int event_type); virtual bool set_interval(unsigned long val); - unsigned long long get_timestamp(void); - unsigned long long get_timestamp(timeval *t); + static unsigned long long get_timestamp(void); + static unsigned long long get_timestamp(timeval *t); +private: + virtual bool on_start(void); + virtual bool on_stop(void); }; -#endif /*_SENSOR_BASE_H_*/ +#endif diff --git a/src/shared/sensor_common.h b/src/shared/sensor_common.h index 4960f68..10c5918 100755 --- a/src/shared/sensor_common.h +++ b/src/shared/sensor_common.h @@ -17,8 +17,8 @@ * */ -#ifndef _SENSOR_COMMON_H_ -#define _SENSOR_COMMON_H_ +#ifndef __SENSOR_COMMON_H__ +#define __SENSOR_COMMON_H__ #ifndef DEPRECATED #define DEPRECATED __attribute__((deprecated)) @@ -27,15 +27,7 @@ #ifdef __cplusplus extern "C" { -#endif /*__cplusplus*/ - -#define MAX_KEY_LENGTH 64 -#define MAX_VALUE_SIZE 12 -#define SENSORHUB_TYPE_CONTEXT 1 -#define SENSORHUB_TYPE_GESTURE 2 -#define MS_TO_SEC 1000 -#define US_TO_SEC 1000000 -#define NS_TO_SEC 1000000000 +#endif /** * @defgroup SENSOR_FRAMEWORK SensorFW @@ -51,6 +43,7 @@ extern "C" */ typedef enum { + ALL_SENSOR = -1, UNKNOWN_SENSOR = 0, ACCELEROMETER_SENSOR, GEOMAGNETIC_SENSOR, @@ -61,87 +54,67 @@ typedef enum { PRESSURE_SENSOR, MOTION_SENSOR, FUSION_SENSOR, - PEDOMETER_SENSOR, CONTEXT_SENSOR, FLAT_SENSOR, - BIO_SENSOR, - BIO_HRM_SENSOR, AUTO_ROTATION_SENSOR, GRAVITY_SENSOR, LINEAR_ACCEL_SENSOR, - ROTATION_VECTOR_SENSOR, ORIENTATION_SENSOR, TEMPERATURE_SENSOR } sensor_type_t; +typedef unsigned int sensor_id_t; + +typedef void *sensor_t; + +typedef enum { + SENSOR_PRIVILEGE_PUBLIC, + SENSOR_PRIVILEGE_INTERNAL, +} sensor_privilege_t; + + +#define SENSOR_DATA_VALUE_SIZE 16 + +/* + * When modifying it, check copy_sensor_data() + */ typedef struct sensor_data_t { - int data_accuracy; - int data_unit_idx; /* - * Use "timestamp" instead of "time_stamp" - * which is going to be removed soon + * Use "accuracy" instead of "data_accuracy" + * which is going to be removed soon */ union { - unsigned long long time_stamp; - unsigned long long timestamp; + int accuracy; + int data_accuracy; //deprecated + }; + + unsigned long long timestamp; + +/* + * Use "value_count" instead of "values_num" + * which is going to be removed soon + */ + union { + int value_count; + int values_num; //deprecated }; - int values_num; - float values[MAX_VALUE_SIZE]; -} sensor_data_t; -typedef struct sensor_event_t { - unsigned int event_type; - int situation; - sensor_data_t data; -} sensor_event_t; + float values[SENSOR_DATA_VALUE_SIZE]; +} sensor_data_t; -#define HUB_DATA_MAX_SIZE 4096 +#define SENSOR_HUB_DATA_SIZE 4096 -typedef struct { +typedef struct sensorhub_data_t { int version; int sensorhub; int type; int hub_data_size; - long long timestamp; - char hub_data[HUB_DATA_MAX_SIZE]; + unsigned long long timestamp; + char hub_data[SENSOR_HUB_DATA_SIZE]; float data[16]; } sensorhub_data_t; -typedef struct sensorhub_event_t { - unsigned int event_type; - int situation; - sensorhub_data_t data; -} sensorhub_event_t; - -typedef struct { - int sensor_unit_idx; - float sensor_min_range; - float sensor_max_range; - float sensor_resolution; - char sensor_name[MAX_KEY_LENGTH]; - char sensor_vendor[MAX_KEY_LENGTH]; -} sensor_properties_t; - -enum sensor_data_unit_idx { - SENSOR_UNDEFINED_UNIT, - SENSOR_UNIT_METRE_PER_SECOND_SQUARED, - SENSOR_UNIT_MICRO_TESLA, - SENSOR_UNIT_DEGREE, - SENSOR_UNIT_LUX, - SENSOR_UNIT_CENTIMETER, - SENSOR_UNIT_LEVEL, - SENSOR_UNIT_STATE_ON_OFF, - SENSOR_UNIT_DEGREE_PER_SECOND, - SENSOR_UNIT_HECTOPASCAL, - SENSOR_UNIT_CELSIUS, - SENSOR_UNIT_METER, - SENSOR_UNIT_STEP, - SENSOR_UNIT_VENDOR_UNIT = 100, - SENSOR_UNIT_FILTER_CONVERTED, - SENSOR_UNIT_SENSOR_END -}; - -enum sensor_data_accuracy { +enum sensor_accuracy_t { SENSOR_ACCURACY_UNDEFINED = -1, SENSOR_ACCURACY_BAD = 0, SENSOR_ACCURACY_NORMAL =1, @@ -149,46 +122,26 @@ enum sensor_data_accuracy { SENSOR_ACCURACY_VERYGOOD = 3 }; -enum sensor_start_option { +/* + * To prevent naming confliction as using same enums as sensor CAPI use + */ +#ifndef __SENSORS_H__ +enum sensor_option_t { SENSOR_OPTION_DEFAULT = 0, - SENSOR_OPTION_ALWAYS_ON = 1, + SENSOR_OPTION_ON_IN_SCREEN_OFF = 1, + SENSOR_OPTION_ON_IN_POWERSAVE_MODE = 2, + SENSOR_OPTION_ALWAYS_ON = SENSOR_OPTION_ON_IN_SCREEN_OFF | SENSOR_OPTION_ON_IN_POWERSAVE_MODE, SENSOR_OPTION_END }; -enum _sensor_current_state { - SENSOR_STATE_UNKNOWN = -1, - SENSOR_STATE_STOPPED = 0, - SENSOR_STATE_STARTED = 1, - SENSOR_STATE_PAUSED = 2 -}; - -enum _sensor_wakeup_state { - SENSOR_WAKEUP_UNKNOWN = -1, - SENSOR_WAKEUP_UNSETTED = 0, - SENSOR_WAKEUP_SETTED = 1, -}; - -enum _sensor_poweroff_state { - SENSOR_POWEROFF_UNKNOWN = -1, - SENSOR_POWEROFF_AWAKEN = 1, -}; +typedef enum sensor_option_t sensor_option_e; +#endif -enum event_situation { - SITUATION_LCD_ON, - SITUATION_LCD_OFF, - SITUATION_SURVIVAL_MODE +enum sensor_interval_t { + SENSOR_INTERVAL_FASTEST = 0, + SENSOR_INTERVAL_NORMAL = 200, }; -enum poll_value_t { - POLL_100HZ_MS = 10, - POLL_50HZ_MS = 20, - POLL_25HZ_MS = 40, - POLL_20HZ_MS = 50, - POLL_10HZ_MS = 100, - POLL_5HZ_MS = 200, - POLL_1HZ_MS = 1000, - POLL_MAX_HZ_MS = POLL_1HZ_MS, -}; typedef enum { CONDITION_NO_OP, @@ -199,6 +152,8 @@ typedef enum { #ifdef __cplusplus } -#endif /*__cplusplus*/ +#endif -#endif /*_SENSOR_COMMON_H_*/ + +#endif +//! End of a file diff --git a/src/shared/sensor_hal.cpp b/src/shared/sensor_hal.cpp index d91a2ec..8b64ff9 100755 --- a/src/shared/sensor_hal.cpp +++ b/src/shared/sensor_hal.cpp @@ -18,18 +18,41 @@ */ #include +#include +#include +#include +#include + +using std::ifstream; +using std::fstream; +using config::CConfig; + +cmutex sensor_hal::m_shared_mutex; + +sensor_hal::sensor_hal() +{ +} + +sensor_hal::~sensor_hal() +{ +} + +bool sensor_hal::init(void *data) +{ + return true; +} bool sensor_hal::set_interval(unsigned long val) { return true; } -long sensor_hal::set_command(const unsigned int cmd, long val) +long sensor_hal::set_command(unsigned int cmd, long val) { return -1; } -int sensor_hal::send_sensorhub_data(const char *data, int data_len) +int sensor_hal::send_sensorhub_data(const char* data, int data_len) { return -1; } @@ -48,7 +71,7 @@ unsigned long long sensor_hal::get_timestamp(void) { struct timespec t; clock_gettime(CLOCK_MONOTONIC, &t); - return ((unsigned long long)(t.tv_sec) * NS_TO_SEC + t.tv_nsec) / MS_TO_SEC; + return ((unsigned long long)(t.tv_sec)*1000000000LL + t.tv_nsec) / 1000; } unsigned long long sensor_hal::get_timestamp(timeval *t) @@ -58,5 +81,387 @@ unsigned long long sensor_hal::get_timestamp(timeval *t) return 0; } - return ((unsigned long long)(t->tv_sec) * US_TO_SEC + t->tv_usec); + return ((unsigned long long)(t->tv_sec)*1000000LL +t->tv_usec); +} + +bool sensor_hal::is_sensorhub_controlled(const string &key) +{ + string key_node = string("/sys/class/sensors/ssp_sensor/") + key; + + if (access(key_node.c_str(), F_OK) == 0) + return true; + + return false; +} + +bool sensor_hal::get_node_path_info(const node_path_info_query &query, node_path_info &info) +{ + bool ret = false; + string model_id; + + if (query.input_method == IIO_METHOD) { + + find_model_id(IIO_METHOD, query.sensor_type, model_id); + if (query.sensorhub_controlled) + ret = get_sensorhub_iio_node_info(model_id, query.sensorhub_interval_node_name, info); + else + ret = get_iio_node_info(model_id, query.iio_enable_node_name, info); + } else { + if (query.sensorhub_controlled) + ret = get_sensorhub_input_event_node_info(query.input_event_key, query.sensorhub_interval_node_name, info); + else + ret = get_input_event_node_info(query.input_event_key, info); + } + + return ret; +} + + +void sensor_hal::show_node_path_info(node_path_info &info) +{ + if (info.data_node_path.size()) + INFO("Data node: %s", info.data_node_path.c_str()); + if (info.enable_node_path.size()) + INFO("Enable node: %s", info.enable_node_path.c_str()); + if (info.interval_node_path.size()) + INFO("Interval node: %s", info.interval_node_path.c_str()); + if (info.buffer_enable_node_path.size()) + INFO("Buffer enable node: %s", info.buffer_enable_node_path.c_str()); + if (info.buffer_length_node_path.size()) + INFO("Buffer length node: %s", info.buffer_length_node_path.c_str()); + if (info.trigger_node_path.size()) + INFO("Trigger node: %s", info.trigger_node_path.c_str()); +} + +bool sensor_hal::get_iio_node_info(const string &key, const string& enable_node_name, node_path_info &info) +{ + string device_num; + + if (!get_device_num(IIO_METHOD, key, device_num)) + return false; + + info.data_node_path = string("/dev/iio:device") + device_num; + + const string base_dir = string("/sys/bus/iio/devices/iio:device") + device_num + string("/"); + + info.base_dir = base_dir; + info.enable_node_path = base_dir + enable_node_name; + info.interval_node_path = base_dir + string("sampling_frequency"); + info.buffer_enable_node_path = base_dir + string("buffer/enable"); + info.buffer_length_node_path = base_dir + string("buffer/length"); + info.trigger_node_path = base_dir + string("trigger/current_trigger"); + info.available_freq_node_path = base_dir + string("sampling_frequency_available"); + info.available_scale_node_path = base_dir + string("in_accel_scale_available"); + + return true; +} + +bool sensor_hal::get_sensorhub_iio_node_info(const string &key, const string &interval_node_name, node_path_info &info) +{ + const string base_dir = "/sys/class/sensors/ssp_sensor/"; + string device_num; + + if (!get_device_num(IIO_METHOD, key, device_num)) + return false; + + info.base_dir = base_dir; + info.data_node_path = string("/dev/iio:device") + device_num; + info.enable_node_path = base_dir + string("enable"); //this may need to be changed + info.interval_node_path = base_dir + interval_node_name; + return true; +} + +bool sensor_hal::get_input_event_node_info(const string &key, node_path_info &info) +{ + string base_dir; + string device_num; + string event_num; + + if (!get_device_num(INPUT_EVENT_METHOD, key, device_num)) + return false; + + base_dir = string("/sys/class/input/input") + device_num + string("/"); + + if (!get_event_num(base_dir, event_num)) + return false; + + info.data_node_path = string("/dev/input/event") + event_num; + + info.enable_node_path = base_dir + string("enable"); + info.interval_node_path = base_dir + string("poll_delay"); + return true; +} + +bool sensor_hal::get_sensorhub_input_event_node_info(const string &key, const string &interval_node_name, node_path_info &info) +{ + const string base_dir = "/sys/class/sensors/ssp_sensor/"; + string device_num; + string event_num; + + if (!get_device_num(INPUT_EVENT_METHOD, key, device_num)) + return false; + + string input_dir = string("/sys/class/input/input") + device_num + string("/"); + + if (!get_event_num(input_dir, event_num)) + return false; + + info.data_node_path = string("/dev/input/event") + event_num; + info.enable_node_path = base_dir + string("enable"); + info.interval_node_path = base_dir + interval_node_name; + return true; +} + +bool sensor_hal::set_node_value(const string &node_path, int value) +{ + fstream node(node_path, fstream::out); + + if (!node) + return false; + + node << value; + + return true; +} + +bool sensor_hal::set_node_value(const string &node_path, unsigned long long value) +{ + fstream node(node_path, fstream::out); + + if (!node) + return false; + + node << value; + + return true; +} + + +bool sensor_hal::get_node_value(const string &node_path, int &value) +{ + fstream node(node_path, fstream::in); + + if (!node) + return false; + + node >> value; + + return true; +} + +bool sensor_hal::set_enable_node(const string &node_path, bool sensorhub_controlled, bool enable, int enable_bit) +{ + int prev_status, status; + + AUTOLOCK(m_shared_mutex); + + if (!get_node_value(node_path, prev_status)) { + ERR("Failed to get node: %s", node_path.c_str()); + return false; + } + + int _enable_bit = sensorhub_controlled ? enable_bit : 0; + + if (enable) + status = prev_status | (1 << _enable_bit); + else + status = prev_status & (~(1 << _enable_bit)); + + if (!set_node_value(node_path, status)) { + ERR("Failed to set node: %s", node_path.c_str()); + return false; + } + + return true; +} + + +bool sensor_hal::find_model_id(int method, const string &sensor_type, string &model_id) +{ + const string input_event_dir = "/sys/class/sensors/"; + const string iio_dir = "/sys/bus/iio/devices/"; + string dir_path; + string name_node, name; + string d_name; + DIR *dir = NULL; + struct dirent *dir_entry = NULL; + bool find = false; + + if (method == IIO_METHOD) + dir_path = iio_dir; + else + dir_path = input_event_dir; + + dir = opendir(dir_path.c_str()); + if (!dir) { + DBG("Failed to open dir: %s", dir_path.c_str()); + return false; + } + + while (!find && (dir_entry = readdir(dir))) { + d_name = string(dir_entry->d_name); + + if ((d_name != ".") && (d_name != "..") && (dir_entry->d_ino != 0)) { + name_node = dir_path + d_name + string("/name"); + + ifstream infile(name_node.c_str()); + + if (!infile) + continue; + + infile >> name; + + if (CConfig::get_instance().is_supported(sensor_type, name)) { + model_id = name; + find = true; + break; + } + } + } + + closedir(dir); + + return find; +} + +bool sensor_hal::verify_iio_trigger(const string &trigger_name) +{ + return true; +} + +bool sensor_hal::get_model_properties(const string &sensor_type, string &model_id, int &input_method) +{ + if (find_model_id(INPUT_EVENT_METHOD, sensor_type, model_id)) { + input_method = INPUT_EVENT_METHOD; + return true; + } else if (find_model_id(IIO_METHOD, sensor_type, model_id)) { + input_method = IIO_METHOD; + return true; + } + + return false; +} + +bool sensor_hal::get_event_num(const string &input_path, string &event_num) +{ + const string event_prefix = "event"; + DIR *dir = NULL; + struct dirent *dir_entry = NULL; + string node_name; + bool find = false; + + dir = opendir(input_path.c_str()); + if (!dir) { + ERR("Failed to open dir: %s", input_path.c_str()); + return false; + } + + int prefix_size = event_prefix.size(); + + while (!find && (dir_entry = readdir(dir))) { + node_name = dir_entry->d_name; + + if (node_name.compare(0, prefix_size, event_prefix) == 0) { + event_num = node_name.substr(prefix_size, node_name.size() - prefix_size); + find = true; + break; + } + } + + closedir(dir); + + return find; +} + +bool sensor_hal::get_device_num(int method, const string &key, string &device_num) +{ + const string input_event_dir = "/sys/class/input/"; + const string iio_dir = "/sys/bus/iio/devices/"; + const string input_event_prefix = "input"; + const string iio_prefix = "iio:device"; + + string dir_path; + string prefix; + size_t prefix_size; + string name_node, name; + string d_name; + DIR *dir = NULL; + struct dirent *dir_entry = NULL; + bool find = false; + + if (method == IIO_METHOD) { + dir_path = iio_dir; + prefix = iio_prefix; + } else { + dir_path = input_event_dir; + prefix = input_event_prefix; + } + + prefix_size = prefix.size(); + + dir = opendir(dir_path.c_str()); + if (!dir) { + ERR("Failed to open dir: %s", dir_path.c_str()); + return false; + } + + while (!find && (dir_entry = readdir(dir))) { + d_name = string(dir_entry->d_name); + + if (d_name.compare(0, prefix_size, prefix) == 0) { + name_node = dir_path + d_name + string("/name"); + + ifstream infile(name_node.c_str()); + if (!infile) + continue; + + infile >> name; + + if (name == key) { + device_num = d_name.substr(prefix_size, d_name.size() - prefix_size); + find = true; + break; + } + } + } + + closedir(dir); + + return find; +} + +bool sensor_hal::get_generic_channel_names(const string &scan_dir, const string &suffix, vector &generic_channel_names) +{ + DIR *dir = NULL; + struct dirent *dir_entry = NULL; + string file_node; + string d_name; + unsigned int pos; + + dir = opendir(scan_dir.c_str()); + if (!dir) { + DBG("Failed to open dir: %s", dir_path.c_str()); + return false; + } + + generic_channel_names.clear(); + + while (true) { + dir_entry = readdir(dir); + if (dir_entry == NULL) + break; + + d_name = string(dir_entry->d_name); + + if ((d_name != ".") && (d_name != "..") && (dir_entry->d_ino != 0)) { + pos = d_name.rfind(suffix.c_str()); + if (pos == string::npos) + continue; + generic_channel_names.push_back(d_name.substr(0 , pos)); + } + } + closedir(dir); + if (generic_channel_names.size() > 0) + return true; + return false; } diff --git a/src/shared/sensor_hal.h b/src/shared/sensor_hal.h index 8e8fed6..3d62cbd 100755 --- a/src/shared/sensor_hal.h +++ b/src/shared/sensor_hal.h @@ -19,15 +19,16 @@ #ifndef _SENSOR_HAL_H_ #define _SENSOR_HAL_H_ - #include #include #include #include -#include +#include #include +#include using std::string; +using std::vector; /* * As of Linux 3.4, there is a new EVIOCSCLOCKID ioctl to set the desired clock @@ -38,15 +39,41 @@ using std::string; #define EVIOCSCLOCKID _IOW('E', 0xa0, int) /* Set clockid to be used for timestamps */ #endif +typedef struct { + string data_node_path; + string enable_node_path; + string interval_node_path; + string buffer_enable_node_path; + string buffer_length_node_path; + string trigger_node_path; + string available_freq_node_path; + string available_scale_node_path; + string base_dir; +} node_path_info; + +typedef struct { + int input_method; + bool sensorhub_controlled; + string sensor_type; + string input_event_key; + string iio_enable_node_name; + string sensorhub_interval_node_name; +} node_path_info_query; + +enum input_method { + IIO_METHOD, + INPUT_EVENT_METHOD, +}; + +#define DEFAULT_WAIT_TIME 0 + class sensor_hal { public: - sensor_hal() {} - virtual ~sensor_hal() {} + sensor_hal(); + virtual ~sensor_hal(); - virtual bool init(void *data = NULL) { - return true; - } + virtual bool init(void *data = NULL); virtual string get_model_id(void) = 0; virtual sensor_type_t get_type(void) = 0; virtual bool enable(void) = 0; @@ -56,12 +83,34 @@ public: virtual bool get_properties(sensor_properties_t &properties) = 0; virtual int get_sensor_data(sensor_data_t &data); virtual int get_sensor_data(sensorhub_data_t &data); - virtual long set_command(const unsigned int cmd, long val); + virtual long set_command(unsigned int cmd, long val); virtual int send_sensorhub_data(const char *data, int data_len); + protected: cmutex m_mutex; + static cmutex m_shared_mutex; + + virtual bool set_enable_node(const string &node_path, bool sensorhub_controlled, bool enable, int enable_bit = 0); + + static unsigned long long get_timestamp(void); + static unsigned long long get_timestamp(timeval *t); + static bool is_sensorhub_controlled(const string &key); + static bool get_model_properties(const string &sensor_type, string &model_id, int &input_method); + static bool get_node_path_info(const node_path_info_query &query, node_path_info &info); + static void show_node_path_info(node_path_info &info); + static bool set_node_value(const string &node_path, int value); + static bool set_node_value(const string &node_path, unsigned long long value); + static bool get_node_value(const string &node_path, int &value); + static bool verify_iio_trigger(const string &trigger_name); + static bool get_generic_channel_names(const string &scan_dir, const string &suffix, vector &generic_channel_names); - unsigned long long get_timestamp(void); - unsigned long long get_timestamp(timeval *t); +private: + static bool find_model_id(int method, const string &sensor_type, string &model_id); + static bool get_event_num(const string &node_path, string &event_num); + static bool get_device_num(int method, const string &key, string &device_num); + static bool get_iio_node_info(const string &key, const string& enable_node_name, node_path_info &info); + static bool get_sensorhub_iio_node_info(const string &key, const string &interval_node_name, node_path_info &info); + static bool get_input_event_node_info(const string &key, node_path_info &info); + static bool get_sensorhub_input_event_node_info(const string &key, const string &interval_node_name, node_path_info &info); }; -#endif /*_SENSOR_HAL_H_*/ +#endif /*_SENSOR_HAL_CLASS_H_*/ diff --git a/src/shared/sensor_info.cpp b/src/shared/sensor_info.cpp new file mode 100755 index 0000000..56da0c9 --- /dev/null +++ b/src/shared/sensor_info.cpp @@ -0,0 +1,311 @@ +/* + * libsensord + * + * Copyright (c) 2014 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 + +sensor_type_t sensor_info::get_type(void) +{ + return m_type; +} + +sensor_id_t sensor_info::get_id(void) +{ + return m_id; +} + +sensor_privilege_t sensor_info::get_privilege(void) +{ + return m_privilege; +} + +const char* sensor_info::get_name(void) +{ + return m_name.c_str(); +} + +const char* sensor_info::get_vendor(void) +{ + return m_vendor.c_str(); +} + +float sensor_info::get_min_range(void) +{ + return m_min_range; +} + +float sensor_info::get_max_range(void) +{ + return m_max_range; +} + +float sensor_info::get_resolution(void) +{ + return m_resolution; +} + +int sensor_info::get_min_interval(void) +{ + return m_min_interval; +} + +int sensor_info::get_fifo_count(void) +{ + return m_fifo_count; +} + +int sensor_info::get_max_batch_count(void) +{ + return m_max_batch_count; +} + +void sensor_info::get_supported_events(vector &events) +{ + events = m_supported_events; +} + +bool sensor_info::is_supported_event(unsigned int event) +{ + auto iter = find(m_supported_events.begin(), m_supported_events.end(), event); + + if (iter == m_supported_events.end()) + return false; + + return true; +} + +void sensor_info::set_type(sensor_type_t type) +{ + m_type = type; +} + +void sensor_info::set_id(sensor_id_t id) +{ + m_id = id; +} + +void sensor_info::set_privilege(sensor_privilege_t privilege) +{ + m_privilege = privilege; +} + +void sensor_info::set_name(const char *name) +{ + m_name = name; +} + +void sensor_info::set_vendor(const char *vendor) +{ + m_vendor = vendor; +} + +void sensor_info::set_min_range(float min_range) +{ + m_min_range = min_range; +} + +void sensor_info::set_max_range(float max_range) +{ + m_max_range = max_range; +} + +void sensor_info::set_resolution(float resolution) +{ + m_resolution = resolution; +} + +void sensor_info::set_min_interval(int min_interval) +{ + m_min_interval = min_interval; +} + +void sensor_info::set_fifo_count(int fifo_count) +{ + m_fifo_count = fifo_count; +} + +void sensor_info::set_max_batch_count(int max_batch_count) +{ + m_max_batch_count = max_batch_count; +} + +void sensor_info::register_supported_event(unsigned int event) +{ + m_supported_events.push_back(event); +} + +void sensor_info::set_supported_events(vector &events) +{ + copy(events.begin(), events.end(), back_inserter(m_supported_events)); +} + +void sensor_info::get_raw_data(raw_data_t &data) +{ + put(data, (int)m_type); + put(data, (int) m_id); + put(data, (int) m_privilege); + put(data, m_name); + put(data, m_vendor); + put(data, m_min_range); + put(data, m_max_range); + put(data, m_resolution); + put(data, m_min_interval); + put(data, m_fifo_count); + put(data, m_max_batch_count); + put(data, m_supported_events); +} + +void sensor_info::set_raw_data(const char *data, int data_len) +{ + raw_data_t raw_data(&data[0], &data[data_len]); + + auto it_r_data = raw_data.begin(); + + int type, id, privilege; + + it_r_data = get(it_r_data, type); + m_type = (sensor_type_t) type; + it_r_data = get(it_r_data, id); + m_id = (sensor_id_t) id; + it_r_data = get(it_r_data, privilege); + m_privilege = (sensor_privilege_t) privilege; + it_r_data = get(it_r_data, m_name); + it_r_data = get(it_r_data, m_vendor); + it_r_data = get(it_r_data, m_min_range); + it_r_data = get(it_r_data, m_max_range); + it_r_data = get(it_r_data, m_resolution); + it_r_data = get(it_r_data, m_min_interval); + it_r_data = get(it_r_data, m_fifo_count); + it_r_data = get(it_r_data, m_max_batch_count); + it_r_data = get(it_r_data, m_supported_events); +} + +void sensor_info::show(void) +{ + INFO("Type = %d", m_type); + INFO("ID = 0x%x", (int)m_id); + INFO("Privilege = %d", (int)m_privilege); + INFO("Name = %s", m_name.c_str()); + INFO("Vendor = %s", m_vendor.c_str()); + INFO("Min_range = %f", m_min_range); + INFO("Max_range = %f", m_max_range); + INFO("Resolution = %f", m_resolution); + INFO("Min_interval = %d", m_min_interval); + INFO("Fifo_count = %d", m_fifo_count); + INFO("Max_batch_count = %d", m_max_batch_count); + + for (int i = 0; i < m_supported_events.size(); ++i) + INFO("supported_events[%d] = 0x%x", i, m_supported_events[i]); +} + + +void sensor_info::clear(void) +{ + m_type = UNKNOWN_SENSOR; + m_id = -1; + m_privilege = SENSOR_PRIVILEGE_PUBLIC; + m_name.clear(); + m_vendor.clear(); + m_min_range = 0.0f; + m_max_range = 0.0f; + m_resolution = 0.0f; + m_min_interval = 0; + m_fifo_count = 0; + m_max_batch_count = 0; + m_supported_events.clear(); +} + + +void sensor_info::put(raw_data_t &data, int value) +{ + char buffer[sizeof(value)]; + + (*(int *) buffer) = value; + + copy(&buffer[0], &buffer[sizeof(buffer)], back_inserter(data)); +} + +void sensor_info::put(raw_data_t &data, float value) +{ + char buffer[sizeof(value)]; + + (*(float *) buffer) = value; + + copy(&buffer[0], &buffer[sizeof(buffer)], back_inserter(data)); +} + +void sensor_info::put(raw_data_t &data, string &value) +{ + put(data, (int) value.size()); + + copy(value.begin(), value.end(), back_inserter(data)); +} + +void sensor_info::put(raw_data_t &data, vector &value) +{ + put(data, (int) value.size()); + + auto it = value.begin(); + + while (it != value.end()) { + put(data, (int) *it); + ++it; + } +} + +raw_data_iterator sensor_info::get(raw_data_iterator it, int &value) +{ + copy(it, it + sizeof(value), (char*) &value); + + return it + sizeof(value); +} + +raw_data_iterator sensor_info::get(raw_data_iterator it, float &value) +{ + copy(it, it + sizeof(value), (char*) &value); + + return it + sizeof(value); +} + +raw_data_iterator sensor_info::get(raw_data_iterator it, string &value) +{ + int len; + + it = get(it, len); + + copy(it, it + len, back_inserter(value)); + + return it + len; +} + +raw_data_iterator sensor_info::get(raw_data_iterator it, vector &value) +{ + int len; + + it = get(it, len); + + int ele; + for (int i = 0; i < len; ++i) { + it = get(it, ele); + value.push_back(ele); + } + + return it; +} diff --git a/src/shared/sensor_info.h b/src/shared/sensor_info.h new file mode 100755 index 0000000..6a8a09e --- /dev/null +++ b/src/shared/sensor_info.h @@ -0,0 +1,95 @@ +/* + * libsensord + * + * Copyright (c) 2014 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 _SENSOR_INFO_H_ +#define _SENSOR_INFO_H_ + +#include +#include +#include +#include + +using std::string; +using std::vector; + +typedef vector raw_data_t; +typedef raw_data_t::iterator raw_data_iterator; + +class sensor_info +{ +public: + sensor_type_t get_type(void); + sensor_id_t get_id(void); + sensor_privilege_t get_privilege(void); + const char* get_name(void); + const char* get_vendor(void); + float get_min_range(void); + float get_max_range(void); + float get_resolution(void); + int get_min_interval(void); + int get_fifo_count(void); + int get_max_batch_count(void); + void get_supported_events(vector &events); + bool is_supported_event(unsigned int event); + + void set_type(sensor_type_t type); + void set_id(sensor_id_t id); + void set_privilege(sensor_privilege_t privilege); + void set_name(const char *name); + void set_vendor(const char *vendor); + void set_min_range(float min_range); + void set_max_range(float max_range); + void set_resolution(float resolution); + void set_min_interval(int min_interval); + void set_fifo_count(int fifo_count); + void set_max_batch_count(int max_batch_count); + void register_supported_event(unsigned int event); + void set_supported_events(vector &events); + + void clear(void); + + void get_raw_data(raw_data_t &data); + void set_raw_data(const char *data, int data_len); + void show(void); +private: + sensor_type_t m_type; + sensor_id_t m_id; + sensor_privilege_t m_privilege; + string m_name; + string m_vendor; + float m_min_range; + float m_max_range; + float m_resolution; + int m_min_interval; + int m_fifo_count; + int m_max_batch_count; + vector m_supported_events; + + void put(raw_data_t &data, int value); + void put(raw_data_t &data, float value); + void put(raw_data_t &data, string &value); + void put(raw_data_t &data, vector &value); + + raw_data_iterator get(raw_data_iterator it, int &value); + raw_data_iterator get(raw_data_iterator it, float &value); + raw_data_iterator get(raw_data_iterator it, string &value); + raw_data_iterator get(raw_data_iterator it, vector &value); +}; + +#endif /* _SENSOR_INFO_H_ */ diff --git a/src/shared/sensor_plugin_loader.cpp b/src/shared/sensor_plugin_loader.cpp index 23f8d7b..9819a15 100755 --- a/src/shared/sensor_plugin_loader.cpp +++ b/src/shared/sensor_plugin_loader.cpp @@ -17,135 +17,267 @@ * */ + #include + #include #include + #include #include + #include +#include #include +#include +#include using std::make_pair; +using std::equal; +using std::unordered_set; #define ROOT_ELEMENT "PLUGIN" #define TEXT_ELEMENT "text" #define PATH_ATTR "path" + #define HAL_ELEMENT "HAL" #define SENSOR_ELEMENT "SENSOR" +#define PLUGINS_CONFIG_PATH "/usr/etc/sensor_plugins.xml" +#define PLUGINS_DIR_PATH "/usr/lib/sensord" + +#define SENSOR_INDEX_SHIFT 16 + sensor_plugin_loader::sensor_plugin_loader() { } -void *sensor_plugin_loader::load_module(const char *path) +sensor_plugin_loader& sensor_plugin_loader::get_instance() { - void *handle = dlopen(path, RTLD_NOW); + static sensor_plugin_loader inst; + return inst; +} - if (!handle) { - DBG("Target file is %s , dlerror : %s", path, dlerror()); - return NULL; +bool sensor_plugin_loader::load_module(const string &path, void** module, void** handle) +{ + void *_handle = dlopen(path.c_str(), RTLD_NOW); + + if (!_handle) { + ERR("Failed with dlopen(%s), dlerror : %s", path.c_str(), dlerror()); + return false; } dlerror(); - typedef void *create_t(void); + typedef void* create_t(void); typedef void destroy_t(void *); - create_t *init_module = (create_t *) dlsym(handle, "create"); - const char *dlsym_error = dlerror(); - if (dlsym_error) { - ERR("Failed to find \"create\" %s", dlsym_error); - dlclose(handle); - return NULL; + create_t* init_module = (create_t*) dlsym(_handle, "create"); + + if (!init_module) { + ERR("Failed to find \"create\" symbol"); + dlclose(_handle); + return false; } - destroy_t *exit_module = (destroy_t *) dlsym(handle, "destroy"); - dlsym_error = dlerror(); + destroy_t* exit_module = (destroy_t*) dlsym(_handle, "destroy"); - if (dlsym_error) { - ERR("Failed to find \"destroy\" %s", dlsym_error); - dlclose(handle); - return NULL; + if (!exit_module) { + ERR("Failed to find \"destroy\" symbol"); + dlclose(_handle); + return false; } - void *module = init_module(); + void *_module = init_module(); - if (!module) { - ERR("Failed to init the module => dlerror : %s , Target file is %s", dlerror(), path); - dlclose(handle); - return NULL; + if (!_module) { + ERR("Failed to init the module, Target file is %s\n", path.c_str()); + exit_module(_module); + dlclose(_handle); + return false; } - return module; + *module = _module; + *handle = _handle; + + return true; } -bool sensor_plugin_loader::insert_module(const char *node_name, const char *path) +bool sensor_plugin_loader::insert_module(plugin_type type, const string &path) { - if (strcmp(node_name, HAL_ELEMENT) == 0) { + if (type == PLUGIN_TYPE_HAL) { DBG("insert sensor plugin [%s]", path); sensor_hal *module; - module = (sensor_hal *)load_module(path); + void *handle; - if (!module) + if (!load_module(path, (void **)&module, &handle)) return false; sensor_type_t sensor_type = module->get_type(); m_sensor_hals.insert(make_pair(sensor_type, module)); - } - - if (strcmp(node_name, SENSOR_ELEMENT) == 0) { - DBG("insert sensor plugin [%s]", path); + } else if (type == PLUGIN_TYPE_SENSOR) { + DBG("insert sensor plugin [%s]", path.c_str()); sensor_base *module; - module = (sensor_base *)load_module(path); + void *handle; - if (!module) + if (!load_module(path, (void**)&module, &handle)) return false; if (!module->init()) { - ERR("Failed to init [%s] module", module->get_name()); + ERR("Failed to init [%s] module\n", module->get_name()); delete module; + dlclose(handle); return false; } - DBG("init [%s] module", module->get_name()); + sensor_type_t sensor_type = module->get_type(); + int idx; + idx = m_sensors.count(sensor_type); + module->set_id(idx << SENSOR_INDEX_SHIFT | sensor_type); m_sensors.insert(make_pair(sensor_type, module)); + }else { + ERR("Not supported type: %d", type); + return false; } return true; } -bool sensor_plugin_loader::load_plugins(const string &plugins_path) +bool sensor_plugin_loader::load_plugins(void) +{ + vector hal_paths, sensor_paths; + vector unique_hal_paths, unique_sensor_paths; + + get_paths_from_config(string(PLUGINS_CONFIG_PATH), hal_paths, sensor_paths); + get_paths_from_dir(string(PLUGINS_DIR_PATH), hal_paths, sensor_paths); + + //remove duplicates while keeping the original ordering => unique_*_paths + unordered_set s; + auto unique = [&s](vector &paths, const string &path) { + if (s.insert(path).second) + paths.push_back(path); + }; + + for_each(hal_paths.begin(), hal_paths.end(), + [&](const string &path) { + unique(unique_hal_paths, path); + } + ); + + for_each(sensor_paths.begin(), sensor_paths.end(), + [&](const string &path) { + unique(unique_sensor_paths, path); + } + ); + + //load plugins specified by unique_*_paths + auto insert = [&](plugin_type type, const string &path) { + insert_module(type, path); + }; + + for_each(unique_hal_paths.begin(), unique_hal_paths.end(), + [&](const string &path) { + insert(PLUGIN_TYPE_HAL, path); + } + ); + + for_each(unique_sensor_paths.begin(), unique_sensor_paths.end(), + [&](const string &path) { + insert(PLUGIN_TYPE_SENSOR, path); + } + ); + + show_sensor_info(); + return true; +} + +void sensor_plugin_loader::show_sensor_info(void) +{ + INFO("========== Loaded sensor information ==========\n"); + + int index = 0; + + auto it = m_sensors.begin(); + + while (it != m_sensors.end()) { + sensor_base* sensor = it->second; + + sensor_info info; + sensor->get_sensor_info(info); + INFO("No:%d [%s]\n", ++index, sensor->get_name()); + info.show(); + it++; + } + + INFO("===============================================\n"); +} + + +bool sensor_plugin_loader::get_paths_from_dir(const string &dir_path, vector &hal_paths, vector &sensor_paths) +{ + const string PLUGIN_POSTFIX = ".so"; + const string HAL_POSTFIX = "_hal.so"; + + DIR *dir = NULL; + struct dirent *dir_entry = NULL; + + dir = opendir(dir_path.c_str()); + + if (!dir) { + ERR("Failed to open dir: %s", dir_path.c_str()); + return false; + } + + string name; + + while (dir_entry = readdir(dir)) { + name = string(dir_entry->d_name); + + if (equal(PLUGIN_POSTFIX.rbegin(), PLUGIN_POSTFIX.rend(), name.rbegin())) { + if (equal(HAL_POSTFIX.rbegin(), HAL_POSTFIX.rend(), name.rbegin())) + hal_paths.push_back(dir_path + "/" + name); + else + sensor_paths.push_back(dir_path + "/" + name); + } + } + + closedir(dir); + return true; +} + +bool sensor_plugin_loader::get_paths_from_config(const string &config_path, vector &hal_paths, vector &sensor_paths) { xmlDocPtr doc; xmlNodePtr cur; - DBG("sensor_plugin_load::load_plugins(\"%s\") is called!", plugins_path.c_str()); - doc = xmlParseFile(plugins_path.c_str()); + doc = xmlParseFile(config_path.c_str()); if (doc == NULL) { - ERR("There is no %s", plugins_path.c_str()); + ERR("There is no %s\n", config_path.c_str()); return false; } cur = xmlDocGetRootElement(doc); if (cur == NULL) { - ERR("There is no root element in %s", plugins_path.c_str()); + ERR("There is no root element in %s\n", config_path.c_str()); xmlFreeDoc(doc); return false; } if (xmlStrcmp(cur->name, (const xmlChar *)ROOT_ELEMENT)) { - ERR("Wrong type document: there is no [%s] root element in %s", ROOT_ELEMENT, plugins_path.c_str()); + ERR("Wrong type document: there is no [%s] root element in %s\n", ROOT_ELEMENT, config_path.c_str()); xmlFreeDoc(doc); return false; } xmlNodePtr plugin_list_node_ptr; xmlNodePtr module_node_ptr; - char *prop = NULL; + char* prop = NULL; + string path, category; + plugin_list_node_ptr = cur->xmlChildrenNode; while (plugin_list_node_ptr != NULL) { @@ -155,72 +287,45 @@ bool sensor_plugin_loader::load_plugins(const string &plugins_path) continue; } - DBG("<%s>", (const char *)plugin_list_node_ptr->name); + DBG("<%s>\n", (const char*)plugin_list_node_ptr->name); module_node_ptr = plugin_list_node_ptr->xmlChildrenNode; - while (module_node_ptr != NULL) { if (!xmlStrcmp(module_node_ptr->name, (const xmlChar *)TEXT_ELEMENT)) { module_node_ptr = module_node_ptr->next; continue; } - string path; - prop = (char *)xmlGetProp(module_node_ptr, (const xmlChar *)PATH_ATTR); + prop = (char*)xmlGetProp(module_node_ptr, (const xmlChar*)PATH_ATTR); path = prop; free(prop); - DBG("<%s path=\"%s\">", (const char *) module_node_ptr->name, path.c_str()); - bool error = insert_module((const char *) plugin_list_node_ptr->name, path.c_str()); - if (!error) { - //ERR("Fail to insert module : [%s]", path.c_str()) ; - } + DBG("<%s path=\"%s\">\n", (const char*) module_node_ptr->name, path.c_str()); + + category = (const char*) plugin_list_node_ptr->name; + + if (category == string(HAL_ELEMENT)) + hal_paths.push_back(path); + else if (category == string(SENSOR_ELEMENT)) + sensor_paths.push_back(path); - DBG(""); + DBG("\n"); module_node_ptr = module_node_ptr->next; } - DBG(""); + DBG("\n"); plugin_list_node_ptr = plugin_list_node_ptr->next; } xmlFreeDoc(doc); - show_sensor_info(); - return true; -} -void sensor_plugin_loader::show_sensor_info(void) -{ - int index = 0; - sensor_plugins::iterator it = m_sensors.begin(); - - INFO("========== Loaded sensor information =========="); - - while (it != m_sensors.end()) { - sensor_base *sensor = it->second; - sensor_properties_t properties; - int default_type = sensor->get_type() << SENSOR_TYPE_SHIFT | 0x1; - - if (sensor->get_properties(default_type, properties)) { - INFO("[%d] %s", ++index, sensor->get_name()); - INFO("name : %s", properties.sensor_name); - INFO("vendor : %s", properties.sensor_vendor); - INFO("unit_idx : %d", properties.sensor_unit_idx); - INFO("min_range : %f", properties.sensor_min_range); - INFO("max_range : %f", properties.sensor_max_range); - INFO("resolution : %f", properties.sensor_resolution); - } - - it++; - } + return true; - INFO("==============================================="); } -sensor_hal *sensor_plugin_loader::get_sensor_hal(sensor_type_t type) +sensor_hal* sensor_plugin_loader::get_sensor_hal(sensor_type_t type) { - sensor_hal_plugins::iterator it_plugins; - it_plugins = m_sensor_hals.find(type); + auto it_plugins = m_sensor_hals.find(type); if (it_plugins == m_sensor_hals.end()) return NULL; @@ -233,19 +338,16 @@ vector sensor_plugin_loader::get_sensor_hals(sensor_type_t type) vector sensor_hal_list; pair ret; ret = m_sensor_hals.equal_range(type); - sensor_hal_plugins::iterator it; - for (it = ret.first; it != ret.second; ++it) { + for (auto it = ret.first; it != ret.second; ++it) sensor_hal_list.push_back(it->second); - } return sensor_hal_list; } -sensor_base *sensor_plugin_loader::get_sensor(sensor_type_t type) +sensor_base* sensor_plugin_loader::get_sensor(sensor_type_t type) { - sensor_plugins::iterator it_plugins; - it_plugins = m_sensors.find(type); + auto it_plugins = m_sensors.find(type); if (it_plugins == m_sensors.end()) return NULL; @@ -257,23 +359,42 @@ vector sensor_plugin_loader::get_sensors(sensor_type_t type) { vector sensor_list; pair ret; - ret = m_sensors.equal_range(type); - sensor_plugins::iterator it; - for (it = ret.first; it != ret.second; ++it) { + if (type == ALL_SENSOR) + ret = std::make_pair(m_sensors.begin(), m_sensors.end()); + else + ret = m_sensors.equal_range(type); + + for (auto it = ret.first; it != ret.second; ++it) sensor_list.push_back(it->second); - } return sensor_list; } + +sensor_base* sensor_plugin_loader::get_sensor(sensor_id_t id) +{ + const int SENSOR_TYPE_MASK = 0x0000FFFF; + vector sensors; + + sensor_type_t type = (sensor_type_t) (id & SENSOR_TYPE_MASK); + int index = id >> SENSOR_INDEX_SHIFT; + + sensors = get_sensors(type); + + if (sensors.size() <= index) + return NULL; + + return sensors[index]; +} + + vector sensor_plugin_loader::get_virtual_sensors(void) { vector virtual_list; - sensor_plugins::iterator sensor_it; - sensor_base *module; + sensor_base* module; - for (sensor_it = m_sensors.begin(); sensor_it != m_sensors.end(); ++sensor_it) { + for (auto sensor_it = m_sensors.begin(); sensor_it != m_sensors.end(); ++sensor_it) { module = sensor_it->second; if (module && module->is_virtual() == true) { @@ -286,24 +407,31 @@ vector sensor_plugin_loader::get_virtual_sensors(void) bool sensor_plugin_loader::destroy() { - sensor_base *sensor; - sensor_plugins::iterator sensor_it; + sensor_base* sensor; - for (sensor_it = m_sensors.begin(); sensor_it != m_sensors.end(); ++sensor_it) { + for (auto sensor_it = m_sensors.begin(); sensor_it != m_sensors.end(); ++sensor_it) { sensor = sensor_it->second; + + //need to dlclose + //unregister_module(module); + delete sensor; } - sensor_hal *sensor_hal; - sensor_hal_plugins::iterator sensor_hal_it; + sensor_hal* sensor_hal; - for (sensor_hal_it = m_sensor_hals.begin(); sensor_hal_it != m_sensor_hals.end(); ++sensor_hal_it) { + for (auto sensor_hal_it = m_sensor_hals.begin(); sensor_hal_it != m_sensor_hals.end(); ++sensor_hal_it) { sensor_hal = sensor_hal_it->second; + + // need to dlclose + //unregister_module(module); + delete sensor_hal; } m_sensors.clear(); m_sensor_hals.clear(); + return true; } diff --git a/src/shared/sensor_plugin_loader.h b/src/shared/sensor_plugin_loader.h index 041aa62..332db4a 100755 --- a/src/shared/sensor_plugin_loader.h +++ b/src/shared/sensor_plugin_loader.h @@ -17,28 +17,32 @@ * */ -#ifndef _SENSOR_PLUGIN_LOADER_H_ -#define _SENSOR_PLUGIN_LOADER_H_ +#if !defined(_SENSOR_PLUGIN_LOADER_CLASS_H_) +#define _SENSOR_PLUGIN_LOADER_CLASS_H_ #include + #include #include + #include #include #include +#include + class sensor_hal; class sensor_base; +class sensor_fusion; using std::pair; using std::vector; using std::multimap; +using std::set; using std::string; using std::istringstream; -#define PLUGINS_FILE_PATH "/usr/etc/sensor_plugins.xml" - -typedef multimap sensor_hal_plugins; +typedef multimap sensor_hal_plugins; /* * a hal_plugins is a group of hal plugin * @@ -47,7 +51,7 @@ typedef multimap sensor_hal_plugins; * */ -typedef multimap sensor_plugins; +typedef multimap sensor_plugins; /* * a sensor_plugins is a group of sensor plugin * @@ -56,33 +60,48 @@ typedef multimap sensor_plugins; * */ +typedef vector fusion_plugins; +/* +* a fusion_plugins is a group of fusion plugin +* +* ... +* +* +*/ + class sensor_plugin_loader { private: + enum plugin_type { + PLUGIN_TYPE_HAL, + PLUGIN_TYPE_SENSOR, + }; + sensor_plugin_loader(); - void *load_module(const char *path); - bool insert_module(const char *node_name, const char *path); + bool load_module(const string &path, void** module, void** handle); + bool insert_module(plugin_type type, const string &path); void show_sensor_info(void); + bool get_paths_from_dir(const string &dir_path, vector &hal_paths, vector &sensor_paths); + bool get_paths_from_config(const string &config_path, vector &hal_paths, vector &sensor_paths); sensor_hal_plugins m_sensor_hals; sensor_plugins m_sensors; + fusion_plugins m_fusions; public: - static sensor_plugin_loader &get_instance() { - static sensor_plugin_loader inst; - return inst; - } - bool load_plugins(const string &plugins_path = PLUGINS_FILE_PATH); + static sensor_plugin_loader& get_instance(); + bool load_plugins(void); - sensor_hal *get_sensor_hal(sensor_type_t type); + sensor_hal* get_sensor_hal(sensor_type_t type); vector get_sensor_hals(sensor_type_t type); - sensor_base *get_sensor(sensor_type_t type); + sensor_base* get_sensor(sensor_type_t type); vector get_sensors(sensor_type_t type); + sensor_base* get_sensor(sensor_id_t id); - vector get_virtual_sensors(void); + vector get_virtual_sensors(void); bool destroy(); }; -#endif /*_SENSOR_PLUGIN_LOADER_H_*/ +#endif /* _SENSOR_PLUGIN_LOADER_CLASS_H_ */ diff --git a/src/shared/sensord-server.pc.in b/src/shared/sensord-server.pc.in old mode 100755 new mode 100644 diff --git a/src/shared/sf_common.h b/src/shared/sf_common.h index caa7260..a47ef4f 100755 --- a/src/shared/sf_common.h +++ b/src/shared/sf_common.h @@ -17,49 +17,68 @@ * */ -#ifndef _SF_COMMON_H_ +#if !defined(_SF_COMMON_H_) #define _SF_COMMON_H_ - #include #include #include #include +#include -#define COMMAND_CHANNEL_PATH "/tmp/sf_command_socket" -#define EVENT_CHANNEL_PATH "/tmp/sf_event_socket" +#define COMMAND_CHANNEL_PATH "/tmp/sf_command_socket" +#define EVENT_CHANNEL_PATH "/tmp/sf_event_socket" -#define MAX_VALUE_SIZE 12 #define MAX_HANDLE 64 #define MAX_HANDLE_REACHED -2 + #define CLIENT_ID_INVALID -1 enum packet_type_t { CMD_NONE = 0, CMD_GET_ID, + CMD_GET_SENSOR_LIST, CMD_HELLO, CMD_BYEBYE, - CMD_WAIT_EVENT, CMD_DONE, CMD_START, CMD_STOP, CMD_REG, CMD_UNREG, - CMD_CHECK_EVENT, CMD_SET_OPTION, CMD_SET_INTERVAL, CMD_UNSET_INTERVAL, CMD_SET_COMMAND, - CMD_GET_PROPERTIES, CMD_GET_DATA, CMD_SEND_SENSORHUB_DATA, CMD_CNT, }; +enum sensor_state_t { + SENSOR_STATE_UNKNOWN = -1, + SENSOR_STATE_STOPPED = 0, + SENSOR_STATE_STARTED = 1, + SENSOR_STATE_PAUSED = 2 +}; + +enum poll_interval_t { + POLL_100HZ_MS = 10, + POLL_50HZ_MS = 20, + POLL_25HZ_MS = 40, + POLL_20HZ_MS = 50, + POLL_10HZ_MS = 100, + POLL_5HZ_MS = 200, + POLL_1HZ_MS = 1000, + POLL_MAX_HZ_MS = POLL_1HZ_MS, +}; + typedef struct { pid_t pid; } cmd_get_id_t; typedef struct { +} cmd_get_sensor_list_t; + +typedef struct { int client_id; int sensor; } cmd_hello_t; @@ -67,26 +86,24 @@ typedef struct { typedef struct { } cmd_byebye_t; -typedef struct { - unsigned int type; -} cmd_get_data_t; typedef struct { unsigned int type; -} cmd_get_properties_t; +} cmd_get_data_t; typedef struct { long value; } cmd_done_t; + typedef struct { int client_id; } cmd_get_id_done_t; typedef struct { - int state; - sensor_properties_t properties; -} cmd_properties_done_t; + int sensor_cnt; + char data[0]; +} cmd_get_sensor_list_done_t; typedef struct { int state; @@ -108,10 +125,6 @@ typedef struct { } cmd_unreg_t; typedef struct { - unsigned int event_type; -} cmd_check_event_t; - -typedef struct { unsigned int interval; } cmd_set_interval_t; @@ -139,6 +152,36 @@ typedef struct { int client_id; } event_channel_ready_t; + +typedef struct { + std::string name; + std::string vendor; + float min_range; + float max_range; + float resolution; + int min_interval; + int fifo_count; + int max_batch_count; +} sensor_properties_t; + + +/* + * When modifying it, check copy_sensor*_data() + */ +typedef struct sensor_event_t { + unsigned int event_type; + sensor_id_t sensor_id; + sensor_data_t data; +} sensor_event_t; + + +typedef struct sensorhub_event_t { + unsigned int event_type; + sensor_id_t sensor_id; + sensorhub_data_t data; +} sensorhub_event_t; + + typedef void *(*cmd_func_t)(void *data, void *cb_data); typedef std::vector event_type_vector; @@ -149,14 +192,31 @@ enum sensorhub_enable_bit { SENSORHUB_GEOMAGNETIC_UNCALIB_ENABLE_BIT, SENSORHUB_GEOMAGNETIC_RAW_ENABLE_BIT, SENSORHUB_GEOMAGNETIC_ENABLE_BIT, + SENSORHUB_PRESSURE_ENABLE_BIT, SENSORHUB_GESTURE_ENABLE_BIT, SENSORHUB_PROXIMITY_ENABLE_BIT, + SENSORHUB_TEMPERATURE_HUMIDITY_ENABLE_BIT, SENSORHUB_LIGHT_ENABLE_BIT, SENSORHUB_PROXIMITY_RAW_ENABLE_BIT, SENSORHUB_ORIENTATION_ENABLE_BIT, + SENSORHUB_STEP_DETECTOR_ENABLE_BIT = 12, + SENSORHUB_SIG_MOTION_ENABLE_BIT, SENSORHUB_GYRO_UNCALIB_ENABLE_BIT, SENSORHUB_GAME_ROTATION_VECTOR_ENABLE_BIT = 15, + SENSORHUB_ROTATION_VECTOR_ENABLE_BIT, + SENSORHUB_STEP_COUNTER_ENABLE_BIT, + SENSORHUB_BIO_HRM_RAW_ENABLE_BIT, + SENSORHUB_BIO_HRM_RAW_FAC_ENABLE_BIT, + SENSORHUB_BIO_HRM_LIB_ENABLE_BIT, + SENSORHUB_TILT_MOTION, + SENSORHUB_UV_SENSOR, + SENSORHUB_PIR_ENABLE_BIT, SENSORHUB_ENABLE_BIT_MAX, }; -#endif /*_SF_COMMON_H_*/ +enum sensor_permission_t { + SENSOR_PERMISSION_NONE = 0, + SENSOR_PERMISSION_STANDARD = 1, +}; + +#endif diff --git a/src/shared/virtual_sensor.cpp b/src/shared/virtual_sensor.cpp index c205fac..221884b 100755 --- a/src/shared/virtual_sensor.cpp +++ b/src/shared/virtual_sensor.cpp @@ -20,12 +20,15 @@ #include #include + virtual_sensor::virtual_sensor() { + } virtual_sensor::~virtual_sensor() { + } bool virtual_sensor::is_virtual(void) @@ -33,6 +36,7 @@ bool virtual_sensor::is_virtual(void) return true; } + bool virtual_sensor::activate(void) { return csensor_event_dispatcher::get_instance().add_active_virtual_sensor(this); diff --git a/src/shared/virtual_sensor.h b/src/shared/virtual_sensor.h index 34112aa..94acc4c 100755 --- a/src/shared/virtual_sensor.h +++ b/src/shared/virtual_sensor.h @@ -28,8 +28,7 @@ public: virtual_sensor(); virtual ~virtual_sensor(); - virtual void synthesize(const sensor_event_t &event, vector &outs) = 0; - virtual int get_sensor_data(const unsigned int event_type, sensor_data_t &data) = 0; + virtual void synthesize(const sensor_event_t& event, vector &outs) = 0; bool is_virtual(void); protected: @@ -37,4 +36,4 @@ protected: bool deactivate(void); }; -#endif /*_VIRTUAL_SENSOR_H_*/ +#endif diff --git a/src/shared/worker_thread.cpp b/src/shared/worker_thread.cpp index 2982cf6..d49d381 100755 --- a/src/shared/worker_thread.cpp +++ b/src/shared/worker_thread.cpp @@ -40,7 +40,7 @@ worker_thread::~worker_thread() bool worker_thread::transition_function(trans_func_index index) { if (m_trans_func[index] != NULL) { - if (!m_trans_func[index](m_context)) { + if(!m_trans_func[index](m_context)) { _T("Transition[%d] function returning false", index); return false; } @@ -55,6 +55,7 @@ worker_thread::worker_state_t worker_thread::get_state(void) return m_state; } + bool worker_thread::start(void) { lock l(m_mutex); @@ -71,7 +72,6 @@ bool worker_thread::start(void) thread th(&worker_thread::main, this); th.detach(); } - return true; } else if (m_state == WORKER_STATE_PAUSED) { m_state = WORKER_STATE_WORKING; @@ -80,6 +80,7 @@ bool worker_thread::start(void) } _T("Failed to start, because current state(%d) is not for START", m_state); + return false; } @@ -93,6 +94,7 @@ bool worker_thread::stop(void) } if ((m_state == WORKER_STATE_WORKING) || (m_state == WORKER_STATE_PAUSED)) { + if (m_state == WORKER_STATE_PAUSED) m_cond_working.notify_one(); @@ -119,7 +121,9 @@ bool worker_thread::pause(void) } _T("Failed to pause, because current state(%d) is not for PAUSE", m_state); + return false; + } bool worker_thread::resume(void) @@ -144,12 +148,13 @@ bool worker_thread::resume(void) /* * After state changed to STOPPED, it should not access member fields, - * because some transition funciton of STOPPED delete this pointer + because some transition funciton of STOPPED delete this pointer */ void worker_thread::main(void) { _T("Worker thread(0x%x) is created", std::this_thread::get_id()); + transition_function(STARTED); while (true) { @@ -164,7 +169,6 @@ void worker_thread::main(void) transition_function(STOPPED); break; } - continue; } @@ -172,6 +176,7 @@ void worker_thread::main(void) if (m_state == WORKER_STATE_PAUSED) { transition_function(PAUSED); + _T("Worker thread(0x%x) is paused", std::this_thread::get_id()); m_cond_working.wait(u); @@ -189,7 +194,6 @@ void worker_thread::main(void) break; } } - _T("Worker thread(0x%x)'s main is terminated", std::this_thread::get_id()); } diff --git a/src/shared/worker_thread.h b/src/shared/worker_thread.h index 63f2082..67b5f10 100755 --- a/src/shared/worker_thread.h +++ b/src/shared/worker_thread.h @@ -17,6 +17,7 @@ * */ + #ifndef _WORKER_THREAD_H_ #define _WORKER_THREAD_H_ @@ -82,4 +83,4 @@ public: void set_context(void *ctx); }; -#endif /*_WORKER_THREAD_H_*/ +#endif -- 2.7.4 From 84fc1f53b481b024fc2538823b74ee2831b4509f Mon Sep 17 00:00:00 2001 From: Amit Dharmapurikar Date: Tue, 11 Nov 2014 17:31:38 +0530 Subject: [PATCH 08/16] Gyro sensor plugin synchronization with IIO interface support - Modified gyro plugin as per latest code - Added IIO driver interface support on RD-PQ target - Added a minor fix in sensor_hal class and modifide accel_sensor_hal.cpp accordingly Change-Id: I86b40f1f4c4bfecfda6406694c4c650751109009 Signed-off-by: Amit Dharmapurikar --- packaging/sensord.spec | 2 +- src/accel/accel_sensor_hal.cpp | 5 +- src/gyro/gyro_sensor.cpp | 42 ++-- src/gyro/gyro_sensor.h | 15 +- src/gyro/gyro_sensor_hal.cpp | 480 +++++++++++++++++------------------------ src/gyro/gyro_sensor_hal.h | 84 +++----- src/shared/sensor_hal.cpp | 1 - src/shared/sensor_hal.h | 1 - 8 files changed, 259 insertions(+), 371 deletions(-) diff --git a/packaging/sensord.spec b/packaging/sensord.spec index 30202f7..079f1cc 100755 --- a/packaging/sensord.spec +++ b/packaging/sensord.spec @@ -9,7 +9,7 @@ Source1: sensord.service Source2: sensord.socket %define accel_state ON -%define gyro_state OFF +%define gyro_state ON %define proxi_state OFF %define light_state OFF %define geo_state OFF diff --git a/src/accel/accel_sensor_hal.cpp b/src/accel/accel_sensor_hal.cpp index 6b155b7..f38a0ed 100755 --- a/src/accel/accel_sensor_hal.cpp +++ b/src/accel/accel_sensor_hal.cpp @@ -47,7 +47,8 @@ using config::CConfig; #define INPUT_NAME "accelerometer_sensor" #define ACCEL_SENSORHUB_POLL_NODE_NAME "accel_poll_delay" -#define SCAN_EL_DIR "scan_elements/" +#define SCAN_EL_DIR "scan_elements/" +#define SCALE_AVAILABLE_NODE "in_accel_scale_available" #define ACCEL_RINGBUF_LEN 32 #define SEC_MSEC 1000 #define MSEC_TO_FREQ(VAL) ((SEC_MSEC) / (VAL)) @@ -94,7 +95,7 @@ accel_sensor_hal::accel_sensor_hal() m_buffer_enable_node_path = info.buffer_enable_node_path; m_buffer_length_node_path = info.buffer_length_node_path; m_available_freq_node_path = info.available_freq_node_path; - m_available_scale_node_path = info.available_scale_node_path; + m_available_scale_node_path = m_accel_dir + string(SCALE_AVAILABLE_NODE); if (!config.get(SENSOR_TYPE_ACCEL, m_model_id, ELEMENT_VENDOR, m_vendor)) { ERR("[VENDOR] is empty\n"); diff --git a/src/gyro/gyro_sensor.cpp b/src/gyro/gyro_sensor.cpp index 78b05be..d057959 100755 --- a/src/gyro/gyro_sensor.cpp +++ b/src/gyro/gyro_sensor.cpp @@ -19,10 +19,10 @@ #include #include + #include #include -#define INITIAL_VALUE -1 #define MS_TO_US 1000 #define DPS_TO_MDPS 1000 #define RAW_DATA_TO_DPS_UNIT(X) ((float)(X)/((float)DPS_TO_MDPS)) @@ -31,7 +31,7 @@ gyro_sensor::gyro_sensor() : m_sensor_hal(NULL) -, m_resolution(INITIAL_VALUE) +, m_resolution(0.0f) { m_name = string(SENSOR_NAME); @@ -42,7 +42,7 @@ gyro_sensor::gyro_sensor() gyro_sensor::~gyro_sensor() { - INFO("gyro_sensor is destroyed!"); + INFO("gyro_sensor is destroyed!\n"); } bool gyro_sensor::init() @@ -57,13 +57,14 @@ bool gyro_sensor::init() sensor_properties_t properties; if (m_sensor_hal->get_properties(properties) == false) { - ERR("sensor->get_properties() is failed!"); + ERR("sensor->get_properties() is failed!\n"); return false; } - m_resolution = properties.sensor_resolution; + m_resolution = properties.resolution; + + INFO("%s is created!\n", sensor_base::get_name()); - INFO("%s is created!", sensor_base::get_name()); return true; } @@ -74,8 +75,8 @@ sensor_type_t gyro_sensor::get_type(void) bool gyro_sensor::working(void *inst) { - gyro_sensor *sensor = (gyro_sensor *)inst; - return sensor->process_event(); + gyro_sensor *sensor = (gyro_sensor*)inst; + return sensor->process_event();; } bool gyro_sensor::process_event(void) @@ -90,6 +91,7 @@ bool gyro_sensor::process_event(void) AUTOLOCK(m_client_info_mutex); if (get_client_cnt(GYROSCOPE_EVENT_RAW_DATA_REPORT_ON_TIME)) { + event.sensor_id = get_id(); event.event_type = GYROSCOPE_EVENT_RAW_DATA_REPORT_ON_TIME; raw_to_base(event.data); push(event); @@ -100,10 +102,8 @@ bool gyro_sensor::process_event(void) bool gyro_sensor::on_start(void) { - AUTOLOCK(m_mutex); - if (!m_sensor_hal->enable()) { - ERR("m_sensor_hal start fail"); + ERR("m_sensor_hal start fail\n"); return false; } @@ -112,22 +112,20 @@ bool gyro_sensor::on_start(void) bool gyro_sensor::on_stop(void) { - AUTOLOCK(m_mutex); - if (!m_sensor_hal->disable()) { - ERR("m_sensor_hal stop fail"); + ERR("m_sensor_hal stop fail\n"); return false; } return stop_poll(); } -bool gyro_sensor::get_properties(const unsigned int type, sensor_properties_t &properties) +bool gyro_sensor::get_properties(sensor_properties_t &properties) { return m_sensor_hal->get_properties(properties); } -int gyro_sensor::get_sensor_data(const unsigned int type, sensor_data_t &data) +int gyro_sensor::get_sensor_data(unsigned int type, sensor_data_t &data) { int state; @@ -137,7 +135,7 @@ int gyro_sensor::get_sensor_data(const unsigned int type, sensor_data_t &data) state = m_sensor_hal->get_sensor_data(data); if (state < 0) { - ERR("m_sensor_hal get struct_data fail"); + ERR("m_sensor_hal get struct_data fail\n"); return -1; } @@ -151,13 +149,13 @@ bool gyro_sensor::set_interval(unsigned long interval) AUTOLOCK(m_mutex); INFO("Polling interval is set to %dms", interval); + return m_sensor_hal->set_interval(interval); } void gyro_sensor::raw_to_base(sensor_data_t &data) { - data.data_unit_idx = SENSOR_UNIT_DEGREE_PER_SECOND; - data.values_num = 3; + data.value_count = 3; data.values[0] = data.values[0] * m_resolution; data.values[1] = data.values[1] * m_resolution; data.values[2] = data.values[2] * m_resolution; @@ -170,14 +168,14 @@ extern "C" void *create(void) try { inst = new gyro_sensor(); } catch (int err) { - ERR("Failed to create gyro_sensor class, errno : %d, errstr : %s", err, strerror(err)); + ERR("gyro_sensor class create fail , errno : %d , errstr : %s\n", err, strerror(err)); return NULL; } - return (void *)inst; + return (void*)inst; } extern "C" void destroy(void *inst) { - delete (gyro_sensor *)inst; + delete (gyro_sensor*)inst;; } diff --git a/src/gyro/gyro_sensor.h b/src/gyro/gyro_sensor.h index 51eb31c..81d7557 100755 --- a/src/gyro/gyro_sensor.h +++ b/src/gyro/gyro_sensor.h @@ -21,11 +21,11 @@ #define _GYRO_SENSOR_H_ #include + #include #include -class gyro_sensor : public physical_sensor -{ +class gyro_sensor : public physical_sensor { public: gyro_sensor(); virtual ~gyro_sensor(); @@ -35,18 +35,17 @@ public: static bool working(void *inst); - virtual bool on_start(void); - virtual bool on_stop(void); - virtual bool set_interval(unsigned long interval); - virtual bool get_properties(const unsigned int type, sensor_properties_t &properties); - int get_sensor_data(const unsigned int type, sensor_data_t &data); + virtual bool get_properties(sensor_properties_t &properties); + int get_sensor_data(unsigned int type, sensor_data_t &data); private: sensor_hal *m_sensor_hal; float m_resolution; + virtual bool on_start(void); + virtual bool on_stop(void); void raw_to_base(sensor_data_t &data); bool process_event(void); }; -#endif /*_GYRO_SENSOR_H_*/ \ No newline at end of file +#endif diff --git a/src/gyro/gyro_sensor_hal.cpp b/src/gyro/gyro_sensor_hal.cpp index 1dda6b9..fe04ca0 100755 --- a/src/gyro/gyro_sensor_hal.cpp +++ b/src/gyro/gyro_sensor_hal.cpp @@ -1,5 +1,5 @@ /* - * sensord + * gyro_sensor_hal * * Copyright (c) 2014 Samsung Electronics Co., Ltd. * @@ -16,99 +16,153 @@ * limitations under the License. * */ - -#include #include #include -#include #include + #include #include + #include +#include +#include +#include #include -#include using std::ifstream; using config::CConfig; -#define INITIAL_VALUE -1 -#define INITIAL_TIME 0 #define DPS_TO_MDPS 1000 -#define MIN_RANGE(RES) (-((2 << (RES))/2)) -#define MAX_RANGE(RES) (((2 << (RES))/2)-1) +#define MIN_RANGE(RES) (-((1 << (RES))/2)) +#define MAX_RANGE(RES) (((1 << (RES))/2)-1) #define RAW_DATA_TO_DPS_UNIT(X) ((float)(X)/((float)DPS_TO_MDPS)) -#define SEC_MSEC 1000 -#define MSEC_TO_FREQ(VAL) (int)((SEC_MSEC) / (VAL)) - #define SENSOR_TYPE_GYRO "GYRO" -#define ELEMENT_NAME "NAME" +#define ELEMENT_NAME "NAME" #define ELEMENT_VENDOR "VENDOR" #define ELEMENT_RAW_DATA_UNIT "RAW_DATA_UNIT" -#define ELEMENT_RESOLUTION "RESOLUTION" -#define ATTR_VALUE "value" +#define ELEMENT_RESOLUTION "RESOLUTION" + +#define ATTR_VALUE "value" -#define ENABLE_VAL 1 -#define DISABLE_VAL 0 -#define DEV_DIR "/dev/" -#define TRIG_PATH "trigger/current_trigger" +#define SCALE_AVAILABLE_NODE "in_anglvel_scale_available" +#define SCAN_EL_DIR "scan_elements/" +#define TRIG_SUFFIX "-trigger" +#define GYRO_RINGBUF_LEN 32 +#define SEC_MSEC 1000 +#define MSEC_TO_FREQ(VAL) ((SEC_MSEC) / (VAL)) +#define NSEC_TO_MUSEC(VAL) ((VAL) / 1000) gyro_sensor_hal::gyro_sensor_hal() -: m_x(INITIAL_VALUE) -, m_y(INITIAL_VALUE) -, m_z(INITIAL_VALUE) +: m_x(-1) +, m_y(-1) +, m_z(-1) +, m_node_handle(-1) , m_polling_interval(POLL_1HZ_MS) -, m_fired_time(INITIAL_TIME) -, m_sensorhub_supported(false) +, m_fired_time(0) { - if (!check_hw_node()) - { - ERR("check_hw_node() fail"); + + const string sensorhub_interval_node_name = "gyro_poll_delay"; + CConfig &config = CConfig::get_instance(); + + node_path_info_query query; + node_path_info info; + int input_method = IIO_METHOD; + + if (!get_model_properties(SENSOR_TYPE_GYRO, m_model_id, input_method)) { + ERR("Failed to find model_properties"); + throw ENXIO; + + } + + query.input_method = input_method; + query.sensorhub_controlled = m_sensorhub_controlled = is_sensorhub_controlled(sensorhub_interval_node_name); + query.sensor_type = SENSOR_TYPE_GYRO; + query.input_event_key = "gyro_sensor"; + query.iio_enable_node_name = "gyro_enable"; + query.sensorhub_interval_node_name = sensorhub_interval_node_name; + + if (!get_node_path_info(query, info)) { + ERR("Failed to get node info"); throw ENXIO; } - CConfig &config = CConfig::get_instance(); + show_node_path_info(info); + + m_data_node = info.data_node_path; + m_interval_node = info.interval_node_path; + m_gyro_dir = info.base_dir; + m_trigger_path = info.trigger_node_path; + m_buffer_enable_node_path = info.buffer_enable_node_path; + m_buffer_length_node_path = info.buffer_length_node_path; + m_available_freq_node_path = info.available_freq_node_path; + m_available_scale_node_path = m_gyro_dir + string(SCALE_AVAILABLE_NODE); - if (!config.get(SENSOR_TYPE_GYRO, m_model_id, ELEMENT_VENDOR, m_vendor)) - { - ERR("[VENDOR] is empty"); + if (!config.get(SENSOR_TYPE_GYRO, m_model_id, ELEMENT_VENDOR, m_vendor)) { + ERR("[VENDOR] is empty\n"); throw ENXIO; } INFO("m_vendor = %s", m_vendor.c_str()); - if (!config.get(SENSOR_TYPE_GYRO, m_model_id, ELEMENT_NAME, m_chip_name)) - { - ERR("[NAME] is empty"); + if (!config.get(SENSOR_TYPE_GYRO, m_model_id, ELEMENT_NAME, m_chip_name)) { + ERR("[NAME] is empty\n"); throw ENXIO; } - INFO("m_chip_name = %s", m_chip_name.c_str()); + INFO("m_chip_name = %s\n",m_chip_name.c_str()); + + if (input_method == IIO_METHOD) { + m_trigger_name = m_model_id + TRIG_SUFFIX; + if (!verify_iio_trigger(m_trigger_name)) { + ERR("Failed verify trigger"); + throw ENXIO; + } + string scan_dir = m_gyro_dir + SCAN_EL_DIR; + if (!get_generic_channel_names(scan_dir, string("_type"), m_generic_channel_names)) + ERR ("Failed to find any input channels"); + else { + INFO ("generic channel names:"); + for (vector ::iterator it = m_generic_channel_names.begin(); + it != m_generic_channel_names.end(); ++it) { + INFO ("%s", it->c_str()); + } + } + } long resolution; - if (!config.get(SENSOR_TYPE_GYRO, m_model_id, ELEMENT_RESOLUTION, resolution)) - { - ERR("[RESOLUTION] is empty"); + if (!config.get(SENSOR_TYPE_GYRO, m_model_id, ELEMENT_RESOLUTION, resolution)) { + ERR("[RESOLUTION] is empty\n"); throw ENXIO; } m_resolution = (int)resolution; - INFO("m_resolution = %d", m_resolution); double raw_data_unit; - if (!config.get(SENSOR_TYPE_GYRO, m_model_id, ELEMENT_RAW_DATA_UNIT, raw_data_unit)) - { - ERR("[RAW_DATA_UNIT] is empty"); + if (!config.get(SENSOR_TYPE_GYRO, m_model_id, ELEMENT_RAW_DATA_UNIT, raw_data_unit)) { + ERR("[RAW_DATA_UNIT] is empty\n"); throw ENXIO; } m_raw_data_unit = (float)(raw_data_unit); - INFO("m_raw_data_unit = %f", m_raw_data_unit); - INFO("RAW_DATA_TO_DPS_UNIT(m_raw_data_unit) = [%f]", RAW_DATA_TO_DPS_UNIT(m_raw_data_unit)); - INFO("gyro_sensor_hal is created!"); + if ((m_node_handle = open(m_data_node.c_str(), O_RDWR)) < 0) { + ERR("gyro handle open fail for gyro processor, error:%s\n", strerror(errno)); + throw ENXIO; + } + + if (setup_channels() == true) + INFO("IIO channel setup successful"); + else { + ERR("IIO channel setup failed"); + throw ENXIO; + } + + INFO("m_raw_data_unit = %f\n",m_raw_data_unit); + INFO("RAW_DATA_TO_DPS_UNIT(m_raw_data_unit) = [%f]",RAW_DATA_TO_DPS_UNIT(m_raw_data_unit)); + INFO("gyro_sensor is created!\n"); } gyro_sensor_hal::~gyro_sensor_hal() @@ -116,10 +170,11 @@ gyro_sensor_hal::~gyro_sensor_hal() enable_resource(false); if (m_data != NULL) delete []m_data; - if (m_fp_buffer > 0) - close(m_fp_buffer); - INFO("gyro_sensor_hal is destroyed!"); + close(m_node_handle); + m_node_handle = -1; + + INFO("gyro_sensor is destroyed!\n"); } string gyro_sensor_hal::get_model_id(void) @@ -132,38 +187,16 @@ sensor_type_t gyro_sensor_hal::get_type(void) return GYROSCOPE_SENSOR; } -bool gyro_sensor_hal::enable_resource(bool enable) -{ - string temp; - int enable_val; - - if(enable) - enable_val = ENABLE_VAL; - else - enable_val = DISABLE_VAL; - - temp = m_gyro_dir + string(SCAN_EL_DIR) + string(CHANNEL_NAME_X) + string(ENABLE_SUFFIX); - update_sysfs_num(temp.c_str(), enable_val); - temp = m_gyro_dir + string(SCAN_EL_DIR) + string(CHANNEL_NAME_Y) + string(ENABLE_SUFFIX); - update_sysfs_num(temp.c_str(), enable_val); - temp = m_gyro_dir + string(SCAN_EL_DIR) + string(CHANNEL_NAME_Z) + string(ENABLE_SUFFIX); - update_sysfs_num(temp.c_str(), enable_val); - temp = m_gyro_dir + string(SCAN_EL_DIR) + string(CHANNEL_NAME_TIME) + string(ENABLE_SUFFIX); - update_sysfs_num(temp.c_str(), enable_val); - setup_trigger(INPUT_TRIG_NAME, enable); - setup_buffer(enable_val); - - return true; -} - bool gyro_sensor_hal::enable(void) { AUTOLOCK(m_mutex); - enable_resource(true); + if (!enable_resource(true)) + return false; + set_interval(m_polling_interval); - m_fired_time = INITIAL_TIME; + m_fired_time = 0; INFO("Gyro sensor real starting"); return true; } @@ -172,35 +205,35 @@ bool gyro_sensor_hal::disable(void) { AUTOLOCK(m_mutex); - enable_resource(false); + if (!enable_resource(false)) + return false; + INFO("Gyro sensor real stopping"); return true; + } bool gyro_sensor_hal::set_interval(unsigned long ms_interval) { - int freq, i, approx_freq; - freq = MSEC_TO_FREQ(ms_interval); - - for (i=0; i < m_sample_freq_count; i++) - { - if (freq == m_sample_freq[i]) - { - if (update_sysfs_num(m_freq_resource.c_str(), freq, true) == 0) - { + int freq, i; + + freq = (int)(MSEC_TO_FREQ(ms_interval)); + + for (i=0; i < m_sample_freq_count; i++) { + if (freq == m_sample_freq[i]) { + if (update_sysfs_num(m_interval_node.c_str(), freq, true) == 0) { INFO("Interval is changed from %lums to %lums]", m_polling_interval, ms_interval); m_polling_interval = ms_interval; return true; } - else - { + else { ERR("Failed to set data %lu\n", ms_interval); return false; } } } - INFO("The interval not supported: %lu\n", ms_interval); + DBG("The interval not supported: %lu\n", ms_interval); ERR("Failed to set data %lu\n", ms_interval); return false; } @@ -212,23 +245,22 @@ bool gyro_sensor_hal::update_value(bool wait) ssize_t read_size; const int TIMEOUT = 1000; - pfd.fd = m_fp_buffer; + pfd.fd = m_node_handle; pfd.events = POLLIN; if (wait) poll(&pfd, 1, TIMEOUT); else poll(&pfd, 1, 0); - read_size = read(m_fp_buffer, m_data, GYRO_RINGBUF_LEN * m_scan_size); - - if (read_size <= 0) - { - ERR("No gyro data available to read\n"); + read_size = read(m_node_handle, m_data, GYRO_RINGBUF_LEN * m_scan_size); + if (read_size <= 0) { + ERR("Gyro:No data available\n"); return false; } - - for (i = 0; i < (read_size / m_scan_size); i++) - decode_data(); + else { + for (i = 0; i < (read_size / m_scan_size); i++) + decode_data(); + } return true; } @@ -241,25 +273,11 @@ bool gyro_sensor_hal::is_data_ready(bool wait) int gyro_sensor_hal::get_sensor_data(sensor_data_t &data) { - const int chance = 3; - int retry = 0; - - while ((m_fired_time == INITIAL_TIME) && (retry++ < chance)) - { - INFO("Try usleep for getting a valid BASE DATA value"); - usleep(m_polling_interval * MS_TO_SEC); - } - - if (m_fired_time == INITIAL_TIME) - { - ERR("get_sensor_data failed"); - return -1; - } + AUTOLOCK(m_value_mutex); - data.data_accuracy = SENSOR_ACCURACY_GOOD; - data.data_unit_idx = SENSOR_UNIT_VENDOR_UNIT; + data.accuracy = SENSOR_ACCURACY_GOOD; data.timestamp = m_fired_time ; - data.values_num = 3; + data.value_count = 3; data.values[0] = m_x; data.values[1] = m_y; data.values[2] = m_z; @@ -269,126 +287,29 @@ int gyro_sensor_hal::get_sensor_data(sensor_data_t &data) bool gyro_sensor_hal::get_properties(sensor_properties_t &properties) { - properties.sensor_unit_idx = SENSOR_UNIT_DEGREE_PER_SECOND; - properties.sensor_min_range = MIN_RANGE(m_resolution) * RAW_DATA_TO_DPS_UNIT(m_raw_data_unit); - properties.sensor_max_range = MAX_RANGE(m_resolution) * RAW_DATA_TO_DPS_UNIT(m_raw_data_unit); - snprintf(properties.sensor_name, sizeof(properties.sensor_name), "%s", m_chip_name.c_str()); - snprintf(properties.sensor_vendor, sizeof(properties.sensor_vendor), "%s", m_vendor.c_str()); - properties.sensor_resolution = RAW_DATA_TO_DPS_UNIT(m_raw_data_unit); + properties.name = m_chip_name; + properties.vendor = m_vendor; + properties.min_range = MIN_RANGE(m_resolution)* RAW_DATA_TO_DPS_UNIT(m_raw_data_unit); + properties.max_range = MAX_RANGE(m_resolution)* RAW_DATA_TO_DPS_UNIT(m_raw_data_unit); + properties.min_interval = 1; + properties.resolution = RAW_DATA_TO_DPS_UNIT(m_raw_data_unit); + properties.fifo_count = 0; + properties.max_batch_count = 0; return true; -} -bool gyro_sensor_hal::is_sensorhub_supported(void) -{ - return false; } -bool gyro_sensor_hal::check_hw_node(void) +bool gyro_sensor_hal::add_gyro_channels_to_array(void) { - string name_node; - string hw_name; - string file_name; - string temp; - DIR *main_dir = NULL; - struct dirent *dir_entry = NULL; - bool find_node = false; - bool find_trigger = false; - - INFO("======================start check_hw_node============================="); - - m_sensorhub_supported = is_sensorhub_supported(); - main_dir = opendir(IIO_DIR); - - if (!main_dir) - { - ERR("Could not open IIO directory\n"); - return false; - } - - m_channels = (struct channel_parameters*) malloc(sizeof(struct channel_parameters) * NO_OF_CHANNELS); - - while (!(find_node && find_trigger)) - { - dir_entry = readdir(main_dir); - if(dir_entry == NULL) - break; - - if ((strncasecmp(dir_entry->d_name , ".", 1 ) != 0) && (strncasecmp(dir_entry->d_name , "..", 2 ) != 0) && (dir_entry->d_ino != 0)) - { - file_name = string(IIO_DIR) + string(dir_entry->d_name) + string(NAME_NODE); - ifstream infile(file_name.c_str()); - - if (!infile) - continue; - - infile >> hw_name; - - if (strncmp(dir_entry->d_name, IIO_DEV_BASE_NAME, IIO_DEV_STR_LEN) == 0) - { - if (CConfig::get_instance().is_supported(SENSOR_TYPE_GYRO, hw_name) == true) - { - m_gyro_dir = string(IIO_DIR) + string(dir_entry->d_name) + string("/"); - m_buffer_access = string(DEV_DIR) + string(dir_entry->d_name); - m_name = m_model_id = hw_name; - find_node = true; - INFO("m_gyro_dir:%s\n", m_gyro_dir.c_str()); - INFO("m_buffer_access:%s\n", m_buffer_access.c_str()); - INFO("m_name:%s\n", m_name.c_str()); - } - } - - if (strncmp(dir_entry->d_name, IIO_TRIG_BASE_NAME, IIO_TRIG_STR_LEN) == 0) - { - if (hw_name == string(INPUT_TRIG_NAME)) - { - m_gyro_trig_dir = string(IIO_DIR) + string(dir_entry->d_name) + string("/"); - find_trigger = true; - DBG("m_gyro_trig_dir:%s\n", m_gyro_trig_dir.c_str()); - } - } - - if (find_node && find_trigger) - break; - } - } - - closedir(main_dir); - - if (find_node && find_trigger) - { - if (setup_channels() == true) - INFO("IIO channel setup successful"); - else - { - ERR("IIO channel setup failed"); + int i = 0; + m_channels = (struct channel_parameters*) malloc(sizeof(struct channel_parameters) * m_generic_channel_names.size()); + for (vector ::iterator it = m_generic_channel_names.begin(); + it != m_generic_channel_names.end(); ++it) { + if (add_channel_to_array(m_gyro_dir.c_str(), it->c_str() , &m_channels[i++]) < 0) { + ERR("Failed to add channel %s to channel array", it->c_str()); return false; } } - return (find_node && find_trigger); -} - -bool gyro_sensor_hal::add_gyro_channels_to_array(void) -{ - if (add_channel_to_array(m_gyro_dir.c_str(), CHANNEL_NAME_X, &m_channels[0]) < 0) - { - ERR("Failed to add %s to channel array", CHANNEL_NAME_X); - return false; - } - if (add_channel_to_array(m_gyro_dir.c_str(), CHANNEL_NAME_Y, &m_channels[1]) < 0) - { - ERR("Failed to add %s to channel array", CHANNEL_NAME_Y); - return false; - } - if (add_channel_to_array(m_gyro_dir.c_str(), CHANNEL_NAME_Z, &m_channels[2]) < 0) - { - ERR("Failed to add %s to channel array", CHANNEL_NAME_Z); - return false; - } - if (add_channel_to_array(m_gyro_dir.c_str(), CHANNEL_NAME_TIME, &m_channels[3]) < 0) - { - ERR("Failed to add channel time_stamp to channel array"); - return false; - } return true; } @@ -396,44 +317,34 @@ bool gyro_sensor_hal::setup_channels(void) { int freq, i; double sf; - string temp; enable_resource(true); - if (!add_gyro_channels_to_array()) + if (!add_gyro_channels_to_array()) { + ERR("Failed to add channels to array!"); return false; + } - sort_channels_by_index(m_channels, NO_OF_CHANNELS); + INFO("Sorting channels by index"); + sort_channels_by_index(m_channels, m_generic_channel_names.size()); + INFO("Sorting channels by index completed"); - m_scan_size = get_channel_array_size(m_channels, NO_OF_CHANNELS); - if (m_scan_size == 0) - { + m_scan_size = get_channel_array_size(m_channels, m_generic_channel_names.size()); + if (m_scan_size == 0) { ERR("Channel array size is zero"); return false; } m_data = new (std::nothrow) char[m_scan_size * GYRO_RINGBUF_LEN]; - if (m_data == NULL) - { + if (m_data == NULL) { ERR("Couldn't create data buffer\n"); return false; } - m_fp_buffer = open(m_buffer_access.c_str(), O_RDONLY | O_NONBLOCK); - if (m_fp_buffer == -1) - { - ERR("Failed to open ring buffer(%s)\n", m_buffer_access.c_str()); - return false; - } - - m_freq_resource = m_gyro_dir + string(GYRO_FREQ); - temp = m_gyro_dir + string(GYRO_FREQ_AVLBL); - FILE *fp = NULL; - fp = fopen(temp.c_str(), "r"); - if (!fp) - { - ERR("Fail to open available frequencies file:%s\n", temp.c_str()); + fp = fopen(m_available_freq_node_path.c_str(), "r"); + if (!fp) { + ERR("Fail to open available frequencies file:%s\n", m_available_freq_node_path.c_str()); return false; } @@ -441,15 +352,15 @@ bool gyro_sensor_hal::setup_channels(void) m_sample_freq[i] = 0; i = 0; + while (fscanf(fp, "%d", &freq) > 0) m_sample_freq[i++] = freq; + m_sample_freq_count = i; - temp = m_gyro_dir + string(GYRO_SCALE_AVLBL); - fp = fopen(temp.c_str(), "r"); - if (!fp) - { - ERR("Fail to open available scale factors file:%s\n", temp.c_str()); + fp = fopen(m_available_scale_node_path.c_str(), "r"); + if (!fp) { + ERR("Fail to open available scale factors file:%s\n", m_available_scale_node_path.c_str()); return false; } @@ -457,8 +368,10 @@ bool gyro_sensor_hal::setup_channels(void) m_scale_factor[i] = 0; i = 0; + while (fscanf(fp, "%lf", &sf) > 0) m_scale_factor[i++] = sf; + m_scale_factor_count = i; return true; @@ -476,20 +389,17 @@ void gyro_sensor_hal::decode_data(void) if ((val >> m_channels[3].valid_bits) & 1) val = (val & m_channels[3].mask) | ~m_channels[3].mask; - m_fired_time = (unsigned long long int)(val); - DBG("m_x = %d, m_y = %d, m_z = %d, time = %lluus", m_x, m_y, m_z, m_fired_time); + m_fired_time = (unsigned long long int)(NSEC_TO_MUSEC(val)); + INFO("m_x = %d, m_y = %d, m_z = %d, time = %lluus", m_x, m_y, m_z, m_fired_time); } -bool gyro_sensor_hal::setup_trigger(char* trig_name, bool verify) +bool gyro_sensor_hal::setup_trigger(const char* trig_name, bool verify) { - string temp; - int ret; + int ret = 0; - temp = m_gyro_dir + string(TRIG_PATH); - update_sysfs_string(temp.c_str(), trig_name, verify); - if (ret < 0) - { - ERR("failed to write to current_trigger\n"); + ret = update_sysfs_string(m_trigger_path.c_str(), trig_name); + if (ret < 0) { + ERR("failed to write to current_trigger,%s,%s\n", m_trigger_path.c_str(), trig_name); return false; } INFO("current_trigger setup successfully\n"); @@ -498,31 +408,42 @@ bool gyro_sensor_hal::setup_trigger(char* trig_name, bool verify) bool gyro_sensor_hal::setup_buffer(int enable) { - string temp; int ret; - temp = m_gyro_dir + string(BUFFER_LEN); - INFO("Buffer Length Setup: %s", temp.c_str()); - ret = update_sysfs_num(temp.c_str(), GYRO_RINGBUF_LEN, true); - if (ret < 0) - { + ret = update_sysfs_num(m_buffer_length_node_path.c_str(), GYRO_RINGBUF_LEN, true); + if (ret < 0) { ERR("failed to write to buffer/length\n"); return false; } INFO("buffer/length setup successfully\n"); - temp = m_gyro_dir + string(BUFFER_EN); - INFO("Buffer Enable: %s", temp.c_str()); - ret = update_sysfs_num(temp.c_str(), enable, true); - if (ret < 0) - { + ret = update_sysfs_num(m_buffer_enable_node_path.c_str(), enable, true); + if (ret < 0) { ERR("failed to write to buffer/enable\n"); return false; } + if (enable) INFO("buffer enabled\n"); else INFO("buffer disabled\n"); + return true; +} + +bool gyro_sensor_hal::enable_resource(bool enable) +{ + string temp; + if(enable) + setup_trigger(m_trigger_name.c_str(), enable); + else + setup_trigger("NULL", enable); + for (vector ::iterator it = m_generic_channel_names.begin(); + it != m_generic_channel_names.end(); ++it) { + temp = m_gyro_dir + string(SCAN_EL_DIR) + *it + string("_en"); + if (update_sysfs_num(temp.c_str(), enable) < 0) + return false; + } + setup_buffer(enable); return true; } @@ -530,20 +451,17 @@ extern "C" void *create(void) { gyro_sensor_hal *inst; - try - { + try { inst = new gyro_sensor_hal(); - } - catch (int err) - { - ERR("Failed to create gyro_sensor_hal class, errno : %d, errstr : %s", err, strerror(err)); + } catch (int err) { + ERR("gyro_sensor class create fail , errno : %d , errstr : %s\n", err, strerror(err)); return NULL; } - return (void *)inst; + return (void*)inst; } extern "C" void destroy(void *inst) { - delete (gyro_sensor_hal *)inst; + delete (gyro_sensor_hal*)inst; } diff --git a/src/gyro/gyro_sensor_hal.h b/src/gyro/gyro_sensor_hal.h index 4865d6f..f2e63a6 100755 --- a/src/gyro/gyro_sensor_hal.h +++ b/src/gyro/gyro_sensor_hal.h @@ -1,5 +1,5 @@ /* - * sensord + * gyro_sensor_hal * * Copyright (c) 2014 Samsung Electronics Co., Ltd. * @@ -24,38 +24,9 @@ #include #include -#define INPUT_DEV_NAME "lsm330dlc-gyro" -#define INPUT_TRIG_NAME "lsm330dlc-gyro-trigger" - -#define IIO_DIR "/sys/bus/iio/devices/" -#define GYRO_FREQ "sampling_frequency" -#define GYRO_FREQ_AVLBL "sampling_frequency_available" -#define GYRO_SCALE_AVLBL "in_anglvel_scale_available" -#define GYRO_X_SCALE "in_anglvel_x_scale" -#define GYRO_Y_SCALE "in_anglvel_y_scale" -#define GYRO_Z_SCALE "in_anglvel_z_scale" - -#define NO_OF_CHANNELS 4 #define MAX_FREQ_COUNT 16 #define MAX_SCALING_COUNT 16 -#define CHANNEL_NAME_X "in_anglvel_x" -#define CHANNEL_NAME_Y "in_anglvel_y" -#define CHANNEL_NAME_Z "in_anglvel_z" -#define CHANNEL_NAME_TIME "in_timestamp" -#define ENABLE_SUFFIX "_en" -#define NAME_NODE "/name" -#define BUFFER_EN "buffer/enable" -#define BUFFER_LEN "buffer/length" -#define SCAN_EL_DIR "scan_elements/" - -#define IIO_DEV_BASE_NAME "iio:device" -#define IIO_TRIG_BASE_NAME "trigger" -#define IIO_DEV_STR_LEN 10 -#define IIO_TRIG_STR_LEN 7 - -#define GYRO_RINGBUF_LEN 32 - using std::string; class gyro_sensor_hal : public sensor_hal @@ -67,55 +38,58 @@ public: sensor_type_t get_type(void); bool enable(void); bool disable(void); - bool set_interval(unsigned long val); + bool set_interval(unsigned long ms_interval); bool is_data_ready(bool wait); virtual int get_sensor_data(sensor_data_t &data); - bool get_properties(sensor_properties_t &properties); - bool check_hw_node(void); + virtual bool get_properties(sensor_properties_t &properties); private: int m_x; int m_y; int m_z; + int m_node_handle; unsigned long m_polling_interval; unsigned long long m_fired_time; - bool m_sensorhub_supported; + + int m_scale_factor_count; + int m_sample_freq_count; + int m_sample_freq[MAX_FREQ_COUNT]; + double m_scale_factor[MAX_SCALING_COUNT]; + char *m_data; + int m_scan_size; + struct channel_parameters *m_channels; + + string m_trigger_name; + string m_trigger_path; + string m_buffer_enable_node_path; + string m_buffer_length_node_path; + string m_available_freq_node_path; + string m_available_scale_node_path; + string m_gyro_dir; + vector m_generic_channel_names; string m_model_id; - string m_name; string m_vendor; string m_chip_name; int m_resolution; float m_raw_data_unit; - string m_polling_resource; + string m_data_node; + string m_interval_node; - string m_gyro_dir; - string m_gyro_trig_dir; - string m_buffer_access; - string m_freq_resource; - - int m_scale_factor_count; - int m_sample_freq_count; - int m_sample_freq[MAX_FREQ_COUNT]; - double m_scale_factor[MAX_SCALING_COUNT]; - - int m_fp_buffer; - char *m_data; - int m_scan_size; - struct channel_parameters *m_channels; + bool m_sensorhub_controlled; cmutex m_value_mutex; - bool enable_resource(bool enable); bool update_value(bool wait); - bool is_sensorhub_supported(void); - + bool setup_trigger(const char* trig_name, bool verify); + bool setup_buffer(int enable); + bool enable_resource(bool enable); bool add_gyro_channels_to_array(void); bool setup_channels(void); - bool setup_buffer(int enable); bool setup_trigger(char* trig_name, bool verify); void decode_data(void); + }; -#endif /*_GYRO_SENSOR_HAL_H_*/ +#endif /*_GYRO_SENSOR_HAL_CLASS_H_*/ diff --git a/src/shared/sensor_hal.cpp b/src/shared/sensor_hal.cpp index 8b64ff9..e036203 100755 --- a/src/shared/sensor_hal.cpp +++ b/src/shared/sensor_hal.cpp @@ -151,7 +151,6 @@ bool sensor_hal::get_iio_node_info(const string &key, const string& enable_node_ info.buffer_length_node_path = base_dir + string("buffer/length"); info.trigger_node_path = base_dir + string("trigger/current_trigger"); info.available_freq_node_path = base_dir + string("sampling_frequency_available"); - info.available_scale_node_path = base_dir + string("in_accel_scale_available"); return true; } diff --git a/src/shared/sensor_hal.h b/src/shared/sensor_hal.h index 3d62cbd..261608e 100755 --- a/src/shared/sensor_hal.h +++ b/src/shared/sensor_hal.h @@ -47,7 +47,6 @@ typedef struct { string buffer_length_node_path; string trigger_node_path; string available_freq_node_path; - string available_scale_node_path; string base_dir; } node_path_info; -- 2.7.4 From 310816e8a2cbcd89a487f18710125d932c78685a Mon Sep 17 00:00:00 2001 From: Vibhor Gaur Date: Fri, 14 Nov 2014 15:11:56 +0530 Subject: [PATCH 09/16] Priority queue implementation for the event queue after change in public code for IIO driver suppoert -Adding priority queue implementation after public code is changed for sync with private code. -Shifting addition into priority list to server side as compared to the previous implementation where it was done on the shared side.Optimizes the number of instructions executed and enables insertion during event registration. -priority list has been declared extern in csensor_event_queue.h since we cannot include the file command_worker.cpp(where it is initially declared) in csensor_event_queue.h as it creates a cyclic dependency and fails in build. Change-Id: I4ca49167c78beac802ba868543817d326e6d8a8e --- src/server/command_worker.cpp | 16 +++++++++++++ src/shared/csensor_event_queue.cpp | 2 +- src/shared/csensor_event_queue.h | 48 +++++++++++++++++++++++++++++++++++++- 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/server/command_worker.cpp b/src/server/command_worker.cpp index 37a65b2..4c3adab 100755 --- a/src/server/command_worker.cpp +++ b/src/server/command_worker.cpp @@ -20,16 +20,27 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include #include #include #include +#include +using namespace std; using std::string; +using std::set; using std::make_pair; #define SECURITY_LIB "/usr/lib/libsecurity-server-client.so.1" +set priority_list; + void *command_worker::m_security_handle = NULL; command_worker::security_server_check_privilege_by_sockfd_t command_worker::security_server_check_privilege_by_sockfd = NULL; command_worker::cmd_handler_t command_worker::m_cmd_handlers[]; @@ -552,6 +563,11 @@ bool command_worker::cmd_register_event(void *payload) ret_value = OP_ERROR; goto out; } + if (cmd->event_type == GRAVITY_EVENT_RAW_DATA_REPORT_ON_TIME || cmd->event_type == LINEAR_ACCEL_EVENT_RAW_DATA_REPORT_ON_TIME || cmd->event_type == ORIENTATION_EVENT_RAW_DATA_REPORT_ON_TIME) { + priority_list.insert(ACCELEROMETER_EVENT_RAW_DATA_REPORT_ON_TIME); + priority_list.insert(GYROSCOPE_EVENT_RAW_DATA_REPORT_ON_TIME); + priority_list.insert(GEOMAGNETIC_EVENT_RAW_DATA_REPORT_ON_TIME); + } m_module->add_client(cmd->event_type); diff --git a/src/shared/csensor_event_queue.cpp b/src/shared/csensor_event_queue.cpp index 40059ea..d3aa0dc 100755 --- a/src/shared/csensor_event_queue.cpp +++ b/src/shared/csensor_event_queue.cpp @@ -77,7 +77,7 @@ void* csensor_event_queue::pop(void) while (m_queue.empty()) m_cond_var.wait(u); - void* event = m_queue.front(); + void* event = m_queue.top(); m_queue.pop(); return event; } diff --git a/src/shared/csensor_event_queue.h b/src/shared/csensor_event_queue.h index cd8dee6..4c554d9 100755 --- a/src/shared/csensor_event_queue.h +++ b/src/shared/csensor_event_queue.h @@ -23,19 +23,65 @@ #include #include #include +#include +using namespace std; using std::queue; using std::mutex; using std::lock_guard; using std::unique_lock; using std::condition_variable; +using std::set; + +extern set priority_list; class csensor_event_queue { private: static const unsigned int QUEUE_FULL_SIZE = 1000; - queue m_queue; + class compare { + public: + bool operator() (void* &v1,void *&v2) { + sensor_event_t *e1 = (sensor_event_t *)v1; + sensor_event_t *e2 = (sensor_event_t *)v2; + bool prioritize_e1 = true; + bool prioritize_e2 = true; + + if (priority_list.empty()) + return (e2->data.timestamp < e1->data.timestamp); + + set::iterator iter_e1 = priority_list.find(e1->event_type); + set::iterator iter_e2 = priority_list.find(e2->event_type); + + if (iter_e1 == priority_list.end()) + prioritize_e1 = false; + + if (iter_e2 == priority_list.end()) + prioritize_e2 = false; + + if (prioritize_e2) { + if (!prioritize_e1) + return true; + else { + if (e2->data.timestamp <= e1->data.timestamp) + return true; + return false; + } + } + else { + if (prioritize_e1) + return false; + else if (e2->data.timestamp <= e1->data.timestamp) + return true; + else + return false; + } + } + }; + + std::priority_queue, compare> m_queue; + mutex m_mutex; condition_variable m_cond_var; -- 2.7.4 From 883785aca77d83af8bb42dd45dfa0fb65d96aa01 Mon Sep 17 00:00:00 2001 From: Amit Dharmapurikar Date: Mon, 17 Nov 2014 15:18:22 +0530 Subject: [PATCH 10/16] Synchronizing geo sensor plugin with addition of IIO interface support Change-Id: I089d290206e03f283ed6a9bac2b1dbb8241bc219 Signed-off-by: Amit Dharmapurikar --- packaging/sensord.spec | 2 +- sensors.xml.in | 6 ++ src/geo/CMakeLists.txt | 3 +- src/geo/geo_sensor.cpp | 62 ++++++------ src/geo/geo_sensor.h | 22 +++-- src/geo/geo_sensor_hal.cpp | 228 +++++++++++++++++++++------------------------ src/geo/geo_sensor_hal.h | 48 ++++++---- src/libsensord/client.cpp | 1 - 8 files changed, 190 insertions(+), 182 deletions(-) diff --git a/packaging/sensord.spec b/packaging/sensord.spec index 079f1cc..7420325 100755 --- a/packaging/sensord.spec +++ b/packaging/sensord.spec @@ -12,7 +12,7 @@ Source2: sensord.socket %define gyro_state ON %define proxi_state OFF %define light_state OFF -%define geo_state OFF +%define geo_state ON %define pressure_state OFF %define temperature_state OFF %define orientation_state OFF diff --git a/sensors.xml.in b/sensors.xml.in index bd078aa..37c1c34 100755 --- a/sensors.xml.in +++ b/sensors.xml.in @@ -148,11 +148,17 @@ + + + + + + diff --git a/src/geo/CMakeLists.txt b/src/geo/CMakeLists.txt index 4478781..d011093 100755 --- a/src/geo/CMakeLists.txt +++ b/src/geo/CMakeLists.txt @@ -15,13 +15,14 @@ include_directories(${CMAKE_SOURCE_DIR}/src/libsensord) include(FindPkgConfig) pkg_check_modules(rpkgs REQUIRED vconf) -add_definitions(${rpkgs_CFLAGS} -DUSE_ONLY_ONE_MODULE) +add_definitions(${rpkgs_CFLAGS} -DUSE_ONLY_ONE_MODULE -DUSE_LCD_TYPE_CHECK) set(PROJECT_MAJOR_VERSION "0") set(PROJECT_MINOR_VERSION "0") set(PROJECT_RELEASE_VERSION "1") set(CMAKE_VERBOSE_MAKEFILE OFF) + FIND_PROGRAM(UNAME NAMES uname) EXEC_PROGRAM("${UNAME}" ARGS "-m" OUTPUT_VARIABLE "ARCH") IF("${ARCH}" MATCHES "^arm.*") diff --git a/src/geo/geo_sensor.cpp b/src/geo/geo_sensor.cpp index 6a80591..029df71 100755 --- a/src/geo/geo_sensor.cpp +++ b/src/geo/geo_sensor.cpp @@ -19,6 +19,7 @@ #include #include + #include #include @@ -26,6 +27,7 @@ geo_sensor::geo_sensor() : m_sensor_hal(NULL) +, m_resolution(0.0f) { m_name = string(SENSOR_NAME); @@ -37,7 +39,7 @@ geo_sensor::geo_sensor() geo_sensor::~geo_sensor() { - INFO("geo_sensor is destroyed!"); + INFO("geo_sensor is destroyed!\n"); } bool geo_sensor::init() @@ -49,7 +51,17 @@ bool geo_sensor::init() return false; } - INFO("%s is created!", sensor_base::get_name()); + sensor_properties_t properties; + + if (m_sensor_hal->get_properties(properties) == false) { + ERR("sensor->get_properties() is failed!\n"); + return false; + } + + m_resolution = properties.resolution; + + INFO("%s is created!\n", sensor_base::get_name()); + return true; } @@ -60,8 +72,8 @@ sensor_type_t geo_sensor::get_type(void) bool geo_sensor::working(void *inst) { - geo_sensor *sensor = (geo_sensor *)inst; - return sensor->process_event(); + geo_sensor *sensor = (geo_sensor*)inst; + return sensor->process_event();; } bool geo_sensor::process_event(void) @@ -77,8 +89,9 @@ bool geo_sensor::process_event(void) AUTOLOCK(m_mutex); if (get_client_cnt(GEOMAGNETIC_EVENT_RAW_DATA_REPORT_ON_TIME)) { + event.sensor_id = get_id(); event.event_type = GEOMAGNETIC_EVENT_RAW_DATA_REPORT_ON_TIME; - + raw_to_base(event.data); push(event); } @@ -87,10 +100,8 @@ bool geo_sensor::process_event(void) bool geo_sensor::on_start(void) { - AUTOLOCK(m_mutex); - if (!m_sensor_hal->enable()) { - ERR("m_sensor_hal start fail"); + ERR("m_sensor_hal start fail\n"); return false; } @@ -99,32 +110,20 @@ bool geo_sensor::on_start(void) bool geo_sensor::on_stop(void) { - AUTOLOCK(m_mutex); - if (!m_sensor_hal->disable()) { - ERR("m_sensor_hal stop fail"); + ERR("m_sensor_hal stop fail\n"); return false; } return stop_poll(); } -long geo_sensor::set_command(const unsigned int cmd, long value) -{ - if (m_sensor_hal->set_command(cmd, value) < 0) { - ERR("m_sensor_hal set_cmd fail"); - return -1; - } - - return 0; -} - -bool geo_sensor::get_properties(const unsigned int type, sensor_properties_t &properties) +bool geo_sensor::get_properties(sensor_properties_t &properties) { return m_sensor_hal->get_properties(properties); } -int geo_sensor::get_sensor_data(const unsigned int type, sensor_data_t &data) +int geo_sensor::get_sensor_data(unsigned int type, sensor_data_t &data) { int state; @@ -134,7 +133,7 @@ int geo_sensor::get_sensor_data(const unsigned int type, sensor_data_t &data) state = m_sensor_hal->get_sensor_data(data); if (state < 0) { - ERR("m_sensor_hal get struct_data fail"); + ERR("m_sensor_hal get struct_data fail\n"); return -1; } @@ -146,9 +145,18 @@ bool geo_sensor::set_interval(unsigned long interval) AUTOLOCK(m_mutex); INFO("Polling interval is set to %dms", interval); + return m_sensor_hal->set_interval(interval); } +void geo_sensor::raw_to_base(sensor_data_t &data) +{ + data.value_count = 3; + data.values[0] = data.values[0] * m_resolution; + data.values[1] = data.values[1] * m_resolution; + data.values[2] = data.values[2] * m_resolution; +} + extern "C" void *create(void) { geo_sensor *inst; @@ -156,14 +164,14 @@ extern "C" void *create(void) try { inst = new geo_sensor(); } catch (int err) { - ERR("Failed to create geo_sensor class, errno : %d, errstr : %s", err, strerror(err)); + ERR("geo_sensor class create fail , errno : %d , errstr : %s\n", err, strerror(err)); return NULL; } - return (void *)inst; + return (void*)inst; } extern "C" void destroy(void *inst) { - delete (geo_sensor *)inst; + delete (geo_sensor*)inst;; } diff --git a/src/geo/geo_sensor.h b/src/geo/geo_sensor.h index 449a4d2..c211e5c 100755 --- a/src/geo/geo_sensor.h +++ b/src/geo/geo_sensor.h @@ -21,11 +21,11 @@ #define _GEO_SENSOR_H_ #include + #include #include -class geo_sensor : public physical_sensor -{ +class geo_sensor : public physical_sensor { public: geo_sensor(); virtual ~geo_sensor(); @@ -35,17 +35,19 @@ public: static bool working(void *inst); - virtual bool on_start(void); - virtual bool on_stop(void); - virtual bool set_interval(unsigned long interval); - virtual long set_command(const unsigned int cmd, long value); - virtual bool get_properties(const unsigned int type, sensor_properties_t &properties); - int get_sensor_data(const unsigned int type, sensor_data_t &data); + virtual bool get_properties(sensor_properties_t &properties); + int get_sensor_data(unsigned int type, sensor_data_t &data); private: sensor_hal *m_sensor_hal; - cmutex m_mutex; + float m_resolution; + + virtual bool on_start(void); + virtual bool on_stop(void); bool process_event(void); + void raw_to_base(sensor_data_t &data); }; -#endif /*_GEO_SENSOR_H_*/ + +#endif + diff --git a/src/geo/geo_sensor_hal.cpp b/src/geo/geo_sensor_hal.cpp index f611162..872525b 100755 --- a/src/geo/geo_sensor_hal.cpp +++ b/src/geo/geo_sensor_hal.cpp @@ -1,5 +1,5 @@ /* - * sensord + * geo_sensor_hal * * Copyright (c) 2014 Samsung Electronics Co., Ltd. * @@ -16,90 +16,116 @@ * limitations under the License. * */ - -#include #include #include -#include #include + #include #include + #include +#include +#include +#include #include using std::ifstream; -using std::string; using config::CConfig; #define SENSOR_TYPE_MAGNETIC "MAGNETIC" #define ELEMENT_NAME "NAME" #define ELEMENT_VENDOR "VENDOR" +#define ELEMENT_RAW_DATA_UNIT "RAW_DATA_UNIT" +#define ELEMENT_MIN_RANGE "MIN_RANGE" +#define ELEMENT_MAX_RANGE "MAX_RANGE" #define ATTR_VALUE "value" -#define SENSOR_MIN_RANGE -1200 -#define SENSOR_MAX_RANGE 1200 -#define INITIAL_TIME 0 -#define INITIAL_VALUE -1 -#define GAUSS_TO_UTESLA(val) ((val) * 100) +#define INITIAL_TIME -1 +#define GAUSS_TO_UTESLA(val) ((val) * 100.0f) geo_sensor_hal::geo_sensor_hal() -: m_x(INITIAL_VALUE) -, m_y(INITIAL_VALUE) -, m_z(INITIAL_VALUE) +: m_x(0) +, m_y(0) +, m_z(0) +, m_hdst(0) +, m_node_handle(-1) , m_polling_interval(POLL_1HZ_MS) , m_fired_time(INITIAL_TIME) -, m_sensorhub_supported(false) { - if (!check_hw_node()) - { - ERR("check_hw_node() fail"); + const string sensorhub_interval_node_name = "mag_poll_delay"; + CConfig &config = CConfig::get_instance(); + + node_path_info_query query; + node_path_info info; + int input_method = IIO_METHOD; + + if (!get_model_properties(SENSOR_TYPE_MAGNETIC, m_model_id, input_method)) { + ERR("Failed to find model_properties"); throw ENXIO; + } - CConfig &config = CConfig::get_instance(); + query.input_method = input_method; + query.sensorhub_controlled = m_sensorhub_controlled = is_sensorhub_controlled(sensorhub_interval_node_name); + query.sensor_type = SENSOR_TYPE_MAGNETIC; + query.input_event_key = "geomagnetic_sensor"; + query.iio_enable_node_name = "geomagnetic_enable"; + query.sensorhub_interval_node_name = sensorhub_interval_node_name; - if (!config.get(SENSOR_TYPE_MAGNETIC, m_model_id, ELEMENT_VENDOR, m_vendor)) - { - ERR("[VENDOR] is empty"); + if (!get_node_path_info(query, info)) { + ERR("Failed to get node info"); throw ENXIO; } - INFO("m_vendor = %s", m_vendor.c_str()); + show_node_path_info(info); + + m_data_node = info.data_node_path; + m_enable_node = info.enable_node_path; + m_interval_node = info.interval_node_path; + + if (input_method == IIO_METHOD) { + m_geo_dir = info.base_dir; + m_x_node = m_geo_dir + string(X_RAW_VAL_NODE); + m_y_node = m_geo_dir + string(Y_RAW_VAL_NODE); + m_z_node = m_geo_dir + string(Z_RAW_VAL_NODE); + m_x_scale_node = m_geo_dir + string(X_SCALE_NODE); + m_y_scale_node = m_geo_dir + string(Y_SCALE_NODE); + m_z_scale_node = m_geo_dir + string(Z_SCALE_NODE); + INFO("Raw data node X: %s", m_x_node.c_str()); + INFO("Raw data node Y: %s", m_y_node.c_str()); + INFO("Raw data node Z: %s", m_z_node.c_str()); + INFO("scale node X: %s", m_x_scale_node.c_str()); + INFO("scale node Y: %s", m_y_scale_node.c_str()); + INFO("scale node Z: %s", m_z_scale_node.c_str()); + } - if (!config.get(SENSOR_TYPE_MAGNETIC, m_model_id, ELEMENT_NAME, m_chip_name)) - { - ERR("[NAME] is empty"); + if (!config.get(SENSOR_TYPE_MAGNETIC, m_model_id, ELEMENT_VENDOR, m_vendor)) { + ERR("[VENDOR] is empty\n"); throw ENXIO; } - INFO("m_chip_name = %s", m_chip_name.c_str()); + INFO("m_vendor = %s", m_vendor.c_str()); - if (!init_resources()) + if (!config.get(SENSOR_TYPE_MAGNETIC, m_model_id, ELEMENT_NAME, m_chip_name)) { + ERR("[NAME] is empty\n"); throw ENXIO; + } - INFO("geo_sensor_hal is created!"); -} + init_resources(); + + INFO("m_chip_name = %s\n",m_chip_name.c_str()); + INFO("m_raw_data_unit = %f\n", m_raw_data_unit); + INFO("geo_sensor_hal is created!\n"); -geo_sensor_hal::~geo_sensor_hal() -{ - INFO("geo_sensor_hal is destroyed!"); } -bool geo_sensor_hal::init_resources(void) +geo_sensor_hal::~geo_sensor_hal() { - ifstream temp_handle; - - if (!read_node_value(m_x_scale_node, m_x_scale)) - return false; - - if (!read_node_value(m_y_scale_node, m_y_scale)) - return false; - - if (!read_node_value(m_z_scale_node, m_z_scale)) - return false; + if (m_node_handle > 0) + close(m_node_handle); + m_node_handle = -1; - INFO("Scale Values: %f, %f, %f", m_x_scale, m_y_scale, m_z_scale); - return true; + INFO("geo_sensor is destroyed!\n"); } string geo_sensor_hal::get_model_id(void) @@ -114,13 +140,14 @@ sensor_type_t geo_sensor_hal::get_type(void) bool geo_sensor_hal::enable(void) { - INFO("Resource already enabled. Enable not supported."); + m_fired_time = INITIAL_TIME; + INFO("Geo sensor real starting"); return true; } bool geo_sensor_hal::disable(void) { - INFO("Disable not supported."); + INFO("Geo sensor real stopping"); return true; } @@ -128,6 +155,7 @@ bool geo_sensor_hal::set_interval(unsigned long val) { INFO("Polling interval cannot be changed."); return true; + } bool geo_sensor_hal::update_value(void) @@ -137,10 +165,8 @@ bool geo_sensor_hal::update_value(void) if (!read_node_value(m_x_node, raw_values[0])) return false; - if (!read_node_value(m_y_node, raw_values[1])) return false; - if (!read_node_value(m_z_node, raw_values[2])) return false; @@ -149,7 +175,8 @@ bool geo_sensor_hal::update_value(void) m_z = GAUSS_TO_UTESLA(raw_values[2] * m_z_scale); m_fired_time = INITIAL_TIME; - INFO("x = %d, y = %d, z = %d, time = %lluus", raw_values[0], raw_values[0], raw_values[0], m_fired_time); + INFO("x = %d, y = %d, z = %d, time = %lluus", raw_values[0], raw_values[1], raw_values[2], m_fired_time); + INFO("x = %f, y = %f, z = %f, time = %lluus", m_x, m_y, m_z, m_fired_time); return true; } @@ -163,9 +190,9 @@ bool geo_sensor_hal::is_data_ready(bool wait) int geo_sensor_hal::get_sensor_data(sensor_data_t &data) { - data.data_unit_idx = SENSOR_UNIT_MICRO_TESLA; + data.accuracy = SENSOR_ACCURACY_GOOD; data.timestamp = m_fired_time; - data.values_num = 3; + data.value_count = 3; data.values[0] = (float)m_x; data.values[1] = (float)m_y; data.values[2] = (float)m_z; @@ -174,97 +201,52 @@ int geo_sensor_hal::get_sensor_data(sensor_data_t &data) bool geo_sensor_hal::get_properties(sensor_properties_t &properties) { - properties.sensor_unit_idx = SENSOR_UNIT_MICRO_TESLA; - properties.sensor_min_range = SENSOR_MIN_RANGE; - properties.sensor_max_range = SENSOR_MAX_RANGE; - snprintf(properties.sensor_name, sizeof(properties.sensor_name), "%s", m_chip_name.c_str()); - snprintf(properties.sensor_vendor, sizeof(properties.sensor_vendor), "%s", m_vendor.c_str()); - properties.sensor_resolution = 1; + properties.name = m_chip_name; + properties.vendor = m_vendor; + properties.min_range = m_min_range; + properties.max_range = m_max_range; + properties.min_interval = 1; + properties.resolution = m_raw_data_unit; + properties.fifo_count = 0; + properties.max_batch_count = 0; return true; } -bool geo_sensor_hal::is_sensorhub_supported(void) -{ - return false; -} - -bool geo_sensor_hal::check_hw_node(void) +bool geo_sensor_hal::init_resources(void) { - string hw_name; - string file_name; - DIR *main_dir = NULL; - struct dirent *dir_entry = NULL; - bool find_node = false; - - INFO("======================start check_hw_node============================="); - - m_sensorhub_supported = is_sensorhub_supported(); - main_dir = opendir(IIO_DIR); + ifstream temp_handle; - if (!main_dir) - { - ERR("Could not open IIO directory\n"); + if (!read_node_value(m_x_scale_node, m_x_scale)) { + ERR("Failed to read x scale node"); return false; } - - while (!find_node) - { - dir_entry = readdir(main_dir); - if(dir_entry == NULL) - break; - - if ((strncasecmp(dir_entry->d_name , ".", 1 ) != 0) && (strncasecmp(dir_entry->d_name , "..", 2 ) != 0) && (dir_entry->d_ino != 0)) - { - file_name = string(IIO_DIR) + string(dir_entry->d_name) + string(NAME_NODE); - ifstream infile(file_name.c_str()); - - if (!infile) - continue; - - infile >> hw_name; - - if (CConfig::get_instance().is_supported(SENSOR_TYPE_MAGNETIC, hw_name) == true) - { - m_name = m_model_id = hw_name; - INFO("m_model_id = %s", m_model_id.c_str()); - - string temp = string(IIO_DIR) + string(dir_entry->d_name); - - m_x_node = temp + string(X_RAW_VAL_NODE); - m_y_node = temp + string(Y_RAW_VAL_NODE); - m_z_node = temp + string(Z_RAW_VAL_NODE); - m_x_scale_node = temp + string(X_SCALE_NODE); - m_y_scale_node = temp + string(Y_SCALE_NODE); - m_z_scale_node = temp + string(Z_SCALE_NODE); - - find_node = true; - break; - } - } + if (!read_node_value(m_y_scale_node, m_y_scale)) { + ERR("Failed to read y scale node"); + return false; } - - closedir(main_dir); - return find_node; + if (!read_node_value(m_z_scale_node, m_z_scale)) { + ERR("Failed to read y scale node"); + return false; + } + INFO("Scale Values: %f, %f, %f", m_x_scale, m_y_scale, m_z_scale); + return true; } extern "C" void *create(void) { geo_sensor_hal *inst; - try - { + try { inst = new geo_sensor_hal(); - } - catch (int err) - { - ERR("Failed to create geo_sensor_hal class, errno : %d, errstr : %s", err, strerror(err)); + } catch (int err) { + ERR("geo_sensor_hal class create fail , errno : %d , errstr : %s\n", err, strerror(err)); return NULL; } - return (void *)inst; + return (void*)inst; } extern "C" void destroy(void *inst) { - delete (geo_sensor_hal *)inst; + delete (geo_sensor_hal*)inst; } diff --git a/src/geo/geo_sensor_hal.h b/src/geo/geo_sensor_hal.h index c723fe6..25183d2 100755 --- a/src/geo/geo_sensor_hal.h +++ b/src/geo/geo_sensor_hal.h @@ -1,5 +1,5 @@ /* - * sensord + * geo_sensor_hal * * Copyright (c) 2014 Samsung Electronics Co., Ltd. * @@ -23,14 +23,12 @@ #include #include -#define IIO_DIR "/sys/bus/iio/devices/" -#define X_RAW_VAL_NODE "/in_magn_x_raw" -#define Y_RAW_VAL_NODE "/in_magn_y_raw" -#define Z_RAW_VAL_NODE "/in_magn_z_raw" -#define X_SCALE_NODE "/in_magn_x_scale" -#define Y_SCALE_NODE "/in_magn_y_scale" -#define Z_SCALE_NODE "/in_magn_z_scale" -#define NAME_NODE "/name" +#define X_RAW_VAL_NODE "in_magn_x_raw" +#define Y_RAW_VAL_NODE "in_magn_y_raw" +#define Z_RAW_VAL_NODE "in_magn_z_raw" +#define X_SCALE_NODE "in_magn_x_scale" +#define Y_SCALE_NODE "in_magn_y_scale" +#define Z_SCALE_NODE "in_magn_z_scale" using std::string; @@ -47,9 +45,17 @@ public: bool is_data_ready(bool wait); virtual int get_sensor_data(sensor_data_t &data); bool get_properties(sensor_properties_t &properties); - bool check_hw_node(void); - private: + string m_model_id; + string m_vendor; + string m_chip_name; + + float m_min_range; + float m_max_range; + float m_raw_data_unit; + + unsigned long m_polling_interval; + double m_x; double m_y; double m_z; @@ -57,15 +63,19 @@ private: double m_y_scale; double m_z_scale; - unsigned long m_polling_interval; + int m_hdst; + unsigned long long m_fired_time; - bool m_sensorhub_supported; + int m_node_handle; - string m_model_id; - string m_name; - string m_vendor; - string m_chip_name; + string m_enable_node; + + /*For Input Method*/ + string m_data_node; + string m_interval_node; + /*For IIO method*/ + string m_geo_dir; string m_x_node; string m_y_node; string m_z_node; @@ -73,11 +83,11 @@ private: string m_y_scale_node; string m_z_scale_node; + bool m_sensorhub_controlled; + cmutex m_value_mutex; - bool enable_resource(string &resource_node, bool enable); bool update_value(void); - bool is_sensorhub_supported(void); bool init_resources(void); }; #endif /*_GEO_SENSOR_HAL_H_*/ diff --git a/src/libsensord/client.cpp b/src/libsensord/client.cpp index 4e967cb..9acf884 100755 --- a/src/libsensord/client.cpp +++ b/src/libsensord/client.cpp @@ -1068,7 +1068,6 @@ API bool sensord_get_data(int handle, unsigned int data_id, sensor_data_t* senso return false; } - client_id = event_listener.get_client_id(); retvm_if ((client_id < 0), false, "Invalid client id : %d, handle: %d, %s, %s", client_id, handle, get_sensor_name(sensor_id), get_client_name()); -- 2.7.4 From 06486f2ad6268befa509b85cd36e3f32efa13c6e Mon Sep 17 00:00:00 2001 From: Ramasamy Date: Wed, 19 Nov 2014 16:39:32 +0530 Subject: [PATCH 11/16] Updating sensor fusion library based on testing on Tizen SDK Updating sensor fusion library based on testing sensor fusion solution on Tizen SDK using emulator event injector inputs. Change-Id: I80179399aaa3a0b27cafdf0792c57443a098a067 --- src/sensor_fusion/euler_angles.cpp | 6 ++-- src/sensor_fusion/euler_angles.h | 2 +- src/sensor_fusion/orientation_filter.cpp | 35 +++++++++++++--------- src/sensor_fusion/orientation_filter.h | 4 +-- src/sensor_fusion/quaternion.cpp | 28 +++++++++++++++++ src/sensor_fusion/quaternion.h | 2 ++ src/sensor_fusion/standalone/gravity_sensor.cpp | 2 +- .../standalone/linear_acceleration_sensor.cpp | 2 +- .../standalone/orientation_sensor.cpp | 8 ++--- .../orientation_filter_main.cpp | 4 +-- 10 files changed, 65 insertions(+), 28 deletions(-) diff --git a/src/sensor_fusion/euler_angles.cpp b/src/sensor_fusion/euler_angles.cpp index 1ecce95..9189e6a 100644 --- a/src/sensor_fusion/euler_angles.cpp +++ b/src/sensor_fusion/euler_angles.cpp @@ -30,9 +30,9 @@ euler_angles::euler_angles() : m_ang(EULER_SIZE) } template -euler_angles::euler_angles(const TYPE roll, const TYPE pitch, const TYPE yaw) +euler_angles::euler_angles(const TYPE roll, const TYPE pitch, const TYPE azimuth) { - TYPE euler_data[EULER_SIZE] = {roll, pitch, yaw}; + TYPE euler_data[EULER_SIZE] = {roll, pitch, azimuth}; vect v(EULER_SIZE, euler_data); m_ang = v; @@ -83,7 +83,7 @@ euler_angles quat2euler(const quaternion q) R33 = 2 * (w * w) - 1 + 2 * (z * z); phi = atan2(R32, R33); - theta = -atan(R31 / sqrt(1 - (R31 * R31))); + theta = atan2(-R31 , sqrt((R32 * R32) + (R33 * R33))); psi = atan2(R21, R11); euler_angles e(phi, theta, psi); diff --git a/src/sensor_fusion/euler_angles.h b/src/sensor_fusion/euler_angles.h index 35d8abb..9c5af06 100644 --- a/src/sensor_fusion/euler_angles.h +++ b/src/sensor_fusion/euler_angles.h @@ -29,7 +29,7 @@ public: vect m_ang; euler_angles(); - euler_angles(const TYPE roll, const TYPE pitch, const TYPE yaw); + euler_angles(const TYPE roll, const TYPE pitch, const TYPE azimuth); euler_angles(const vect v); euler_angles(const euler_angles& e); ~euler_angles(); diff --git a/src/sensor_fusion/orientation_filter.cpp b/src/sensor_fusion/orientation_filter.cpp index 7d0cc5a..14058a6 100644 --- a/src/sensor_fusion/orientation_filter.cpp +++ b/src/sensor_fusion/orientation_filter.cpp @@ -22,18 +22,25 @@ #include "orientation_filter.h" +//Windowing is used for buffering of previous samples for statistical analysis #define MOVING_AVERAGE_WINDOW_LENGTH 20 +//Earth's Gravity #define GRAVITY 9.80665 #define PI 3.141593 +//Needed for non-zero initialization for statistical analysis #define NON_ZERO_VAL 0.1 +//microseconds to seconds #define US2S (1.0 / 1000000.0) -#define SAMPLE_FREQ 100000 +//Initialize sampling interval to 100000microseconds +#define SAMPLE_INTV 100000 +// constants for computation of covariance and transition matrices #define ZIGMA_W (0.05 * DEG2RAD) #define TAU_W 1000 #define QWB_CONST ((2 * (ZIGMA_W * ZIGMA_W)) / TAU_W) #define F_CONST (-1 / TAU_W) +// M-matrix, V-vector, MxN=> matrix dimension, R-RowCount, C-Column count #define M3X3R 3 #define M3X3C 3 #define M6X6R 6 @@ -59,7 +66,7 @@ orientation_filter::orientation_filter() m_var_gyr_z = vec; m_var_roll = vec; m_var_pitch = vec; - m_var_yaw = vec; + m_var_azimuth = vec; m_tran_mat = mat6x6; m_measure_mat = mat6x6; @@ -74,7 +81,7 @@ orientation_filter::orientation_filter() m_pitch_phase_compensation = 1; m_roll_phase_compensation = 1; - m_yaw_phase_compensation = 1; + m_azimuth_phase_compensation = 1; m_magnetic_alignment_factor = 1; m_gyro.m_time_stamp = 0; @@ -91,7 +98,7 @@ inline void orientation_filter::initialize_sensor_data(const sensor_data acc_data(V1x3S); vect gyr_data(V1x3S); - unsigned long long sample_interval_gyro = SAMPLE_FREQ; + unsigned long long sample_interval_gyro = SAMPLE_INTV; m_accel.m_data = accel.m_data; m_gyro.m_data = gyro.m_data; @@ -147,21 +154,21 @@ template inline void orientation_filter::compute_covariance() { TYPE var_gyr_x, var_gyr_y, var_gyr_z; - TYPE var_roll, var_pitch, var_yaw; + TYPE var_roll, var_pitch, var_azimuth; insert_end(m_var_gyr_x, m_gyro.m_data.m_vec[0]); insert_end(m_var_gyr_y, m_gyro.m_data.m_vec[1]); insert_end(m_var_gyr_z, m_gyro.m_data.m_vec[2]); insert_end(m_var_roll, m_orientation.m_ang.m_vec[0]); insert_end(m_var_pitch, m_orientation.m_ang.m_vec[1]); - insert_end(m_var_yaw, m_orientation.m_ang.m_vec[2]); + insert_end(m_var_azimuth, m_orientation.m_ang.m_vec[2]); var_gyr_x = var(m_var_gyr_x); var_gyr_y = var(m_var_gyr_y); var_gyr_z = var(m_var_gyr_z); var_roll = var(m_var_roll); var_pitch = var(m_var_pitch); - var_yaw = var(m_var_yaw); + var_azimuth = var(m_var_azimuth); m_driv_cov.m_mat[0][0] = var_gyr_x; m_driv_cov.m_mat[1][1] = var_gyr_y; @@ -172,13 +179,13 @@ inline void orientation_filter::compute_covariance() m_aid_cov.m_mat[0][0] = var_roll; m_aid_cov.m_mat[1][1] = var_pitch; - m_aid_cov.m_mat[2][2] = var_yaw; + m_aid_cov.m_mat[2][2] = var_azimuth; } template inline void orientation_filter::time_update() { - quaternion quat_diff, quat_error; + quaternion quat_diff, quat_error, quat_output; euler_angles euler_error; euler_angles orientation; @@ -210,14 +217,14 @@ inline void orientation_filter::time_update() quat_diff = (m_quat_driv * quat_rot_inc) * (TYPE) 0.5; m_quat_driv = m_quat_driv + (quat_diff * (TYPE) m_gyro_dt * (TYPE) PI); - m_quat_driv.quat_normalize(); + quat_output = phase_correction(m_quat_driv, m_quat_aid); - orientation = quat2euler(m_quat_driv); + orientation = rad2deg(quat2euler(quat_output)); - m_orientation.m_ang.m_vec[0] = orientation.m_ang.m_vec[0] * m_roll_phase_compensation; - m_orientation.m_ang.m_vec[1] = orientation.m_ang.m_vec[1] * m_pitch_phase_compensation; - m_orientation.m_ang.m_vec[2] = orientation.m_ang.m_vec[2] * m_yaw_phase_compensation; + m_orientation.m_ang.m_vec[0] = orientation.m_ang.m_vec[0] * m_pitch_phase_compensation; + m_orientation.m_ang.m_vec[1] = orientation.m_ang.m_vec[1] * m_roll_phase_compensation; + m_orientation.m_ang.m_vec[2] = orientation.m_ang.m_vec[2] * m_azimuth_phase_compensation; m_rot_matrix = quat2rot_mat(m_quat_driv); diff --git a/src/sensor_fusion/orientation_filter.h b/src/sensor_fusion/orientation_filter.h index 02ece6f..f7bc4d7 100644 --- a/src/sensor_fusion/orientation_filter.h +++ b/src/sensor_fusion/orientation_filter.h @@ -38,7 +38,7 @@ public: vect m_var_gyr_z; vect m_var_roll; vect m_var_pitch; - vect m_var_yaw; + vect m_var_azimuth; matrix m_driv_cov; matrix m_aid_cov; matrix m_tran_mat; @@ -56,7 +56,7 @@ public: int m_pitch_phase_compensation; int m_roll_phase_compensation; - int m_yaw_phase_compensation; + int m_azimuth_phase_compensation; int m_magnetic_alignment_factor; orientation_filter(); diff --git a/src/sensor_fusion/quaternion.cpp b/src/sensor_fusion/quaternion.cpp index 7272012..55da0db 100755 --- a/src/sensor_fusion/quaternion.cpp +++ b/src/sensor_fusion/quaternion.cpp @@ -23,6 +23,20 @@ #define QUAT_SIZE 4 +template int sgn(T val) { + if (val >= 0) + return 1; + else + return -1; +} + +template T mag(T val) { + if (val < 0) + return val * (T)-1; + else + return val; +} + template quaternion::quaternion() : m_quat(QUAT_SIZE) { @@ -117,4 +131,18 @@ quaternion operator +(const quaternion q1, const quaternion q2) return (q1.m_quat + q2.m_quat); } + +template +quaternion phase_correction(const quaternion q1, const quaternion q2) +{ + T w, x, y, z; + w = mag(q1.m_quat.m_vec[0]) * sgn(q2.m_quat.m_vec[0]); + x = mag(q1.m_quat.m_vec[1]) * sgn(q2.m_quat.m_vec[1]); + y = mag(q1.m_quat.m_vec[2]) * sgn(q2.m_quat.m_vec[2]); + z = mag(q1.m_quat.m_vec[3]) * sgn(q2.m_quat.m_vec[3]); + + quaternion q(w, x, y, z); + + return q; +} #endif //_QUATERNION_H_ diff --git a/src/sensor_fusion/quaternion.h b/src/sensor_fusion/quaternion.h index 44eaa58..225b129 100755 --- a/src/sensor_fusion/quaternion.h +++ b/src/sensor_fusion/quaternion.h @@ -42,6 +42,8 @@ public: const quaternion q2); template friend quaternion operator +(const quaternion q1, const quaternion q2); + template friend quaternion phase_correction(const quaternion q1, + const quaternion q2); }; #include "quaternion.cpp" diff --git a/src/sensor_fusion/standalone/gravity_sensor.cpp b/src/sensor_fusion/standalone/gravity_sensor.cpp index 8470e36..94a82e7 100644 --- a/src/sensor_fusion/standalone/gravity_sensor.cpp +++ b/src/sensor_fusion/standalone/gravity_sensor.cpp @@ -17,7 +17,7 @@ * */ -#ifdef _GRAVITY_SENSOR_H +#ifdef _GRAVITY_SENSOR_H_ #define GRAVITY 9.80665 diff --git a/src/sensor_fusion/standalone/linear_acceleration_sensor.cpp b/src/sensor_fusion/standalone/linear_acceleration_sensor.cpp index a29a9ca..b38a2d1 100644 --- a/src/sensor_fusion/standalone/linear_acceleration_sensor.cpp +++ b/src/sensor_fusion/standalone/linear_acceleration_sensor.cpp @@ -17,7 +17,7 @@ * */ -#ifdef _LINEAR_ACCELERATION_SENSOR_H +#ifdef _LINEAR_ACCELERATION_SENSOR_H_ sensor_data linear_acceleration_sensor::get_linear_acceleration(const sensor_data accel, const sensor_data gyro, const sensor_data magnetic) diff --git a/src/sensor_fusion/standalone/orientation_sensor.cpp b/src/sensor_fusion/standalone/orientation_sensor.cpp index 26412c2..c006922 100644 --- a/src/sensor_fusion/standalone/orientation_sensor.cpp +++ b/src/sensor_fusion/standalone/orientation_sensor.cpp @@ -17,7 +17,7 @@ * */ -#ifdef _ORIENTATION_SENSOR_H +#ifdef _ORIENTATION_SENSOR_H_ float bias_accel[] = {0.098586, 0.18385, (10.084 - GRAVITY)}; float bias_gyro[] = {-5.3539, 0.24325, 2.3391}; @@ -31,7 +31,7 @@ float scale_magnetic = 1; int pitch_phase_compensation = -1; int roll_phase_compensation = -1; -int yaw_phase_compensation = -1; +int azimuth_phase_compensation = -1; int magnetic_alignment_factor = -1; void pre_process_data(sensor_data &data_out, sensor_data &data_in, float *bias, int *sign, float scale) @@ -55,7 +55,7 @@ euler_angles orientation_sensor::get_orientation(sensor_data accel orien_filter.m_pitch_phase_compensation = pitch_phase_compensation; orien_filter.m_roll_phase_compensation = roll_phase_compensation; - orien_filter.m_yaw_phase_compensation = yaw_phase_compensation; + orien_filter.m_azimuth_phase_compensation = azimuth_phase_compensation; orien_filter.m_magnetic_alignment_factor = magnetic_alignment_factor; return orien_filter.get_orientation(accel_data, gyro_data, magnetic_data); @@ -73,7 +73,7 @@ rotation_matrix orientation_sensor::get_rotation_matrix(sensor_data &data_out, sensor_data &data_in, float *bias, int *sign, float scale) @@ -105,7 +105,7 @@ int main() orien_filter.m_pitch_phase_compensation = pitch_phase_compensation; orien_filter.m_roll_phase_compensation = roll_phase_compensation; - orien_filter.m_yaw_phase_compensation = yaw_phase_compensation; + orien_filter.m_azimuth_phase_compensation = azimuth_phase_compensation; orien_filter.m_magnetic_alignment_factor = magnetic_alignment_factor; orientation = orien_filter.get_orientation(accel_data, gyro_data, magnetic_data); -- 2.7.4 From 26b02228f39d46e722208e74223c514269e03594 Mon Sep 17 00:00:00 2001 From: Amit Dharmapurikar Date: Thu, 20 Nov 2014 10:29:04 +0530 Subject: [PATCH 12/16] Synchronizing proxi sensor plugin with addition of IIO interface support Change-Id: Ic505aab62f6a42572a6cf441a9eb18b196ccb524 Signed-off-by: Amit Dharmapurikar --- packaging/sensord.spec | 2 +- src/proxi/CMakeLists.txt | 4 +- src/proxi/proxi_sensor.cpp | 38 ++++--- src/proxi/proxi_sensor.h | 25 ++-- src/proxi/proxi_sensor_hal.cpp | 252 ++++++++++++++++------------------------- src/proxi/proxi_sensor_hal.h | 46 +++----- 6 files changed, 151 insertions(+), 216 deletions(-) diff --git a/packaging/sensord.spec b/packaging/sensord.spec index 7420325..eb9d4c2 100755 --- a/packaging/sensord.spec +++ b/packaging/sensord.spec @@ -10,7 +10,7 @@ Source2: sensord.socket %define accel_state ON %define gyro_state ON -%define proxi_state OFF +%define proxi_state ON %define light_state OFF %define geo_state ON %define pressure_state OFF diff --git a/src/proxi/CMakeLists.txt b/src/proxi/CMakeLists.txt index 6a52735..f679e41 100755 --- a/src/proxi/CMakeLists.txt +++ b/src/proxi/CMakeLists.txt @@ -15,7 +15,7 @@ include_directories(${CMAKE_SOURCE_DIR}/src/libsensord) include(FindPkgConfig) pkg_check_modules(rpkgs REQUIRED vconf) -add_definitions(${rpkgs_CFLAGS} -DUSE_ONLY_ONE_MODULE) +add_definitions(${rpkgs_CFLAGS} -DUSE_ONLY_ONE_MODULE -DUSE_LCD_TYPE_CHECK) set(PROJECT_MAJOR_VERSION "0") set(PROJECT_MINOR_VERSION "0") @@ -43,7 +43,7 @@ add_library(${SENSOR_NAME} SHARED ) add_library(${SENSOR_HAL_NAME} SHARED - proxi_sensor_hal.cpp + proxi_sensor_hal.cpp ) target_link_libraries(${SENSOR_NAME} ${rpkgs_LDFLAGS} ${GLES_LDFLAGS} "-lm") diff --git a/src/proxi/proxi_sensor.cpp b/src/proxi/proxi_sensor.cpp index 4799da4..c55bc74 100755 --- a/src/proxi/proxi_sensor.cpp +++ b/src/proxi/proxi_sensor.cpp @@ -19,9 +19,11 @@ #include #include + #include #include + #define SENSOR_NAME "PROXI_SENSOR" proxi_sensor::proxi_sensor() @@ -39,7 +41,7 @@ proxi_sensor::proxi_sensor() proxi_sensor::~proxi_sensor() { - INFO("proxi_sensor is destroyed!"); + INFO("proxi_sensor is destroyed!\n"); } bool proxi_sensor::init() @@ -51,7 +53,7 @@ bool proxi_sensor::init() return false; } - INFO("%s is created!", sensor_base::get_name()); + INFO("%s is created!\n", sensor_base::get_name()); return true; } @@ -62,7 +64,7 @@ sensor_type_t proxi_sensor::get_type(void) bool proxi_sensor::working(void *inst) { - proxi_sensor *sensor = (proxi_sensor *)inst; + proxi_sensor *sensor = (proxi_sensor*)inst; return sensor->process_event(); } @@ -79,6 +81,7 @@ bool proxi_sensor::process_event(void) AUTOLOCK(m_client_info_mutex); AUTOLOCK(m_mutex); + event.sensor_id = get_id(); if (get_client_cnt(PROXIMITY_EVENT_DISTANCE_DATA_REPORT_ON_TIME)) { event.event_type = PROXIMITY_EVENT_DISTANCE_DATA_REPORT_ON_TIME; raw_to_base(event.data); @@ -103,10 +106,8 @@ bool proxi_sensor::process_event(void) bool proxi_sensor::on_start(void) { - AUTOLOCK(m_mutex); - if (!m_sensor_hal->enable()) { - ERR("m_sensor_hal start fail"); + ERR("m_sensor_hal start fail\n"); return false; } @@ -115,22 +116,25 @@ bool proxi_sensor::on_start(void) bool proxi_sensor::on_stop(void) { - AUTOLOCK(m_mutex); - if (!m_sensor_hal->disable()) { - ERR("m_sensor_hal stop fail"); + ERR("m_sensor_hal stop fail\n"); return false; } return stop_poll(); } -bool proxi_sensor::get_properties(const unsigned int type, sensor_properties_t &properties) +bool proxi_sensor::get_properties(sensor_properties_t &properties) { - return m_sensor_hal->get_properties(properties); + m_sensor_hal->get_properties(properties); + + properties.min_range = properties.min_range * 5; + properties.max_range = properties.max_range * 5; + + return true; } -int proxi_sensor::get_sensor_data(const unsigned int type, sensor_data_t &data) +int proxi_sensor::get_sensor_data(unsigned int type, sensor_data_t &data) { int state; @@ -140,7 +144,7 @@ int proxi_sensor::get_sensor_data(const unsigned int type, sensor_data_t &data) state = m_sensor_hal->get_sensor_data(data); if (state < 0) { - ERR("m_sensor_hal get struct_data fail"); + ERR("m_sensor_hal get struct_data fail\n"); return -1; } @@ -159,7 +163,7 @@ void proxi_sensor::raw_to_base(sensor_data_t &data) void proxi_sensor::raw_to_state(sensor_data_t &data) { - data.values_num = 1; + data.value_count = 1; } extern "C" void *create(void) @@ -169,14 +173,14 @@ extern "C" void *create(void) try { inst = new proxi_sensor(); } catch (int err) { - ERR("Failed to create proxi_sensor class, errno : %d, errstr : %s", err, strerror(err)); + ERR("proxi_sensor class create fail , errno : %d , errstr : %s\n", err, strerror(err)); return NULL; } - return (void *)inst; + return (void*)inst; } extern "C" void destroy(void *inst) { - delete (proxi_sensor *)inst; + delete (proxi_sensor*)inst; } diff --git a/src/proxi/proxi_sensor.h b/src/proxi/proxi_sensor.h index 5b8a411..b61da04 100755 --- a/src/proxi/proxi_sensor.h +++ b/src/proxi/proxi_sensor.h @@ -21,33 +21,36 @@ #define _PROXI_SENSOR_H_ #include + #include #include -class proxi_sensor : public physical_sensor -{ +class proxi_sensor : public physical_sensor { public: proxi_sensor(); virtual ~proxi_sensor(); - virtual bool init(); - virtual sensor_type_t get_type(void); + bool init(); + sensor_type_t get_type(void); static bool working(void *inst); - virtual bool on_start(void); - virtual bool on_stop(void); - - virtual bool get_properties(const unsigned int type, sensor_properties_t &properties); - virtual int get_sensor_data(const unsigned int type, sensor_data_t &data); + virtual bool get_properties(sensor_properties_t &properties); + int get_sensor_data(unsigned int type, sensor_data_t &data); private: sensor_hal *m_sensor_hal; - cmutex m_value_mutex; int m_state; + cmutex m_value_mutex; + + + virtual bool on_start(void); + virtual bool on_stop(void); + void raw_to_base(sensor_data_t &data); void raw_to_state(sensor_data_t &data); bool process_event(void); }; -#endif /*_PROXI_SENSOR_H_*/ + +#endif diff --git a/src/proxi/proxi_sensor_hal.cpp b/src/proxi/proxi_sensor_hal.cpp index c1fb0d7..1701100 100755 --- a/src/proxi/proxi_sensor_hal.cpp +++ b/src/proxi/proxi_sensor_hal.cpp @@ -1,5 +1,5 @@ /* - * sensord + * proxi_sensor_hal * * Copyright (c) 2014 Samsung Electronics Co., Ltd. * @@ -16,90 +16,111 @@ * limitations under the License. * */ - -#include #include #include -#include #include + #include #include + #include +#include +#include +#include #include -using std::ifstream; -using config::CConfig; +#define NO_FLAG 0 +#define PROXIMITY_TYPE 8 -#define INITIAL_VALUE -1 -#define INITIAL_TIME 0 +#define EVENT_DIR "events/" +#define EVENT_EN_NODE "in_proximity_thresh_either_en" #define SENSOR_TYPE_PROXI "PROXI" -#define ELEMENT_NAME "NAME" +#define ELEMENT_NAME "NAME" #define ELEMENT_VENDOR "VENDOR" -#define ATTR_VALUE "value" +#define ATTR_VALUE "value" -#define INPUT_NAME "proximity_sensor" +#define PROXI_CODE 0x0019 -#define NO_FLAG 0 -#define ENABLE_VAL true -#define DISABLE_VAL false +using std::ifstream; +using config::CConfig; proxi_sensor_hal::proxi_sensor_hal() : m_state(PROXIMITY_STATE_FAR) -, m_node_handle(INITIAL_VALUE) -, m_fired_time(INITIAL_TIME) -, m_sensorhub_supported(false) +, m_fired_time(0) +, m_node_handle(-1) { - int fd, ret; + const string sensorhub_interval_node_name = "prox_poll_delay"; + CConfig &config = CConfig::get_instance(); - if (!check_hw_node()) - { - ERR("check_hw_node() fail"); + node_path_info_query query; + node_path_info info; + int input_method = IIO_METHOD; + + if (!get_model_properties(SENSOR_TYPE_PROXI, m_model_id, input_method)) { + ERR("Failed to find model_properties"); throw ENXIO; + } - CConfig &config = CConfig::get_instance(); + query.input_method = input_method; + query.sensorhub_controlled = m_sensorhub_controlled = false; + query.sensor_type = SENSOR_TYPE_PROXI; + query.input_event_key = "proximity_sensor"; + query.iio_enable_node_name = "proximity_enable"; + query.sensorhub_interval_node_name = sensorhub_interval_node_name; - if (!config.get(SENSOR_TYPE_PROXI, m_model_id, ELEMENT_VENDOR, m_vendor)) - { - ERR("[VENDOR] is empty"); + if (!get_node_path_info(query, info)) { + ERR("Failed to get node info"); + throw ENXIO; + } + + m_data_node = info.data_node_path; + m_enable_node = info.base_dir + string(EVENT_DIR) + string(EVENT_EN_NODE); + + INFO("data node: %s",m_data_node.c_str()); + INFO("enable node: %s",m_enable_node.c_str()); + + if (!config.get(SENSOR_TYPE_PROXI, m_model_id, ELEMENT_VENDOR, m_vendor)) { + ERR("[VENDOR] is empty\n"); throw ENXIO; } INFO("m_vendor = %s", m_vendor.c_str()); - if (!config.get(SENSOR_TYPE_PROXI, m_model_id, ELEMENT_NAME, m_chip_name)) - { - ERR("[NAME] is empty"); + if (!config.get(SENSOR_TYPE_PROXI, m_model_id, ELEMENT_NAME, m_chip_name)) { + ERR("[NAME] is empty\n"); throw ENXIO; } - INFO("m_chip_name = %s", m_chip_name.c_str()); + INFO("m_chip_name = %s\n",m_chip_name.c_str()); - fd = open(m_event_resource.c_str(), NO_FLAG); - if (fd == -1) - { + int fd, ret; + fd = open(m_data_node.c_str(), NO_FLAG); + if (fd == -1) { ERR("Could not open event resource"); throw ENXIO; } - ret = ioctl(fd, IOCTL_IIO_EVENT_FD, &m_event_fd); + ret = ioctl(fd, IOCTL_IIO_EVENT_FD, &m_node_handle); close(fd); - if ((ret == -1) || (m_event_fd == -1)) - { + if ((ret == -1) || (m_node_handle == -1)) { ERR("Failed to retrieve event fd"); throw ENXIO; } - INFO("proxi_sensor_hal is created!"); + INFO("Proxi_sensor_hal is created!\n"); + } proxi_sensor_hal::~proxi_sensor_hal() { - close(m_event_fd); - INFO("proxi_sensor_hal is destroyed!"); + close(m_node_handle); + m_node_handle = -1; + + INFO("Proxi_sensor_hal is destroyed!\n"); } string proxi_sensor_hal::get_model_id(void) @@ -114,7 +135,7 @@ sensor_type_t proxi_sensor_hal::get_type(void) bool proxi_sensor_hal::enable_resource(bool enable) { - update_sysfs_num(m_enable_resource.c_str(), enable); + update_sysfs_num(m_enable_node.c_str(), enable); return true; } @@ -122,10 +143,10 @@ bool proxi_sensor_hal::enable(void) { AUTOLOCK(m_mutex); - enable_resource(ENABLE_VAL); + enable_resource(true); m_fired_time = 0; - INFO("Proximity sensor real starting"); + INFO("Proxi sensor real starting"); return true; } @@ -133,9 +154,9 @@ bool proxi_sensor_hal::disable(void) { AUTOLOCK(m_mutex); - enable_resource(DISABLE_VAL); + enable_resource(true); - INFO("Proximity sensor real stopping"); + INFO("Proxi sensor real stopping"); return true; } @@ -146,71 +167,60 @@ bool proxi_sensor_hal::update_value(bool wait) FD_ZERO(&readfds); FD_ZERO(&exceptfds); - FD_SET(m_event_fd, &readfds); - FD_SET(m_event_fd, &exceptfds); + FD_SET(m_node_handle, &readfds); + FD_SET(m_node_handle, &exceptfds); int ret; - ret = select(m_event_fd + 1, &readfds, NULL, &exceptfds, NULL); + ret = select(m_node_handle + 1, &readfds, NULL, &exceptfds, NULL); - if (ret == -1) - { - ERR("select error:%s m_event_fd:d", strerror(errno), m_event_fd); + if (ret == -1) { + ERR("select error:%s m_node_handle:%d", strerror(errno), m_node_handle); return false; } - else if (!ret) - { + else if (!ret) { DBG("select timeout"); return false; } - if (FD_ISSET(m_event_fd, &exceptfds)) - { + if (FD_ISSET(m_node_handle, &exceptfds)) { ERR("select exception occurred!"); return false; } - if (FD_ISSET(m_event_fd, &readfds)) - { + if (FD_ISSET(m_node_handle, &readfds)) { INFO("proximity event detection!"); - int len = read(m_event_fd, &proxi_event, sizeof(proxi_event)); + int len = read(m_node_handle, &proxi_event, sizeof(proxi_event)); - if (len == -1) - { - DBG("Error in read(m_event_fd):%s.", strerror(errno)); + if (len == -1) { + DBG("Error in read(m_node_handle):%s.", strerror(errno)); return false; } ull_bytes_t ev_data; ev_data.num = proxi_event.event_id; - if (ev_data.bytes[CH_TYPE] == PROXIMITY_TYPE) - { + if (ev_data.bytes[CH_TYPE] == PROXIMITY_TYPE) { AUTOLOCK(m_value_mutex); int temp; temp = GET_DIR_VAL(ev_data.bytes[DIRECTION]); - if (temp == PROXIMITY_NODE_STATE_FAR) - { + if (temp == PROXIMITY_NODE_STATE_FAR) { INFO("PROXIMITY_STATE_FAR state occurred"); m_state = PROXIMITY_STATE_FAR; } - else if (temp == PROXIMITY_NODE_STATE_NEAR) - { + else if (temp == PROXIMITY_NODE_STATE_NEAR) { INFO("PROXIMITY_STATE_NEAR state occurred"); m_state = PROXIMITY_STATE_NEAR; } - else - { + else { ERR("PROXIMITY_STATE Unknown: %d", proxi_event.event_id); return false; } } m_fired_time = proxi_event.timestamp; } - else - { + else { ERR("No proximity event data available to read"); return false; } - return true; } @@ -224,110 +234,42 @@ bool proxi_sensor_hal::is_data_ready(bool wait) int proxi_sensor_hal::get_sensor_data(sensor_data_t &data) { AUTOLOCK(m_value_mutex); - data.data_accuracy = SENSOR_ACCURACY_UNDEFINED; - data.data_unit_idx = SENSOR_UNIT_STATE_ON_OFF; + data.accuracy = SENSOR_ACCURACY_UNDEFINED; data.timestamp = m_fired_time; - data.values_num = 1; - data.values[0] = (float)(m_state); + data.value_count = 1; + data.values[0] = m_state; + return 0; } bool proxi_sensor_hal::get_properties(sensor_properties_t &properties) { - properties.sensor_unit_idx = SENSOR_UNIT_STATE_ON_OFF; - properties.sensor_min_range = 0; - properties.sensor_max_range = 1; - snprintf(properties.sensor_name, sizeof(properties.sensor_name), "%s", m_chip_name.c_str()); - snprintf(properties.sensor_vendor, sizeof(properties.sensor_vendor), "%s", m_vendor.c_str()); - properties.sensor_resolution = 1; + properties.name = m_chip_name; + properties.vendor = m_vendor; + properties.min_range = 0; + properties.max_range = 1; + properties.min_interval = 1; + properties.resolution = 1; + properties.fifo_count = 0; + properties.max_batch_count = 0; return true; } -bool proxi_sensor_hal::is_sensorhub_supported(void) -{ - return false; -} - -bool proxi_sensor_hal::check_hw_node(void) -{ - string name_node; - string hw_name; - string file_name; - DIR *main_dir = NULL; - struct dirent *dir_entry = NULL; - bool find_node = false; - - INFO("======================start check_hw_node============================="); - - m_sensorhub_supported = is_sensorhub_supported(); - main_dir = opendir(IIO_DIR); - - if (!main_dir) - { - ERR("Could not open IIO directory\n"); - return false; - } - - while (!find_node) - { - dir_entry = readdir(main_dir); - if(dir_entry == NULL) - break; - - if ((strncasecmp(dir_entry->d_name , ".", 1 ) != 0) && (strncasecmp(dir_entry->d_name , "..", 2 ) != 0) && (dir_entry->d_ino != 0)) - { - file_name = string(IIO_DIR) + string(dir_entry->d_name) + string(NAME_NODE); - - ifstream infile(file_name.c_str()); - - if (!infile) - continue; - - infile >> hw_name; - - if (strncmp(dir_entry->d_name, IIO_DEV_BASE_NAME, IIO_DEV_STR_LEN) == 0) - { - if (CConfig::get_instance().is_supported(SENSOR_TYPE_PROXI, hw_name) == true) - { - m_name = m_model_id = hw_name; - m_proxi_dir = string(dir_entry->d_name); - m_enable_resource = string(IIO_DIR) + m_proxi_dir + string(EVENT_DIR) + string(EVENT_EN_NODE); - m_event_resource = string(DEV_DIR) + m_proxi_dir; - - INFO("m_enable_resource = %s", m_enable_resource.c_str()); - INFO("m_model_id = %s", m_model_id.c_str()); - INFO("m_proxi_dir = %s", m_proxi_dir.c_str()); - INFO("m_event_resource = %s", m_event_resource.c_str()); - - find_node = true; - break; - } - } - } - } - - closedir(main_dir); - return find_node; -} - extern "C" void *create(void) { proxi_sensor_hal *inst; - try - { + try { inst = new proxi_sensor_hal(); - } - catch (int err) - { - ERR("Failed to create proxi_sensor_hal class, errno : %d, errstr : %s", err, strerror(err)); + } catch (int err) { + ERR("proxi_sensor class create fail , errno : %d , errstr : %s\n", err, strerror(err)); return NULL; } - return (void *)inst; + return (void*)inst; } extern "C" void destroy(void *inst) { - delete (proxi_sensor_hal *)inst; + delete (proxi_sensor_hal*)inst; } diff --git a/src/proxi/proxi_sensor_hal.h b/src/proxi/proxi_sensor_hal.h index bd273fa..6113397 100755 --- a/src/proxi/proxi_sensor_hal.h +++ b/src/proxi/proxi_sensor_hal.h @@ -1,5 +1,5 @@ /* - * sensord + * proxi_sensor_hal * * Copyright (c) 2014 Samsung Electronics Co., Ltd. * @@ -23,24 +23,17 @@ #include #include -#define IIO_DIR "/sys/bus/iio/devices/" -#define NAME_NODE "/name" -#define EVENT_DIR "/events" -#define EVENT_EN_NODE "/in_proximity_thresh_either_en" -#define DEV_DIR "/dev/" - -#define IIO_DEV_BASE_NAME "iio:device" -#define IIO_DEV_STR_LEN 10 - -#define PROXIMITY_NODE_STATE_NEAR 1 -#define PROXIMITY_NODE_STATE_FAR 2 -#define PROXIMITY_TYPE 8 - using std::string; class proxi_sensor_hal : public sensor_hal { public: + enum proxi_node_state_event_t { //changed as per IIO definitions + PROXIMITY_NODE_STATE_NEAR = 1, + PROXIMITY_NODE_STATE_FAR = 2, + PROXIMITY_NODE_STATE_UNKNOWN = 0, + }; + proxi_sensor_hal(); virtual ~proxi_sensor_hal(); string get_model_id(void); @@ -49,31 +42,24 @@ public: bool disable(void); bool is_data_ready(bool wait); virtual int get_sensor_data(sensor_data_t &data); - bool get_properties(sensor_properties_t &properties); - bool check_hw_node(void); - + virtual bool get_properties(sensor_properties_t &properties); private: - unsigned int m_state; - int m_node_handle; - unsigned long long m_fired_time; - bool m_sensorhub_supported; - string m_model_id; - string m_name; string m_vendor; string m_chip_name; - string m_proxi_dir; + string m_enable_node; + string m_data_node; - string m_enable_resource; - string m_event_resource; + unsigned int m_state; - cmutex m_value_mutex; + unsigned long long m_fired_time; - int m_event_fd; + int m_node_handle; + bool m_sensorhub_controlled; + cmutex m_value_mutex; - bool enable_resource(bool enable); bool update_value(bool wait); - bool is_sensorhub_supported(void); + bool enable_resource(bool enable); }; #endif /*_PROXI_SENSOR_HAL_H_*/ -- 2.7.4 From 6bf910fdf6bf2e1cd212ccaf79bceecce1c67961 Mon Sep 17 00:00:00 2001 From: Ramasamy Date: Wed, 19 Nov 2014 17:41:05 +0530 Subject: [PATCH 13/16] Cleanup of sensor fusion related code Cleanup of legacy sensor fusion code remaining after private code sync with public code. Change-Id: I66673a52e23c67e4839a0abce1f10b959cb5610f --- src/shared/csensor_event_dispatcher.cpp | 4 --- src/shared/fusion_util.cpp | 56 --------------------------------- src/shared/fusion_util.h | 34 -------------------- src/shared/sensor_plugin_loader.cpp | 7 +---- src/shared/sensor_plugin_loader.h | 11 ------- src/shared/virtual_sensor.cpp | 7 +++++ src/shared/virtual_sensor.h | 5 ++- 7 files changed, 12 insertions(+), 112 deletions(-) delete mode 100644 src/shared/fusion_util.cpp delete mode 100755 src/shared/fusion_util.h diff --git a/src/shared/csensor_event_dispatcher.cpp b/src/shared/csensor_event_dispatcher.cpp index 3b90c34..f9f90d5 100755 --- a/src/shared/csensor_event_dispatcher.cpp +++ b/src/shared/csensor_event_dispatcher.cpp @@ -151,7 +151,6 @@ void csensor_event_dispatcher::dispatch_event(void) } else { sensor_event_t sensor_events[MAX_SENSOR_EVENT]; unsigned int event_cnt = 0; - sensor_events[event_cnt++] = *((sensor_event_t *)seed_event); virtual_sensors v_sensors = get_active_virtual_sensors(); @@ -160,11 +159,8 @@ void csensor_event_dispatcher::dispatch_event(void) while (it_v_sensor != v_sensors.end()) { int synthesized_cnt; - v_sensor_events.clear(); - (*it_v_sensor)->synthesize(*((sensor_event_t *)seed_event), v_sensor_events); - synthesized_cnt = v_sensor_events.size(); for (int i = 0; i < synthesized_cnt; ++i) diff --git a/src/shared/fusion_util.cpp b/src/shared/fusion_util.cpp deleted file mode 100644 index b6d2c14..0000000 --- a/src/shared/fusion_util.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * libsensord-share - * - * Copyright (c) 2014 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 - -int quat_to_matrix(const float *quat, float *R) -{ - if(quat == NULL || R == NULL) - return -1; - - float q0 = quat[3]; - float q1 = quat[0]; - float q2 = quat[1]; - float q3 = quat[2]; - - float sq_q1 = 2 * q1 * q1; - float sq_q2 = 2 * q2 * q2; - float sq_q3 = 2 * q3 * q3; - float q1_q2 = 2 * q1 * q2; - float q3_q0 = 2 * q3 * q0; - float q1_q3 = 2 * q1 * q3; - float q2_q0 = 2 * q2 * q0; - float q2_q3 = 2 * q2 * q3; - float q1_q0 = 2 * q1 * q0; - - R[0] = 1 - sq_q2 - sq_q3; - R[1] = q1_q2 - q3_q0; - R[2] = q1_q3 + q2_q0; - R[3] = q1_q2 + q3_q0; - R[4] = 1 - sq_q1 - sq_q3; - R[5] = q2_q3 - q1_q0; - R[6] = q1_q3 - q2_q0; - R[7] = q2_q3 + q1_q0; - R[8] = 1 - sq_q1 - sq_q2; - - return 0; -} - diff --git a/src/shared/fusion_util.h b/src/shared/fusion_util.h deleted file mode 100755 index 53310c1..0000000 --- a/src/shared/fusion_util.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * libsensord-share - * - * Copyright (c) 2014 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 _FUSION_UTIL_H_ -#define _FUSION_UTIL_H_ - -#ifdef __cplusplus -extern "C" -{ -#endif - -int quat_to_matrix(const float *quat, float *R); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/shared/sensor_plugin_loader.cpp b/src/shared/sensor_plugin_loader.cpp index 9819a15..09f6711 100755 --- a/src/shared/sensor_plugin_loader.cpp +++ b/src/shared/sensor_plugin_loader.cpp @@ -17,15 +17,11 @@ * */ - #include - #include #include - #include #include - #include #include #include @@ -39,7 +35,6 @@ using std::unordered_set; #define ROOT_ELEMENT "PLUGIN" #define TEXT_ELEMENT "text" #define PATH_ATTR "path" - #define HAL_ELEMENT "HAL" #define SENSOR_ELEMENT "SENSOR" @@ -106,7 +101,7 @@ bool sensor_plugin_loader::load_module(const string &path, void** module, void** bool sensor_plugin_loader::insert_module(plugin_type type, const string &path) { if (type == PLUGIN_TYPE_HAL) { - DBG("insert sensor plugin [%s]", path); + DBG("insert sensor plugin [%s]", path.c_str()); sensor_hal *module; void *handle; diff --git a/src/shared/sensor_plugin_loader.h b/src/shared/sensor_plugin_loader.h index 332db4a..5964835 100755 --- a/src/shared/sensor_plugin_loader.h +++ b/src/shared/sensor_plugin_loader.h @@ -33,7 +33,6 @@ class sensor_hal; class sensor_base; -class sensor_fusion; using std::pair; using std::vector; @@ -60,15 +59,6 @@ typedef multimap sensor_plugins; * */ -typedef vector fusion_plugins; -/* -* a fusion_plugins is a group of fusion plugin -* -* ... -* -* -*/ - class sensor_plugin_loader { private: @@ -87,7 +77,6 @@ private: sensor_hal_plugins m_sensor_hals; sensor_plugins m_sensors; - fusion_plugins m_fusions; public: static sensor_plugin_loader& get_instance(); diff --git a/src/shared/virtual_sensor.cpp b/src/shared/virtual_sensor.cpp index 221884b..bd0aa84 100755 --- a/src/shared/virtual_sensor.cpp +++ b/src/shared/virtual_sensor.cpp @@ -46,3 +46,10 @@ bool virtual_sensor::deactivate(void) { return csensor_event_dispatcher::get_instance().delete_active_virtual_sensor(this); } + +bool virtual_sensor::push(sensor_event_t const &event) +{ + csensor_event_queue::get_instance().push(event); + return true; +} + diff --git a/src/shared/virtual_sensor.h b/src/shared/virtual_sensor.h index 94acc4c..d592ce6 100755 --- a/src/shared/virtual_sensor.h +++ b/src/shared/virtual_sensor.h @@ -28,12 +28,15 @@ public: virtual_sensor(); virtual ~virtual_sensor(); - virtual void synthesize(const sensor_event_t& event, vector &outs) = 0; + virtual void synthesize(const sensor_event_t &event, vector &outs) = 0; + virtual int get_sensor_data(const unsigned int event_type, sensor_data_t &data) = 0; bool is_virtual(void); protected: bool activate(void); bool deactivate(void); + + bool push(sensor_event_t const &event); }; #endif -- 2.7.4 From f0b884c4461d0bcaf247f70c260dd3e434c62b50 Mon Sep 17 00:00:00 2001 From: Ramasamy Date: Thu, 20 Nov 2014 11:08:23 +0530 Subject: [PATCH 14/16] Renaming cconfig files and references to csensor_config renaming cconfig to csensor_config to add cvirtual_sensor_config class for adding virtual sensor configuration. A virtual parent class cconfig will be added later. Change-Id: I801793991233a0b27cafdf0792c57443a098a067 --- src/accel/accel_sensor_hal.cpp | 6 ++--- src/geo/geo_sensor_hal.cpp | 9 +++----- src/gyro/gyro_sensor_hal.cpp | 7 +++--- src/light/light_sensor_hal.cpp | 6 ++--- src/pressure/pressure_sensor.cpp | 6 ++--- src/pressure/pressure_sensor_hal.cpp | 9 +++----- src/proxi/proxi_sensor.cpp | 2 -- src/proxi/proxi_sensor.h | 1 - src/proxi/proxi_sensor_hal.cpp | 12 ++++------ src/shared/CMakeLists.txt | 4 ++-- src/shared/{cconfig.cpp => csensor_config.cpp} | 32 +++++++++++++------------- src/shared/{cconfig.h => csensor_config.h} | 20 ++++++++-------- src/shared/sensor_hal.cpp | 6 ++--- src/temperature/temperature_sensor_hal.cpp | 9 +++----- 14 files changed, 57 insertions(+), 72 deletions(-) rename src/shared/{cconfig.cpp => csensor_config.cpp} (84%) rename src/shared/{cconfig.h => csensor_config.h} (85%) diff --git a/src/accel/accel_sensor_hal.cpp b/src/accel/accel_sensor_hal.cpp index f38a0ed..cb74385 100755 --- a/src/accel/accel_sensor_hal.cpp +++ b/src/accel/accel_sensor_hal.cpp @@ -20,13 +20,13 @@ #include #include -#include +#include #include #include using std::ifstream; -using config::CConfig; +using config::csensor_config; #define GRAVITY 9.80665 #define G_TO_MG 1000 @@ -63,7 +63,7 @@ accel_sensor_hal::accel_sensor_hal() , m_fired_time(0) { const string sensorhub_interval_node_name = "accel_poll_delay"; - CConfig &config = CConfig::get_instance(); + csensor_config &config = csensor_config::get_instance(); node_path_info_query query; node_path_info info; diff --git a/src/geo/geo_sensor_hal.cpp b/src/geo/geo_sensor_hal.cpp index 872525b..bfbb50e 100755 --- a/src/geo/geo_sensor_hal.cpp +++ b/src/geo/geo_sensor_hal.cpp @@ -19,18 +19,15 @@ #include #include #include - #include -#include - +#include #include #include #include -#include #include using std::ifstream; -using config::CConfig; +using config::csensor_config; #define SENSOR_TYPE_MAGNETIC "MAGNETIC" #define ELEMENT_NAME "NAME" @@ -53,7 +50,7 @@ geo_sensor_hal::geo_sensor_hal() , m_fired_time(INITIAL_TIME) { const string sensorhub_interval_node_name = "mag_poll_delay"; - CConfig &config = CConfig::get_instance(); + csensor_config &config = csensor_config::get_instance(); node_path_info_query query; node_path_info info; diff --git a/src/gyro/gyro_sensor_hal.cpp b/src/gyro/gyro_sensor_hal.cpp index fe04ca0..34184bc 100755 --- a/src/gyro/gyro_sensor_hal.cpp +++ b/src/gyro/gyro_sensor_hal.cpp @@ -21,16 +21,15 @@ #include #include -#include +#include #include #include #include -#include #include using std::ifstream; -using config::CConfig; +using config::csensor_config; #define DPS_TO_MDPS 1000 #define MIN_RANGE(RES) (-((1 << (RES))/2)) @@ -63,7 +62,7 @@ gyro_sensor_hal::gyro_sensor_hal() { const string sensorhub_interval_node_name = "gyro_poll_delay"; - CConfig &config = CConfig::get_instance(); + csensor_config &config = csensor_config::get_instance(); node_path_info_query query; node_path_info info; diff --git a/src/light/light_sensor_hal.cpp b/src/light/light_sensor_hal.cpp index 445828c..b429ffc 100755 --- a/src/light/light_sensor_hal.cpp +++ b/src/light/light_sensor_hal.cpp @@ -23,12 +23,12 @@ #include #include #include -#include +#include #include #include using std::ifstream; -using config::CConfig; +using config::csensor_config; #define BIAS 1 #define INITIAL_VALUE -1 @@ -52,7 +52,7 @@ light_sensor_hal::light_sensor_hal() throw ENXIO; } - CConfig &config = CConfig::get_instance(); + csensor_config &config = csensor_config::get_instance(); if (!config.get(SENSOR_TYPE_LIGHT, m_model_id, ELEMENT_VENDOR, m_vendor)) { diff --git a/src/pressure/pressure_sensor.cpp b/src/pressure/pressure_sensor.cpp index e13c3a4..35461a7 100755 --- a/src/pressure/pressure_sensor.cpp +++ b/src/pressure/pressure_sensor.cpp @@ -23,9 +23,9 @@ #include #include #include -#include +#include -using config::CConfig; +using config::csensor_config; using std::bind1st; using std::mem_fun; @@ -79,7 +79,7 @@ bool pressure_sensor::init() string model_id = m_sensor_hal->get_model_id(); - CConfig &config = CConfig::get_instance(); + csensor_config &config = csensor_config::get_instance(); double temperature_resolution; diff --git a/src/pressure/pressure_sensor_hal.cpp b/src/pressure/pressure_sensor_hal.cpp index 37dc7ac..764adc9 100755 --- a/src/pressure/pressure_sensor_hal.cpp +++ b/src/pressure/pressure_sensor_hal.cpp @@ -19,18 +19,15 @@ #include #include #include - #include -#include - +#include #include #include #include -#include #include using std::ifstream; -using config::CConfig; +using config::csensor_config; #define SENSOR_TYPE_PRESSURE "PRESSURE" #define ELEMENT_NAME "NAME" @@ -61,7 +58,7 @@ pressure_sensor_hal::pressure_sensor_hal() throw ENXIO; } - CConfig &config = CConfig::get_instance(); + csensor_config &config = csensor_config::get_instance(); if (!config.get(SENSOR_TYPE_PRESSURE, m_model_id, ELEMENT_VENDOR, m_vendor)) { diff --git a/src/proxi/proxi_sensor.cpp b/src/proxi/proxi_sensor.cpp index c55bc74..37dfce6 100755 --- a/src/proxi/proxi_sensor.cpp +++ b/src/proxi/proxi_sensor.cpp @@ -19,11 +19,9 @@ #include #include - #include #include - #define SENSOR_NAME "PROXI_SENSOR" proxi_sensor::proxi_sensor() diff --git a/src/proxi/proxi_sensor.h b/src/proxi/proxi_sensor.h index b61da04..f058724 100755 --- a/src/proxi/proxi_sensor.h +++ b/src/proxi/proxi_sensor.h @@ -21,7 +21,6 @@ #define _PROXI_SENSOR_H_ #include - #include #include diff --git a/src/proxi/proxi_sensor_hal.cpp b/src/proxi/proxi_sensor_hal.cpp index 1701100..328211e 100755 --- a/src/proxi/proxi_sensor_hal.cpp +++ b/src/proxi/proxi_sensor_hal.cpp @@ -19,16 +19,16 @@ #include #include #include - #include -#include - +#include #include #include #include -#include #include +using std::ifstream; +using config::csensor_config; + #define NO_FLAG 0 #define PROXIMITY_TYPE 8 @@ -42,8 +42,6 @@ #define PROXI_CODE 0x0019 -using std::ifstream; -using config::CConfig; proxi_sensor_hal::proxi_sensor_hal() : m_state(PROXIMITY_STATE_FAR) @@ -51,7 +49,7 @@ proxi_sensor_hal::proxi_sensor_hal() , m_node_handle(-1) { const string sensorhub_interval_node_name = "prox_poll_delay"; - CConfig &config = CConfig::get_instance(); + csensor_config &config = csensor_config::get_instance(); node_path_info_query query; node_path_info info; diff --git a/src/shared/CMakeLists.txt b/src/shared/CMakeLists.txt index 8ef03d8..9bad5a5 100755 --- a/src/shared/CMakeLists.txt +++ b/src/shared/CMakeLists.txt @@ -38,7 +38,7 @@ include_directories(${CMAKE_SOURCE_DIR}/src/libsensord) add_library(sensord-server SHARED crw_lock.cpp worker_thread.cpp - cconfig.cpp + csensor_config.cpp csensor_event_queue.cpp csensor_event_dispatcher.cpp csensor_usage.cpp @@ -74,7 +74,7 @@ install(FILES ${PROJECT_NAME}.pc DESTINATION lib/pkgconfig) install(FILES crw_lock.h worker_thread.h - cconfig.h + csensor_config.h csensor_event_queue.h cinterval_info_list.h sensor_plugin_loader.h diff --git a/src/shared/cconfig.cpp b/src/shared/csensor_config.cpp similarity index 84% rename from src/shared/cconfig.cpp rename to src/shared/csensor_config.cpp index 34a7337..5c831b7 100755 --- a/src/shared/cconfig.cpp +++ b/src/shared/csensor_config.cpp @@ -1,7 +1,7 @@ /* * libsensord-share * - * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * Copyright (c) 2013 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. @@ -17,7 +17,7 @@ * */ -#include +#include #include "common.h" #include #include @@ -33,17 +33,17 @@ using std::ifstream; #define MODEL_ID_ATTR "id" #define DEFAULT_ATTR "value" -CConfig::CConfig() +csensor_config::csensor_config() { } -CConfig& CConfig::get_instance(void) +csensor_config& csensor_config::get_instance(void) { static bool load_done = false; - static CConfig inst; + static csensor_config inst; if (!load_done) { - inst.load_config(); + inst.load_config(SENSOR_CONFIG_FILE_PATH); inst.get_device_id(); if (!inst.m_device_id.empty()) INFO("Device ID = %s", inst.m_device_id.c_str()); @@ -55,12 +55,12 @@ CConfig& CConfig::get_instance(void) return inst; } -bool CConfig::load_config(const string& config_path) +bool csensor_config::load_config(const string& config_path) { xmlDocPtr doc; xmlNodePtr cur; - DBG("CConfig::load_config(\"%s\") is called!\n",config_path.c_str()); + DBG("csensor_config::load_config(\"%s\") is called!\n",config_path.c_str()); doc = xmlParseFile(config_path.c_str()); @@ -163,7 +163,7 @@ bool CConfig::load_config(const string& config_path) } -bool CConfig::get(const string& sensor_type,const string& model_id, const string& element, const string& attr, string& value) +bool csensor_config::get(const string& sensor_type,const string& model_id, const string& element, const string& attr, string& value) { auto it_model_list = m_sensor_config.find(sensor_type); @@ -198,7 +198,7 @@ bool CConfig::get(const string& sensor_type,const string& model_id, const string return true; } -bool CConfig::get(const string& sensor_type, const string& model_id, const string& element, const string& attr, double& value) +bool csensor_config::get(const string& sensor_type, const string& model_id, const string& element, const string& attr, double& value) { string str_value; @@ -213,7 +213,7 @@ bool CConfig::get(const string& sensor_type, const string& model_id, const strin return true; } -bool CConfig::get(const string& sensor_type, const string& model_id, const string& element, const string& attr, long& value) +bool csensor_config::get(const string& sensor_type, const string& model_id, const string& element, const string& attr, long& value) { string str_value; @@ -228,7 +228,7 @@ bool CConfig::get(const string& sensor_type, const string& model_id, const strin return true; } -bool CConfig::get(const string& sensor_type, const string& model_id, const string& element, string& value) +bool csensor_config::get(const string& sensor_type, const string& model_id, const string& element, string& value) { if (get(sensor_type, model_id, element, m_device_id, value)) return true; @@ -239,7 +239,7 @@ bool CConfig::get(const string& sensor_type, const string& model_id, const strin return false; } -bool CConfig::get(const string& sensor_type, const string& model_id, const string& element, double& value) +bool csensor_config::get(const string& sensor_type, const string& model_id, const string& element, double& value) { if (get(sensor_type, model_id, element, m_device_id, value)) return true; @@ -250,7 +250,7 @@ bool CConfig::get(const string& sensor_type, const string& model_id, const strin return false; } -bool CConfig::get(const string& sensor_type, const string& model_id, const string& element, long& value) +bool csensor_config::get(const string& sensor_type, const string& model_id, const string& element, long& value) { if (get(sensor_type, model_id, element, m_device_id, value)) return true; @@ -261,7 +261,7 @@ bool CConfig::get(const string& sensor_type, const string& model_id, const strin return false; } -bool CConfig::is_supported(const string& sensor_type,const string& model_id) +bool csensor_config::is_supported(const string& sensor_type,const string& model_id) { auto it_model_list = m_sensor_config.find(sensor_type); @@ -276,7 +276,7 @@ bool CConfig::is_supported(const string& sensor_type,const string& model_id) return true; } -bool CConfig::get_device_id(void) +bool csensor_config::get_device_id(void) { const string INFO_INI_PATH = "/etc/info.ini"; const string START_DELIMETER = "Model="; diff --git a/src/shared/cconfig.h b/src/shared/csensor_config.h similarity index 85% rename from src/shared/cconfig.h rename to src/shared/csensor_config.h index 0017e27..03e72ea 100755 --- a/src/shared/cconfig.h +++ b/src/shared/csensor_config.h @@ -1,7 +1,7 @@ /* * libsensord-share * - * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * Copyright (c) 2013 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. @@ -17,8 +17,8 @@ * */ -#if !defined(_CCONFIG_CLASS_H_) -#define _CCONFIG_CLASS_H_ +#if !defined(_CSENSOR_CONFIG_CLASS_H_) +#define _CSENSOR_CONFIG_CLASS_H_ #include #include @@ -28,7 +28,7 @@ using std::unordered_map; using std::string; using std::istringstream; -#define CONFIG_FILE_PATH "/usr/etc/sensors.xml" +#define SENSOR_CONFIG_FILE_PATH "/usr/etc/sensors.xml" typedef unordered_map Element; /* @@ -76,17 +76,17 @@ typedef unordered_map Sensor_config; namespace config { - class CConfig + class csensor_config { private: - CConfig(); - CConfig(CConfig const&) {}; - CConfig& operator=(CConfig const&); - bool load_config(const string& config_path = CONFIG_FILE_PATH); + csensor_config(); + csensor_config(csensor_config const&) {}; + csensor_config& operator=(csensor_config const&); + bool load_config(const string& config_path); Sensor_config m_sensor_config; string m_device_id; public: - static CConfig& get_instance(void); + static csensor_config& get_instance(void); bool get(const string& sensor_type, const string& model_id, const string& element, const string& attr, string& value); bool get(const string& sensor_type, const string& model_id, const string& element, const string& attr, double& value); diff --git a/src/shared/sensor_hal.cpp b/src/shared/sensor_hal.cpp index e036203..55c20f7 100755 --- a/src/shared/sensor_hal.cpp +++ b/src/shared/sensor_hal.cpp @@ -21,11 +21,11 @@ #include #include #include -#include +#include using std::ifstream; using std::fstream; -using config::CConfig; +using config::csensor_config; cmutex sensor_hal::m_shared_mutex; @@ -310,7 +310,7 @@ bool sensor_hal::find_model_id(int method, const string &sensor_type, string &mo infile >> name; - if (CConfig::get_instance().is_supported(sensor_type, name)) { + if (csensor_config::get_instance().is_supported(sensor_type, name)) { model_id = name; find = true; break; diff --git a/src/temperature/temperature_sensor_hal.cpp b/src/temperature/temperature_sensor_hal.cpp index 2ba0b57..9d81381 100755 --- a/src/temperature/temperature_sensor_hal.cpp +++ b/src/temperature/temperature_sensor_hal.cpp @@ -19,17 +19,14 @@ #include #include #include - #include -#include - +#include #include #include -#include #include using std::ifstream; -using config::CConfig; +using config::csensor_config; #define SENSOR_TYPE_TEMPERATURE "TEMPERATURE" #define ELEMENT_NAME "NAME" @@ -56,7 +53,7 @@ temperature_sensor_hal::temperature_sensor_hal() throw ENXIO; } - CConfig &config = CConfig::get_instance(); + csensor_config &config = csensor_config::get_instance(); if (!config.get(SENSOR_TYPE_TEMPERATURE, m_model_id, ELEMENT_VENDOR, m_vendor)) { -- 2.7.4 From c5d0338570c682b4f7d38882dd9a4232d5917422 Mon Sep 17 00:00:00 2001 From: Ramasamy Date: Thu, 20 Nov 2014 11:54:27 +0530 Subject: [PATCH 15/16] Adding virtual sensor XML and parser files Adding new virtual sensor XML file and XML parser files that are needed for configuration of the virtual sensors. Change-Id: I66673a52e23c67e1234a0abce1f10b959cb5610f --- src/shared/CMakeLists.txt | 2 + src/shared/cvirtual_sensor_config.cpp | 319 ++++++++++++++++++++++++++++++++++ src/shared/cvirtual_sensor_config.h | 82 +++++++++ virtual_sensors.xml.in | 41 +++++ 4 files changed, 444 insertions(+) create mode 100755 src/shared/cvirtual_sensor_config.cpp create mode 100755 src/shared/cvirtual_sensor_config.h create mode 100644 virtual_sensors.xml.in diff --git a/src/shared/CMakeLists.txt b/src/shared/CMakeLists.txt index 9bad5a5..4a305da 100755 --- a/src/shared/CMakeLists.txt +++ b/src/shared/CMakeLists.txt @@ -39,6 +39,7 @@ add_library(sensord-server SHARED crw_lock.cpp worker_thread.cpp csensor_config.cpp + cvirtual_sensor_config.cpp csensor_event_queue.cpp csensor_event_dispatcher.cpp csensor_usage.cpp @@ -75,6 +76,7 @@ install(FILES crw_lock.h worker_thread.h csensor_config.h + cvirtual_sensor_config.h csensor_event_queue.h cinterval_info_list.h sensor_plugin_loader.h diff --git a/src/shared/cvirtual_sensor_config.cpp b/src/shared/cvirtual_sensor_config.cpp new file mode 100755 index 0000000..1666430 --- /dev/null +++ b/src/shared/cvirtual_sensor_config.cpp @@ -0,0 +1,319 @@ +/* + * libsensord-share + * + * Copyright (c) 2013 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 "common.h" +#include +#include +#include +#include +#include +#include + +using std::ifstream; +using std::string; +using std::stringstream; + +#define ROOT_ELEMENT "VIRTUAL_SENSOR" +#define TEXT_ELEMENT "text" +#define MODEL_ID_ATTR "id" +#define DEFAULT_ATTR "value" +#define DEFAULT_ATTR1 "value1" +#define DEFAULT_ATTR2 "value2" +#define DEFAULT_ATTR3 "value3" + +cvirtual_sensor_config::cvirtual_sensor_config() +{ +} + +cvirtual_sensor_config& cvirtual_sensor_config::get_instance(void) +{ + static bool load_done = false; + static cvirtual_sensor_config inst; + + if (!load_done) { + inst.load_config(VIRTUAL_SENSOR_CONFIG_FILE_PATH); + inst.get_device_id(); + if (!inst.m_device_id.empty()) + INFO("Device ID = %s", inst.m_device_id.c_str()); + else + ERR("Failed to get Device ID"); + load_done = true; + } + + return inst; +} + +bool cvirtual_sensor_config::load_config(const string& config_path) +{ + xmlDocPtr doc; + xmlNodePtr cur; + + DBG("cvirtual_sensor_config::load_config(\"%s\") is called!\n",config_path.c_str()); + + doc = xmlParseFile(config_path.c_str()); + + if (doc == NULL) { + ERR("There is no %s\n",config_path.c_str()); + return false; + } + + cur = xmlDocGetRootElement(doc); + if(cur == NULL) { + ERR("There is no root element in %s\n",config_path.c_str()); + xmlFreeDoc(doc); + return false; + } + + if(xmlStrcmp(cur->name, (const xmlChar *)ROOT_ELEMENT)) { + ERR("Wrong type document: there is no [%s] root element in %s\n",ROOT_ELEMENT,config_path.c_str()); + xmlFreeDoc(doc); + return false; + } + + xmlNodePtr virtual_sensor_node_ptr; + xmlNodePtr element_node_ptr; + xmlAttrPtr attr_ptr; + char* prop = NULL; + + virtual_sensor_node_ptr = cur->xmlChildrenNode; + + while (virtual_sensor_node_ptr != NULL) { + //skip garbage element, [text] + if (!xmlStrcmp(virtual_sensor_node_ptr->name,(const xmlChar *)TEXT_ELEMENT)) { + virtual_sensor_node_ptr = virtual_sensor_node_ptr->next; + continue; + } + + //insert Model_list to config map + m_virtual_sensor_config[(const char*)virtual_sensor_node_ptr->name]; + DBG("<%s>\n",(const char*)virtual_sensor_node_ptr->name); + + element_node_ptr = virtual_sensor_node_ptr->xmlChildrenNode; + while (element_node_ptr != NULL) { + //skip garbage element, [text] + if (!xmlStrcmp(element_node_ptr->name,(const xmlChar *)TEXT_ELEMENT)) { + element_node_ptr = element_node_ptr->next; + continue; + } + + //insert Element to Model + m_virtual_sensor_config[(const char*)virtual_sensor_node_ptr->name][(const char*)element_node_ptr->name]; + DBG("<%s><%s>\n",(const char*)virtual_sensor_node_ptr->name,(const char*)element_node_ptr->name); + + attr_ptr = element_node_ptr->properties; + while (attr_ptr != NULL) { + + string key,value; + key = (char*)attr_ptr->name; + prop = (char*)xmlGetProp(element_node_ptr,attr_ptr->name); + value = prop; + free(prop); + + //insert attribute to Element + m_virtual_sensor_config[(const char*)virtual_sensor_node_ptr->name][(const char*)element_node_ptr->name][key]=value; + DBG("<%s><%s \"%s\"=\"%s\">\n",(const char*)virtual_sensor_node_ptr->name,(const char*)element_node_ptr->name,key.c_str(),value.c_str()); + attr_ptr = attr_ptr->next; + } + + + element_node_ptr = element_node_ptr->next; + } + + DBG("\n"); + virtual_sensor_node_ptr = virtual_sensor_node_ptr->next; + } + + xmlFreeDoc(doc); + return true; +} + +bool cvirtual_sensor_config::get(const string& sensor_type, const string& element, const string& attr, string& value) +{ + auto it_virtual_sensor_list = m_virtual_sensor_config.find(sensor_type); + + if (it_virtual_sensor_list == m_virtual_sensor_config.end()) { + ERR("There is no <%s> element\n",sensor_type.c_str()); + return false; + } + + auto it_element = it_virtual_sensor_list->second.find(element); + + if (it_element == it_virtual_sensor_list->second.end()) { + ERR("There is no <%s><%s> element\n",sensor_type.c_str(),element.c_str()); + return false; + } + + auto it_attr = it_element->second.find(attr); + + if (it_attr == it_element->second.end()) { + DBG("There is no <%s><%s \"%s\">\n",sensor_type.c_str(),element.c_str(),attr.c_str()); + return false; + } + + value = it_attr->second; + + return true; +} + +bool cvirtual_sensor_config::get(const string& sensor_type, const string& element, const string& attr, float *value) +{ + string str_value; + + if (get(sensor_type,element,attr,str_value) == false) + return false; + + stringstream str_stream(str_value); + + str_stream >> *value; + + return true; +} + +bool cvirtual_sensor_config::get(const string& sensor_type, const string& element, const string& attr, int *value) +{ + string str_value; + + if (get(sensor_type,element,attr,str_value) == false) + return false; + + stringstream str_stream(str_value); + + str_stream >> *value; + + return true; +} + +bool cvirtual_sensor_config::get(const string& sensor_type, const string& element, string& value) +{ + if (get(sensor_type, element, DEFAULT_ATTR, value)) + return true; + + return false; +} + +bool cvirtual_sensor_config::get(const string& sensor_type, const string& element, float *value, int count) +{ + if (count == 1) + { + if (get(sensor_type, element, DEFAULT_ATTR, value)) + return true; + } + else if (count == 3) + { + if (!get(sensor_type, element, DEFAULT_ATTR1, value)) + return false; + + value++; + + if (!get(sensor_type, element, DEFAULT_ATTR2, value)) + return false; + + value++; + + if (!get(sensor_type, element, DEFAULT_ATTR3, value)) + return false; + + return true; + } + else + { + DBG("Count value not supported.\n"); + } + + return false; +} + +bool cvirtual_sensor_config::get(const string& sensor_type, const string& element, int *value, int count) +{ + if (count == 1) + { + if (get(sensor_type, element, DEFAULT_ATTR, value)) + return true; + } + else if (count == 3) + { + if (!get(sensor_type, element, DEFAULT_ATTR1, value)) + return false; + + value++; + + if (!get(sensor_type, element, DEFAULT_ATTR2, value)) + return false; + + value++; + + if (!get(sensor_type, element, DEFAULT_ATTR3, value)) + return false; + + return true; + } + else + { + DBG("Count value not supported.\n"); + } + + return false; +} + +bool cvirtual_sensor_config::is_supported(const string& sensor_type) +{ + auto it_virtual_sensor_list = m_virtual_sensor_config.find(sensor_type); + + if (it_virtual_sensor_list == m_virtual_sensor_config.end()) + return false; + + return true; +} + +bool cvirtual_sensor_config::get_device_id(void) +{ + const string INFO_INI_PATH = "/etc/info.ini"; + const string START_DELIMETER = "Model="; + const string END_DELIMETER = ";"; + string line; + ifstream in_file; + std::size_t start_pos, end_pos; + bool ret = false; + + in_file.open(INFO_INI_PATH); + + if (!in_file.is_open()) + return false; + + while (!in_file.eof()) { + getline(in_file, line); + start_pos = line.find(START_DELIMETER); + + if (start_pos != std::string::npos) { + start_pos = start_pos + START_DELIMETER.size(); + end_pos = line.find(END_DELIMETER, start_pos); + + if (end_pos != std::string::npos) { + m_device_id = line.substr(start_pos, end_pos - start_pos); + ret = true; + break; + } + } + } + + in_file.close(); + + return ret; +} diff --git a/src/shared/cvirtual_sensor_config.h b/src/shared/cvirtual_sensor_config.h new file mode 100755 index 0000000..35e93ad --- /dev/null +++ b/src/shared/cvirtual_sensor_config.h @@ -0,0 +1,82 @@ +/* + * libsensord-share + * + * Copyright (c) 2013 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. + * + */ + +#if !defined(_CVIRTUAL_SENSOR_CONFIG_CLASS_H_) +#define _CVIRTUAL_SENSOR_CONFIG_CLASS_H_ + +#include +#include +#include + +using std::unordered_map; +using std::string; +using std::istringstream; + +#define VIRTUAL_SENSOR_CONFIG_FILE_PATH "/usr/etc/virtual_sensors.xml" + +typedef unordered_map Element; +/* +* an Element is a group of attributes +* +* +*/ + +typedef unordered_map Virtual_sensor; +/* +* a Virtual_sensor is a group of elements to consist of one virtual sensor's configuration +* +* +* +* ... +*/ + +typedef unordered_map Virtual_sensor_config; +/* +* a Virtual_sensor_config represents virtual_sensors.xml +* +* +* +* +*/ + +class cvirtual_sensor_config +{ +private: + cvirtual_sensor_config(); + cvirtual_sensor_config(cvirtual_sensor_config const&) {}; + cvirtual_sensor_config& operator=(cvirtual_sensor_config const&); + bool load_config(const string& config_path); + Virtual_sensor_config m_virtual_sensor_config; + string m_device_id; +public: + static cvirtual_sensor_config& get_instance(void); + + bool get(const string& sensor_type, const string& element, const string& attr, string& value); + bool get(const string& sensor_type, const string& element, const string& attr, float *value); + bool get(const string& sensor_type, const string& element, const string& attr, int *value); + + bool get(const string& sensor_type, const string& element, string& value); + bool get(const string& sensor_type, const string& element, float *value, int count =1); + bool get(const string& sensor_type, const string& element, int *value, int count = 1); + + bool is_supported(const string &sensor_type); + bool get_device_id(void); +}; + +#endif diff --git a/virtual_sensors.xml.in b/virtual_sensors.xml.in new file mode 100644 index 0000000..42c9d72 --- /dev/null +++ b/virtual_sensors.xml.in @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- 2.7.4 From 4e843927e279ed03c43a8e4f17baf4a793439b62 Mon Sep 17 00:00:00 2001 From: Amit Dharmapurikar Date: Fri, 21 Nov 2014 18:08:58 +0530 Subject: [PATCH 16/16] Synchronizing pressure sensor plugin with addition of IIO interface support Change-Id: I298a4e11dd2944724988d0fce061f4fee7a3e459 Signed-off-by: Amit Dharmapurikar --- packaging/sensord.spec | 2 +- src/pressure/CMakeLists.txt | 2 +- src/pressure/pressure_sensor.cpp | 50 +++---- src/pressure/pressure_sensor.h | 8 +- src/pressure/pressure_sensor_hal.cpp | 248 ++++++++++++++--------------------- src/pressure/pressure_sensor_hal.h | 35 ++--- 6 files changed, 133 insertions(+), 212 deletions(-) diff --git a/packaging/sensord.spec b/packaging/sensord.spec index eb9d4c2..c9e381a 100755 --- a/packaging/sensord.spec +++ b/packaging/sensord.spec @@ -13,7 +13,7 @@ Source2: sensord.socket %define proxi_state ON %define light_state OFF %define geo_state ON -%define pressure_state OFF +%define pressure_state ON %define temperature_state OFF %define orientation_state OFF %define gravity_state OFF diff --git a/src/pressure/CMakeLists.txt b/src/pressure/CMakeLists.txt index b1984d0..ea13698 100755 --- a/src/pressure/CMakeLists.txt +++ b/src/pressure/CMakeLists.txt @@ -15,7 +15,7 @@ include_directories(${CMAKE_SOURCE_DIR}/src/libsensord) include(FindPkgConfig) pkg_check_modules(rpkgs REQUIRED vconf) -add_definitions(${rpkgs_CFLAGS} -DUSE_ONLY_ONE_MODULE) +add_definitions(${rpkgs_CFLAGS} -DUSE_ONLY_ONE_MODULE -DUSE_LCD_TYPE_CHECK) set(PROJECT_MAJOR_VERSION "0") set(PROJECT_MINOR_VERSION "0") diff --git a/src/pressure/pressure_sensor.cpp b/src/pressure/pressure_sensor.cpp index 35461a7..710c86a 100755 --- a/src/pressure/pressure_sensor.cpp +++ b/src/pressure/pressure_sensor.cpp @@ -29,17 +29,15 @@ using config::csensor_config; using std::bind1st; using std::mem_fun; -#define SENSOR_NAME "PRESSURE_SENSOR" -#define SENSOR_TYPE_PRESSURE "PRESSURE" +#define SENSOR_NAME "PRESSURE_SENSOR" +#define SENSOR_TYPE_PRESSURE "PRESSURE" #define ELEMENT_NAME "NAME" #define ELEMENT_VENDOR "VENDOR" #define ELEMENT_TEMPERATURE_RESOLUTION "TEMPERATURE_RESOLUTION" #define ELEMENT_TEMPERATURE_OFFSET "TEMPERATURE_OFFSET" #define ATTR_VALUE "value" -#define SEA_LEVEL_RESOLUTION 0.01 -#define ALT_CONST1 44330.0 -#define ALT_CONST2 (1.0f/5.255f) +#define SEA_LEVEL_RESOLUTION 0.01 pressure_sensor::pressure_sensor() : m_sensor_hal(NULL) @@ -61,21 +59,19 @@ bool pressure_sensor::init() { m_sensor_hal = sensor_plugin_loader::get_instance().get_sensor_hal(PRESSURE_SENSOR); - if (!m_sensor_hal) - { + if (!m_sensor_hal) { ERR("cannot load sensor_hal[%s]", sensor_base::get_name()); return false; } sensor_properties_t properties; - if (m_sensor_hal->get_properties(properties) == false) - { + if (!m_sensor_hal->get_properties(properties)) { ERR("sensor->get_properties() is failed!\n"); return false; } - m_resolution = properties.sensor_resolution; + m_resolution = properties.resolution; string model_id = m_sensor_hal->get_model_id(); @@ -83,8 +79,7 @@ bool pressure_sensor::init() double temperature_resolution; - if (!config.get(SENSOR_TYPE_PRESSURE, model_id, ELEMENT_TEMPERATURE_RESOLUTION, temperature_resolution)) - { + if (!config.get(SENSOR_TYPE_PRESSURE, model_id, ELEMENT_TEMPERATURE_RESOLUTION, temperature_resolution)) { ERR("[TEMPERATURE_RESOLUTION] is empty\n"); throw ENXIO; } @@ -94,8 +89,7 @@ bool pressure_sensor::init() double temperature_offset; - if (!config.get(SENSOR_TYPE_PRESSURE, model_id, ELEMENT_TEMPERATURE_OFFSET, temperature_offset)) - { + if (!config.get(SENSOR_TYPE_PRESSURE, model_id, ELEMENT_TEMPERATURE_OFFSET, temperature_offset)) { ERR("[TEMPERATURE_OFFSET] is empty\n"); throw ENXIO; } @@ -122,7 +116,6 @@ bool pressure_sensor::working(void *inst) bool pressure_sensor::process_event(void) { sensor_event_t event; - int pressure; if (!m_sensor_hal->is_data_ready(true)) return true; @@ -131,8 +124,8 @@ bool pressure_sensor::process_event(void) AUTOLOCK(m_client_info_mutex); - if (get_client_cnt(PRESSURE_EVENT_RAW_DATA_REPORT_ON_TIME)) - { + if (get_client_cnt(PRESSURE_EVENT_RAW_DATA_REPORT_ON_TIME)) { + event.sensor_id = get_id(); event.event_type = PRESSURE_EVENT_RAW_DATA_REPORT_ON_TIME; raw_to_base(event.data); push(event); @@ -143,8 +136,7 @@ bool pressure_sensor::process_event(void) bool pressure_sensor::on_start(void) { - if (!m_sensor_hal->enable()) - { + if (!m_sensor_hal->enable()) { ERR("m_sensor_hal start fail\n"); return false; } @@ -154,8 +146,7 @@ bool pressure_sensor::on_start(void) bool pressure_sensor::on_stop(void) { - if (!m_sensor_hal->disable()) - { + if (!m_sensor_hal->disable()) { ERR("m_sensor_hal stop fail\n"); return false; } @@ -163,12 +154,12 @@ bool pressure_sensor::on_stop(void) return stop_poll(); } -bool pressure_sensor::get_properties(const unsigned int type, sensor_properties_t &properties) +bool pressure_sensor::get_properties(sensor_properties_t &properties) { return m_sensor_hal->get_properties(properties); } -int pressure_sensor::get_sensor_data(const unsigned int type, sensor_data_t &data) +int pressure_sensor::get_sensor_data(unsigned int type, sensor_data_t &data) { int ret; @@ -177,8 +168,7 @@ int pressure_sensor::get_sensor_data(const unsigned int type, sensor_data_t &dat if (ret < 0) return -1; - if (type == PRESSURE_BASE_DATA_SET) - { + if (type == PRESSURE_BASE_DATA_SET) { raw_to_base(data); return 0; } @@ -197,25 +187,23 @@ bool pressure_sensor::set_interval(unsigned long interval) float pressure_sensor::pressure_to_altitude(float pressure) { - return ALT_CONST1 * (1.0f - pow(pressure/m_sea_level_pressure, ALT_CONST2)); + return 44330.0f * (1.0f - pow(pressure/m_sea_level_pressure, 1.0f/5.255f)); } void pressure_sensor::raw_to_base(sensor_data_t &data) { m_sea_level_pressure = data.values[1] * SEA_LEVEL_RESOLUTION; data.values[1] = pressure_to_altitude(data.values[0]); + data.value_count = 3; } extern "C" void *create(void) { pressure_sensor *inst; - try - { + try { inst = new pressure_sensor(); - } - catch (int err) - { + } catch (int err) { ERR("pressure_sensor class create fail , errno : %d , errstr : %s\n", err, strerror(err)); return NULL; } diff --git a/src/pressure/pressure_sensor.h b/src/pressure/pressure_sensor.h index 2124f6c..1da5cab 100755 --- a/src/pressure/pressure_sensor.h +++ b/src/pressure/pressure_sensor.h @@ -25,8 +25,7 @@ #include #include -class pressure_sensor : public physical_sensor -{ +class pressure_sensor : public physical_sensor { public: pressure_sensor(); virtual ~pressure_sensor(); @@ -37,8 +36,8 @@ public: static bool working(void *inst); bool set_interval(unsigned long interval); - bool get_properties(const unsigned int type, sensor_properties_t &properties); - int get_sensor_data(const unsigned int type, sensor_data_t &data); + virtual bool get_properties(sensor_properties_t &properties); + int get_sensor_data(unsigned int type, sensor_data_t &data); private: sensor_hal *m_sensor_hal; @@ -53,6 +52,7 @@ private: bool process_event(void); float pressure_to_altitude(float pressure); void raw_to_base(sensor_data_t &data); + }; #endif diff --git a/src/pressure/pressure_sensor_hal.cpp b/src/pressure/pressure_sensor_hal.cpp index 764adc9..2823baa 100755 --- a/src/pressure/pressure_sensor_hal.cpp +++ b/src/pressure/pressure_sensor_hal.cpp @@ -21,55 +21,87 @@ #include #include #include -#include #include +#include #include +#include #include using std::ifstream; +using std::string; using config::csensor_config; #define SENSOR_TYPE_PRESSURE "PRESSURE" #define ELEMENT_NAME "NAME" #define ELEMENT_VENDOR "VENDOR" #define ELEMENT_RAW_DATA_UNIT "RAW_DATA_UNIT" +#define ELEMENT_RESOLUTION "RESOLUTION" #define ELEMENT_MIN_RANGE "MIN_RANGE" #define ELEMENT_MAX_RANGE "MAX_RANGE" +#define ELEMENT_TEMPERATURE_RESOLUTION "TEMPERATURE_RESOLUTION" +#define ELEMENT_TEMPERATURE_OFFSET "TEMPERATURE_OFFSET" +#define ATTR_VALUE "value" + +#define SEA_LEVEL_PRESSURE 101325.0 -#define ENABLE_VAL true -#define DISABLE_VAL false -#define SEA_LEVEL_PRESSURE 101325.0 -#define NO_FLAG 0 -#define TIMEOUT 1 +#define EVENT_EN_NODE "events/in_pressure_mag_either_en" +#define PRESSURE_SCALE "/in_pressure_scale" +#define PRESSURE_RAW "/in_pressure_raw" +#define TEMP_OFFSET "/in_temp_offset" +#define TEMP_SCALE "/in_temp_scale" +#define TEMP_RAW "/in_temp_raw" +#define NO_FLAG 0 +#define TIMEOUT 1 pressure_sensor_hal::pressure_sensor_hal() : m_pressure(0) , m_temperature(0) , m_polling_interval(POLL_1HZ_MS) , m_fired_time(0) -, m_sensorhub_supported(false) +, m_node_handle(-1) { - int fd, ret; - string file_name; + const string sensorhub_interval_node_name = "pressure_poll_delay"; + csensor_config &config = csensor_config::get_instance(); - if (!check_hw_node()) - { - ERR("check_hw_node() fail"); + node_path_info_query query; + node_path_info info; + int input_method = IIO_METHOD; + + if (!get_model_properties(SENSOR_TYPE_PRESSURE, m_model_id, input_method)) { + ERR("Failed to find model_properties"); throw ENXIO; + } - csensor_config &config = csensor_config::get_instance(); + query.input_method = input_method; + query.sensorhub_controlled = m_sensorhub_controlled = is_sensorhub_controlled(sensorhub_interval_node_name); + query.sensor_type = SENSOR_TYPE_PRESSURE; + query.input_event_key = "pressure_sensor"; + query.iio_enable_node_name = EVENT_EN_NODE; + query.sensorhub_interval_node_name = sensorhub_interval_node_name; + + if (!get_node_path_info(query, info)) { + ERR("Failed to get node info"); + throw ENXIO; + } + m_data_node = info.data_node_path; + m_pressure_dir = info.base_dir; + m_enable_node = info.enable_node_path; + m_pressure_node = m_pressure_dir + string(PRESSURE_RAW); + m_temp_node = m_pressure_dir + string(TEMP_RAW); + + INFO("m_data_node:%s",m_data_node.c_str()); + INFO("m_pressure_dir:%s",m_pressure_dir.c_str()); + INFO("m_enable_node:%s",m_enable_node.c_str()); - if (!config.get(SENSOR_TYPE_PRESSURE, m_model_id, ELEMENT_VENDOR, m_vendor)) - { + if (!config.get(SENSOR_TYPE_PRESSURE, m_model_id, ELEMENT_VENDOR, m_vendor)) { ERR("[VENDOR] is empty\n"); throw ENXIO; } INFO("m_vendor = %s", m_vendor.c_str()); - if (!config.get(SENSOR_TYPE_PRESSURE, m_model_id, ELEMENT_NAME, m_chip_name)) - { + if (!config.get(SENSOR_TYPE_PRESSURE, m_model_id, ELEMENT_NAME, m_chip_name)) { ERR("[NAME] is empty\n"); throw ENXIO; } @@ -78,8 +110,7 @@ pressure_sensor_hal::pressure_sensor_hal() double min_range; - if (!config.get(SENSOR_TYPE_PRESSURE, m_model_id, ELEMENT_MIN_RANGE, min_range)) - { + if (!config.get(SENSOR_TYPE_PRESSURE, m_model_id, ELEMENT_MIN_RANGE, min_range)) { ERR("[MIN_RANGE] is empty\n"); throw ENXIO; } @@ -89,8 +120,7 @@ pressure_sensor_hal::pressure_sensor_hal() double max_range; - if (!config.get(SENSOR_TYPE_PRESSURE, m_model_id, ELEMENT_MAX_RANGE, max_range)) - { + if (!config.get(SENSOR_TYPE_PRESSURE, m_model_id, ELEMENT_MAX_RANGE, max_range)) { ERR("[MAX_RANGE] is empty\n"); throw ENXIO; } @@ -100,8 +130,7 @@ pressure_sensor_hal::pressure_sensor_hal() double raw_data_unit; - if (!config.get(SENSOR_TYPE_PRESSURE, m_model_id, ELEMENT_RAW_DATA_UNIT, raw_data_unit)) - { + if (!config.get(SENSOR_TYPE_PRESSURE, m_model_id, ELEMENT_RAW_DATA_UNIT, raw_data_unit)) { ERR("[RAW_DATA_UNIT] is empty\n"); throw ENXIO; } @@ -109,15 +138,17 @@ pressure_sensor_hal::pressure_sensor_hal() m_raw_data_unit = (float)(raw_data_unit); INFO("m_raw_data_unit = %f\n", m_raw_data_unit); - file_name = string(IIO_DIR) + m_pressure_dir + string(TEMP_SCALE); + string file_name; + + file_name = m_pressure_dir + string(TEMP_SCALE); if (!read_node_value(file_name, m_temp_scale)) throw ENXIO; - file_name = string(IIO_DIR) + m_pressure_dir + string(TEMP_OFFSET); + file_name = m_pressure_dir + string(TEMP_OFFSET); if (!read_node_value(file_name, m_temp_offset)) throw ENXIO; - file_name = string(IIO_DIR) + m_pressure_dir + string(PRESSURE_SCALE); + file_name = m_pressure_dir + string(PRESSURE_SCALE); if (!read_node_value(file_name, m_pressure_scale)) throw ENXIO; @@ -125,20 +156,19 @@ pressure_sensor_hal::pressure_sensor_hal() INFO("Temperature offset:%f", m_temp_offset); INFO("Pressure scale:%d", m_pressure_scale); - fd = open(m_event_resource.c_str(), NO_FLAG); - if (fd == -1) - { + int fd, ret; + fd = open(m_data_node.c_str(), NO_FLAG); + if (fd == -1) { ERR("Could not open event resource"); throw ENXIO; } - ret = ioctl(fd, IOCTL_IIO_EVENT_FD, &m_event_fd); + ret = ioctl(fd, IOCTL_IIO_EVENT_FD, &m_node_handle); close(fd); - if ((ret == -1) || (m_event_fd == -1)) - { - ERR("Failed to retrieve event fd"); + if ((ret == -1) || (m_node_handle == -1)) { + ERR("Failed to retrieve node handle from event node: %s", m_data_node.c_str()); throw ENXIO; } @@ -147,7 +177,9 @@ pressure_sensor_hal::pressure_sensor_hal() pressure_sensor_hal::~pressure_sensor_hal() { - close(m_event_fd); + close(m_node_handle); + m_node_handle = -1; + INFO("pressure_sensor_hal is destroyed!\n"); } @@ -161,17 +193,11 @@ sensor_type_t pressure_sensor_hal::get_type(void) return PRESSURE_SENSOR; } -bool pressure_sensor_hal::enable_resource(bool enable) -{ - update_sysfs_num(m_enable_resource.c_str(), enable); - return true; -} - bool pressure_sensor_hal::enable(void) { AUTOLOCK(m_mutex); - - enable_resource(ENABLE_VAL); + update_sysfs_num(m_enable_node.c_str(), true); + set_interval(m_polling_interval); m_fired_time = 0; INFO("Pressure sensor real starting"); @@ -182,7 +208,7 @@ bool pressure_sensor_hal::disable(void) { AUTOLOCK(m_mutex); - enable_resource(DISABLE_VAL); + update_sysfs_num(m_enable_node.c_str(), false); INFO("Pressure sensor real stopping"); return true; @@ -190,6 +216,7 @@ bool pressure_sensor_hal::disable(void) bool pressure_sensor_hal::set_interval(unsigned long val) { + INFO("set_interval not supported"); return true; } @@ -204,47 +231,40 @@ bool pressure_sensor_hal::update_value(bool wait) FD_ZERO(&readfds); FD_ZERO(&exceptfds); - FD_SET(m_event_fd, &readfds); - FD_SET(m_event_fd, &exceptfds); + FD_SET(m_node_handle, &readfds); + FD_SET(m_node_handle, &exceptfds); - if (wait) - { + if (wait) { tv.tv_sec = TIMEOUT; tv.tv_usec = 0; } - else - { + else { tv.tv_sec = 0; tv.tv_usec = 0; } - ret = select(m_event_fd + 1, &readfds, NULL, &exceptfds, &tv); + ret = select(m_node_handle + 1, &readfds, NULL, &exceptfds, &tv); - if (ret == -1) - { - ERR("select error:%s m_event_fd:d", strerror(errno), m_event_fd); + if (ret == -1) { + ERR("select error:%s m_node_handle:d", strerror(errno), m_node_handle); return false; } - else if (!ret) - { + else if (!ret) { DBG("select timeout"); return false; } - if (FD_ISSET(m_event_fd, &exceptfds)) - { + if (FD_ISSET(m_node_handle, &exceptfds)) { ERR("select exception occurred!"); return false; } - if (FD_ISSET(m_event_fd, &readfds)) - { + if (FD_ISSET(m_node_handle, &readfds)) { INFO("pressure event detection!"); - int len = read(m_event_fd, &pressure_event, sizeof(pressure_event)); + int len = read(m_node_handle, &pressure_event, sizeof(pressure_event)); - if (len == -1) - { - DBG("Error in read(m_event_fd):%s.", strerror(errno)); + if (len == -1) { + ERR("Error in read(m_event_fd):%s.", strerror(errno)); return false; } m_fired_time = pressure_event.timestamp; @@ -255,12 +275,12 @@ bool pressure_sensor_hal::update_value(bool wait) m_pressure = ((float)raw_pressure_count)/((float)m_pressure_scale); m_temperature = m_temp_offset + ((float)raw_temp_count)/((float)m_temp_scale); } - else - { + else { ERR("No pressure event data available to read"); return false; } return true; + } bool pressure_sensor_hal::is_data_ready(bool wait) @@ -273,10 +293,9 @@ bool pressure_sensor_hal::is_data_ready(bool wait) int pressure_sensor_hal::get_sensor_data(sensor_data_t &data) { AUTOLOCK(m_value_mutex); - data.data_accuracy = SENSOR_ACCURACY_GOOD; - data.data_unit_idx = SENSOR_UNIT_HECTOPASCAL; + data.accuracy = SENSOR_ACCURACY_GOOD; data.timestamp = m_fired_time ; - data.values_num = 3; + data.value_count = 3; data.values[0] = m_pressure; data.values[1] = SEA_LEVEL_PRESSURE; data.values[2] = m_temperature; @@ -284,98 +303,27 @@ int pressure_sensor_hal::get_sensor_data(sensor_data_t &data) return 0; } + bool pressure_sensor_hal::get_properties(sensor_properties_t &properties) { - properties.sensor_unit_idx = SENSOR_UNIT_HECTOPASCAL; - properties.sensor_min_range = m_min_range; - properties.sensor_max_range = m_max_range; - snprintf(properties.sensor_name, sizeof(properties.sensor_name), "%s", m_chip_name.c_str()); - snprintf(properties.sensor_vendor, sizeof(properties.sensor_vendor), "%s", m_vendor.c_str()); - properties.sensor_resolution = m_raw_data_unit; + properties.name = m_chip_name; + properties.vendor = m_vendor; + properties.min_range = m_min_range; + properties.max_range = m_max_range; + properties.min_interval = 1; + properties.resolution = m_raw_data_unit; + properties.fifo_count = 0; + properties.max_batch_count = 0; return true; } -bool pressure_sensor_hal::is_sensorhub_supported(void) -{ - return false; -} - -bool pressure_sensor_hal::check_hw_node(void) -{ - string name_node; - string hw_name; - string file_name; - - DIR *main_dir = NULL; - struct dirent *dir_entry = NULL; - bool find_node = false; - - INFO("======================start check_hw_node=============================\n"); - - m_sensorhub_supported = is_sensorhub_supported(); - - main_dir = opendir(IIO_DIR); - - if (!main_dir) - { - ERR("Could not open IIO directory\n"); - return false; - } - - while (!find_node) - { - dir_entry = readdir(main_dir); - if(dir_entry == NULL) - break; - - if ((strncasecmp(dir_entry->d_name ,".",1 ) != 0) && (strncasecmp(dir_entry->d_name ,"..",2 ) != 0) && (dir_entry->d_ino != 0)) - { - file_name = string(IIO_DIR) + string(dir_entry->d_name) + string(NAME_NODE); - - ifstream infile(file_name.c_str()); - - if (!infile) - continue; - - infile >> hw_name; - - if (strncmp(dir_entry->d_name, IIO_DEV_BASE_NAME, IIO_DEV_STR_LEN) == 0) - { - if (CConfig::get_instance().is_supported(SENSOR_TYPE_PRESSURE, hw_name) == true) - { - m_name = m_model_id = hw_name; - m_pressure_dir = string(dir_entry->d_name); - m_enable_resource = string(IIO_DIR) + m_pressure_dir + string(EVENT_DIR) + string(EVENT_EN_NODE); - m_event_resource = string(DEV_DIR) + m_pressure_dir; - m_pressure_node = string(IIO_DIR) + m_pressure_dir + string(PRESSURE_RAW); - m_temp_node = string(IIO_DIR) + m_pressure_dir + string(TEMP_RAW); - - INFO("m_enable_resource = %s", m_enable_resource.c_str()); - INFO("m_model_id = %s", m_model_id.c_str()); - INFO("m_pressure_dir = %s", m_pressure_dir.c_str()); - INFO("m_event_resource = %s", m_event_resource.c_str()); - - find_node = true; - break; - } - } - } - } - - closedir(main_dir); - return find_node; -} - extern "C" void *create(void) { pressure_sensor_hal *inst; - try - { + try { inst = new pressure_sensor_hal(); - } - catch (int err) - { + } catch (int err) { ERR("pressure_sensor_hal class create fail , errno : %d , errstr : %s\n", err, strerror(err)); return NULL; } diff --git a/src/pressure/pressure_sensor_hal.h b/src/pressure/pressure_sensor_hal.h index 96fc362..42e5310 100755 --- a/src/pressure/pressure_sensor_hal.h +++ b/src/pressure/pressure_sensor_hal.h @@ -23,20 +23,6 @@ #include #include -#define IIO_DIR "/sys/bus/iio/devices/" -#define NAME_NODE "/name" -#define EVENT_DIR "/events" -#define EVENT_EN_NODE "/in_pressure_mag_either_en" -#define DEV_DIR "/dev/" -#define PRESSURE_SCALE "/in_pressure_scale" -#define PRESSURE_RAW "/in_pressure_raw" -#define TEMP_OFFSET "/in_temp_offset" -#define TEMP_SCALE "/in_temp_scale" -#define TEMP_RAW "/in_temp_raw" - -#define IIO_DEV_BASE_NAME "iio:device" -#define IIO_DEV_STR_LEN 10 - using std::string; class pressure_sensor_hal : public sensor_hal @@ -46,17 +32,15 @@ public: virtual ~pressure_sensor_hal(); string get_model_id(void); sensor_type_t get_type(void); - bool enable(void); bool disable(void); bool set_interval(unsigned long val); bool is_data_ready(bool wait); virtual int get_sensor_data(sensor_data_t &data); - bool get_properties(sensor_properties_t &properties); + virtual bool get_properties(sensor_properties_t &properties); private: string m_model_id; - string m_name; string m_vendor; string m_chip_name; @@ -67,6 +51,8 @@ private: float m_temp_offset; float m_temperature; + int m_resolution; + float m_min_range; float m_max_range; float m_raw_data_unit; @@ -74,21 +60,20 @@ private: unsigned long m_polling_interval; unsigned long long m_fired_time; - int m_event_fd; + int m_node_handle; + + string m_enable_node; + string m_data_node; + string m_interval_node; string m_pressure_dir; string m_pressure_node; string m_temp_node; - string m_event_resource; - string m_enable_resource; - cmutex m_value_mutex; + bool m_sensorhub_controlled; - bool m_sensorhub_supported; + cmutex m_value_mutex; - bool check_hw_node(void); bool update_value(bool wait); - bool enable_resource(bool enable); - bool is_sensorhub_supported(void); }; #endif /*_PRESSURE_SENSOR_HAL_CLASS_H_*/ -- 2.7.4