Root cause (verified against our exact IDF tree, not the community guess): the "258" in "sdio_write_task: Failed to send data: 258" is NOT a timeout (that is 263). 258 = 0x102 = ESP_ERR_INVALID_ARG. On the ESP32-P4, block- mode CMD53 writes require the SOURCE buffer to be 64-byte (cache-line) aligned; the IDF sdmmc driver rejects a misaligned source with INVALID_ARG BEFORE any bus activity. esp_hosts write loop then declares "Unrecoverable host sdio state" and reboots the whole P4. The audio TX payload is not 64-aligned, so streaming mic audio wedged on the very FIRST frame (which is exactly what we saw: listening -> instant Failed to send -> reboot). This also explains why buffer/queue/clock/retry tuning all did nothing: the write never reached the bus. And why our symptom was instant, not after ~100 writes (the community block-mode-desync theory) — it is the first misaligned buffer, every time. Fix: vendored esp_hosted 2.12.11 as an editable local component (overrides the registry copy) and bounce a misaligned TX payload through one aligned DMA scratch buffer in hosted_sdio_write_block (port_esp_hosted_host_sdio.c). TX is serialized by the bus lock so a single static bounce buffer is safe; freed in hosted_sdio_deinit. Host-only change — no C6 reflash. VERIFIED ON HARDWARE (autonomous self-test): 40s of continuous mic-audio upstream streaming — the traffic that previously wedged on the first frame — ran clean, zero timeouts, zero reboots. A guarded SDIO_TX_SELFTEST harness is kept (compiled out) for future SDIO stress testing. Credit: root cause + patch designed via multi-agent investigation; the precise 258=INVALID_ARG decode (correcting the upstream community timeout assumption) came from checking our actual esp_err.h. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
371 lines
8.3 KiB
C
371 lines
8.3 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include "driver/gpio.h"
|
|
#include "driver/uart.h"
|
|
#if CONFIG_BT_ENABLED
|
|
#include "esp_bt.h"
|
|
#endif
|
|
#include "slave_bt.h"
|
|
#include "slave_bt_uart.h"
|
|
#include "interface.h"
|
|
#include "esp_hosted_transport.h"
|
|
#include "esp_hosted_transport_init.h"
|
|
#include "esp_hosted_interface.h"
|
|
|
|
#ifdef CONFIG_BT_ENABLED
|
|
#include "esp_log.h"
|
|
#include "esp_hosted_log.h"
|
|
#include "soc/lldesc.h"
|
|
#include "esp_mac.h"
|
|
|
|
static const char *TAG = "h_bt";
|
|
|
|
#if BLUETOOTH_HCI
|
|
/* ***** HCI specific part ***** */
|
|
|
|
#define VHCI_MAX_TIMEOUT_MS 2000
|
|
static SemaphoreHandle_t vhci_send_sem;
|
|
|
|
static void controller_rcv_pkt_ready(void)
|
|
{
|
|
if (vhci_send_sem)
|
|
xSemaphoreGive(vhci_send_sem);
|
|
}
|
|
|
|
static int host_rcv_pkt(uint8_t *data, uint16_t len)
|
|
{
|
|
interface_buffer_handle_t buf_handle;
|
|
uint8_t *buf = NULL;
|
|
|
|
buf = (uint8_t *) malloc(len);
|
|
|
|
if (!buf) {
|
|
ESP_LOGE(TAG, "HCI Send packet: memory allocation failed");
|
|
return ESP_FAIL;
|
|
}
|
|
|
|
memcpy(buf, data, len);
|
|
|
|
memset(&buf_handle, 0, sizeof(buf_handle));
|
|
|
|
buf_handle.if_type = ESP_HCI_IF;
|
|
buf_handle.if_num = 0;
|
|
buf_handle.payload_len = len;
|
|
buf_handle.payload = buf;
|
|
buf_handle.wlan_buf_handle = buf;
|
|
buf_handle.free_buf_handle = free;
|
|
|
|
ESP_HEXLOGV("bt_tx new", data, len, 32);
|
|
|
|
if (send_to_host_queue(&buf_handle, PRIO_Q_BT)) {
|
|
free(buf);
|
|
return ESP_FAIL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static esp_vhci_host_callback_t vhci_host_cb = {
|
|
.notify_host_send_available = controller_rcv_pkt_ready,
|
|
.notify_host_recv = host_rcv_pkt
|
|
};
|
|
|
|
void process_hci_rx_pkt(uint8_t *payload, uint16_t payload_len)
|
|
{
|
|
/* VHCI needs one extra byte at the start of payload */
|
|
/* that is accommodated in esp_payload_header */
|
|
ESP_HEXLOGV("bt_rx", payload, payload_len, 32);
|
|
|
|
payload--;
|
|
payload_len++;
|
|
|
|
if (!esp_vhci_host_check_send_available()) {
|
|
ESP_LOGD(TAG, "VHCI not available");
|
|
}
|
|
|
|
#if SOC_ESP_NIMBLE_CONTROLLER
|
|
esp_vhci_host_send_packet(payload, payload_len);
|
|
#else
|
|
if (vhci_send_sem) {
|
|
if (xSemaphoreTake(vhci_send_sem, VHCI_MAX_TIMEOUT_MS) == pdTRUE) {
|
|
esp_vhci_host_send_packet(payload, payload_len);
|
|
} else {
|
|
ESP_LOGI(TAG, "VHCI sem timeout");
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#elif BLUETOOTH_UART
|
|
/* ***** UART specific part ***** */
|
|
|
|
#endif
|
|
|
|
#if BLUETOOTH_HCI
|
|
#if SOC_ESP_NIMBLE_CONTROLLER
|
|
|
|
#if ESP_IDF_VERSION <= ESP_IDF_VERSION_VAL(5, 3, 0)
|
|
#include "ble_hci_trans.h"
|
|
|
|
typedef enum {
|
|
DATA_TYPE_COMMAND = 1,
|
|
DATA_TYPE_ACL = 2,
|
|
DATA_TYPE_SCO = 3,
|
|
DATA_TYPE_EVENT = 4
|
|
} serial_data_type_t;
|
|
|
|
/* Host-to-controller command. */
|
|
#define BLE_HCI_TRANS_BUF_CMD 3
|
|
|
|
/* ACL_DATA_MBUF_LEADINGSPACE: The leadingspace in user info header for ACL data */
|
|
#define ACL_DATA_MBUF_LEADINGSPACE 4
|
|
|
|
void esp_vhci_host_send_packet(uint8_t *data, uint16_t len)
|
|
{
|
|
if (*(data) == DATA_TYPE_COMMAND) {
|
|
struct ble_hci_cmd *cmd = NULL;
|
|
cmd = (struct ble_hci_cmd *) ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_CMD);
|
|
if (!cmd) {
|
|
ESP_LOGE(TAG, "Failed to allocate memory for HCI transport buffer");
|
|
return;
|
|
}
|
|
|
|
memcpy((uint8_t *)cmd, data + 1, len - 1);
|
|
ble_hci_trans_hs_cmd_tx((uint8_t *)cmd);
|
|
}
|
|
|
|
if (*(data) == DATA_TYPE_ACL) {
|
|
struct os_mbuf *om = os_msys_get_pkthdr(len, ACL_DATA_MBUF_LEADINGSPACE);
|
|
assert(om);
|
|
os_mbuf_append(om, &data[1], len - 1);
|
|
ble_hci_trans_hs_acl_tx(om);
|
|
}
|
|
|
|
}
|
|
|
|
bool esp_vhci_host_check_send_available() {
|
|
// not need to check in esp new controller
|
|
return true;
|
|
}
|
|
|
|
int
|
|
ble_hs_hci_rx_evt(uint8_t *hci_ev, void *arg)
|
|
{
|
|
uint16_t len = hci_ev[1] + 3;
|
|
uint8_t *data = (uint8_t *)malloc(len);
|
|
data[0] = 0x04;
|
|
memcpy(&data[1], hci_ev, len - 1);
|
|
ble_hci_trans_buf_free(hci_ev);
|
|
vhci_host_cb.notify_host_recv(data, len);
|
|
free(data);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
ble_hs_rx_data(struct os_mbuf *om, void *arg)
|
|
{
|
|
uint16_t len = om->om_len + 1;
|
|
uint8_t *data = (uint8_t *)malloc(len);
|
|
data[0] = 0x02;
|
|
os_mbuf_copydata(om, 0, len - 1, &data[1]);
|
|
vhci_host_cb.notify_host_recv(data, len);
|
|
free(data);
|
|
os_mbuf_free_chain(om);
|
|
return 0;
|
|
}
|
|
#endif // ESP_IDF_VERSION <= ESP_IDF_VERSION_VAL(5, 3, 0)
|
|
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
esp_err_t init_bluetooth(void)
|
|
{
|
|
esp_err_t ret = ESP_FAIL;
|
|
|
|
#if CONFIG_BT_ENABLED
|
|
uint8_t mac[BSSID_BYTES_SIZE] = {0};
|
|
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
|
|
|
|
ESP_ERROR_CHECK(esp_read_mac(mac, ESP_MAC_BT));
|
|
ESP_LOGI(TAG, "ESP Bluetooth MAC addr: %02x:%02x:%02x:%02x:%02x:%02x",
|
|
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
|
|
|
#ifdef BLUETOOTH_UART
|
|
slave_bt_init_uart(&bt_cfg);
|
|
#endif
|
|
|
|
ret = esp_bt_controller_init(&bt_cfg);
|
|
if (ret != ESP_OK) {
|
|
ESP_LOGE(TAG, "esp_bt_controller_init() FAILED");
|
|
return ret;
|
|
}
|
|
|
|
#if BLUETOOTH_HCI
|
|
vhci_send_sem = xSemaphoreCreateBinary();
|
|
if (vhci_send_sem == NULL) {
|
|
ESP_LOGE(TAG, "Failed to create VHCI send sem");
|
|
return ESP_ERR_NO_MEM;
|
|
}
|
|
|
|
xSemaphoreGive(vhci_send_sem);
|
|
#endif
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
esp_err_t deinit_bluetooth(bool mem_release)
|
|
{
|
|
esp_err_t result = ESP_FAIL;
|
|
|
|
#ifdef CONFIG_BT_ENABLED
|
|
#if BLUETOOTH_HCI
|
|
if (vhci_send_sem) {
|
|
/* Dummy take and give sema before deleting it */
|
|
xSemaphoreTake(vhci_send_sem, portMAX_DELAY);
|
|
xSemaphoreGive(vhci_send_sem);
|
|
vSemaphoreDelete(vhci_send_sem);
|
|
vhci_send_sem = NULL;
|
|
}
|
|
#endif
|
|
result = esp_bt_controller_deinit();
|
|
if (result != ESP_OK) {
|
|
ESP_LOGE(TAG, "esp_bt_controller_deinit FAILED");
|
|
return result;
|
|
}
|
|
|
|
if (mem_release) {
|
|
result = ESP_OK;
|
|
#if BLUETOOTH_BLE
|
|
result = esp_bt_controller_mem_release(ESP_BT_MODE_BLE);
|
|
#elif BLUETOOTH_BT
|
|
result = esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
|
|
#elif BLUETOOTH_BT_BLE
|
|
result = esp_bt_controller_mem_release(ESP_BT_MODE_BTDM);
|
|
#endif
|
|
if (result != ESP_OK) {
|
|
ESP_LOGE(TAG, "esp_bt_controller_mem_release FAILED");
|
|
return result;
|
|
}
|
|
}
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
esp_err_t enable_bluetooth(void)
|
|
{
|
|
esp_err_t ret = ESP_FAIL;
|
|
|
|
#ifdef CONFIG_BT_ENABLED
|
|
#if BLUETOOTH_BLE
|
|
ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
|
|
#elif BLUETOOTH_BT
|
|
ret = esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT);
|
|
#elif BLUETOOTH_BT_BLE
|
|
ret = esp_bt_controller_enable(ESP_BT_MODE_BTDM);
|
|
#endif
|
|
if (ret != ESP_OK) {
|
|
ESP_LOGE(TAG, "esp_bt_controller_enable FAILED (or not called)");
|
|
return ret;
|
|
}
|
|
|
|
#if BLUETOOTH_HCI
|
|
#if SOC_ESP_NIMBLE_CONTROLLER && (ESP_IDF_VERSION <= ESP_IDF_VERSION_VAL(5, 3, 0))
|
|
ble_hci_trans_cfg_hs((ble_hci_trans_rx_cmd_fn *)ble_hs_hci_rx_evt,NULL,
|
|
(ble_hci_trans_rx_acl_fn *)ble_hs_rx_data,NULL);
|
|
#else
|
|
ret = esp_vhci_host_register_callback(&vhci_host_cb);
|
|
|
|
if (ret != ESP_OK) {
|
|
ESP_LOGE(TAG, "Failed to register VHCI callback");
|
|
return ret;
|
|
}
|
|
#endif
|
|
#endif // BLUETOOTH_HCI
|
|
#endif // CONFIG_BT_ENABLED
|
|
|
|
return ret;
|
|
}
|
|
|
|
esp_err_t disable_bluetooth(void)
|
|
{
|
|
#ifdef CONFIG_BT_ENABLED
|
|
#if BLUETOOTH_HCI
|
|
// unregister callback functions
|
|
#if SOC_ESP_NIMBLE_CONTROLLER && (ESP_IDF_VERSION <= ESP_IDF_VERSION_VAL(5, 3, 0))
|
|
ble_hci_trans_cfg_hs(NULL, NULL,
|
|
NULL, NULL);
|
|
#else
|
|
esp_vhci_host_callback_t null_cb = {
|
|
NULL, NULL };
|
|
esp_vhci_host_register_callback(&null_cb);
|
|
#endif
|
|
#endif
|
|
|
|
return esp_bt_controller_disable();
|
|
#else
|
|
return ESP_FAIL;
|
|
#endif
|
|
}
|
|
|
|
uint8_t get_bluetooth_capabilities(void)
|
|
{
|
|
uint8_t cap = 0;
|
|
#ifdef CONFIG_BT_ENABLED
|
|
ESP_LOGI(TAG, "- BT/BLE");
|
|
#if BLUETOOTH_HCI
|
|
|
|
#if CONFIG_ESP_SPI_HOST_INTERFACE
|
|
ESP_LOGI(TAG, " - HCI Over SPI");
|
|
cap |= ESP_BT_SPI_SUPPORT;
|
|
#elif CONFIG_ESP_SDIO_HOST_INTERFACE
|
|
ESP_LOGI(TAG, " - HCI Over SDIO");
|
|
cap |= ESP_BT_SDIO_SUPPORT;
|
|
#endif
|
|
|
|
#elif BLUETOOTH_UART
|
|
ESP_LOGI(TAG, " - HCI Over UART");
|
|
cap |= ESP_BT_UART_SUPPORT;
|
|
#endif
|
|
|
|
#if BLUETOOTH_BLE
|
|
ESP_LOGI(TAG, " - BLE only");
|
|
cap |= ESP_BLE_ONLY_SUPPORT;
|
|
#elif BLUETOOTH_BT
|
|
ESP_LOGI(TAG, " - BR_EDR only");
|
|
cap |= ESP_BR_EDR_ONLY_SUPPORT;
|
|
#elif BLUETOOTH_BT_BLE
|
|
ESP_LOGI(TAG, " - BT/BLE dual mode");
|
|
cap |= ESP_BLE_ONLY_SUPPORT | ESP_BR_EDR_ONLY_SUPPORT;
|
|
#endif
|
|
#endif
|
|
return cap;
|
|
}
|
|
|
|
uint32_t get_bluetooth_ext_capabilities(void)
|
|
{
|
|
uint32_t ext_cap = 0;
|
|
#ifdef CONFIG_BT_ENABLED
|
|
ESP_LOGI(TAG, "- BT/BLE (extended)");
|
|
#if BLUETOOTH_HCI
|
|
#if CONFIG_ESP_SPI_HD_HOST_INTERFACE
|
|
ESP_LOGI(TAG, " - HCI Over SPI HD");
|
|
ext_cap |= ESP_BT_INTERFACE_SUPPORT;
|
|
#endif
|
|
#if CONFIG_ESP_UART_HOST_INTERFACE
|
|
ESP_LOGI(TAG, " - HCI Over UART (VHCI)");
|
|
ext_cap |= ESP_BT_INTERFACE_SUPPORT;
|
|
#endif
|
|
#endif
|
|
#endif
|
|
return ext_cap;
|
|
}
|