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

688 lines
19 KiB
C

/*
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
#include <unistd.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_hosted_log.h"
#include "driver/uart.h"
#include "endian.h"
#include "interface.h"
#include "mempool.h"
#include "memdump.h"
#include "stats.h"
#include "esp_idf_version.h"
#include "esp_hosted_interface.h"
#include "esp_hosted_transport.h"
#include "esp_hosted_transport_init.h"
#include "esp_hosted_header.h"
#include "esp_hosted_coprocessor_fw_ver.h"
#include "slave_util.h"
#include "slave_config.h"
#include "mempool.h"
#if H_USE_MEMPOOL
// memory should be 4 byte aligned for DMA access
#define MEM_ALIGNMENT_BYTES 4
#endif
#define HOSTED_UART CONFIG_ESP_UART_PORT
#define HOSTED_UART_GPIO_TX CONFIG_ESP_UART_PIN_TX
#define HOSTED_UART_GPIO_RX CONFIG_ESP_UART_PIN_RX
#define HOSTED_UART_BAUD_RATE CONFIG_ESP_UART_BAUDRATE
#define HOSTED_UART_NUM_DATA_BITS CONFIG_ESP_UART_NUM_DATA_BITS
#define HOSTED_UART_PARITY CONFIG_ESP_UART_PARITY
#define HOSTED_UART_STOP_BITS CONFIG_ESP_UART_STOP_BITS
#define HOSTED_UART_TX_QUEUE_SIZE CONFIG_ESP_UART_TX_Q_SIZE
#define HOSTED_UART_RX_QUEUE_SIZE CONFIG_ESP_UART_RX_Q_SIZE
#define HOSTED_UART_CHECKSUM CONFIG_ESP_UART_CHECKSUM
#define BUFFER_SIZE MAX_TRANSPORT_BUF_SIZE
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) && (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(6, 0, 0))
/**
* For ESP-IDF v5.5, Building ESP32 with UART Transport can fail due to
* lack of IRAM space.
* To reduce IRAM usage
* - `CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH=y`
* - `CONFIG_RINGBUF_PLACE_ISR_FUNCTIONS_INTO_FLASH=y`
* should be enabled
*/
#if CONFIG_IDF_TARGET_ESP32 && (!CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH || !CONFIG_RINGBUF_PLACE_ISR_FUNCTIONS_INTO_FLASH)
#error Building for UART transport can fail due to lack of IRAM space
#error To free up IRAM, enable Component config --> ESP Ringbuf ---> Place non-ISR ringbuf functions into flash and
#error Component config --> ESP Ringbuf ---> Place ISR ringbuf functions into flash
#error or uncomment
#error CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH=y and CONFIG_RINGBUF_PLACE_ISR_FUNCTIONS_INTO_FLASH=y in sdkconfig.defaults.esp32 and regenerate sdkconfig
#endif
#endif
static const char TAG[] = "UART_DRIVER";
// UART is low throughput, so throttling should not be needed
#define USE_DATA_THROTTLING (0)
// check if HOSTED_UART is the same as debug console uart
#if CONFIG_ESP_CONSOLE_UART
#if CONFIG_ESP_CONSOLE_UART_NUM == HOSTED_UART
#error "ESP Console UART and Hosted UART are the same. Select another UART port."
#endif
#endif
// for flow control
static volatile uint8_t wifi_flow_ctrl = 0;
static void flow_ctrl_task(void* pvParameters);
static SemaphoreHandle_t flow_ctrl_sem = NULL;
#define TRIGGER_FLOW_CTRL() if(flow_ctrl_sem) xSemaphoreGive(flow_ctrl_sem);
static interface_handle_t * h_uart_init(void);
static int32_t h_uart_write(interface_handle_t *handle, interface_buffer_handle_t *buf_handle);
static int h_uart_read(interface_handle_t *if_handle, interface_buffer_handle_t *buf_handle);
static esp_err_t h_uart_reset(interface_handle_t *handle);
static void h_uart_deinit(interface_handle_t *handle);
if_ops_t if_ops = {
.init = h_uart_init,
.write = h_uart_write,
.read = h_uart_read,
.reset = h_uart_reset,
.deinit = h_uart_deinit,
};
static interface_handle_t if_handle_g;
static interface_context_t context;
#if H_USE_MEMPOOL
static hosted_mempool_t * buf_mp_tx_g;
static hosted_mempool_t * buf_mp_rx_g;
#endif
static SemaphoreHandle_t uart_rx_sem;
static QueueHandle_t uart_rx_queue[MAX_PRIORITY_QUEUES];
static void uart_rx_task(void* pvParameters);
static inline void h_uart_mempool_create(void)
{
#if H_USE_MEMPOOL
hosted_mempool_config_t config = {
.pre_allocated_mem = NULL,
.pre_allocated_mem_size = 0,
.num_blocks = HOSTED_UART_TX_QUEUE_SIZE,
.block_size = BUFFER_SIZE,
.alignment_in_bytes = MEM_ALIGNMENT_BYTES,
.malloc = slave_util_malloc,
.calloc = slave_util_calloc,
.memset = memset,
.free = free,
};
buf_mp_tx_g = hosted_mempool_create(&config);
config.num_blocks = HOSTED_UART_RX_QUEUE_SIZE;
buf_mp_rx_g = hosted_mempool_create(&config);
assert(buf_mp_tx_g);
assert(buf_mp_rx_g);
#endif
}
static inline void h_uart_mempool_destroy(void)
{
#if H_USE_MEMPOOL
hosted_mempool_destroy(buf_mp_tx_g);
hosted_mempool_destroy(buf_mp_rx_g);
#endif
}
static inline void *h_uart_buffer_tx_alloc(size_t nbytes, uint need_memset)
{
MEMPOOL_ALLOC(buf_mp_tx_g, nbytes, need_memset);
}
static inline void h_uart_buffer_tx_free(void *buf)
{
MEMPOOL_FREE(buf_mp_tx_g, buf);
}
static inline void *h_uart_buffer_rx_alloc(uint need_memset)
{
MEMPOOL_ALLOC(buf_mp_rx_g, BUFFER_SIZE, need_memset);
}
static inline void h_uart_buffer_rx_free(void *buf)
{
MEMPOOL_FREE(buf_mp_rx_g, buf);
}
static void flow_ctrl_task(void* pvParameters)
{
flow_ctrl_sem = xSemaphoreCreateBinary();
assert(flow_ctrl_sem);
for(;;) {
interface_buffer_handle_t buf_handle = {0};
xSemaphoreTake(flow_ctrl_sem, portMAX_DELAY);
if (wifi_flow_ctrl)
buf_handle.wifi_flow_ctrl_en = H_FLOW_CTRL_ON;
else
buf_handle.wifi_flow_ctrl_en = H_FLOW_CTRL_OFF;
ESP_LOGV(TAG, "flow_ctrl %u", buf_handle.wifi_flow_ctrl_en);
send_to_host_queue(&buf_handle, PRIO_Q_SERIAL);
}
}
#if USE_DATA_THROTTLING
static void start_rx_data_throttling_if_needed(void)
{
uint32_t queue_load;
uint8_t load_percent;
if (slv_cfg_g.throttle_high_threshold > 0) {
/* Already throttling, nothing to be done */
if (slv_state_g.current_throttling)
return;
queue_load = uxQueueMessagesWaiting(uart_rx_queue[PRIO_Q_OTHERS]);
load_percent = (queue_load*100/HOSTED_UART_RX_QUEUE_SIZE);
if (load_percent > slv_cfg_g.throttle_high_threshold) {
slv_state_g.current_throttling = 1;
wifi_flow_ctrl = 1;
#if ESP_PKT_STATS
pkt_stats.sta_flowctrl_on++;
#endif
TRIGGER_FLOW_CTRL();
}
}
}
static void stop_rx_data_throttling_if_needed(void)
{
uint32_t queue_load;
uint8_t load_percent;
if (slv_state_g.current_throttling) {
queue_load = uxQueueMessagesWaiting(uart_rx_queue[PRIO_Q_OTHERS]);
load_percent = (queue_load*100/HOSTED_UART_RX_QUEUE_SIZE);
if (load_percent < slv_cfg_g.throttle_low_threshold) {
slv_state_g.current_throttling = 0;
wifi_flow_ctrl = 0;
#if ESP_PKT_STATS
pkt_stats.sta_flowctrl_off++;
#endif
TRIGGER_FLOW_CTRL();
}
}
}
#endif
static void uart_rx_read_done(void *handle)
{
uint8_t * buf = (uint8_t *)handle;
h_uart_buffer_rx_free(buf);
}
static uint8_t * uart_scratch_buf = NULL;
static void uart_rx_task(void* pvParameters)
{
struct esp_payload_header *header = NULL;
interface_buffer_handle_t buf_handle = {0};
uint8_t * buf = NULL;
uint16_t len = 0, offset = 0;
#if HOSTED_UART_CHECKSUM
uint16_t rx_checksum = 0, checksum = 0;
#endif
int bytes_read;
int total_len;
uint8_t flags = 0;
// delay for a while to let app main threads start and become ready
vTaskDelay(100 / portTICK_PERIOD_MS);
// now ready: open data path
if (context.event_handler) {
context.event_handler(ESP_OPEN_DATA_PATH);
}
if (!uart_scratch_buf) {
uart_scratch_buf = malloc(BUFFER_SIZE);
assert(uart_scratch_buf);
}
header = (struct esp_payload_header *)uart_scratch_buf;
// process all data in buffer until there isn't enough to form a packet header
while (1) {
// get the header
bytes_read = uart_read_bytes(HOSTED_UART, uart_scratch_buf,
sizeof(struct esp_payload_header), portMAX_DELAY);
ESP_LOGD(TAG, "Read %d bytes (header)", bytes_read);
if (bytes_read < sizeof(struct esp_payload_header)) {
ESP_LOGE(TAG, "Failed to read header");
continue;
}
len = le16toh(header->len);
offset = le16toh(header->offset);
if (offset != sizeof(struct esp_payload_header)) {
ESP_LOGE(TAG, "invalid offset in header");
continue;
}
total_len = len + sizeof(struct esp_payload_header);
if (total_len > BUFFER_SIZE) {
ESP_LOGE(TAG, "incoming data too big: %d", total_len);
continue;
}
// get the data, if any
if (len) {
bytes_read = uart_read_bytes(HOSTED_UART, &uart_scratch_buf[offset],
len, portMAX_DELAY);
ESP_LOGD(TAG, "Read %d bytes (payload)", bytes_read);
if (bytes_read < len) {
ESP_LOGE(TAG, "Failed to read payload");
continue;
}
}
// process flags
flags = header->flags;
if (flags & FLAG_POWER_SAVE_STARTED) {
ESP_LOGI(TAG, "Host informed starting to power sleep");
if (context.event_handler) {
context.event_handler(ESP_POWER_SAVE_ON);
}
} else if (flags & FLAG_POWER_SAVE_STOPPED) {
ESP_LOGI(TAG, "Host informed that it waken up");
if (context.event_handler) {
context.event_handler(ESP_POWER_SAVE_OFF);
}
}
#if HOSTED_UART_CHECKSUM
// calculate checksum over data in scratch buffer
rx_checksum = le16toh(header->checksum);
header->checksum = 0;
checksum = compute_checksum(uart_scratch_buf, total_len);
if (checksum != rx_checksum) {
ESP_LOGE(TAG, "%s: cal_chksum[%u] != exp_chksum[%u], drop len[%u] offset[%u]",
__func__, checksum, rx_checksum, len, offset);
continue;
}
#endif
// allocate a rx buffer
buf = h_uart_buffer_rx_alloc(MEMSET_REQUIRED);
assert(buf);
// copy data to the buffer
memcpy(buf, uart_scratch_buf, total_len);
/* Process received data */
buf_handle.payload = buf;
buf_handle.payload_len = total_len;
buf_handle.if_type = header->if_type;
buf_handle.if_num = header->if_num;
buf_handle.free_buf_handle = uart_rx_read_done;
buf_handle.priv_buffer_handle = buf;
#if USE_DATA_THROTTLING
start_rx_data_throttling_if_needed();
#endif
#if ESP_PKT_STATS
if (header->if_type == ESP_STA_IF)
pkt_stats.hs_bus_sta_in++;
#endif
if (header->if_type == ESP_SERIAL_IF) {
xQueueSend(uart_rx_queue[PRIO_Q_SERIAL], &buf_handle, portMAX_DELAY);
} else if (header->if_type == ESP_HCI_IF) {
xQueueSend(uart_rx_queue[PRIO_Q_BT], &buf_handle, portMAX_DELAY);
} else {
xQueueSend(uart_rx_queue[PRIO_Q_OTHERS], &buf_handle, portMAX_DELAY);
}
xSemaphoreGive(uart_rx_sem);
}
}
static int h_uart_read(interface_handle_t *if_handle, interface_buffer_handle_t *buf_handle)
{
if (!if_handle || (if_handle->state != ACTIVE) || !buf_handle) {
ESP_LOGE(TAG, "%s: Invalid state/args", __func__);
return ESP_FAIL;
}
xSemaphoreTake(uart_rx_sem, portMAX_DELAY);
if (pdFALSE == xQueueReceive(uart_rx_queue[PRIO_Q_SERIAL], buf_handle, 0))
if (pdFALSE == xQueueReceive(uart_rx_queue[PRIO_Q_BT], buf_handle, 0))
if (pdFALSE == xQueueReceive(uart_rx_queue[PRIO_Q_OTHERS], buf_handle, 0)) {
ESP_LOGE(TAG, "%s No element in rx queue", __func__);
return ESP_FAIL;
}
#if USE_DATA_THROTTLING
stop_rx_data_throttling_if_needed();
#endif
return buf_handle->payload_len;
}
static int32_t h_uart_write(interface_handle_t *handle, interface_buffer_handle_t *buf_handle)
{
uint32_t total_len = 0;
uint8_t* sendbuf = NULL;
uint16_t offset = sizeof(struct esp_payload_header);
struct esp_payload_header *header = NULL;
int tx_len;
if (!handle || !buf_handle) {
ESP_LOGE(TAG , "Invalid arguments");
return ESP_FAIL;
}
if (handle->state != ACTIVE) {
return ESP_FAIL;
}
if (!buf_handle->wifi_flow_ctrl_en) {
// skip this check for flow control packets (they don't have a payload)
if (!buf_handle->payload_len || !buf_handle->payload){
ESP_LOGE(TAG , "Invalid arguments, len:%"PRIu16, buf_handle->payload_len);
return ESP_FAIL;
}
}
total_len = buf_handle->payload_len + offset;
sendbuf = h_uart_buffer_tx_alloc(total_len, MEMSET_REQUIRED);
if (sendbuf == NULL) {
ESP_LOGE(TAG , "send buffer[%"PRIu32"] malloc fail", total_len);
MEM_DUMP("malloc failed");
return ESP_FAIL;
}
header = (struct esp_payload_header *) sendbuf;
/* Initialize header */
header->if_type = buf_handle->if_type;
header->if_num = buf_handle->if_num;
header->len = htole16(buf_handle->payload_len);
header->offset = htole16(offset);
header->seq_num = htole16(buf_handle->seq_num);
header->flags = buf_handle->flag;
header->throttle_cmd = buf_handle->wifi_flow_ctrl_en;
memcpy(sendbuf + offset, buf_handle->payload, buf_handle->payload_len);
#if HOSTED_UART_CHECKSUM
header->checksum = htole16(compute_checksum(sendbuf,
offset+buf_handle->payload_len));
#endif
ESP_LOGD(TAG, "sending %"PRIu32 " bytes", total_len);
ESP_HEXLOGD("uart_tx", sendbuf, total_len, 32);
tx_len = uart_write_bytes(HOSTED_UART, (const char*)sendbuf, total_len);
// wait until all data is transmitted
uart_wait_tx_done(HOSTED_UART, portMAX_DELAY);
if ((tx_len < 0) || (tx_len != total_len)) {
ESP_LOGE(TAG , "uart transmit error");
h_uart_buffer_tx_free(sendbuf);
return ESP_FAIL;
}
#if ESP_PKT_STATS
if (header->if_type == ESP_STA_IF)
pkt_stats.sta_sh_out++;
else if (header->if_type == ESP_SERIAL_IF)
pkt_stats.serial_tx_total++;
#endif
h_uart_buffer_tx_free(sendbuf);
return buf_handle->payload_len;
}
static interface_handle_t * h_uart_init(void)
{
if (if_handle_g.state >= DEACTIVE) {
return &if_handle_g;
}
uint16_t prio_q_idx = 0;
// initialise UART
const uart_config_t uart_config = {
.baud_rate = HOSTED_UART_BAUD_RATE,
.data_bits = HOSTED_UART_NUM_DATA_BITS,
.parity = HOSTED_UART_PARITY,
.stop_bits = HOSTED_UART_STOP_BITS,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_DEFAULT,
};
ESP_ERROR_CHECK(uart_driver_install(HOSTED_UART, BUFFER_SIZE, BUFFER_SIZE,
0, NULL, 0));
ESP_ERROR_CHECK(uart_param_config(HOSTED_UART, &uart_config));
ESP_ERROR_CHECK(uart_set_pin(HOSTED_UART, HOSTED_UART_GPIO_TX, HOSTED_UART_GPIO_RX,
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
ESP_LOGI(TAG, "UART GPIOs: Tx: %"PRIu16 ", Rx: %"PRIu16 ", Baud Rate %i",
HOSTED_UART_GPIO_TX, HOSTED_UART_GPIO_RX, HOSTED_UART_BAUD_RATE);
ESP_LOGI(TAG, "Hosted UART Queue Sizes: Tx: %"PRIu16 ", Rx: %"PRIu16,
HOSTED_UART_TX_QUEUE_SIZE, HOSTED_UART_RX_QUEUE_SIZE);
// prepare buffers
h_uart_mempool_create();
uart_rx_sem = xSemaphoreCreateCounting(HOSTED_UART_RX_QUEUE_SIZE * MAX_PRIORITY_QUEUES, 0);
assert(uart_rx_sem != NULL);
for (prio_q_idx = 0; prio_q_idx < MAX_PRIORITY_QUEUES; prio_q_idx++) {
uart_rx_queue[prio_q_idx] = xQueueCreate(HOSTED_UART_RX_QUEUE_SIZE, sizeof(interface_buffer_handle_t));
assert(uart_rx_queue[prio_q_idx] != NULL);
}
// start up tasks
assert(xTaskCreate(uart_rx_task, "uart_rx_task" ,
CONFIG_ESP_HOSTED_DEFAULT_TASK_STACK_SIZE, NULL,
CONFIG_ESP_HOSTED_DEFAULT_TASK_PRIORITY, NULL) == pdTRUE);
assert(xTaskCreate(flow_ctrl_task, "flow_ctrl_task" ,
CONFIG_ESP_HOSTED_DEFAULT_TASK_STACK_SIZE, NULL ,
CONFIG_ESP_HOSTED_DEFAULT_TASK_PRIORITY, NULL) == pdTRUE);
// data path opened
memset(&if_handle_g, 0, sizeof(if_handle_g));
if_handle_g.state = ACTIVE;
return &if_handle_g;
}
static void h_uart_deinit(interface_handle_t * handle)
{
#if H_HOST_PS_ALLOWED && H_PS_UNLOAD_BUS_WHILE_PS
esp_err_t ret;
if (if_handle_g.state == DEINIT) {
ESP_LOGW(TAG, "UART already deinitialized");
return;
}
if_handle_g.state = DEINIT;
h_uart_mempool_destroy();
// close data path
if (context.event_handler) {
context.event_handler(ESP_CLOSE_DATA_PATH);
}
ret = uart_flush_input(HOSTED_UART);
if (ret != ESP_OK)
ESP_LOGE(TAG, "%s: Failed to flush uart Rx", __func__);
ret = uart_wait_tx_done(HOSTED_UART, 100); // wait 100 RTOS ticks for Tx to be empty
if (ret != ESP_OK)
ESP_LOGE(TAG, "%s: Failed to flush uart Tx", __func__);
uart_driver_delete(HOSTED_UART);
#endif
}
static esp_err_t h_uart_reset(interface_handle_t *handle)
{
esp_err_t ret;
ret = uart_flush_input(HOSTED_UART);
if (ret != ESP_OK)
ESP_LOGE(TAG, "%s: Failed to flush uart Rx", __func__);
ret = uart_wait_tx_done(HOSTED_UART, 100); // wait 100 RTOS ticks for Tx to be empty
if (ret != ESP_OK)
ESP_LOGE(TAG, "%s: Failed to flush uart Tx", __func__);
return ret;
}
interface_context_t *interface_insert_driver(int (*event_handler)(uint8_t val))
{
memset(&context, 0, sizeof(context));
context.type = UART;
context.if_ops = &if_ops;
context.event_handler = event_handler;
return &context;
}
int interface_remove_driver()
{
memset(&context, 0, sizeof(context));
return 0;
}
void generate_startup_event(uint8_t cap, uint32_t ext_cap)
{
struct esp_payload_header *header = NULL;
interface_buffer_handle_t buf_handle = {0};
struct esp_priv_event *event = NULL;
uint8_t *pos = NULL;
uint16_t len = 0;
uint8_t raw_tp_cap = 0;
uint32_t total_len = 0;
int tx_len;
buf_handle.payload = h_uart_buffer_tx_alloc(512, MEMSET_REQUIRED);
assert(buf_handle.payload);
raw_tp_cap = debug_get_raw_tp_conf();
header = (struct esp_payload_header *) buf_handle.payload;
header->if_type = ESP_PRIV_IF;
header->if_num = 0;
header->offset = htole16(sizeof(struct esp_payload_header));
header->priv_pkt_type = ESP_PACKET_TYPE_EVENT;
/* Populate event data */
event = (struct esp_priv_event *) (buf_handle.payload + sizeof(struct esp_payload_header));
event->event_type = ESP_PRIV_EVENT_INIT;
/* Populate TLVs for event */
pos = event->event_data;
/* TLVs start */
/* TLV - Board type */
ESP_LOGI(TAG, "Slave chip Id[%x]", CONFIG_IDF_FIRMWARE_CHIP_ID);
*pos = ESP_PRIV_FIRMWARE_CHIP_ID; pos++;len++;
*pos = LENGTH_1_BYTE; pos++;len++;
*pos = CONFIG_IDF_FIRMWARE_CHIP_ID; pos++;len++;
/* TLV - Capability */
*pos = ESP_PRIV_CAPABILITY; pos++;len++;
*pos = LENGTH_1_BYTE; pos++;len++;
*pos = (cap & 0xFF); pos++;len++;
/* TLV - Extended Capability */
*pos = ESP_PRIV_CAP_EXT; pos++;len++;
*pos = LENGTH_4_BYTE; pos++;len++;
*pos = (ext_cap & 0xFF); pos++;len++;
*pos = (ext_cap >> 8) & 0xFF; pos++;len++;
*pos = (ext_cap >> 16) & 0xFF; pos++;len++;
*pos = (ext_cap >> 24) & 0xFF; pos++;len++;
*pos = ESP_PRIV_TEST_RAW_TP; pos++;len++;
*pos = LENGTH_1_BYTE; pos++;len++;
*pos = raw_tp_cap; pos++;len++;
*pos = ESP_PRIV_RX_Q_SIZE; pos++;len++;
*pos = LENGTH_1_BYTE; pos++;len++;
*pos = HOSTED_UART_RX_QUEUE_SIZE; pos++;len++;
*pos = ESP_PRIV_TX_Q_SIZE; pos++;len++;
*pos = LENGTH_1_BYTE; pos++;len++;
*pos = HOSTED_UART_TX_QUEUE_SIZE; pos++;len++;
// convert fw version into a uint32_t
uint32_t fw_version = ESP_HOSTED_VERSION_VAL(PROJECT_VERSION_MAJOR_1,
PROJECT_VERSION_MINOR_1,
PROJECT_VERSION_PATCH_1);
// send fw version as a little-endian uint32_t
*pos = ESP_PRIV_FIRMWARE_VERSION; pos++;len++;
*pos = LENGTH_4_BYTE; pos++;len++;
// send fw_version as a little endian 32bit value
*pos = (fw_version & 0xff); pos++;len++;
*pos = (fw_version >> 8) & 0xff; pos++;len++;
*pos = (fw_version >> 16) & 0xff; pos++;len++;
*pos = (fw_version >> 24) & 0xff; pos++;len++;
/* TLVs end */
event->event_len = len;
/* payload len = Event len + sizeof(event type) + sizeof(event len) */
len += 2;
header->len = htole16(len);
total_len = len + sizeof(struct esp_payload_header);
buf_handle.payload_len = total_len;
#if HOSTED_UART_CHECKSUM
header->checksum = htole16(compute_checksum(buf_handle.payload, len + sizeof(struct esp_payload_header)));
#endif
tx_len = uart_write_bytes(HOSTED_UART, (const char*)buf_handle.payload, buf_handle.payload_len);
if ((tx_len < 0) || (tx_len != buf_handle.payload_len)) {
ESP_LOGE(TAG , "startup: uart slave transmit error");
}
// wait until all data is transmitted
uart_wait_tx_done(HOSTED_UART, portMAX_DELAY);
}