/****************************************************************************** * * Copyright (C) 2009-2013 Broadcom Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ******************************************************************************/ #ifdef BT_SUPPORT_NVM #include #endif /* BT_SUPPORT_NVM */ #include #include #include "bta/bta_gattc_co.h" #include "bta/bta_gattc_ci.h" // #include "btif_util.h" #include "btm_int.h" #include "nvs.h" #include "nvs_flash.h" #include "osi/list.h" #include "esp_err.h" #include "osi/allocator.h" #if( defined BLE_INCLUDED ) && (BLE_INCLUDED == TRUE) #if( defined BTA_GATT_INCLUDED ) && (GATTC_INCLUDED == TRUE) #define GATT_CACHE_PREFIX "gatt_" #define INVALID_ADDR_NUM 0xff #define MAX_DEVICE_IN_CACHE 50 #define MAX_ADDR_LIST_CACHE_BUF 2048 #ifdef BT_SUPPORT_NVM static FILE *sCacheFD = 0; static void getFilename(char *buffer, BD_ADDR bda) { sprintf(buffer, "%s%02x%02x%02x%02x%02x%02x", GATT_CACHE_PREFIX , bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); } static void cacheClose() { if (sCacheFD != 0) { fclose(sCacheFD); sCacheFD = 0; } } static bool cacheOpen(BD_ADDR bda, bool to_save) { char fname[255] = {0}; getFilename(fname, bda); cacheClose(); sCacheFD = fopen(fname, to_save ? "w" : "r"); return (sCacheFD != 0); } static void cacheReset(BD_ADDR bda) { char fname[255] = {0}; getFilename(fname, bda); unlink(fname); } #else static const char *cache_key = "gattc_cache_key"; static const char *cache_addr = "cache_addr_tab"; nvs_handle nvs_fp; typedef struct { //save the service data in the list according to the address nvs_handle cache_fp; BOOLEAN is_open; BD_ADDR addr; hash_key_t hash_key; list_t *assoc_addr; }cache_addr_info_t; typedef struct { //save the address list in the cache nvs_handle addr_fp; BOOLEAN is_open; UINT8 num_addr; cache_addr_info_t cache_addr[MAX_DEVICE_IN_CACHE]; }cache_env_t; cache_env_t cache_env; static void getFilename(char *buffer, hash_key_t hash) { sprintf(buffer, "%s%02x%02x%02x%02x", GATT_CACHE_PREFIX, hash[0], hash[1], hash[2], hash[3]); } static void cacheClose(BD_ADDR bda) { UINT8 index = 0; if ((index = bta_gattc_co_find_addr_in_cache(bda)) != INVALID_ADDR_NUM) { if (cache_env.cache_addr[index].is_open) { nvs_close(cache_env.cache_addr[index].cache_fp); cache_env.cache_addr[index].is_open = FALSE; } } } static bool cacheOpen(BD_ADDR bda, bool to_save, UINT8 *index) { UNUSED(to_save); char fname[255] = {0}; UINT8 *assoc_addr = NULL; esp_err_t status = ESP_FAIL; hash_key_t hash_key = {0}; if (((*index = bta_gattc_co_find_addr_in_cache(bda)) != INVALID_ADDR_NUM) || ((assoc_addr = bta_gattc_co_cache_find_src_addr(bda, index)) != NULL)) { if (cache_env.cache_addr[*index].is_open) { return TRUE; } else { memcpy(hash_key, cache_env.cache_addr[*index].hash_key, sizeof(hash_key_t)); getFilename(fname, hash_key); if ((status = nvs_open(fname, NVS_READWRITE, &cache_env.cache_addr[*index].cache_fp)) == ESP_OK) { // Set the open flag to TRUE when success to open the hash file. cache_env.cache_addr[*index].is_open = TRUE; } } } return ((status == ESP_OK) ? true : false); } static void cacheReset(BD_ADDR bda) { char fname[255] = {0}; getFilename(fname, bda); UINT8 index = 0; //cache_env.cache_addr if ((index = bta_gattc_co_find_addr_in_cache(bda)) != INVALID_ADDR_NUM) { //clear the association address pending in the source address. bta_gattc_co_cache_clear_assoc_addr(bda); if (cache_env.cache_addr[index].is_open) { nvs_erase_all(cache_env.cache_addr[index].cache_fp); nvs_close(cache_env.cache_addr[index].cache_fp); cache_env.cache_addr[index].is_open = FALSE; } else { cacheOpen(bda, false, &index); if (cache_env.cache_addr[index].is_open) { nvs_erase_all(cache_env.cache_addr[index].cache_fp); nvs_close(cache_env.cache_addr[index].cache_fp); cache_env.cache_addr[index].is_open = FALSE; } else { APPL_TRACE_ERROR("%s cacheOpen failed", __func__); return; } } if(cache_env.num_addr == 0) { APPL_TRACE_ERROR("%s cache addr list error", __func__); return; } UINT8 num = cache_env.num_addr; //delete the server_bda in the addr_info list. for(UINT8 i = index; i < (num - 1); i++) { memcpy(&cache_env.cache_addr[i], &cache_env.cache_addr[i+1], sizeof(cache_addr_info_t)); } //reduced the number address counter also cache_env.num_addr--; //update addr list to nvs flash if(cache_env.num_addr > 0) { //update UINT8 *p_buf = osi_malloc(MAX_ADDR_LIST_CACHE_BUF); if(!p_buf) { APPL_TRACE_ERROR("%s malloc error", __func__); return; } UINT16 length = cache_env.num_addr*(sizeof(BD_ADDR) + sizeof(hash_key_t)); for (UINT8 i = 0; i < cache_env.num_addr; i++) { //copy the address to the buffer. memcpy(p_buf + i*(sizeof(BD_ADDR) + sizeof(hash_key_t)), cache_env.cache_addr[i].addr, sizeof(BD_ADDR)); //copy the hash key to the buffer. memcpy(p_buf + i*(sizeof(BD_ADDR) + sizeof(hash_key_t)) + sizeof(BD_ADDR), cache_env.cache_addr[i].hash_key, sizeof(hash_key_t)); } if (cache_env.is_open) { if (nvs_set_blob(cache_env.addr_fp, cache_key, p_buf, length) != ESP_OK) { APPL_TRACE_WARNING("%s, nvs set blob failed", __func__); } } osi_free(p_buf); } else { //erase if (cache_env.is_open) { nvs_erase_all(cache_env.addr_fp); nvs_close(cache_env.addr_fp); cache_env.is_open = FALSE; } else { APPL_TRACE_WARNING("cache_env status is error"); } } } } #endif /* BT_SUPPORT_NVM */ /***************************************************************************** ** Function Declarations *****************************************************************************/ /******************************************************************************* ** ** Function bta_gattc_co_cache_open ** ** Description This callout function is executed by GATTC when a GATT server ** cache is ready to be sent. ** ** Parameter server_bda: server bd address of this cache belongs to ** evt: call in event to be passed in when cache open is done. ** conn_id: connection ID of this cache operation attach to. ** to_save: open cache to save or to load. ** ** Returns void. ** *******************************************************************************/ tBTA_GATT_STATUS bta_gattc_co_cache_open(BD_ADDR server_bda, BOOLEAN to_save, UINT8 *index) { /* open NV cache and send call in */ tBTA_GATT_STATUS status = BTA_GATT_OK; if (!cacheOpen(server_bda, to_save, index)) { status = BTA_GATT_ERROR; } APPL_TRACE_DEBUG("%s() - status=%d", __func__, status); return status; } /******************************************************************************* ** ** Function bta_gattc_co_cache_load ** ** Description This callout function is executed by GATT when server cache ** is required to load. ** ** Parameter server_bda: server bd address of this cache belongs to ** evt: call in event to be passed in when cache save is done. ** num_attr: number of attribute to be save. ** attr_index: starting attribute index of the save operation. ** conn_id: connection ID of this cache operation attach to. ** Returns ** *******************************************************************************/ tBTA_GATT_STATUS bta_gattc_co_cache_load(tBTA_GATTC_NV_ATTR *attr, UINT8 index) { #if (!CONFIG_BT_STACK_NO_LOG) UINT16 num_attr = 0; #endif tBTA_GATT_STATUS status = BTA_GATT_ERROR; size_t length = 0; // Read the size of memory space required for blob nvs_get_blob(cache_env.cache_addr[index].cache_fp, cache_key, NULL, &length); // Read previously saved blob if available esp_err_t err_code = nvs_get_blob(cache_env.cache_addr[index].cache_fp, cache_key, attr, &length); #if (!CONFIG_BT_STACK_NO_LOG) num_attr = length / sizeof(tBTA_GATTC_NV_ATTR); #endif status = (err_code == ESP_OK && length != 0) ? BTA_GATT_OK : BTA_GATT_ERROR; APPL_TRACE_DEBUG("%s() - read=%d, status=%d, err_code = %d", __func__, num_attr, status, err_code); return status; } size_t bta_gattc_get_cache_attr_length(UINT8 index) { size_t length = 0; if (index == INVALID_ADDR_NUM) { return 0; } // Read the size of memory space required for blob nvs_get_blob(cache_env.cache_addr[index].cache_fp, cache_key, NULL, &length); return length; } /******************************************************************************* ** ** Function bta_gattc_co_cache_save ** ** Description This callout function is executed by GATT when a server cache ** is available to save. ** ** Parameter server_bda: server bd address of this cache belongs to ** evt: call in event to be passed in when cache save is done. ** num_attr: number of attribute to be save. ** p_attr: pointer to the list of attributes to save. ** attr_index: starting attribute index of the save operation. ** conn_id: connection ID of this cache operation attach to. ** Returns ** *******************************************************************************/ void bta_gattc_co_cache_save (BD_ADDR server_bda, UINT16 num_attr, tBTA_GATTC_NV_ATTR *p_attr_list) { tBTA_GATT_STATUS status = BTA_GATT_OK; hash_key_t hash_key = {0}; UINT8 index = INVALID_ADDR_NUM; //calculate the hash value of the attribute table which should be added to the nvs flash. hash_function_blob((unsigned char *)p_attr_list, sizeof(tBTA_GATTC_NV_ATTR)*num_attr, hash_key); //save the address list to the nvs flash bta_gattc_co_cache_addr_save(server_bda, hash_key); if (cacheOpen(server_bda, TRUE, &index)) { esp_err_t err_code = nvs_set_blob(cache_env.cache_addr[index].cache_fp, cache_key, p_attr_list, sizeof(tBTA_GATTC_NV_ATTR)*num_attr); status = (err_code == ESP_OK) ? BTA_GATT_OK : BTA_GATT_ERROR; } else { status = BTA_GATT_ERROR; } #if CONFIG_BT_STACK_NO_LOG (void) status; #endif APPL_TRACE_DEBUG("%s() wrote hash_key = %x%x%x%x, num_attr = %d, status = %d.", __func__, hash_key[0], hash_key[1], hash_key[2], hash_key[3], num_attr, status); } /******************************************************************************* ** ** Function bta_gattc_co_cache_close ** ** Description This callout function is executed by GATTC when a GATT server ** cache is written completely. ** ** Parameter server_bda: server bd address of this cache belongs to ** conn_id: connection ID of this cache operation attach to. ** ** Returns void. ** *******************************************************************************/ void bta_gattc_co_cache_close(BD_ADDR server_bda, UINT16 conn_id) { UNUSED(conn_id); //#ifdef BT_SUPPORT_NVM cacheClose(server_bda); //#endif /* BT_SUPPORT_NVM */ /* close NV when server cache is done saving or loading, does not need to do anything for now on Insight */ BTIF_TRACE_DEBUG("%s()", __FUNCTION__); } /******************************************************************************* ** ** Function bta_gattc_co_cache_reset ** ** Description This callout function is executed by GATTC to reset cache in ** application ** ** Parameter server_bda: server bd address of this cache belongs to ** ** Returns void. ** *******************************************************************************/ void bta_gattc_co_cache_reset(BD_ADDR server_bda) { cacheReset(server_bda); } void bta_gattc_co_cache_addr_init(void) { nvs_handle fp; esp_err_t err_code; UINT8 num_addr; UINT8 *p_buf = osi_malloc(MAX_ADDR_LIST_CACHE_BUF); size_t length = MAX_ADDR_LIST_CACHE_BUF; if ((err_code = nvs_open(cache_addr, NVS_READWRITE, &fp)) == ESP_OK) { cache_env.addr_fp = fp; cache_env.is_open = TRUE; // Read previously saved blob if available if ((err_code = nvs_get_blob(fp, cache_key, p_buf, &length)) != ESP_OK) { if(err_code != ESP_ERR_NVS_NOT_FOUND) { APPL_TRACE_ERROR("%s, Line = %d, nvs flash get blob data fail, err_code = 0x%x", __func__, __LINE__, err_code); } osi_free(p_buf); return; } num_addr = length / (sizeof(BD_ADDR) + sizeof(hash_key_t)); cache_env.num_addr = num_addr; //read the address from nvs flash to cache address list. for (UINT8 i = 0; i < num_addr; i++) { memcpy(cache_env.cache_addr[i].addr, p_buf + i*(sizeof(BD_ADDR) + sizeof(hash_key_t)), sizeof(BD_ADDR)); memcpy(cache_env.cache_addr[i].hash_key, p_buf + i*(sizeof(BD_ADDR) + sizeof(hash_key_t)) + sizeof(BD_ADDR), sizeof(hash_key_t)); APPL_TRACE_DEBUG("cache_addr[%x] = %x:%x:%x:%x:%x:%x", i, cache_env.cache_addr[i].addr[0], cache_env.cache_addr[i].addr[1], cache_env.cache_addr[i].addr[2], cache_env.cache_addr[i].addr[3], cache_env.cache_addr[i].addr[4], cache_env.cache_addr[i].addr[5]); APPL_TRACE_DEBUG("hash_key[%x] = %x%x%x%x", i, cache_env.cache_addr[i].hash_key[0], cache_env.cache_addr[i].hash_key[1], cache_env.cache_addr[i].hash_key[2], cache_env.cache_addr[i].hash_key[3]); bta_gattc_co_cache_new_assoc_list(cache_env.cache_addr[i].addr, i); } } else { APPL_TRACE_ERROR("%s, Line = %d, nvs flash open fail, err_code = %x", __func__, __LINE__, err_code); osi_free(p_buf); return; } osi_free(p_buf); return; } void bta_gattc_co_cache_addr_deinit(void) { if(!cache_env.is_open) { return; } nvs_close(cache_env.addr_fp); cache_env.is_open = false; for(UINT8 i = 0; i< cache_env.num_addr; i++) { cache_addr_info_t *addr_info = &cache_env.cache_addr[i]; if(addr_info) { nvs_close(addr_info->cache_fp); addr_info->is_open = false; if(addr_info->assoc_addr) { list_free(addr_info->assoc_addr); } } } } BOOLEAN bta_gattc_co_addr_in_cache(BD_ADDR bda) { UINT8 addr_index = 0; UINT8 num = cache_env.num_addr; cache_addr_info_t *addr_info = &cache_env.cache_addr[0]; for (addr_index = 0; addr_index < num; addr_index++) { if (!memcmp(addr_info->addr, bda, sizeof(BD_ADDR))) { return TRUE; } } return FALSE; } UINT8 bta_gattc_co_find_addr_in_cache(BD_ADDR bda) { UINT8 addr_index = 0; UINT8 num = cache_env.num_addr; cache_addr_info_t *addr_info = &cache_env.cache_addr[0]; for (addr_index = 0; addr_index < num; addr_index++, addr_info++) { if (!memcmp(addr_info->addr, bda, sizeof(BD_ADDR))) { return addr_index; } } return INVALID_ADDR_NUM; } UINT8 bta_gattc_co_find_hash_in_cache(hash_key_t hash_key) { UINT8 index = 0; UINT8 num = cache_env.num_addr; cache_addr_info_t *addr_info = &cache_env.cache_addr[0]; for (index = 0; index < num; index++) { if (!memcmp(addr_info->hash_key, hash_key, sizeof(hash_key_t))) { return index; } } return INVALID_ADDR_NUM; } UINT8 bta_gattc_co_get_addr_num(void) { return cache_env.num_addr; } void bta_gattc_co_get_addr_list(BD_ADDR *addr_list) { UINT8 num = cache_env.num_addr; for (UINT8 i = 0; i < num; i++) { memcpy(addr_list[i], cache_env.cache_addr[i].addr, sizeof(BD_ADDR)); } } void bta_gattc_co_cache_addr_save(BD_ADDR bd_addr, hash_key_t hash_key) { esp_err_t err_code; UINT8 num = ++cache_env.num_addr; UINT8 index = 0; UINT8 *p_buf = osi_malloc(MAX_ADDR_LIST_CACHE_BUF); // check the address list has the same hash key or not if (bta_gattc_co_find_hash_in_cache(hash_key) != INVALID_ADDR_NUM) { APPL_TRACE_DEBUG("%s(), the hash key already in the cache list.", __func__); if ((index = bta_gattc_co_find_addr_in_cache(bd_addr)) != INVALID_ADDR_NUM) { APPL_TRACE_DEBUG("%s(), the hash bd_addr already in the cache list, index = %x", __func__, index); //if the bd_addr already in the address list, update the hash key in it. memcpy(cache_env.cache_addr[index].addr, bd_addr, sizeof(BD_ADDR)); memcpy(cache_env.cache_addr[index].hash_key, hash_key, sizeof(hash_key_t)); } else { //if the bd_addr didn't in the address list, added the bd_addr to the last of the address list. memcpy(cache_env.cache_addr[num - 1].hash_key, hash_key, sizeof(hash_key_t)); memcpy(cache_env.cache_addr[num - 1].addr, bd_addr, sizeof(BD_ADDR)); } } else { APPL_TRACE_DEBUG("%s(), num = %d", __func__, num); memcpy(cache_env.cache_addr[num - 1].addr, bd_addr, sizeof(BD_ADDR)); memcpy(cache_env.cache_addr[num - 1].hash_key, hash_key, sizeof(hash_key_t)); } nvs_handle *fp = &cache_env.addr_fp; UINT16 length = num*(sizeof(BD_ADDR) + sizeof(hash_key_t)); for (UINT8 i = 0; i < num; i++) { //copy the address to the buffer. memcpy(p_buf + i*(sizeof(BD_ADDR) + sizeof(hash_key_t)), cache_env.cache_addr[i].addr, sizeof(BD_ADDR)); //copy the hash key to the buffer. memcpy(p_buf + i*(sizeof(BD_ADDR) + sizeof(hash_key_t)) + sizeof(BD_ADDR), cache_env.cache_addr[i].hash_key, sizeof(hash_key_t)); } if (cache_env.is_open) { if ((err_code = nvs_set_blob(cache_env.addr_fp, cache_key, p_buf, length)) != ESP_OK) { APPL_TRACE_WARNING("%s(), nvs set blob fail, err %d", __func__, err_code); } } else { if ((err_code = nvs_open(cache_addr, NVS_READWRITE , fp)) == ESP_OK) { cache_env.is_open = true; if (( err_code = nvs_set_blob(cache_env.addr_fp, cache_key, p_buf, length)) != ESP_OK) { APPL_TRACE_WARNING("%s(), nvs set blob fail, err %d", __func__, err_code); } } else { APPL_TRACE_ERROR("%s, Line = %d, nvs flash open fail, err_code = %x", __func__, __LINE__, err_code); } } //free the buffer after used. osi_free(p_buf); return; } BOOLEAN bta_gattc_co_cache_new_assoc_list(BD_ADDR src_addr, UINT8 index) { cache_addr_info_t *addr_info = &cache_env.cache_addr[index]; addr_info->assoc_addr = list_new(osi_free_func); return (addr_info->assoc_addr != NULL ? TRUE : FALSE); } BOOLEAN bta_gattc_co_cache_append_assoc_addr(BD_ADDR src_addr, BD_ADDR assoc_addr) { UINT8 addr_index = 0; cache_addr_info_t *addr_info; UINT8 *p_assoc_buf = osi_malloc(sizeof(BD_ADDR)); memcpy(p_assoc_buf, assoc_addr, sizeof(BD_ADDR)); if ((addr_index = bta_gattc_co_find_addr_in_cache(src_addr)) != INVALID_ADDR_NUM) { addr_info = &cache_env.cache_addr[addr_index]; if (addr_info->assoc_addr == NULL) { addr_info->assoc_addr =list_new(NULL); } return list_append(addr_info->assoc_addr, p_assoc_buf); } return FALSE; } BOOLEAN bta_gattc_co_cache_remove_assoc_addr(BD_ADDR src_addr, BD_ADDR assoc_addr) { UINT8 addr_index = 0; cache_addr_info_t *addr_info; if ((addr_index = bta_gattc_co_find_addr_in_cache(src_addr)) != INVALID_ADDR_NUM) { addr_info = &cache_env.cache_addr[addr_index]; if (addr_info->assoc_addr != NULL) { for (list_node_t *sn = list_begin(addr_info->assoc_addr); sn != list_end(addr_info->assoc_addr); sn = list_next(sn)) { void *addr = list_node(sn); if (!memcmp(addr, assoc_addr, sizeof(BD_ADDR))) { return list_remove(addr_info->assoc_addr, addr); } } //return list_remove(addr_info->assoc_addr, assoc_addr); } else { return FALSE; } } return FALSE; } UINT8* bta_gattc_co_cache_find_src_addr(BD_ADDR assoc_addr, UINT8 *index) { UINT8 num = cache_env.num_addr; cache_addr_info_t *addr_info = &cache_env.cache_addr[0]; UINT8 *addr_data; //Check the assoc_addr list is NULL or not if (addr_info->assoc_addr == NULL) { *index = INVALID_ADDR_NUM; return NULL; } for (int i = 0; i < num; i++) { for (const list_node_t *node = list_begin(addr_info->assoc_addr); node != list_end(addr_info->assoc_addr); node = list_next(node)) { addr_data = (UINT8 *)list_node(node); if (!memcmp(addr_data, assoc_addr, sizeof(BD_ADDR))) { *index = i; return (UINT8 *)addr_info->addr; } } addr_info++; if (addr_info->assoc_addr == NULL) { *index = INVALID_ADDR_NUM; return NULL; } } *index = INVALID_ADDR_NUM; return NULL; } BOOLEAN bta_gattc_co_cache_clear_assoc_addr(BD_ADDR src_addr) { UINT8 addr_index = 0; cache_addr_info_t *addr_info; if ((addr_index = bta_gattc_co_find_addr_in_cache(src_addr)) != INVALID_ADDR_NUM) { addr_info = &cache_env.cache_addr[addr_index]; if (addr_info->assoc_addr != NULL) { list_clear(addr_info->assoc_addr); } else { return FALSE; } return TRUE; } return FALSE; } #endif /* #if( defined BLE_INCLUDED ) && (BLE_INCLUDED == TRUE) */ #endif /* #if( defined BTA_GATT_INCLUDED ) && (BTA_GATT_INCLUDED == TRUE) */