mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 11:10:54 +03:00
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:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user