From ca97da138b4ec5d5d9c347d1e1647ae7f071a83e Mon Sep 17 00:00:00 2001 From: Wang Mengyang Date: Fri, 11 Sep 2026 18:32:13 +0800 Subject: [PATCH 1/3] fix(bt): fixed multiple CVE issues for Bluetooth Classic Controller - fixed multiple CVE bugs of Bluetooth Classic controller on ESP32-S31 - coex: improved BR/EDR ACL active scheduling in case of Wi-Fi coexistence - SCO: reject unexpected LMP PDU received in SCO related LMP procedures --- components/bt/controller/esp32s31/btdm_broker.c | 8 ++++++-- components/bt/controller/lib_esp32s31/esp32s31-bt-lib | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/components/bt/controller/esp32s31/btdm_broker.c b/components/bt/controller/esp32s31/btdm_broker.c index a2c532b4db5..7dc713f14e5 100644 --- a/components/bt/controller/esp32s31/btdm_broker.c +++ b/components/bt/controller/esp32s31/btdm_broker.c @@ -239,6 +239,7 @@ extern int btdm_common_sched_bredr_on_sched_hw_list_done(void *param); extern int hci_tl_bredr_on_rx_cmd_c2h_num_pkt(void *param); extern int hci_tl_bredr_on_rx_cmd_set_c2h_flow_ctrl(void *param); extern int odm_afh_on_coex_wifi_channel_change(void *param); +extern int olc_acl_on_coex_schm_update(void *param); extern int olc_intc_on_hal_exit_isr(void *param); extern int olc_sleep_on_sched_actual_time_get(void *param); extern int olc_sleep_on_sched_get_earlist_ticks(void *param); @@ -406,11 +407,14 @@ const void * const _base_linear_broker_flash[] = BTDM_BROKER_NODE_DEF_FLASH( #endif /* UC_BT_CTRL_BLE_IS_ENABLE */ const void * const _btdm_coex_linear_broker_flash[] = BTDM_BROKER_NODE_DEF_FLASH( -#if UC_BT_CTRL_BLE_IS_ENABLE [1] = BTDM_BROKER_ENTRY_DEF_FLASH( +#if UC_BT_CTRL_BLE_IS_ENABLE brk_sym_coexHook_WcEp3uxRHd6HYgB0pn0L, - ), #endif /* UC_BT_CTRL_BLE_IS_ENABLE */ +#if UC_BT_CTRL_BR_EDR_IS_ENABLE + olc_acl_on_coex_schm_update, +#endif /* UC_BT_CTRL_BR_EDR_IS_ENABLE */ + ), [2] = BTDM_BROKER_ENTRY_DEF_FLASH( #if UC_BT_CTRL_BLE_IS_ENABLE brk_sym_coexHook_wiWNhAUWlHyTZ7Z5ZC5Z, diff --git a/components/bt/controller/lib_esp32s31/esp32s31-bt-lib b/components/bt/controller/lib_esp32s31/esp32s31-bt-lib index 20e84f6ebe5..6c9006063d8 160000 --- a/components/bt/controller/lib_esp32s31/esp32s31-bt-lib +++ b/components/bt/controller/lib_esp32s31/esp32s31-bt-lib @@ -1 +1 @@ -Subproject commit 20e84f6ebe5f278a9d72f8a2676206e0c40d26be +Subproject commit 6c9006063d8e9e09915416cbda267e01cf2248be From 67368e2404533167ab008dd4504831b33ab77da7 Mon Sep 17 00:00:00 2001 From: liqigan Date: Tue, 18 Aug 2026 15:05:54 +0800 Subject: [PATCH 2/3] fix(bt): Check the peer ECDH public key when using mbedtls --- .../porting_btdm/controller/bredr/src/bredr.c | 44 ++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/components/bt/porting_btdm/controller/bredr/src/bredr.c b/components/bt/porting_btdm/controller/bredr/src/bredr.c index 50caaa53e22..4ff407bcf42 100644 --- a/components/bt/porting_btdm/controller/bredr/src/bredr.c +++ b/components/bt/porting_btdm/controller/bredr/src/bredr.c @@ -295,6 +295,41 @@ static int bredr_psa_gen_keypair(bool p256, uint8_t *public_key, uint8_t *privat return 0; } +/* + * CVE-2018-5383: validate the peer's public key is a valid point on the curve + * before using it in the DH computation. PSA validates the point (rejects the + * point at infinity and out-of-range coordinates) when importing it as a public key. + */ +static int bredr_psa_validate_peer_pubkey(bool p256, const uint8_t *peer_pub_key_x, const uint8_t *peer_pub_key_y) +{ + const size_t coord_len = p256 ? BREDR_P256_COORD_LEN : BREDR_P192_COORD_LEN; + const size_t pub_len = p256 ? BREDR_PUB_KEY_LEN_P256 : BREDR_PUB_KEY_LEN_P192; + + uint8_t pk[BREDR_PUB_KEY_LEN_P256]; /* uncompressed: 0x04 || X || Y */ + + pk[0] = 0x04; + btdm_swap_buf(&pk[1], peer_pub_key_x, coord_len); + btdm_swap_buf(&pk[1 + coord_len], peer_pub_key_y, coord_len); + + psa_key_attributes_t pub_attr = PSA_KEY_ATTRIBUTES_INIT; + psa_key_id_t pub_key_id = 0; + psa_status_t pub_status; + + psa_set_key_type(&pub_attr, PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1)); + psa_set_key_bits(&pub_attr, p256 ? 256 : 192); + psa_set_key_usage_flags(&pub_attr, 0); + + pub_status = psa_import_key(&pub_attr, pk, pub_len, &pub_key_id); + psa_reset_key_attributes(&pub_attr); + if (pub_status != PSA_SUCCESS) { + ESP_LOGE(TAG_BREDR_CRYPTO, "invalid peer public key: psa_import_key failed: %d", (int)pub_status); + return -1; + } + + psa_destroy_key(pub_key_id); + return 0; +} + static int bredr_psa_gen_dhkey(bool p256, const uint8_t *peer_pub_key_x, const uint8_t *peer_pub_key_y, const uint8_t *our_priv_key, uint8_t *out_dhkey) { @@ -306,11 +341,18 @@ static int bredr_psa_gen_dhkey(bool p256, const uint8_t *peer_pub_key_x, const u uint8_t pk[65]; uint8_t dh[32]; - btdm_swap_buf(priv, our_priv_key, priv_len); + /* Validate the peer's public key before using it in the DH computation. */ + if (bredr_psa_validate_peer_pubkey(p256, peer_pub_key_x, peer_pub_key_y) != 0) { + return -1; + } + + /* Reconstruct the peer public key in uncompressed form (0x04 || X || Y). */ pk[0] = 0x04; btdm_swap_buf(&pk[1], peer_pub_key_x, coord_len); btdm_swap_buf(&pk[1 + coord_len], peer_pub_key_y, coord_len); + btdm_swap_buf(priv, our_priv_key, priv_len); + psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT; psa_key_id_t key_id = 0; psa_status_t status; From 0ef1beb7cdf0d734856964c20b519964834a3ba9 Mon Sep 17 00:00:00 2001 From: ShenWeilong Date: Mon, 14 Sep 2026 12:09:12 +0800 Subject: [PATCH 3/3] feat(bt): block the hci task if no available hci cmd buffer for ESP32-S31, ESP32-H4 --- .../bt/controller/lib_esp32h4/esp32h4-bt-lib | 2 +- .../controller/lib_esp32s31/esp32s31-bt-lib | 2 +- .../bt/include/esp32h4/include/esp_bt.h | 75 +++++------ .../bt/include/esp32s31/include/esp_bt.h | 5 +- .../btdm_common/include/btdm_user_cfg.h | 6 + .../bt/porting_btdm/transport/Kconfig.in | 8 ++ .../transport/driver/common/hci_driver_h4.c | 120 +++++++++++++++--- .../driver/uart/hci_driver_uart_dma.c | 77 +++++++++-- .../transport/include/common/hci_driver_h4.h | 12 ++ 9 files changed, 235 insertions(+), 72 deletions(-) diff --git a/components/bt/controller/lib_esp32h4/esp32h4-bt-lib b/components/bt/controller/lib_esp32h4/esp32h4-bt-lib index 00b5a98c1b6..9358cd13cc5 160000 --- a/components/bt/controller/lib_esp32h4/esp32h4-bt-lib +++ b/components/bt/controller/lib_esp32h4/esp32h4-bt-lib @@ -1 +1 @@ -Subproject commit 00b5a98c1b6d82d0eab1a9231fed1e495a391893 +Subproject commit 9358cd13cc50ed6bb099170eddd314ec3abfc76e diff --git a/components/bt/controller/lib_esp32s31/esp32s31-bt-lib b/components/bt/controller/lib_esp32s31/esp32s31-bt-lib index 6c9006063d8..849186e8618 160000 --- a/components/bt/controller/lib_esp32s31/esp32s31-bt-lib +++ b/components/bt/controller/lib_esp32s31/esp32s31-bt-lib @@ -1 +1 @@ -Subproject commit 6c9006063d8e9e09915416cbda267e01cf2248be +Subproject commit 849186e861821a30d2c3ef519dfdb08ef68b7cee diff --git a/components/bt/include/esp32h4/include/esp_bt.h b/components/bt/include/esp32h4/include/esp_bt.h index c80b8027372..7ecc5dfeb6c 100644 --- a/components/bt/include/esp32h4/include/esp_bt.h +++ b/components/bt/include/esp32h4/include/esp_bt.h @@ -113,7 +113,7 @@ #include "../../common/btdm_le.h" #endif /* SOC_BLE_SUPPORTED */ - #define BTDM_CONFIG_VERSION 0x20260127 + #define BTDM_CONFIG_VERSION 0x20260911 #define BTDM_CONFIG_MAGIC_VALUE 0x5a5aa5a5 /* Types definition @@ -124,15 +124,16 @@ * @brief BTDM controller common configuration options */ typedef struct { - uint32_t version; /*!< Version number of the defined structure */ - uint16_t task_stack_size; /*!< Size of Bluetooth controller task stack */ - uint8_t task_prio; /*!< Priority of the Bluetooth controller task */ - uint8_t task_run_cpu; /*!< CPU number on which the Bluetooth controller task runs */ - uint8_t hci_cmd_num; /*!< HCI command buffer number */ - uint8_t sleep_en; /*!< Enable sleep functionality */ - uint8_t version_num; /*!< Hardware version number of this chip */ - uint8_t bluetooth_mode; /*!< Controller mode: BR/EDR, BLE or Dual Mode */ - uint32_t magic; /*!< Magic number for configuration validation */ + uint32_t version; /*!< Version number of the defined structure */ + uint16_t task_stack_size; /*!< Size of Bluetooth controller task stack */ + uint8_t task_prio; /*!< Priority of the Bluetooth controller task */ + uint8_t task_run_cpu; /*!< CPU number on which the Bluetooth controller task runs */ + uint8_t hci_cmd_num; /*!< HCI command buffer number */ + uint8_t nonblocking_cmd_buf; /*!< Non-blocking mode for command buffer allocation */ + uint8_t sleep_en; /*!< Enable sleep functionality */ + uint8_t version_num; /*!< Hardware version number of this chip */ + uint8_t bluetooth_mode; /*!< Controller mode: BR/EDR, BLE or Dual Mode */ + uint32_t magic; /*!< Magic number for configuration validation */ } esp_bt_ctrl_btdm_config_t; /** @@ -157,38 +158,40 @@ #endif // defined(CONFIG_BTDM_CTRL_MODE_BLE_ONLY) #if SOC_BT_CLASSIC_SUPPORTED - #define BT_CONTROLLER_INIT_CONFIG_DEFAULT() \ - { \ - .ble = _BT_CTRL_LE_INIT_CONFIG_DEFAULT(), \ - .bredr = _BT_CTRL_BREDR_INIT_CONFIG_DEFAULT(), \ - .btdm = \ - { \ - .version = BTDM_CONFIG_VERSION, \ - .task_stack_size = UC_BT_CTRL_TASK_STACK_SIZE, \ - .task_prio = ESP_TASK_BT_CONTROLLER_PRIO, \ - .task_run_cpu = CONFIG_BT_CTRL_PINNED_TO_CORE, \ - .hci_cmd_num = CONFIG_BT_CTRL_HCI_CMD_NUM, \ - .sleep_en = UC_BT_CTRL_SLEEP_ENABLE, \ - .version_num = 0, \ - .bluetooth_mode = BTDM_CONTROLLER_MODE_EFF, \ - .magic = BTDM_CONFIG_MAGIC_VALUE, \ - }, \ +#define BT_CONTROLLER_INIT_CONFIG_DEFAULT() \ + { \ + .ble = _BT_CTRL_LE_INIT_CONFIG_DEFAULT(), \ + .bredr = _BT_CTRL_BREDR_INIT_CONFIG_DEFAULT(), \ + .btdm = \ + { \ + .version = BTDM_CONFIG_VERSION, \ + .task_stack_size = UC_BT_CTRL_TASK_STACK_SIZE, \ + .task_prio = ESP_TASK_BT_CONTROLLER_PRIO, \ + .task_run_cpu = CONFIG_BT_CTRL_PINNED_TO_CORE, \ + .hci_cmd_num = CONFIG_BT_CTRL_HCI_CMD_NUM, \ + .nonblocking_cmd_buf = UC_BT_CTRL_NONBLOCK_CMD_BUF, \ + .sleep_en = UC_BT_CTRL_SLEEP_ENABLE, \ + .version_num = 0, \ + .bluetooth_mode = BTDM_CONTROLLER_MODE_EFF, \ + .magic = BTDM_CONFIG_MAGIC_VALUE, \ + }, \ } - #else +#else #define BT_CONTROLLER_INIT_CONFIG_DEFAULT() \ { \ .ble = _BT_CTRL_LE_INIT_CONFIG_DEFAULT(), \ .btdm = \ { \ - .version = BTDM_CONFIG_VERSION, \ - .task_stack_size = UC_BT_CTRL_TASK_STACK_SIZE, \ - .task_prio = ESP_TASK_BT_CONTROLLER_PRIO, \ - .task_run_cpu = CONFIG_BT_CTRL_PINNED_TO_CORE, \ - .hci_cmd_num = CONFIG_BT_CTRL_HCI_CMD_NUM, \ - .sleep_en = UC_BT_CTRL_SLEEP_ENABLE, \ - .version_num = 0, \ - .bluetooth_mode = BTDM_CONTROLLER_MODE_EFF, \ - .magic = BTDM_CONFIG_MAGIC_VALUE, \ + .version = BTDM_CONFIG_VERSION, \ + .task_stack_size = UC_BT_CTRL_TASK_STACK_SIZE, \ + .task_prio = ESP_TASK_BT_CONTROLLER_PRIO, \ + .task_run_cpu = CONFIG_BT_CTRL_PINNED_TO_CORE, \ + .hci_cmd_num = CONFIG_BT_CTRL_HCI_CMD_NUM, \ + .nonblocking_cmd_buf = UC_BT_CTRL_NONBLOCK_CMD_BUF, \ + .sleep_en = UC_BT_CTRL_SLEEP_ENABLE, \ + .version_num = 0, \ + .bluetooth_mode = BTDM_CONTROLLER_MODE_EFF, \ + .magic = BTDM_CONFIG_MAGIC_VALUE, \ }, \ } #endif // SOC_BT_CLASSIC_SUPPORTED diff --git a/components/bt/include/esp32s31/include/esp_bt.h b/components/bt/include/esp32s31/include/esp_bt.h index cee53bdc3df..6370d5c4d00 100644 --- a/components/bt/include/esp32s31/include/esp_bt.h +++ b/components/bt/include/esp32s31/include/esp_bt.h @@ -112,7 +112,7 @@ typedef struct { #include "../../common/btdm_le.h" #endif /* SOC_BLE_SUPPORTED */ -#define BTDM_CONFIG_VERSION 0x20260127 +#define BTDM_CONFIG_VERSION 0x20260911 #define BTDM_CONFIG_MAGIC_VALUE 0x5a5aa5a5 /* Types definition @@ -128,6 +128,7 @@ typedef struct { uint8_t task_prio; /*!< Priority of the Bluetooth controller task */ uint8_t task_run_cpu; /*!< CPU number on which the Bluetooth controller task runs */ uint8_t hci_cmd_num; /*!< HCI command buffer number */ + uint8_t nonblocking_cmd_buf; /*!< Non-blocking mode for command buffer allocation */ uint8_t sleep_en; /*!< Enable sleep functionality */ uint8_t version_num; /*!< Hardware version number of this chip */ uint8_t bluetooth_mode; /*!< Controller mode: BR/EDR, BLE or Dual Mode */ @@ -167,6 +168,7 @@ typedef struct { .task_prio = ESP_TASK_BT_CONTROLLER_PRIO, \ .task_run_cpu = CONFIG_BT_CTRL_PINNED_TO_CORE, \ .hci_cmd_num = CONFIG_BT_CTRL_HCI_CMD_NUM, \ + .nonblocking_cmd_buf = UC_BT_CTRL_NONBLOCK_CMD_BUF, \ .sleep_en = UC_BT_CTRL_SLEEP_ENABLE, \ .version_num = 0, \ .bluetooth_mode = BTDM_CONTROLLER_MODE_EFF, \ @@ -184,6 +186,7 @@ typedef struct { .task_prio = ESP_TASK_BT_CONTROLLER_PRIO, \ .task_run_cpu = CONFIG_BT_CTRL_PINNED_TO_CORE, \ .hci_cmd_num = CONFIG_BT_CTRL_HCI_CMD_NUM, \ + .nonblocking_cmd_buf = UC_BT_CTRL_NONBLOCK_CMD_BUF, \ .sleep_en = UC_BT_CTRL_SLEEP_ENABLE, \ .version_num = 0, \ .bluetooth_mode = BTDM_CONTROLLER_MODE_EFF, \ diff --git a/components/bt/porting_btdm/controller/btdm_common/include/btdm_user_cfg.h b/components/bt/porting_btdm/controller/btdm_common/include/btdm_user_cfg.h index b5b5d936586..43664af4995 100644 --- a/components/bt/porting_btdm/controller/btdm_common/include/btdm_user_cfg.h +++ b/components/bt/porting_btdm/controller/btdm_common/include/btdm_user_cfg.h @@ -24,6 +24,12 @@ extern "C" { #define UC_BT_CTRL_TASK_STACK_SIZE CONFIG_BT_CTRL_TASK_STACK_SIZE +#ifdef CONFIG_BT_CTRL_NONBLOCK_CMD_BUF +#define UC_BT_CTRL_NONBLOCK_CMD_BUF CONFIG_BT_CTRL_NONBLOCK_CMD_BUF +#else +#define UC_BT_CTRL_NONBLOCK_CMD_BUF (0) +#endif + #ifdef CONFIG_BT_CTRL_HCI_INTERFACE_USE_UART #define UC_HCI_UART_EN CONFIG_BT_CTRL_HCI_INTERFACE_USE_UART #else diff --git a/components/bt/porting_btdm/transport/Kconfig.in b/components/bt/porting_btdm/transport/Kconfig.in index 93d1f08a003..9ad7b273d2e 100644 --- a/components/bt/porting_btdm/transport/Kconfig.in +++ b/components/bt/porting_btdm/transport/Kconfig.in @@ -133,4 +133,12 @@ menu "Bluetooth controller HCI" Deprecated: this option is no longer used after HCI UART DMA switched to UHCI driver APIs, and will be removed in a future release. + config BT_CTRL_NONBLOCK_CMD_BUF + bool "Return NULL when no HCI command buffer is available instead of blocking" + default y if BT_CTRL_UART_HCI_DMA_MODE || BT_NIMBLE_ENABLED + default n + help + By default, the API will block if no HCI command buffer is available. + It is recommended to enable this option when HCI Tx and Rx processing run within the same task. + Otherwise, the task may deadlock. endmenu diff --git a/components/bt/porting_btdm/transport/driver/common/hci_driver_h4.c b/components/bt/porting_btdm/transport/driver/common/hci_driver_h4.c index 76d9d7924a8..ef9e2ede27a 100644 --- a/components/bt/porting_btdm/transport/driver/common/hci_driver_h4.c +++ b/components/bt/porting_btdm/transport/driver/common/hci_driver_h4.c @@ -45,6 +45,14 @@ #define HCI_H4_SM_W4_HEADER 1 #define HCI_H4_SM_W4_PAYLOAD 2 #define HCI_H4_SM_COMPLETED 3 +#define HCI_H4_SM_WAIT_RESET 4 + +/* Buffer allocation failed. The stream is still in sync, so the state machine + * is kept as is and the allocation is retried on the next pass. + */ +#define HCI_H4_ERR_MEM (-1) +/* The stream cannot be parsed anymore, the current packet has to be dropped. */ +#define HCI_H4_ERR_SYNC_LOSS (-2) #define TAG "HCI_H4" @@ -78,7 +86,7 @@ hci_h4_frame_start(struct hci_h4_sm *rxs, uint8_t pkt_type) #endif // (!CONFIG_BT_CONTROLLER_ENABLED) default: /* !TODO: Sync loss. Need to wait for reset. */ - return -1; + return HCI_H4_ERR_SYNC_LOSS; } return 0; @@ -129,7 +137,7 @@ hci_h4_sm_w4_header(struct hci_h4_sm *h4sm, struct hci_h4_input_buffer *ib) HCI_TRANS_ASSERT(h4sm->allocs && h4sm->allocs->cmd, 0, 0); h4sm->pkt = h4sm->allocs->cmd(); if (!h4sm->pkt) { - return -1; + return HCI_H4_ERR_MEM; } memcpy(h4sm->pkt->data, h4sm->hdr, h4sm->len); @@ -143,7 +151,7 @@ hci_h4_sm_w4_header(struct hci_h4_sm *h4sm, struct hci_h4_input_buffer *ib) h4sm->exp_len = btdm_get_le16(&h4sm->hdr[2]) + 4; h4sm->pkt = h4sm->allocs->bredr_acl(conn_handle); if (!h4sm->pkt) { - return -1; + return HCI_H4_ERR_MEM; } memcpy(h4sm->pkt->data, h4sm->hdr, h4sm->len); break; @@ -153,17 +161,20 @@ hci_h4_sm_w4_header(struct hci_h4_sm *h4sm, struct hci_h4_input_buffer *ib) if (HCI_INTERNAL_CONN_IS_BLE(conn_handle)) { h4sm->om = h4sm->allocs->acl(); if (!h4sm->om) { - return -1; + return HCI_H4_ERR_MEM; } if (ble_mbuf_append(h4sm->om, h4sm->hdr, h4sm->len)) { - return -1; + /* Release the mbuf so that the retry starts from a clean state. */ + h4sm->frees->acl(h4sm->om); + h4sm->om = NULL; + return HCI_H4_ERR_MEM; } h4sm->exp_len = btdm_get_le16(&h4sm->hdr[2]) + 4; break; } #endif // UC_BT_CTRL_BLE_IS_ENABLE - return -1; + return HCI_H4_ERR_SYNC_LOSS; #if UC_BT_CTRL_BR_EDR_IS_ENABLE case HCI_H4_SYNC: conn_handle = btdm_get_le16(&h4sm->hdr[0]) & HCI_INTERNAL_CONN_MASK; @@ -214,7 +225,7 @@ hci_h4_sm_w4_header(struct hci_h4_sm *h4sm, struct hci_h4_input_buffer *ib) } else { h4sm->buf = h4sm->allocs->evt(0); if (!h4sm->buf) { - return -1; + return HCI_H4_ERR_MEM; } } @@ -230,14 +241,14 @@ hci_h4_sm_w4_header(struct hci_h4_sm *h4sm, struct hci_h4_input_buffer *ib) h4sm->exp_len = (btdm_get_le16(&h4sm->hdr[2]) & 0x3fff) + 4; h4sm->buf = h4sm->allocs->iso(h4sm->exp_len); if (!h4sm->buf) { - return -1; + return HCI_H4_ERR_MEM; } memcpy(h4sm->buf, h4sm->hdr, h4sm->len); break; #endif // CONFIG_BT_LE_ISO_SUPPORT default: - return -2; + return HCI_H4_ERR_SYNC_LOSS; } return 0; @@ -288,13 +299,13 @@ hci_h4_sm_w4_payload(struct hci_h4_sm *h4sm, len = BLE_MBUF_PKTLEN(h4sm->om) - mbuf_len; h4sm->len += len; hci_h4_ib_consume(ib, len); - return -1; + return HCI_H4_ERR_MEM; } } #endif // UC_BT_CTRL_BLE_IS_ENABLE break; default: - return -2; + return HCI_H4_ERR_SYNC_LOSS; } h4sm->len += len; @@ -389,6 +400,67 @@ hci_h4_sm_completed(struct hci_h4_sm *h4sm) } } +/* H4 type + HCI Reset (opcode 0x0C03, plen 0). */ +static const uint8_t s_hci_h4_reset_pattern[] = {HCI_H4_CMD, 0x03, 0x0C, 0x00}; + +static int +hci_h4_sm_dispatch_reset_cmd(struct hci_h4_sm *h4sm) +{ + HCI_TRANS_ASSERT(h4sm->allocs && h4sm->allocs->cmd, 0, 0); + h4sm->pkt_type = HCI_H4_CMD; + h4sm->pkt = h4sm->allocs->cmd(); + if (!h4sm->pkt) { + return HCI_H4_ERR_MEM; + } + + memcpy(h4sm->pkt->data, &s_hci_h4_reset_pattern[1], 3); + h4sm->len = 3; + hci_h4_sm_completed(h4sm); + h4sm->reset_match_idx = 0; + h4sm->state = HCI_H4_SM_W4_PKT_TYPE; + return 0; +} + +static int +hci_h4_sm_wait_for_reset(struct hci_h4_sm *h4sm, struct hci_h4_input_buffer *ib) +{ + int rc; + + /* Pattern already fully matched; retry delivery after a previous OOM. */ + if (h4sm->reset_match_idx == sizeof(s_hci_h4_reset_pattern)) { + return hci_h4_sm_dispatch_reset_cmd(h4sm); + } + + for (uint16_t i = 0; i < ib->len; i++) { + if (ib->buf[i] == s_hci_h4_reset_pattern[h4sm->reset_match_idx]) { + h4sm->reset_match_idx++; + } else { + if (ib->buf[i] == s_hci_h4_reset_pattern[0]) { + h4sm->reset_match_idx = 1; + } else { + h4sm->reset_match_idx = 0; + } + } + if (h4sm->reset_match_idx == sizeof(s_hci_h4_reset_pattern)) { + hci_h4_ib_consume(ib, i + 1); + rc = hci_h4_sm_dispatch_reset_cmd(h4sm); + if (rc == HCI_H4_ERR_MEM) { + /* Reset bytes are already consumed; keep idx at 4 and retry alloc. */ + h4sm->state = HCI_H4_SM_WAIT_RESET; + } + return rc; + } + } + + if (h4sm->reset_match_idx) { + /* Need more data */ + hci_h4_ib_consume(ib, ib->len); + return 1; + } + + return HCI_H4_ERR_SYNC_LOSS; +} + static int hci_h4_sm_free_buf(struct hci_h4_sm *h4sm) { @@ -466,7 +538,8 @@ hci_h4_sm_rx(struct hci_h4_sm *h4sm, const uint8_t *buf, uint16_t len) switch (h4sm->state) { case HCI_H4_SM_W4_PKT_TYPE: if (hci_h4_frame_start(h4sm, ib.buf[0]) < 0) { - return -1; + rc = HCI_H4_ERR_SYNC_LOSS; + break; } hci_h4_ib_consume(&ib, 1); @@ -492,27 +565,32 @@ hci_h4_sm_rx(struct hci_h4_sm *h4sm, const uint8_t *buf, uint16_t len) hci_h4_sm_completed(h4sm); h4sm->state = HCI_H4_SM_W4_PKT_TYPE; break; + case HCI_H4_SM_WAIT_RESET: + rc = hci_h4_sm_wait_for_reset(h4sm, &ib); + break; default: - return -1; + rc = HCI_H4_ERR_SYNC_LOSS; + break; } } - if (rc < 0) { + if ((rc < 0) && (rc != HCI_H4_ERR_MEM)) { hci_h4_sm_free_buf(h4sm); - h4sm->state = HCI_H4_SM_W4_PKT_TYPE; - return -1; + h4sm->reset_match_idx = 0; + h4sm->state = HCI_H4_SM_WAIT_RESET; + return rc; } + /* Calculate consumed bytes * * Note: we should always consume some bytes unless there is an oom error. - * It's also possible that we have an oom error but already consumed some - * data, in such case just return success and error will be returned on next - * pass. + * On oom the state machine is left untouched so that the allocation is + * retried on the next pass, and the number of bytes actually taken from + * `buf` is reported to let the caller resubmit the remaining data. */ len = len - ib.len; if (len == 0) { - HCI_TRANS_ASSERT((rc < 0), rc, ib.len); - return -1; + HCI_TRANS_ASSERT((rc == HCI_H4_ERR_MEM), rc, ib.len); } return len; diff --git a/components/bt/porting_btdm/transport/driver/uart/hci_driver_uart_dma.c b/components/bt/porting_btdm/transport/driver/uart/hci_driver_uart_dma.c index bf863fc387f..c7ceb231bbe 100644 --- a/components/bt/porting_btdm/transport/driver/uart/hci_driver_uart_dma.c +++ b/components/bt/porting_btdm/transport/driver/uart/hci_driver_uart_dma.c @@ -53,6 +53,9 @@ typedef struct { uhci_transmit_buffer_info_t *tx_segments; /*!< Scratch array of UHCI TX segments for one multi-buffer transaction. */ volatile hci_trans_tx_state_t hci_tx_state; /*!< Only one HCI packet is in flight (tx-list entries cannot be mixed). */ volatile bool rx_copy_overflow; /*!< Set in ISR when rx_copy_ringbuf cannot accept a DMA slice. */ + uint8_t *rx_pending_item; /*!< Original xRingbufferReceive() pointer; held until H4 consumes the slice. */ + uint8_t *rx_pending_data; /*!< Unparsed remainder of rx_pending_item (item + already consumed). */ + size_t rx_pending_len; /*!< Bytes still to feed to hci_h4_sm_rx() from rx_pending_data. */ } hci_driver_uart_dma_env_t; /* Max UHCI TX segments in one uhci_multi_buffer_transmit(); maps to max_transmit_buffer_count. */ @@ -77,7 +80,6 @@ typedef struct { */ #define HCI_UHCI_RX_DESC_MEM (UC_BT_CTRL_HCI_TRANS_RX_MEM_NUM * HCI_RX_DMA_RING_SIZE) #define HCI_RX_COPY_RINGBUF_SIZE HCI_RX_DMA_RING_SIZE - static const char *TAG = "uart_dma"; static hci_driver_uart_dma_env_t s_hci_driver_uart_dma_env; static struct hci_h4_sm s_hci_driver_uart_h4_sm; @@ -85,6 +87,18 @@ static portMUX_TYPE s_hci_tx_state_mux = portMUX_INITIALIZER_UNLOCKED; static int hci_driver_uart_dma_tx_submit(void); +static void +hci_driver_uart_dma_rx_pending_clear(void) +{ + if (s_hci_driver_uart_dma_env.rx_pending_item && s_hci_driver_uart_dma_env.rx_copy_ringbuf) { + vRingbufferReturnItem(s_hci_driver_uart_dma_env.rx_copy_ringbuf, + s_hci_driver_uart_dma_env.rx_pending_item); + } + s_hci_driver_uart_dma_env.rx_pending_item = NULL; + s_hci_driver_uart_dma_env.rx_pending_data = NULL; + s_hci_driver_uart_dma_env.rx_pending_len = 0; +} + /** * @brief Free the DMA ring, software RX ringbuf and TX segment scratch array. * @@ -94,6 +108,8 @@ static int hci_driver_uart_dma_tx_submit(void); static void hci_driver_uart_dma_memory_deinit(void) { + hci_driver_uart_dma_rx_pending_clear(); + if (s_hci_driver_uart_dma_env.rx_copy_ringbuf) { vRingbufferDelete(s_hci_driver_uart_dma_env.rx_copy_ringbuf); s_hci_driver_uart_dma_env.rx_copy_ringbuf = NULL; @@ -423,18 +439,55 @@ hci_driver_uart_dma_h4_frame_cb(uint8_t pkt_type, void *data, int pkt_len, uint8 return forward_cb(pkt_type, data, pkt_len, HCI_DRIVER_DIR_H2C, data_source); } +/** + * @brief Feed one H4 slice and either return the ringbuf item or hold the remainder. + * + * @return true if the caller may continue with the next ringbuf item. + * false if H4 consumed fewer bytes than offered (typically OOM); the + * unparsed tail is stored in rx_pending_* and must be retried + * on the next process_rx() entry. + */ +static bool +hci_driver_uart_dma_h4_feed(uint8_t *item, uint8_t *data, size_t item_size) +{ + int ret; + + ESP_LOGD(TAG, "uart rx"); + ESP_LOG_BUFFER_HEXDUMP(TAG, data, item_size, ESP_LOG_DEBUG); + ret = hci_h4_sm_rx(s_hci_driver_uart_dma_env.h4_sm, data, (uint16_t)item_size); + if (ret < 0) { + ESP_LOGW(TAG, "parse rx data error!\n"); +#if UC_BT_CTRL_BLE_IS_ENABLE + r_ble_ll_hci_ev_hw_err(ESP_HCI_SYNC_LOSS_ERR); +#endif // #if UC_BT_CTRL_BLE_IS_ENABLE + } else if ((size_t)ret < item_size) { + /* Keep the ringbuf item checked out so the unparsed bytes stay valid. */ + s_hci_driver_uart_dma_env.rx_pending_item = item; + s_hci_driver_uart_dma_env.rx_pending_data = data + ret; + s_hci_driver_uart_dma_env.rx_pending_len = item_size - (size_t)ret; + return false; + } + + vRingbufferReturnItem(s_hci_driver_uart_dma_env.rx_copy_ringbuf, item); + s_hci_driver_uart_dma_env.rx_pending_item = NULL; + s_hci_driver_uart_dma_env.rx_pending_data = NULL; + s_hci_driver_uart_dma_env.rx_pending_len = 0; + + return true; +} + /** * @brief Drain the software RX ringbuf into the H4 state machine. * * Timeout is 0: the process task is already woken by process_sem. Chunks may be * one DMA node or a short EOF tail; H4 concatenates them into HCI packets. + * If H4 returns a partial consume, the remainder is retried first on the next call. */ static void hci_driver_uart_dma_process_rx(void) { size_t item_size; uint8_t *rx_data; - int ret; if (s_hci_driver_uart_dma_env.rx_copy_overflow) { ESP_LOGE(TAG, "RX software ring buffer overflow, HCI stream may lose sync"); @@ -444,17 +497,17 @@ hci_driver_uart_dma_process_rx(void) #endif // #if UC_BT_CTRL_BLE_IS_ENABLE } + if (s_hci_driver_uart_dma_env.rx_pending_item) { + if (!hci_driver_uart_dma_h4_feed(s_hci_driver_uart_dma_env.rx_pending_item, + s_hci_driver_uart_dma_env.rx_pending_data, + s_hci_driver_uart_dma_env.rx_pending_len)) { + return; + } + } + while ((rx_data = xRingbufferReceive(s_hci_driver_uart_dma_env.rx_copy_ringbuf, &item_size, 0)) != NULL) { - ESP_LOGD(TAG, "uart rx"); - ESP_LOG_BUFFER_HEXDUMP(TAG, rx_data, item_size, ESP_LOG_DEBUG); - ret = hci_h4_sm_rx(s_hci_driver_uart_dma_env.h4_sm, rx_data, (uint16_t)item_size); - /* Return the item before parsing the next slice so the ringbuf can accept more ISR copies. */ - vRingbufferReturnItem(s_hci_driver_uart_dma_env.rx_copy_ringbuf, rx_data); - if (ret < 0) { - ESP_LOGW(TAG, "parse rx data error!\n"); -#if UC_BT_CTRL_BLE_IS_ENABLE - r_ble_ll_hci_ev_hw_err(ESP_HCI_SYNC_LOSS_ERR); -#endif // #if UC_BT_CTRL_BLE_IS_ENABLE + if (!hci_driver_uart_dma_h4_feed(rx_data, rx_data, item_size)) { + return; } } } diff --git a/components/bt/porting_btdm/transport/include/common/hci_driver_h4.h b/components/bt/porting_btdm/transport/include/common/hci_driver_h4.h index fc35f4a4125..3fff2d394bd 100644 --- a/components/bt/porting_btdm/transport/include/common/hci_driver_h4.h +++ b/components/bt/porting_btdm/transport/include/common/hci_driver_h4.h @@ -96,6 +96,7 @@ struct hci_h4_sm { uint8_t state; uint8_t pkt_type; uint8_t min_len; + uint8_t reset_match_idx; uint16_t len; uint16_t exp_len; uint8_t hdr[4]; @@ -115,6 +116,17 @@ void hci_h4_sm_init(struct hci_h4_sm *h4sm, const struct hci_h4_frees *frees, hci_h4_frame_cb *frame_cb); +/** + * @brief Feed received H4 data into the parser state machine. + * + * @return Number of bytes of `buf` that have been parsed, which may be less + * than `len` (0 included) when a buffer allocation failed. The + * unparsed data has to be submitted again, the allocation is retried + * on the next call. + * A negative value means the stream cannot be parsed anymore and the + * current packet has been dropped, the caller should report a sync + * loss. + */ int hci_h4_sm_rx(struct hci_h4_sm *h4sm, const uint8_t *buf, uint16_t len); #endif /* _HCI_H4_H_ */