/** ***************************************************************************************** * Copyright(c) 2017, Realtek Semiconductor Corporation. All rights reserved. ***************************************************************************************** * @file peripheral_app.c * @brief This file handles BLE peripheral application routines. * @author jane * @date 2017-06-06 * @version v1.0 ************************************************************************************** * @attention *

© COPYRIGHT 2017 Realtek Semiconductor Corporation

************************************************************************************** */ /*============================================================================* * Header Files *============================================================================*/ #include #include #include #include #include #include #if F_BT_ANCS_CLIENT_SUPPORT #include #include "ancs.h" #endif #include #include #include "trace.h" #include "app.h" #include "io_uart.h" /** @defgroup PERIPH_APP Peripheral Application * @brief This file handles BLE peripheral application routines. * @{ */ /*============================================================================* * Variables *============================================================================*/ /** @addtogroup PERIPH_SEVER_CALLBACK Profile Server Callback Event Handler * @brief Handle profile server callback event * @{ */ T_SERVER_ID simp_srv_id; /**< Simple ble service id*/ T_SERVER_ID bas_srv_id; /**< Battery service id */ /** @} */ /* End of group PERIPH_SEVER_CALLBACK */ /** @defgroup PERIPH_GAP_MSG GAP Message Handler * @brief Handle GAP Message * @{ */ T_GAP_DEV_STATE gap_dev_state = {0, 0, 0, 0}; /**< GAP device state */ T_GAP_CONN_STATE gap_conn_state = GAP_CONN_STATE_DISCONNECTED; /**< GAP connection state */ /*============================================================================* * Functions *============================================================================*/ void app_handle_gap_msg(T_IO_MSG *p_gap_msg); /** * @brief All the application messages are pre-handled in this function * @note All the IO MSGs are sent to this function, then the event handling * function shall be called according to the MSG type. * @param[in] io_msg IO message data * @return void */ void app_handle_io_msg(T_IO_MSG io_msg) { uint16_t msg_type = io_msg.type; switch (msg_type) { case IO_MSG_TYPE_BT_STATUS: { app_handle_gap_msg(&io_msg); } break; #if F_BT_ANCS_CLIENT_SUPPORT case IO_MSG_TYPE_ANCS: { ancs_handle_msg(&io_msg); } break; #endif case IO_MSG_TYPE_UART: { APP_PRINT_INFO0("[app] app_handle_io_msg: UART data msg."); io_handle_uart_msg(&io_msg); //Add user code here! } break; default: break; } } /** * @brief Handle msg GAP_MSG_LE_DEV_STATE_CHANGE * @note All the gap device state events are pre-handled in this function. * Then the event handling function shall be called according to the new_state * @param[in] new_state New gap device state * @param[in] cause GAP device state change cause * @return void */ void app_handle_dev_state_evt(T_GAP_DEV_STATE new_state, uint16_t cause) { APP_PRINT_INFO3("app_handle_dev_state_evt: init state %d, adv state %d, cause 0x%x", new_state.gap_init_state, new_state.gap_adv_state, cause); if (gap_dev_state.gap_init_state != new_state.gap_init_state) { if (new_state.gap_init_state == GAP_INIT_STATE_STACK_READY) { APP_PRINT_INFO0("GAP stack ready"); /*stack ready*/ // le_adv_start(); } } if (gap_dev_state.gap_adv_state != new_state.gap_adv_state) { if (new_state.gap_adv_state == GAP_ADV_STATE_IDLE) { if (new_state.gap_adv_sub_state == GAP_ADV_TO_IDLE_CAUSE_CONN) { APP_PRINT_INFO0("GAP adv stoped: because connection created"); } else { APP_PRINT_INFO0("GAP adv stoped"); } } else if (new_state.gap_adv_state == GAP_ADV_STATE_ADVERTISING) { APP_PRINT_INFO0("GAP adv start"); } } gap_dev_state = new_state; } /** * @brief Handle msg GAP_MSG_LE_CONN_STATE_CHANGE * @note All the gap conn state events are pre-handled in this function. * Then the event handling function shall be called according to the new_state * @param[in] conn_id Connection ID * @param[in] new_state New gap connection state * @param[in] disc_cause Use this cause when new_state is GAP_CONN_STATE_DISCONNECTED * @return void */ void app_handle_conn_state_evt(uint8_t conn_id, T_GAP_CONN_STATE new_state, uint16_t disc_cause) { APP_PRINT_INFO4("app_handle_conn_state_evt: conn_id %d old_state %d new_state %d, disc_cause 0x%x", conn_id, gap_conn_state, new_state, disc_cause); switch (new_state) { case GAP_CONN_STATE_DISCONNECTED: { if ((disc_cause != (HCI_ERR | HCI_ERR_REMOTE_USER_TERMINATE)) && (disc_cause != (HCI_ERR | HCI_ERR_LOCAL_HOST_TERMINATE))) { APP_PRINT_ERROR1("app_handle_conn_state_evt: connection lost cause 0x%x", disc_cause); } le_adv_start(); } break; case GAP_CONN_STATE_CONNECTED: { uint16_t conn_interval; uint16_t conn_latency; uint16_t conn_supervision_timeout; uint8_t remote_bd[6]; T_GAP_REMOTE_ADDR_TYPE remote_bd_type; le_get_conn_param(GAP_PARAM_CONN_INTERVAL, &conn_interval, conn_id); le_get_conn_param(GAP_PARAM_CONN_LATENCY, &conn_latency, conn_id); le_get_conn_param(GAP_PARAM_CONN_TIMEOUT, &conn_supervision_timeout, conn_id); le_get_conn_addr(conn_id, remote_bd, &remote_bd_type); APP_PRINT_INFO5("GAP_CONN_STATE_CONNECTED:remote_bd %s, remote_addr_type %d, conn_interval 0x%x, conn_latency 0x%x, conn_supervision_timeout 0x%x", TRACE_BDADDR(remote_bd), remote_bd_type, conn_interval, conn_latency, conn_supervision_timeout); } break; default: break; } gap_conn_state = new_state; } /** * @brief Handle msg GAP_MSG_LE_AUTHEN_STATE_CHANGE * @note All the gap authentication state events are pre-handled in this function. * Then the event handling function shall be called according to the new_state * @param[in] conn_id Connection ID * @param[in] new_state New authentication state * @param[in] cause Use this cause when new_state is GAP_AUTHEN_STATE_COMPLETE * @return void */ void app_handle_authen_state_evt(uint8_t conn_id, uint8_t new_state, uint16_t cause) { APP_PRINT_INFO2("app_handle_authen_state_evt:conn_id %d, cause 0x%x", conn_id, cause); switch (new_state) { case GAP_AUTHEN_STATE_STARTED: { APP_PRINT_INFO0("app_handle_authen_state_evt: GAP_AUTHEN_STATE_STARTED"); } break; case GAP_AUTHEN_STATE_COMPLETE: { if (cause == GAP_SUCCESS) { #if F_BT_ANCS_CLIENT_SUPPORT ancs_start_discovery(conn_id); #endif APP_PRINT_INFO0("app_handle_authen_state_evt: GAP_AUTHEN_STATE_COMPLETE pair success"); } else { APP_PRINT_INFO0("app_handle_authen_state_evt: GAP_AUTHEN_STATE_COMPLETE pair failed"); } } break; default: { APP_PRINT_ERROR1("app_handle_authen_state_evt: unknown newstate %d", new_state); } break; } } /** * @brief Handle msg GAP_MSG_LE_CONN_MTU_INFO * @note This msg is used to inform APP that exchange mtu procedure is completed. * @param[in] conn_id Connection ID * @param[in] mtu_size New mtu size * @return void */ void app_handle_conn_mtu_info_evt(uint8_t conn_id, uint16_t mtu_size) { APP_PRINT_INFO2("app_handle_conn_mtu_info_evt: conn_id %d, mtu_size %d", conn_id, mtu_size); } /** * @brief Handle msg GAP_MSG_LE_CONN_PARAM_UPDATE * @note All the connection parameter update change events are pre-handled in this function. * @param[in] conn_id Connection ID * @param[in] status New update state * @param[in] cause Use this cause when status is GAP_CONN_PARAM_UPDATE_STATUS_FAIL * @return void */ void app_handle_conn_param_update_evt(uint8_t conn_id, uint8_t status, uint16_t cause) { switch (status) { case GAP_CONN_PARAM_UPDATE_STATUS_SUCCESS: { uint16_t conn_interval; uint16_t conn_slave_latency; uint16_t conn_supervision_timeout; le_get_conn_param(GAP_PARAM_CONN_INTERVAL, &conn_interval, conn_id); le_get_conn_param(GAP_PARAM_CONN_LATENCY, &conn_slave_latency, conn_id); le_get_conn_param(GAP_PARAM_CONN_TIMEOUT, &conn_supervision_timeout, conn_id); APP_PRINT_INFO3("app_handle_conn_param_update_evt update success:conn_interval 0x%x, conn_slave_latency 0x%x, conn_supervision_timeout 0x%x", conn_interval, conn_slave_latency, conn_supervision_timeout); } break; case GAP_CONN_PARAM_UPDATE_STATUS_FAIL: { APP_PRINT_ERROR1("app_handle_conn_param_update_evt update failed: cause 0x%x", cause); } break; case GAP_CONN_PARAM_UPDATE_STATUS_PENDING: { APP_PRINT_INFO0("app_handle_conn_param_update_evt update pending."); } break; default: break; } } /** * @brief All the BT GAP MSG are pre-handled in this function. * @note Then the event handling function shall be called according to the * subtype of T_IO_MSG * @param[in] p_gap_msg Pointer to GAP msg * @return void */ void app_handle_gap_msg(T_IO_MSG *p_gap_msg) { T_LE_GAP_MSG gap_msg; uint8_t conn_id; memcpy(&gap_msg, &p_gap_msg->u.param, sizeof(p_gap_msg->u.param)); APP_PRINT_TRACE1("app_handle_gap_msg: subtype %d", p_gap_msg->subtype); switch (p_gap_msg->subtype) { case GAP_MSG_LE_DEV_STATE_CHANGE: { app_handle_dev_state_evt(gap_msg.msg_data.gap_dev_state_change.new_state, gap_msg.msg_data.gap_dev_state_change.cause); } break; case GAP_MSG_LE_CONN_STATE_CHANGE: { app_handle_conn_state_evt(gap_msg.msg_data.gap_conn_state_change.conn_id, (T_GAP_CONN_STATE)gap_msg.msg_data.gap_conn_state_change.new_state, gap_msg.msg_data.gap_conn_state_change.disc_cause); } break; case GAP_MSG_LE_CONN_MTU_INFO: { app_handle_conn_mtu_info_evt(gap_msg.msg_data.gap_conn_mtu_info.conn_id, gap_msg.msg_data.gap_conn_mtu_info.mtu_size); } break; case GAP_MSG_LE_CONN_PARAM_UPDATE: { app_handle_conn_param_update_evt(gap_msg.msg_data.gap_conn_param_update.conn_id, gap_msg.msg_data.gap_conn_param_update.status, gap_msg.msg_data.gap_conn_param_update.cause); } break; case GAP_MSG_LE_AUTHEN_STATE_CHANGE: { app_handle_authen_state_evt(gap_msg.msg_data.gap_authen_state.conn_id, gap_msg.msg_data.gap_authen_state.new_state, gap_msg.msg_data.gap_authen_state.status); } break; case GAP_MSG_LE_BOND_JUST_WORK: { conn_id = gap_msg.msg_data.gap_bond_just_work_conf.conn_id; le_bond_just_work_confirm(conn_id, GAP_CFM_CAUSE_ACCEPT); APP_PRINT_INFO0("GAP_MSG_LE_BOND_JUST_WORK"); } break; case GAP_MSG_LE_BOND_PASSKEY_DISPLAY: { uint32_t display_value = 0; conn_id = gap_msg.msg_data.gap_bond_passkey_display.conn_id; le_bond_get_display_key(conn_id, &display_value); APP_PRINT_INFO1("GAP_MSG_LE_BOND_PASSKEY_DISPLAY:passkey %d", display_value); le_bond_passkey_display_confirm(conn_id, GAP_CFM_CAUSE_ACCEPT); } break; case GAP_MSG_LE_BOND_USER_CONFIRMATION: { uint32_t display_value = 0; conn_id = gap_msg.msg_data.gap_bond_user_conf.conn_id; le_bond_get_display_key(conn_id, &display_value); APP_PRINT_INFO1("GAP_MSG_LE_BOND_USER_CONFIRMATION: passkey %d", display_value); le_bond_user_confirm(conn_id, GAP_CFM_CAUSE_ACCEPT); } break; case GAP_MSG_LE_BOND_PASSKEY_INPUT: { uint32_t passkey = 888888; conn_id = gap_msg.msg_data.gap_bond_passkey_input.conn_id; APP_PRINT_INFO1("GAP_MSG_LE_BOND_PASSKEY_INPUT: conn_id %d", conn_id); le_bond_passkey_input_confirm(conn_id, passkey, GAP_CFM_CAUSE_ACCEPT); } break; case GAP_MSG_LE_BOND_OOB_INPUT: { uint8_t oob_data[GAP_OOB_LEN] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; conn_id = gap_msg.msg_data.gap_bond_oob_input.conn_id; APP_PRINT_INFO0("GAP_MSG_LE_BOND_OOB_INPUT"); le_bond_set_param(GAP_PARAM_BOND_OOB_DATA, GAP_OOB_LEN, oob_data); le_bond_oob_input_confirm(conn_id, GAP_CFM_CAUSE_ACCEPT); } break; default: APP_PRINT_ERROR1("app_handle_gap_msg: unknown subtype %d", p_gap_msg->subtype); break; } } /** @} */ /* End of group PERIPH_GAP_MSG */ /** @defgroup PERIPH_GAP_CALLBACK GAP Callback Event Handler * @brief Handle GAP callback event * @{ */ /** * @brief Callback for gap le to notify app * @param[in] cb_type callback msy type @ref GAP_LE_MSG_Types. * @param[in] p_cb_data point to callback data @ref T_LE_CB_DATA. * @retval result @ref T_APP_RESULT */ T_APP_RESULT app_gap_callback(uint8_t cb_type, void *p_cb_data) { T_APP_RESULT result = APP_RESULT_SUCCESS; T_LE_CB_DATA *p_data = (T_LE_CB_DATA *)p_cb_data; switch (cb_type) { case GAP_MSG_LE_DATA_LEN_CHANGE_INFO: APP_PRINT_INFO3("GAP_MSG_LE_DATA_LEN_CHANGE_INFO: conn_id %d, tx octets 0x%x, max_tx_time 0x%x", p_data->p_le_data_len_change_info->conn_id, p_data->p_le_data_len_change_info->max_tx_octets, p_data->p_le_data_len_change_info->max_tx_time); break; case GAP_MSG_LE_MODIFY_WHITE_LIST: APP_PRINT_INFO2("GAP_MSG_LE_MODIFY_WHITE_LIST: operation %d, cause 0x%x", p_data->p_le_modify_white_list_rsp->operation, p_data->p_le_modify_white_list_rsp->cause); break; default: APP_PRINT_ERROR1("app_gap_callback: unhandled cb_type 0x%x", cb_type); break; } return result; } /** @} */ /* End of group PERIPH_GAP_CALLBACK */ /** @defgroup PERIPH_SEVER_CALLBACK Profile Server Callback Event Handler * @brief Handle profile server callback event * @{ */ /** * @brief All the BT Profile service callback events are handled in this function * @note Then the event handling function shall be called according to the * service_id * @param service_id Profile service ID * @param p_data Pointer to callback data * @return T_APP_RESULT, which indicates the function call is successful or not * @retval APP_RESULT_SUCCESS Function run successfully * @retval others Function run failed, and return number indicates the reason */ T_APP_RESULT app_profile_callback(T_SERVER_ID service_id, void *p_data) { T_APP_RESULT app_result = APP_RESULT_SUCCESS; if (service_id == SERVICE_PROFILE_GENERAL_ID) { T_SERVER_APP_CB_DATA *p_param = (T_SERVER_APP_CB_DATA *)p_data; switch (p_param->eventId) { case PROFILE_EVT_SRV_REG_COMPLETE:// srv register result event. APP_PRINT_INFO1("PROFILE_EVT_SRV_REG_COMPLETE: result %d", p_param->event_data.service_reg_result); break; case PROFILE_EVT_SEND_DATA_COMPLETE: APP_PRINT_INFO5("PROFILE_EVT_SEND_DATA_COMPLETE: conn_id %d, cause 0x%x, service_id %d, attrib_idx 0x%x, credits %d", p_param->event_data.send_data_result.conn_id, p_param->event_data.send_data_result.cause, p_param->event_data.send_data_result.service_id, p_param->event_data.send_data_result.attrib_idx, p_param->event_data.send_data_result.credits); if (p_param->event_data.send_data_result.cause == GAP_SUCCESS) { APP_PRINT_INFO0("PROFILE_EVT_SEND_DATA_COMPLETE success"); } else { APP_PRINT_ERROR0("PROFILE_EVT_SEND_DATA_COMPLETE failed"); } break; default: break; } } else if (service_id == simp_srv_id) { TSIMP_CALLBACK_DATA *p_simp_cb_data = (TSIMP_CALLBACK_DATA *)p_data; switch (p_simp_cb_data->msg_type) { case SERVICE_CALLBACK_TYPE_INDIFICATION_NOTIFICATION: { switch (p_simp_cb_data->msg_data.notification_indification_index) { case SIMP_NOTIFY_INDICATE_V3_ENABLE: { APP_PRINT_INFO0("SIMP_NOTIFY_INDICATE_V3_ENABLE"); } break; case SIMP_NOTIFY_INDICATE_V3_DISABLE: { APP_PRINT_INFO0("SIMP_NOTIFY_INDICATE_V3_DISABLE"); } break; case SIMP_NOTIFY_INDICATE_V4_ENABLE: { APP_PRINT_INFO0("SIMP_NOTIFY_INDICATE_V4_ENABLE"); } break; case SIMP_NOTIFY_INDICATE_V4_DISABLE: { APP_PRINT_INFO0("SIMP_NOTIFY_INDICATE_V4_DISABLE"); } break; default: break; } } break; case SERVICE_CALLBACK_TYPE_READ_CHAR_VALUE: { if (p_simp_cb_data->msg_data.read_value_index == SIMP_READ_V1) { uint8_t value[2] = {0x01, 0x02}; APP_PRINT_INFO0("SIMP_READ_V1"); simp_ble_service_set_parameter(SIMPLE_BLE_SERVICE_PARAM_V1_READ_CHAR_VAL, 2, &value); } } break; case SERVICE_CALLBACK_TYPE_WRITE_CHAR_VALUE: { switch (p_simp_cb_data->msg_data.write.opcode) { case SIMP_WRITE_V2: { APP_PRINT_INFO2("SIMP_WRITE_V2: write type %d, len %d", p_simp_cb_data->msg_data.write.write_type, p_simp_cb_data->msg_data.write.len); } break; default: break; } } break; default: break; } } else if (service_id == bas_srv_id) { T_BAS_CALLBACK_DATA *p_bas_cb_data = (T_BAS_CALLBACK_DATA *)p_data; switch (p_bas_cb_data->msg_type) { case SERVICE_CALLBACK_TYPE_INDIFICATION_NOTIFICATION: { switch (p_bas_cb_data->msg_data.notification_indification_index) { case BAS_NOTIFY_BATTERY_LEVEL_ENABLE: { APP_PRINT_INFO0("BAS_NOTIFY_BATTERY_LEVEL_ENABLE"); } break; case BAS_NOTIFY_BATTERY_LEVEL_DISABLE: { APP_PRINT_INFO0("BAS_NOTIFY_BATTERY_LEVEL_DISABLE"); } break; default: break; } } break; case SERVICE_CALLBACK_TYPE_READ_CHAR_VALUE: { if (p_bas_cb_data->msg_data.read_value_index == BAS_READ_BATTERY_LEVEL) { uint8_t battery_level = 90; APP_PRINT_INFO1("BAS_READ_BATTERY_LEVEL: battery_level %d", battery_level); bas_set_parameter(BAS_PARAM_BATTERY_LEVEL, 1, &battery_level); } } break; default: break; } } return app_result; } /** @} */ /* End of group PERIPH_SEVER_CALLBACK */ /** @} */ /* End of group PERIPH_APP */