OVMS3-idf/components/bt/bluedroid/bta/gatt/bta_gattc_co.c

652 lines
23 KiB
C

/******************************************************************************
*
* 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 <unistd.h>
#endif /* BT_SUPPORT_NVM */
#include <string.h>
#include <stdio.h>
#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) */