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

343 lines
8.4 KiB
C

/*
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include <esp_err.h>
#include <esp_log.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/queue.h>
#include <protocomm.h>
#include <protocomm_priv.h>
#include "protocomm_pserial.h"
#include "esp_hosted_transport.h"
#include "esp_hosted_log.h"
static const char TAG[] = "protocomm_pserial";
#define EPNAME_MAX 16
#if defined(CONFIG_IDF_TARGET_ESP32C2)
#define REQ_Q_MAX 3
#else
#define REQ_Q_MAX 10
#endif
#define SIZE_OF_TYPE 1
#define SIZE_OF_LENGTH 2
#define PROTO_PSER_TLV_T_EPNAME 1
#define PROTO_PSER_TLV_T_DATA 2
struct pserial_config {
pserial_xmit xmit;
pserial_recv recv;
QUEUE_HANDLE req_queue;
};
typedef struct {
int len;
uint8_t *data;
int msg_id;
} serial_arg_t;
static esp_err_t parse_tlv(uint8_t **buf, size_t *total_len,
int *type, size_t *len, uint8_t **ptr)
{
uint8_t *b = *buf;
uint16_t *out_len = NULL;
if (*total_len == 0) {
return ESP_FAIL;
}
*type = b[0];
out_len = (uint16_t *)(b + 1);
*len = *out_len;
*ptr = (uint8_t *) (b + 3);
/*printf("*len %d \n", *len); */
*total_len -= (*len + 1 + 2);
*buf = b + 1 + 2 + (*len);
return ESP_OK;
}
static esp_err_t compose_tlv(char *epname, uint8_t **out, size_t *outlen)
{
uint16_t len = 0;
uint16_t ep_len = strlen(epname);
/*
* TLV (Type - Length - Value) structure is as follows:
* --------------------------------------------------------------------------------------------
* Endpoint Type | Endpoint Length | Endpoint Value | Data Type | Data Length | Data Value |
* --------------------------------------------------------------------------------------------
*
* Bytes used per field as follows:
* --------------------------------------------------------------------------------------------
* 1 | 2 | Endpoint length | 1 | 2 | Data length |
* --------------------------------------------------------------------------------------------
*/
uint32_t buf_len = SIZE_OF_TYPE + SIZE_OF_LENGTH +
ep_len + SIZE_OF_TYPE + SIZE_OF_LENGTH + *outlen;
uint8_t *buf = (uint8_t *)calloc(1, buf_len);
if (buf == NULL) {
ESP_LOGE(TAG,"%s Mem Alloc Failed [%d]bytes", __func__, (int)buf_len);
return ESP_FAIL;
}
buf[len] = PROTO_PSER_TLV_T_EPNAME;
len++;
buf[len] = (ep_len & 0xFF);
len++;
buf[len] = ((ep_len >> 8) & 0xFF);
len++;
memcpy(&buf[len], epname, strlen(epname));
len = len + strlen(epname) ;
buf[len] = PROTO_PSER_TLV_T_DATA;
len++;
buf[len] = (*outlen & 0xFF);
len++;
buf[len] = ((*outlen >> 8) & 0xFF);
len++;
buf_len = len + *outlen;
memcpy(&buf[len], (*out), *outlen);
free(*out);
*out = buf;
*outlen = buf_len;
return ESP_OK;
}
static esp_err_t rpc_req_handler(protocomm_t *pc,
uint8_t *in, size_t in_len)
{
uint8_t *buf = in;
size_t total_len = 0, len = 0;
int type = 0, ret = 0;
uint8_t *ptr = NULL;
char epname[EPNAME_MAX] = {0};
uint8_t *data = NULL;
size_t data_len = 0;
uint8_t *out = NULL;
size_t outlen = 0;
struct pserial_config *pserial_cfg = NULL;
total_len = in_len;
while (parse_tlv(&buf, &total_len, &type, &len, &ptr) == 0) {
/*ESP_LOGI(TAG, "Parsed type %d len %d", type, len); */
switch(type) {
case PROTO_PSER_TLV_T_EPNAME:
if (len >= EPNAME_MAX - 1) {
ESP_LOGE(TAG, "EP Name bigger than supported");
return ESP_FAIL;
}
memcpy(epname, ptr, len);
/*ESP_LOGI(TAG, "Found ep %s", epname); */
break;
case PROTO_PSER_TLV_T_DATA:
data = ptr;
data_len = len;
break;
default:
ESP_LOGE(TAG, "Invalid type found in the packet");
return ESP_FAIL;
}
}
if (data == NULL || data_len == 0 || strlen(epname) == 0) {
ESP_LOGE(TAG, "TLV components not complete for parsing");
return ESP_FAIL;
}
ret = protocomm_req_handle(pc, epname, 0, data,
data_len, &out, (ssize_t *) &outlen);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Error in handling protocomm request %d", ret);
return ESP_FAIL;
}
pserial_cfg = pc->priv;
ret = compose_tlv(RPC_EP_NAME_RSP, &out, &outlen);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to compose tlv");
return ESP_FAIL;
}
/*ESP_LOG_BUFFER_HEXDUMP("serial_tx", out, outlen<16?outlen:16, ESP_LOG_INFO); */
ret = (pserial_cfg->xmit)(out, (ssize_t) outlen);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to transmit data");
return ESP_FAIL;
}
return ESP_OK;
}
static esp_err_t rpc_evt_handler(protocomm_t *pc,
uint8_t *in, size_t in_len, int msg_id)
{
int ret = 0;
char epname[EPNAME_MAX] = RPC_EP_NAME_EVT;
uint8_t *out = NULL;
size_t outlen = 0;
struct pserial_config *pserial_cfg = NULL;
ret = protocomm_req_handle(pc, epname, msg_id,
in, in_len, &out, (ssize_t *) &outlen);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Error in handling protocomm request %d", ret);
return ESP_FAIL;
}
pserial_cfg = pc->priv;
ret = compose_tlv(RPC_EP_NAME_EVT, &out, &outlen);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to compose tlv");
return ESP_FAIL;
}
ret = (pserial_cfg->xmit)(out, (ssize_t) outlen);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to transmit data");
return ESP_FAIL;
}
return ESP_OK;
}
esp_err_t protocomm_pserial_data_ready(protocomm_t *pc,
uint8_t *in, int len, int msg_id)
{
struct pserial_config *pserial_cfg = NULL;
serial_arg_t arg = {0};
uint8_t *buf = NULL;
pserial_cfg = (struct pserial_config *) pc->priv;
if (!pserial_cfg) {
ESP_LOGE(TAG, "Unexpected. No pserial_cfg found");
return ESP_FAIL;
}
if (len) {
buf = (uint8_t *)malloc(len);
if (buf == NULL) {
ESP_LOGE(TAG,"%s Failed to allocate memory", __func__);
return ESP_FAIL;
}
memcpy(buf, in, len);
}
arg.msg_id = msg_id;
arg.len = len;
arg.data = buf;
if (xQueueSend(pserial_cfg->req_queue, &arg, portMAX_DELAY) != pdTRUE) {
ESP_LOGE(TAG, "Failed to indicate data ready");
if (buf)
free(buf);
return ESP_FAIL;
}
return ESP_OK;
}
static esp_err_t rpc_add_ep(const char *ep_name,
protocomm_req_handler_t req_handler, void *priv_data)
{
ESP_LOGD(TAG, "Adding endpoint for rpc");
return ESP_OK;
}
static esp_err_t rpc_remove_ep(const char *ep_name)
{
ESP_LOGD(TAG, "Removing endpoint for rpc");
return ESP_OK;
}
static void pserial_task(void *params)
{
protocomm_t *pc = (protocomm_t *) params;
struct pserial_config *pserial_cfg = NULL;
int len = 0, ret = 0;
serial_arg_t arg = {0};
pserial_cfg = (struct pserial_config *) pc->priv;
if (!pserial_cfg) {
ESP_LOGE(TAG, "Unexpected. No pserial_cfg found");
return;
}
while (xQueueReceive(pserial_cfg->req_queue, &arg, portMAX_DELAY) == pdTRUE) {
if ((arg.msg_id > RPC_ID__Event_Base) &&
(arg.msg_id < RPC_ID__Event_Max)) {
/* Events */
ESP_HEXLOGV("pserial_evt_rx", arg.data, arg.len, 32);
ret = rpc_evt_handler(pc, arg.data, arg.len, arg.msg_id);
if (ret)
ESP_LOGI(TAG, "protobuf rpc event handling failed %d\n", ret);
} else {
/* Request */
len = pserial_cfg->recv(arg.data, arg.len);
if (len) {
ESP_HEXLOGV("pserial_req_rx", arg.data, arg.len, 32);
ret = rpc_req_handler(pc, arg.data, len);
if (ret)
ESP_LOGI(TAG, "protocom rpc req handling failed %d\n", ret);
}
}
if (arg.data)
free(arg.data);
}
ESP_LOGI(TAG, "Unexpected termination of pserial task");
}
esp_err_t protocomm_pserial_start(protocomm_t *pc,
pserial_xmit xmit, pserial_recv recv)
{
struct pserial_config *pserial_cfg = NULL;
if (pc == NULL) {
return ESP_ERR_INVALID_ARG;
}
pc->add_endpoint = rpc_add_ep;
pc->remove_endpoint = rpc_remove_ep;
pserial_cfg = (struct pserial_config *) malloc(sizeof(struct pserial_config));
if (pserial_cfg == NULL) {
ESP_LOGE(TAG,"%s Failed to allocate memory", __func__);
return ESP_ERR_NO_MEM;
}
pserial_cfg->xmit = xmit;
pserial_cfg->recv = recv;
pserial_cfg->req_queue = xQueueCreate(REQ_Q_MAX, sizeof(serial_arg_t));
pc->priv = pserial_cfg;
#define ESP_HOSTED_PROTOBUF_TASK_STACK_SIZE (CONFIG_ESP_HOSTED_DEFAULT_TASK_STACK_SIZE+1024)
xTaskCreate(pserial_task, "pserial_task",
ESP_HOSTED_PROTOBUF_TASK_STACK_SIZE,
(void *) pc, CONFIG_ESP_HOSTED_DEFAULT_TASK_PRIORITY, NULL);
return ESP_OK;
}
esp_err_t protocomm_pserial_stop(protocomm_t *pc)
{
struct pserial_config *pserial_cfg = NULL;
if (pc->priv) {
pserial_cfg = (struct pserial_config *) pc->priv;
vQueueDelete(pserial_cfg->req_queue);
free(pserial_cfg);
pc->priv = NULL;
}
return ESP_OK;
}