Files
desklock/firmware/components/esp_hosted/slave/main/host_power_save.h
T
jpmschweitzerandClaude Fable 5 cb5826e02b
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
Fork fix: the SDIO wedge is FIXED (esp-hosted-mcu #167)
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>
2026-07-15 09:50:27 +02:00

111 lines
3.7 KiB
C

/*
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __HOST_POWER_SAVE_H__
#define __HOST_POWER_SAVE_H__
#include <stdint.h>
#include "sdkconfig.h"
#include "interface.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Callback structure for host power save events */
typedef struct {
void (*host_power_save_on_prepare_cb)(void); /* Prepare to enter power save */
void (*host_power_save_on_ready_cb)(void); /* Power save active, device ready */
void (*host_power_save_off_prepare_cb)(void); /* Prepare to exit power save */
void (*host_power_save_off_ready_cb)(void); /* Power save off, device ready */
} host_power_save_callbacks_t;
typedef enum {
HOSTED_POWER_SAVE_TYPE_NONE = 0,
HOSTED_POWER_SAVE_TYPE_LIGHT_SLEEP,
HOSTED_POWER_SAVE_TYPE_DEEP_SLEEP,
} esp_hosted_power_save_type_t;
/* Configuration structure for host power save initialization */
typedef struct {
uint8_t enable; /* Enable/disable host power save */
esp_hosted_power_save_type_t host_ps_type; /* Host power save type : Reserved for future use */
int host_wakeup_gpio; /* GPIO pin for host wakeup (-1 to use default from Kconfig) */
uint8_t host_wakeup_level; /* Active level for host wakeup (0 or 1) */
host_power_save_callbacks_t callbacks; /* Callbacks for power save events */
} host_power_save_config_t;
#if defined(CONFIG_ESP_HOSTED_HOST_POWER_SAVE_ENABLED)
#define H_HOST_PS_ALLOWED 1
#else
#define H_HOST_PS_ALLOWED 0
#endif
#define H_HOST_WAKE_UP_GPIO -1 /* default, overridden later */
#if H_HOST_PS_ALLOWED && defined(CONFIG_ESP_HOSTED_HOST_DEEP_SLEEP_ALLOWED)
#define H_HOST_PS_DEEP_SLEEP_ALLOWED 1
#else
#define H_HOST_PS_DEEP_SLEEP_ALLOWED 0
#endif
#if H_HOST_PS_DEEP_SLEEP_ALLOWED && CONFIG_ESP_HOSTED_HOST_WAKEUP_GPIO == -1
#error "CONFIG_HOST_WAKEUP_GPIO is not configured. Either disable host power save or configure the host wakeup GPIO pin"
#else
#undef H_HOST_WAKE_UP_GPIO
#define H_HOST_WAKE_UP_GPIO CONFIG_ESP_HOSTED_HOST_WAKEUP_GPIO
#endif
#if defined(CONFIG_ESP_HOSTED_HOST_WAKEUP_GPIO_LEVEL)
#define H_HOST_WAKEUP_GPIO_LEVEL CONFIG_ESP_HOSTED_HOST_WAKEUP_GPIO_LEVEL
#else
#define H_HOST_WAKEUP_GPIO_LEVEL 1 /* High */
#endif
#if defined(CONFIG_ESP_HOSTED_UNLOAD_BUS_DRIVER_DURING_HOST_SLEEP)
#define H_PS_UNLOAD_BUS_WHILE_PS 1
#else
#define H_PS_UNLOAD_BUS_WHILE_PS 0
#endif
/* Helper macro for default configuration */
#define HOST_POWER_SAVE_DEFAULT_CONFIG() { \
.enable = 1, \
.host_wakeup_gpio = H_HOST_WAKE_UP_GPIO, \
.host_wakeup_level = H_HOST_WAKEUP_GPIO_LEVEL, \
.callbacks = {0} \
}
#define HOST_POWER_SAVE_DEFAULT_CONFIG_DISABLED() { \
.enable = 0, \
.host_wakeup_gpio = H_HOST_WAKE_UP_GPIO, \
.host_wakeup_level = H_HOST_WAKEUP_GPIO_LEVEL, \
.callbacks = {0} \
}
int host_power_save_init(host_power_save_config_t *config);
int host_power_save_deinit(void);
int host_power_save_set_callbacks(host_power_save_callbacks_t *callbacks);
int is_host_wakeup_needed(interface_buffer_handle_t *buf_handle);
int wakeup_host_mandate(uint32_t timeout_ms);
int wakeup_host(uint32_t timeout_ms);
int host_power_save_alert(uint32_t ps_evt);
/**
* @brief Check if host is currently in power saving mode
* @return 1 if host is power saving, 0 otherwise
* @note Thread-safe and ISR-safe
*/
int is_host_power_saving(void);
#ifdef __cplusplus
}
#endif
#endif