Files
desklock/firmware/components/esp_hosted/common/mempool/mempool.c
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

203 lines
5.0 KiB
C

/*
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_log.h"
#include "sdkconfig.h"
#include "mempool.h"
#include "mempool_ll.h"
static const char *TAG = "HS_MP";
#define MEMPOOL_NAME_STR_SIZE 32
#define IS_MEMPOOL_ALIGNED(VAL, BYTES) (!((VAL) & (BYTES - 1)))
#define MEMPOOL_ALIGNED(VAL, BYTES) ((VAL) + (BYTES) - \
((VAL) & (BYTES - 1)))
typedef struct hosted_mempool_t {
struct os_mempool *pool;
uint8_t *heap;
uint8_t static_heap;
size_t num_blocks;
size_t block_size;
int alignment_bytes;
void * (*malloc)(size_t size, hosted_mem_cap_t cap);
void * (*calloc)(size_t num_elem, size_t size_elem, hosted_mem_cap_t cap);
void * (*memset)(void *s, int c, size_t n);
void (*free)(void *ptr);
struct mempool_ops_t *ops;
} hosted_mempool_t;
#define MEMPOOL_FREE(freefn, x) do { \
if (x) { \
freefn(x); \
} \
} while (0);
/* For Statically allocated memory, pass as pre_allocated_mem.
* If NULL passed, will allocate from heap
*/
hosted_mempool_t * hosted_mempool_create(hosted_mempool_config_t * config)
{
if (!config ||
!config->malloc ||
!config->calloc ||
!config->memset ||
!config->free) {
ESP_LOGE(TAG, "NULL config, or required memory functions not provided");
return NULL;
}
struct hosted_mempool_t *new = NULL;
struct os_mempool *pool = NULL;
uint8_t *heap = NULL;
new = (hosted_mempool_t *)config->calloc(1, sizeof(hosted_mempool_t), HOSTED_MEM_CAP_NONE);
if (!new) {
ESP_LOGE(TAG, "hosted mempool init failed: no mem");
goto free_buffs;
}
new->ops = os_mempool_get_ops();
if (!new->ops) {
ESP_LOGE(TAG, "hosted mempool init failed: no mempool ops");
goto free_buffs;
}
pool = (struct os_mempool *)config->calloc(1, sizeof(struct os_mempool), HOSTED_MEM_CAP_NONE);
if (!pool) {
ESP_LOGE(TAG, "pool init failed: no mem");
goto free_buffs;
}
if (!config->pre_allocated_mem) {
/* no pre-allocated mem, allocate new */
heap = (uint8_t *)config->malloc(MEMPOOL_ALIGNED(
OS_MEMPOOL_BYTES(config->num_blocks, config->block_size),
config->alignment_in_bytes),
HOSTED_MEM_CAP_DMA);
if (!heap) {
ESP_LOGE(TAG, "mempool create failed: no mem\n");
goto free_buffs;
}
} else {
/* preallocated memory for mem pool */
if (config->pre_allocated_mem_size < OS_MEMPOOL_BYTES(config->num_blocks,
config->block_size)) {
ESP_LOGE(TAG, "mempool create failed: insufficient memory");
goto free_buffs;
}
if (!IS_MEMPOOL_ALIGNED((unsigned long)config->pre_allocated_mem,
config->alignment_in_bytes)) {
ESP_LOGE(TAG, "mempool create failed: mempool start addr unaligned");
goto free_buffs;
}
heap = config->pre_allocated_mem;
}
char str[MEMPOOL_NAME_STR_SIZE] = {0};
snprintf(str, MEMPOOL_NAME_STR_SIZE, "hosted_%p", pool);
if (new->ops->mempool_init(pool, config->num_blocks, config->block_size, heap, str)) {
ESP_LOGE(TAG, "mempool_init failed");
goto free_buffs;
}
new->heap = heap;
new->pool = pool;
new->num_blocks = config->num_blocks;
new->block_size = config->block_size;
if (config->pre_allocated_mem)
new->static_heap = 1;
// record the alignment
new->alignment_bytes = config->alignment_in_bytes;
// save the memory functions
new->malloc = config->malloc;
new->calloc = config->calloc;
new->memset = config->memset;
new->free = config->free;
return new;
free_buffs:
MEMPOOL_FREE(config->free, new);
MEMPOOL_FREE(config->free, pool);
if (!config->pre_allocated_mem)
MEMPOOL_FREE(config->free, heap);
return NULL;
}
void hosted_mempool_destroy(hosted_mempool_t *mempool)
{
if (!mempool)
return;
#if MEMPOOL_DEBUG
ESP_LOGI(MEM_TAG, "Destroy mempool %p num_blk[%lu] blk_size:[%lu]", mempool->pool, mempool->num_blocks, mempool->block_size);
#endif
mempool->ops->mempool_unregister(mempool->pool);
MEMPOOL_FREE(mempool->free, mempool->pool);
if (!mempool->static_heap)
MEMPOOL_FREE(mempool->free, mempool->heap);
MEMPOOL_FREE(mempool->free, mempool);
}
void * hosted_mempool_alloc(hosted_mempool_t *mempool,
size_t nbytes, uint8_t need_memset)
{
if (!mempool) {
ESP_LOGE(TAG, "mempool %p is NULL", mempool);
return NULL;
}
void *mem = NULL;
#if MYNEWT_VAL(OS_MEMPOOL_CHECK)
assert(mempool->heap);
assert(mempool->pool);
#endif
if(nbytes > mempool->block_size) {
ESP_LOGE(TAG, "Exp alloc bytes[%u] > mempool block size[%u]\n",
nbytes, mempool->block_size);
return NULL;
}
mem = mempool->ops->memblock_get(mempool->pool);
if (mem && need_memset)
mempool->memset(mem, 0, nbytes);
if (!mem) {
ESP_LOGE(TAG, "mempool %p alloc failed nbytes[%u]", mempool, nbytes);
}
return mem;
}
int hosted_mempool_free(hosted_mempool_t *mempool, void *mem)
{
if (!mem) {
return 0;
}
if (!mempool) {
ESP_LOGE(TAG, "%s: mempool %p is NULL", __func__, mempool);
return MEMPOOL_FAIL;
}
#if MYNEWT_VAL(OS_MEMPOOL_CHECK)
assert(mempool->heap);
assert(mempool->pool);
#endif
return mempool->ops->memblock_put(mempool->pool, mem);
}