diff options
Diffstat (limited to 'src/ble/profile/client/gaps_client.c')
-rw-r--r-- | src/ble/profile/client/gaps_client.c | 500 |
1 files changed, 500 insertions, 0 deletions
diff --git a/src/ble/profile/client/gaps_client.c b/src/ble/profile/client/gaps_client.c new file mode 100644 index 0000000..81b6837 --- /dev/null +++ b/src/ble/profile/client/gaps_client.c @@ -0,0 +1,500 @@ +/** +***************************************************************************************** +* Copyright(c) 2016, Realtek Semiconductor Corporation. All rights reserved. +***************************************************************************************** + * @file gaps_client.c + * @brief GAPS client source file. + * @details + * @author jane + * @date 2016-02-18 + * @version v0.1 + ****************************************************************************** + */ + +/** Add Includes here **/ +#include <trace.h> +#include <string.h> +#include <gaps_client.h> +#include <os_mem.h> + +/******************************************************************************************************** +* local static variables defined here, only used in this source file. +********************************************************************************************************/ + +/** + * @brief GAPS Client Link control block definition. + */ +typedef struct +{ + T_GAPS_DISC_STATE disc_state; + uint16_t hdl_cache[HDL_GAPS_CACHE_LEN]; +} T_GAPS_LINK, *P_GAPS_LINK; + +/** @brief GAPS Client link table */ +static P_GAPS_LINK gaps_table; +static uint8_t gaps_link_num; + +/**< GAPS client ID. */ +static T_CLIENT_ID gaps_client = CLIENT_PROFILE_GENERAL_ID; +/**< Callback used to send data to app from GAPS client layer. */ +static P_FUN_GENERAL_APP_CB gaps_client_cb = NULL; + +/** + * @brief Used by application, to start the discovery procedure of GAP service. + * @param[in] conn_id connection ID. + * @retval true send request to upper stack success. + * @retval false send request to upper stack failed. + * + * <b>Example usage</b> + * \code{.c} + static T_USER_CMD_PARSE_RESULT cmd_gapdis(T_USER_CMD_PARSED_VALUE *p_parse_value) + { + uint8_t conn_id = p_parse_value->dw_param[0]; + bool ret = gaps_start_discovery(conn_id); + ...... + } + * \endcode + */ +bool gaps_start_discovery(uint8_t conn_id) +{ + PROFILE_PRINT_INFO0("gaps_start_discovery"); + /* First clear handle cache. */ + if (conn_id >= gaps_link_num) + { + PROFILE_PRINT_ERROR1("gaps_start_discovery: failed invalid conn_id %d", conn_id); + return false; + } + memset(&gaps_table[conn_id], 0, sizeof(T_GAPS_LINK)); + gaps_table[conn_id].disc_state = DISC_GAPS_START; + if (client_by_uuid_srv_discovery(conn_id, gaps_client, + GATT_UUID_GAP) == GAP_CAUSE_SUCCESS) + { + return true; + } + return false; +} + + +/** + * @brief Used by application, to read data from server by using handles. + * @param[in] conn_id connection ID. + * @param[in] read_type one of characteristic that has the readable property. + * @retval true send request to upper stack success. + * @retval false send request to upper stack failed. + * + * <b>Example usage</b> + * \code{.c} + 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); + ...... + } + * \endcode + */ +bool gaps_read(uint8_t conn_id, T_GAPS_READ_TYPE read_type) +{ + bool hdl_valid = false; + uint16_t handle; + uint16_t *hdl_cache; + if (conn_id >= gaps_link_num) + { + PROFILE_PRINT_ERROR1("gaps_read: failed invalid conn_id %d", conn_id); + return false; + } + hdl_cache = gaps_table[conn_id].hdl_cache; + + PROFILE_PRINT_INFO1("gaps_read: charType = %d", read_type); + + switch (read_type) + { + case GAPS_READ_DEVICE_NAME: + if (hdl_cache[HDL_GAPS_DEVICE_NAME]) + { + handle = hdl_cache[HDL_GAPS_DEVICE_NAME]; + hdl_valid = true; + } + break; + case GAPS_READ_APPEARANCE: + if (hdl_cache[HDL_GAPS_APPEARANCE]) + { + handle = hdl_cache[HDL_GAPS_APPEARANCE]; + hdl_valid = true; + } + break; + case GAPS_READ_CENTRAL_ADDR_RESOLUTION: + if (hdl_cache[HDL_GAPS_CENTRAL_ADDR_RESOLUTION]) + { + handle = hdl_cache[HDL_GAPS_CENTRAL_ADDR_RESOLUTION]; + hdl_valid = true; + } + break; + default: + return false; + } + + if (hdl_valid) + { + if (client_attr_read(conn_id, gaps_client, handle) == GAP_CAUSE_SUCCESS) + { + return true; + } + } + + PROFILE_PRINT_WARN0("gaps_read: Request fail! Please check!"); + return false; +} + +/** + * @brief Used by application, to get handle cache. + * @param[in] conn_id connection ID. + * @param[in,out] p_hdl_cache pointer of the handle cache table + * @param[in] len the length of handle cache table + * @retval true success. + * @retval false failed. + * + * <b>Example usage</b> + * \code{.c} + 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); + ...... + } + * \endcode + */ +bool gaps_get_hdl_cache(uint8_t conn_id, uint16_t *p_hdl_cache, uint8_t len) +{ + if (conn_id >= gaps_link_num) + { + PROFILE_PRINT_ERROR1("gaps_get_hdl_cache: failed invalid conn_id %d", conn_id); + return false; + } + if (gaps_table[conn_id].disc_state != DISC_GAPS_DONE) + { + PROFILE_PRINT_ERROR1("gaps_get_hdl_cache: failed invalid state %d", gaps_table[conn_id].disc_state); + return false; + } + if (len != sizeof(uint16_t) * HDL_GAPS_CACHE_LEN) + { + PROFILE_PRINT_ERROR1("gaps_get_hdl_cache: failed invalid len %d", len); + return false; + } + memcpy(p_hdl_cache, gaps_table[conn_id].hdl_cache, len); + return true; +} + +/** + * @brief Used by application, to set handle cache. + * @param[in] conn_id connection ID. + * @param[in] p_hdl_cache pointer of the handle cache table + * @param[in] len the length of handle cache table + * @retval true success. + * @retval false failed. + * + * <b>Example usage</b> + * \code{.c} + void app_discov_services(uint8_t conn_id, bool start) + { + ...... + 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); + } + ...... + } + * \endcode + */ +bool gaps_set_hdl_cache(uint8_t conn_id, uint16_t *p_hdl_cache, uint8_t len) +{ + if (conn_id >= gaps_link_num) + { + PROFILE_PRINT_ERROR1("gaps_set_hdl_cache: failed invalid conn_id %d", conn_id); + return false; + } + if (gaps_table[conn_id].disc_state != DISC_GAPS_IDLE) + { + PROFILE_PRINT_ERROR1("gaps_set_hdl_cache: failed invalid state %d", gaps_table[conn_id].disc_state); + return false; + } + if (len != sizeof(uint16_t) * HDL_GAPS_CACHE_LEN) + { + PROFILE_PRINT_ERROR1("gaps_set_hdl_cache: failed invalid len %d", len); + return false; + } + memcpy(gaps_table[conn_id].hdl_cache, p_hdl_cache, len); + gaps_table[conn_id].disc_state = DISC_GAPS_DONE; + return true; +} + +/** + * @brief Used by application, to check Resolvable Private Address Only Characteristics whether existing. + * @param[in] conn_id connection ID. + * @param[in,out] p_is_exist whether existing + * @retval true success. + * @retval false failed. + */ +bool gaps_check_resolvable_private_addr_only_char(uint8_t conn_id, bool *p_is_exist) +{ + if (conn_id >= gaps_link_num) + { + PROFILE_PRINT_ERROR1("gaps_get_hdl_cache: failed invalid conn_id %d", conn_id); + return false; + } + if (gaps_table[conn_id].disc_state != DISC_GAPS_DONE) + { + PROFILE_PRINT_ERROR1("gaps_get_hdl_cache: failed invalid state %d", gaps_table[conn_id].disc_state); + return false; + } + if (gaps_table[conn_id].hdl_cache[HDL_GAPS_RESOLVABLE_PRIVATE_ADDR_ONLY] == 0) + { + *p_is_exist = false; + } + else + { + *p_is_exist = true; + } + return true; +} + +static bool gaps_client_start_gap_char_discovery(uint8_t conn_id) +{ + uint16_t start_handle; + uint16_t end_handle; + + PROFILE_PRINT_INFO0("gap_client_start_gap_char_discovery"); + start_handle = gaps_table[conn_id].hdl_cache[HDL_GAPS_SRV_START]; + end_handle = gaps_table[conn_id].hdl_cache[HDL_GAPS_SRV_END]; + if (client_all_char_discovery(conn_id, gaps_client, start_handle, + end_handle) == GAP_CAUSE_SUCCESS) + { + return true; + } + return false; +} + +static void gaps_client_discover_state_cb(uint8_t conn_id, T_DISCOVERY_STATE discovery_state) +{ + bool cb_flag = false; + T_GAPS_CLIENT_CB_DATA cb_data; + cb_data.cb_type = GAPS_CLIENT_CB_TYPE_DISC_STATE; + + PROFILE_PRINT_INFO1("gaps_client_discover_state_cb: discovery_state %d", discovery_state); + + switch (discovery_state) + { + case DISC_STATE_SRV_DONE: + /* Indicate that service handle found. Start discover characteristic. */ + if ((gaps_table[conn_id].hdl_cache[HDL_GAPS_SRV_START] != 0) + || (gaps_table[conn_id].hdl_cache[HDL_GAPS_SRV_END] != 0)) + { + if (gaps_client_start_gap_char_discovery(conn_id) == false) + { + gaps_table[conn_id].disc_state = DISC_GAPS_FAILED; + cb_flag = true; + } + } + /* No GAP service handle found. Discover procedure complete. */ + else + { + gaps_table[conn_id].disc_state = DISC_GAPS_FAILED; + cb_flag = true; + } + break; + + case DISC_STATE_CHAR_DONE: + gaps_table[conn_id].disc_state = DISC_GAPS_DONE; + cb_flag = true; + break; + + case DISC_STATE_FAILED: + PROFILE_PRINT_ERROR0("DISC_STATE_FAILED"); + gaps_table[conn_id].disc_state = DISC_GAPS_FAILED; + cb_flag = true; + break; + default: + PROFILE_PRINT_ERROR0("Invalid Discovery State!"); + break; + } + /* Send discover state to application if needed. */ + if (cb_flag && gaps_client_cb) + { + cb_data.cb_content.disc_state = gaps_table[conn_id].disc_state; + (*gaps_client_cb)(gaps_client, conn_id, &cb_data); + } + return; +} + +static void gaps_client_discover_result_cb(uint8_t conn_id, T_DISCOVERY_RESULT_TYPE result_type, + T_DISCOVERY_RESULT_DATA result_data) +{ + PROFILE_PRINT_INFO1("gap_client_discover_result_cb: result_type = %d", result_type); + uint16_t handle; + switch (result_type) + { + case DISC_RESULT_SRV_DATA: + gaps_table[conn_id].hdl_cache[HDL_GAPS_SRV_START] = + result_data.p_srv_disc_data->att_handle; + gaps_table[conn_id].hdl_cache[HDL_GAPS_SRV_END] = + result_data.p_srv_disc_data->end_group_handle; + break; + + case DISC_RESULT_CHAR_UUID16: + handle = result_data.p_char_uuid16_disc_data->value_handle; + /* we should inform intrested handles to upper application. */ + switch (result_data.p_char_uuid16_disc_data->uuid16) + { + case GATT_UUID_CHAR_DEVICE_NAME: + gaps_table[conn_id].hdl_cache[HDL_GAPS_DEVICE_NAME] = handle; + break; + case GATT_UUID_CHAR_APPEARANCE: + gaps_table[conn_id].hdl_cache[HDL_GAPS_APPEARANCE] = handle; + break; + case GATT_UUID_CHAR_CENTRAL_ADDRESS_RESOLUTION: + gaps_table[conn_id].hdl_cache[HDL_GAPS_CENTRAL_ADDR_RESOLUTION] = handle; + PROFILE_PRINT_INFO0("GATT_UUID_CHAR_CENTRAL_ADDRESS_RESOLUTION found"); + break; + case GATT_UUID_CHAR_RESOLVABLE_PRIVATE_ADDRESS_ONLY: + gaps_table[conn_id].hdl_cache[HDL_GAPS_RESOLVABLE_PRIVATE_ADDR_ONLY] = handle; + PROFILE_PRINT_INFO0("GATT_UUID_CHAR_RESOLVABLE_PRIVATE_ADDRESS_ONLY found"); + break; + default: + /* have no interest on this handle. */ + break; + } + break; + + default: + PROFILE_PRINT_ERROR0("Invalid Discovery Result Type!"); + break; + } + + return; +} + +static void gaps_client_read_result_cb(uint8_t conn_id, uint16_t cause, + uint16_t handle, uint16_t value_size, uint8_t *p_value) +{ + T_GAPS_CLIENT_CB_DATA cb_data; + uint16_t *hdl_cache; + hdl_cache = gaps_table[conn_id].hdl_cache; + cb_data.cb_type = GAPS_CLIENT_CB_TYPE_READ_RESULT; + cb_data.cb_content.read_result.cause = cause; + + PROFILE_PRINT_INFO2("gap_client_read_result_cb: handle 0x%x, cause 0x%x", handle, cause); + + if (handle == hdl_cache[HDL_GAPS_APPEARANCE]) + { + cb_data.cb_content.read_result.type = GAPS_READ_APPEARANCE; + if (cause == GAP_SUCCESS) + { + uint16_t appearance; + if (value_size != 2) + { + PROFILE_PRINT_ERROR1("gaps_client_read_result_cb: invalid cccd len %d", value_size); + return; + } + LE_ARRAY_TO_UINT16(appearance, p_value); + cb_data.cb_content.read_result.data.appearance = appearance; + } + } + else if (handle == hdl_cache[HDL_GAPS_DEVICE_NAME]) + { + cb_data.cb_content.read_result.type = GAPS_READ_DEVICE_NAME; + if (cause == GAP_SUCCESS) + { + cb_data.cb_content.read_result.data.device_name.value_size = value_size; + cb_data.cb_content.read_result.data.device_name.p_value = p_value; + } + } + else if (handle == hdl_cache[HDL_GAPS_CENTRAL_ADDR_RESOLUTION]) + { + cb_data.cb_content.read_result.type = GAPS_READ_CENTRAL_ADDR_RESOLUTION; + if (cause == GAP_SUCCESS) + { + cb_data.cb_content.read_result.data.central_addr_res = *p_value; + } + } + else + { + return; + } + + /* Inform application the read result. */ + if (gaps_client_cb) + { + (*gaps_client_cb)(gaps_client, conn_id, &cb_data); + } + return; +} + + +static void gaps_client_disc_cb(uint8_t conn_id) +{ + PROFILE_PRINT_INFO0("gap_client_disc_cb."); + if (conn_id >= gaps_link_num) + { + PROFILE_PRINT_ERROR1("gaps_client_disc_cb: failed invalid conn_id %d", conn_id); + return; + } + memset(&gaps_table[conn_id], 0, sizeof(T_GAPS_LINK)); + return; +} +/** + * @brief GAPS Client Callbacks. +*/ +const T_FUN_CLIENT_CBS gaps_client_cbs = +{ + gaps_client_discover_state_cb, //!< Discovery State callback function pointer + gaps_client_discover_result_cb, //!< Discovery result callback function pointer + gaps_client_read_result_cb, //!< Read response callback function pointer + NULL, //!< Write result callback function pointer + NULL, //!< Notify Indicate callback function pointer + gaps_client_disc_cb //!< Link disconnection callback function pointer +}; + +/** + * @brief Add gap service client to application. + * @param[in] app_cb pointer of app callback function to handle specific client module data. + * @param[in] link_num initialize link num. + * @return Client ID of the specific client module. + * @retval 0xff failed. + * @retval other success. + * + * <b>Example usage</b> + * \code{.c} + void app_le_profile_init(void) + { + client_init(1); + gaps_client_id = gaps_add_client(app_client_callback, APP_MAX_LINKS); + } + * \endcode + */ +T_CLIENT_ID gaps_add_client(P_FUN_GENERAL_APP_CB app_cb, uint8_t link_num) +{ + uint16_t size; + if (link_num > GAPS_MAX_LINKS) + { + PROFILE_PRINT_ERROR1("gaps_add_client: invalid link_num %d", link_num); + return 0xff; + } + if (false == client_register_spec_client_cb(&gaps_client, &gaps_client_cbs)) + { + gaps_client = CLIENT_PROFILE_GENERAL_ID; + PROFILE_PRINT_ERROR0("gaps_add_client Fail !!!"); + return gaps_client; + } + PROFILE_PRINT_INFO1("gaps_add_client: client ID = %d", gaps_client); + + /* register callback for profile to inform application that some events happened. */ + gaps_client_cb = app_cb; + gaps_link_num = link_num; + size = gaps_link_num * sizeof(T_GAPS_LINK); + gaps_table = os_mem_zalloc(RAM_TYPE_DATA_ON, size); + + return gaps_client; +} + |