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_ */