fix: Fix missing handling for HCI_ERR_CONNECTION_EXISTS

- Closes https://github.com/espressif/esp-idf/issues/18562
This commit is contained in:
yangfeng
2026-05-19 09:55:38 +08:00
parent ed2360fff0
commit a9af2eee5c
10 changed files with 134 additions and 4 deletions

View File

@@ -48,6 +48,14 @@ config BT_CLASSIC_ENABLED
help
For now this option needs "SMP_ENABLE" to be set to yes
config BT_CLASSIC_MAX_RECONNECT_ON_COLLISION
int "Maximum number of reconnection attempts in case of collision"
depends on BT_CLASSIC_ENABLED
default 5
help
The maximum number of reconnection attempts when encountering rejection of connection
request with error code 0x0B(Connection Already Exists) from peer device
config BT_CLASSIC_ENABLE_POWER_CTRL_VSC
bool "Enable Espressif Vendor-specific HCI commands for power control of Classic Bluetooth"
depends on BT_CLASSIC_ENABLED

View File

@@ -32,6 +32,12 @@
#define UC_BT_CLASSIC_ENABLED FALSE
#endif
#ifdef CONFIG_BT_CLASSIC_MAX_RECONNECT_ON_COLLISION
#define UC_BT_CLASSIC_MAX_RECONNECT_ON_COLLISION CONFIG_BT_CLASSIC_MAX_RECONNECT_ON_COLLISION
#else
#define UC_BT_CLASSIC_MAX_RECONNECT_ON_COLLISION 5
#endif
//A2DP
#ifdef CONFIG_BT_A2DP_ENABLE
#define UC_BT_A2DP_ENABLED CONFIG_BT_A2DP_ENABLE

View File

@@ -73,6 +73,8 @@
#define SDP_INCLUDED TRUE
#define BTA_DM_QOS_INCLUDED TRUE
#define BR_EDR_MAX_RECONNECT_ON_COLLISION UC_BT_CLASSIC_MAX_RECONNECT_ON_COLLISION
#define ESP_BT_CLASSIC_ENABLE_POWER_CTRL_VSC UC_BT_CLASSIC_ENABLE_POWER_CTRL_VSC
#if (UC_BT_A2DP_ENABLED == TRUE)

View File

@@ -321,6 +321,7 @@ static void btu_general_alarm_process(void *param)
break;
case BTU_TTYPE_L2CAP_LINK:
case BTU_TTYPE_L2CAP_LINK_RETRY:
case BTU_TTYPE_L2CAP_CHNL:
case BTU_TTYPE_L2CAP_HOLD:
case BTU_TTYPE_L2CAP_INFO:

View File

