Fork fix: the SDIO wedge is FIXED (esp-hosted-mcu #167)
Test, Build and Push / test-gateway (push) Successful in 12s
Test, Build and Push / release (push) Skipped
Test, Build and Push / build-gateway (push) Skipped

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>
This commit is contained in:
2026-07-15 09:50:27 +02:00
co-authored by Claude Fable 5
parent ac9d24e0f9
commit cb5826e02b
666 changed files with 216925 additions and 0 deletions
@@ -0,0 +1,3 @@
idf_component_register(SRCS "host_gpio_expander_example.c"
PRIV_REQUIRES nvs_flash
INCLUDE_DIRS ".")
@@ -0,0 +1,112 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_hosted.h"
static const char* TAG = "gpio_expander_example";
#define SLAVE_GPIO_PIN 2
static esp_err_t configure_and_test_gpio(esp_hosted_cp_gpio_config_t *config, const char *demo_name)
{
esp_err_t ret;
int level;
ESP_LOGW(TAG, "---- %s ----", demo_name);
ret = esp_hosted_cp_gpio_config(config);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "GPIO config failed: 0x%x", ret);
return ret;
}
// For output modes, toggle the pin
if (config->mode == H_CP_GPIO_MODE_OUTPUT ||
config->mode == H_CP_GPIO_MODE_OUTPUT_OD) {
ESP_LOGI(TAG, "Setting GPIO %d LOW", SLAVE_GPIO_PIN);
esp_hosted_cp_gpio_set_level(SLAVE_GPIO_PIN, 0);
vTaskDelay(pdMS_TO_TICKS(500));
ESP_LOGI(TAG, "Setting GPIO %d HIGH", SLAVE_GPIO_PIN);
esp_hosted_cp_gpio_set_level(SLAVE_GPIO_PIN, 1);
vTaskDelay(pdMS_TO_TICKS(500));
ESP_LOGI(TAG, "GPIO toggled successfully");
} else {
// For input modes, read the level
ret = esp_hosted_cp_gpio_get_level(SLAVE_GPIO_PIN, &level);
if (ret == ESP_OK) {
ESP_LOGI(TAG, "GPIO %d level: %d", SLAVE_GPIO_PIN, level);
}
}
return ESP_OK;
}
void app_main(void)
{
esp_err_t ret;
esp_hosted_cp_gpio_config_t gpio_config = {
.pin_bit_mask = (1ULL << SLAVE_GPIO_PIN),
};
// Initialize ESP-Hosted
ret = esp_hosted_init();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "ESP-Hosted init failed: 0x%x", ret);
return;
}
ret = esp_hosted_connect_to_slave();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Connect to slave failed: %s", esp_err_to_name(ret));
goto cleanup;
}
ESP_LOGI(TAG, "ESP-Hosted initialized successfully");
// Demo 1: Standard output
gpio_config.mode = H_CP_GPIO_MODE_OUTPUT;
gpio_config.pull_up_en = 0;
gpio_config.pull_down_en = 0;
if (configure_and_test_gpio(&gpio_config, "Demo 1: Standard Output") != ESP_OK) {
goto cleanup;
}
// Demo 2: Open-drain output with pull-up
gpio_config.mode = H_CP_GPIO_MODE_OUTPUT_OD;
gpio_config.pull_up_en = 1;
gpio_config.pull_down_en = 0;
if (configure_and_test_gpio(&gpio_config, "Demo 2: Open-Drain Output") != ESP_OK) {
goto cleanup;
}
// Demo 3: Input with pull-up
gpio_config.mode = H_CP_GPIO_MODE_INPUT;
gpio_config.pull_up_en = 1;
gpio_config.pull_down_en = 0;
if (configure_and_test_gpio(&gpio_config, "Demo 3: Input with Pull-Up") != ESP_OK) {
goto cleanup;
}
// Demo 4: Input with pull-down
gpio_config.mode = H_CP_GPIO_MODE_INPUT;
gpio_config.pull_up_en = 0;
gpio_config.pull_down_en = 1;
if (configure_and_test_gpio(&gpio_config, "Demo 4: Input with Pull-Down") != ESP_OK) {
goto cleanup;
}
ESP_LOGI(TAG, "All demos completed successfully!");
cleanup:
esp_hosted_deinit();
}
@@ -0,0 +1,10 @@
dependencies:
espressif/esp_hosted:
rules:
- if: target in [esp32p4, esp32h2]
version: '>=2.9.5'
espressif/esp_wifi_remote:
rules:
- if: target in [esp32p4, esp32h2]
- if: idf_version <6.1
version: '>=1.3.1'