From 35287fd97c0520c51e04bca67d6b88a83d602c28 Mon Sep 17 00:00:00 2001 From: Jonathan Beri Date: Tue, 23 Jun 2026 21:22:55 -0700 Subject: [PATCH] pico_cyw43_driver: cybt: re-read transient corrupt ring index cybt_get_bt_buf_index() asserts when the BT controller's bt2host / host2bt ring indices read back out of range over the gSPI backplane. Under transient bus contention these indices can read corrupt for a single access, so the assert turns a recoverable glitch into a system abort. Re-read the index up to CYBT_BUF_INDEX_REREAD_MAX (8) times and, if it still does not validate, return CYBT_ERR_HCI_READ_FAILED so the caller can drop the cycle instead of aborting. Reproduced on a CYW43439 (Pico 2 W) under concurrent WiFi+BT load: the assert fired at boot and under sustained traffic; with the re-read the controller recovers and BT stays up. Assisted-by: Claude Opus 4.8 Signed-off-by: Jonathan Beri --- .../cybt_shared_bus/cybt_shared_bus_driver.c | 55 +++++++++++-------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/src/rp2_common/pico_cyw43_driver/cybt_shared_bus/cybt_shared_bus_driver.c b/src/rp2_common/pico_cyw43_driver/cybt_shared_bus/cybt_shared_bus_driver.c index dcfca3a15f..061175f744 100644 --- a/src/rp2_common/pico_cyw43_driver/cybt_shared_bus/cybt_shared_bus_driver.c +++ b/src/rp2_common/pico_cyw43_driver/cybt_shared_bus/cybt_shared_bus_driver.c @@ -498,38 +498,47 @@ void cybt_debug_dump(void) { #endif } +/* + * The BT controller's ring indices are read over the shared gSPI backplane, + * which carries WiFi traffic concurrently. Under bus contention a single read + * can return a transiently out-of-range value; asserting on it turns a + * recoverable glitch into a system abort. Re-read up to + * CYBT_BUF_INDEX_REREAD_MAX times (the corruption does not persist) and, only + * if it still does not validate, return an error so the caller drops the cycle + * gracefully (cyw43_bluetooth_has_pending -> "no pending"; an HCI read -> + * CYBT_ERR_HCI_READ_FAILED) instead of aborting. + */ +#define CYBT_BUF_INDEX_REREAD_MAX 8 + cybt_result_t cybt_get_bt_buf_index(cybt_fw_membuf_index_t *p_buf_index) { uint32_t buf[4]; - cybt_mem_read(H2B_BUF_IN_ADDR, (uint8_t *) buf, sizeof(buf)); + for (int attempt = 0; attempt < CYBT_BUF_INDEX_REREAD_MAX; attempt++) { + cybt_mem_read(H2B_BUF_IN_ADDR, (uint8_t *) buf, sizeof(buf)); - p_buf_index->host2bt_in_val = buf[0]; - p_buf_index->host2bt_out_val = buf[1]; - p_buf_index->bt2host_in_val = buf[2]; - p_buf_index->bt2host_out_val = buf[3]; - - cybt_debug("cybt_get_bt_buf_index: h2b_in = 0x%08lx, h2b_out = 0x%08lx, b2h_in = 0x%08lx, b2h_out = 0x%08lx\n", - p_buf_index->host2bt_in_val, - p_buf_index->host2bt_out_val, - p_buf_index->bt2host_in_val, - p_buf_index->bt2host_out_val); + p_buf_index->host2bt_in_val = buf[0]; + p_buf_index->host2bt_out_val = buf[1]; + p_buf_index->bt2host_in_val = buf[2]; + p_buf_index->bt2host_out_val = buf[3]; + if (p_buf_index->host2bt_in_val < BTSDIO_FWBUF_SIZE && + p_buf_index->host2bt_out_val < BTSDIO_FWBUF_SIZE && + p_buf_index->bt2host_in_val < BTSDIO_FWBUF_SIZE && + p_buf_index->bt2host_out_val < BTSDIO_FWBUF_SIZE) { #if CYBT_CORRUPTION_TEST - if (p_buf_index->host2bt_in_val >= BTSDIO_FWBUF_SIZE || p_buf_index->host2bt_out_val >= BTSDIO_FWBUF_SIZE || - p_buf_index->bt2host_in_val >= BTSDIO_FWBUF_SIZE || p_buf_index->bt2host_out_val >= BTSDIO_FWBUF_SIZE) { - cybt_error("cybt_get_bt_buf_index invalid buffer value\n"); - cybt_debug_dump(); - } else { - memcpy((uint8_t *) &last_buf_index, (uint8_t *) p_buf_index, sizeof(cybt_fw_membuf_index_t)); - } + memcpy((uint8_t *) &last_buf_index, (uint8_t *) p_buf_index, + sizeof(cybt_fw_membuf_index_t)); #endif + return CYBT_SUCCESS; + } - assert(p_buf_index->host2bt_in_val < BTSDIO_FWBUF_SIZE); - assert(p_buf_index->host2bt_out_val < BTSDIO_FWBUF_SIZE); - assert(p_buf_index->bt2host_in_val < BTSDIO_FWBUF_SIZE); - assert(p_buf_index->bt2host_out_val < BTSDIO_FWBUF_SIZE); + cybt_error("cybt_get_bt_buf_index: out-of-range index (attempt %d/%d), re-reading\n", + attempt + 1, CYBT_BUF_INDEX_REREAD_MAX); + } - return CYBT_SUCCESS; + cybt_error("cybt_get_bt_buf_index: index still corrupt after %d reads; dropping cycle\n", + CYBT_BUF_INDEX_REREAD_MAX); + return CYBT_ERR_HCI_READ_FAILED; } static cybt_result_t cybt_reg_write(uint32_t reg_addr, uint32_t value) {