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>
182 lines
4.7 KiB
C
182 lines
4.7 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include "slave_gpio_expander.h"
|
|
#include "slave_transport_gpio_pin_guard.h"
|
|
#include "slave_control.h"
|
|
#include "esp_log.h"
|
|
|
|
#if H_GPIO_EXPANDER_SUPPORT
|
|
|
|
static const char* TAG = "slave_gpio_expander";
|
|
|
|
esp_err_t req_gpio_config(Rpc *req, Rpc *resp, void *priv_data)
|
|
{
|
|
RPC_TEMPLATE(RpcRespGpioConfig, resp_gpio_config,
|
|
RpcReqGpioConfig, req_gpio_config,
|
|
rpc__resp__gpio_config__init);
|
|
|
|
gpio_config_t config = {0};
|
|
|
|
config.mode = req_payload->config->mode;
|
|
config.pull_up_en = req_payload->config->pull_up_en;
|
|
config.pull_down_en = req_payload->config->pull_down_en;
|
|
config.intr_type = req_payload->config->intr_type;
|
|
config.pin_bit_mask = req_payload->config->pin_bit_mask;
|
|
|
|
if (config.intr_type != GPIO_INTR_DISABLE) {
|
|
ESP_LOGE(TAG, "ISR is not supported from slave yet. please contact through github issues, if needed.");
|
|
resp_payload->resp = ESP_ERR_NOT_SUPPORTED;
|
|
return ESP_OK;
|
|
}
|
|
|
|
for (int pin = 0; pin < GPIO_NUM_MAX; ++pin) {
|
|
if (config.pin_bit_mask & (1ULL << pin)) {
|
|
if (!transport_gpio_pin_guard_is_eligible((gpio_num_t)pin)) {
|
|
ESP_LOGE(TAG, "GPIO pin %d is not allowed to be configured", pin);
|
|
resp_payload->resp = ESP_ERR_INVALID_ARG;
|
|
return ESP_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
RPC_RET_FAIL_IF(gpio_config(&config));
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t req_gpio_reset(Rpc *req, Rpc *resp, void *priv_data)
|
|
{
|
|
RPC_TEMPLATE(RpcRespGpioResetPin, resp_gpio_reset,
|
|
RpcReqGpioResetPin, req_gpio_reset_pin,
|
|
rpc__resp__gpio_reset_pin__init);
|
|
|
|
gpio_num_t gpio_num;
|
|
gpio_num = req_payload->gpio_num;
|
|
|
|
if (!transport_gpio_pin_guard_is_eligible(gpio_num)) {
|
|
ESP_LOGE(TAG, "GPIO pin %d is not allowed to be configured", gpio_num);
|
|
resp_payload->resp = ESP_ERR_INVALID_ARG;
|
|
return ESP_OK;
|
|
}
|
|
|
|
RPC_RET_FAIL_IF(gpio_reset_pin(gpio_num));
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t req_gpio_set_level(Rpc *req, Rpc *resp, void *priv_data)
|
|
{
|
|
RPC_TEMPLATE(RpcRespGpioSetLevel, resp_gpio_set_level,
|
|
RpcReqGpioSetLevel, req_gpio_set_level,
|
|
rpc__resp__gpio_set_level__init);
|
|
|
|
gpio_num_t gpio_num;
|
|
gpio_num = req_payload->gpio_num;
|
|
|
|
uint32_t level;
|
|
level = req_payload->level;
|
|
|
|
if (!transport_gpio_pin_guard_is_eligible(gpio_num)) {
|
|
ESP_LOGE(TAG, "GPIO pin %d is not allowed to be configured", gpio_num);
|
|
resp_payload->resp = ESP_ERR_INVALID_ARG;
|
|
return ESP_OK;
|
|
}
|
|
|
|
RPC_RET_FAIL_IF(gpio_set_level(gpio_num, level));
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t req_gpio_get_level(Rpc *req, Rpc *resp, void *priv_data)
|
|
{
|
|
RPC_TEMPLATE(RpcRespGpioGetLevel, resp_gpio_get_level,
|
|
RpcReqGpioGetLevel, req_gpio_get_level,
|
|
rpc__resp__gpio_get_level__init);
|
|
|
|
gpio_num_t gpio_num;
|
|
gpio_num = req_payload->gpio_num;
|
|
|
|
if (!transport_gpio_pin_guard_is_eligible(gpio_num)) {
|
|
ESP_LOGE(TAG, "GPIO pin %d is not allowed to be configured", gpio_num);
|
|
resp_payload->resp = ESP_ERR_INVALID_ARG;
|
|
return ESP_OK;
|
|
}
|
|
|
|
int level = gpio_get_level(gpio_num);
|
|
|
|
resp_payload->level = level;
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t req_gpio_set_direction(Rpc *req, Rpc *resp, void *priv_data)
|
|
{
|
|
RPC_TEMPLATE(RpcRespGpioSetDirection, resp_gpio_set_direction,
|
|
RpcReqGpioSetDirection, req_gpio_set_direction,
|
|
rpc__resp__gpio_set_direction__init);
|
|
|
|
gpio_num_t gpio_num;
|
|
gpio_num = req_payload->gpio_num;
|
|
|
|
gpio_mode_t mode;
|
|
mode = req_payload->mode;
|
|
|
|
if (!transport_gpio_pin_guard_is_eligible(gpio_num)) {
|
|
ESP_LOGE(TAG, "GPIO pin %d is not allowed to be configured", gpio_num);
|
|
resp_payload->resp = ESP_ERR_INVALID_ARG;
|
|
return ESP_OK;
|
|
}
|
|
|
|
RPC_RET_FAIL_IF(gpio_set_direction(gpio_num, mode));
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t req_gpio_input_enable(Rpc *req, Rpc *resp, void *priv_data)
|
|
{
|
|
RPC_TEMPLATE(RpcRespGpioInputEnable, resp_gpio_input_enable,
|
|
RpcReqGpioInputEnable, req_gpio_input_enable,
|
|
rpc__resp__gpio_input_enable__init);
|
|
|
|
gpio_num_t gpio_num;
|
|
gpio_num = req_payload->gpio_num;
|
|
|
|
if (!transport_gpio_pin_guard_is_eligible(gpio_num)) {
|
|
ESP_LOGE(TAG, "GPIO pin %d is not allowed to be configured", gpio_num);
|
|
resp_payload->resp = ESP_ERR_INVALID_ARG;
|
|
return ESP_OK;
|
|
}
|
|
|
|
RPC_RET_FAIL_IF(gpio_input_enable(gpio_num));
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t req_gpio_set_pull_mode(Rpc *req, Rpc *resp, void *priv_data)
|
|
{
|
|
RPC_TEMPLATE(RpcRespGpioSetPullMode, resp_gpio_set_pull_mode,
|
|
RpcReqGpioSetPullMode, req_gpio_set_pull_mode,
|
|
rpc__resp__gpio_set_pull_mode__init);
|
|
|
|
gpio_num_t gpio_num;
|
|
gpio_num = req_payload->gpio_num;
|
|
|
|
gpio_pull_mode_t pull_mode;
|
|
pull_mode = req_payload->pull;
|
|
|
|
if (!transport_gpio_pin_guard_is_eligible(gpio_num)) {
|
|
ESP_LOGE(TAG, "GPIO pin %d is not allowed to be configured", gpio_num);
|
|
resp_payload->resp = ESP_ERR_INVALID_ARG;
|
|
return ESP_OK;
|
|
}
|
|
|
|
RPC_RET_FAIL_IF(gpio_set_pull_mode(gpio_num, pull_mode));
|
|
|
|
return ESP_OK;
|
|
}
|
|
#endif
|