@@ -172,6 +172,9 @@ typedef void (*tBTU_EVENT_CALLBACK)(BT_HDR *p_hdr);
/* BTU internal timer for BR/EDR power control*/
#define BTU_TTYPE_BTM_BREDR_PWR_CTRL 112
/* L2CAP host-driven Create_Connection retry back-off timer */
#define BTU_TTYPE_L2CAP_LINK_RETRY 113
/* BTU Task Signal */
typedef enum {
SIG_BTU_START_UP = 0,

View File

@@ -25,6 +25,7 @@
#define L2C_INT_H
#include <stdbool.h>
#include "common/bt_target.h"
#include "stack/btm_api.h"
#include "stack/l2c_api.h"
#include "stack/l2cdefs.h"
@@ -75,6 +76,12 @@
#define L2CAP_CACHE_ATT_ACL_NUM 10
/* Maximum number of host-driven Create_Connection retries before reporting
* connection_exist failure to the application layer. */
#define L2CAP_MAX_RECONNECT_ON_COLLISION BR_EDR_MAX_RECONNECT_ON_COLLISION
/* Back-off (in seconds) between two host-driven Create_Connection retries. */
#define L2C_LP_CONN_RETRY_DELAY_TOUT 1 /* 1 second */
/* Define the possible L2CAP channel states. The names of
** the states may seem a bit strange, but they are taken from
** the Bluetooth specification.
@@ -393,6 +400,13 @@ typedef struct t_l2c_linkcb {
TIMER_LIST_ENT info_timer_entry; /* Timer entry for info resp timeout evt */
TIMER_LIST_ENT upda_con_timer; /* Timer entry for update connection parameter */
BD_ADDR remote_bd_addr; /* The BD address of the remote */
#if (CLASSIC_BT_INCLUDED == TRUE)
UINT8 br_edr_create_con_retries; /* Host-driven Create_Connection retry counter,
* incremented for each non-success Connection
* Complete that keeps CCBs on this LCB. */
TIMER_LIST_ENT retry_timer_entry; /* Back-off timer between host-driven
* Create_Connection retries. */
#endif
UINT8 link_role; /* Master or slave */
UINT8 id;
@@ -733,6 +747,9 @@ extern BOOLEAN l2c_link_hci_conn_comp (UINT8 status, UINT16 handle, BD_ADDR p_b
extern BOOLEAN l2c_link_hci_disc_comp (UINT16 handle, UINT8 reason);
extern BOOLEAN l2c_link_hci_qos_violation (UINT16 handle);
extern void l2c_link_timeout (tL2C_LCB *p_lcb);
#if (CLASSIC_BT_INCLUDED == TRUE)
extern void l2c_link_create_conn_retry (tL2C_LCB *p_lcb);
#endif
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);

View File

@@ -165,16 +165,28 @@ static void l2c_csm_closed (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
p_ccb->p_lcb->handle, TRUE, &l2c_link_sec_comp, p_ccb);
break;
case L2CEVT_LP_CONNECT_CFM_NEG: /* Link failed */
case L2CEVT_LP_CONNECT_CFM_NEG: { /* Link failed */
tL2C_CONN_INFO *p_ci = (tL2C_CONN_INFO *)p_data;
/* Disconnect unless ACL collision and upper layer wants to handle it */
if (p_ci->status != HCI_ERR_CONNECTION_EXISTS
|| !btm_acl_notif_conn_collision(p_ccb->p_lcb->remote_bd_addr)) {
BOOLEAN keep_for_collision = FALSE;
#if (CLASSIC_BT_INCLUDED == TRUE)
if (p_ci->status == HCI_ERR_CONNECTION_EXISTS
&& p_ccb->p_lcb->br_edr_create_con_retries <= L2CAP_MAX_RECONNECT_ON_COLLISION
&& btm_acl_notif_conn_collision(p_ccb->p_lcb->remote_bd_addr)) {
keep_for_collision = TRUE;
}
#else
if (p_ci->status == HCI_ERR_CONNECTION_EXISTS
&& btm_acl_notif_conn_collision(p_ccb->p_lcb->remote_bd_addr)) {
keep_for_collision = TRUE;
}
#endif
if (!keep_for_collision) {
L2CAP_TRACE_API ("L2CAP - Calling ConnectCfm_Cb(), CID: 0x%04x Status: %d", p_ccb->local_cid, p_ci->status);
l2cu_release_ccb (p_ccb);
(*connect_cfm)(local_cid, p_ci->status);
}
break;
}
case L2CEVT_L2CA_CONNECT_REQ: /* API connect request */
/* Cancel sniff mode if needed */

View File

@@ -219,6 +219,10 @@ BOOLEAN l2c_link_hci_conn_comp (UINT8 status, UINT16 handle, BD_ADDR p_bda)
btu_stop_timer (&p_lcb->timer_entry);
#if (CLASSIC_BT_INCLUDED == TRUE)
/* Link came up successfully; reset host-driven Create_Connection
* retry counter so a future failure on this BDA starts fresh. */
p_lcb->br_edr_create_con_retries = 0;
btu_stop_timer(&p_lcb->retry_timer_entry);
/* For all channels, send the event through their FSMs */
for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb) {
l2c_csm_execute (p_ccb, L2CEVT_LP_CONNECT_CFM, &ci);
@@ -258,6 +262,33 @@ BOOLEAN l2c_link_hci_conn_comp (UINT8 status, UINT16 handle, BD_ADDR p_bda)
if (ci.status == HCI_ERR_CONNECTION_EXISTS) {
/* we are in collision situation, wait for connection request from controller */
p_lcb->link_state = LST_CONNECTING;
#if (CLASSIC_BT_INCLUDED == TRUE)
if (++p_lcb->br_edr_create_con_retries <= L2CAP_MAX_RECONNECT_ON_COLLISION) {
L2CAP_TRACE_WARNING("L2CAP - Conn Comp status: 0x%02x, retry "
"Create_Connection (%u/%u) in %u sec",
status,
p_lcb->br_edr_create_con_retries,
L2CAP_MAX_RECONNECT_ON_COLLISION,
L2C_LP_CONN_RETRY_DELAY_TOUT);
btu_stop_timer(&p_lcb->timer_entry);
p_lcb->retry_timer_entry.param = (TIMER_PARAM_TYPE)p_lcb;
btu_start_timer(&p_lcb->retry_timer_entry,
BTU_TTYPE_L2CAP_LINK_RETRY,
L2C_LP_CONN_RETRY_DELAY_TOUT);
} else {
L2CAP_TRACE_WARNING("L2CAP - giving up Create_Connection "
"after %u retries, last status: 0x%02x",
p_lcb->br_edr_create_con_retries, status);
for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; ) {
tL2C_CCB *pn = p_ccb->p_next_ccb;
l2c_csm_execute (p_ccb, L2CEVT_LP_CONNECT_CFM_NEG, &ci);
p_ccb = pn;
}
btu_stop_timer(&p_lcb->timer_entry);
btu_stop_timer(&p_lcb->retry_timer_entry);
l2cu_release_lcb(p_lcb);
}
#endif ///CLASSIC_BT_INCLUDED == TRUE
} else {
l2cu_create_conn(p_lcb, BT_TRANSPORT_BR_EDR);
}
@@ -584,7 +615,43 @@ BOOLEAN l2c_link_hci_qos_violation (UINT16 handle)
return (TRUE);
}
#if (CLASSIC_BT_INCLUDED == TRUE)
/*******************************************************************************
**
** Function l2c_link_create_conn_retry
**
** Description Back-off timer between two host-driven Create_Connection
** retries fired. Re-issue the connection attempt now. If
** the LCB has been torn down (no CCBs left, link no longer
** in a connecting state) we silently drop the retry.
**
** Returns void
**
*******************************************************************************/
void l2c_link_create_conn_retry (tL2C_LCB *p_lcb)
{
if (p_lcb == NULL || !p_lcb->in_use) {
return;
}
/* If the application/upper layer already tore everything down while we
* were waiting for the back-off, do nothing. */
if (p_lcb->ccb_queue.p_first_ccb == NULL) {
L2CAP_TRACE_WARNING("L2CAP - retry timer fired but no CCB left, "
"dropping retry");
return;
}
L2CAP_TRACE_EVENT("L2CAP - back-off elapsed, re-issuing Create_Connection "
"(retry %u/%u)",
p_lcb->br_edr_create_con_retries,
L2CAP_MAX_RECONNECT_ON_COLLISION);
/* l2cu_create_conn() will (re)set link_state and arm the 60s
* BTU_TTYPE_L2CAP_LINK timer on p_lcb->timer_entry. */
l2cu_create_conn(p_lcb, BT_TRANSPORT_BR_EDR);
}
#endif ///CLASSIC_BT_INCLUDED == TRUE
/*******************************************************************************
**

View File

@@ -988,6 +988,12 @@ void l2c_process_timeout (TIMER_LIST_ENT *p_tle)
l2c_link_timeout ((tL2C_LCB *)p_tle->param);
break;
#if (CLASSIC_BT_INCLUDED == TRUE)
case BTU_TTYPE_L2CAP_LINK_RETRY:
/* Back-off between host-driven Create_Connection retries expired:
* re-issue the connection attempt now. */
l2c_link_create_conn_retry ((tL2C_LCB *)p_tle->param);
break;
case BTU_TTYPE_L2CAP_CHNL:
l2c_csm_execute (((tL2C_CCB *)p_tle->param), L2CEVT_TIMEOUT, NULL);
break;

