diff options
Diffstat (limited to 'src/ble/profile/server/lns.c')
-rw-r--r-- | src/ble/profile/server/lns.c | 1712 |
1 files changed, 1712 insertions, 0 deletions
diff --git a/src/ble/profile/server/lns.c b/src/ble/profile/server/lns.c new file mode 100644 index 0000000..e06b7cf --- /dev/null +++ b/src/ble/profile/server/lns.c @@ -0,0 +1,1712 @@ +/********************************************************************************************************* +* Copyright(c) 2017, Realtek Semiconductor Corporation. All rights reserved. +********************************************************************************************************** +* @file lns.c +* @brief Location and Navigation Service source file. +* @details Interfaces to access Location and Navigation Service. +* @author +* @date 2017-09-22 +* @version v0.1 +********************************************************************************************************* +*/ + +#include "stdint.h" +#include "gatt.h" +#include <string.h> +#include "trace.h" +#include "lns.h" + + +/******************************************************************************************************** +* local static variables defined here, only used in this source file. +********************************************************************************************************/ + +#define LNS_POSITION_QUALITY_CHAR_SUPPORT +#define LNS_LN_CONTROL_POINT_CHAR_SUPPORT +#define LNS_LN_NAVIGATION_CHAR_SUPPORT + +#define LNS_CP_PARA_NAME_OF_ROUTE_MAX_LEN 10 + +#define LNS_LN_FEATURE_VALUE_INDEX 2//read +#define LNS_LN_LOCATION_AND_SPEED_VALUE_INDEX 4//notify +#define LNS_LN_POSITION_QUALITY_VALUE_INDEX 7//read +#define LNS_LN_CP_VALUE_INDEX 9 //write, indicate +#define LNS_LN_NAVIGATION_VALUE_INDEX 12//notify + +#define LNS_LN_LOCATION_AND_SPEED_CCCD_INDEX 5 +#define LNS_LN_CP_CCCD_INDEX 10 +#define LNS_LN_NAVIGATION_CCCD_INDEX 13 + +#define GATT_UUID_SERVICE_LOCATION_AND_NAVIGATION 0x1819 +#define GATT_UUID_CHAR_LNS_LOCATION_AND_SPEED 0x2A67 +#define GATT_UUID_CHAR_LNS_NAVIGATION 0x2A68 +#define GATT_UUID_CHAR_LNS_POSITION_QUALITY 0x2A69 +#define GATT_UUID_CHAR_LNS_LN_FEATURE 0x2A6A +#define GATT_UUID_CHAR_LNS_LN_CP 0x2A6B + + + +#define LN_CP_RSPCODE_RESERVED 0x00 +#define LN_CP_RSPCODE_SUCCESS 0x01 +#define LN_CP_RSPCODE_OPCODE_UNSUPPORT 0x02 +#define LN_CP_RSPCODE_INVALID_PARAMETER 0x03 +#define LN_CP_RSPCODE_OPERATION_FAILED 0x04 + + +#define LN_CTL_PNT_OPERATE_ACTIVE(x) \ + ( (x == LN_CP_OPCODE_SET_CUMULATIVE_VALUE) || \ + (x == LN_CP_OPCODE_MASK_LOCATION_AND_SPEED_CHAR_CONTENT) || \ + (x == LN_CP_OPCODE_NAVIGATION_CONTROL) || \ + (x == LN_CP_OPCODE_REQUEST_NUMBER_OF_ROUTES) || \ + (x == LN_CP_OPCODE_REQUEST_NAME_OF_ROUTE) || \ + (x == LN_CP_OPCODE_SELECT_ROUTE) || \ + (x == LN_CP_OPCODE_SET_FIX_RATE) || \ + (x == LN_CP_OPCODE_SET_ELEVATION) || \ + (x == LN_CP_OPCODE_RESPONSE_CODE) ) + +#define LNS_POSITION_AND_QUALITY_VALUE_MAX_LEN 16 +#define LNS_LOCATION_AND_SPEED_VALUE_MAX_LEN 28 +#define LNS_NAVIGATION_VALUE_MAX_LEN 19 + +uint32_t lns_ln_feature_support = 0; + +T_LNS_NOTIFY_INDICATE_FLAG lns_notify_indicate_flag = {0}; +T_LNS_CONTROL_POINT lns_ctl_pnt = {0}; + +T_POSITION_QUALITY_VALUE lns_position_quality_value = {0}; +uint8_t lns_pq_value_for_read_cfm[LNS_POSITION_AND_QUALITY_VALUE_MAX_LEN] = {0}; +uint8_t lns_pq_value_length_for_read_cfm = 0; + +LOCATION_AND_SPEED_VALUE lns_location_and_speed_value = {0}; +T_NAVIGATION_VALUE lns_navigation_value = {0}; +uint16_t lns_cp_number_of_route = 0; +uint8_t lns_cp_name_of_route[LNS_CP_PARA_NAME_OF_ROUTE_MAX_LEN] = {0}; +uint8_t lns_cp_name_of_route_len = 0; + +/**< Function pointer used to send event to application from location and navigation profile. */ + +static P_FUN_SERVER_GENERAL_CB pfn_lns_app_cb = NULL; + + +/**< @brief profile/service definition. */ +static const T_ATTRIB_APPL lns_attr_tbl[] = +{ + /*----------------- Health Temperature Service -------------------*/ + /* <<Primary Service>>, .. 0,*/ + { + (ATTRIB_FLAG_VALUE_INCL | ATTRIB_FLAG_LE), /* wFlags */ + { /* bTypeValue */ + LO_WORD(GATT_UUID_PRIMARY_SERVICE), + HI_WORD(GATT_UUID_PRIMARY_SERVICE), + LO_WORD(GATT_UUID_SERVICE_LOCATION_AND_NAVIGATION), /* service UUID */ + HI_WORD(GATT_UUID_SERVICE_LOCATION_AND_NAVIGATION) + }, + UUID_16BIT_SIZE, /* bValueLen */ + NULL, /* pValueContext */ + GATT_PERM_READ /* wPermissions */ + }, + + /* <<Characteristic>>, .. 1, LN Feature*/ + { + ATTRIB_FLAG_VALUE_INCL, /* wFlags */ + { /* bTypeValue */ + LO_WORD(GATT_UUID_CHARACTERISTIC), + HI_WORD(GATT_UUID_CHARACTERISTIC), + GATT_CHAR_PROP_READ /* characteristic properties */ + /* characteristic UUID not needed here, is UUID of next attrib. */ + }, + 1, /* bValueLen */ + NULL, + GATT_PERM_READ /* wPermissions */ + }, + /* Temperature Measurement value 2,*/ + { + ATTRIB_FLAG_VALUE_APPL, /* wFlags */ + { /* bTypeValue */ + LO_WORD(GATT_UUID_CHAR_LNS_LN_FEATURE), + HI_WORD(GATT_UUID_CHAR_LNS_LN_FEATURE) + }, + 0, /* bValueLen */ + NULL, + GATT_PERM_READ /* wPermissions */ + }, + + /* <<Characteristic>>, ..Location and Speed */ + { + ATTRIB_FLAG_VALUE_INCL, /* wFlags */ + { /* bTypeValue */ + LO_WORD(GATT_UUID_CHARACTERISTIC), + HI_WORD(GATT_UUID_CHARACTERISTIC), + GATT_CHAR_PROP_NOTIFY /* characteristic properties */ + /* characteristic UUID not needed here, is UUID of next attrib. */ + }, + 1, /* bValueLen */ + NULL, + GATT_PERM_READ /* wPermissions */ + }, + { + ATTRIB_FLAG_VALUE_APPL, /* wFlags */ + { /* bTypeValue */ + LO_WORD(GATT_UUID_CHAR_LNS_LOCATION_AND_SPEED), + HI_WORD(GATT_UUID_CHAR_LNS_LOCATION_AND_SPEED) + }, + 0, /* bValueLen */ + NULL, + GATT_PERM_READ /* wPermissions */ + }, + /* client characteristic configuration */ + { + ATTRIB_FLAG_VALUE_INCL | ATTRIB_FLAG_CCCD_APPL, /* wFlags */ + { /* bTypeValue */ + LO_WORD(GATT_UUID_CHAR_CLIENT_CONFIG), + HI_WORD(GATT_UUID_CHAR_CLIENT_CONFIG), + /* NOTE: this value has an instantiation for each client, a write to */ + /* this attribute does not modify this default value: */ + LO_WORD(GATT_CLIENT_CHAR_CONFIG_DEFAULT), /* client char. config. bit field */ + HI_WORD(GATT_CLIENT_CHAR_CONFIG_DEFAULT) + }, + 2, /* bValueLen */ + NULL, + (GATT_PERM_READ | GATT_PERM_WRITE) /* wPermissions */ + }, + +#ifdef LNS_POSITION_QUALITY_CHAR_SUPPORT + /* <<Characteristic>>, .. Position Quality*/ + { + ATTRIB_FLAG_VALUE_INCL, /* wFlags */ + { /* bTypeValue */ + LO_WORD(GATT_UUID_CHARACTERISTIC), + HI_WORD(GATT_UUID_CHARACTERISTIC), + GATT_CHAR_PROP_READ /* characteristic properties */ + /* characteristic UUID not needed here, is UUID of next attrib. */ + }, + 1, /* bValueLen */ + NULL, + GATT_PERM_READ /* wPermissions */ + }, + { + ATTRIB_FLAG_VALUE_APPL, /* wFlags */ + { /* bTypeValue */ + LO_WORD(GATT_UUID_CHAR_LNS_POSITION_QUALITY), + HI_WORD(GATT_UUID_CHAR_LNS_POSITION_QUALITY) + }, + 0, /* bValueLen */ + NULL, + GATT_PERM_READ /* wPermissions */ + }, +#endif + +#ifdef LNS_LN_CONTROL_POINT_CHAR_SUPPORT + /* <<Characteristic>>, .. LN Control Point*/ + { + ATTRIB_FLAG_VALUE_INCL, /* wFlags */ + { /* bTypeValue */ + LO_WORD(GATT_UUID_CHARACTERISTIC), + HI_WORD(GATT_UUID_CHARACTERISTIC), + (GATT_CHAR_PROP_WRITE | GATT_CHAR_PROP_INDICATE) /* characteristic properties */ + /* characteristic UUID not needed here, is UUID of next attrib. */ + }, + 1, /* bValueLen */ + NULL, + GATT_PERM_READ /* wPermissions */ + }, + /* Measurement Interval value*/ + { + ATTRIB_FLAG_VALUE_APPL, /* wFlags */ + { /* bTypeValue */ + LO_WORD(GATT_UUID_CHAR_LNS_LN_CP), + HI_WORD(GATT_UUID_CHAR_LNS_LN_CP) + }, + 0, /* bValueLen */ + NULL, + GATT_PERM_WRITE/* wPermissions */ + }, + /* client characteristic configuration */ + { + ATTRIB_FLAG_VALUE_INCL | ATTRIB_FLAG_CCCD_APPL, /* wFlags */ + { /* bTypeValue */ + LO_WORD(GATT_UUID_CHAR_CLIENT_CONFIG), + HI_WORD(GATT_UUID_CHAR_CLIENT_CONFIG), + /* NOTE: this value has an instantiation for each client, a write to */ + /* this attribute does not modify this default value: */ + LO_WORD(GATT_CLIENT_CHAR_CONFIG_DEFAULT), /* client char. config. bit field */ + HI_WORD(GATT_CLIENT_CHAR_CONFIG_DEFAULT) + }, + 2, /* bValueLen */ + NULL, + (GATT_PERM_READ | GATT_PERM_WRITE) /* wPermissions */ + }, +#endif + +#ifdef LNS_LN_NAVIGATION_CHAR_SUPPORT + /* <<Characteristic>>, .. Navigation*/ + { + ATTRIB_FLAG_VALUE_INCL, /* wFlags */ + { /* bTypeValue */ + LO_WORD(GATT_UUID_CHARACTERISTIC), + HI_WORD(GATT_UUID_CHARACTERISTIC), + GATT_CHAR_PROP_NOTIFY/* characteristic properties */ + /* characteristic UUID not needed here, is UUID of next attrib. */ + }, + 1, /* bValueLen */ + NULL, + GATT_PERM_READ /* wPermissions */ + }, + { + ATTRIB_FLAG_VALUE_APPL, /* wFlags */ + { /* bTypeValue */ + LO_WORD(GATT_UUID_CHAR_LNS_NAVIGATION), + HI_WORD(GATT_UUID_CHAR_LNS_NAVIGATION) + }, + 0, /* bValueLen */ + NULL, + GATT_PERM_NOTIF_IND_AUTHEN_REQ/* wPermissions */ + }, + /* client characteristic configuration */ + { + ATTRIB_FLAG_VALUE_INCL | ATTRIB_FLAG_CCCD_APPL, /* wFlags */ + { /* bTypeValue */ + LO_WORD(GATT_UUID_CHAR_CLIENT_CONFIG), + HI_WORD(GATT_UUID_CHAR_CLIENT_CONFIG), + LO_WORD(GATT_CLIENT_CHAR_CONFIG_DEFAULT), /* client char. config. bit field */ + HI_WORD(GATT_CLIENT_CHAR_CONFIG_DEFAULT) + }, + 2, /* bValueLen */ + NULL, + (GATT_PERM_READ | GATT_PERM_WRITE) /* wPermissions */ + } +#endif + +}; + +/**< @brief Health Temperature service size definition. */ +const uint16_t lns_attr_tbl_size = sizeof(lns_attr_tbl); + +/** + * @brief Set a Location And Navigation service parameter. + * + * NOTE: You can call this function with a Location And Navigation service parameter type and it will set the + * Location And Navigation service parameter. Location And Navigation service parameters are defined + * in @ref T_LNS_PARAM_TYPE. + * If the "len" field sets to the size of a "uint16_t" ,the + * "p_value" field must point to a data with type of "uint16_t". + * + * @param[in] param_type Location And Navigation service parameter type: @ref T_LNS_PARAM_TYPE + * @param[in] len Length of data to write + * @param[in] p_value Pointer to data to write. This is dependent on + * the parameter type and WILL be cast to the appropriate + * data type (For example: if data type of param is uint16_t, p_value will be cast to + * pointer of uint16_t). + * + * @return Operation result. + * @retval true Operation success. + * @retval false Operation failure. + * + * <b>Example usage</b> + * \code{.c} + void test(void) + { + T_LOCATION_AND_SPEED_VALUE_FLAG flag; + flag.instantaneous_speed_present = 1; + flag.total_distance_present = 1; + flag.location_present = 1; + flag.elevation_present = 1; + flag.heading_present = 1; + flag.rolling_time_present = 1; + flag.utc_time_present = 1; + flag.position_status = 1; + flag.speed_and_distance_format = 1; + flag.elevation_source = 1; + flag.heading_source = 1; + flag.rfu = 0; + + lns_set_parameter(LNS_LAS_PARAM_INC_FLAG, 2, &flag); + } + * \endcode + */ +bool lns_set_parameter(T_LNS_PARAM_TYPE param_type, uint8_t len, void *p_value) +{ + bool ret = true; + + switch (param_type) + { + default: + { + ret = false; + PROFILE_PRINT_ERROR0("lns_set_parameter failed\n"); + } + break; + + case LNS_PARAM_LN_FEATURE_SUPPORT: + { + if (len != sizeof(uint32_t)) + { + ret = false; + } + else + { + memcpy(&lns_ln_feature_support, p_value, len); + } + } + break; + + //Location And Speed Value + case LNS_LAS_PARAM_INC_FLAG: + { + if (len != sizeof(uint16_t)) + { + ret = false; + } + else + { + memcpy(&lns_location_and_speed_value.flag, p_value, len); + } + } + break; + + case LNS_LAS_PARAM_INSTANTANEOUS_SPEED: + { + if (len != sizeof(uint16_t)) + { + ret = false; + } + else + { + memcpy(&lns_location_and_speed_value.instantaneous_speed, p_value, len); + + } + } + break; + + case LNS_LAS_PARAM_TOTAL_DISTANCE: + { + if (len != 3) + { + ret = false; + } + else + { + memcpy(&lns_location_and_speed_value.total_distance, p_value, len); + + } + } + break; + + case LNS_LAS_PARAM_LOCATION_LATITUDE: + { + if (len != 3) + { + ret = false; + } + else + { + memcpy(&lns_location_and_speed_value.location_latitude, p_value, len); + + } + } + break; + + case LNS_LAS_PARAM_LOCATION_LONGITUDE: + { + if (len != 3) + { + ret = false; + } + else + { + memcpy(&lns_location_and_speed_value.location_latitude, p_value, len); + + } + } + break; + + case LNS_LAS_PARAM_ELEVATION: + { + if (len != 3) + { + ret = false; + } + else + { + memcpy(&lns_location_and_speed_value.elevation, p_value, len); + + } + } + break; + + case LNS_LAS_PARAM_HEADING: + { + if (len != sizeof(uint16_t)) + { + ret = false; + } + else + { + memcpy(&lns_location_and_speed_value.heading, p_value, len); + + } + } + break; + + case LNS_LAS_PARAM_ROLLING_TIME: + { + if (len != sizeof(uint8_t)) + { + ret = false; + } + else + { + memcpy(&lns_location_and_speed_value.rolling_time, p_value, len); + + } + } + break; + + case LNS_LAS_PARAM_UTC_TIME: + { + if (len != 7) + { + ret = false; + } + else + { + memcpy(&lns_location_and_speed_value.utc_time, p_value, len); + + } + } + break; + + case LNS_LAS_PARAM_POSITION_STATUS: + { + lns_location_and_speed_value.flag.position_status = *(uint8_t *)p_value; + } + break; + + case LNS_LAS_PARAM_SPEED_AND_DISTANCE_FORMAT: + { + lns_location_and_speed_value.flag.speed_and_distance_format = *(uint8_t *)p_value; + } + break; + + case LNS_LAS_PARAM_ELEVATION_SOURCE: + { + lns_location_and_speed_value.flag.elevation_source = *(uint8_t *)p_value; + } + break; + + case LNS_LAS_PARAM_HEADING_SOURCE: + { + lns_location_and_speed_value.flag.heading_source = *(uint8_t *)p_value; + } + break; + + //Navigation value + case LNS_NAVIGATION_PARAM_INC_FLAG: + { + if (len != sizeof(uint16_t)) + { + ret = false; + } + else + { + memcpy(&lns_navigation_value.flag, p_value, len); + } + } + break; + + case LNS_NAVIGATION_PARAM_BEARING: + { + if (len != sizeof(uint16_t)) + { + ret = false; + } + else + { + memcpy(&lns_navigation_value.bearing, p_value, len); + + } + } + break; + + case LNS_NAVIGATION_PARAM_HEADING: + { + if (len != sizeof(uint16_t)) + { + ret = false; + } + else + { + memcpy(&lns_navigation_value.bearing, p_value, len); + + } + } + break; + + case LNS_NAVIGATION_PARAM_REMAINING_DISTANCE: + { + if (len != 3) + { + ret = false; + } + else + { + memcpy(&lns_navigation_value.reamining_distance, p_value, len); + + } + } + break; + + case LNS_NAVIGATION_PARAM_REMAINING_VERTICAL_DISTANCE: + { + if (len != 3) + { + ret = false; + } + else + { + memcpy(&lns_navigation_value.reamining_vertical_distance, p_value, len); + + } + } + break; + + case LNS_NAVIGATION_PARAM_ESTIMATED_TIME_OF_ARRIVAL: + { + if (len != 7) + { + ret = false; + } + else + { + memcpy(&lns_navigation_value.estimated_time_of_arrival, p_value, len); + + } + } + break; + + case LNS_NAVIGATION_PARAM_POSITION_STATUS: + { + lns_navigation_value.flag.position_status = *(uint8_t *)p_value; + } + break; + case LNS_NAVIGATION_PARAM_HEADING_SOURCE: + { + lns_navigation_value.flag.heading_source = *(uint8_t *)p_value; + } + break; + + case LNS_NAVIGATION_PARAM_NAVIGATION_INDICATOR_TYPE: + { + lns_navigation_value.flag.navigation_indicator_type = *(uint8_t *)p_value; + } + break; + + case LNS_NAVIGATION_PARAM_WAYPOINT_REACHED: + { + lns_navigation_value.flag.waypoint_reached = *(uint8_t *)p_value; + } + break; + + case LNS_NAVIGATION_PARAM_DESTINATION_REACHED: + { + lns_navigation_value.flag.destination_reached = *(uint8_t *)p_value; + } + break; + + case LNS_PQ_PARAM_INC_FLAG: + { + if (len != sizeof(uint16_t)) + { + ret = false; + } + else + { + memcpy(&lns_position_quality_value.flag, p_value, len); + } + } + break; + + case LNS_PQ_PARAM_NUMBER_OF_BEACONS_IN_SOLUTION: + { + if (len != sizeof(uint8_t)) + { + ret = false; + } + else + { + memcpy(&lns_position_quality_value.number_of_beacons_in_solution, p_value, len); + } + } + break; + + case LNS_PQ_PARAM_NUMBER_OF_BEACONS_IN_VIEW: + { + if (len != sizeof(uint8_t)) + { + ret = false; + } + else + { + memcpy(&lns_position_quality_value.number_of_beacons_in_view, p_value, len); + } + } + break; + + + case LNS_PQ_PARAM_TIME_TO_FIRST_FIX: + { + if (len != sizeof(uint16_t)) + { + ret = false; + } + else + { + memcpy(&lns_position_quality_value.time_to_first_fix, p_value, len); + } + } + break; + + case LNS_PQ_PARAM_EHPE: + { + if (len != sizeof(uint32_t)) + { + ret = false; + } + else + { + memcpy(&lns_position_quality_value.ehpe, p_value, len); + } + } + break; + + case LNS_PQ_PARAM_EVPE: + { + if (len != sizeof(uint32_t)) + { + ret = false; + } + else + { + memcpy(&lns_position_quality_value.evpe, p_value, len); + } + } + break; + case LNS_PQ_PARAM_HDOP: + { + if (len != sizeof(uint8_t)) + { + ret = false; + } + else + { + memcpy(&lns_position_quality_value.hdop, p_value, len); + } + } + break; + + case LNS_PQ_PARAM_VDOP: + { + if (len != sizeof(uint8_t)) + { + ret = false; + } + else + { + memcpy(&lns_position_quality_value.vdop, p_value, len); + } + } + break; + + case LNS_CP_PARA_NUMBER_OF_ROUTE: + { + if (len != sizeof(uint16_t)) + { + ret = false; + } + else + { + memcpy(&lns_cp_number_of_route, p_value, len); + } + } + break; + + case LNS_CP_PARA_NAME_OF_ROUTE: + { + if (len > LNS_CP_PARA_NAME_OF_ROUTE_MAX_LEN) + { + ret = false; + } + else + { + memcpy(&lns_cp_name_of_route, p_value, len); + lns_cp_name_of_route_len = len; + } + } + break; + case LNS_PARAM_CTL_PNT_PROG_CLR: + { + if (len != 0) + { + ret = false; + } + else + { + lns_ctl_pnt.value[0] = LN_CP_OPCODE_RESERVED; + } + } + break; + + } + + return ret; +} + +void lns_format_pq_value_for_read() +{ + uint8_t cur_offset = 0; + + memcpy(&lns_pq_value_for_read_cfm[cur_offset], &lns_position_quality_value.flag, 2); + cur_offset += 2; + + if (lns_position_quality_value.flag.number_of_beacons_in_solution_present) + { + memcpy(&lns_pq_value_for_read_cfm[cur_offset], + &lns_position_quality_value.number_of_beacons_in_solution, 1); + cur_offset += 1; + } + + if (lns_position_quality_value.flag.number_of_beacons_in_view_present) + { + memcpy(&lns_pq_value_for_read_cfm[cur_offset], + &lns_position_quality_value.number_of_beacons_in_view, 1); + cur_offset += 1; + } + + if (lns_position_quality_value.flag.time_to_first_fix_present) + { + memcpy(&lns_pq_value_for_read_cfm[cur_offset], &lns_position_quality_value.time_to_first_fix, 2); + cur_offset += 2; + } + + if (lns_position_quality_value.flag.ehpe_present) + { + memcpy(&lns_pq_value_for_read_cfm[cur_offset], &lns_position_quality_value.ehpe, 4); + cur_offset += 4; + } + + if (lns_position_quality_value.flag.evpe_present) + { + memcpy(&lns_pq_value_for_read_cfm[cur_offset], &lns_position_quality_value.evpe, 4); + cur_offset += 4; + } + + if (lns_position_quality_value.flag.hdop_present) + { + memcpy(&lns_pq_value_for_read_cfm[cur_offset], &lns_position_quality_value.hdop, 1); + cur_offset += 1; + } + + if (lns_position_quality_value.flag.vdop_present) + { + memcpy(&lns_pq_value_for_read_cfm[cur_offset], + &lns_position_quality_value.vdop, 1); + cur_offset += 1; + } + + lns_pq_value_length_for_read_cfm = cur_offset; + +} + +uint8_t lns_get_fisrt_value_offset_to_notify() +{ + + uint8_t cur_offset = 2; + + if (lns_location_and_speed_value.flag.instantaneous_speed_present) + { + cur_offset += 2; + } + + if (lns_location_and_speed_value.flag.total_distance_present) + { + cur_offset += 3; + } + + if (lns_location_and_speed_value.flag.location_present) + { + cur_offset += 4; + cur_offset += 4; + } + + if (lns_location_and_speed_value.flag.elevation_present) + { + cur_offset += 3; + } + + if (lns_location_and_speed_value.flag.heading_present) + { + cur_offset += 2; + } + + + if (lns_location_and_speed_value.flag.rolling_time_present) + { + cur_offset += 1; + } + + if (cur_offset > 20) + { + return cur_offset - 1; + } + + + if (lns_location_and_speed_value.flag.utc_time_present) + { + cur_offset += 7; + } + + if (cur_offset > 20) + { + return cur_offset - 7; + } + + return cur_offset; + +} + +/** + * @brief Send location and speed value notification data. + * Application shall call @ref lns_set_parameter to set location and speed value first, + * and the call this api to send the notication value. + * + * @param[in] conn_id Connection id. + * @param[in] service_id Service id. + * + * @return service id @ref T_SERVER_ID. + * + * <b>Example usage</b> + * \code{.c} + void test(void) + { + bool op_result; + T_LOCATION_AND_SPEED_VALUE_FLAG flag; + flag.instantaneous_speed_present = 1; + flag.total_distance_present = 1; + flag.location_present = 1; + flag.elevation_present = 1; + flag.heading_present = 1; + flag.rolling_time_present = 1; + flag.utc_time_present = 1; + flag.position_status = 1; + flag.speed_and_distance_format = 1; + flag.elevation_source = 1; + flag.heading_source = 1; + flag.rfu = 0; + + lns_set_parameter(LNS_LAS_PARAM_INC_FLAG, 2, &flag); + + op_result = lns_location_and_speed_value_notify(p_parse_value->dw_param[0], lns_id); + } + * \endcode + */ +bool lns_location_and_speed_value_notify(uint8_t conn_id, T_SERVER_ID service_id) +{ + bool ret = true; + + T_LOCATION_AND_SPEED_VALUE_FLAG flag = {0}; + uint8_t location_and_speed_value[20]; + + + uint8_t cur_offset = 2; + uint8_t first_notify_value_offset = lns_get_fisrt_value_offset_to_notify(); + + if (lns_location_and_speed_value.flag.instantaneous_speed_present) + { + flag.instantaneous_speed_present = 1; + memcpy(&location_and_speed_value[cur_offset], &lns_location_and_speed_value.instantaneous_speed, 2); + cur_offset += 2; + } + + if (lns_location_and_speed_value.flag.total_distance_present) + { + flag.total_distance_present = 1; + memcpy(&location_and_speed_value[cur_offset], &lns_location_and_speed_value.total_distance, 3); + cur_offset += 3; + } + + if (lns_location_and_speed_value.flag.location_present) + { + flag.location_present = 1; + memcpy(&location_and_speed_value[cur_offset], &lns_location_and_speed_value.location_latitude, 4); + cur_offset += 4; + memcpy(&location_and_speed_value[cur_offset], &lns_location_and_speed_value.location_longitude, 4); + cur_offset += 4; + } + + if (lns_location_and_speed_value.flag.elevation_present) + { + flag.elevation_present = 1; + memcpy(&location_and_speed_value[cur_offset], &lns_location_and_speed_value.elevation, 3); + cur_offset += 3; + } + + if (lns_location_and_speed_value.flag.heading_present) + { + flag.heading_present = 1; + memcpy(&location_and_speed_value[cur_offset], + &lns_location_and_speed_value.heading, 2); + cur_offset += 2; + } + + if (cur_offset == first_notify_value_offset) + { + //copy flag + memcpy(&location_and_speed_value[0], &flag, 2); + //send first notification here + server_send_data(conn_id, service_id, LNS_LN_LOCATION_AND_SPEED_VALUE_INDEX, + location_and_speed_value, cur_offset, GATT_PDU_TYPE_NOTIFICATION); + + //send remain data + memset(&flag, 0, 2); + cur_offset = 2; + if (lns_location_and_speed_value.flag.rolling_time_present) + { + memcpy(&location_and_speed_value[cur_offset], + &lns_location_and_speed_value.rolling_time, 1); + flag.rolling_time_present = 1; + cur_offset += 1; + } + + if (lns_location_and_speed_value.flag.utc_time_present) + { + + memcpy(&location_and_speed_value[cur_offset], + &lns_location_and_speed_value.utc_time, 7); + flag.utc_time_present = 1; + cur_offset += 7; + } + memcpy(&location_and_speed_value[0], &flag, 2); + //send second notification + server_send_data(conn_id, service_id, LNS_LN_LOCATION_AND_SPEED_VALUE_INDEX, + location_and_speed_value, cur_offset, GATT_PDU_TYPE_NOTIFICATION); + + return ret; + } + + if (lns_location_and_speed_value.flag.rolling_time_present) + { + flag.rolling_time_present = 1; + memcpy(&location_and_speed_value[cur_offset], + &lns_location_and_speed_value.rolling_time, 1); + cur_offset += 1; + } + + if (cur_offset == first_notify_value_offset) + { + //copy flag + memcpy(&location_and_speed_value[0], &flag, 2); + //send first notification here + server_send_data(conn_id, service_id, LNS_LN_LOCATION_AND_SPEED_VALUE_INDEX, + location_and_speed_value, cur_offset, GATT_PDU_TYPE_NOTIFICATION); + + + //send remain data + memset(&flag, 0, 2); + cur_offset = 2; + if (lns_location_and_speed_value.flag.utc_time_present) + { + memcpy(&location_and_speed_value[cur_offset], + &lns_location_and_speed_value.utc_time, 7); + flag.utc_time_present = 1; + cur_offset += 7; + } + memcpy(&location_and_speed_value[0], &lns_location_and_speed_value.flag, 2); + //send second notification + server_send_data(conn_id, service_id, LNS_LN_LOCATION_AND_SPEED_VALUE_INDEX, + location_and_speed_value, cur_offset, GATT_PDU_TYPE_NOTIFICATION); + + return ret; + } + + if (lns_location_and_speed_value.flag.utc_time_present) + { + if (cur_offset <= 13) + { + memcpy(&location_and_speed_value[cur_offset], + &lns_location_and_speed_value.utc_time, 7); + flag.utc_time_present = 1; + cur_offset += 7; + } + else if ((cur_offset > 13) && (cur_offset <= 19)) + { + memcpy(&location_and_speed_value[cur_offset], + &lns_location_and_speed_value.utc_time, (20 - cur_offset)); + flag.utc_time_present = 1; + cur_offset = 20; + } + } + memcpy(&location_and_speed_value[0], &lns_location_and_speed_value.flag, 2); + //send nortification + server_send_data(conn_id, service_id, LNS_LN_LOCATION_AND_SPEED_VALUE_INDEX, + location_and_speed_value, cur_offset, GATT_PDU_TYPE_NOTIFICATION); + + return ret; +} + +/** + * @brief Send navigation value notification data. + * Application shall call @ref lns_set_parameter to set navigation value first, + * and the call this api to send the notication value. + * + * @param[in] conn_id Connection id. + * @param[in] service_id Service id. + * + * @return service id @ref T_SERVER_ID. + * + * <b>Example usage</b> + * \code{.c} + void test(void) + { + bool op_result; + + T_NAVIGATION_VALUE_FLAG flag; + flag.remaining_distance_present = 1; + flag.remaining_vertical_distance_present = 1; + flag.estimated_time_of_arrival_present = 1; + flag.position_status = 1; + flag.heading_source = 1; + flag.navigation_indicator_type = 1; + flag.waypoint_reached = 1; + flag.destination_reached = 1; + flag.rfus = 0; + + lns_set_parameter(LNS_NAVIGATION_PARAM_INC_FLAG, 2, &flag); + + op_result = lns_navigation_value_notify(p_parse_value->dw_param[0], lns_id); + + } + * \endcode + */ +bool lns_navigation_value_notify(uint8_t conn_id, T_SERVER_ID service_id) +{ + uint8_t navigation_value[LNS_NAVIGATION_VALUE_MAX_LEN]; + uint8_t iCurOffset = 0; + + memcpy(&navigation_value[iCurOffset], &lns_navigation_value.flag, 2); + iCurOffset += 2; + memcpy(&navigation_value[iCurOffset], &lns_navigation_value.bearing, 2); + iCurOffset += 2; + memcpy(&navigation_value[iCurOffset], &lns_navigation_value.heading, 2); + iCurOffset += 2; + + if (lns_navigation_value.flag.remaining_distance_present) + { + memcpy(&navigation_value[iCurOffset], &lns_navigation_value.reamining_distance, 3); + iCurOffset += 3; + } + + if (lns_navigation_value.flag.remaining_vertical_distance_present) + { + memcpy(&navigation_value[iCurOffset], &lns_navigation_value.reamining_vertical_distance, 3); + iCurOffset += 3; + } + + if (lns_navigation_value.flag.estimated_time_of_arrival_present) + { + memcpy(&navigation_value[iCurOffset], &lns_navigation_value.estimated_time_of_arrival, 7); + iCurOffset += 7; + } + + PROFILE_PRINT_INFO0("lns_navigation_value_notify"); + return server_send_data(conn_id, service_id, LNS_LN_NAVIGATION_VALUE_INDEX, navigation_value, + iCurOffset, GATT_PDU_TYPE_NOTIFICATION); +} + +bool lns_ln_ctl_pnt_value_indicate(uint8_t conn_id, T_SERVER_ID service_id, uint8_t op_code, + uint8_t rsp_code, + uint8_t *p_parameter, uint8_t para_len) +{ + uint16_t attrib_index = LNS_LN_CP_VALUE_INDEX; + + uint8_t *p_data; + uint16_t data_len; + + lns_ctl_pnt.value[0] = LN_CP_OPCODE_RESPONSE_CODE; + lns_ctl_pnt.value[1] = op_code; /* Control Point request opcode. */ + lns_ctl_pnt.value[2] = rsp_code; + + if (p_parameter != NULL && para_len != 0) + { + memcpy(&lns_ctl_pnt.value[3], p_parameter, para_len); + } + + lns_ctl_pnt.cur_length = 3 * sizeof(uint8_t) + para_len; + + + p_data = lns_ctl_pnt.value; + data_len = lns_ctl_pnt.cur_length; + + // send indication to client + return server_send_data(conn_id, service_id, attrib_index, p_data, data_len, + GATT_PDU_TYPE_INDICATION); +} + + + + +static void lns_ctl_pnt_set_cumulative_value(uint8_t conn_id, T_SERVER_ID service_id, + uint8_t rsp_code) +{ + uint8_t op_code = LN_CP_OPCODE_SET_CUMULATIVE_VALUE; + lns_ln_ctl_pnt_value_indicate(conn_id, service_id, op_code, rsp_code, NULL, 0); +} + +static void lns_ctl_pnt_mask_location_speed_char_content(uint8_t conn_id, T_SERVER_ID service_id, + uint8_t rsp_code) +{ + uint8_t op_code = LN_CP_OPCODE_MASK_LOCATION_AND_SPEED_CHAR_CONTENT; + lns_ln_ctl_pnt_value_indicate(conn_id, service_id, op_code, rsp_code, NULL, 0); +} + +static void lns_ctl_pnt_navigation_control(uint8_t conn_id, T_SERVER_ID service_id, + uint8_t rsp_code) +{ + uint8_t op_code = LN_CP_OPCODE_NAVIGATION_CONTROL; + lns_ln_ctl_pnt_value_indicate(conn_id, service_id, op_code, rsp_code, NULL, 0); +} + +static void lns_ctl_pnt_request_number_of_routes(uint8_t conn_id, T_SERVER_ID service_id, + uint8_t rsp_code, uint16_t number_of_route) +{ + uint8_t op_code = LN_CP_OPCODE_REQUEST_NUMBER_OF_ROUTES; + lns_ln_ctl_pnt_value_indicate(conn_id, service_id, op_code, rsp_code, (uint8_t *)&number_of_route, + 2); +} + +static void lns_ctl_pnt_request_name_of_route(uint8_t conn_id, T_SERVER_ID service_id, + uint8_t rsp_code, uint8_t *pNameOfRoute, uint8_t len) +{ + uint8_t op_code = LN_CP_OPCODE_REQUEST_NAME_OF_ROUTE; + lns_ln_ctl_pnt_value_indicate(conn_id, service_id, op_code, rsp_code, pNameOfRoute, len); +} + +static void lns_ctl_pnt_select_route(uint8_t conn_id, T_SERVER_ID service_id, uint8_t rsp_code) +{ + uint8_t op_code = LN_CP_OPCODE_SELECT_ROUTE; + lns_ln_ctl_pnt_value_indicate(conn_id, service_id, op_code, rsp_code, NULL, 0); +} + +static void lns_ctl_pnt_set_fix_rate(uint8_t conn_id, T_SERVER_ID service_id, uint8_t rsp_code) +{ + uint8_t op_code = LN_CP_OPCODE_SET_FIX_RATE; + lns_ln_ctl_pnt_value_indicate(conn_id, service_id, op_code, rsp_code, NULL, 0); +} + +static void lns_ctl_pnt_set_elevation(uint8_t conn_id, T_SERVER_ID service_id, uint8_t rsp_code) +{ + uint8_t op_code = LN_CP_OPCODE_SET_ELEVATION; + lns_ln_ctl_pnt_value_indicate(conn_id, service_id, op_code, rsp_code, NULL, 0); +} + +static uint8_t lns_handle_ctl_pnt_proc_2(uint8_t service_id, uint16_t write_length, + uint8_t *value_ptr) +{ + T_LNS_CALLBACK_DATA callback_data; + uint8_t resp_code = LN_CP_RSPCODE_SUCCESS; + uint16_t parameter_length = 0; + memcpy(lns_ctl_pnt.value, value_ptr, write_length); + if (write_length >= 1) + { + parameter_length = write_length - 1; + } + + PROFILE_PRINT_INFO1("lns_handle_ctl_pnt_proc request: OpCode=0x%x", lns_ctl_pnt.value[0]); + + callback_data.msg_type = SERVICE_CALLBACK_TYPE_WRITE_CHAR_VALUE; + callback_data.msg_data.write.opcode = (T_LN_CP_OPCODE)lns_ctl_pnt.value[0]; + + switch (lns_ctl_pnt.value[0]) + { + case LN_CP_OPCODE_NAVIGATION_CONTROL: + { + if (parameter_length == 1) + { + memcpy(&callback_data.msg_data.write.cp_parameter.navigation_control, + &lns_ctl_pnt.value[1], 1); + } + else + { + resp_code = LN_CP_RSPCODE_INVALID_PARAMETER; + } + } + + break; + + case LN_CP_OPCODE_SET_CUMULATIVE_VALUE: + { + if (parameter_length == 3) + { + memcpy(&callback_data.msg_data.write.cp_parameter.cumulative_total_distance, + &lns_ctl_pnt.value[1], 3); + } + else + { + resp_code = LN_CP_RSPCODE_INVALID_PARAMETER; + } + } + break; + + case LN_CP_OPCODE_MASK_LOCATION_AND_SPEED_CHAR_CONTENT: + { + if (parameter_length == 2) + { + memcpy(&callback_data.msg_data.write.cp_parameter.mask_location_and_speed, + &lns_ctl_pnt.value[1], 2); + } + else + { + resp_code = LN_CP_RSPCODE_INVALID_PARAMETER; + } + } + break; + + case LN_CP_OPCODE_REQUEST_NUMBER_OF_ROUTES: + { + //no parameter + } + break; + case LN_CP_OPCODE_REQUEST_NAME_OF_ROUTE: + { + if (parameter_length == 2) + { + memcpy(&callback_data.msg_data.write.cp_parameter.number_of_desired_route, + &lns_ctl_pnt.value[1], 1); + } + else + { + resp_code = LN_CP_RSPCODE_INVALID_PARAMETER; + } + } + break; + + case LN_CP_OPCODE_SELECT_ROUTE: + { + if (parameter_length == 2) + { + memcpy(&callback_data.msg_data.write.cp_parameter.select_route_desired_route_number, + &lns_ctl_pnt.value[1], 2); + } + else + { + resp_code = LN_CP_RSPCODE_INVALID_PARAMETER; + } + } + break; + + case LN_CP_OPCODE_SET_FIX_RATE: + { + if (parameter_length == 1) + { + memcpy(&callback_data.msg_data.write.cp_parameter.set_fix_rate, + &lns_ctl_pnt.value[1], 1); + } + else + { + resp_code = LN_CP_RSPCODE_INVALID_PARAMETER; + } + } + break; + + case LN_CP_OPCODE_SET_ELEVATION: + { + if (parameter_length == 3) + { + memcpy(&callback_data.msg_data.write.cp_parameter.set_elevation, + &lns_ctl_pnt.value[1], 3); + } + else + { + resp_code = LN_CP_RSPCODE_INVALID_PARAMETER; + } + } + break; + + default: + { + resp_code = LN_CP_RSPCODE_OPCODE_UNSUPPORT; + } + break; + } + + if (resp_code == LN_CP_RSPCODE_SUCCESS) + { + pfn_lns_app_cb(service_id, (void *)&callback_data); + } + return resp_code; +} + +/** + * @brief handle control point write (request). + * + * @param[in] conn_id Connection id. + * @param[in] service_id Service id. + * @param[in] write_length write request data length. + * @param[in] value_ptr pointer to write request data. + * @return none + * @retval void +*/ +static T_APP_RESULT lns_handle_ctl_pnt_proc(uint8_t conn_id, T_SERVER_ID service_id, + uint16_t write_length, uint8_t *p_value) +{ + T_APP_RESULT cause = APP_RESULT_SUCCESS; + memcpy(lns_ctl_pnt.value, p_value, write_length); + + PROFILE_PRINT_INFO1("lns_handle_ctl_pnt_proc request: op_code=0x%x", lns_ctl_pnt.value[0]); + + switch (lns_ctl_pnt.value[0]) + { + case LN_CP_OPCODE_NAVIGATION_CONTROL: + { + if (!lns_notify_indicate_flag.navigation_enable) + { + cause = APP_RESULT_CCCD_IMPROPERLY_CONFIGURED; + } + } + break; + + default: + { + } + break; + } + return cause; +} + +static void lns_ctl_pnt_write_ind_post_proc(uint8_t conn_id, T_SERVER_ID service_id, + uint16_t attrib_index, uint16_t write_length, uint8_t *p_value) +{ + uint8_t resp_code = LN_CP_RSPCODE_SUCCESS; + + bool error = false; + memcpy(lns_ctl_pnt.value, p_value, write_length); + lns_ctl_pnt.cur_length = write_length; + + PROFILE_PRINT_INFO1("lns_ctl_pnt_write_ind_post_proc: OpCode=0x%x", lns_ctl_pnt.value[0]); + + resp_code = lns_handle_ctl_pnt_proc_2(service_id, write_length, p_value); + + switch (lns_ctl_pnt.value[0]) + { + default: + { + error = true; + } + break; + + case LN_CP_OPCODE_SET_CUMULATIVE_VALUE: + { + lns_ctl_pnt_set_cumulative_value(conn_id, service_id, resp_code); + } + break; + + case LN_CP_OPCODE_MASK_LOCATION_AND_SPEED_CHAR_CONTENT: + { + lns_ctl_pnt_mask_location_speed_char_content(conn_id, service_id, resp_code); + } + break; + + case LN_CP_OPCODE_NAVIGATION_CONTROL: + { + lns_ctl_pnt_navigation_control(conn_id, service_id, resp_code); + } + break; + + case LN_CP_OPCODE_REQUEST_NUMBER_OF_ROUTES: + { + lns_ctl_pnt_request_number_of_routes(conn_id, service_id, resp_code, lns_cp_number_of_route); + } + break; + case LN_CP_OPCODE_REQUEST_NAME_OF_ROUTE: + { + lns_ctl_pnt_request_name_of_route(conn_id, service_id, resp_code, lns_cp_name_of_route, + lns_cp_name_of_route_len); + } + break; + + case LN_CP_OPCODE_SELECT_ROUTE: + { + lns_ctl_pnt_select_route(conn_id, service_id, resp_code); + } + break; + + case LN_CP_OPCODE_SET_FIX_RATE: + { + lns_ctl_pnt_set_fix_rate(conn_id, service_id, resp_code); + } + break; + + case LN_CP_OPCODE_SET_ELEVATION: + { + lns_ctl_pnt_set_elevation(conn_id, service_id, resp_code); + } + break; + } + + if (error) + { + lns_ln_ctl_pnt_value_indicate(conn_id, service_id, lns_ctl_pnt.value[0], + LN_CP_RSPCODE_OPCODE_UNSUPPORT, NULL, 0); + } + +} + +/** + * @brief read characteristic data from service. + * + * @param[in] conn_id Connection id. + * @param[in] service_id Service id. + * @param[in] attrib_index Attribute index of getting characteristic data. + * @param[in] offset Used for Blob Read. + * @param[in] p_length length of getting characteristic data. + * @param[in] pp_value data got from service. + * @return Profile procedure result +*/ +T_APP_RESULT lns_attr_read_cb(uint8_t conn_id, T_SERVER_ID service_id, uint16_t attrib_index, + uint16_t offset, uint16_t *p_length, uint8_t **pp_value) +{ + T_APP_RESULT cause = APP_RESULT_SUCCESS; + + PROFILE_PRINT_INFO2("lns_attr_read_cb iAttribIndex = %d iOffset %x", attrib_index, offset); + + *p_length = 0; + lns_ctl_pnt.value[0] = LN_CP_OPCODE_RESERVED;////// + + switch (attrib_index) + { + case LNS_LN_FEATURE_VALUE_INDEX: + { + *p_length = sizeof(lns_ln_feature_support); + *pp_value = (uint8_t *)&lns_ln_feature_support; + } + break; + + case LNS_LN_POSITION_QUALITY_VALUE_INDEX: + { + T_LNS_CALLBACK_DATA callback_data; + callback_data.msg_type = SERVICE_CALLBACK_TYPE_READ_CHAR_VALUE; + callback_data.msg_data.read_value_index = LNS_READ_POSITION_QUALITY_VALUE; + pfn_lns_app_cb(service_id, (void *)&callback_data); + //after app call lns_set_parameter to set Position Quality Value, profile will response this value to remote device + lns_format_pq_value_for_read(); + *p_length = lns_pq_value_length_for_read_cfm; + *pp_value = lns_pq_value_for_read_cfm; + } + break; + + default: + { + PROFILE_PRINT_ERROR1("lns_attr_read_cb iAttribIndex = %d not found", attrib_index); + cause = APP_RESULT_ATTR_NOT_FOUND; + } + break; + } + + return cause; +} + +/** + * @brief write characteristic data from service. + * + * @param[in] conn_id Connection id. + * @param[in] service_id Service id. + * @param[in] attrib_index Attribute index of characteristic. + * @param[in] write_type Write type. + * @param length length of value to be written. + * @param p_value value to be written. + * @param[in] p_write_ind_post_proc pointer of a function to handle attribute write. + * @return Profile procedure result +*/ +T_APP_RESULT lns_attr_write_cb(uint8_t conn_id, T_SERVER_ID service_id, uint16_t attrib_index, + T_WRITE_TYPE write_type, + uint16_t length, uint8_t *p_value, P_FUN_WRITE_IND_POST_PROC *p_write_ind_post_proc) +{ + T_APP_RESULT cause = APP_RESULT_SUCCESS; + PROFILE_PRINT_INFO2("lns_attr_write_cb attrib_index = %d length %x", attrib_index, length); + + switch (attrib_index) + { + case LNS_LN_CP_VALUE_INDEX: + { + /* Attribute value has variable size, make sure written value size is valid. */ + if ((length > LNS_MAX_CTL_PNT_VALUE) || (p_value == NULL)) + { + cause = APP_RESULT_INVALID_VALUE_SIZE; + } + /* Make sure Control Point is not "Process already in progress". */ + else if (lns_ctl_pnt.value[0] == LN_CP_OPCODE_RESPONSE_CODE) + { + PROFILE_PRINT_ERROR1("lns_attr_write_cb: lns_ctl_pnt.value[0] %d ", lns_ctl_pnt.value[0]); + cause = APP_RESULT_PROC_ALREADY_IN_PROGRESS; + } + /* Make sure Control Point is configured indication enable. */ + else if (!lns_notify_indicate_flag.ln_cp_indicate_enable) + { + cause = APP_RESULT_CCCD_IMPROPERLY_CONFIGURED; + } + else + { + cause = lns_handle_ctl_pnt_proc(conn_id, service_id, length, p_value); + if (cause == APP_RESULT_SUCCESS) + { + *p_write_ind_post_proc = lns_ctl_pnt_write_ind_post_proc; + } + else + { + *p_write_ind_post_proc = NULL; + } + } + + } + break; + + default: + { + PROFILE_PRINT_ERROR1("lns_attr_write_cb attrib_index = %d not found", attrib_index); + cause = APP_RESULT_ATTR_NOT_FOUND; + } + break; + } + + return cause; +} + +/** + * @brief update CCCD bits from stack. + * + * @param[in] conn_id Connection id. + * @param[in] service_id Service id. + * @param[in] index Attribute index of characteristic data. + * @param[in] ccc_bits CCCD bits from stack. + * @return None +*/ +void lns_cccd_update_cb(uint8_t conn_id, T_SERVER_ID service_id, uint16_t index, uint16_t ccc_bits) +{ + T_LNS_CALLBACK_DATA callback_data; + bool bHandle = true; + + PROFILE_PRINT_INFO2("lns_cccd_update_cb index = %d ccc_bits %x", index, ccc_bits); + + switch (index) + { + case LNS_LN_LOCATION_AND_SPEED_CCCD_INDEX: + { + if (ccc_bits & GATT_CLIENT_CHAR_CONFIG_NOTIFY) + { + // Enable Notification + lns_notify_indicate_flag.location_and_speed_notify_enable = 1; + callback_data.msg_data.notification_indification_index = + LNS_NOTIFY_INDICATE_LOCATION_AND_SPEED_ENABLE; + } + else + { + lns_notify_indicate_flag.location_and_speed_notify_enable = 0; + callback_data.msg_data.notification_indification_index = + LNS_NOTIFY_INDICATE_LOCATION_AND_SPEED_DISABLE; + } + } + break; + + case LNS_LN_CP_CCCD_INDEX: + { + if (ccc_bits & GATT_CLIENT_CHAR_CONFIG_INDICATE) + { + // Enable Indofication + lns_notify_indicate_flag.ln_cp_indicate_enable = 1; + callback_data.msg_data.notification_indification_index = LNS_NOTIFY_INDICATE_CP_ENABLE; + } + else + { + lns_notify_indicate_flag.ln_cp_indicate_enable = 0; + callback_data.msg_data.notification_indification_index = LNS_NOTIFY_INDICATE_CP_DISABLE; + } + + } + break; + + case LNS_LN_NAVIGATION_CCCD_INDEX: + { + if (ccc_bits & GATT_CLIENT_CHAR_CONFIG_NOTIFY) + { + // Enable Notification + lns_notify_indicate_flag.navigation_enable = 1; + callback_data.msg_data.notification_indification_index = LNS_NOTIFY_INDICATE_NAVIGATION_ENABLE; + } + else + { + lns_notify_indicate_flag.navigation_enable = 0; + callback_data.msg_data.notification_indification_index = LNS_NOTIFY_INDICATE_NAVIGATION_DISABLE; + } + } + break; + + default: + { + bHandle = false; + PROFILE_PRINT_ERROR1("lns_cccd_update_cb Index = %d not found", index); + } + break; + + } + /* Notify Application. */ + if (pfn_lns_app_cb && (bHandle == true)) + { + callback_data.msg_type = SERVICE_CALLBACK_TYPE_INDIFICATION_NOTIFICATION; + pfn_lns_app_cb(service_id, (void *)&callback_data); + } + return; +} + +/** + * @brief LOCATION AND NAVIGATION Service Callbacks. +*/ +const T_FUN_GATT_SERVICE_CBS lns_cbs = +{ + lns_attr_read_cb, // Read callback function pointer + lns_attr_write_cb, // Write callback function pointer + lns_cccd_update_cb // CCCD update callback function pointer +}; + +/** + * @brief Add location and navigation service to the BLE stack database. + * + * + * @param[in] p_func Callback when service attribute was read, write or cccd update. + * @return Service id generated by the BLE stack: @ref T_SERVER_ID. + * @retval 0xFF Operation failure. + * @retval others Service id assigned by stack. + * + * <b>Example usage</b> + * \code{.c} + void profile_init() + { + server_init(1); + lns_id = lns_add_service(app_handle_profile_message); + } + * \endcode + */ +uint8_t lns_add_service(void *p_func) +{ + uint8_t service_id; + if (false == server_add_service(&service_id, + (uint8_t *)lns_attr_tbl, + lns_attr_tbl_size, + lns_cbs)) + { + PROFILE_PRINT_ERROR1("lns_add_service: ServiceId %d", service_id); + service_id = 0xff; + } + pfn_lns_app_cb = (P_FUN_SERVER_GENERAL_CB)p_func; + return service_id; +} + + |