diff options
Diffstat (limited to 'src/ble/profile/client/lls_client.c')
-rw-r--r-- | src/ble/profile/client/lls_client.c | 623 |
1 files changed, 623 insertions, 0 deletions
diff --git a/src/ble/profile/client/lls_client.c b/src/ble/profile/client/lls_client.c new file mode 100644 index 0000000..ef47d8b --- /dev/null +++ b/src/ble/profile/client/lls_client.c @@ -0,0 +1,623 @@ +/** +***************************************************************************************** +* Copyright(c) 2016, Realtek Semiconductor Corporation. All rights reserved. +***************************************************************************************** + * @file lls_client.c + * @brief Lls BLE client source file. + * @details + * @author ken + * @date 2017-12-05 + * @version v1.0 + ****************************************************************************** + */ + +/** Add Includes here **/ +#include <trace.h> +#include <string.h> +#include <lls_client.h> +#include <os_mem.h> + +/******************************************************************************************************** +* local static variables defined here, only used in this source file. +********************************************************************************************************/ + +typedef struct +{ + T_LLS_DISC_STATE disc_state; + uint16_t hdl_cache[HDL_LLS_CACHE_LEN]; +} T_LLS_LINK, *P_LLS_LINK; + +static P_LLS_LINK lls_table; +static uint8_t lls_link_num; + +/**< lls client ID. */ +static T_CLIENT_ID lls_client = CLIENT_PROFILE_GENERAL_ID; +/**< Callback used to send data to app from lls client layer. */ +static P_FUN_GENERAL_APP_CB lls_client_cb = NULL; + +/** + * @brief Used by application, to start the discovery procedure of lls server. + * @param[in] conn_id connection ID. + * @retval true send request to upper stack success. + * @retval false send request to upper stack failed. + */ +bool lls_client_start_discovery(uint8_t conn_id) +{ + PROFILE_PRINT_INFO0("lls_client_start_discovery"); + if (conn_id >= lls_link_num) + { + PROFILE_PRINT_ERROR1("lls_client_start_discovery: failed invalid conn_id %d", conn_id); + return false; + } + /* First clear handle cache. */ + memset(&lls_table[conn_id], 0, sizeof(T_LLS_LINK)); + lls_table[conn_id].disc_state = DISC_LLS_START; + if (client_by_uuid_srv_discovery(conn_id, lls_client, + GATT_UUID_LINK_LOSS_SERVICE) == 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. + */ +bool lls_client_read_by_handle(uint8_t conn_id, T_LLS_READ_TYPE read_type) +{ + bool hdl_valid = false; + uint16_t handle; + if (conn_id >= lls_link_num) + { + PROFILE_PRINT_ERROR1("lls_client_read_by_handle: failed invalid conn_id %d", conn_id); + return false; + } + + switch (read_type) + { + case LLS_READ_PARA: + if (lls_table[conn_id].hdl_cache[HDL_LLS_PARA]) + { + handle = lls_table[conn_id].hdl_cache[HDL_LLS_PARA]; + hdl_valid = true; + } + break; + default: + return false; + } + + if (hdl_valid) + { + if (client_attr_read(conn_id, lls_client, handle) == GAP_CAUSE_SUCCESS) + { + return true; + } + } + + APP_PRINT_WARN0("lls_client_read_by_handle: Request fail! Please check!"); + return false; +} + +/** + * @brief Used by application, to read data from server by using UUIDs. + * @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. + */ +bool lls_client_read_by_uuid(uint8_t conn_id, + T_LLS_READ_TYPE read_type)//~~~~~~~~~~~~~~~~~~will delete +{ + uint16_t start_handle; + uint16_t end_handle; + uint16_t uuid16; + if (conn_id >= lls_link_num) + { + PROFILE_PRINT_ERROR1("lls_client_read_by_uuid: failed invalid conn_id %d", conn_id); + return false; + } + + switch (read_type) + { + case LLS_READ_PARA: + start_handle = lls_table[conn_id].hdl_cache[HDL_LLS_SRV_START]; + end_handle = lls_table[conn_id].hdl_cache[HDL_LLS_SRV_END]; + uuid16 = GATT_UUID_CHAR_ALERT_LEVEL; + break; + default: + return false; + } + + if (client_attr_read_using_uuid(conn_id, lls_client, start_handle, end_handle, + uuid16, NULL) == GAP_CAUSE_SUCCESS) + { + return true; + } + return false; +} + +///** +// * @brief Used by application, to enable or disable the notification of peer server's V3 Notify Characteristic. +// * @param[in] conn_id connection ID. +// * @param[in] notify 0--disable the notification, 1--enable the notification. +// * @retval true send request to upper stack success. +// * @retval false send request to upper stack failed. +// */ +//bool lls_client_set_v3_notify(uint8_t conn_id, bool notify) +//{ +// if (conn_id >= kns_link_num) +// { +// PROFILE_PRINT_ERROR1("kns_client_set_v3_notify: failed invalid conn_id %d", conn_id); +// return false; +// } + +// if (kns_table[conn_id].hdl_cache[HDL_KNS_NOTIFY_KEY_CCCD]) +// { +// uint16_t handle = kns_table[conn_id].hdl_cache[HDL_KNS_NOTIFY_KEY_CCCD]; +// uint16_t length = sizeof(uint16_t); +// uint16_t cccd_bits = notify ? 1 : 0; +// if (client_attr_write(conn_id, kns_client, GATT_WRITE_TYPE_REQ, handle, +// length, (uint8_t *)&cccd_bits) == GAP_CAUSE_SUCCESS) +// { +// return true; +// } +// } + +// APP_PRINT_WARN0("kns_client_set_v3_notify: Request fail! Please check!"); +// return false; +//} + +/** + * @brief Used by application, to enable or disable the indication of peer server's V4 Indicate Characteristic. + * @param[in] conn_id connection ID. + * @param[in] ind 0--disable the indication, 1--enable the indication. + * @retval true send request to upper stack success. + * @retval false send request to upper stack failed. + */ +//bool simp_ble_client_set_v4_ind(uint8_t conn_id, bool ind) +//{ +// if (conn_id >= simp_link_num) +// { +// PROFILE_PRINT_ERROR1("simp_ble_client_set_v4_ind: failed invalid conn_id %d", conn_id); +// return false; +// } + +// if (simp_table[conn_id].hdl_cache[HDL_SIMBLE_V4_INDICATE_CCCD]) +// { +// uint16_t handle = simp_table[conn_id].hdl_cache[HDL_SIMBLE_V4_INDICATE_CCCD]; +// uint16_t length = sizeof(uint16_t); +// uint16_t cccd_bits = ind ? 2 : 0; +// if (client_attr_write(conn_id, simp_client, GATT_WRITE_TYPE_REQ, handle, +// length, (uint8_t *)&cccd_bits) == GAP_CAUSE_SUCCESS) +// { +// return true; +// } +// } + +// APP_PRINT_WARN0("simp_ble_client_set_v4_ind: Request fail! Please check!"); +// return false; +//} + +/** + * @brief Used by application, to write data of write Characteristic. + * @param[in] conn_id connection ID. + * @param[in] length write data length + * @param[in] p_value point the value to write + * @param[in] type write type. + * @retval true send request to upper stack success. + * @retval false send request to upper stack failed. + */ +bool lls_client_write_char(uint8_t conn_id, uint16_t length, uint8_t *p_value, + T_GATT_WRITE_TYPE type) +{ + if (conn_id >= lls_link_num) + { + PROFILE_PRINT_ERROR1("lls_client_write_char: failed invalid conn_id %d", conn_id); + return false; + } + + if (lls_table[conn_id].hdl_cache[HDL_LLS_PARA]) + { + uint16_t handle = lls_table[conn_id].hdl_cache[HDL_LLS_PARA]; + if (client_attr_write(conn_id, lls_client, type, handle, + length, p_value) == GAP_CAUSE_SUCCESS) + { + return true; + } + } + + APP_PRINT_WARN0("lls_ble_client_write_char: Request fail! Please check!"); + return false; +} + +/** + * @brief Used by application, to get 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. + */ +bool lls_client_get_hdl_cache(uint8_t conn_id, uint16_t *p_hdl_cache, uint8_t len) +{ + if (conn_id >= lls_link_num) + { + PROFILE_PRINT_ERROR1("lls_client_get_hdl_cache: failed invalid conn_id %d", conn_id); + return false; + } + if (lls_table[conn_id].disc_state != DISC_LLS_DONE) + { + PROFILE_PRINT_ERROR1("lls_client_get_hdl_cache: failed invalid state %d", + lls_table[conn_id].disc_state); + return false; + } + if (len != sizeof(uint16_t) * HDL_LLS_CACHE_LEN) + { + PROFILE_PRINT_ERROR1("lls_client_get_hdl_cache: failed invalid len %d", len); + return false; + } + memcpy(p_hdl_cache, lls_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. + */ +bool lls_client_set_hdl_cache(uint8_t conn_id, uint16_t *p_hdl_cache, uint8_t len) +{ + if (conn_id >= lls_link_num) + { + PROFILE_PRINT_ERROR1("lls_client_set_hdl_cache: failed invalid conn_id %d", conn_id); + return false; + } + if (lls_table[conn_id].disc_state != DISC_LLS_IDLE) + { + PROFILE_PRINT_ERROR1("lls_client_set_hdl_cache: failed invalid state %d", + lls_table[conn_id].disc_state); + return false; + } + if (len != sizeof(uint16_t) * HDL_LLS_CACHE_LEN) + { + PROFILE_PRINT_ERROR1("lls_client_set_hdl_cache: failed invalid len %d", len); + return false; + } + memcpy(lls_table[conn_id].hdl_cache, p_hdl_cache, len); + lls_table[conn_id].disc_state = DISC_LLS_DONE; + return true; +} + +static bool lls_client_start_char_discovery(uint8_t conn_id) +{ + uint16_t start_handle; + uint16_t end_handle; + + APP_PRINT_INFO0("lls_client_start_ias_char_discovery"); + start_handle = lls_table[conn_id].hdl_cache[HDL_LLS_SRV_START]; + end_handle = lls_table[conn_id].hdl_cache[HDL_LLS_SRV_END]; + if (client_all_char_discovery(conn_id, lls_client, start_handle, + end_handle) == GAP_CAUSE_SUCCESS) + { + return true; + } + return false; +} + +//static bool lls_client_start_char_descriptor_discovery(uint8_t conn_id) +//{ +// uint16_t start_handle; +// uint16_t end_handle; + +// PROFILE_PRINT_INFO0("lls_client_start_char_descriptor_discovery"); +// start_handle = lls_table[conn_id].hdl_cache[HDL_LLS_NOTIFY_KEY]; +// end_handle = lls_table[conn_id].hdl_cache[HDL_LLS_SRV_END]; +// if (client_all_char_descriptor_discovery(conn_id, lls_client, start_handle, +// end_handle) == GAP_CAUSE_SUCCESS) +// { +// return true; +// } +// return false; +//} +static void lls_client_discover_state_cb(uint8_t conn_id, T_DISCOVERY_STATE discovery_state) +{ + bool cb_flag = false; + T_LLS_CLIENT_CB_DATA cb_data; + cb_data.cb_type = LLS_CLIENT_CB_TYPE_DISC_STATE; + + APP_PRINT_INFO1("lls_client_discover_state_cb: discovery_state %d", discovery_state); + if (lls_table[conn_id].disc_state == DISC_LLS_START) + { + uint16_t *hdl_cache; + hdl_cache = lls_table[conn_id].hdl_cache; + + switch (discovery_state) + { + case DISC_STATE_SRV_DONE: + /* Indicate that service handle found. Start discover characteristic. */ + if ((hdl_cache[HDL_LLS_SRV_START] != 0) + || (hdl_cache[HDL_LLS_SRV_END] != 0)) + { + if (lls_client_start_char_discovery(conn_id) == false) + { + lls_table[conn_id].disc_state = DISC_LLS_FAILED; + cb_flag = true; + } + } + /* No Ias BLE service handle found. Discover procedure complete. */ + else + { + lls_table[conn_id].disc_state = DISC_LLS_FAILED; + cb_flag = true; + } + break; + case DISC_STATE_CHAR_DONE: + lls_table[conn_id].disc_state = DISC_LLS_DONE; + cb_flag = true; + break; + case DISC_STATE_FAILED: + lls_table[conn_id].disc_state = DISC_LLS_FAILED; + cb_flag = true; + break; + default: + APP_PRINT_ERROR0("lls_handle_discover_state: Invalid Discovery State!"); + break; + } + } + + /* Send discover state to application if needed. */ + if (cb_flag && lls_client_cb) + { + cb_data.cb_content.disc_state = lls_table[conn_id].disc_state; + (*lls_client_cb)(lls_client, conn_id, &cb_data); + } + return; +} + +/** + * @brief Called by profile client layer, when discover result fetched. + * @param conn_id: connection ID. + * @param result_type: indicate which type of value discovered in service discovery procedure. + * @param result_data: value discovered. + * @retval None + */ +static void lls_client_discover_result_cb(uint8_t conn_id, + T_DISCOVERY_RESULT_TYPE result_type, + T_DISCOVERY_RESULT_DATA result_data) +{ + APP_PRINT_INFO1("lls_client_discover_result_cb: result_type %d", result_type); + if (lls_table[conn_id].disc_state == DISC_LLS_START) + { + uint16_t handle; + uint16_t *hdl_cache; + hdl_cache = lls_table[conn_id].hdl_cache; + + switch (result_type) + { + case DISC_RESULT_SRV_DATA: + hdl_cache[HDL_LLS_SRV_START] = result_data.p_srv_disc_data->att_handle; + hdl_cache[HDL_LLS_SRV_END] = result_data.p_srv_disc_data->end_group_handle; + break; + case DISC_RESULT_CHAR_UUID128: + break; + + case DISC_RESULT_CHAR_UUID16: + handle = result_data.p_char_uuid16_disc_data->value_handle; + switch (result_data.p_char_uuid16_disc_data->uuid16) + { + case GATT_UUID_CHAR_ALERT_LEVEL: + hdl_cache[HDL_LLS_PARA] = handle; + break; + default: + /* have no interest on this handle. */ + break; + } + break; + + case DISC_RESULT_CHAR_DESC_UUID16: + /* When use client_all_char_descriptor_discovery. */ + break; + + default: + APP_PRINT_ERROR0("lls_handle_discover_result: Invalid Discovery Result Type!"); + break; + } + } + + return; +} + +static void lls_client_read_result_cb(uint8_t conn_id, uint16_t cause, + uint16_t handle, uint16_t value_size, uint8_t *p_value) +{ + T_LLS_CLIENT_CB_DATA cb_data; + uint16_t *hdl_cache; + hdl_cache = lls_table[conn_id].hdl_cache; + + cb_data.cb_type = LLS_CLIENT_CB_TYPE_READ_RESULT; + + APP_PRINT_INFO2("lls_client_read_result_cb: handle 0x%x, cause 0x%x", handle, cause); + cb_data.cb_content.read_result.cause = cause; + + if (handle == hdl_cache[HDL_LLS_PARA]) + { + cb_data.cb_content.read_result.type = LLS_READ_PARA; + if (cause == GAP_SUCCESS) + { + cb_data.cb_content.read_result.data.v1_read.p_value = p_value; + cb_data.cb_content.read_result.data.v1_read.value_size = value_size; + } + else + { + cb_data.cb_content.read_result.data.v1_read.value_size = 0; + } + } +// else if (handle == hdl_cache[HDL_LLS_NOTIFY_KEY_CCCD]) +// { +// cb_data.cb_content.read_result.type = LLS_READ_KEY_NOTIFY_CCCD; +// if (cause == GAP_SUCCESS) +// { +// uint16_t ccc_bit; +// if (value_size != 2) +// { +// PROFILE_PRINT_ERROR1("kns_client_read_result_cb: invalid cccd len %d", value_size); +// return; +// } +// LE_ARRAY_TO_UINT16(ccc_bit, p_value); + +// if (ccc_bit & GATT_CLIENT_CHAR_CONFIG_NOTIFY) +// { +// cb_data.cb_content.read_result.data.v3_notify_cccd = true; +// } +// else +// { +// cb_data.cb_content.read_result.data.v3_notify_cccd = false; +// } +// } +// } + else + { + return; + } + /* Inform application the read result. */ + if (lls_client_cb) + { + (*lls_client_cb)(lls_client, conn_id, &cb_data); + } + + return; +} + +static void lls_client_write_result_cb(uint8_t conn_id, T_GATT_WRITE_TYPE type, + uint16_t handle, uint16_t cause, + uint8_t credits) +{ + T_LLS_CLIENT_CB_DATA cb_data; + uint16_t *hdl_cache; + hdl_cache = lls_table[conn_id].hdl_cache; + cb_data.cb_type = LLS_CLIENT_CB_TYPE_WRITE_RESULT; + + PROFILE_PRINT_INFO2("lls_client_write_result_cb: handle 0x%x, cause 0x%x", handle, cause); + cb_data.cb_content.write_result.cause = cause; + + if (handle == hdl_cache[HDL_LLS_PARA]) + { + cb_data.cb_content.write_result.type = LLS_WRITE_PARA; + } + else + { + return; + } + /* Inform application the write result. */ + if (lls_client_cb) + { + (*lls_client_cb)(lls_client, conn_id, &cb_data); + } + + return; +} + +//static T_APP_RESULT lls_client_notif_ind_result_cb(uint8_t conn_id, bool notify, +// uint16_t handle, +// uint16_t value_size, uint8_t *p_value) +//{ +// T_APP_RESULT app_result = APP_RESULT_SUCCESS; +// T_KNS_CLIENT_CB_DATA cb_data; +// uint16_t *hdl_cache; +// hdl_cache = kns_table[conn_id].hdl_cache; + +// cb_data.cb_type = KNS_CLIENT_CB_TYPE_NOTIF_IND_RESULT; + +// if (handle == hdl_cache[HDL_KNS_NOTIFY_KEY]) +// { +// cb_data.cb_content.notif_ind_data.type = KNS_KEY_NOTIFY; +// cb_data.cb_content.notif_ind_data.data.value_size = value_size; +// cb_data.cb_content.notif_ind_data.data.p_value = p_value; +// } +// else +// { +// return app_result; +// } +// /* Inform application the notif/ind result. */ +// if (kns_client_cb) +// { +// app_result = (*kns_client_cb)(kns_client, conn_id, &cb_data); +// } + +// return app_result; +//} + +static void lls_client_disconnect_cb(uint8_t conn_id) +{ + APP_PRINT_INFO0("lls_client_disconnect_cb."); + if (conn_id >= lls_link_num) + { + PROFILE_PRINT_ERROR1("lls_client_disconnect_cb: failed invalid conn_id %d", conn_id); + return; + } + memset(&lls_table[conn_id], 0, sizeof(T_LLS_LINK)); + return; +} + +/** + * @brief Ias BLE Client Callbacks. +*/ +const T_FUN_CLIENT_CBS lls_client_cbs = +{ + lls_client_discover_state_cb, //!< Discovery State callback function pointer + lls_client_discover_result_cb, //!< Discovery result callback function pointer + lls_client_read_result_cb, //!< Read response callback function pointer + lls_client_write_result_cb, //!< Write result callback function pointer + NULL,//kns_client_notif_ind_result_cb, //!< Notify Indicate callback function pointer + lls_client_disconnect_cb //!< Link disconnection callback function pointer +}; + +/** + * @brief Add lls 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); + lls_client_id = lls_ble_add_client(app_client_callback, APP_MAX_LINKS); + } + * \endcode + */ +T_CLIENT_ID lls_add_client(P_FUN_GENERAL_APP_CB app_cb, uint8_t link_num) +{ + uint16_t size; + if (link_num > LLS_MAX_LINKS) + { + PROFILE_PRINT_ERROR1("lls_add_client: invalid link_num %d", link_num); + return 0xff; + } + if (false == client_register_spec_client_cb(&lls_client, &lls_client_cbs)) + { + lls_client = CLIENT_PROFILE_GENERAL_ID; + APP_PRINT_ERROR0("lls_add_client failed"); + return lls_client; + } + APP_PRINT_INFO1("lls_add_client: lls_client %d", lls_client); + + /* register callback for profile to inform application that some events happened. */ + lls_client_cb = app_cb; + lls_link_num = link_num; + size = lls_link_num * sizeof(T_LLS_LINK); + lls_table = os_mem_zalloc(RAM_TYPE_DATA_ON, size); + + return lls_client; +} + |