diff options
Diffstat (limited to 'src/sample/ble_central')
-rw-r--r-- | src/sample/ble_central/app_flags.h | 38 | ||||
-rw-r--r-- | src/sample/ble_central/app_task.c | 109 | ||||
-rw-r--r-- | src/sample/ble_central/app_task.h | 27 | ||||
-rw-r--r-- | src/sample/ble_central/central_app.c | 928 | ||||
-rw-r--r-- | src/sample/ble_central/central_app.h | 69 | ||||
-rw-r--r-- | src/sample/ble_central/link_mgr.c | 139 | ||||
-rw-r--r-- | src/sample/ble_central/link_mgr.h | 111 | ||||
-rw-r--r-- | src/sample/ble_central/main.c | 178 | ||||
-rw-r--r-- | src/sample/ble_central/user_cmd.c | 1066 | ||||
-rw-r--r-- | src/sample/ble_central/user_cmd.h | 31 |
10 files changed, 2696 insertions, 0 deletions
diff --git a/src/sample/ble_central/app_flags.h b/src/sample/ble_central/app_flags.h new file mode 100644 index 0000000..a1a5956 --- /dev/null +++ b/src/sample/ble_central/app_flags.h @@ -0,0 +1,38 @@ +/** +***************************************************************************************** +* Copyright(c) 2017, Realtek Semiconductor Corporation. All rights reserved. +***************************************************************************************** + * @file app_flags.h + * @brief This file is used to config app functions. + * @author jane + * @date 2017-06-06 + * @version v1.0 + ************************************************************************************** + * @attention + * <h2><center>© COPYRIGHT 2017 Realtek Semiconductor Corporation</center></h2> + ************************************************************************************** + */ +#ifndef _APP_FLAGS_H_ +#define _APP_FLAGS_H_ + +#include "upperstack_config.h" + +/** @defgroup CENTRAL_Config Central App Configuration + * @brief This file is used to config app functions. + * @{ + */ +/*============================================================================* + * Constants + *============================================================================*/ + +/** @brief Config APP LE link number */ +#define APP_MAX_LINKS 2 +/** @brief Config GATT services storage: 0-Not save, 1-Save to flash + * + * If configure to 1, the GATT services discovery results will save to the flash. + */ +#define F_BT_GATT_SRV_HANDLE_STORAGE 0 + +/** @} */ /* End of group CENTRAL_Config */ + +#endif diff --git a/src/sample/ble_central/app_task.c b/src/sample/ble_central/app_task.c new file mode 100644 index 0000000..f8330a2 --- /dev/null +++ b/src/sample/ble_central/app_task.c @@ -0,0 +1,109 @@ +/** +***************************************************************************************** +* Copyright(c) 2017, Realtek Semiconductor Corporation. All rights reserved. +***************************************************************************************** + * @file app_task.c + * @brief Routines to create App task and handle events & messages + * @author jane + * @date 2017-06-02 + * @version v1.0 + ************************************************************************************** + * @attention + * <h2><center>© COPYRIGHT 2017 Realtek Semiconductor Corporation</center></h2> + ************************************************************************************** + */ + +/*============================================================================* + * Header Files + *============================================================================*/ +#include <os_msg.h> +#include <os_task.h> +#include <gap.h> +#include <gap_le.h> +#include <trace.h> +#include <app_task.h> +#include <app_msg.h> +#include <app_task.h> +#include <central_app.h> +#include <data_uart.h> +#include <user_cmd_parse.h> +#include <user_cmd.h> + +/** @defgroup CENTRAL_APP_TASK Central App Task + * @brief This file handles the implementation of application task related functions. + * + * Create App task and handle events & messages + * @{ + */ +/*============================================================================* + * Macros + *============================================================================*/ +#define APP_TASK_PRIORITY 1 //!< Task priorities +#define APP_TASK_STACK_SIZE 256 * 4 //!< Task stack size +#define MAX_NUMBER_OF_GAP_MESSAGE 0x20 //!< GAP message queue size +#define MAX_NUMBER_OF_IO_MESSAGE 0x20 //!< IO message queue size +#define MAX_NUMBER_OF_EVENT_MESSAGE (MAX_NUMBER_OF_GAP_MESSAGE + MAX_NUMBER_OF_IO_MESSAGE) //!< Event message queue size + +/*============================================================================* + * Variables + *============================================================================*/ +void *app_task_handle; //!< APP Task handle +void *evt_queue_handle; //!< Event queue handle +void *io_queue_handle; //!< IO queue handle + +/*============================================================================* + * Functions + *============================================================================*/ +void app_main_task(void *p_param); + +/** + * @brief Initialize App task + * @return void + */ +void app_task_init() +{ + os_task_create(&app_task_handle, "app", app_main_task, 0, APP_TASK_STACK_SIZE, + APP_TASK_PRIORITY); +} + +/** + * @brief App task to handle events & messages + * @param[in] p_param Parameters sending to the task + * @return void + */ +void app_main_task(void *p_param) +{ + uint8_t event; + + os_msg_queue_create(&io_queue_handle, MAX_NUMBER_OF_IO_MESSAGE, sizeof(T_IO_MSG)); + os_msg_queue_create(&evt_queue_handle, MAX_NUMBER_OF_EVENT_MESSAGE, sizeof(uint8_t)); + + gap_start_bt_stack(evt_queue_handle, io_queue_handle, MAX_NUMBER_OF_GAP_MESSAGE); + + data_uart_init(evt_queue_handle, io_queue_handle); + user_cmd_init(&user_cmd_if, "central"); + + driver_init(); + while (true) + { + if (os_msg_recv(evt_queue_handle, &event, 0xFFFFFFFF) == true) + { + if (event == EVENT_IO_TO_APP) + { + T_IO_MSG io_msg; + if (os_msg_recv(io_queue_handle, &io_msg, 0) == true) + { + app_handle_io_msg(io_msg); + } + } + else + { + gap_handle_msg(event); + } + } + } +} + +/** @} */ /* End of group CENTRAL_APP_TASK */ + + diff --git a/src/sample/ble_central/app_task.h b/src/sample/ble_central/app_task.h new file mode 100644 index 0000000..8dfcd2a --- /dev/null +++ b/src/sample/ble_central/app_task.h @@ -0,0 +1,27 @@ +/** +***************************************************************************************** +* Copyright(c) 2017, Realtek Semiconductor Corporation. All rights reserved. +***************************************************************************************** + * @file app_task.h + * @brief Routines to create App task and handle events & messages + * @author jane + * @date 2017-06-02 + * @version v1.0 + ************************************************************************************** + * @attention + * <h2><center>© COPYRIGHT 2017 Realtek Semiconductor Corporation</center></h2> + ************************************************************************************** + */ +#ifndef _APP_TASK_H_ +#define _APP_TASK_H_ + +extern void driver_init(void); + +/** + * @brief Initialize App task + * @return void + */ +void app_task_init(void); + +#endif + diff --git a/src/sample/ble_central/central_app.c b/src/sample/ble_central/central_app.c new file mode 100644 index 0000000..7304b17 --- /dev/null +++ b/src/sample/ble_central/central_app.c @@ -0,0 +1,928 @@ +/** +***************************************************************************************** +* Copyright(c) 2017, Realtek Semiconductor Corporation. All rights reserved. +***************************************************************************************** + * @file central_app.c + * @brief This file handles BLE central application routines. + * @author jane + * @date 2017-06-06 + * @version v1.0 + ************************************************************************************** + * @attention + * <h2><center>© COPYRIGHT 2017 Realtek Semiconductor Corporation</center></h2> + ************************************************************************************** + */ + +/*============================================================================* + * Header Files + *============================================================================*/ +#include <app_msg.h> +#include <string.h> +#include <trace.h> +#include <gap_scan.h> +#include <gap.h> +#include <gap_msg.h> +#include <gap_bond_le.h> +#include <central_app.h> +#include <link_mgr.h> +#include <user_cmd.h> +#include <user_cmd_parse.h> +#include <simple_ble_client.h> +#include <gaps_client.h> +#include <bas_client.h> + + +/** @defgroup CENTRAL_APP Central Application + * @brief This file handles BLE central application routines. + * @{ + */ +/*============================================================================* + * Variables + *============================================================================*/ +/** @addtogroup CENTRAL_CLIIENT_CALLBACK + * @{ + */ +T_CLIENT_ID simple_ble_client_id; /**< Simple ble service client id*/ +T_CLIENT_ID gaps_client_id; /**< gap service client id*/ +T_CLIENT_ID bas_client_id; /**< battery service client id*/ +/** @} */ /* End of group CENTRAL_CLIIENT_CALLBACK */ + +/** @defgroup CENTRAL_GAP_MSG GAP Message Handler + * @brief Handle GAP Message + * @{ + */ +T_GAP_DEV_STATE gap_dev_state = {0, 0, 0, 0}; /**< GAP device state */ +/*============================================================================* + * Functions + *============================================================================*/ +void app_discov_services(uint8_t conn_id, bool start); +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; + uint8_t rx_char; + + switch (msg_type) + { + case IO_MSG_TYPE_BT_STATUS: + { + app_handle_gap_msg(&io_msg); + } + break; + case IO_MSG_TYPE_UART: + /* We handle user command informations from Data UART in this branch. */ + rx_char = (uint8_t)io_msg.subtype; + user_cmd_collect(&user_cmd_if, &rx_char, sizeof(rx_char), user_cmd_table); + 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, scan state %d, cause 0x%x", + new_state.gap_init_state, + new_state.gap_scan_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) + { + uint8_t bt_addr[6]; + APP_PRINT_INFO0("GAP stack ready"); + /*stack ready*/ + gap_get_param(GAP_PARAM_BD_ADDR, bt_addr); + data_uart_print("local bd addr: 0x%2x:%2x:%2x:%2x:%2x:%2x\r\n", + bt_addr[5], + bt_addr[4], + bt_addr[3], + bt_addr[2], + bt_addr[1], + bt_addr[0]); + } + } + + if (gap_dev_state.gap_scan_state != new_state.gap_scan_state) + { + if (new_state.gap_scan_state == GAP_SCAN_STATE_IDLE) + { + APP_PRINT_INFO0("GAP scan stop"); + data_uart_print("GAP scan stop\r\n"); + } + else if (new_state.gap_scan_state == GAP_SCAN_STATE_SCANNING) + { + APP_PRINT_INFO0("GAP scan start"); + data_uart_print("GAP scan start\r\n"); + } + } + + 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) +{ + if (conn_id >= APP_MAX_LINKS) + { + return; + } + + APP_PRINT_INFO4("app_handle_conn_state_evt: conn_id %d, conn_state(%d -> %d), disc_cause 0x%x", + conn_id, app_link_table[conn_id].conn_state, new_state, disc_cause); + + app_link_table[conn_id].conn_state = new_state; + 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_ERROR2("app_handle_conn_state_evt: connection lost, conn_id %d, cause 0x%x", conn_id, + disc_cause); + } + + data_uart_print("Disconnect conn_id %d\r\n", conn_id); + memset(&app_link_table[conn_id], 0, sizeof(T_APP_LINK)); + } + break; + + case GAP_CONN_STATE_CONNECTED: + { + le_get_conn_addr(conn_id, app_link_table[conn_id].bd_addr, + &app_link_table[conn_id].bd_type); + data_uart_print("Connected success conn_id %d\r\n", conn_id); + } + break; + + default: + break; + + } +} + +/** + * @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) + { + data_uart_print("Pair success\r\n"); + APP_PRINT_INFO0("app_handle_authen_state_evt: GAP_AUTHEN_STATE_COMPLETE pair success"); + + } + else + { + data_uart_print("Pair failed: cause 0x%x\r\n", cause); + 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); + app_discov_services(conn_id, true); +} + +/** + * @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_INFO4("app_handle_conn_param_update_evt update success:conn_id %d, conn_interval 0x%x, conn_slave_latency 0x%x, conn_supervision_timeout 0x%x", + conn_id, conn_interval, conn_slave_latency, conn_supervision_timeout); + } + break; + + case GAP_CONN_PARAM_UPDATE_STATUS_FAIL: + { + APP_PRINT_ERROR2("app_handle_conn_param_update_evt update failed: conn_id %d, cause 0x%x", + conn_id, cause); + } + break; + + case GAP_CONN_PARAM_UPDATE_STATUS_PENDING: + { + APP_PRINT_INFO1("app_handle_conn_param_update_evt update pending: conn_id %d", conn_id); + } + 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_INFO2("GAP_MSG_LE_BOND_PASSKEY_DISPLAY: conn_id %d, passkey %d", + conn_id, display_value); + le_bond_passkey_display_confirm(conn_id, GAP_CFM_CAUSE_ACCEPT); + data_uart_print("GAP_MSG_LE_BOND_PASSKEY_DISPLAY: conn_id %d, passkey %d\r\n", + conn_id, + display_value); + } + 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_INFO2("GAP_MSG_LE_BOND_USER_CONFIRMATION: conn_id %d, passkey %d", + conn_id, display_value); + data_uart_print("GAP_MSG_LE_BOND_USER_CONFIRMATION: conn_id %d, passkey %d\r\n", + conn_id, + 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); + data_uart_print("GAP_MSG_LE_BOND_PASSKEY_INPUT: conn_id %d\r\n", 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_INFO1("GAP_MSG_LE_BOND_OOB_INPUT: conn_id %d", conn_id); + 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 CENTRAL_GAP_MSG */ + +/** @defgroup CENTRAL_SCAN_MGR Scan Information manager + * @brief Scan Information manager + * @{ + */ +/** + * @brief Use 16 bit uuid to filter scan information + * @param[in] uuid 16 bit UUID. + * @param[in] scan_info point to scan information data. + * @return filter result + * @retval true found success + * @retval false not found + */ +bool filter_scan_info_by_uuid(uint16_t uuid, T_LE_SCAN_INFO *scan_info) +{ + uint8_t buffer[32]; + uint8_t pos = 0; + + while (pos < scan_info->data_len) + { + /* Length of the AD structure. */ + uint8_t length = scan_info->data[pos++]; + uint8_t type; + + if ((length < 1) || (length >= 31)) + { + return false; + } + + if ((length > 0x01) && ((pos + length) <= 31)) + { + /* Copy the AD Data to buffer. */ + memcpy(buffer, scan_info->data + pos + 1, length - 1); + /* AD Type, one octet. */ + type = scan_info->data[pos]; + + switch (type) + { + case GAP_ADTYPE_16BIT_MORE: + case GAP_ADTYPE_16BIT_COMPLETE: + case GAP_ADTYPE_SERVICES_LIST_16BIT: + { + uint16_t *p_uuid = (uint16_t *)(buffer); + uint8_t i = length - 1; + + while (i >= 2) + { + APP_PRINT_INFO2(" AD Data: UUID16 List Item %d = 0x%x", i / 2, *p_uuid); + if (*p_uuid == uuid) + { + return true; + } + p_uuid++; + i -= 2; + } + } + break; + + default: + break; + } + } + + pos += length; + } + return false; +} +/** @} */ /* End of group CENTRAL_SCAN_MGR */ + +/** @defgroup CENTRAL_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_SCAN_INFO: + APP_PRINT_INFO5("GAP_MSG_LE_SCAN_INFO:adv_type 0x%x, bd_addr %s, remote_addr_type %d, rssi %d, data_len %d", + p_data->p_le_scan_info->adv_type, + TRACE_BDADDR(p_data->p_le_scan_info->bd_addr), + p_data->p_le_scan_info->remote_addr_type, + p_data->p_le_scan_info->rssi, + p_data->p_le_scan_info->data_len); + /* User can split interested information by using the function as follow. */ + if (filter_scan_info_by_uuid(GATT_UUID_SIMPLE_PROFILE, p_data->p_le_scan_info)) + { + APP_PRINT_INFO0("Found simple ble service"); + link_mgr_add_device(p_data->p_le_scan_info->bd_addr, p_data->p_le_scan_info->remote_addr_type); + } + /* If you want to parse the scan info, please reference function app_parse_scan_info in observer app. */ + break; + + case GAP_MSG_LE_CONN_UPDATE_IND: + APP_PRINT_INFO5("GAP_MSG_LE_CONN_UPDATE_IND: conn_id %d, conn_interval_max 0x%x, conn_interval_min 0x%x, conn_latency 0x%x,supervision_timeout 0x%x", + p_data->p_le_conn_update_ind->conn_id, + p_data->p_le_conn_update_ind->conn_interval_max, + p_data->p_le_conn_update_ind->conn_interval_min, + p_data->p_le_conn_update_ind->conn_latency, + p_data->p_le_conn_update_ind->supervision_timeout); + /* if reject the proposed connection parameter from peer device, use APP_RESULT_REJECT. */ + result = APP_RESULT_ACCEPT; + break; + + default: + APP_PRINT_ERROR1("app_gap_callback: unhandled cb_type 0x%x", cb_type); + break; + } + return result; +} +/** @} */ /* End of group CENTRAL_GAP_CALLBACK */ + +/** @defgroup CENTRAL_SRV_DIS GATT Services discovery and storage + * @brief GATT Services discovery and storage + * @{ + */ +/** + * @brief Discovery GATT services + * @param conn_id connection ID. + * @param start first call. true - first call this function after conncection, false - not first + * @retval None + */ +void app_discov_services(uint8_t conn_id, bool start) +{ + if (app_link_table[conn_id].conn_state != GAP_CONN_STATE_CONNECTED) + { + APP_PRINT_ERROR1("app_discov_services: conn_id %d not connected ", conn_id); + return; + } + if (start) + { +#if F_BT_GATT_SRV_HANDLE_STORAGE + bool is_disc = true; + T_APP_SRVS_HDL_TABLE app_srvs_table; + if (app_load_srvs_hdl_table(&app_srvs_table) == 0) + { + if ((app_srvs_table.srv_found_flags != 0) && + (app_srvs_table.bd_type == app_link_table[conn_id].bd_type) && + (memcmp(app_srvs_table.bd_addr, app_link_table[conn_id].bd_addr, GAP_BD_ADDR_LEN) == 0)) + { + APP_PRINT_INFO1("app_discov_services: load from flash, srv_found_flags 0x%x", + app_srvs_table.srv_found_flags); + app_link_table[conn_id].srv_found_flags = app_srvs_table.srv_found_flags; + if (app_srvs_table.srv_found_flags & APP_DISCOV_GAPS_FLAG) + { + gaps_set_hdl_cache(conn_id, app_srvs_table.gaps_hdl_cache, sizeof(uint16_t) * HDL_GAPS_CACHE_LEN); + } + if (app_srvs_table.srv_found_flags & APP_DISCOV_SIMP_FLAG) + { + simp_ble_client_set_hdl_cache(conn_id, app_srvs_table.simp_hdl_cache, + sizeof(uint16_t) * HDL_SIMBLE_CACHE_LEN); + } + if (app_srvs_table.srv_found_flags & APP_DISCOV_BAS_FLAG) + { + bas_set_hdl_cache(conn_id, app_srvs_table.bas_hdl_cache, sizeof(uint16_t) * HDL_BAS_CACHE_LEN); + } + is_disc = false; + } + } + else + { + APP_PRINT_ERROR0("app_load_srvs_hdl_table: failed"); + } + + if (is_disc) + { + if (gaps_start_discovery(conn_id) == false) + { + APP_PRINT_ERROR1("app_discov_services: discover gaps failed conn_id %d", conn_id); + } + } +#else + if (gaps_start_discovery(conn_id) == false) + { + APP_PRINT_ERROR1("app_discov_services: discover gaps failed conn_id %d", conn_id); + } +#endif + return; + } + if ((app_link_table[conn_id].discovered_flags & APP_DISCOV_SIMP_FLAG) == 0) + { + if (simp_ble_client_start_discovery(conn_id) == false) + { + APP_PRINT_ERROR1("app_discov_services: discover simp failed conn_id %d", conn_id); + } + } + else if ((app_link_table[conn_id].discovered_flags & APP_DISCOV_BAS_FLAG) == 0) + { + if (bas_start_discovery(conn_id) == false) + { + APP_PRINT_ERROR1("app_discov_services: discover bas failed conn_id %d", conn_id); + } + } + else + { + APP_PRINT_INFO2("app_discov_services: discover complete, conn_id %d, srv_found_flags 0x%x", + conn_id, app_link_table[conn_id].srv_found_flags); +#if F_BT_GATT_SRV_HANDLE_STORAGE + if (app_link_table[conn_id].srv_found_flags != 0) + { + T_APP_SRVS_HDL_TABLE app_srvs_table; + memset(&app_srvs_table, 0, sizeof(T_APP_SRVS_HDL_TABLE)); + app_srvs_table.bd_type = app_link_table[conn_id].bd_type; + app_srvs_table.srv_found_flags = app_link_table[conn_id].srv_found_flags; + memcpy(app_srvs_table.bd_addr, app_link_table[conn_id].bd_addr, GAP_BD_ADDR_LEN); + gaps_get_hdl_cache(conn_id, app_srvs_table.gaps_hdl_cache, sizeof(uint16_t) * HDL_GAPS_CACHE_LEN); + simp_ble_client_get_hdl_cache(conn_id, app_srvs_table.simp_hdl_cache, + sizeof(uint16_t) * HDL_SIMBLE_CACHE_LEN); + bas_get_hdl_cache(conn_id, app_srvs_table.bas_hdl_cache, sizeof(uint16_t) * HDL_BAS_CACHE_LEN); + if (app_save_srvs_hdl_table(&app_srvs_table) != 0) + { + APP_PRINT_ERROR0("app_save_srvs_hdl_table: failed"); + } + } +#endif + } + + return; +} +/** @} */ /* End of group CENTRAL_SRV_DIS */ + +/** @defgroup CENTRAL_CLIIENT_CALLBACK Profile Client Callback Event Handler + * @brief Handle profile client callback event + * @{ + */ + +/** + * @brief Callback will be called when data sent from profile client layer. + * @param client_id the ID distinguish which module sent the data. + * @param conn_id connection ID. + * @param p_data pointer to data. + * @retval result @ref T_APP_RESULT + */ +T_APP_RESULT app_client_callback(T_CLIENT_ID client_id, uint8_t conn_id, void *p_data) +{ + T_APP_RESULT result = APP_RESULT_SUCCESS; + APP_PRINT_INFO2("app_client_callback: client_id %d, conn_id %d", + client_id, conn_id); + if (client_id == gaps_client_id) + { + T_GAPS_CLIENT_CB_DATA *p_gaps_cb_data = (T_GAPS_CLIENT_CB_DATA *)p_data; + switch (p_gaps_cb_data->cb_type) + { + case GAPS_CLIENT_CB_TYPE_DISC_STATE: + switch (p_gaps_cb_data->cb_content.disc_state) + { + case DISC_GAPS_DONE: + app_link_table[conn_id].discovered_flags |= APP_DISCOV_GAPS_FLAG; + app_link_table[conn_id].srv_found_flags |= APP_DISCOV_GAPS_FLAG; + app_discov_services(conn_id, false); + /* Discovery Simple BLE service procedure successfully done. */ + APP_PRINT_INFO0("app_client_callback: discover gaps procedure done."); + break; + case DISC_GAPS_FAILED: + app_link_table[conn_id].discovered_flags |= APP_DISCOV_GAPS_FLAG; + app_discov_services(conn_id, false); + /* Discovery Request failed. */ + APP_PRINT_INFO0("app_client_callback: discover gaps request failed."); + break; + default: + break; + } + break; + case GAPS_CLIENT_CB_TYPE_READ_RESULT: + switch (p_gaps_cb_data->cb_content.read_result.type) + { + case GAPS_READ_DEVICE_NAME: + if (p_gaps_cb_data->cb_content.read_result.cause == GAP_SUCCESS) + { + APP_PRINT_INFO1("GAPS_READ_DEVICE_NAME: device name %s.", + TRACE_STRING(p_gaps_cb_data->cb_content.read_result.data.device_name.p_value)); + } + else + { + APP_PRINT_INFO1("GAPS_READ_DEVICE_NAME: failded cause 0x%x", + p_gaps_cb_data->cb_content.read_result.cause); + } + break; + case GAPS_READ_APPEARANCE: + if (p_gaps_cb_data->cb_content.read_result.cause == GAP_SUCCESS) + { + APP_PRINT_INFO1("GAPS_READ_APPEARANCE: appearance %d", + p_gaps_cb_data->cb_content.read_result.data.appearance); + } + else + { + APP_PRINT_INFO1("GAPS_READ_APPEARANCE: failded cause 0x%x", + p_gaps_cb_data->cb_content.read_result.cause); + } + break; + case GAPS_READ_CENTRAL_ADDR_RESOLUTION: + if (p_gaps_cb_data->cb_content.read_result.cause == GAP_SUCCESS) + { + APP_PRINT_INFO1("GAPS_READ_CENTRAL_ADDR_RESOLUTION: central_addr_res %d", + p_gaps_cb_data->cb_content.read_result.data.central_addr_res); + } + else + { + APP_PRINT_INFO1("GAPS_READ_CENTRAL_ADDR_RESOLUTION: failded cause 0x%x", + p_gaps_cb_data->cb_content.read_result.cause); + } + break; + default: + break; + } + break; + + default: + break; + } + } + else if (client_id == simple_ble_client_id) + { + T_SIMP_CLIENT_CB_DATA *p_simp_client_cb_data = (T_SIMP_CLIENT_CB_DATA *)p_data; + uint16_t value_size; + uint8_t *p_value; + switch (p_simp_client_cb_data->cb_type) + { + case SIMP_CLIENT_CB_TYPE_DISC_STATE: + switch (p_simp_client_cb_data->cb_content.disc_state) + { + case DISC_SIMP_DONE: + /* Discovery Simple BLE service procedure successfully done. */ + app_link_table[conn_id].discovered_flags |= APP_DISCOV_SIMP_FLAG; + app_link_table[conn_id].srv_found_flags |= APP_DISCOV_SIMP_FLAG; + app_discov_services(conn_id, false); + APP_PRINT_INFO0("app_client_callback: discover simp procedure done."); + break; + case DISC_SIMP_FAILED: + /* Discovery Request failed. */ + app_link_table[conn_id].discovered_flags |= APP_DISCOV_SIMP_FLAG; + app_discov_services(conn_id, false); + APP_PRINT_INFO0("app_client_callback: discover simp request failed."); + break; + default: + break; + } + break; + case SIMP_CLIENT_CB_TYPE_READ_RESULT: + switch (p_simp_client_cb_data->cb_content.read_result.type) + { + case SIMP_READ_V1_READ: + if (p_simp_client_cb_data->cb_content.read_result.cause == GAP_SUCCESS) + { + value_size = p_simp_client_cb_data->cb_content.read_result.data.v1_read.value_size; + p_value = p_simp_client_cb_data->cb_content.read_result.data.v1_read.p_value; + APP_PRINT_INFO2("SIMP_READ_V1_READ: value_size %d, value %b", + value_size, TRACE_BINARY(value_size, p_value)); + } + else + { + APP_PRINT_ERROR1("SIMP_READ_V1_READ: failed cause 0x%x", + p_simp_client_cb_data->cb_content.read_result.cause); + } + break; + case SIMP_READ_V3_NOTIFY_CCCD: + if (p_simp_client_cb_data->cb_content.read_result.cause == GAP_SUCCESS) + { + APP_PRINT_INFO1("SIMP_READ_V3_NOTIFY_CCCD: notify %d", + p_simp_client_cb_data->cb_content.read_result.data.v3_notify_cccd); + } + else + { + APP_PRINT_ERROR1("SIMP_READ_V3_NOTIFY_CCCD: failed cause 0x%x", + p_simp_client_cb_data->cb_content.read_result.cause); + }; + break; + case SIMP_READ_V4_INDICATE_CCCD: + if (p_simp_client_cb_data->cb_content.read_result.cause == GAP_SUCCESS) + { + APP_PRINT_INFO1("SIMP_READ_V4_INDICATE_CCCD: indicate %d", + p_simp_client_cb_data->cb_content.read_result.data.v4_indicate_cccd); + } + else + { + APP_PRINT_ERROR1("SIMP_READ_V4_INDICATE_CCCD: failed cause 0x%x", + p_simp_client_cb_data->cb_content.read_result.cause); + }; + break; + + default: + break; + } + break; + case SIMP_CLIENT_CB_TYPE_WRITE_RESULT: + switch (p_simp_client_cb_data->cb_content.write_result.type) + { + case SIMP_WRITE_V2_WRITE: + APP_PRINT_INFO1("SIMP_WRITE_V2_WRITE: write result 0x%x", + p_simp_client_cb_data->cb_content.write_result.cause); + break; + case SIMP_WRITE_V3_NOTIFY_CCCD: + APP_PRINT_INFO1("SIMP_WRITE_V3_NOTIFY_CCCD: write result 0x%x", + p_simp_client_cb_data->cb_content.write_result.cause); + break; + case SIMP_WRITE_V4_INDICATE_CCCD: + APP_PRINT_INFO1("SIMP_WRITE_V4_INDICATE_CCCD: write result 0x%x", + p_simp_client_cb_data->cb_content.write_result.cause); + break; + default: + break; + } + break; + case SIMP_CLIENT_CB_TYPE_NOTIF_IND_RESULT: + switch (p_simp_client_cb_data->cb_content.notif_ind_data.type) + { + case SIMP_V3_NOTIFY: + value_size = p_simp_client_cb_data->cb_content.notif_ind_data.data.value_size; + p_value = p_simp_client_cb_data->cb_content.notif_ind_data.data.p_value; + APP_PRINT_INFO2("SIMP_V3_NOTIFY: value_size %d, value %b", + value_size, TRACE_BINARY(value_size, p_value)); + break; + case SIMP_V4_INDICATE: + value_size = p_simp_client_cb_data->cb_content.notif_ind_data.data.value_size; + p_value = p_simp_client_cb_data->cb_content.notif_ind_data.data.p_value; + APP_PRINT_INFO2("SIMP_V4_INDICATE: value_size %d, value %b", + value_size, TRACE_BINARY(value_size, p_value)); + break; + default: + break; + } + break; + + default: + break; + } + } + else if (client_id == bas_client_id) + { + T_BAS_CLIENT_CB_DATA *p_bas_cb_data = (T_BAS_CLIENT_CB_DATA *)p_data; + switch (p_bas_cb_data->cb_type) + { + case BAS_CLIENT_CB_TYPE_DISC_STATE: + switch (p_bas_cb_data->cb_content.disc_state) + { + case DISC_BAS_DONE: + /* Discovery BAS procedure successfully done. */ + app_link_table[conn_id].discovered_flags |= APP_DISCOV_BAS_FLAG; + app_link_table[conn_id].srv_found_flags |= APP_DISCOV_BAS_FLAG; + app_discov_services(conn_id, false); + APP_PRINT_INFO0("app_client_callback: discover bas procedure done"); + break; + case DISC_BAS_FAILED: + /* Discovery Request failed. */ + app_link_table[conn_id].discovered_flags |= APP_DISCOV_BAS_FLAG; + app_discov_services(conn_id, false); + APP_PRINT_INFO0("app_client_callback: discover bas procedure failed"); + break; + default: + break; + } + break; + case BAS_CLIENT_CB_TYPE_READ_RESULT: + switch (p_bas_cb_data->cb_content.read_result.type) + { + case BAS_READ_BATTERY_LEVEL: + if (p_bas_cb_data->cb_content.read_result.cause == GAP_SUCCESS) + { + APP_PRINT_INFO1("BAS_READ_BATTERY_LEVEL: battery level %d", + p_bas_cb_data->cb_content.read_result.data.battery_level); + } + else + { + APP_PRINT_ERROR1("BAS_READ_BATTERY_LEVEL: failed cause 0x%x", + p_bas_cb_data->cb_content.read_result.cause); + } + break; + case BAS_READ_NOTIFY: + if (p_bas_cb_data->cb_content.read_result.cause == GAP_SUCCESS) + { + APP_PRINT_INFO1("BAS_READ_NOTIFY: notify %d", + p_bas_cb_data->cb_content.read_result.data.notify); + } + else + { + APP_PRINT_ERROR1("BAS_READ_NOTIFY: failed cause 0x%x", + p_bas_cb_data->cb_content.read_result.cause); + }; + break; + + default: + break; + } + break; + case BAS_CLIENT_CB_TYPE_WRITE_RESULT: + switch (p_bas_cb_data->cb_content.write_result.type) + { + case BAS_WRITE_NOTIFY_ENABLE: + APP_PRINT_INFO1("BAS_WRITE_NOTIFY_ENABLE: write result 0x%x", + p_bas_cb_data->cb_content.write_result.cause); + break; + case BAS_WRITE_NOTIFY_DISABLE: + APP_PRINT_INFO1("BAS_WRITE_NOTIFY_DISABLE: write result 0x%x", + p_bas_cb_data->cb_content.write_result.cause); + break; + default: + break; + } + break; + case BAS_CLIENT_CB_TYPE_NOTIF_IND_RESULT: + APP_PRINT_INFO1("BAS_CLIENT_CB_TYPE_NOTIF_IND_RESULT: battery level %d", + p_bas_cb_data->cb_content.notify_data.battery_level); + break; + + default: + break; + } + } + + return result; +} + +/** @} */ /* End of group CENTRAL_CLIENT_CALLBACK */ +/** @} */ /* End of group CENTRAL_APP */ + diff --git a/src/sample/ble_central/central_app.h b/src/sample/ble_central/central_app.h new file mode 100644 index 0000000..c7f8779 --- /dev/null +++ b/src/sample/ble_central/central_app.h @@ -0,0 +1,69 @@ +/** +***************************************************************************************** +* Copyright(c) 2017, Realtek Semiconductor Corporation. All rights reserved. +***************************************************************************************** + * @file central_app.h + * @brief This file handles BLE central application routines. + * @author jane + * @date 2017-06-06 + * @version v1.0 + ************************************************************************************** + * @attention + * <h2><center>© COPYRIGHT 2017 Realtek Semiconductor Corporation</center></h2> + ************************************************************************************** + */ + +#ifndef _CENTRAL_APP_H_ +#define _CENTRAL_APP_H_ + +#ifdef __cplusplus +extern "C" { +#endif +/*============================================================================* + * Header Files + *============================================================================*/ +#include <profile_client.h> +#include <app_msg.h> + +/*============================================================================* + * Variables + *============================================================================*/ +extern T_CLIENT_ID gaps_client_id; /**< Simple ble service client id*/ +extern T_CLIENT_ID simple_ble_client_id; /**< gap service client id*/ +extern T_CLIENT_ID bas_client_id; /**< battery service client id*/ + +/*============================================================================* + * Functions + *============================================================================*/ + +/** + * @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); +/** + * @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); + +/** + * @brief Callback will be called when data sent from profile client layer. + * @param client_id the ID distinguish which module sent the data. + * @param conn_id connection ID. + * @param p_data pointer to data. + * @retval result @ref T_APP_RESULT + */ +T_APP_RESULT app_client_callback(T_CLIENT_ID client_id, uint8_t conn_id, void *p_data); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/sample/ble_central/link_mgr.c b/src/sample/ble_central/link_mgr.c new file mode 100644 index 0000000..a097eab --- /dev/null +++ b/src/sample/ble_central/link_mgr.c @@ -0,0 +1,139 @@ +/** +***************************************************************************************** +* Copyright(c) 2017, Realtek Semiconductor Corporation. All rights reserved. +***************************************************************************************** + * @file link_mgr.c + * @brief Multilink manager functions. + * @author jane + * @date 2017-06-06 + * @version v1.0 + ************************************************************************************** + * @attention + * <h2><center>© COPYRIGHT 2017 Realtek Semiconductor Corporation</center></h2> + ************************************************************************************** + */ +/*============================================================================* + * Header Files + *============================================================================*/ +#include <link_mgr.h> +#include <trace.h> +#include <string.h> +#include <ftl.h> + +/*============================================================================* + * Constants + *============================================================================*/ +#if F_BT_GATT_SRV_HANDLE_STORAGE +/** @addtogroup CENTRAL_SRV_DIS + * @{ + */ +/** @brief Define start offset of the falsh to save GATT Server information. */ +#define APP_SRVS_HDL_TABLE_FLASH_OFFSET 0 +/** @} */ /* End of group CENTRAL_SRV_DIS */ + +#endif + +/*============================================================================* + * Variables + *============================================================================*/ +/** @addtogroup CENTRAL_GAP_MSG + * @{ + */ +T_APP_LINK app_link_table[APP_MAX_LINKS]; +/** @} */ +/** @addtogroup CENTRAL_SCAN_MGR + * @{ + */ +T_DEV_INFO dev_list[APP_MAX_DEVICE_INFO]; +uint8_t dev_list_count = 0; +/** @} */ + +/*============================================================================* + * Functions + *============================================================================*/ +/** @addtogroup CENTRAL_SCAN_MGR + * @{ + */ +/** + * @brief Add device information to device list. + * + * @param[in] bd_addr Peer device address. + * @param[in] bd_type Peer device address type. + * @retval true Success. + * @retval false Failed, device list is full. + */ +bool link_mgr_add_device(uint8_t *bd_addr, uint8_t bd_type) +{ + /* If result count not at max */ + if (dev_list_count < APP_MAX_DEVICE_INFO) + { + uint8_t i; + /* Check if device is already in device list*/ + for (i = 0; i < dev_list_count; i++) + { + if (memcmp(bd_addr, dev_list[i].bd_addr, GAP_BD_ADDR_LEN) == 0) + { + return true; + } + } + + /*Add addr to device list list*/ + memcpy(dev_list[dev_list_count].bd_addr, bd_addr, GAP_BD_ADDR_LEN); + dev_list[dev_list_count].bd_type = bd_type; + + /*Increment device list count*/ + dev_list_count++; + } + else + { + return false; + } + return true; +} + +/** + * @brief Clear device list. + * @retval None. + */ +void link_mgr_clear_device_list(void) +{ + dev_list_count = 0; +} +/** @} */ + +#if F_BT_GATT_SRV_HANDLE_STORAGE +/** @addtogroup CENTRAL_SRV_DIS + * @{ + */ +/** + * @brief Save GATT Services information into flash. + * @param[in] p_info the buffer for saving data + * @retval 0 Save success. + * @retval other Failed. + */ +uint32_t app_save_srvs_hdl_table(T_APP_SRVS_HDL_TABLE *p_info) +{ + APP_PRINT_INFO0("app_save_srvs_hdl_table"); + return ftl_save(p_info, APP_SRVS_HDL_TABLE_FLASH_OFFSET, sizeof(T_APP_SRVS_HDL_TABLE)); +} + +/** + * @brief load GATT Services information from storage + * @param[out] p_info the buffer for loading data + * @retval 0 Load success. + * @retval other Failed. + */ +uint32_t app_load_srvs_hdl_table(T_APP_SRVS_HDL_TABLE *p_info) +{ + uint32_t result; + result = ftl_load(p_info, APP_SRVS_HDL_TABLE_FLASH_OFFSET, + sizeof(T_APP_SRVS_HDL_TABLE)); + APP_PRINT_INFO1("app_load_srvs_hdl_table: result %u", result); + if (result) + { + memset(p_info, 0, sizeof(T_APP_SRVS_HDL_TABLE)); + } + return result; +} +/** @} */ +#endif diff --git a/src/sample/ble_central/link_mgr.h b/src/sample/ble_central/link_mgr.h new file mode 100644 index 0000000..dbe8717 --- /dev/null +++ b/src/sample/ble_central/link_mgr.h @@ -0,0 +1,111 @@ +/** +***************************************************************************************** +* Copyright(c) 2017, Realtek Semiconductor Corporation. All rights reserved. +***************************************************************************************** + * @file link_mgr.h + * @brief Define multilink manager struct and functions. + * @author jane + * @date 2017-06-06 + * @version v1.0 + ************************************************************************************** + * @attention + * <h2><center>© COPYRIGHT 2017 Realtek Semiconductor Corporation</center></h2> + ************************************************************************************** + */ +#ifndef _LINK_MANAGER_H_ +#define _LINK_MANAGER_H_ +/*============================================================================* + * Header Files + *============================================================================*/ +#include <app_msg.h> +#include <gap_conn_le.h> +#include <profile_client.h> +#include <simple_ble_client.h> +#include <gaps_client.h> +#include <bas_client.h> + +/*============================================================================* + * Constants + *============================================================================*/ +/** @brief Define device list table size. */ +#define APP_MAX_DEVICE_INFO 6 + +/** @addtogroup CENTRAL_SRV_DIS + * @{ + */ +/** @defgroup APP_DISCOV_BITS Application discover services Bits +* @{ + */ +#define APP_DISCOV_GAPS_FLAG 0x01 +#define APP_DISCOV_SIMP_FLAG 0x02 +#define APP_DISCOV_BAS_FLAG 0x04 +/** + * @} + */ +/** @} */ +/** @addtogroup CENTRAL_GAP_MSG + * @{ + */ +/** + * @brief Application Link control block definition. + */ +typedef struct +{ + T_GAP_CONN_STATE conn_state; /**< Connection state. */ + uint8_t discovered_flags; /**< discovered flags. */ + uint8_t srv_found_flags; /**< service founded flogs. */ + T_GAP_REMOTE_ADDR_TYPE bd_type; /**< remote BD type*/ + uint8_t bd_addr[GAP_BD_ADDR_LEN]; /**< remote BD */ +} T_APP_LINK; +/** @} */ /* End of group CENTRAL_GAP_MSG */ +/** @addtogroup CENTRAL_SCAN_MGR + * @{ + */ +/** + * @brief Device list block definition. + */ +typedef struct +{ + uint8_t bd_addr[GAP_BD_ADDR_LEN]; /**< remote BD */ + uint8_t bd_type; /**< remote BD type*/ +} T_DEV_INFO; +/** @} */ +#if F_BT_GATT_SRV_HANDLE_STORAGE +/** @addtogroup CENTRAL_SRV_DIS + * @{ + */ +/** @brief App link table */ +typedef struct +{ + uint8_t srv_found_flags; + uint8_t bd_type; /**< remote BD type*/ + uint8_t bd_addr[GAP_BD_ADDR_LEN]; /**< remote BD */ + uint32_t reserved; + uint16_t gaps_hdl_cache[HDL_GAPS_CACHE_LEN]; + uint16_t simp_hdl_cache[HDL_SIMBLE_CACHE_LEN]; + uint16_t bas_hdl_cache[HDL_BAS_CACHE_LEN]; +} T_APP_SRVS_HDL_TABLE; +/** @} */ +#endif + +/*============================================================================* + * Variables + *============================================================================*/ +/** @brief App link table */ +extern T_APP_LINK app_link_table[APP_MAX_LINKS]; +/** @brief Device list table, used to save discovered device informations. */ +extern T_DEV_INFO dev_list[APP_MAX_DEVICE_INFO]; +/** @brief The number of device informations saved in dev_list. */ +extern uint8_t dev_list_count; + +/*============================================================================* + * Functions + *============================================================================*/ +bool link_mgr_add_device(uint8_t *bd_addr, uint8_t bd_type); +void link_mgr_clear_device_list(void); +#if F_BT_GATT_SRV_HANDLE_STORAGE +uint32_t app_save_srvs_hdl_table(T_APP_SRVS_HDL_TABLE *p_info); +uint32_t app_load_srvs_hdl_table(T_APP_SRVS_HDL_TABLE *p_info); +#endif + +#endif diff --git a/src/sample/ble_central/main.c b/src/sample/ble_central/main.c new file mode 100644 index 0000000..48a1563 --- /dev/null +++ b/src/sample/ble_central/main.c @@ -0,0 +1,178 @@ +/** +***************************************************************************************** +* Copyright(c) 2017, Realtek Semiconductor Corporation. All rights reserved. +***************************************************************************************** + * @file main.c + * @brief Source file for BLE central project, mainly used for initialize modules + * @author jane + * @date 2017-06-12 + * @version v1.0 + ************************************************************************************** + * @attention + * <h2><center>© COPYRIGHT 2017 Realtek Semiconductor Corporation</center></h2> + ************************************************************************************** + */ + +/*============================================================================* + * Header Files + *============================================================================*/ +#include <os_sched.h> +#include <string.h> +#include <app_task.h> +#include <trace.h> +#include <gap.h> +#include <gap_bond_le.h> +#include <gap_scan.h> +#include <profile_client.h> +#include <gap_msg.h> +#include <central_app.h> +#include <simple_ble_client.h> +#include <gaps_client.h> +#include <bas_client.h> +#include <link_mgr.h> + +/** @defgroup CENTRAL_DEMO_MAIN Central Main + * @brief Main file to initialize hardware and BT stack and start task scheduling + * @{ + */ + +/*============================================================================* + * Constants + *============================================================================*/ +/** @brief Default scan interval (units of 0.625ms, 0x10=2.5ms) */ +#define DEFAULT_SCAN_INTERVAL 0x10 +/** @brief Default scan window (units of 0.625ms, 0x10=2.5ms) */ +#define DEFAULT_SCAN_WINDOW 0x10 + + +/*============================================================================* + * Functions + *============================================================================*/ +/** + * @brief Initialize central and gap bond manager related parameters + * @return void + */ +void app_le_gap_init(void) +{ + /* Device name and device appearance */ + uint8_t device_name[GAP_DEVICE_NAME_LEN] = "BLE_CENTRAL"; + uint16_t appearance = GAP_GATT_APPEARANCE_UNKNOWN; + + /* Scan parameters */ + uint8_t scan_mode = GAP_SCAN_MODE_ACTIVE; + uint16_t scan_interval = DEFAULT_SCAN_INTERVAL; + uint16_t scan_window = DEFAULT_SCAN_WINDOW; + uint8_t scan_filter_policy = GAP_SCAN_FILTER_ANY; + uint8_t scan_filter_duplicate = GAP_SCAN_FILTER_DUPLICATE_ENABLE; + + /* GAP Bond Manager parameters */ + uint8_t auth_pair_mode = GAP_PAIRING_MODE_PAIRABLE; + uint16_t auth_flags = GAP_AUTHEN_BIT_BONDING_FLAG; + uint8_t auth_io_cap = GAP_IO_CAP_NO_INPUT_NO_OUTPUT; + uint8_t auth_oob = false; + uint8_t auth_use_fix_passkey = false; + uint32_t auth_fix_passkey = 0; + uint8_t auth_sec_req_enable = false; + uint16_t auth_sec_req_flags = GAP_AUTHEN_BIT_BONDING_FLAG; + + /* Set device name and device appearance */ + le_set_gap_param(GAP_PARAM_DEVICE_NAME, GAP_DEVICE_NAME_LEN, device_name); + le_set_gap_param(GAP_PARAM_APPEARANCE, sizeof(appearance), &appearance); + + /* Set scan parameters */ + le_scan_set_param(GAP_PARAM_SCAN_MODE, sizeof(scan_mode), &scan_mode); + le_scan_set_param(GAP_PARAM_SCAN_INTERVAL, sizeof(scan_interval), &scan_interval); + le_scan_set_param(GAP_PARAM_SCAN_WINDOW, sizeof(scan_window), &scan_window); + le_scan_set_param(GAP_PARAM_SCAN_FILTER_POLICY, sizeof(scan_filter_policy), + &scan_filter_policy); + le_scan_set_param(GAP_PARAM_SCAN_FILTER_DUPLICATES, sizeof(scan_filter_duplicate), + &scan_filter_duplicate); + + /* Setup the GAP Bond Manager */ + gap_set_param(GAP_PARAM_BOND_PAIRING_MODE, sizeof(auth_pair_mode), &auth_pair_mode); + gap_set_param(GAP_PARAM_BOND_AUTHEN_REQUIREMENTS_FLAGS, sizeof(auth_flags), &auth_flags); + gap_set_param(GAP_PARAM_BOND_IO_CAPABILITIES, sizeof(auth_io_cap), &auth_io_cap); + gap_set_param(GAP_PARAM_BOND_OOB_ENABLED, sizeof(auth_oob), &auth_oob); + le_bond_set_param(GAP_PARAM_BOND_FIXED_PASSKEY, sizeof(auth_fix_passkey), &auth_fix_passkey); + le_bond_set_param(GAP_PARAM_BOND_FIXED_PASSKEY_ENABLE, sizeof(auth_use_fix_passkey), + &auth_use_fix_passkey); + le_bond_set_param(GAP_PARAM_BOND_SEC_REQ_ENABLE, sizeof(auth_sec_req_enable), &auth_sec_req_enable); + le_bond_set_param(GAP_PARAM_BOND_SEC_REQ_REQUIREMENT, sizeof(auth_sec_req_flags), + &auth_sec_req_flags); + + /* register gap message callback */ + le_register_app_cb(app_gap_callback); +} + +/** + * @brief Add GATT clients and register callbacks + * @return void + */ +void app_le_profile_init(void) +{ + client_init(3); + gaps_client_id = gaps_add_client(app_client_callback, APP_MAX_LINKS); + simple_ble_client_id = simp_ble_add_client(app_client_callback, APP_MAX_LINKS); + bas_client_id = bas_add_client(app_client_callback, APP_MAX_LINKS); +} + +/** + * @brief Contains the initialization of pinmux settings and pad settings + * @note All the pinmux settings and pad settings shall be initiated in this function, + * but if legacy driver is used, the initialization of pinmux setting and pad setting + * should be performed with the IO initializing. + * @return void + */ +void board_init(void) +{ + +} + +/** + * @brief Contains the initialization of peripherals + * @note Both new architecture driver and legacy driver initialization method can be used + * @return void + */ +void driver_init(void) +{ + +} + +/** + * @brief Contains the power mode settings + * @return void + */ +void pwr_mgr_init(void) +{ +} + +/** + * @brief Contains the initialization of all tasks + * @note There is only one task in BLE Central APP, thus only one APP task is init here + * @return void + */ +void task_init(void) +{ + app_task_init(); +} + +/** + * @brief Entry of APP code + * @return int (To avoid compile warning) + */ +int main(void) +{ + board_init(); + le_gap_init(APP_MAX_LINKS); + gap_lib_init(); + app_le_gap_init(); + app_le_profile_init(); + pwr_mgr_init(); + task_init(); + os_sched_start(); + + return 0; +} +/** @} */ /* End of group CENTRAL_DEMO_MAIN */ + + diff --git a/src/sample/ble_central/user_cmd.c b/src/sample/ble_central/user_cmd.c new file mode 100644 index 0000000..ca26481 --- /dev/null +++ b/src/sample/ble_central/user_cmd.c @@ -0,0 +1,1066 @@ +/** +***************************************************************************************** +* Copyright(c) 2017, Realtek Semiconductor Corporation. All rights reserved. +***************************************************************************************** + * @file user_cmd.c + * @brief User defined test commands. + * @details User command interfaces. + * @author jane + * @date 2017-06-06 + * @version v1.0 + ************************************************************************************** + * @attention + * <h2><center>© COPYRIGHT 2017 Realtek Semiconductor Corporation</center></h2> + ************************************************************************************** + */ +/*============================================================================* + * Header Files + *============================================================================*/ +#include <string.h> +#include <trace.h> +#include <profile_client.h> +#include <gap_bond_le.h> +#include <gap_scan.h> +#include <user_cmd.h> +#include <gap.h> +#include <gap_conn_le.h> +#include <link_mgr.h> +#include <simple_ble_client.h> +#include <gaps_client.h> +#include <bas_client.h> + + +/** @defgroup CENTRAL_CMD Central User Command + * @brief This file handles Central User Command. + * @{ + */ +/*============================================================================* + * Variables + *============================================================================*/ +/** @brief User command interface data, used to parse the commands from Data UART. */ +T_USER_CMD_IF user_cmd_if; + +/*============================================================================* + * Functions + *============================================================================*/ +/** + * @brief Show all devices connecting status + * + * <b>Command table define</b> + * \code{.c} + { + "showcon", + "showcon\n\r", + "Show all devices connecting status\n\r", + cmd_showcon + }, + * \endcode + */ +static T_USER_CMD_PARSE_RESULT cmd_showcon(T_USER_CMD_PARSED_VALUE *p_parse_value) +{ + uint8_t conn_id; + T_GAP_CONN_INFO conn_info; + for (conn_id = 0; conn_id < APP_MAX_LINKS; conn_id++) + { + if (le_get_conn_info(conn_id, &conn_info)) + { + data_uart_print("ShowCon conn_id %d state 0x%x role %d\r\n", conn_id, + conn_info.conn_state, conn_info.role); + data_uart_print("RemoteBd = [%02x:%02x:%02x:%02x:%02x:%02x] type = %d\r\n", + conn_info.remote_bd[5], conn_info.remote_bd[4], + conn_info.remote_bd[3], conn_info.remote_bd[2], + conn_info.remote_bd[1], conn_info.remote_bd[0], + conn_info.remote_bd_type); + } + } + data_uart_print("active link num %d, idle link num %d\r\n", + le_get_active_link_num(), le_get_idle_link_num()); + return (RESULT_SUCCESS); +} + +/** + * @brief LE connection param update request + * + * <b>Command table define</b> + * \code{.c} + { + "conupdreq", + "conupdreq [conn_id] [interval_min] [interval_max] [latency] [supervision_timeout]\n\r", + "LE connection param update request\r\n\ + sample: conupdreq 0 0x30 0x40 0 500\n\r", + cmd_conupdreq + }, + * \endcode + */ +static T_USER_CMD_PARSE_RESULT cmd_conupdreq(T_USER_CMD_PARSED_VALUE *p_parse_value) +{ + T_GAP_CAUSE cause; + uint8_t conn_id = p_parse_value->dw_param[0]; + uint16_t conn_interval_min = p_parse_value->dw_param[1]; + uint16_t conn_interval_max = p_parse_value->dw_param[2]; + uint16_t conn_latency = p_parse_value->dw_param[3]; + uint16_t supervision_timeout = p_parse_value->dw_param[4]; + + + cause = le_update_conn_param(conn_id, + conn_interval_min, + conn_interval_max, + conn_latency, + supervision_timeout, + 2 * (conn_interval_min - 1), + 2 * (conn_interval_max - 1) + ); + return (T_USER_CMD_PARSE_RESULT)cause; +} + +/** + * @brief Disconnect to remote device + * + * <b>Command table define</b> + * \code{.c} + { + "disc", + "disc [conn_id]\n\r", + "Disconnect to remote device\n\r", + cmd_disc + }, + * \endcode + */ +static T_USER_CMD_PARSE_RESULT cmd_disc(T_USER_CMD_PARSED_VALUE *p_parse_value) +{ + uint8_t conn_id = p_parse_value->dw_param[0]; + T_GAP_CAUSE cause; + cause = le_disconnect(conn_id); + return (T_USER_CMD_PARSE_RESULT)cause; +} + +/** + * @brief Config authentication mode + * + * <b>Command table define</b> + * \code{.c} + { + "authmode", + "authmode [auth_flags] [io_cap] [sec_enable] [oob_enable]\n\r", + "Config authentication mode\r\n\ + [auth_flags]:authentication req bit field: bit0-(bonding), bit2-(MITM), bit3-(SC)\r\n\ + [io_cap]:set io Capabilities: 0-(display only), 1-(display yes/no), 2-(keyboard noly), 3-(no IO), 4-(keyboard display)\r\n\ + [sec_enable]:Start smp pairing procedure when connected: 0-(disable), 1-(enable)\r\n\ + [oob_enable]:Enable oob flag: 0-(disable), 1-(enable)\r\n\ + sample: authmode 0x5 2 1 0\n\r", + cmd_authmode + }, + * \endcode + */ +static T_USER_CMD_PARSE_RESULT cmd_authmode(T_USER_CMD_PARSED_VALUE *p_parse_value) +{ + T_GAP_CAUSE cause; + uint8_t auth_pair_mode = GAP_PAIRING_MODE_PAIRABLE; + uint16_t auth_flags = GAP_AUTHEN_BIT_BONDING_FLAG; + uint8_t auth_io_cap = GAP_IO_CAP_NO_INPUT_NO_OUTPUT; + uint8_t oob_enable = false; + uint8_t auth_sec_req_enable = false; + uint16_t auth_sec_req_flags = GAP_AUTHEN_BIT_BONDING_FLAG; + + if (p_parse_value->param_count > 0) + { + auth_flags = p_parse_value->dw_param[0]; + auth_sec_req_flags = p_parse_value->dw_param[0]; + } + if (p_parse_value->param_count > 1) + { + auth_io_cap = p_parse_value->dw_param[1]; + } + if (p_parse_value->param_count > 2) + { + auth_sec_req_enable = p_parse_value->dw_param[2]; + } + if (p_parse_value->param_count > 3) + { + oob_enable = p_parse_value->dw_param[3]; + } + gap_set_param(GAP_PARAM_BOND_PAIRING_MODE, sizeof(auth_pair_mode), &auth_pair_mode); + gap_set_param(GAP_PARAM_BOND_AUTHEN_REQUIREMENTS_FLAGS, sizeof(auth_flags), &auth_flags); + gap_set_param(GAP_PARAM_BOND_IO_CAPABILITIES, sizeof(auth_io_cap), &auth_io_cap); + gap_set_param(GAP_PARAM_BOND_OOB_ENABLED, sizeof(uint8_t), &oob_enable); + le_bond_set_param(GAP_PARAM_BOND_SEC_REQ_ENABLE, sizeof(auth_sec_req_enable), &auth_sec_req_enable); + le_bond_set_param(GAP_PARAM_BOND_SEC_REQ_REQUIREMENT, sizeof(auth_sec_req_flags), + &auth_sec_req_flags); + cause = gap_set_pairable_mode(); + return (T_USER_CMD_PARSE_RESULT)cause; +} + +/** + * @brief Send authentication request + * + * <b>Command table define</b> + * \code{.c} + { + "sauth", + "sauth [conn_id]\n\r", + "Send authentication request\n\r", + cmd_sauth + }, + * \endcode + */ +static T_USER_CMD_PARSE_RESULT cmd_sauth(T_USER_CMD_PARSED_VALUE *p_parse_value) +{ + uint8_t conn_id = p_parse_value->dw_param[0]; + T_GAP_CAUSE cause; + cause = le_bond_pair(conn_id); + return (T_USER_CMD_PARSE_RESULT)cause; +} + +/** + * @brief Send user confirmation when show GAP_MSG_LE_BOND_USER_CONFIRMATION + * + * <b>Command table define</b> + * \code{.c} + { + "userconf", + "userconf [conn_id] [conf]\n\r", + "Send user confirmation when show GAP_MSG_LE_BOND_USER_CONFIRMATION\r\n\ + [conf]: 0-(Reject), 1-(Accept)\r\n\ + sample: userconf 0 1\n\r", + cmd_userconf + }, + * \endcode + */ +static T_USER_CMD_PARSE_RESULT cmd_userconf(T_USER_CMD_PARSED_VALUE *p_parse_value) +{ + uint8_t conn_id = p_parse_value->dw_param[0]; + T_GAP_CFM_CAUSE confirm = GAP_CFM_CAUSE_ACCEPT; + T_GAP_CAUSE cause; + if (p_parse_value->dw_param[1] == 0) + { + confirm = GAP_CFM_CAUSE_REJECT; + } + cause = le_bond_user_confirm(conn_id, confirm); + return (T_USER_CMD_PARSE_RESULT)cause; +} + +/** + * @brief Input passkey when show GAP_MSG_LE_BOND_PASSKEY_INPUT + * + * <b>Command table define</b> + * \code{.c} + { + "authkey", + "authkey [conn_id] [passkey]\n\r", + "Input passkey when show GAP_MSG_LE_BOND_PASSKEY_INPUT\r\n\ + [passkey]: 0 - 999999\r\n\ + sample: authkey 0 123456\n\r", + cmd_authkey + }, + * \endcode + */ +static T_USER_CMD_PARSE_RESULT cmd_authkey(T_USER_CMD_PARSED_VALUE *p_parse_value) +{ + uint8_t conn_id = p_parse_value->dw_param[0]; + uint32_t passcode = p_parse_value->dw_param[1]; + T_GAP_CAUSE cause; + T_GAP_CFM_CAUSE confirm = GAP_CFM_CAUSE_ACCEPT; + if (passcode > GAP_PASSCODE_MAX) + { + confirm = GAP_CFM_CAUSE_REJECT; + } + cause = le_bond_passkey_input_confirm(conn_id, passcode, + confirm); + return (T_USER_CMD_PARSE_RESULT)cause; +} + +/** + * @brief Clear all bonded devices information + * + * <b>Command table define</b> + * \code{.c} + { + "bondclear", + "bondclear\n\r", + "Clear all bonded devices information\n\r", + cmd_bondclear + }, + * \endcode + */ +static T_USER_CMD_PARSE_RESULT cmd_bondclear(T_USER_CMD_PARSED_VALUE *p_parse_value) +{ + le_bond_clear_all_keys(); + return (RESULT_SUCCESS); +} + +/** + * @brief Get all Bonded devices information + * + * <b>Command table define</b> + * \code{.c} + { + "bondinfo", + "bondinfo\n\r", + "Get all Bonded devices information\n\r", + cmd_bondinfo + }, + * \endcode + */ +static T_USER_CMD_PARSE_RESULT cmd_bondinfo(T_USER_CMD_PARSED_VALUE *p_parse_value) +{ + uint8_t i; + T_LE_KEY_ENTRY *p_entry; + uint8_t bond_storage_num = le_get_max_le_paired_device_num(); + for (i = 0; i < bond_storage_num; i++) + { + p_entry = le_find_key_entry_by_idx(i); + if (p_entry != NULL) + { + data_uart_print("bond_dev[%d]: bd 0x%02x%02x%02x%02x%02x%02x, addr_type %d, flags 0x%x\r\n", + p_entry->idx, + p_entry->remote_bd.addr[5], + p_entry->remote_bd.addr[4], + p_entry->remote_bd.addr[3], + p_entry->remote_bd.addr[2], + p_entry->remote_bd.addr[1], + p_entry->remote_bd.addr[0], + p_entry->remote_bd.remote_bd_type, + p_entry->flags); + } + } + + return (RESULT_SUCCESS); +} +/************************** Central only *************************************/ +/** + * @brief Start scan + * + * <b>Command table define</b> + * \code{.c} + { + "scan", + "scan [filter_policy] [filter_duplicate]\n\r", + "Start scan\r\n\ + [filter_policy]: 0-(any), 1-(whitelist), 2-(any RPA), 3-(whitelist RPA) \r\n\ + [filter_duplicate]: 0-(disable), 1-(enable) \n\r", + cmd_scan + }, + * \endcode + */ +static T_USER_CMD_PARSE_RESULT cmd_scan(T_USER_CMD_PARSED_VALUE *p_parse_value) +{ + T_GAP_CAUSE cause; + uint8_t scan_filter_policy = GAP_SCAN_FILTER_ANY; + uint8_t scan_filter_duplicate = GAP_SCAN_FILTER_DUPLICATE_ENABLE; + + if (p_parse_value->param_count > 0) + { + scan_filter_policy = p_parse_value->dw_param[0]; + } + if (p_parse_value->param_count > 1) + { + scan_filter_duplicate = p_parse_value->dw_param[1]; + } + + link_mgr_clear_device_list(); + le_scan_set_param(GAP_PARAM_SCAN_FILTER_POLICY, sizeof(scan_filter_policy), + &scan_filter_policy); + le_scan_set_param(GAP_PARAM_SCAN_FILTER_DUPLICATES, sizeof(scan_filter_duplicate), + &scan_filter_duplicate); + cause = le_scan_start(); + return (T_USER_CMD_PARSE_RESULT)cause; +} + +/** + * @brief Stop scan + * + * <b>Command table define</b> + * \code{.c} + { + "stopscan", + "stopscan\n\r", + "Stop scan\n\r", + cmd_stopscan + }, + * \endcode + */ +static T_USER_CMD_PARSE_RESULT cmd_stopscan(T_USER_CMD_PARSED_VALUE *p_parse_value) +{ + T_GAP_CAUSE cause; + cause = le_scan_stop(); + return (T_USER_CMD_PARSE_RESULT)cause; +} + +/** + * @brief Show scan dev list: filter simple ble service + * + * <b>Command table define</b> + * \code{.c} + { + "showdev", + "showdev\n\r", + "Show scan dev list: filter simple ble service\n\r", + cmd_showdev + }, + * \endcode + */ +static T_USER_CMD_PARSE_RESULT cmd_showdev(T_USER_CMD_PARSED_VALUE *p_parse_value) +{ + uint8_t i; + data_uart_print("Advertising and Scan response: filter uuid = 0xA00A dev list\r\n"); + for (i = 0; i < dev_list_count; i++) + { + data_uart_print("RemoteBd[%d] = [%02x:%02x:%02x:%02x:%02x:%02x] type = %d\r\n", + i, + dev_list[i].bd_addr[5], dev_list[i].bd_addr[4], + dev_list[i].bd_addr[3], dev_list[i].bd_addr[2], + dev_list[i].bd_addr[1], dev_list[i].bd_addr[0], + dev_list[i].bd_type); + if (dev_list[i].bd_type == GAP_REMOTE_ADDR_LE_RANDOM) + { + uint8_t addr = dev_list[i].bd_addr[5] & RANDOM_ADDR_MASK; + if (addr == RANDOM_ADDR_MASK_STATIC) + { + data_uart_print("Static Random Addr\r\n"); + } + else if (addr == RANDOM_ADDR_MASK_RESOLVABLE) + { + data_uart_print("Resolv Random Addr\r\n"); + } + else if (addr == RANDOM_ADDR_MASK_NON_RESOLVABLE) + { + data_uart_print("Non-resolv Random Addr\r\n"); + } + else + { + data_uart_print("Unknown Random Addr\r\n"); + } + } + } + + return (RESULT_SUCCESS); +} + +/** + * @brief Connect to remote device: use address + * + * <b>Command table define</b> + * \code{.c} + { + "con", + "con [BD0] [BD1] [BD2] [BD3] [BD4] [BD5] [addr_type]\n\r", + "Connect to remote device: use address\r\n\ + [BD0] [BD1] [BD2] [BD3] [BD4] [BD5]: remote device address\r\n\ + [addr_type]: 0-(public), 1-(random)\r\n\ + sample: con x11 x22 x33 x44 x55 x66 0 \n\r", + cmd_con + }, + * \endcode + */ +static T_USER_CMD_PARSE_RESULT cmd_con(T_USER_CMD_PARSED_VALUE *p_parse_value) +{ + T_GAP_CAUSE cause; + uint8_t addr[6] = {0}; + uint8_t addr_len; + uint8_t addr_type = GAP_REMOTE_ADDR_LE_PUBLIC; + T_GAP_LE_CONN_REQ_PARAM conn_req_param; + + conn_req_param.scan_interval = 0x10; + conn_req_param.scan_window = 0x10; + conn_req_param.conn_interval_min = 80; + conn_req_param.conn_interval_max = 80; + conn_req_param.conn_latency = 0; + conn_req_param.supv_tout = 1000; + conn_req_param.ce_len_min = 2 * (conn_req_param.conn_interval_min - 1); + conn_req_param.ce_len_max = 2 * (conn_req_param.conn_interval_max - 1); + le_set_conn_param(GAP_CONN_PARAM_1M, &conn_req_param); + + for (addr_len = 0; addr_len < GAP_BD_ADDR_LEN; addr_len++) + { + addr[addr_len] = p_parse_value->dw_param[GAP_BD_ADDR_LEN - addr_len - 1]; + } + if (p_parse_value->param_count >= 7) + { + addr_type = p_parse_value->dw_param[6]; + } + + cause = le_connect(GAP_PHYS_CONN_INIT_1M_BIT, addr, (T_GAP_REMOTE_ADDR_TYPE)addr_type, + GAP_LOCAL_ADDR_LE_PUBLIC, + 1000); + + return (T_USER_CMD_PARSE_RESULT)cause; +} + +/** + * @brief Connect to remote device: use showdev to show idx + * + * <b>Command table define</b> + * \code{.c} + { + "condev", + "condev [idx]\n\r", + "Connect to remote device: use showdev to show idx\r\n\ + [idx]: use cmd showdev to show idx before use this cmd\r\n\ + sample: condev 0\n\r", + cmd_condev + }, + * \endcode + */ +static T_USER_CMD_PARSE_RESULT cmd_condev(T_USER_CMD_PARSED_VALUE *p_parse_value) +{ + uint8_t dev_idx = p_parse_value->dw_param[0]; + if (dev_idx < dev_list_count) + { + T_GAP_CAUSE cause; + T_GAP_LE_CONN_REQ_PARAM conn_req_param; + + conn_req_param.scan_interval = 0x10; + conn_req_param.scan_window = 0x10; + conn_req_param.conn_interval_min = 80; + conn_req_param.conn_interval_max = 80; + conn_req_param.conn_latency = 0; + conn_req_param.supv_tout = 1000; + conn_req_param.ce_len_min = 2 * (conn_req_param.conn_interval_min - 1); + conn_req_param.ce_len_max = 2 * (conn_req_param.conn_interval_max - 1); + le_set_conn_param(GAP_CONN_PARAM_1M, &conn_req_param); + cause = le_connect(GAP_PHYS_CONN_INIT_1M_BIT, + dev_list[dev_idx].bd_addr, + (T_GAP_REMOTE_ADDR_TYPE)dev_list[dev_idx].bd_type, + GAP_LOCAL_ADDR_LE_PUBLIC, + 1000); + return (T_USER_CMD_PARSE_RESULT)cause; + } + else + { + return RESULT_ERR; + } +} +/************************** GATT client *************************************/ +/** + * @brief Read GAP service characteristic value + * + * <b>Command table define</b> + * \code{.c} + { + "gapread", + "gapread [conn_id] [type]\n\r", + "Read GAP service characteristic value\r\n\ + [type]: 0-(read device name), 1-(read appearance)\r\n\ + simple: gapread 0 0\n\r", + cmd_gapread + }, + * \endcode + */ +static T_USER_CMD_PARSE_RESULT cmd_gapread(T_USER_CMD_PARSED_VALUE *p_parse_value) +{ + uint8_t conn_id = p_parse_value->dw_param[0]; + T_GAPS_READ_TYPE read_type = (T_GAPS_READ_TYPE)p_parse_value->dw_param[1]; + bool ret = gaps_read(conn_id, read_type); + if (ret) + { + return (RESULT_SUCCESS); + } + else + { + return (RESULT_ERR); + } +} + +/** + * @brief List GAP service handle cache + * + * <b>Command table define</b> + * \code{.c} + { + "gaphdl", + "gaphdl [conn_id]\n\r", + "List GAP service handle cache\n\r", + cmd_gaphdl + }, + * \endcode + */ +static T_USER_CMD_PARSE_RESULT cmd_gaphdl(T_USER_CMD_PARSED_VALUE *p_parse_value) +{ + uint8_t conn_id = p_parse_value->dw_param[0]; + uint16_t hdl_cache[HDL_GAPS_CACHE_LEN]; + uint8_t hdl_idx; + bool ret = gaps_get_hdl_cache(conn_id, hdl_cache, + sizeof(uint16_t) * HDL_GAPS_CACHE_LEN); + + if (ret) + { + for (hdl_idx = HDL_GAPS_SRV_START; hdl_idx < HDL_GAPS_CACHE_LEN; hdl_idx++) + { + data_uart_print("-->Index %d -- Handle 0x%x\r\n", hdl_idx, hdl_cache[hdl_idx]); + } + return (RESULT_SUCCESS); + } + else + { + return (RESULT_ERR); + } +} + +/** + * @brief Read simple ble service characteristic and descriptor value + * + * <b>Command table define</b> + * \code{.c} + { + "simpread", + "simpread [conn_id] [type] [pattern]\n\r", + "Read simple ble service characteristic and descriptor value\r\n\ + [type]: 0-(read v1), 1-(v3 cccd), 2-(v4 cccd)\r\n\ + [pattern]: 0-(read by handle), 1-(read by uuid)\r\n\ + sample: simpread 0 1 0 \n\r", + cmd_simpread + }, + * \endcode + */ +static T_USER_CMD_PARSE_RESULT cmd_simpread(T_USER_CMD_PARSED_VALUE *p_parse_value) +{ + /* Indicate which char to be read. */ + uint8_t conn_id = p_parse_value->dw_param[0]; + bool ret; + T_SIMP_READ_TYPE read_type = (T_SIMP_READ_TYPE)p_parse_value->dw_param[1]; + /* Read by handle or UUID, 1--by UUID, 0--by handle. */ + uint8_t read_pattern = (uint8_t)p_parse_value->dw_param[2]; + + if (read_pattern) + { + ret = simp_ble_client_read_by_uuid(conn_id, read_type); + } + else + { + ret = simp_ble_client_read_by_handle(conn_id, read_type); + } + + if (ret) + { + return (RESULT_SUCCESS); + } + else + { + return (RESULT_ERR); + } +} + +/** + * @brief Config simple ble service client characteristic configuration descriptor value + * + * <b>Command table define</b> + * \code{.c} + { + "simpcccd", + "simpcccd [conn_id] [type] [enable]\n\r", + "Config simple ble service client characteristic configuration descriptor value\r\n\ + [type]: 0-(v3 notify), 1-(v4 indication)\r\n\ + [enable]: 0-(disable), 1-(enable) \r\n\ + sample: simpcccd 0 1 1\n\r", + cmd_simpcccd + }, + * \endcode + */ +static T_USER_CMD_PARSE_RESULT cmd_simpcccd(T_USER_CMD_PARSED_VALUE *p_parse_value) +{ + /* Indicate which char CCCD command. */ + bool ret = false; + uint8_t conn_id = p_parse_value->dw_param[0]; + uint8_t type = p_parse_value->dw_param[1]; + /* Enable or disable, 1--enable, 0--disable. */ + bool cccd_data = (bool)p_parse_value->dw_param[2]; + + switch (type) + { + case 0:/* V3 Notify char notif enable/disable. */ + ret = simp_ble_client_set_v3_notify(conn_id, cccd_data); + break; + case 1:/* V4 Indicate char indicate enable/disable. */ + ret = simp_ble_client_set_v4_ind(conn_id, cccd_data); + break; + default: + break; + } + + if (ret) + { + return (RESULT_SUCCESS); + } + else + { + return (RESULT_ERR); + } +} + +/** + * @brief Write simple ble service V2 characteristic value + * + * <b>Command table define</b> + * \code{.c} + { + "simpwritev2", + "simpwritev2 [conn_id] [type] [len]\n\r", + "Write simple ble service V2 characteristic value\r\n\ + [type]: 1-(write request), 2-(write command) \r\n\ + [len]: type=1 len range:0-270, type=2 len range: 0-(mtu-3)\r\n\ + sample: simpwritev2 0 1 10\n\r", + cmd_simpwritev2 + }, + * \endcode + */ +static T_USER_CMD_PARSE_RESULT cmd_simpwritev2(T_USER_CMD_PARSED_VALUE *p_parse_value) +{ + uint8_t conn_id = p_parse_value->dw_param[0]; + T_GATT_WRITE_TYPE write_type = (T_GATT_WRITE_TYPE)p_parse_value->dw_param[1]; + uint8_t data[270]; + uint16_t length = 270; + uint16_t i; + for (i = 0; i < 270; i++) + { + data[i] = i; + } + if (write_type > GATT_WRITE_TYPE_CMD) + { + return (RESULT_ERR); + } + if (p_parse_value->param_count > 2) + { + length = p_parse_value->dw_param[2]; + if (length > 270) + { + length = 270; + } + } + if (simp_ble_client_write_v2_char(conn_id, length, data, write_type)) + { + return (RESULT_SUCCESS); + } + else + { + return (RESULT_ERR); + } +} + +/** + * @brief List simple ble service handle cache + * + * <b>Command table define</b> + * \code{.c} + { + "simphdl", + "simphdl [conn_id]\n\r", + "List simple ble service handle cache\n\r", + cmd_simphdl + }, + * \endcode + */ +static T_USER_CMD_PARSE_RESULT cmd_simphdl(T_USER_CMD_PARSED_VALUE *p_parse_value) +{ + uint8_t conn_id = p_parse_value->dw_param[0]; + uint16_t hdl_cache[HDL_SIMBLE_CACHE_LEN]; + uint8_t hdl_idx; + bool ret = simp_ble_client_get_hdl_cache(conn_id, hdl_cache, + sizeof(uint16_t) * HDL_SIMBLE_CACHE_LEN); + + if (ret) + { + for (hdl_idx = HDL_SIMBLE_SRV_START; hdl_idx < HDL_SIMBLE_CACHE_LEN; hdl_idx++) + { + data_uart_print("-->Index %d -- Handle 0x%x\r\n", hdl_idx, hdl_cache[hdl_idx]); + } + return (RESULT_SUCCESS); + } + else + { + return (RESULT_ERR); + } +} + +/** + * @brief Read battery service characteristic and descriptor value + * + * <b>Command table define</b> + * \code{.c} + { + "basread", + "basread [conn_id] [type]\n\r", + "Read battery service characteristic and descriptor value\r\n\ + [type]: 0-(battery level value), 1-(battery cccd)\r\n\ + sample: basread 0 1 \n\r", + cmd_basread + }, + * \endcode + */ +static T_USER_CMD_PARSE_RESULT cmd_basread(T_USER_CMD_PARSED_VALUE *p_parse_value) +{ + uint8_t conn_id = p_parse_value->dw_param[0]; + bool ret = false; + if (p_parse_value->dw_param[1] == 0) + { + ret = bas_read_battery_level(conn_id); + } + else if (p_parse_value->dw_param[1] == 1) + { + ret = bas_read_notify(conn_id); + } + + if (ret) + { + return (RESULT_SUCCESS); + } + else + { + return (RESULT_ERR); + } +} + +/** + * @brief Config battery service client characteristic configuration descriptor value + * + * <b>Command table define</b> + * \code{.c} + { + "bascccd", + "bascccd [conn_id] [notify]\n\r", + "Config battery service client characteristic configuration descriptor value\r\n\ + [notify]: 0-(disable), 1-(enable)\n\r", + cmd_bascccd + }, + * \endcode + */ +static T_USER_CMD_PARSE_RESULT cmd_bascccd(T_USER_CMD_PARSED_VALUE *p_parse_value) +{ + uint8_t conn_id = p_parse_value->dw_param[0]; + bool notify = p_parse_value->dw_param[1]; + bool ret; + ret = bas_set_notify(conn_id, notify); + + if (ret) + { + return (RESULT_SUCCESS); + } + else + { + return (RESULT_ERR); + } +} + +/** + * @brief List battery service handle cache + * + * <b>Command table define</b> + * \code{.c} + { + "bashdl", + "bashdl [conn_id]\n\r", + "List battery service handle cache\n\r", + cmd_bashdl + }, + * \endcode + */ +static T_USER_CMD_PARSE_RESULT cmd_bashdl(T_USER_CMD_PARSED_VALUE *p_parse_value) +{ + uint8_t conn_id = p_parse_value->dw_param[0]; + uint16_t hdl_cache[HDL_BAS_CACHE_LEN]; + uint8_t hdl_idx; + bool ret = bas_get_hdl_cache(conn_id, hdl_cache, + sizeof(uint16_t) * HDL_BAS_CACHE_LEN); + + if (ret) + { + for (hdl_idx = HDL_BAS_SRV_START; hdl_idx < HDL_BAS_CACHE_LEN; hdl_idx++) + { + data_uart_print("-->Index %d -- Handle 0x%x\r\n", hdl_idx, hdl_cache[hdl_idx]); + } + return (RESULT_SUCCESS); + } + else + { + return (RESULT_ERR); + } +} + +/** @brief User command table */ +const T_USER_CMD_TABLE_ENTRY user_cmd_table[] = +{ + /************************** Common cmd *************************************/ + { + "conupdreq", + "conupdreq [conn_id] [interval_min] [interval_max] [latency] [supervision_timeout]\n\r", + "LE connection param update request\r\n\ + sample: conupdreq 0 0x30 0x40 0 500\n\r", + cmd_conupdreq + }, + { + "showcon", + "showcon\n\r", + "Show all devices connecting status\n\r", + cmd_showcon + }, + { + "disc", + "disc [conn_id]\n\r", + "Disconnect to remote device\n\r", + cmd_disc + }, + { + "authmode", + "authmode [auth_flags] [io_cap] [sec_enable] [oob_enable]\n\r", + "Config authentication mode\r\n\ + [auth_flags]:authentication req bit field: bit0-(bonding), bit2-(MITM), bit3-(SC)\r\n\ + [io_cap]:set io Capabilities: 0-(display only), 1-(display yes/no), 2-(keyboard noly), 3-(no IO), 4-(keyboard display)\r\n\ + [sec_enable]:Start smp pairing procedure when connected: 0-(disable), 1-(enable)\r\n\ + [oob_enable]:Enable oob flag: 0-(disable), 1-(enable)\r\n\ + sample: authmode 0x5 2 1 0\n\r", + cmd_authmode + }, + { + "sauth", + "sauth [conn_id]\n\r", + "Send authentication request\n\r", + cmd_sauth + }, + { + "userconf", + "userconf [conn_id] [conf]\n\r", + "Send user confirmation when show GAP_MSG_LE_BOND_USER_CONFIRMATION\r\n\ + [conf]: 0-(Reject), 1-(Accept)\r\n\ + sample: userconf 0 1\n\r", + cmd_userconf + }, + { + "authkey", + "authkey [conn_id] [passkey]\n\r", + "Input passkey when show GAP_MSG_LE_BOND_PASSKEY_INPUT\r\n\ + [passkey]: 0 - 999999\r\n\ + sample: authkey 0 123456\n\r", + cmd_authkey + }, + { + "bondinfo", + "bondinfo\n\r", + "Get all Bonded devices information\n\r", + cmd_bondinfo + }, + { + "bondclear", + "bondclear\n\r", + "Clear all bonded devices information\n\r", + cmd_bondclear + }, + /************************** Central only *************************************/ + { + "scan", + "scan [filter_policy] [filter_duplicate]\n\r", + "Start scan\r\n\ + [filter_policy]: 0-(any), 1-(whitelist), 2-(any RPA), 3-(whitelist RPA) \r\n\ + [filter_duplicate]: 0-(disable), 1-(enable) \n\r", + cmd_scan + }, + { + "stopscan", + "stopscan\n\r", + "Stop scan\n\r", + cmd_stopscan + }, + { + "showdev", + "showdev\n\r", + "Show scan dev list: filter simple ble service\n\r", + cmd_showdev + }, + { + "con", + "con [BD0] [BD1] [BD2] [BD3] [BD4] [BD5] [addr_type]\n\r", + "Connect to remote device: use address\r\n\ + [BD0] [BD1] [BD2] [BD3] [BD4] [BD5]: remote device address\r\n\ + [addr_type]: 0-(public), 1-(random)\r\n\ + sample: con x11 x22 x33 x44 x55 x66 0 \n\r", + cmd_con + }, + { + "condev", + "condev [idx]\n\r", + "Connect to remote device: use showdev to show idx\r\n\ + [idx]: use cmd showdev to show idx before use this cmd\r\n\ + sample: condev 0\n\r", + cmd_condev + }, + /************************** GATT client *************************************/ + /*GAPS client*/ + { + "gapread", + "gapread [conn_id] [type]\n\r", + "Read GAP service characteristic value\r\n\ + [type]: 0-(read device name), 1-(read appearance)\r\n\ + simple: gapread 0 0\n\r", + cmd_gapread + }, + { + "gaphdl", + "gaphdl [conn_id]\n\r", + "List GAP service handle cache\n\r", + cmd_gaphdl + }, + /*Simple ble client*/ + { + "simpread", + "simpread [conn_id] [type] [pattern]\n\r", + "Read simple ble service characteristic and descriptor value\r\n\ + [type]: 0-(read v1), 1-(v3 cccd), 2-(v4 cccd)\r\n\ + [pattern]: 0-(read by handle), 1-(read by uuid)\r\n\ + sample: simpread 0 1 0 \n\r", + cmd_simpread + }, + { + "simpcccd", + "simpcccd [conn_id] [type] [enable]\n\r", + "Config simple ble service client characteristic configuration descriptor value\r\n\ + [type]: 0-(v3 notify), 1-(v4 indication)\r\n\ + [enable]: 0-(disable), 1-(enable) \r\n\ + sample: simpcccd 0 1 1\n\r", + cmd_simpcccd + }, + { + "simpwritev2", + "simpwritev2 [conn_id] [type] [len]\n\r", + "Write simple ble service V2 characteristic value\r\n\ + [type]: 1-(write request), 2-(write command) \r\n\ + [len]: type=1 len range:0-270, type=2 len range: 0-(mtu-3)\r\n\ + sample: simpwritev2 0 1 10\n\r", + cmd_simpwritev2 + }, + { + "simphdl", + "simphdl [conn_id]\n\r", + "List simple ble service handle cache\n\r", + cmd_simphdl + }, + /*BAS client*/ + { + "basread", + "basread [conn_id] [type]\n\r", + "Read battery service characteristic and descriptor value\r\n\ + [type]: 0-(battery level value), 1-(battery cccd)\r\n\ + sample: basread 0 1 \n\r", + cmd_basread + }, + { + "bascccd", + "bascccd [conn_id] [notify]\n\r", + "Config battery service client characteristic configuration descriptor value\r\n\ + [notify]: 0-(disable), 1-(enable)\n\r", + cmd_bascccd + }, + { + "bashdl", + "bashdl [conn_id]\n\r", + "List battery service handle cache\n\r", + cmd_bashdl + }, + /* MUST be at the end: */ + { + 0, + 0, + 0, + 0 + } +}; +/** @} */ /* End of group CENTRAL_CMD */ + + diff --git a/src/sample/ble_central/user_cmd.h b/src/sample/ble_central/user_cmd.h new file mode 100644 index 0000000..c1615aa --- /dev/null +++ b/src/sample/ble_central/user_cmd.h @@ -0,0 +1,31 @@ +/** +********************************************************************************************************* +* Copyright(c) 2016, Realtek Semiconductor Corporation. All rights reserved. +********************************************************************************************************** +* @file user_cmd.h +* @brief Define user command. +* @details +* @author jane +* @date 2016-02-18 +* @version v0.1 +********************************************************************************************************* +*/ +#ifndef _USER_CMD_H_ +#define _USER_CMD_H_ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include <data_uart.h> +#include <user_cmd_parse.h> + +extern const T_USER_CMD_TABLE_ENTRY user_cmd_table[]; +extern T_USER_CMD_IF user_cmd_if; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif + |