mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
fix(ble/bluedroid): BLE credit, reject when p_rcb NULL, timeout and leak fixes
- l2c_int: align struct/constant types with l2c_ble/l2c_main
- l2c_api: null/state checks in L2CA_SendFixedChnlData
- l2c_ble: reject when p_rcb==NULL, add L2CAP_CMD_BLE_FLOW_CTRL_CREDIT;
l2cble_init_direct_conn int64_t timeout and link_timeout==0 fix
- l2c_link: null/state checks and cleanup in hci_disc_comp/timeout/send_to_lower
- l2c_main: free p_msg on FCR non-Basic and COC branches; fix LE credit handling;
process_l2cap_cmd bounds
- l2c_utils: credit/queue cleanup and null checks in l2cu_disconnect_chnl
(cherry picked from commit 16d523e9bf)
Co-authored-by: zhiweijian <zhiweijian@espressif.com>
This commit is contained in:
@@ -461,7 +461,7 @@ typedef struct t_l2c_linkcb {
|
||||
*/
|
||||
/* create connection retry count*/
|
||||
UINT8 retry_create_con;
|
||||
UINT32 start_time_s;
|
||||
int64_t start_time_s;
|
||||
#endif
|
||||
|
||||
#if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE)
|
||||
@@ -736,7 +736,7 @@ extern void l2c_link_timeout (tL2C_LCB *p_lcb);
|
||||
extern void l2c_info_timeout (tL2C_LCB *p_lcb);
|
||||
extern void l2c_link_check_send_pkts (tL2C_LCB *p_lcb, tL2C_CCB *p_ccb, BT_HDR *p_buf);
|
||||
extern void l2c_link_adjust_allocation (void);
|
||||
extern void l2c_link_process_num_completed_pkts (UINT8 *p);
|
||||
extern void l2c_link_process_num_completed_pkts (UINT8 *p, UINT8 evt_len);
|
||||
extern void l2c_link_process_num_completed_blocks (UINT8 controller_id, UINT8 *p, UINT16 evt_len);
|
||||
extern void l2c_link_processs_num_bufs (UINT16 num_lm_acl_bufs);
|
||||
extern UINT8 l2c_link_pkts_rcvd (UINT16 *num_pkts, UINT16 *handles);
|
||||
|
||||
@@ -1863,6 +1863,11 @@ UINT16 L2CA_SendFixedChnlData (UINT16 fixed_cid, BD_ADDR rem_bda, BT_HDR *p_buf)
|
||||
|
||||
BOOLEAN L2CA_CheckIsCongest(UINT16 fixed_cid, BD_ADDR addr)
|
||||
{
|
||||
if ((fixed_cid < L2CAP_FIRST_FIXED_CHNL) || (fixed_cid > L2CAP_LAST_FIXED_CHNL)) {
|
||||
L2CAP_TRACE_ERROR ("L2CA_CheckIsCongest() Invalid CID: 0x%04x", fixed_cid);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
tL2C_LCB *p_lcb;
|
||||
p_lcb = l2cu_find_lcb_by_bd_addr(addr, BT_TRANSPORT_LE);
|
||||
|
||||
|
||||
@@ -828,7 +828,6 @@ void l2cble_process_sig_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len)
|
||||
STREAM_TO_UINT16(mps, p);
|
||||
STREAM_TO_UINT16(credits, p);
|
||||
L2CAP_TRACE_DEBUG("%s spsm %x, scid %x", __func__, spsm, scid);
|
||||
UNUSED(spsm);
|
||||
|
||||
p_ccb = l2cu_find_ccb_by_remote_cid(p_lcb, scid);
|
||||
if (p_ccb) {
|
||||
@@ -836,12 +835,11 @@ void l2cble_process_sig_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len)
|
||||
break;
|
||||
}
|
||||
|
||||
#if 0
|
||||
p_rcb = l2cu_find_ble_rcb_by_psm(spsm);
|
||||
if (p_rcb == NULL) {
|
||||
l2cu_reject_ble_connection(p_lcb, id, L2CAP_LE_RESULT_NO_PSM);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
p_ccb = l2cu_allocate_ccb(p_lcb, 0);
|
||||
if (p_ccb == NULL) {
|
||||
@@ -862,6 +860,34 @@ void l2cble_process_sig_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len)
|
||||
l2cu_send_peer_ble_credit_based_conn_res(p_ccb, L2CAP_LE_RESULT_CONN_OK);
|
||||
break;
|
||||
}
|
||||
case L2CAP_CMD_BLE_FLOW_CTRL_CREDIT: {
|
||||
if (cmd_len < L2CAP_CMD_BLE_FLOW_CTRL_CREDIT_LEN) {
|
||||
L2CAP_TRACE_WARNING ("L2CAP - LE - flow ctrl credit too short: %d", cmd_len);
|
||||
return;
|
||||
}
|
||||
tL2C_CCB *p_ccb = NULL;
|
||||
UINT16 lcid;
|
||||
UINT16 credit;
|
||||
STREAM_TO_UINT16(lcid, p);
|
||||
STREAM_TO_UINT16(credit, p);
|
||||
|
||||
p_ccb = l2cu_find_ccb_by_cid(p_lcb, lcid);
|
||||
if (p_ccb == NULL) {
|
||||
L2CAP_TRACE_WARNING ("L2CAP - LE - flow ctrl credit for unknown CID: 0x%04x", lcid);
|
||||
break;
|
||||
}
|
||||
|
||||
if ((p_ccb->peer_conn_cfg.credits + credit) > L2CAP_LE_MAX_CREDIT) {
|
||||
L2CAP_TRACE_WARNING ("L2CAP - LE - credit overflow, disconnecting CID: 0x%04x", lcid);
|
||||
l2cble_send_peer_disc_req(p_ccb);
|
||||
} else {
|
||||
p_ccb->peer_conn_cfg.credits += credit;
|
||||
L2CAP_TRACE_DEBUG ("L2CAP - LE - credits updated to %d for CID: 0x%04x",
|
||||
p_ccb->peer_conn_cfg.credits, lcid);
|
||||
l2c_link_check_send_pkts(p_ccb->p_lcb, NULL, NULL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case L2CAP_CMD_DISC_REQ: {
|
||||
if (cmd_len < 4) {
|
||||
L2CAP_TRACE_WARNING ("L2CAP - LE - short cmd: %d", cmd_len);
|
||||
@@ -988,13 +1014,20 @@ BOOLEAN l2cble_init_direct_conn (tL2C_LCB *p_lcb)
|
||||
uint32_t link_timeout = L2CAP_BLE_LINK_CONNECT_TOUT;
|
||||
if(GATTC_CONNECT_RETRY_COUNT) {
|
||||
if(!p_lcb->retry_create_con) {
|
||||
p_lcb->start_time_s = (esp_system_get_time()/1000);
|
||||
p_lcb->start_time_s = esp_system_get_time() / 1000;
|
||||
}
|
||||
uint32_t current_time = (esp_system_get_time()/1000);
|
||||
link_timeout = (L2CAP_BLE_LINK_CONNECT_TOUT*1000 - (current_time - p_lcb->start_time_s))/1000;
|
||||
int64_t current_time = esp_system_get_time() / 1000;
|
||||
int64_t elapsed_ms = current_time - p_lcb->start_time_s;
|
||||
int64_t timeout_ms = (int64_t)L2CAP_BLE_LINK_CONNECT_TOUT * 1000;
|
||||
int64_t remaining_ms = timeout_ms - elapsed_ms;
|
||||
|
||||
if(link_timeout == 0 || link_timeout > L2CAP_BLE_LINK_CONNECT_TOUT) {
|
||||
if (remaining_ms <= 0 || remaining_ms > timeout_ms) {
|
||||
link_timeout = L2CAP_BLE_LINK_CONNECT_TOUT;
|
||||
} else {
|
||||
link_timeout = (uint32_t)(remaining_ms / 1000);
|
||||
if (link_timeout == 0) {
|
||||
link_timeout = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -379,6 +379,9 @@ BOOLEAN l2c_link_hci_disc_comp (UINT16 handle, UINT8 reason)
|
||||
} else {
|
||||
#if (BLE_INCLUDED == TRUE)
|
||||
tL2C_LINK_STATE link_state_temp = p_lcb->link_state;
|
||||
#if ((BLE_42_ADV_EN == TRUE) || (GATTC_CONNECT_RETRY_EN == TRUE) || (BLE_50_EXTEND_ADV_EN == TRUE))
|
||||
UINT8 link_role_temp = p_lcb->link_role;
|
||||
#endif
|
||||
#endif // (BLE_INCLUDED == TRUE)
|
||||
/* There can be a case when we rejected PIN code authentication */
|
||||
/* otherwise save a new reason */
|
||||
@@ -469,6 +472,7 @@ BOOLEAN l2c_link_hci_disc_comp (UINT16 handle, UINT8 reason)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
/* l2cu_create_conn must not release the LCB on failure */
|
||||
if (l2cu_create_conn(p_lcb, transport)) {
|
||||
lcb_is_free = FALSE; /* still using this lcb */
|
||||
}
|
||||
@@ -476,13 +480,15 @@ BOOLEAN l2c_link_hci_disc_comp (UINT16 handle, UINT8 reason)
|
||||
|
||||
p_lcb->p_pending_ccb = NULL;
|
||||
#if (BLE_INCLUDED == TRUE)
|
||||
if(p_lcb->transport == BT_TRANSPORT_LE) {
|
||||
/* Use p_lcb->transport so BLE reconnection (slave adv restart / master retry) runs even when
|
||||
* there was no pending CCB; 'transport' is only set when (p_first_ccb || p_pending_ccb). */
|
||||
if (p_lcb->transport == BT_TRANSPORT_LE) {
|
||||
// for legacy adv, adv restart in gatt_le_connect_cback->gatt_cleanup_upon_disc->BTM_Recovery_Pre_State
|
||||
if (reason == HCI_ERR_CONN_FAILED_ESTABLISHMENT) {
|
||||
|
||||
#if (BLE_42_FEATURE_SUPPORT == TRUE)
|
||||
#if (BLE_42_ADV_EN == TRUE)
|
||||
if(!btm_ble_inter_get() && p_lcb->link_role == HCI_ROLE_SLAVE) {
|
||||
if(!btm_ble_inter_get() && link_role_temp == HCI_ROLE_SLAVE) {
|
||||
L2CAP_TRACE_DEBUG("slave resatrt adv, retry count %d reason 0x%x\n", p_lcb->retry_create_con, reason);
|
||||
tBTM_STATUS start_adv_status = btm_ble_start_adv();
|
||||
if (start_adv_status != BTM_SUCCESS) {
|
||||
@@ -496,7 +502,7 @@ BOOLEAN l2c_link_hci_disc_comp (UINT16 handle, UINT8 reason)
|
||||
if ((reason == HCI_ERR_CONN_FAILED_ESTABLISHMENT) || (link_state_temp == LST_CONNECTING)) {
|
||||
|
||||
#if (GATTC_CONNECT_RETRY_EN == TRUE)
|
||||
if(p_lcb->link_role == HCI_ROLE_MASTER && p_lcb->retry_create_con < GATTC_CONNECT_RETRY_COUNT) {
|
||||
if(link_role_temp == HCI_ROLE_MASTER && p_lcb->retry_create_con < GATTC_CONNECT_RETRY_COUNT) {
|
||||
L2CAP_TRACE_DEBUG("master retry connect, retry count %d reason 0x%x\n", p_lcb->retry_create_con, reason);
|
||||
p_lcb->retry_create_con ++;
|
||||
// create connection retry
|
||||
@@ -513,7 +519,7 @@ BOOLEAN l2c_link_hci_disc_comp (UINT16 handle, UINT8 reason)
|
||||
if ((reason == HCI_ERR_CONN_FAILED_ESTABLISHMENT) || (link_state_temp == LST_DISCONNECTED)) {
|
||||
#if (BLE_50_FEATURE_SUPPORT == TRUE)
|
||||
#if (BLE_50_EXTEND_ADV_EN == TRUE)
|
||||
if(btm_ble_inter_get() && p_lcb->link_role == HCI_ROLE_SLAVE) {
|
||||
if(btm_ble_inter_get() && link_role_temp == HCI_ROLE_SLAVE) {
|
||||
L2CAP_TRACE_DEBUG("slave restart extend adv, retry count %d reason 0x%x\n", p_lcb->retry_create_con, reason);
|
||||
tBTM_STATUS start_adv_status = BTM_BleStartExtAdvRestart(handle);
|
||||
if (start_adv_status != BTM_SUCCESS) {
|
||||
@@ -626,6 +632,7 @@ void l2c_link_timeout (tL2C_LCB *p_lcb)
|
||||
#endif
|
||||
/* Release the LCB */
|
||||
l2cu_release_lcb (p_lcb);
|
||||
return;
|
||||
}
|
||||
|
||||
/* If link is connected, check for inactivity timeout */
|
||||
@@ -1314,6 +1321,13 @@ static BOOLEAN l2c_link_send_to_lower (tL2C_LCB *p_lcb, BT_HDR *p_buf)
|
||||
acl_data_size = controller->get_acl_data_size_classic();
|
||||
xmit_window = l2cb.controller_xmit_window;
|
||||
}
|
||||
|
||||
if (p_buf->len <= HCI_DATA_PREAMBLE_SIZE) {
|
||||
L2CAP_TRACE_ERROR ("l2c_link_send_to_lower - bad buffer len: %u", p_buf->len);
|
||||
osi_free(p_buf);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
num_segs = (p_buf->len - HCI_DATA_PREAMBLE_SIZE + acl_data_size - 1) / acl_data_size;
|
||||
|
||||
|
||||
@@ -1395,15 +1409,26 @@ static BOOLEAN l2c_link_send_to_lower (tL2C_LCB *p_lcb, BT_HDR *p_buf)
|
||||
** Returns void
|
||||
**
|
||||
*******************************************************************************/
|
||||
void l2c_link_process_num_completed_pkts (UINT8 *p)
|
||||
void l2c_link_process_num_completed_pkts (UINT8 *p, UINT8 evt_len)
|
||||
{
|
||||
UINT8 num_handles, xx;
|
||||
UINT16 handle;
|
||||
UINT16 num_sent;
|
||||
tL2C_LCB *p_lcb;
|
||||
|
||||
if (evt_len < 1) {
|
||||
L2CAP_TRACE_ERROR ("l2c_link_process_num_completed_pkts: evt too short (len=%u)", evt_len);
|
||||
return;
|
||||
}
|
||||
|
||||
STREAM_TO_UINT8 (num_handles, p);
|
||||
|
||||
if (num_handles > (evt_len - 1) / 4) {
|
||||
L2CAP_TRACE_ERROR ("l2c_link_process_num_completed_pkts: num_handles %u exceeds evt_len %u, truncating",
|
||||
num_handles, evt_len);
|
||||
num_handles = (evt_len - 1) / 4;
|
||||
}
|
||||
|
||||
for (xx = 0; xx < num_handles; xx++) {
|
||||
STREAM_TO_UINT16 (handle, p);
|
||||
STREAM_TO_UINT16 (num_sent, p);
|
||||
|
||||
@@ -150,7 +150,6 @@ void l2c_rcv_acl_data (BT_HDR *p_msg)
|
||||
#if (!CONFIG_BT_STACK_NO_LOG)
|
||||
UINT16 psm;
|
||||
#endif
|
||||
UINT16 credit;
|
||||
|
||||
/* Extract the handle */
|
||||
STREAM_TO_UINT16 (handle, p);
|
||||
@@ -292,6 +291,9 @@ void l2c_rcv_acl_data (BT_HDR *p_msg)
|
||||
if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE) {
|
||||
#if (CLASSIC_BT_INCLUDED == TRUE)
|
||||
l2c_fcr_proc_pdu (p_ccb, p_msg);
|
||||
#else
|
||||
/* Classic FCR not compiled (e.g. BLE-only); free p_msg to avoid leak */
|
||||
osi_free (p_msg);
|
||||
#endif ///CLASSIC_BT_INCLUDED == TRUE
|
||||
} else {
|
||||
(*l2cb.fixed_reg[rcv_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb)
|
||||
@@ -310,31 +312,24 @@ void l2c_rcv_acl_data (BT_HDR *p_msg)
|
||||
osi_free (p_msg);
|
||||
} else {
|
||||
if (p_lcb->transport == BT_TRANSPORT_LE) {
|
||||
// Got a pkt, valid send out credits to the peer device
|
||||
credit = L2CAP_LE_DEFAULT_CREDIT;
|
||||
L2CAP_TRACE_DEBUG("%s Credits received %d",__func__, credit);
|
||||
if((p_ccb->peer_conn_cfg.credits + credit) > L2CAP_LE_MAX_CREDIT) {
|
||||
/* we have received credits more than max coc credits,
|
||||
* so disconnecting the Le Coc Channel
|
||||
*/
|
||||
#if (BLE_INCLUDED == TRUE)
|
||||
l2cble_send_peer_disc_req (p_ccb);
|
||||
#endif ///BLE_INCLUDED == TRUE
|
||||
} else {
|
||||
p_ccb->peer_conn_cfg.credits += credit;
|
||||
l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL);
|
||||
}
|
||||
l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL);
|
||||
}
|
||||
/* Basic mode packets go straight to the state machine */
|
||||
if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_BASIC_MODE) {
|
||||
#if (CLASSIC_BT_INCLUDED == TRUE)
|
||||
#if (L2CAP_COC_INCLUDED == TRUE)
|
||||
l2c_csm_execute (p_ccb, L2CEVT_L2CAP_DATA, p_msg);
|
||||
#endif ///CLASSIC_BT_INCLUDED == TRUE
|
||||
#else
|
||||
/* COC not included: state machine not compiled; free p_msg to avoid leak */
|
||||
osi_free (p_msg);
|
||||
#endif
|
||||
} else {
|
||||
/* eRTM or streaming mode, so we need to validate states first */
|
||||
if ((p_ccb->chnl_state == CST_OPEN) || (p_ccb->chnl_state == CST_CONFIG)) {
|
||||
#if (CLASSIC_BT_INCLUDED == TRUE)
|
||||
l2c_fcr_proc_pdu (p_ccb, p_msg);
|
||||
#else
|
||||
/* Classic FCR not compiled (e.g. BLE-only); free p_msg to avoid leak */
|
||||
osi_free (p_msg);
|
||||
#endif ///CLASSIC_BT_INCLUDED == TRUE
|
||||
} else {
|
||||
osi_free (p_msg);
|
||||
@@ -423,14 +418,26 @@ static void process_l2cap_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len)
|
||||
|
||||
switch (cmd_code) {
|
||||
case L2CAP_CMD_REJECT:
|
||||
if (cmd_len < L2CAP_CMD_REJECT_LEN) {
|
||||
L2CAP_TRACE_WARNING ("L2CAP - cmd reject too short, cmd_len: %d", cmd_len);
|
||||
break;
|
||||
}
|
||||
STREAM_TO_UINT16 (rej_reason, p);
|
||||
if (rej_reason == L2CAP_CMD_REJ_MTU_EXCEEDED) {
|
||||
if (cmd_len < L2CAP_CMD_REJECT_LEN + 2) {
|
||||
L2CAP_TRACE_WARNING ("L2CAP - MTU rej too short, cmd_len: %d", cmd_len);
|
||||
break;
|
||||
}
|
||||
STREAM_TO_UINT16 (rej_mtu, p);
|
||||
/* What to do with the MTU reject ? We have negotiated an MTU. For now */
|
||||
/* we will ignore it and let a higher protocol timeout take care of it */
|
||||
L2CAP_TRACE_WARNING ("L2CAP - MTU rej Handle: %d MTU: %d", p_lcb->handle, rej_mtu);
|
||||
}
|
||||
if (rej_reason == L2CAP_CMD_REJ_INVALID_CID) {
|
||||
if (cmd_len < L2CAP_CMD_REJECT_LEN + 4) {
|
||||
L2CAP_TRACE_WARNING ("L2CAP - CID rej too short, cmd_len: %d", cmd_len);
|
||||
break;
|
||||
}
|
||||
STREAM_TO_UINT16 (rcid, p);
|
||||
STREAM_TO_UINT16 (lcid, p);
|
||||
|
||||
|
||||
@@ -1899,15 +1899,31 @@ void l2cu_disconnect_chnl (tL2C_CCB *p_ccb)
|
||||
UINT16 local_cid = p_ccb->local_cid;
|
||||
|
||||
if (local_cid >= L2CAP_BASE_APPL_CID) {
|
||||
tL2CA_DISCONNECT_IND_CB *p_disc_cb = p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb;
|
||||
tL2CA_DISCONNECT_IND_CB *p_disc_cb = NULL;
|
||||
|
||||
if (p_ccb->p_rcb != NULL) {
|
||||
p_disc_cb = p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb;
|
||||
}
|
||||
|
||||
L2CAP_TRACE_WARNING ("L2CAP - disconnect_chnl CID: 0x%04x", local_cid);
|
||||
|
||||
l2cu_send_peer_disc_req (p_ccb);
|
||||
|
||||
/*
|
||||
* l2cu_send_peer_disc_req drains xmit_hold_q in basic FCR mode.
|
||||
* Free the queue and NULL it here to prevent l2cu_release_ccb from
|
||||
* calling fixed_queue_free again on already-dequeued buffers.
|
||||
*/
|
||||
if (p_ccb->xmit_hold_q != NULL) {
|
||||
fixed_queue_free(p_ccb->xmit_hold_q, osi_free_func);
|
||||
p_ccb->xmit_hold_q = NULL;
|
||||
}
|
||||
|
||||
l2cu_release_ccb (p_ccb);
|
||||
|
||||
(*p_disc_cb)(local_cid, FALSE);
|
||||
if (p_disc_cb) {
|
||||
(*p_disc_cb)(local_cid, FALSE);
|
||||
}
|
||||
} else {
|
||||
/* failure on the AMP channel, probably need to disconnect ACL */
|
||||
L2CAP_TRACE_ERROR ("L2CAP - disconnect_chnl CID: 0x%04x Ignored", local_cid);
|
||||
|
||||
Reference in New Issue
Block a user