fix(ble/bluedroid): fix GATT client cache and service discovery

(cherry picked from commit b3ff15ed31)

Co-authored-by: zhiweijian <zhiweijian@espressif.com>
This commit is contained in:
Zhi Wei Jian
2026-06-10 19:54:05 +08:00
committed by BOT
parent 0da4c99922
commit 8c4345b672
11 changed files with 1130 additions and 369 deletions

View File

@@ -189,7 +189,7 @@ void bta_gattc_disable(tBTA_GATTC_CB *p_cb)
*******************************************************************************/
void bta_gattc_register(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_data)
{
tBTA_GATTC cb_data;
tBTA_GATTC cb_data = {0};
UINT8 i;
tBT_UUID *p_app_uuid = &p_data->api_reg.app_uuid;
tBTA_GATTC_INT_START_IF *p_buf;
@@ -383,7 +383,7 @@ void bta_gattc_process_api_open_cancel (tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_
UINT16 event = ((BT_HDR *)p_msg)->event;
tBTA_GATTC_CLCB *p_clcb = NULL;
tBTA_GATTC_RCB *p_clreg;
tBTA_GATTC cb_data;
tBTA_GATTC cb_data = {0};
UNUSED(p_cb);
if (p_msg->api_cancel_conn.is_direct) {
@@ -425,7 +425,7 @@ void bta_gattc_process_api_open_cancel (tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_
void bta_gattc_process_enc_cmpl(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg)
{
tBTA_GATTC_RCB *p_clreg;
tBTA_GATTC cb_data;
tBTA_GATTC cb_data = {0};
UNUSED(p_cb);
p_clreg = bta_gattc_cl_get_regcb(p_msg->enc_cmpl.client_if);
@@ -451,7 +451,7 @@ void bta_gattc_process_enc_cmpl(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg)
*******************************************************************************/
void bta_gattc_cancel_open_error(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
{
tBTA_GATTC cb_data;
tBTA_GATTC cb_data = {0};
UNUSED(p_data);
memset(&cb_data, 0, sizeof(cb_data));
@@ -513,6 +513,8 @@ void bta_gattc_open(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
return;
}
memset(&gattc_data, 0, sizeof(gattc_data));
p_tcb = gatt_find_tcb_by_addr(p_data->api_conn.remote_bda, BT_TRANSPORT_LE);
if(p_tcb) {
found_app = gatt_find_specific_app_in_hold_link(p_tcb, p_clcb->p_rcb->client_if);
@@ -584,6 +586,8 @@ void bta_gattc_init_bk_conn(tBTA_GATTC_API_OPEN *p_data, tBTA_GATTC_RCB *p_clreg
tBTA_GATTC_CLCB *p_clcb;
tBTA_GATTC_DATA gattc_data;
memset(&gattc_data, 0, sizeof(gattc_data));
if (bta_gattc_mark_bg_conn(p_data->client_if, p_data->remote_bda, TRUE, FALSE)) {
/* always call open to hold a connection */
if (!GATT_Connect(p_data->client_if, p_data->remote_bda,
@@ -634,8 +638,7 @@ void bta_gattc_init_bk_conn(tBTA_GATTC_API_OPEN *p_data, tBTA_GATTC_RCB *p_clreg
void bta_gattc_cancel_bk_conn(tBTA_GATTC_API_CANCEL_OPEN *p_data)
{
tBTA_GATTC_RCB *p_clreg;
tBTA_GATTC cb_data;
tBTA_GATTC cb_data = {0};
memset(&cb_data, 0, sizeof(cb_data));
cb_data.cancel_open.status = BTA_GATT_ERROR;
cb_data.cancel_open.client_if = p_data->client_if;
@@ -667,7 +670,7 @@ void bta_gattc_cancel_bk_conn(tBTA_GATTC_API_CANCEL_OPEN *p_data)
*******************************************************************************/
void bta_gattc_cancel_open_ok(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
{
tBTA_GATTC cb_data;
tBTA_GATTC cb_data = {0};
UNUSED(p_data);
if ( p_clcb->p_rcb->p_cback ) {
@@ -691,8 +694,7 @@ void bta_gattc_cancel_open_ok(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
*******************************************************************************/
void bta_gattc_cancel_open(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
{
tBTA_GATTC cb_data;
tBTA_GATTC cb_data = {0};
if (GATT_CancelConnect(p_clcb->p_rcb->client_if, p_data->api_cancel_conn.remote_bda, TRUE)) {
bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_CANCEL_OPEN_OK_EVT, p_data);
} else {
@@ -759,12 +761,18 @@ void bta_gattc_conn(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
bta_gattc_register_service_change_notify(p_clcb->bta_conn_id, p_clcb->bda);
} else
#endif
{ /* cache is building */
APPL_TRACE_DEBUG("%s cache not found, start discovery %u", __func__, bta_gattc_cb.auto_disc);
{ /* cache miss or cache load failed */
APPL_TRACE_DEBUG("%s cache not found, auto_disc=%u", __func__, bta_gattc_cb.auto_disc);
if (bta_gattc_cb.auto_disc) {
p_clcb->p_srcb->state = BTA_GATTC_SERV_DISC;
/* cache load failure, start discovery */
bta_gattc_start_discover(p_clcb, NULL);
} else {
/* Auto discovery is disabled: roll the SRCB back to
* SERV_IDLE so it is not stuck in SERV_LOAD. The app is
* expected to drive service discovery explicitly via
* BTA_GATTC_ServiceSearchRequest() once it is ready. */
p_clcb->p_srcb->state = BTA_GATTC_SERV_IDLE;
}
}
} else { /* cache is building */
@@ -852,8 +860,7 @@ void bta_gattc_disconncback(tBTA_GATTC_RCB *p_rcb, tBTA_GATTC_DATA *p_data)
*******************************************************************************/
void bta_gattc_close_fail(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
{
tBTA_GATTC cb_data;
tBTA_GATTC cb_data = {0};
if ( p_clcb->p_rcb->p_cback ) {
memset(&cb_data, 0, sizeof(tBTA_GATTC));
cb_data.close.client_if = p_clcb->p_rcb->client_if;
@@ -883,8 +890,7 @@ void bta_gattc_close(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
}
tBTA_GATTC_CBACK *p_cback = p_clcb->p_rcb->p_cback;
tBTA_GATTC_RCB *p_clreg = p_clcb->p_rcb;
tBTA_GATTC cb_data;
tBTA_GATTC cb_data = {0};
APPL_TRACE_DEBUG("bta_gattc_close conn_id=%d", p_clcb->bta_conn_id);
cb_data.close.client_if = p_clcb->p_rcb->client_if;
@@ -908,9 +914,13 @@ void bta_gattc_close(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
(* p_cback)(BTA_GATTC_CLOSE_EVT, (tBTA_GATTC *)&cb_data);
}
// Please note that BTA_GATTC_CLOSE_EVT will run in the BTC task.
// because bta_gattc_deregister_cmpl did not execute as expected(this is a known issue),
// we will run it again in bta_gattc_clcb_dealloc_by_conn_id.
/*
* Free the CLCB in the BTA task after the close callback has been copied to
* BTC. This keeps the original close-callback semantics while avoiding BTC
* task access to core stack control blocks without locking.
*/
bta_gattc_clcb_dealloc(p_clcb);
if (p_clreg->num_clcb == 0 && p_clreg->dereg_pending) {
bta_gattc_deregister_cmpl(p_clreg);
}
@@ -1078,7 +1088,7 @@ void bta_gattc_start_discover(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
APPL_TRACE_ERROR("discovery on server failed");
bta_gattc_reset_discover_st(p_clcb->p_srcb, p_clcb->status);
//discover service complete, trigger callback
tBTA_GATTC cb_data;
tBTA_GATTC cb_data = {0};
cb_data.dis_cmpl.status = p_clcb->status;
cb_data.dis_cmpl.conn_id = p_clcb->bta_conn_id;
( *p_clcb->p_rcb->p_cback)(BTA_GATTC_DIS_SRVC_CMPL_EVT, &cb_data);
@@ -1380,7 +1390,7 @@ void bta_gattc_confirm(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
void bta_gattc_read_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_OP_CMPL *p_data)
{
UINT8 event;
tBTA_GATTC cb_data;
tBTA_GATTC cb_data = {0};
tBTA_GATT_UNFMT read_value;
if (p_clcb->p_q_cmd == NULL) {
@@ -1434,8 +1444,21 @@ void bta_gattc_write_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_OP_CMPL *p_data)
APPL_TRACE_ERROR("%s, p_data->p_cmpl is NULL", __func__);
UINT16 handle = p_clcb->p_q_cmd->api_write.handle;
tBTA_GATTC_EVT cmpl_evt = p_clcb->p_q_cmd->api_write.cmpl_evt;
tBTA_GATTC_CONN *p_conn = bta_gattc_conn_find(p_clcb->bda);
bta_gattc_free_command_data(p_clcb);
bta_gattc_pop_command_to_send(p_clcb);
/* If this completion belongs to the internal service-change CCC write,
* clear the in-progress flag and swallow the event so the application
* does not see a spurious BTA_GATTC_WRITE_DESCR_EVT, and a future
* bta_gattc_register_service_change_notify() can retry. */
if (p_conn &&
p_conn->write_remote_svc_change_ccc_in_progress &&
p_conn->svc_change_descr_handle == handle) {
p_conn->write_remote_svc_change_ccc_in_progress = FALSE;
p_conn->write_remote_svc_change_ccc_done = FALSE;
APPL_TRACE_ERROR("svc chg ccc: p_cmpl NULL");
return;
}
cb_data.write.status = BTA_GATT_ERROR;
cb_data.write.handle = handle;
cb_data.write.conn_id = p_clcb->bta_conn_id;
@@ -1452,7 +1475,7 @@ void bta_gattc_write_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_OP_CMPL *p_data)
p_clcb->p_q_cmd->api_write.write_type == BTA_GATTC_WRITE_PREPARE) {
// Check if the parameters are valid
// should not happen, but just in case
if (p_clcb->p_q_cmd->api_write.p_value == NULL) {
if (p_clcb->p_q_cmd->api_write.p_value == NULL && p_clcb->p_q_cmd->api_write.len != 0) {
APPL_TRACE_ERROR("%s, p_clcb->p_q_cmd->api_write.p_value is NULL", __func__);
bta_gattc_free_command_data(p_clcb);
bta_gattc_pop_command_to_send(p_clcb);
@@ -1461,10 +1484,21 @@ void bta_gattc_write_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_OP_CMPL *p_data)
( *p_clcb->p_rcb->p_cback)(BTA_GATTC_PREP_WRITE_EVT, (tBTA_GATTC *)&cb_data);
return;
}
// Should check the value received from the peer device is correct or not.
if (memcmp(p_clcb->p_q_cmd->api_write.p_value, p_data->p_cmpl->att_value.value,
p_data->p_cmpl->att_value.len) != 0) {
cb_data.write.status = BTA_GATT_INVALID_PDU;
/* Rsp value is one ATT chunk (<= MTU-5), not necessarily full api_write.len. */
{
UINT16 rsp_len = p_data->p_cmpl->att_value.len;
UINT16 req_len = p_clcb->p_q_cmd->api_write.len;
tGATT_VALUE *a = &p_data->p_cmpl->att_value;
tBTA_GATTC_API_WRITE *w = &p_clcb->p_q_cmd->api_write;
if (a->handle != w->handle || a->offset != w->offset || rsp_len > req_len ||
(req_len > 0 && rsp_len == 0) ||
(rsp_len > 0 && w->p_value != NULL &&
memcmp(w->p_value, a->value, rsp_len) != 0)) {
APPL_TRACE_ERROR("%s prep_write rsp bad h %u/%u o %u/%u len %u/%u", __func__,
a->handle, w->handle, a->offset, w->offset, rsp_len, req_len);
cb_data.write.status = BTA_GATT_INVALID_PDU;
}
}
event = BTA_GATTC_PREP_WRITE_EVT;
@@ -1475,12 +1509,17 @@ void bta_gattc_write_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_OP_CMPL *p_data)
bta_gattc_free_command_data(p_clcb);
bta_gattc_pop_command_to_send(p_clcb);
cb_data.write.conn_id = p_clcb->bta_conn_id;
if (p_conn && p_conn->svc_change_descr_handle == cb_data.write.handle) {
if(cb_data.write.status != BTA_GATT_OK) {
if (p_conn &&
p_conn->write_remote_svc_change_ccc_in_progress &&
p_conn->svc_change_descr_handle == cb_data.write.handle) {
p_conn->write_remote_svc_change_ccc_in_progress = FALSE;
if (cb_data.write.status == BTA_GATT_OK) {
p_conn->write_remote_svc_change_ccc_done = TRUE;
} else {
p_conn->write_remote_svc_change_ccc_done = FALSE;
APPL_TRACE_ERROR("service change write ccc failed");
}
return;
return; /* internal CCC write: don't forward to application */
}
/* write complete, callback */
( *p_clcb->p_rcb->p_cback)(event, (tBTA_GATTC *)&cb_data);
@@ -1497,7 +1536,7 @@ void bta_gattc_write_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_OP_CMPL *p_data)
*******************************************************************************/
void bta_gattc_exec_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_OP_CMPL *p_data)
{
tBTA_GATTC cb_data;
tBTA_GATTC cb_data = {0};
//free the command data store in the queue.
bta_gattc_free_command_data(p_clcb);
bta_gattc_pop_command_to_send(p_clcb);
@@ -1522,7 +1561,7 @@ void bta_gattc_exec_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_OP_CMPL *p_data)
*******************************************************************************/
void bta_gattc_cfg_mtu_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_OP_CMPL *p_data)
{
tBTA_GATTC cb_data;
tBTA_GATTC cb_data = {0};
//free the command data store in the queue.
bta_gattc_free_command_data(p_clcb);
bta_gattc_pop_command_to_send(p_clcb);
@@ -1650,7 +1689,7 @@ void bta_gattc_ignore_op_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
void bta_gattc_search(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
{
tBTA_GATT_STATUS status = GATT_INTERNAL_ERROR;
tBTA_GATTC cb_data;
tBTA_GATTC cb_data = {0};
APPL_TRACE_DEBUG("bta_gattc_search conn_id=%d", p_clcb->bta_conn_id);
if (p_clcb->p_srcb && p_clcb->p_srcb->p_srvc_cache) {
status = BTA_GATT_OK;
@@ -1782,7 +1821,7 @@ void bta_gattc_deregister_cmpl(tBTA_GATTC_RCB *p_clreg)
{
tBTA_GATTC_CB *p_cb = &bta_gattc_cb;
tBTA_GATTC_IF client_if = p_clreg->client_if;
tBTA_GATTC cb_data;
tBTA_GATTC cb_data = {0};
tBTA_GATTC_CBACK *p_cback = p_clreg->p_cback;
memset(&cb_data, 0, sizeof(tBTA_GATTC));
@@ -1910,7 +1949,7 @@ static void bta_gattc_enc_cmpl_cback(tGATT_IF gattc_if, BD_ADDR bda)
*******************************************************************************/
void bta_gattc_process_api_refresh(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg)
{
tBTA_GATTC_SERV *p_srvc_cb = bta_gattc_find_srvr_cache(p_msg->api_refresh.remote_bda);
tBTA_GATTC_SERV *p_srvc_cb;
tBTA_GATTC_CLCB *p_clcb = &bta_gattc_cb.clcb[0];
BOOLEAN found = FALSE;
UINT8 i;
@@ -1918,6 +1957,14 @@ void bta_gattc_process_api_refresh(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg)
APPL_TRACE_DEBUG("%s", __func__);
#if (GATTC_CACHE_NVS == TRUE)
if (p_msg->api_refresh.erase_flash) {
bta_gattc_cache_reset(p_msg->api_refresh.remote_bda);
}
#endif
p_srvc_cb = bta_gattc_find_srvr_cache(p_msg->api_refresh.remote_bda);
if (p_srvc_cb != NULL) {
/* try to find a CLCB */
if (p_srvc_cb->connected && p_srvc_cb->num_clcb != 0) {
@@ -2014,11 +2061,17 @@ void bta_gattc_process_api_cache_get_addr_list(tBTA_GATTC_CB *p_cb, tBTA_GATTC_D
*******************************************************************************/
void bta_gattc_process_api_cache_clean(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg)
{
tBTA_GATTC_SERV *p_srvc_cb = bta_gattc_find_srvr_cache(p_msg->api_clean.remote_bda);
tBTA_GATTC_SERV *p_srvc_cb;
UNUSED(p_cb);
APPL_TRACE_DEBUG("%s", __func__);
#if (GATTC_CACHE_NVS == TRUE)
bta_gattc_cache_reset(p_msg->api_clean.remote_bda);
#endif
p_srvc_cb = bta_gattc_find_srvr_cache(p_msg->api_clean.remote_bda);
if (p_srvc_cb != NULL && p_srvc_cb->p_srvc_cache != NULL) {
//mark it and delete the cache */
list_free(p_srvc_cb->p_srvc_cache);
@@ -2094,6 +2147,8 @@ BOOLEAN bta_gattc_process_srvc_chg_ind(UINT16 conn_id,
tBTA_GATTC_CONN *p_conn = bta_gattc_conn_find(p_clcb->bda);
if(p_conn) {
p_conn->write_remote_svc_change_ccc_done = FALSE;
p_conn->write_remote_svc_change_ccc_in_progress = FALSE;
p_conn->svc_change_descr_handle = 0;
}
bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_DISCOVER_EVT, NULL);
}
@@ -2302,7 +2357,7 @@ static void bta_gattc_cmpl_sendmsg(UINT16 conn_id, tGATTC_OPTYPE op,
********************************************************************************/
static void bta_gattc_cong_cback (UINT16 conn_id, BOOLEAN congested)
{
tBTA_GATTC cb_data;
tBTA_GATTC cb_data = {0};
cb_data.congest.conn_id = conn_id;
cb_data.congest.congested = congested;
btc_gattc_congest_callback(&cb_data);
@@ -2345,6 +2400,8 @@ void bta_gattc_init_clcb_conn(UINT8 cif, BD_ADDR remote_bda)
tBTA_GATTC_DATA gattc_data;
UINT16 conn_id;
memset(&gattc_data, 0, sizeof(gattc_data));
/* should always get the connection ID */
if (GATT_GetConnIdIfConnected(cif, remote_bda, &conn_id, BTA_GATT_TRANSPORT_LE) == FALSE) {
APPL_TRACE_ERROR("bta_gattc_init_clcb_conn ERROR: not a connected device");
@@ -2416,7 +2473,8 @@ tBTA_GATTC_FIND_SERVICE_CB bta_gattc_register_service_change_notify(UINT16 conn_
tBT_UUID gatt_service_change_uuid = {LEN_UUID_16, {GATT_UUID_GATT_SRV_CHGD}};
tBT_UUID gatt_ccc_uuid = {LEN_UUID_16, {GATT_UUID_CHAR_CLIENT_CONFIG}};
tBTA_GATTC_CONN *p_conn = bta_gattc_conn_find_alloc(remote_bda);
if(p_conn && p_conn->write_remote_svc_change_ccc_done) {
if (p_conn && (p_conn->write_remote_svc_change_ccc_done ||
p_conn->write_remote_svc_change_ccc_in_progress)) {
return SERVICE_CHANGE_CCC_WRITTEN_SUCCESS;
}
@@ -2483,17 +2541,26 @@ tBTA_GATTC_FIND_SERVICE_CB bta_gattc_register_service_change_notify(UINT16 conn_
}
if (gatt_ccc_found == TRUE){
if (p_conn) {
/*
* The in-progress flag is required so that bta_gattc_write_cmpl can
* recognize this internal CCC write and avoid forwarding the response
* to the application as a spurious BTA_GATTC_WRITE_DESCR_EVT. If the
* connection tracking slot could not be obtained (e.g. conn_track is
* full), skip the write entirely instead of issuing an untracked one.
*/
if (p_conn == NULL) {
APPL_TRACE_ERROR("%s: no conn_track, skip ccc", __func__);
result = SERVICE_CHANGE_WRITE_CCC_FAILED;
} else {
p_conn->svc_change_descr_handle = p_desc->handle;
p_conn->write_remote_svc_change_ccc_done = TRUE;
p_conn->write_remote_svc_change_ccc_in_progress = TRUE;
result = SERVICE_CHANGE_CCC_WRITTEN_SUCCESS;
uint16_t indicate_value = GATT_CLT_CONFIG_INDICATION;
tBTA_GATT_UNFMT indicate_v;
indicate_v.len = 2;
indicate_v.p_value = (uint8_t *)&indicate_value;
BTA_GATTC_WriteCharDescr (conn_id, p_desc->handle, BTA_GATTC_TYPE_WRITE, &indicate_v, BTA_GATT_AUTH_REQ_NONE);
}
result = SERVICE_CHANGE_CCC_WRITTEN_SUCCESS;
uint16_t indicate_value = GATT_CLT_CONFIG_INDICATION;
tBTA_GATT_UNFMT indicate_v;
indicate_v.len = 2;
indicate_v.p_value = (uint8_t *)&indicate_value;
BTA_GATTC_WriteCharDescr (conn_id, p_desc->handle, BTA_GATTC_TYPE_WRITE, &indicate_v, BTA_GATT_AUTH_REQ_NONE);
}
else if (gatt_service_change_found == TRUE) {
/* Gatt service char found, but service change char ccc not found,

View File

@@ -91,6 +91,7 @@ void BTA_GATTC_AppRegister(tBT_UUID *p_app_uuid, tBTA_GATTC_CBACK *p_client_cb)
}
if ((p_buf = (tBTA_GATTC_API_REG *) osi_malloc(sizeof(tBTA_GATTC_API_REG))) != NULL) {
memset(p_buf, 0, sizeof(*p_buf));
p_buf->hdr.event = BTA_GATTC_API_REG_EVT;
if (p_app_uuid != NULL) {
memcpy(&p_buf->app_uuid, p_app_uuid, sizeof(tBT_UUID));
@@ -206,6 +207,7 @@ void BTA_GATTC_CancelOpen(tBTA_GATTC_IF client_if, BD_ADDR remote_bda, BOOLEAN i
tBTA_GATTC_API_CANCEL_OPEN *p_buf;
if ((p_buf = (tBTA_GATTC_API_CANCEL_OPEN *) osi_malloc(sizeof(tBTA_GATTC_API_CANCEL_OPEN))) != NULL) {
memset(p_buf, 0, sizeof(tBTA_GATTC_API_CANCEL_OPEN));
p_buf->hdr.event = BTA_GATTC_API_CANCEL_OPEN_EVT;
p_buf->client_if = client_if;
@@ -780,6 +782,7 @@ void BTA_GATTC_PrepareWrite (UINT16 conn_id, UINT16 handle,
p_buf->handle = handle;
p_buf->write_type = BTA_GATTC_WRITE_PREPARE;
p_buf->cmpl_evt = BTA_GATTC_PREP_WRITE_EVT;
p_buf->offset = offset;
p_buf->len = len;
@@ -827,6 +830,7 @@ void BTA_GATTC_PrepareWriteCharDescr (UINT16 conn_id, UINT16 handle,
p_buf->auth_req = auth_req;
p_buf->handle = handle;
p_buf->write_type = BTA_GATTC_WRITE_PREPARE;
p_buf->cmpl_evt = BTA_GATTC_PREP_WRITE_EVT;
p_buf->offset = offset;
if (p_data && p_data->len != 0) {
@@ -1043,12 +1047,6 @@ tBTA_GATT_STATUS BTA_GATTC_DeregisterForNotifications (tBTA_GATTC_IF client_if,
*******************************************************************************/
void BTA_GATTC_Refresh(BD_ADDR remote_bda, bool erase_flash)
{
#if(GATTC_CACHE_NVS == TRUE)
if(erase_flash) {
/* used to reset cache in application */
bta_gattc_cache_reset(remote_bda);
}
#endif
//If the registration callback is NULL, return
if(bta_sys_is_register(BTA_ID_GATTC) == FALSE) {
return;
@@ -1056,8 +1054,10 @@ void BTA_GATTC_Refresh(BD_ADDR remote_bda, bool erase_flash)
tBTA_GATTC_API_CACHE_REFRESH *p_buf;
if ((p_buf = (tBTA_GATTC_API_CACHE_REFRESH *) osi_malloc(sizeof(tBTA_GATTC_API_CACHE_REFRESH))) != NULL) {
memset(p_buf, 0, sizeof(tBTA_GATTC_API_CACHE_REFRESH));
p_buf->hdr.event = BTA_GATTC_API_REFRESH_EVT;
memcpy(p_buf->remote_bda, remote_bda, BD_ADDR_LEN);
p_buf->erase_flash = erase_flash ? TRUE : FALSE;
bta_sys_sendmsg(p_buf);
}
@@ -1106,11 +1106,6 @@ void BTA_GATTC_CacheGetAddrList(tBTA_GATTC_IF client_if)
*******************************************************************************/
void BTA_GATTC_Clean(BD_ADDR remote_bda)
{
#if(GATTC_CACHE_NVS == TRUE)
/* used to reset cache in application */
bta_gattc_cache_reset(remote_bda);
#endif
tBTA_GATTC_API_CACHE_CLEAN *p_buf;
if ((p_buf = (tBTA_GATTC_API_CACHE_CLEAN *) osi_malloc(sizeof(tBTA_GATTC_API_CACHE_CLEAN))) != NULL) {

File diff suppressed because it is too large Load Diff

View File

@@ -15,9 +15,6 @@
* 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"
@@ -30,6 +27,8 @@
#include "osi/list.h"
#include "esp_err.h"
#include "osi/allocator.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#if( defined BLE_INCLUDED ) && (BLE_INCLUDED == TRUE)
#if( defined BTA_GATT_INCLUDED ) && (GATTC_INCLUDED == TRUE)
@@ -40,42 +39,6 @@
#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(void)
{
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";
@@ -98,6 +61,60 @@ typedef struct {
static cache_env_t *cache_env = NULL;
/* Protect |cache_env|; exported callouts re-enter each other (e.g. cacheOpen -> find_addr).
*
* The mutex is created exactly once from bta_gattc_co_cache_addr_init() on the
* BT host startup task before any other callout becomes reachable, so we do
* NOT need any spinlock around the creation. The *Static variant places the
* storage in .bss and avoids any allocation. */
static StaticSemaphore_t s_cache_env_mutex_buf;
static SemaphoreHandle_t s_cache_env_mutex = NULL;
/* Must be called from the BT host startup task (single-threaded context)
* before any other cache_env_* user becomes reachable. Idempotent. */
static void cache_env_mutex_init_once(void)
{
if (s_cache_env_mutex == NULL) {
s_cache_env_mutex = xSemaphoreCreateRecursiveMutexStatic(&s_cache_env_mutex_buf);
}
}
static void cache_env_lock(void)
{
if (s_cache_env_mutex != NULL) {
(void)xSemaphoreTakeRecursive(s_cache_env_mutex, portMAX_DELAY);
}
}
static void cache_env_unlock(void)
{
if (s_cache_env_mutex != NULL) {
xSemaphoreGiveRecursive(s_cache_env_mutex);
}
}
/* Format a per-device GATT cache NVS namespace name into |buffer|.
*
* Output layout: GATT_CACHE_PREFIX (5 chars: "gatt_") + sizeof(hash_key_t)*2
* hex chars (currently 8) + NUL = 14 bytes total.
*
* Contract:
* |buffer| MUST be at least NVS_KEY_NAME_MAX_SIZE (16) bytes, which is also
* the NVS limit for a namespace name. Today's 14-byte output leaves only
* 2 bytes of headroom -- if GATT_CACHE_PREFIX is ever lengthened or
* sizeof(hash_key_t) is ever increased so that
*
* strlen(GATT_CACHE_PREFIX) + 2*sizeof(hash_key_t) + 1 > NVS_KEY_NAME_MAX_SIZE
*
* this sprintf() will silently overflow the caller's stack buffer AND
* produce a namespace name that nvs_open() will reject. In that case
* switch to snprintf(buffer, NVS_KEY_NAME_MAX_SIZE, ...) and propagate
* the truncation as an error to callers.
*
* Callers (keep this list in sync if a new one is added):
* - cacheOpen() [open by current hash]
* - bta_gattc_co_cache_addr_save() [erase old namespace on hash change]
*/
static void getFilename(char *buffer, hash_key_t hash)
{
sprintf(buffer, "%s%02x%02x%02x%02x", GATT_CACHE_PREFIX,
@@ -107,6 +124,9 @@ static void getFilename(char *buffer, hash_key_t hash)
static void cacheClose(BD_ADDR bda)
{
UINT8 index = 0;
if (cache_env == NULL) {
return;
}
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);
@@ -117,8 +137,13 @@ static void cacheClose(BD_ADDR bda)
static bool cacheOpen(BD_ADDR bda, bool to_save, UINT8 *index)
{
if (cache_env == NULL) {
return false;
}
UNUSED(to_save);
char fname[255] = {0};
/* NVS namespace name is limited to (NVS_KEY_NAME_MAX_SIZE - 1) characters.
* getFilename() produces GATT_CACHE_PREFIX (5) + 8 hex chars + NUL = 14 bytes. */
char fname[NVS_KEY_NAME_MAX_SIZE] = {0};
UINT8 *assoc_addr = NULL;
esp_err_t status = ESP_FAIL;
hash_key_t hash_key = {0};
@@ -141,8 +166,9 @@ static bool cacheOpen(BD_ADDR bda, bool to_save, UINT8 *index)
static void cacheReset(BD_ADDR bda, BOOLEAN update)
{
char fname[255] = {0};
getFilename(fname, bda);
if (cache_env == NULL) {
return;
}
UINT8 index = 0;
//cache_env->cache_addr
if ((index = bta_gattc_co_find_addr_in_cache(bda)) != INVALID_ADDR_NUM) {
@@ -153,18 +179,21 @@ static void cacheReset(BD_ADDR bda, BOOLEAN update)
nvs_close(cache_env->cache_addr[index].cache_fp);
cache_env->cache_addr[index].is_open = FALSE;
} else {
cacheOpen(bda, false, &index);
if (index == INVALID_ADDR_NUM) {
APPL_TRACE_ERROR("%s INVALID ADDR NUM", __func__);
return;
}
/* The entry exists but is not currently held open in this session;
* try to (re)open it best-effort so we can erase the on-flash blob.
* The recursive cache_env lock is held throughout, so cacheOpen()'s
* internal find_addr_in_cache(bda) cannot disagree with the outer
* find above, i.e. *index will remain valid here. We deliberately
* do NOT bail out on cacheOpen() failure: the entry must still be
* removed from RAM to keep num_addr/cache_addr[] consistent and to
* avoid OOB in callers. */
(void)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;
APPL_TRACE_ERROR("%s: cacheOpen fail, evict RAM", __func__);
}
}
if(cache_env->num_addr == 0) {
@@ -180,6 +209,10 @@ static void cacheReset(BD_ADDR bda, BOOLEAN update)
cache_env->cache_addr[index].assoc_addr = NULL;
}
if (cache_env->num_addr > MAX_DEVICE_IN_CACHE) {
APPL_TRACE_WARNING("%s: num_addr %u exceeds max, clamping", __func__, cache_env->num_addr);
cache_env->num_addr = MAX_DEVICE_IN_CACHE;
}
UINT8 num = cache_env->num_addr;
//delete the server_bda in the addr_info list.
for(UINT8 i = index; i < (num - 1); i++) {
@@ -231,7 +264,6 @@ static void cacheReset(BD_ADDR bda, BOOLEAN update)
}
}
#endif /* BT_SUPPORT_NVM */
/*****************************************************************************
** Function Declarations
*****************************************************************************/
@@ -253,13 +285,19 @@ static void cacheReset(BD_ADDR bda, BOOLEAN update)
*******************************************************************************/
tBTA_GATT_STATUS bta_gattc_co_cache_open(BD_ADDR server_bda, BOOLEAN to_save, UINT8 *index)
{
cache_env_lock();
/* open NV cache and send call in */
tBTA_GATT_STATUS status = BTA_GATT_OK;
if (cache_env == NULL) {
cache_env_unlock();
return BTA_GATT_ERROR;
}
if (!cacheOpen(server_bda, to_save, index)) {
status = BTA_GATT_ERROR;
}
APPL_TRACE_DEBUG("%s() - status=%d", __func__, status);
cache_env_unlock();
return status;
}
@@ -280,11 +318,22 @@ tBTA_GATT_STATUS bta_gattc_co_cache_open(BD_ADDR server_bda, BOOLEAN to_save, UI
*******************************************************************************/
tBTA_GATT_STATUS bta_gattc_co_cache_load(tBTA_GATTC_NV_ATTR *attr, UINT8 index)
{
cache_env_lock();
#if (!CONFIG_BT_STACK_NO_LOG)
UINT16 num_attr = 0;
#endif
tBTA_GATT_STATUS status = BTA_GATT_ERROR;
size_t length = 0;
if (cache_env == NULL) {
cache_env_unlock();
return BTA_GATT_ERROR;
}
if (index >= MAX_DEVICE_IN_CACHE) {
cache_env_unlock();
return BTA_GATT_ERROR;
}
// 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
@@ -296,18 +345,28 @@ tBTA_GATT_STATUS bta_gattc_co_cache_load(tBTA_GATTC_NV_ATTR *attr, UINT8 index)
APPL_TRACE_DEBUG("%s() - read=%d, status=%d, err_code = %d",
__func__, num_attr, status, err_code);
cache_env_unlock();
return status;
}
size_t bta_gattc_get_cache_attr_length(UINT8 index)
{
cache_env_lock();
size_t length = 0;
if (index == INVALID_ADDR_NUM) {
if (cache_env == NULL) {
cache_env_unlock();
return 0;
}
if (index == INVALID_ADDR_NUM || index >= MAX_DEVICE_IN_CACHE) {
cache_env_unlock();
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);
cache_env_unlock();
return length;
}
@@ -330,6 +389,13 @@ size_t bta_gattc_get_cache_attr_length(UINT8 index)
void bta_gattc_co_cache_save (BD_ADDR server_bda, UINT16 num_attr,
tBTA_GATTC_NV_ATTR *p_attr_list)
{
cache_env_lock();
if (cache_env == NULL) {
cache_env_unlock();
return;
}
tBTA_GATT_STATUS status = BTA_GATT_OK;
hash_key_t hash_key = {0};
UINT8 index = INVALID_ADDR_NUM;
@@ -350,6 +416,7 @@ void bta_gattc_co_cache_save (BD_ADDR server_bda, UINT16 num_attr,
(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);
cache_env_unlock();
}
/*******************************************************************************
@@ -367,14 +434,14 @@ void bta_gattc_co_cache_save (BD_ADDR server_bda, UINT16 num_attr,
*******************************************************************************/
void bta_gattc_co_cache_close(BD_ADDR server_bda, UINT16 conn_id)
{
cache_env_lock();
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__);
cache_env_unlock();
}
/*******************************************************************************
@@ -391,18 +458,22 @@ void bta_gattc_co_cache_close(BD_ADDR server_bda, UINT16 conn_id)
*******************************************************************************/
void bta_gattc_co_cache_reset(BD_ADDR server_bda)
{
cache_env_lock();
cacheReset(server_bda, TRUE);
cache_env_unlock();
}
void bta_gattc_co_cache_addr_init(void)
{
cache_env_mutex_init_once();
cache_env_lock();
nvs_handle_t fp;
esp_err_t err_code;
UINT8 num_addr;
size_t length = MAX_ADDR_LIST_CACHE_BUF;
UINT8 *p_buf = osi_malloc(MAX_ADDR_LIST_CACHE_BUF);
if (p_buf == NULL) {
APPL_TRACE_ERROR("%s malloc failed!", __func__);
cache_env_unlock();
return;
}
@@ -410,6 +481,7 @@ void bta_gattc_co_cache_addr_init(void)
if (cache_env == NULL) {
APPL_TRACE_ERROR("%s malloc failed!", __func__);
osi_free(p_buf);
cache_env_unlock();
return;
}
@@ -420,19 +492,53 @@ void bta_gattc_co_cache_addr_init(void)
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) {
if (err_code == ESP_ERR_NVS_NOT_FOUND) {
length = 0;
} else {
APPL_TRACE_ERROR("%s, Line = %d, nvs flash get blob data fail, err_code = 0x%x", __func__, __LINE__, err_code);
osi_free(p_buf);
if (cache_env->is_open) {
nvs_close(cache_env->addr_fp);
cache_env->is_open = FALSE;
}
osi_free(cache_env);
cache_env = NULL;
cache_env_unlock();
return;
}
}
const size_t rec_sz = sizeof(BD_ADDR) + sizeof(hash_key_t);
if (length == 0) {
cache_env->num_addr = 0;
osi_free(p_buf);
cache_env_unlock();
return;
}
num_addr = length / (sizeof(BD_ADDR) + sizeof(hash_key_t));
cache_env->num_addr = num_addr;
if ((length % rec_sz) != 0) {
APPL_TRACE_ERROR("%s: bad blob len %zu", __func__, length);
(void)nvs_erase_key(fp, cache_key);
cache_env->num_addr = 0;
osi_free(p_buf);
if (cache_env->is_open) {
nvs_close(cache_env->addr_fp);
cache_env->is_open = FALSE;
}
osi_free(cache_env);
cache_env = NULL;
cache_env_unlock();
return;
}
size_t n_entries = length / rec_sz;
const BOOLEAN truncated = (n_entries > (size_t)MAX_DEVICE_IN_CACHE) ? TRUE : FALSE;
if (truncated) {
APPL_TRACE_WARNING("%s: trunc %zu->%d", __func__, n_entries, MAX_DEVICE_IN_CACHE);
n_entries = MAX_DEVICE_IN_CACHE;
}
cache_env->num_addr = (UINT8)n_entries;
//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));
for (UINT8 i = 0; i < cache_env->num_addr; i++) {
memcpy(cache_env->cache_addr[i].addr, p_buf + i * rec_sz, sizeof(BD_ADDR));
memcpy(cache_env->cache_addr[i].hash_key, p_buf + i * rec_sz + 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]);
@@ -440,21 +546,30 @@ void bta_gattc_co_cache_addr_init(void)
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);
}
if (truncated) {
UINT16 out_len = (UINT16)(cache_env->num_addr * rec_sz);
if (nvs_set_blob(fp, cache_key, p_buf, out_len) != ESP_OK) {
APPL_TRACE_WARNING("%s: nvs trunc fail", __func__);
}
}
} else {
APPL_TRACE_ERROR("%s, Line = %d, nvs flash open fail, err_code = %x", __func__, __LINE__, err_code);
osi_free(p_buf);
osi_free(cache_env);
cache_env = NULL;
cache_env_unlock();
return;
}
osi_free(p_buf);
return;
cache_env_unlock();
}
void bta_gattc_co_cache_addr_deinit(void)
{
cache_env_lock();
if(cache_env == NULL) {
cache_env_unlock();
return;
}
@@ -463,112 +578,222 @@ void bta_gattc_co_cache_addr_deinit(void)
if (!cache_env->is_open) {
osi_free(cache_env);
cache_env = NULL;
cache_env_unlock();
return;
}
nvs_close(cache_env->addr_fp);
cache_env->is_open = false;
for(UINT8 i = 0; i< cache_env->num_addr; i++) {
UINT8 num = cache_env->num_addr;
if (num > MAX_DEVICE_IN_CACHE) {
num = MAX_DEVICE_IN_CACHE;
}
for (UINT8 i = 0; i < num; i++) {
cache_addr_info_t *addr_info = &cache_env->cache_addr[i];
if(addr_info) {
if (addr_info->is_open) {
nvs_close(addr_info->cache_fp);
addr_info->is_open = false;
if(addr_info->assoc_addr) {
list_free(addr_info->assoc_addr);
}
}
if (addr_info->assoc_addr != NULL) {
list_free(addr_info->assoc_addr);
addr_info->assoc_addr = NULL;
}
}
osi_free(cache_env);
cache_env = NULL;
cache_env_unlock();
}
BOOLEAN bta_gattc_co_addr_in_cache(BD_ADDR bda)
{
cache_env_lock();
if (cache_env == NULL) {
cache_env_unlock();
return FALSE;
}
UINT8 addr_index = 0;
UINT8 num = cache_env->num_addr;
if (num > MAX_DEVICE_IN_CACHE) {
num = MAX_DEVICE_IN_CACHE;
}
cache_addr_info_t *addr_info = &cache_env->cache_addr[0];
for (addr_index = 0; addr_index < num; addr_index++) {
for (addr_index = 0; addr_index < num; addr_index++, addr_info++) {
if (!memcmp(addr_info->addr, bda, sizeof(BD_ADDR))) {
cache_env_unlock();
return TRUE;
}
}
cache_env_unlock();
return FALSE;
}
UINT8 bta_gattc_co_find_addr_in_cache(BD_ADDR bda)
{
cache_env_lock();
if (cache_env == NULL) {
cache_env_unlock();
return INVALID_ADDR_NUM;
}
UINT8 addr_index = 0;
UINT8 num = cache_env->num_addr;
if (num > MAX_DEVICE_IN_CACHE) {
num = MAX_DEVICE_IN_CACHE;
}
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))) {
cache_env_unlock();
return addr_index;
}
}
cache_env_unlock();
return INVALID_ADDR_NUM;
}
UINT8 bta_gattc_co_find_hash_in_cache(hash_key_t hash_key)
{
cache_env_lock();
if (cache_env == NULL) {
cache_env_unlock();
return INVALID_ADDR_NUM;
}
UINT8 index = 0;
UINT8 num = cache_env->num_addr;
if (num > MAX_DEVICE_IN_CACHE) {
num = MAX_DEVICE_IN_CACHE;
}
cache_addr_info_t *addr_info = &cache_env->cache_addr[0];
for (index = 0; index < num; index++) {
for (index = 0; index < num; index++, addr_info++) {
if (!memcmp(addr_info->hash_key, hash_key, sizeof(hash_key_t))) {
cache_env_unlock();
return index;
}
}
cache_env_unlock();
return INVALID_ADDR_NUM;
}
UINT8 bta_gattc_co_get_addr_num(void)
{
cache_env_lock();
if (cache_env == NULL) {
cache_env_unlock();
return 0;
}
return cache_env->num_addr;
if (cache_env->num_addr > MAX_DEVICE_IN_CACHE) {
cache_env_unlock();
return MAX_DEVICE_IN_CACHE;
}
UINT8 n = cache_env->num_addr;
cache_env_unlock();
return n;
}
void bta_gattc_co_get_addr_list(BD_ADDR *addr_list)
{
cache_env_lock();
if (cache_env == NULL || addr_list == NULL) {
cache_env_unlock();
return;
}
UINT8 num = cache_env->num_addr;
if (num > MAX_DEVICE_IN_CACHE) {
num = MAX_DEVICE_IN_CACHE;
}
for (UINT8 i = 0; i < num; i++) {
memcpy(addr_list[i], cache_env->cache_addr[i].addr, sizeof(BD_ADDR));
}
cache_env_unlock();
}
void bta_gattc_co_cache_addr_save(BD_ADDR bd_addr, hash_key_t hash_key)
{
cache_env_lock();
if (cache_env == NULL) {
cache_env_unlock();
return;
}
esp_err_t err_code;
UINT8 index = 0;
UINT8 new_index = cache_env->num_addr;
UINT8 *p_buf = osi_malloc(MAX_ADDR_LIST_CACHE_BUF);
if (p_buf == NULL) {
APPL_TRACE_ERROR("%s malloc failed!", __func__);
cache_env_unlock();
return;
}
if (cache_env->num_addr > MAX_DEVICE_IN_CACHE) {
APPL_TRACE_WARNING("%s: num_addr %u exceeds max, clamping", __func__, cache_env->num_addr);
cache_env->num_addr = MAX_DEVICE_IN_CACHE;
}
// check the address list has the same address or not
// for the same address, it's hash key may be change due to service change
if ((index = bta_gattc_co_find_addr_in_cache(bd_addr)) != INVALID_ADDR_NUM) {
APPL_TRACE_DEBUG("%s the bd_addr already in the cache list, index = %x", __func__, index);
/* If the hash key changed (service change on the same peer), the
* per-device NVS namespace is keyed by the OLD hash. Just overwriting
* the in-RAM hash would leave cache_fp/is_open pointing at the old
* namespace, so a subsequent cacheOpen() would short-circuit on
* is_open==TRUE and the new attribute blob would be written into the
* stale namespace. After reboot the addr table maps bd_addr to the
* NEW hash and the load path would miss (and the old namespace is
* orphaned in NVS). Erase the old namespace and clear is_open so
* the next cacheOpen() reopens with the new hash-derived filename. */
if (memcmp(cache_env->cache_addr[index].hash_key, hash_key, sizeof(hash_key_t)) != 0) {
APPL_TRACE_WARNING("%s: hash chg "MACSTR" %02x%02x%02x%02x->%02x%02x%02x%02x, erase NVS",
__func__, MAC2STR(bd_addr),
cache_env->cache_addr[index].hash_key[0], cache_env->cache_addr[index].hash_key[1],
cache_env->cache_addr[index].hash_key[2], cache_env->cache_addr[index].hash_key[3],
hash_key[0], hash_key[1], hash_key[2], hash_key[3]);
if (cache_env->cache_addr[index].is_open) {
(void)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 {
/* Not currently held open in this session; transiently open
* the old namespace just to erase its blob. Best-effort:
* ignore failures so a missing/corrupt old namespace does
* not block writing the new one. */
char fname_old[NVS_KEY_NAME_MAX_SIZE] = {0};
nvs_handle_t old_fp;
getFilename(fname_old, cache_env->cache_addr[index].hash_key);
if (nvs_open(fname_old, NVS_READWRITE, &old_fp) == ESP_OK) {
(void)nvs_erase_all(old_fp);
nvs_close(old_fp);
}
}
}
//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 (cache_env->num_addr >= MAX_DEVICE_IN_CACHE) {
while (cache_env->num_addr >= MAX_DEVICE_IN_CACHE) {
APPL_TRACE_WARNING("%s cache list full and remove the oldest addr info", __func__);
UINT8 before = cache_env->num_addr;
cacheReset(cache_env->cache_addr[0].addr, FALSE);
if (cache_env->num_addr >= before) {
APPL_TRACE_ERROR("%s: cache eviction failed", __func__);
osi_free(p_buf);
cache_env_unlock();
return;
}
}
new_index = cache_env->num_addr;
assert(new_index < MAX_DEVICE_IN_CACHE);
if (new_index >= MAX_DEVICE_IN_CACHE) {
APPL_TRACE_ERROR("%s: invalid new_index %u", __func__, new_index);
osi_free(p_buf);
cache_env_unlock();
return;
}
memcpy(cache_env->cache_addr[new_index].addr, bd_addr, sizeof(BD_ADDR));
memcpy(cache_env->cache_addr[new_index].hash_key, hash_key, sizeof(hash_key_t));
cache_env->num_addr++;
@@ -603,22 +828,45 @@ void bta_gattc_co_cache_addr_save(BD_ADDR bd_addr, hash_key_t hash_key)
//free the buffer after used.
osi_free(p_buf);
return;
cache_env_unlock();
}
BOOLEAN bta_gattc_co_cache_new_assoc_list(BD_ADDR src_addr, UINT8 index)
{
cache_env_lock();
if (cache_env == NULL) {
cache_env_unlock();
return FALSE;
}
UNUSED(src_addr);
if (index >= MAX_DEVICE_IN_CACHE) {
cache_env_unlock();
return FALSE;
}
cache_addr_info_t *addr_info = &cache_env->cache_addr[index];
/* Idempotent: if a list already exists at this slot (e.g. caller invoked
* us twice on the same index), free it first so we don't leak the prior
* list_t. The current sole caller is bta_gattc_co_cache_addr_init() which
* runs after a fresh memset, so this path is normally a no-op; the guard
* just keeps the function safe to reuse. */
if (addr_info->assoc_addr != NULL) {
list_free(addr_info->assoc_addr);
addr_info->assoc_addr = NULL;
}
addr_info->assoc_addr = list_new(osi_free_func);
return (addr_info->assoc_addr != NULL ? TRUE : FALSE);
BOOLEAN ok = (addr_info->assoc_addr != NULL ? TRUE : FALSE);
cache_env_unlock();
return ok;
}
BOOLEAN bta_gattc_co_cache_append_assoc_addr(BD_ADDR src_addr, BD_ADDR assoc_addr)
{
cache_env_lock();
UINT8 addr_index = 0;
cache_addr_info_t *addr_info;
UINT8 *p_assoc_buf = osi_malloc(sizeof(BD_ADDR));
if(!p_assoc_buf) {
cache_env_unlock();
return FALSE;
}
memcpy(p_assoc_buf, assoc_addr, sizeof(BD_ADDR));
@@ -630,6 +878,7 @@ BOOLEAN bta_gattc_co_cache_append_assoc_addr(BD_ADDR src_addr, BD_ADDR assoc_add
if (addr_info->assoc_addr == NULL) {
APPL_TRACE_ERROR("assoc_addr list creation failed");
osi_free(p_assoc_buf);
cache_env_unlock();
return FALSE;
}
@@ -638,6 +887,7 @@ BOOLEAN bta_gattc_co_cache_append_assoc_addr(BD_ADDR src_addr, BD_ADDR assoc_add
if (!memcmp(list_node(sn), assoc_addr, sizeof(BD_ADDR))) {
APPL_TRACE_WARNING("Association already exists");
osi_free(p_assoc_buf);
cache_env_unlock();
return TRUE;
}
}
@@ -645,19 +895,23 @@ BOOLEAN bta_gattc_co_cache_append_assoc_addr(BD_ADDR src_addr, BD_ADDR assoc_add
if (!list_append(addr_info->assoc_addr, p_assoc_buf)) {
APPL_TRACE_ERROR("Failed to append to assoc_addr list");
osi_free(p_assoc_buf);
cache_env_unlock();
return FALSE;
}
cache_env_unlock();
return TRUE;
} else {
osi_free(p_assoc_buf);
}
cache_env_unlock();
return FALSE;
}
BOOLEAN bta_gattc_co_cache_remove_assoc_addr(BD_ADDR src_addr, BD_ADDR assoc_addr)
{
cache_env_lock();
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) {
@@ -667,20 +921,34 @@ BOOLEAN bta_gattc_co_cache_remove_assoc_addr(BD_ADDR src_addr, BD_ADDR assoc_add
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);
BOOLEAN removed = list_remove(addr_info->assoc_addr, addr);
cache_env_unlock();
return removed;
}
}
//return list_remove(addr_info->assoc_addr, assoc_addr);
} else {
cache_env_unlock();
return FALSE;
}
}
cache_env_unlock();
return FALSE;
}
UINT8* bta_gattc_co_cache_find_src_addr(BD_ADDR assoc_addr, UINT8 *index)
{
cache_env_lock();
if (index == NULL) {
cache_env_unlock();
return NULL;
}
if (cache_env == NULL) {
*index = INVALID_ADDR_NUM;
cache_env_unlock();
return NULL;
}
UINT8 num = (cache_env->num_addr > MAX_DEVICE_IN_CACHE) ? MAX_DEVICE_IN_CACHE : cache_env->num_addr;
cache_addr_info_t *addr_info = &cache_env->cache_addr[0];
UINT8 *addr_data;
@@ -695,30 +963,37 @@ UINT8* bta_gattc_co_cache_find_src_addr(BD_ADDR assoc_addr, UINT8 *index)
addr_data = (UINT8 *)list_node(node);
if (!memcmp(addr_data, assoc_addr, sizeof(BD_ADDR))) {
*index = i;
return (UINT8 *)addr_info->addr;
UINT8 *ret = (UINT8 *)addr_info->addr;
cache_env_unlock();
return ret;
}
}
addr_info++;
}
*index = INVALID_ADDR_NUM;
cache_env_unlock();
return NULL;
}
BOOLEAN bta_gattc_co_cache_clear_assoc_addr(BD_ADDR src_addr)
{
cache_env_lock();
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);
cache_env_unlock();
return TRUE;
} else {
cache_env_unlock();
return FALSE;
}
return TRUE;
}
cache_env_unlock();
return FALSE;
}

View File

@@ -491,6 +491,12 @@ static char *gattc_evt_code(tBTA_GATTC_INT_EVT evt_code)
return "BTA_GATTC_API_READ_BY_TYPE_EVT";
case BTA_GATTC_API_READ_MULTI_VAR_EVT:
return "BTA_GATTC_API_READ_MULTI_VAR_EVT";
case BTA_GATTC_ENC_CMPL_EVT:
return "BTA_GATTC_ENC_CMPL_EVT";
case BTA_GATTC_API_CACHE_ASSOC_EVT:
return "BTA_GATTC_API_CACHE_ASSOC_EVT";
case BTA_GATTC_API_CACHE_GET_ADDR_LIST_EVT:
return "BTA_GATTC_API_CACHE_GET_ADDR_LIST_EVT";
default:
return "unknown GATTC event code";
}
@@ -541,7 +547,8 @@ uint8_t bta_gattc_cl_rcb_active_count(void)
for (uint8_t i = 0; i < BTA_GATTC_CL_MAX; i ++) {
if (bta_gattc_cb.cl_rcb[i].in_use &&
memcmp(bta_gattc_cb.cl_rcb[i].app_uuid.uu.uuid128, dm_gattc_uuid, 16)) {
(bta_gattc_cb.cl_rcb[i].app_uuid.len != LEN_UUID_128 ||
memcmp(bta_gattc_cb.cl_rcb[i].app_uuid.uu.uuid128, dm_gattc_uuid, LEN_UUID_128))) {
count++;
}
}

View File

@@ -154,9 +154,12 @@ tBTA_GATTC_CLCB *bta_gattc_find_clcb_by_conn_id (UINT16 conn_id)
tBTA_GATTC_CLCB *p_clcb = &bta_gattc_cb.clcb[0];
UINT8 i;
if (conn_id == 0 || conn_id == GATT_INVALID_CONN_ID) {
return NULL;
}
for (i = 0; i < BTA_GATTC_CLCB_MAX; i ++, p_clcb ++) {
if (p_clcb->in_use &&
p_clcb->bta_conn_id == conn_id) {
if (p_clcb->in_use && p_clcb->bta_conn_id == conn_id) {
return p_clcb;
}
}
@@ -342,7 +345,7 @@ tBTA_GATTC_SERV *bta_gattc_find_srvr_cache(BD_ADDR bda)
UINT8 i;
for (i = 0; i < BTA_GATTC_KNOWN_SR_MAX; i ++, p_srcb ++) {
if (bdcmp(p_srcb->server_bda, bda) == 0) {
if (p_srcb->in_use && bdcmp(p_srcb->server_bda, bda) == 0) {
return p_srcb;
}
}
@@ -387,7 +390,7 @@ tBTA_GATTC_SERV *bta_gattc_srcb_alloc(BD_ADDR bda)
if (!p_tcb->in_use) {
found = TRUE;
break;
} else if (!p_tcb->connected) {
} else if (!p_tcb->connected && p_tcb->num_clcb == 0) {
p_recycle = p_tcb;
}
}
@@ -417,6 +420,38 @@ tBTA_GATTC_SERV *bta_gattc_srcb_alloc(BD_ADDR bda)
return p_tcb;
}
/******************************************************************************
*
* Fixed-length prefix size of a GATTC API message in the heap buffer passed
* to bta_gattc_enqueue. Must match osi_malloc sizes in bta_gattc_api.c.
* Variable payloads (write value, search UUID) are copied separately.
*
******************************************************************************/
static size_t bta_gattc_enqueue_api_fixed_size(UINT16 event)
{
switch (event) {
case BTA_GATTC_API_READ_EVT:
case BTA_GATTC_API_READ_BY_TYPE_EVT:
return sizeof(tBTA_GATTC_API_READ);
case BTA_GATTC_API_WRITE_EVT:
return sizeof(tBTA_GATTC_API_WRITE);
case BTA_GATTC_API_EXEC_EVT:
return sizeof(tBTA_GATTC_API_EXEC);
case BTA_GATTC_API_CFG_MTU_EVT:
return sizeof(tBTA_GATTC_API_CFG_MTU);
case BTA_GATTC_API_SEARCH_EVT:
return sizeof(tBTA_GATTC_API_SEARCH);
case BTA_GATTC_API_CONFIRM_EVT:
return sizeof(tBTA_GATTC_API_CONFIRM);
case BTA_GATTC_API_READ_MULTI_EVT:
case BTA_GATTC_API_READ_MULTI_VAR_EVT:
return sizeof(tBTA_GATTC_API_READ_MULTI);
default:
APPL_TRACE_ERROR("%s: unexpected event 0x%x", __func__, event);
return 0;
}
}
static BOOLEAN bta_gattc_has_prepare_command_in_queue(tBTA_GATTC_CLCB *p_clcb)
{
assert(p_clcb != NULL);
@@ -485,14 +520,29 @@ BOOLEAN bta_gattc_enqueue(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
if (p_data->hdr.event == BTA_GATTC_API_WRITE_EVT) {
len = p_data->api_write.len;
if ((cmd_data = (tBTA_GATTC_DATA *)osi_malloc(sizeof(tBTA_GATTC_DATA) + len)) != NULL) {
memset(cmd_data, 0, sizeof(tBTA_GATTC_DATA) + len);
memcpy(cmd_data, p_data, sizeof(tBTA_GATTC_DATA));
cmd_data->api_write.p_value = (UINT8 *)(cmd_data + 1);
memcpy(cmd_data->api_write.p_value, p_data->api_write.p_value, len);
if (len > 0) {
if (p_data->api_write.p_value == NULL) {
APPL_TRACE_ERROR("%s(), write len=%u but p_value is NULL", __func__, len);
return FALSE;
}
if ((cmd_data = (tBTA_GATTC_DATA *)osi_malloc(sizeof(tBTA_GATTC_DATA) + len)) != NULL) {
memset(cmd_data, 0, sizeof(tBTA_GATTC_DATA) + len);
memcpy(cmd_data, p_data, sizeof(tBTA_GATTC_API_WRITE));
cmd_data->api_write.p_value = (UINT8 *)(cmd_data + 1);
memcpy(cmd_data->api_write.p_value, p_data->api_write.p_value, len);
} else {
APPL_TRACE_ERROR("%s(), line = %d, alloc fail, no memory.", __func__, __LINE__);
return FALSE;
}
} else {
APPL_TRACE_ERROR("%s(), line = %d, alloc fail, no memory.", __func__, __LINE__);
return FALSE;
/* len == 0: no payload to copy; keep p_value NULL like BTA_GATTC_API_SEARCH_EVT without UUID */
if ((cmd_data = (tBTA_GATTC_DATA *)osi_malloc(sizeof(tBTA_GATTC_DATA))) != NULL) {
memset(cmd_data, 0, sizeof(tBTA_GATTC_DATA));
memcpy(cmd_data, p_data, sizeof(tBTA_GATTC_API_WRITE));
} else {
APPL_TRACE_ERROR("%s(), line = %d, alloc fail, no memory.", __func__, __LINE__);
return FALSE;
}
}
} else if (p_data->hdr.event == BTA_GATTC_API_SEARCH_EVT) {
/*
@@ -547,7 +597,7 @@ BOOLEAN bta_gattc_enqueue(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
if ((cmd_data = (tBTA_GATTC_DATA *)osi_malloc(len)) != NULL) {
memset(cmd_data, 0, len);
/* Copy the structure */
memcpy(cmd_data, p_data, sizeof(tBTA_GATTC_DATA));
memcpy(cmd_data, p_data, sizeof(tBTA_GATTC_API_SEARCH));
/* Update pointer to point to the space after the structure */
cmd_data->api_search.p_srvc_uuid = (tBT_UUID *)(cmd_data + 1);
/* Copy the UUID data */
@@ -561,16 +611,22 @@ BOOLEAN bta_gattc_enqueue(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
/* p_srvc_uuid is NULL, no extra space needed (search all services) */
if ((cmd_data = (tBTA_GATTC_DATA *)osi_malloc(sizeof(tBTA_GATTC_DATA))) != NULL) {
memset(cmd_data, 0, sizeof(tBTA_GATTC_DATA));
memcpy(cmd_data, p_data, sizeof(tBTA_GATTC_DATA));
memcpy(cmd_data, p_data, sizeof(tBTA_GATTC_API_SEARCH));
} else {
APPL_TRACE_ERROR("%s(), line = %d, alloc fail, no memory.", __func__, __LINE__);
return FALSE;
}
}
} else {
size_t copy_sz = bta_gattc_enqueue_api_fixed_size(p_data->hdr.event);
if (copy_sz == 0) {
APPL_TRACE_ERROR("%s(), line = %d, unknown event for queue copy 0x%x.", __func__, __LINE__,
p_data->hdr.event);
return FALSE;
}
if ((cmd_data = (tBTA_GATTC_DATA *)osi_malloc(sizeof(tBTA_GATTC_DATA))) != NULL) {
memset(cmd_data, 0, sizeof(tBTA_GATTC_DATA));
memcpy(cmd_data, p_data, sizeof(tBTA_GATTC_DATA));
memcpy(cmd_data, p_data, copy_sz);
} else {
APPL_TRACE_ERROR("%s(), line = %d, alloc fail, no memory.", __func__, __LINE__);
return FALSE;
@@ -911,7 +967,10 @@ tBTA_GATTC_CONN *bta_gattc_conn_alloc(BD_ADDR remote_bda)
#if BTA_GATT_DEBUG == TRUE
APPL_TRACE_DEBUG("bta_gattc_conn_alloc: found conn_track[%d] available", i_conn);
#endif
p_conn->in_use = TRUE;
p_conn->in_use = TRUE;
p_conn->svc_change_descr_handle = 0;
p_conn->write_remote_svc_change_ccc_in_progress = FALSE;
p_conn->write_remote_svc_change_ccc_done = FALSE;
bdcpy(p_conn->remote_bda, remote_bda);
return p_conn;
}
@@ -979,6 +1038,9 @@ BOOLEAN bta_gattc_conn_dealloc(BD_ADDR remote_bda)
if (p_conn != NULL) {
p_conn->in_use = FALSE;
p_conn->svc_change_descr_handle = 0;
p_conn->write_remote_svc_change_ccc_in_progress = FALSE;
p_conn->write_remote_svc_change_ccc_done = FALSE;
memset(p_conn->remote_bda, 0, BD_ADDR_LEN);
return TRUE;
}

View File

@@ -211,6 +211,7 @@ typedef struct {
typedef struct {
BT_HDR hdr;
BD_ADDR remote_bda;
BOOLEAN erase_flash;
} tBTA_GATTC_API_CACHE_REFRESH;
typedef struct {
@@ -399,6 +400,8 @@ typedef struct {
BOOLEAN in_use;
BD_ADDR remote_bda;
UINT16 svc_change_descr_handle;
/* Tracks the in-flight internal write to the Service Changed CCC descriptor */
BOOLEAN write_remote_svc_change_ccc_in_progress;
BOOLEAN write_remote_svc_change_ccc_done;
} tBTA_GATTC_CONN;
@@ -559,7 +562,7 @@ extern void bta_gattc_get_db_with_operation(UINT16 conn_id,
extern void bta_gattc_get_gatt_db(UINT16 conn_id, UINT16 start_handle, UINT16 end_handle, btgatt_db_element_t **db, UINT16 *count);
extern tBTA_GATT_STATUS bta_gattc_init_cache(tBTA_GATTC_SERV *p_srvc_cb);
extern void bta_gattc_rebuild_cache(tBTA_GATTC_SERV *p_srcv, UINT16 num_attr, tBTA_GATTC_NV_ATTR *attr);
extern tBTA_GATT_STATUS bta_gattc_rebuild_cache(tBTA_GATTC_SERV *p_srcv, UINT16 num_attr, tBTA_GATTC_NV_ATTR *attr);
extern void bta_gattc_cache_save(tBTA_GATTC_SERV *p_srvc_cb, UINT16 conn_id);
extern void bta_gattc_reset_discover_st(tBTA_GATTC_SERV *p_srcb, tBTA_GATT_STATUS status);

View File

@@ -203,7 +203,7 @@ static void btc_gattc_cback(tBTA_GATTC_EVT event, tBTA_GATTC *p_data)
static void btc_gattc_app_register(btc_ble_gattc_args_t *arg)
{
tBT_UUID app_uuid;
tBT_UUID app_uuid = {0};
app_uuid.len = 2;
app_uuid.uu.uuid16 = arg->app_reg.app_id;
BTA_GATTC_AppRegister(&app_uuid, btc_gattc_cback);
@@ -991,8 +991,6 @@ void btc_gattc_cb_handler(btc_msg_t *msg)
case BTA_GATTC_CLOSE_EVT: {
tBTA_GATTC_CLOSE *close = &arg->close;
// Free gattc clcb in BTC task to avoid race condition
bta_gattc_clcb_dealloc_by_conn_id(close->conn_id);
gattc_if = close->client_if;
param.close.status = close->status;
param.close.conn_id = BTC_GATT_GET_CONN_ID(close->conn_id);

View File

@@ -390,6 +390,18 @@ tGATT_STATUS attp_send_msg_to_l2cap(tGATT_TCB *p_tcb, BT_HDR *p_toL2CAP)
if (p_tcb->att_lcid == L2CAP_ATT_CID) {
/* L2CA_SendFixedChnlData() silently drops (osi_free) the buffer when the
* ATT fixed channel is already in cong_sent state, yet still returns
* L2CAP_DW_CONGESTED. Without distinguishing this from the post-enqueue
* congestion case, the upper layer would treat a dropped PDU as "sent"
* and wait for a response that never arrives. Detect the drop path
* up-front, release the buffer here and surface it as GATT_BUSY so that
* callers go through their failure path instead. */
if (L2CA_CheckIsCongest(L2CAP_ATT_CID, p_tcb->peer_bda)) {
GATT_TRACE_WARNING("ATT fixed channel already congested, drop PDU");
osi_free(p_toL2CAP);
return GATT_BUSY;
}
l2cap_ret = L2CA_SendFixedChnlData (L2CAP_ATT_CID, p_tcb->peer_bda, p_toL2CAP);
} else {
#if (CLASSIC_BT_INCLUDED == TRUE)
@@ -403,6 +415,8 @@ tGATT_STATUS attp_send_msg_to_l2cap(tGATT_TCB *p_tcb, BT_HDR *p_toL2CAP)
GATT_TRACE_DEBUG("ATT failed to pass msg to L2CAP");
return GATT_INTERNAL_ERROR;
} else if (l2cap_ret == L2CAP_DW_CONGESTED) {
/* Buffer was enqueued by L2CAP before congestion was reported;
* L2CAP retains ownership of it. */
GATT_TRACE_DEBUG("ATT congested, message accepted");
return GATT_CONGESTED;
}

View File

@@ -1750,9 +1750,22 @@ tGATT_STATUS GATTS_HandleMultiValueNotification (UINT16 conn_id, tGATT_HLV *tupl
return GATT_ILLEGAL_PARAMETER;
}
{
UINT32 new_len = (UINT32)notif.len + 4U + (UINT32)p_hlv->length;
if (new_len > (UINT32)GATT_MAX_ATTR_LEN) {
GATT_TRACE_ERROR("%s: len %u>MAX_ATTR_LEN", __func__, (unsigned)new_len);
return GATT_ILLEGAL_PARAMETER;
}
}
UINT16_TO_STREAM(p, p_hlv->handle); //handle
UINT16_TO_STREAM(p, p_hlv->length); //length
memcpy (p, p_hlv->value, p_hlv->length); //value
if (p_hlv->length > 0) {
if (p_hlv->value == NULL) {
return GATT_ILLEGAL_PARAMETER;
}
memcpy (p, p_hlv->value, p_hlv->length); //value
}
GATT_TRACE_DEBUG("%s handle %x, length %u", __func__, p_hlv->handle, p_hlv->length);
p += p_hlv->length;
notif.len += 4 + p_hlv->length;

View File

@@ -1124,10 +1124,14 @@ BOOLEAN gatt_cl_send_next_cmd_inq(tGATT_TCB *p_tcb)
if (att_ret == GATT_SUCCESS || att_ret == GATT_CONGESTED) {
sent = TRUE;
p_cmd->to_send = FALSE;
if(p_cmd->p_cmd) {
osi_free(p_cmd->p_cmd);
p_cmd->p_cmd = NULL;
}
/* On GATT_SUCCESS / GATT_CONGESTED, L2CAP has taken ownership of
* p_cmd->p_cmd (it was either accepted normally, or enqueued just
* before the channel turned congested). The "already congested"
* drop path inside L2CA_SendFixedChnlData() is filtered out earlier
* by attp_send_msg_to_l2cap() and returned as GATT_BUSY, which
* falls into the error branch below. So we must not free the
* buffer here. */
p_cmd->p_cmd = NULL;
/* dequeue the request if is write command or sign write */
if (p_cmd->op_code != GATT_CMD_WRITE && p_cmd->op_code != GATT_SIGN_CMD_WRITE) {
@@ -1145,13 +1149,22 @@ BOOLEAN gatt_cl_send_next_cmd_inq(tGATT_TCB *p_tcb)
gatt_end_operation(p_clcb, att_ret, NULL);
}
} else {
GATT_TRACE_ERROR("gatt_cl_send_next_cmd_inq: L2CAP sent error");
GATT_TRACE_ERROR("gatt_cl_send_next_cmd_inq: L2CAP sent error, status=%d", att_ret);
/* attp_send_msg_to_l2cap() already freed p_cmd->p_cmd on failure */
p_cmd->p_cmd = NULL;
memset(p_cmd, 0, sizeof(tGATT_CMD_Q));
p_tcb->pending_cl_req ++;
p_tcb->pending_cl_req %= GATT_CL_MAX_LCB;
p_cmd->to_send = FALSE;
/* Dequeue the failing command so pending_cl_req is advanced and the
* associated p_clcb can be retrieved. */
p_clcb = gatt_cmd_dequeue(p_tcb, &rsp_code);
p_cmd = &p_tcb->cl_cmd_q[p_tcb->pending_cl_req];
/* Notify the upper layer about the failure. Without this the
* response timer is never armed (non-write ops) and the write
* completion callback is never fired, leaving the application
* stuck waiting for a callback that will never come. The p_clcb
* would also leak. */
if (p_clcb != NULL) {
gatt_end_operation(p_clcb, att_ret, NULL);
}
}
}