Merge branch 'feat/support_cble50y24_108_v5.4' into 'release/v5.4'

Support multi-connection optimization and examples on Bluedroid (v5.4)

See merge request espressif/esp-idf!41199
This commit is contained in:
Island
2025-08-19 21:11:00 +08:00
36 changed files with 1767 additions and 37 deletions
+6 -2
View File
@@ -1169,9 +1169,13 @@ menu "BT DEBUG LOG LEVEL"
endmenu #BT DEBUG LOG LEVEL
config BT_ACL_CONNECTIONS
int "BT/BLE MAX ACL CONNECTIONS(1~9)"
int "BT/BLE MAX ACL CONNECTIONS"
depends on BT_BLUEDROID_ENABLED
range 1 9
range 1 2 if IDF_TARGET_ESP32C2
range 1 9 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32S3
range 1 15 if IDF_TARGET_ESP32H2
range 1 50
default 2 if IDF_TARGET_ESP32C2
default 4
help
Maximum BT/BLE connection count. The ESP32-C3/S3 chip supports a maximum of 10 instances,
@@ -1759,6 +1759,42 @@ esp_err_t esp_ble_gap_set_csa_support(uint8_t csa_select)
return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gap_args_t), NULL, NULL)
== BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
}
#if CONFIG_SOC_BLE_MULTI_CONN_OPTIMIZATION
esp_err_t esp_ble_gap_set_common_factor(uint32_t common_factor)
{
esp_ble_vendor_cmd_params_t vs_cmd;
uint8_t cmd_param[5];
cmd_param[0] = common_factor & 0xFF;
cmd_param[1] = (common_factor >> 8) & 0xFF;
cmd_param[2] = (common_factor >> 16) & 0xFF;
cmd_param[3] = (common_factor >> 24) & 0xFF;
cmd_param[4] = 0x01;
vs_cmd.opcode = 0xFD0F;
vs_cmd.param_len = 5;
vs_cmd.p_param_buf = cmd_param;
return esp_ble_gap_vendor_command_send(&vs_cmd);
}
esp_err_t esp_ble_gap_set_sch_len(uint8_t role, uint32_t len)
{
esp_ble_vendor_cmd_params_t vs_cmd;
uint8_t cmd_param[5];
cmd_param[0] = role;
cmd_param[1] = len & 0xFF;
cmd_param[2] = (len >> 8) & 0xFF;
cmd_param[3] = (len >> 16) & 0xFF;
cmd_param[4] = (len >> 24) & 0xFF;
vs_cmd.opcode = 0xFD10;
vs_cmd.param_len = 5;
vs_cmd.p_param_buf = cmd_param;
return esp_ble_gap_vendor_command_send(&vs_cmd);
}
#endif // CONFIG_SOC_BLE_MULTI_CONN_OPTIMIZATION
#endif // (BLE_VENDOR_HCI_EN == TRUE)
#if (BLE_FEAT_POWER_CONTROL_EN == TRUE)
@@ -246,6 +246,8 @@ typedef enum {
ESP_GAP_BLE_SUBRATE_CHANGE_EVT, /*!< when Connection Subrate Update procedure has completed and some parameters of the specified connection have changed, the event comes */
ESP_GAP_BLE_SET_HOST_FEATURE_CMPL_EVT, /*!< When host feature set complete, the event comes */
ESP_GAP_BLE_READ_CHANNEL_MAP_COMPLETE_EVT, /*!< When BLE channel map result is received, the event comes */
ESP_GAP_BLE_SET_COMMON_FACTOR_CMPL_EVT, /*!< When set the common factor complete, the event comes */
ESP_GAP_BLE_SET_SCH_LEN_CMPL_EVT, /*!< When set the scheduling length complete, the event comes */
ESP_GAP_BLE_EVT_MAX, /*!< when maximum advertising event complete, the event comes */
} esp_gap_ble_cb_event_t;
@@ -1748,6 +1750,18 @@ typedef union {
uint8_t param_len; /*!< The length of the event parameter buffer (for internal use only) */
uint8_t *param_buf; /*!< The pointer of the event parameter buffer (for internal use only) */
} vendor_hci_evt; /*!< Event parameter of ESP_GAP_BLE_VENDOR_HCI_EVT */
/**
* @brief ESP_GAP_BLE_SET_COMMON_FACTOR_CMPL_EVT
*/
struct ble_set_common_factor_cmpl_evt_param {
esp_bt_status_t status; /*!< Indicate common factor set operation success status */
} set_common_factor_cmpl; /*!< Event parameter of ESP_GAP_BLE_SET_COMMON_FACTOR_CMPL_EVT */
/**
* @brief ESP_GAP_BLE_SET_SCH_LEN_CMPL_EVT
*/
struct ble_set_sch_len_cmpl_evt_param {
esp_bt_status_t status; /*!< Indicate scheduling length set operation success status */
} set_sch_len_cmpl; /*!< Event parameter of ESP_GAP_BLE_SET_SCH_LEN_CMPL_EVT */
#endif // #if (BLE_VENDOR_HCI_EN == TRUE)
#if (BLE_FEAT_POWER_CONTROL_EN == TRUE)
/**
@@ -3090,6 +3104,46 @@ esp_err_t esp_ble_gap_set_csa_support(uint8_t csa_select);
*/
esp_err_t esp_ble_gap_set_vendor_event_mask(esp_ble_vendor_evt_mask_t event_mask);
/**
* @brief This function is used to set a common connection interval factor for multiple central-role connections.
* When multiple BLE connections in the central role exist, it is recommended that
* each connection interval be configured to either the same value or an integer
* multiple of the others. And use this function to set the common factor of all
* connection intervalsin the controller. The controller will then arrange the scheduling
* of each connection based on this factor to minimize or avoid connection conflicts.
*
* @note - This function is used in multi-connection scenarios.
* - This function takes effect only when the connection role is central.
* - This function only needs to be called once and before establishing the connection.
*
* @param[in] common_factor: The common connection interval factor (in units of 625us)
* used for scheduling across all central-role connections.
*
* @return
* - ESP_OK : success
* - other : failed
*/
esp_err_t esp_ble_gap_set_common_factor(uint32_t common_factor);
/**
* @brief This function is used to Set the scheduling protection time for specific LE role.
* It can be used to configures the minimum protection time to be reserved for a
* connection's TX/RX operations, ensuring that a complete transmission and
* reception cycle is not interrupted. It helps prevent disconnect in scenarios
* with multiple connections competing for time slots.
*
* @note - This function is used in multi-connection scenarios.
* - This function must be called before establishing the connection.
*
* @param[in] role: 0: Central 1: Peripheral
* @param[in] len: The protection time length of the corresponding role (in units of us)
*
* @return
* - ESP_OK : success
* - other : failed
*/
esp_err_t esp_ble_gap_set_sch_len(uint8_t role, uint32_t len);
/**
* @brief This function is used to read the current and maximum transmit power levels of the local Controller.
*
@@ -1441,10 +1441,23 @@ static void btc_ble_vendor_hci_cmd_complete_callback(tBTA_VSC_CMPL *p_param)
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_GAP_BLE;
msg.act = ESP_GAP_BLE_VENDOR_CMD_COMPLETE_EVT;
if (!param_invalid) {
param.vendor_cmd_cmpl.opcode = p_param->opcode;
param.vendor_cmd_cmpl.param_len = p_param->param_len;
param.vendor_cmd_cmpl.p_param_buf = p_param->p_param_buf;
switch (p_param->opcode) {
case 0xFD0F:
msg.act = ESP_GAP_BLE_SET_COMMON_FACTOR_CMPL_EVT;
param.set_common_factor_cmpl.status = p_param->p_param_buf[0];
break;
case 0xFD10:
msg.act = ESP_GAP_BLE_SET_SCH_LEN_CMPL_EVT;
param.set_sch_len_cmpl.status = p_param->p_param_buf[0];
break;
default:
param.vendor_cmd_cmpl.opcode = p_param->opcode;
param.vendor_cmd_cmpl.param_len = p_param->param_len;
param.vendor_cmd_cmpl.p_param_buf = p_param->p_param_buf;
break;
}
} else {
if (p_param) {
param.vendor_cmd_cmpl.opcode = p_param->opcode;
@@ -1455,7 +1468,11 @@ static void btc_ble_vendor_hci_cmd_complete_callback(tBTA_VSC_CMPL *p_param)
param.vendor_cmd_cmpl.p_param_buf = NULL;
}
ret = btc_transfer_context(&msg, &param, sizeof(esp_ble_gap_cb_param_t), btc_gap_ble_cb_deep_copy, btc_gap_ble_cb_deep_free);
if (msg.act == ESP_GAP_BLE_VENDOR_CMD_COMPLETE_EVT) {
ret = btc_transfer_context(&msg, &param, sizeof(esp_ble_gap_cb_param_t), btc_gap_ble_cb_deep_copy, btc_gap_ble_cb_deep_free);
} else {
ret = btc_transfer_context(&msg, &param, sizeof(esp_ble_gap_cb_param_t), NULL, NULL);
}
if (ret != BT_STATUS_SUCCESS) {
BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__);
@@ -1093,8 +1093,12 @@
/* The number of security records for peer devices. 15 AS Default*/
#ifndef BTM_SEC_MAX_DEVICE_RECORDS
#if (UC_BT_SMP_MAX_BONDS < UC_BT_ACL_CONNECTIONS)
#define BTM_SEC_MAX_DEVICE_RECORDS UC_BT_ACL_CONNECTIONS
#else
#define BTM_SEC_MAX_DEVICE_RECORDS UC_BT_SMP_MAX_BONDS
#endif
#endif
#if BTA_SDP_INCLUDED
#define BTM_SDP_SEC_SERVICE_RECORDS 1
@@ -78,7 +78,7 @@ static const UINT8 base_uuid[LEN_UUID_128] = {0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x0
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static UINT32 gatt_tcb_id;
static UINT8 gatt_tcb_id[GATT_MAX_PHY_CHANNEL / 8 + 1];
/*******************************************************************************
**
@@ -1005,7 +1005,7 @@ UINT8 gatt_find_i_tcb_free(void)
UINT8 i = 0, j = GATT_INDEX_INVALID;
for (i = 0; i < GATT_MAX_PHY_CHANNEL; i ++) {
if (!((1 << i) & gatt_tcb_id)) {
if (!(gatt_tcb_id[i >> 3] & (1 << (i & 0x7)))) {
j = i;
break;
}
@@ -1030,9 +1030,9 @@ tGATT_TCB *gatt_tcb_alloc(UINT8 tcb_idx)
/* Add tcb block to list in gatt_cb */
list_append(gatt_cb.p_tcb_list, p_tcb);
/* Update tcb id */
gatt_tcb_id |= 1 << tcb_idx;
} else if(p_tcb) {
osi_free(p_tcb);
gatt_tcb_id[tcb_idx >> 3] |= (1 << (tcb_idx & 0x7));
} else if (p_tcb) {
osi_free(p_tcb);
p_tcb = NULL;
}
return p_tcb;
@@ -1051,7 +1051,7 @@ void gatt_tcb_free( tGATT_TCB *p_tcb)
{
UINT8 tcb_idx = p_tcb->tcb_idx;
if (list_remove(gatt_cb.p_tcb_list, p_tcb)) {
gatt_tcb_id &= ~(1 << tcb_idx);
gatt_tcb_id[tcb_idx >> 3] &= ~(1 << (tcb_idx & 0x7));
}
}
/*******************************************************************************
@@ -1078,9 +1078,9 @@ tGATT_TCB *gatt_allocate_tcb_by_bdaddr(BD_ADDR bda, tBT_TRANSPORT transport)
}
if (i != GATT_INDEX_INVALID) {
p_tcb = gatt_tcb_alloc(i);
if (!p_tcb) {
return NULL;
}
if (!p_tcb) {
return NULL;
}
if (allocated) {
memset(p_tcb, 0, sizeof(tGATT_TCB));
p_tcb->pending_enc_clcb = fixed_queue_new(QUEUE_SIZE_MAX);
@@ -151,7 +151,11 @@ typedef UINT16 tGATT_DISCONN_REASON;
#define GATT_INVALID_CONN_ID 0xFFFF
#ifndef GATT_CL_MAX_LCB
#define GATT_CL_MAX_LCB 12 // 22
#if (GATT_MAX_PHY_CHANNEL > 12)
#define GATT_CL_MAX_LCB GATT_MAX_PHY_CHANNEL
#else
#define GATT_CL_MAX_LCB 12
#endif
#endif
#ifndef GATT_MAX_SCCB
@@ -1077,32 +1077,34 @@ void l2c_link_check_send_pkts (tL2C_LCB *p_lcb, tL2C_CCB *p_ccb, BT_HDR *p_buf)
L2CAP_TRACE_ERROR("l2cab is_cong_cback_context");
return;
}
list_node_t* start_p_node = NULL;
/* If we are in a scenario where there are not enough buffers for each link to
** have at least 1, then do a round-robin for all the LCBs
*/
if ( (p_lcb == NULL) || (p_lcb->link_xmit_quota == 0) ) {
list_node_t *p_node = NULL;
tL2C_LCB *p_lcb_cur = NULL;
tL2C_LCB *p_lcb_cur = NULL;
if (p_lcb == NULL) {
p_node = list_begin(l2cb.p_lcb_pool);
p_lcb = list_node(p_node);
p_lcb = list_node(p_node);
} else if (!single_write) {
for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) {
p_lcb_cur = list_node(p_node);
if (p_lcb_cur == p_lcb) {
p_node = list_next(p_node);
if (p_node) {
p_lcb = list_node(p_node);
}
break;
}
}
for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) {
p_lcb_cur = list_node(p_node);
if (p_lcb_cur == p_lcb) {
p_node = list_next(p_node);
start_p_node = p_node;
break;
}
}
}
/* Loop through, starting at the next */
for ( ; p_node; p_node = list_next(p_node)) {
p_lcb = list_node(p_node);
do {
/* Check for wraparound */
if (p_node == list_end(l2cb.p_lcb_pool)) {
p_node = list_begin(l2cb.p_lcb_pool);
}
p_lcb = list_node(p_node);
#if (BLE_INCLUDED == TRUE)
L2CAP_TRACE_DEBUG("window = %d,robin_unacked = %d,robin_quota=%d",l2cb.controller_le_xmit_window,l2cb.ble_round_robin_unacked,l2cb.ble_round_robin_quota);
#endif ///BLE_INCLUDED == TRUE
@@ -1118,20 +1120,22 @@ void l2c_link_check_send_pkts (tL2C_LCB *p_lcb, tL2C_CCB *p_ccb, BT_HDR *p_buf)
#else
))
#endif ///BLE_INCLUDED == TRUE
{
break;
}
/* Check for wraparound */
if (p_node == list_end(l2cb.p_lcb_pool)) {
if (p_node == list_end(l2cb.p_lcb_pool)) {
p_node = list_begin(l2cb.p_lcb_pool);
p_lcb = list_node(p_node);
}
p_lcb = list_node(p_node);
}
L2CAP_TRACE_DEBUG("in_use=%d,segment_being_sent=%d,link_state=%d,link_xmit_quota=%d",p_lcb->in_use,p_lcb->partial_segment_being_sent,p_lcb->link_state,p_lcb->link_xmit_quota);
if ( (!p_lcb->in_use)
|| (p_lcb->partial_segment_being_sent)
|| (p_lcb->link_state != LST_CONNECTED)
|| (p_lcb->link_xmit_quota != 0)
|| (L2C_LINK_CHECK_POWER_MODE (p_lcb)) ) {
p_node = list_next(p_node);
continue;
}
@@ -1148,7 +1152,8 @@ void l2c_link_check_send_pkts (tL2C_LCB *p_lcb, tL2C_CCB *p_ccb, BT_HDR *p_buf)
else if ((p_buf = l2cu_get_next_buffer_to_send (p_lcb)) != NULL) {
l2c_link_send_to_lower (p_lcb, p_buf);
}
}
p_node = list_next(p_node);
} while (p_node != start_p_node);
/* If we finished without using up our quota, no need for a safety check */
if ( (l2cb.controller_xmit_window > 0)