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>
688 lines
19 KiB
C
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);
|
|
}
|