View File

@@ -88,6 +88,9 @@ tL2C_LCB *l2cu_allocate_lcb (BD_ADDR p_bd_addr, BOOLEAN is_bonding, tBT_TRANSPOR
btu_free_timer(&p_lcb->timer_entry);
btu_free_timer(&p_lcb->info_timer_entry);
btu_free_timer(&p_lcb->upda_con_timer);
#if (CLASSIC_BT_INCLUDED == TRUE)
btu_free_timer(&p_lcb->retry_timer_entry);
#endif
memset (p_lcb, 0, sizeof (tL2C_LCB));
memcpy (p_lcb->remote_bd_addr, p_bd_addr, BD_ADDR_LEN);
@@ -114,6 +117,7 @@ tL2C_LCB *l2cu_allocate_lcb (BD_ADDR p_bd_addr, BOOLEAN is_bonding, tBT_TRANSPOR
#endif
{
#if (CLASSIC_BT_INCLUDED == TRUE)
p_lcb->retry_timer_entry.param = (TIMER_PARAM_TYPE)p_lcb;
l2cb.num_links_active++;
l2c_link_adjust_allocation();
#endif // #if (CLASSIC_BT_INCLUDED == TRUE)
@@ -181,6 +185,10 @@ void l2cu_release_lcb (tL2C_LCB *p_lcb)
memset(&p_lcb->info_timer_entry, 0, sizeof(TIMER_LIST_ENT));
btu_free_timer(&p_lcb->upda_con_timer);
memset(&p_lcb->upda_con_timer, 0, sizeof(TIMER_LIST_ENT));
#if (CLASSIC_BT_INCLUDED == TRUE)
btu_free_timer(&p_lcb->retry_timer_entry);
memset(&p_lcb->retry_timer_entry, 0, sizeof(TIMER_LIST_ENT));
#endif
/* Release any unfinished L2CAP packet on this link */
if (p_lcb->p_hcit_rcv_acl) {