Files
desklock/firmware/components/esp_hosted/slave/main/slave_control.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

235 lines
13 KiB
C

/*
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __SLAVE_CONTROL__H__
#define __SLAVE_CONTROL__H__
#include <esp_err.h>
#include "esp_idf_version.h"
#include "interface.h"
#include "sdkconfig.h"
#include "esp_hosted_rpc.h"
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
#define TIMEOUT_IN_SEC (1000 / portTICK_PERIOD_MS)
#else
#define TIMEOUT_IN_SEC (1000 / portTICK_RATE_MS)
#endif
#define SSID_LENGTH 32
#define PASSWORD_LENGTH 64
#define VENDOR_OUI_BUF 3
#define SUCCESS 0
#define FAILURE -1
#define mem_free(x) \
{ \
if (x) { \
free(x); \
x = NULL; \
} \
}
#define NTFY_TEMPLATE(NtFy_MsgId, NtFy_TyPe, NtFy_StRuCt, InIt_FuN) \
NtFy_TyPe *ntfy_payload = NULL; \
ntfy_payload = (NtFy_TyPe*)calloc(1,sizeof(NtFy_TyPe)); \
if (!ntfy_payload) { \
ESP_LOGE(TAG,"Failed to allocate memory"); \
return ESP_ERR_NO_MEM; \
} \
InIt_FuN(ntfy_payload); \
ntfy->payload_case = NtFy_MsgId; \
ntfy->NtFy_StRuCt = ntfy_payload; \
ntfy_payload->resp = SUCCESS;
#define RPC_TEMPLATE(RspTyPe, RspStRuCt, ReqType, ReqStruct, InIt_FuN) \
RspTyPe *resp_payload = NULL; \
ReqType *req_payload = NULL; \
if (!req || !resp || !req->ReqStruct) { \
ESP_LOGE(TAG, "Invalid parameters"); \
return ESP_FAIL; \
} \
req_payload = req->ReqStruct; \
resp_payload = (RspTyPe *)calloc(1, sizeof(RspTyPe)); \
if (!resp_payload) { \
ESP_LOGE(TAG, "Failed to alloc mem for resp.%s\n",#RspStRuCt); \
return ESP_ERR_NO_MEM; \
} \
resp->RspStRuCt = resp_payload; \
InIt_FuN(resp_payload); \
resp_payload->resp = SUCCESS; \
/* Simple is same above just, we dod not need req_payload unused warning */
#define RPC_TEMPLATE_SIMPLE(RspTyPe, RspStRuCt, ReqType, ReqStruct, InIt_FuN) \
RspTyPe *resp_payload = NULL; \
if (!req || !resp) { \
ESP_LOGE(TAG, "Invalid parameters"); \
return ESP_FAIL; \
} \
resp_payload = (RspTyPe *)calloc(1, sizeof(RspTyPe)); \
if (!resp_payload) { \
ESP_LOGE(TAG, "Failed to alloc mem for resp.%s\n",#RspStRuCt); \
return ESP_ERR_NO_MEM; \
} \
resp->RspStRuCt = resp_payload; \
InIt_FuN(resp_payload); \
resp_payload->resp = SUCCESS; \
#define RPC_RESP_ASSIGN_FIELD(PaRaM) \
resp_payload->PaRaM = PaRaM
#define RPC_RET_FAIL_IF(ConDiTiOn) do { \
int rEt = (ConDiTiOn); \
if (rEt) { \
resp_payload->resp = rEt; \
ESP_LOGE(TAG, "%s:%u failed [%s] = [%d]", __func__,__LINE__,#ConDiTiOn, rEt); \
return ESP_OK; \
} \
} while(0);
#define RPC_ALLOC_ELEMENT(TyPe,MsG_StRuCt,InIt_FuN) { \
TyPe *NeW_AllocN = (TyPe *)calloc(1, sizeof(TyPe)); \
if (!NeW_AllocN) { \
ESP_LOGI(TAG,"Failed to allocate memory for req.%s\n",#MsG_StRuCt); \
resp_payload->resp = RPC_ERR_MEMORY_FAILURE; \
goto err; \
} \
MsG_StRuCt = NeW_AllocN; \
InIt_FuN(MsG_StRuCt); \
}
#define NTFY_ALLOC_ELEMENT(TyPe,MsG_StRuCt,InIt_FuN) { \
TyPe *NeW_AllocN = (TyPe *)calloc(1, sizeof(TyPe)); \
if (!NeW_AllocN) { \
ESP_LOGI(TAG,"Failed to allocate memory for req.%s\n",#MsG_StRuCt); \
ntfy_payload->resp = RPC_ERR_MEMORY_FAILURE; \
goto err; \
} \
MsG_StRuCt = NeW_AllocN; \
InIt_FuN(MsG_StRuCt); \
}
#define NTFY_COPY_BYTES(dest, src, num) \
do { \
if (num) { \
dest.data = (uint8_t *)calloc(1, num); \
if (!dest.data) { \
ESP_LOGE(TAG, "%s:%u Failed to duplicate bytes\n",__func__,__LINE__); \
ntfy_payload->resp = FAILURE; \
return ESP_OK; \
} \
memcpy(dest.data, src, num); \
dest.len = num; \
} \
} while(0)
#define NTFY_COPY_STR(dest, src, max_len) \
if (src) { \
dest.data = (uint8_t*)strndup((char*)src, max_len); \
if (!dest.data) { \
ESP_LOGE(TAG, "%s:%u Failed to duplicate bytes\n",__func__,__LINE__); \
ntfy_payload->resp = FAILURE; \
return ESP_OK; \
} \
dest.len = min(max_len,strlen((char*)src)+1); \
}
#define RPC_REQ_COPY_BYTES(dest, src, num_bytes) \
if (src.len && src.data) \
memcpy((char*)dest, src.data, min(min(sizeof(dest), num_bytes), src.len));
#define RPC_REQ_COPY_STR RPC_REQ_COPY_BYTES
#define RPC_RESP_COPY_STR(dest, src, max_len) \
if (src) { \
dest.data = (uint8_t*)strndup((char*)src, max_len); \
if (!dest.data) { \
ESP_LOGE(TAG, "%s:%u Failed to duplicate bytes\n",__func__,__LINE__); \
resp_payload->resp = FAILURE; \
return ESP_OK; \
} \
dest.len = min(max_len,strlen((char*)src)+1); \
}
#define RPC_RESP_COPY_BYTES_SRC_UNCHECKED(dest, src, num) \
do { \
if (num) { \
dest.data = (uint8_t *)calloc(1, num); \
if (!dest.data) { \
ESP_LOGE(TAG, "%s:%u Failed to duplicate bytes\n",__func__,__LINE__); \
resp_payload->resp = FAILURE; \
return ESP_OK; \
} \
memcpy(dest.data, src, num); \
dest.len = num; \
} \
} while(0)
#define RPC_RESP_COPY_BYTES(dest, src, num) \
if (src) { \
RPC_RESP_COPY_BYTES_SRC_UNCHECKED(dest, src, num); \
}
#define RPC_COPY_STR(dest, src, max_len) \
if (src) { \
dest.data = (uint8_t*)strndup((char*)src, max_len); \
if (!dest.data) { \
ESP_LOGE(TAG, "%s:%u Failed to duplicate bytes\n",__func__,__LINE__); \
return FAILURE; \
} \
dest.len = min(max_len,strlen((char*)src)+1); \
}
#define RPC_COPY_BYTES(dest, src, num) \
do { \
if (num) { \
dest.data = (uint8_t *)calloc(1, num); \
if (!dest.data) { \
ESP_LOGE(TAG, "%s:%u Failed to duplicate bytes\n",__func__,__LINE__); \
return FAILURE; \
} \
memcpy(dest.data, src, num); \
dest.len = num; \
} \
} while(0)
esp_err_t data_transfer_handler(uint32_t session_id,const uint8_t *inbuf,
ssize_t inlen,uint8_t **outbuf, ssize_t *outlen, void *priv_data);
esp_err_t rpc_evt_handler(uint32_t session_id,const uint8_t *inbuf,
ssize_t inlen, uint8_t **outbuf, ssize_t *outlen, void *priv_data);
void send_event_to_host(int event_id);
void send_event_data_to_host(int event_id, void *data, int size);
#ifdef CONFIG_ESP_HOSTED_ENABLE_PEER_DATA_TRANSFER
#include "esp_hosted_peer_data.h"
#endif
#include "esp_wifi.h"
#ifdef CONFIG_ESP_HOSTED_NETWORK_SPLIT_ENABLED
/* DHCP/DNS status structure */
typedef struct {
int iface;
int net_link_up;
int dhcp_up;
uint8_t dhcp_ip[64];
uint8_t dhcp_nm[64];
uint8_t dhcp_gw[64];
int dns_up;
uint8_t dns_ip[64];
int dns_type;
} rpc_dhcp_dns_status_t;
/* Function declarations */
#endif /* CONFIG_ESP_HOSTED_NETWORK_SPLIT_ENABLED */
#endif /*__SLAVE_CONTROL__H__*/