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>
183 lines
5.2 KiB
C
183 lines
5.2 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include "esp_log.h"
|
|
#include "esp_vfs_eventfd.h"
|
|
#include "esp_openthread.h"
|
|
#include "esp_openthread_types.h"
|
|
#include "sdkconfig.h"
|
|
|
|
#include "esp_hosted_transport_init.h"
|
|
#include "slave_util.h"
|
|
#include "slave_openthread.h"
|
|
|
|
#if CONFIG_OPENTHREAD_ENABLED
|
|
|
|
#if !CONFIG_SOC_IEEE802154_SUPPORTED
|
|
#error "Thread RCP is only supported for the SoCs which have IEEE 802.15.4"
|
|
#endif
|
|
|
|
static const char TAG[] = "h_ot";
|
|
|
|
static bool eventfd_registered = false;
|
|
|
|
static slave_openthread_state_t slave_ot_state = SLAVE_OT_STATE_NOT_INITED;
|
|
static bool slave_ot_started = false;
|
|
|
|
#define ESP_OPENTHREAD_HOSTED_RADIO_CONFIG() \
|
|
{ \
|
|
.radio_mode = RADIO_MODE_NATIVE, \
|
|
}
|
|
|
|
#define ESP_OPENTHREAD_HOSTED_PORT_CONFIG() \
|
|
{ \
|
|
.storage_partition_name = "nvs", \
|
|
.netif_queue_size = CONFIG_ESP_HOSTED_OT_NETIF_QUEUE_SIZE, \
|
|
.task_queue_size = CONFIG_ESP_HOSTED_OT_TASK_QUEUE_SIZE, \
|
|
}
|
|
|
|
#if CONFIG_ESP_HOSTED_OT_TRANSPORT_UART
|
|
// init values for data_bits, parity, stop_bits not done here
|
|
#define ESP_OPENTHREAD_HOSTED_HOST_CONFIG() \
|
|
{ \
|
|
.host_connection_mode = HOST_CONNECTION_MODE_RCP_UART, \
|
|
.host_uart_config = { \
|
|
.port = CONFIG_ESP_HOSTED_OT_UART_PORT, \
|
|
.uart_config = \
|
|
{ \
|
|
.baud_rate = CONFIG_ESP_HOSTED_OT_UART_BAUDRATE, \
|
|
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE, \
|
|
.rx_flow_ctrl_thresh = 0, \
|
|
.source_clk = UART_SCLK_DEFAULT, \
|
|
}, \
|
|
.rx_pin = CONFIG_ESP_HOSTED_OT_UART_PIN_RX, \
|
|
.tx_pin = CONFIG_ESP_HOSTED_OT_UART_PIN_TX, \
|
|
}, \
|
|
}
|
|
#endif // ESP_HOSTED_OT_TRANSPORT_UART
|
|
|
|
esp_err_t slave_openthread_init(void)
|
|
{
|
|
if (!eventfd_registered) {
|
|
// register only once
|
|
|
|
// eventfds used for:
|
|
// * openthread task queue
|
|
// * radio driver
|
|
esp_vfs_eventfd_config_t eventfd_config = {
|
|
.max_fds = 2,
|
|
};
|
|
|
|
if (esp_vfs_eventfd_register(&eventfd_config) == ESP_OK) {
|
|
eventfd_registered = true;
|
|
} else {
|
|
ESP_LOGE(TAG, "esp_vfs_eventfd_register failed");
|
|
return ESP_FAIL;
|
|
}
|
|
}
|
|
slave_ot_state = SLAVE_OT_STATE_INITED;
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t slave_openthread_deinit(void)
|
|
{
|
|
// nothing to do
|
|
|
|
slave_ot_state = SLAVE_OT_STATE_NOT_INITED;
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t slave_openthread_start(void)
|
|
{
|
|
if (slave_ot_started) // already started
|
|
return ESP_OK;
|
|
|
|
esp_err_t ret;
|
|
|
|
static esp_openthread_config_t config = {
|
|
.netif_config = {0},
|
|
.platform_config = {
|
|
.radio_config = ESP_OPENTHREAD_HOSTED_RADIO_CONFIG(),
|
|
.host_config = ESP_OPENTHREAD_HOSTED_HOST_CONFIG(),
|
|
.port_config = ESP_OPENTHREAD_HOSTED_PORT_CONFIG(),
|
|
},
|
|
};
|
|
#if CONFIG_ESP_HOSTED_OT_TRANSPORT_UART
|
|
config.platform_config.host_config.host_uart_config.uart_config.data_bits = CONFIG_ESP_HOSTED_OT_UART_NUM_DATA_BITS;
|
|
config.platform_config.host_config.host_uart_config.uart_config.parity = CONFIG_ESP_HOSTED_OT_UART_PARITY;
|
|
config.platform_config.host_config.host_uart_config.uart_config.stop_bits = CONFIG_ESP_HOSTED_OT_UART_STOP_BITS;
|
|
#endif
|
|
#if CONFIG_ESP_HOSTED_OT_TRANSPORT_HOSTED
|
|
#error OpenThread over ESP-Hosted transport not yet supported
|
|
#endif
|
|
|
|
ret = esp_openthread_start(&config);
|
|
|
|
if (ret == ESP_OK) {
|
|
slave_ot_started = true;
|
|
slave_ot_state = SLAVE_OT_STATE_READY; // enabled and ready
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
esp_err_t slave_openthread_stop(void)
|
|
{
|
|
if (!slave_ot_started) // already stopped
|
|
return ESP_OK;
|
|
|
|
#if 0
|
|
// todo: enabled after IDF build error is fixed
|
|
esp_err_t res = esp_openthread_stop();
|
|
if (res != ESP_OK) {
|
|
ESP_LOGE(TAG, "esp_openthread_stop failed");
|
|
}
|
|
slave_ot_started = false;
|
|
slave_ot_state = SLAVE_OT_STATE_ENABLED; // enabled but not ready
|
|
|
|
return ret;
|
|
#else
|
|
ESP_LOGW(TAG, "esp_openthread_stop() not supported for now due to build break");
|
|
return ESP_ERR_NOT_SUPPORTED;
|
|
#endif
|
|
}
|
|
|
|
uint32_t get_ot_ext_capabilities(void)
|
|
{
|
|
uint32_t ext_cap = 0;
|
|
|
|
ESP_LOGI(TAG, "- OpenThread");
|
|
ext_cap |= ESP_OT_SUPPORT;
|
|
|
|
#if CONFIG_ESP_HOSTED_OT_TRANSPORT_UART
|
|
ESP_LOGI(TAG, " - OT over UART");
|
|
#endif
|
|
return ext_cap;
|
|
}
|
|
#else
|
|
uint32_t get_ot_ext_capabilities(void)
|
|
{
|
|
// no OT capabilities
|
|
return 0;
|
|
}
|
|
#endif // CONFIG_OPENTHREAD_ENABLED
|
|
|
|
slave_openthread_state_t slave_openthread_get_state(void)
|
|
{
|
|
return slave_ot_state;
|
|
}
|
|
|
|
// return ESP_OK if current slave feature state is bigger or equal to expected state
|
|
// else return ESP_FAIL
|
|
esp_err_t slave_openthread_state_check(slave_openthread_state_t current_state,
|
|
slave_openthread_state_t expected_state)
|
|
{
|
|
if (current_state >= expected_state)
|
|
return ESP_OK;
|
|
else
|
|
return ESP_FAIL;
|
|
}
|