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>
1908 lines
54 KiB
C
1908 lines
54 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <string.h>
|
|
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/event_groups.h"
|
|
#include "esp_log.h"
|
|
#include "esp_app_desc.h"
|
|
|
|
#ifdef CONFIG_ESP_HOSTED_CP_WIFI
|
|
#include "slave_wifi_std.h"
|
|
#endif
|
|
#if H_WIFI_ENTERPRISE_SUPPORT
|
|
#include "slave_wifi_enterprise.h"
|
|
#endif
|
|
#ifdef CONFIG_ESP_HOSTED_NETWORK_SPLIT_ENABLED
|
|
#include "slave_network_split.h"
|
|
#endif
|
|
|
|
#include "esp_image_format.h"
|
|
#include "esp_partition.h"
|
|
|
|
#include "slave_control.h"
|
|
#include "esp_hosted_rpc.pb-c.h"
|
|
#include "esp_ota_ops.h"
|
|
#include "esp_mac.h"
|
|
#include "esp_hosted_rpc.h"
|
|
#include "esp_hosted_transport.h"
|
|
#include "esp_hosted_bitmasks.h"
|
|
|
|
#include "slave_config.h"
|
|
#include "esp_hosted_log.h"
|
|
#include "slave_bt.h"
|
|
#include "esp_hosted_coprocessor_fw_ver.h"
|
|
#include "slave_gpio_expander.h"
|
|
#include "slave_ext_coex.h"
|
|
|
|
#if CONFIG_ESP_HOSTED_OT_RCP_ENABLED
|
|
#include "slave_openthread.h"
|
|
#endif
|
|
|
|
#define IFACE_MAC_SIZE 8 // 6 for MAC-48, 8 for EIU-64, 2 for EFUSE_EXT
|
|
|
|
#define TIMEOUT_IN_MIN (60*TIMEOUT_IN_SEC)
|
|
#define TIMEOUT_IN_HOUR (60*TIMEOUT_IN_MIN)
|
|
#define RESTART_TIMEOUT (2*TIMEOUT_IN_SEC)
|
|
|
|
#define MIN_HEARTBEAT_INTERVAL (1)
|
|
#define MAX_HEARTBEAT_INTERVAL (24*60*60)
|
|
|
|
enum {
|
|
OTA_NOT_STARTED,
|
|
OTA_IN_PROGRESS,
|
|
OTA_FAILED,
|
|
OTA_COMPLETED,
|
|
OTA_ACTIVATED,
|
|
};
|
|
|
|
#ifdef CONFIG_ESP_HOSTED_MEM_MONITOR
|
|
// structures for mem monitor event
|
|
typedef struct {
|
|
uint32_t internal_mem_dma;
|
|
uint32_t internal_mem_8bit;
|
|
uint32_t external_mem_dma;
|
|
uint32_t external_mem_8bit;
|
|
} mem_monitor_params_t;
|
|
|
|
typedef struct {
|
|
uint32_t total_free_heap_size;
|
|
uint32_t min_free_heap_size;
|
|
mem_monitor_params_t free_size;
|
|
mem_monitor_params_t largest_free_block;
|
|
} mem_monitor_event_t;
|
|
|
|
// static variables for mem monitor
|
|
static TimerHandle_t mem_monitor_timer_handle = NULL;
|
|
static mem_monitor_params_t mem_monitor_params = { 0 };
|
|
static bool mem_monitor_report_always = false;
|
|
static uint32_t mem_monitor_interval_sec = 0;
|
|
#endif
|
|
|
|
uint8_t ota_status = OTA_NOT_STARTED;
|
|
|
|
typedef struct esp_rpc_cmd {
|
|
int req_num;
|
|
esp_err_t (*command_handler)(Rpc *req,
|
|
Rpc *resp, void *priv_data);
|
|
} esp_rpc_req_t;
|
|
|
|
static const char* TAG = "slave_rpc";
|
|
static TimerHandle_t handle_heartbeat_task;
|
|
static uint32_t hb_num;
|
|
|
|
static esp_ota_handle_t handle;
|
|
const esp_partition_t* update_partition = NULL;
|
|
static bool first_ota_write = false;
|
|
|
|
#if H_OTA_CHECK_IMAGE_VALIDITY
|
|
#define OTA_IMAGE_HEADER_SIZE (sizeof(esp_image_header_t) + \
|
|
sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t))
|
|
|
|
static const esp_app_desc_t *esp_hosted_get_app_desc_from_ota_img(const void *data_buf)
|
|
{
|
|
return (const esp_app_desc_t *)((const uint8_t *)data_buf +
|
|
sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t));
|
|
}
|
|
#endif
|
|
|
|
extern esp_err_t wlan_sta_rx_callback(void *buffer, uint16_t len, void *eb);
|
|
extern esp_err_t wlan_ap_rx_callback(void *buffer, uint16_t len, void *eb);
|
|
|
|
extern volatile uint8_t station_connected;
|
|
extern volatile uint8_t softap_started;
|
|
|
|
#ifdef CONFIG_ESP_HOSTED_ENABLE_PEER_DATA_TRANSFER
|
|
/* Array of callback slots (empty slot has callback = NULL, msg_id = -1 is invalid sentinel) */
|
|
static struct {
|
|
uint32_t msg_id;
|
|
void (*callback)(uint32_t msg_id_recvd, const uint8_t *data_recvd, size_t data_len_recvd, void *local_context);
|
|
void *local_context;
|
|
} custom_msg_callbacks[CONFIG_ESP_HOSTED_MAX_CUSTOM_MSG_HANDLERS] = {
|
|
[0 ... (CONFIG_ESP_HOSTED_MAX_CUSTOM_MSG_HANDLERS - 1)] = {
|
|
.msg_id = (uint32_t)-1,
|
|
.callback = NULL
|
|
}
|
|
};
|
|
|
|
static SemaphoreHandle_t custom_callbacks_mutex = NULL;
|
|
|
|
#endif
|
|
|
|
/* OTA end timer callback */
|
|
void vTimerCallback( TimerHandle_t xTimer )
|
|
{
|
|
xTimerDelete(xTimer, 0);
|
|
#ifdef CONFIG_ESP_HOSTED_CP_WIFI
|
|
esp_unregister_shutdown_handler((shutdown_handler_t)esp_wifi_stop);
|
|
#endif // CONFIG_ESP_HOSTED_CP_WIFI
|
|
esp_restart();
|
|
}
|
|
|
|
/* Function OTA begin */
|
|
static esp_err_t req_ota_begin_handler (Rpc *req,
|
|
Rpc *resp, void *priv_data)
|
|
{
|
|
esp_err_t ret = ESP_OK;
|
|
RpcRespOTABegin *resp_payload = NULL;
|
|
|
|
if (!req || !resp) {
|
|
ESP_LOGE(TAG, "Invalid parameters");
|
|
return ESP_FAIL;
|
|
}
|
|
|
|
ESP_LOGI(TAG, "OTA update started");
|
|
|
|
resp_payload = (RpcRespOTABegin *)
|
|
calloc(1,sizeof(RpcRespOTABegin));
|
|
if (!resp_payload) {
|
|
ESP_LOGE(TAG,"Failed to allocate memory");
|
|
return ESP_ERR_NO_MEM;
|
|
}
|
|
rpc__resp__otabegin__init(resp_payload);
|
|
resp->payload_case = RPC__PAYLOAD_RESP_OTA_BEGIN;
|
|
resp->resp_ota_begin = resp_payload;
|
|
|
|
/* Identify next OTA partition */
|
|
update_partition = esp_ota_get_next_update_partition(NULL);
|
|
if (update_partition == NULL) {
|
|
ESP_LOGE(TAG, "Failed to get next update partition");
|
|
ret = -1;
|
|
goto err;
|
|
}
|
|
|
|
ESP_LOGI(TAG, "Prepare partition for OTA\n");
|
|
ret = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &handle);
|
|
if (ret) {
|
|
ESP_LOGE(TAG, "OTA begin failed[%d]", ret);
|
|
goto err;
|
|
}
|
|
ota_status = OTA_IN_PROGRESS;
|
|
|
|
first_ota_write = true;
|
|
|
|
resp_payload->resp = SUCCESS;
|
|
return ESP_OK;
|
|
err:
|
|
resp_payload->resp = ret;
|
|
return ESP_OK;
|
|
|
|
}
|
|
|
|
/* Function OTA write */
|
|
static esp_err_t req_ota_write_handler (Rpc *req,
|
|
Rpc *resp, void *priv_data)
|
|
{
|
|
esp_err_t ret = ESP_OK;
|
|
RpcRespOTAWrite *resp_payload = NULL;
|
|
|
|
if (!req || !resp) {
|
|
ESP_LOGE(TAG, "Invalid parameters");
|
|
return ESP_FAIL;
|
|
}
|
|
|
|
resp_payload = (RpcRespOTAWrite *)calloc(1,sizeof(RpcRespOTAWrite));
|
|
if (!resp_payload) {
|
|
ESP_LOGE(TAG,"Failed to allocate memory");
|
|
return ESP_ERR_NO_MEM;
|
|
}
|
|
|
|
rpc__resp__otawrite__init(resp_payload);
|
|
resp->payload_case = RPC__PAYLOAD_RESP_OTA_WRITE;
|
|
resp->resp_ota_write = resp_payload;
|
|
|
|
// Check image validity before writing if it's the first chunk
|
|
if (first_ota_write) {
|
|
ESP_LOGI(TAG, "Flashing image\n");
|
|
first_ota_write = false;
|
|
|
|
#if H_OTA_CHECK_IMAGE_VALIDITY
|
|
// sanity check: first write should contain enough data to query app header
|
|
if (req->req_ota_write->ota_data.len < OTA_IMAGE_HEADER_SIZE) {
|
|
ESP_LOGE(TAG, "First OTA write is too small to contain app header");
|
|
resp_payload->resp = ESP_ERR_INVALID_SIZE;
|
|
return ESP_OK;
|
|
}
|
|
|
|
// do additional OTA image checking
|
|
// - SPI FLASH mode of incoming OTA is compatible with current image
|
|
const esp_image_header_t *img_hdr = (const esp_image_header_t *)req->req_ota_write->ota_data.data;
|
|
const esp_app_desc_t *app_desc = esp_hosted_get_app_desc_from_ota_img(req->req_ota_write->ota_data.data);
|
|
esp_err_t validity_ret = esp_ota_check_image_validity(update_partition->type, img_hdr, app_desc);
|
|
if (validity_ret != ESP_OK) {
|
|
ESP_LOGE(TAG, "OTA image validity check failed with error: %s", esp_err_to_name(validity_ret));
|
|
resp_payload->resp = validity_ret;
|
|
return ESP_OK;
|
|
}
|
|
#else
|
|
ESP_LOGW(TAG, "esp_ota_check_image_validity() not available in this IDF version, skipping validation");
|
|
resp_payload->resp = ESP_OK;
|
|
#endif
|
|
}
|
|
|
|
ret = esp_ota_write( handle, (const void *)req->req_ota_write->ota_data.data,
|
|
req->req_ota_write->ota_data.len);
|
|
if (ret != ESP_OK) {
|
|
ESP_LOGE(TAG, "OTA write failed with return code 0x%x",ret);
|
|
resp_payload->resp = ret;
|
|
return ESP_OK;
|
|
}
|
|
resp_payload->resp = SUCCESS;
|
|
return ESP_OK;
|
|
}
|
|
|
|
/* Function OTA end */
|
|
static esp_err_t req_ota_end_handler (Rpc *req,
|
|
Rpc *resp, void *priv_data)
|
|
{
|
|
esp_err_t ret = ESP_OK;
|
|
RpcRespOTAEnd *resp_payload = NULL;
|
|
|
|
if (!req || !resp) {
|
|
ESP_LOGE(TAG, "Invalid parameters");
|
|
return ESP_FAIL;
|
|
}
|
|
|
|
resp_payload = (RpcRespOTAEnd *)calloc(1,sizeof(RpcRespOTAEnd));
|
|
if (!resp_payload) {
|
|
ESP_LOGE(TAG,"Failed to allocate memory");
|
|
return ESP_ERR_NO_MEM;
|
|
}
|
|
rpc__resp__otaend__init(resp_payload);
|
|
resp->payload_case = RPC__PAYLOAD_RESP_OTA_END;
|
|
resp->resp_ota_end = resp_payload;
|
|
|
|
ret = esp_ota_end(handle);
|
|
if (ret != ESP_OK) {
|
|
if (ret == ESP_ERR_OTA_VALIDATE_FAILED) {
|
|
ESP_LOGE(TAG, "Image validation failed, image is corrupted");
|
|
} else {
|
|
ESP_LOGE(TAG, "OTA update failed in end (%s)!", esp_err_to_name(ret));
|
|
}
|
|
ota_status = OTA_FAILED;
|
|
goto err;
|
|
}
|
|
|
|
ESP_LOGI(TAG, "**** OTA updated successful, ready for activation ****");
|
|
ota_status = OTA_COMPLETED;
|
|
resp_payload->resp = SUCCESS;
|
|
return ESP_OK;
|
|
err:
|
|
resp_payload->resp = ret;
|
|
return ESP_OK;
|
|
}
|
|
|
|
/* Function OTA activate */
|
|
static esp_err_t req_ota_activate_handler (Rpc *req,
|
|
Rpc *resp, void *priv_data)
|
|
{
|
|
esp_err_t ret = ESP_OK;
|
|
RpcRespOTAActivate *resp_payload = NULL;
|
|
TimerHandle_t xTimer = NULL;
|
|
|
|
if (!req || !resp) {
|
|
ESP_LOGE(TAG, "Invalid parameters");
|
|
return ESP_FAIL;
|
|
}
|
|
|
|
resp_payload = (RpcRespOTAActivate *)calloc(1,sizeof(RpcRespOTAActivate));
|
|
if (!resp_payload) {
|
|
ESP_LOGE(TAG,"Failed to allocate memory");
|
|
return ESP_ERR_NO_MEM;
|
|
}
|
|
rpc__resp__otaactivate__init(resp_payload);
|
|
resp->payload_case = RPC__PAYLOAD_RESP_OTA_ACTIVATE;
|
|
resp->resp_ota_activate = resp_payload;
|
|
|
|
ret = ESP_OK;
|
|
switch (ota_status) {
|
|
case OTA_COMPLETED:
|
|
break;
|
|
case OTA_IN_PROGRESS:
|
|
ESP_LOGW(TAG, "OTA in progress");
|
|
goto err;
|
|
break;
|
|
case OTA_NOT_STARTED:
|
|
ESP_LOGW(TAG, "OTA not started");
|
|
goto err;
|
|
break;
|
|
case OTA_FAILED:
|
|
ESP_LOGW(TAG, "OTA failed");
|
|
goto err;
|
|
break;
|
|
default:
|
|
ESP_LOGW(TAG, "OTA status unknown");
|
|
goto err;
|
|
break;
|
|
}
|
|
|
|
/* set OTA partition for next boot */
|
|
ret = esp_ota_set_boot_partition(update_partition);
|
|
if (ret != ESP_OK) {
|
|
ESP_LOGE(TAG, "esp_ota_set_boot_partition failed (%s)!", esp_err_to_name(ret));
|
|
goto err;
|
|
}
|
|
ota_status = OTA_ACTIVATED;
|
|
/* Create timer to reboot system and activate OTA */
|
|
xTimer = xTimerCreate("OTAActivateTimer", RESTART_TIMEOUT , pdFALSE, 0, vTimerCallback);
|
|
if (xTimer == NULL) {
|
|
ESP_LOGE(TAG, "Failed to create timer to restart system");
|
|
ret = -1;
|
|
goto err;
|
|
}
|
|
ret = xTimerStart(xTimer, 0);
|
|
if (ret != pdPASS) {
|
|
ESP_LOGE(TAG, "Failed to start timer to restart system");
|
|
ret = -2;
|
|
goto err;
|
|
}
|
|
ESP_LOGE(TAG, "**** OTA activation initiated, ESP32 will reboot in 2 sec ****");
|
|
resp_payload->resp = SUCCESS;
|
|
return ESP_OK;
|
|
err:
|
|
resp_payload->resp = ret;
|
|
return ESP_OK;
|
|
}
|
|
|
|
static void heartbeat_timer_cb(TimerHandle_t xTimer)
|
|
{
|
|
send_event_to_host(RPC_ID__Event_Heartbeat);
|
|
hb_num++;
|
|
}
|
|
|
|
static void stop_heartbeat(void)
|
|
{
|
|
if (handle_heartbeat_task &&
|
|
xTimerIsTimerActive(handle_heartbeat_task)) {
|
|
ESP_LOGI(TAG, "Stopping HB timer");
|
|
xTimerStop(handle_heartbeat_task, portMAX_DELAY);
|
|
xTimerDelete(handle_heartbeat_task, portMAX_DELAY);
|
|
handle_heartbeat_task = NULL;
|
|
}
|
|
hb_num = 0;
|
|
}
|
|
|
|
static esp_err_t start_heartbeat(int duration)
|
|
{
|
|
esp_err_t ret = ESP_OK;
|
|
|
|
handle_heartbeat_task = xTimerCreate("HB_Timer",
|
|
duration*TIMEOUT_IN_SEC, pdTRUE, 0, heartbeat_timer_cb);
|
|
if (handle_heartbeat_task == NULL) {
|
|
ESP_LOGE(TAG, "Failed to Heartbeat");
|
|
return ESP_FAIL;
|
|
}
|
|
|
|
ret = xTimerStart(handle_heartbeat_task, 0);
|
|
if (ret != pdPASS) {
|
|
ESP_LOGE(TAG, "Failed to start Heartbeat");
|
|
return ESP_FAIL;
|
|
}
|
|
ESP_LOGI(TAG, "HB timer started for %u sec\n", duration);
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
static esp_err_t configure_heartbeat(bool enable, int hb_duration)
|
|
{
|
|
esp_err_t ret = ESP_OK;
|
|
int duration = hb_duration ;
|
|
|
|
if (!enable) {
|
|
ESP_LOGI(TAG, "Stop Heartbeat");
|
|
stop_heartbeat();
|
|
|
|
} else {
|
|
if ((duration < MIN_HEARTBEAT_INTERVAL) ||
|
|
(duration > MAX_HEARTBEAT_INTERVAL)) {
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
stop_heartbeat();
|
|
|
|
ret = start_heartbeat(duration);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Function to config heartbeat */
|
|
static esp_err_t req_config_heartbeat(Rpc *req,
|
|
Rpc *resp, void *priv_data)
|
|
{
|
|
RPC_TEMPLATE(RpcRespConfigHeartbeat,
|
|
resp_config_heartbeat,
|
|
RpcReqConfigHeartbeat,
|
|
req_config_heartbeat,
|
|
rpc__resp__config_heartbeat__init);
|
|
|
|
RPC_RET_FAIL_IF(configure_heartbeat(req_payload->enable, req_payload->duration));
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
static esp_err_t req_get_coprocessor_fw_version(Rpc *req, Rpc *resp, void *priv_data)
|
|
{
|
|
RPC_TEMPLATE_SIMPLE(RpcRespGetCoprocessorFwVersion, resp_get_coprocessor_fwversion,
|
|
RpcReqGetCoprocessorFwVersion, req_get_coprocessor_fwversion,
|
|
rpc__resp__get_coprocessor_fw_version__init);
|
|
|
|
resp_payload->major1 = PROJECT_VERSION_MAJOR_1;
|
|
resp_payload->minor1 = PROJECT_VERSION_MINOR_1;
|
|
resp_payload->patch1 = PROJECT_VERSION_PATCH_1;
|
|
resp_payload->revision = -1;
|
|
resp_payload->prerelease = -1;
|
|
resp_payload->build = -1;
|
|
resp_payload->chip_id = CONFIG_IDF_FIRMWARE_CHIP_ID;
|
|
RPC_RESP_COPY_STR(resp_payload->idf_target, CONFIG_IDF_TARGET, strlen(CONFIG_IDF_TARGET));
|
|
resp_payload->resp = ESP_OK;
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
static esp_err_t req_iface_mac_addr_len_get(Rpc *req, Rpc *resp, void *priv_data)
|
|
{
|
|
RPC_TEMPLATE(RpcRespIfaceMacAddrLenGet, resp_iface_mac_addr_len_get,
|
|
RpcReqIfaceMacAddrLenGet, req_iface_mac_addr_len_get,
|
|
rpc__resp__iface_mac_addr_len_get__init);
|
|
|
|
size_t len = esp_mac_addr_len_get(req_payload->type);
|
|
|
|
resp_payload->type = req_payload->type;
|
|
resp_payload->len = len;
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
static esp_err_t req_iface_mac_addr_set_get(Rpc *req, Rpc *resp, void *priv_data)
|
|
{
|
|
RPC_TEMPLATE(RpcRespIfaceMacAddrSetGet, resp_iface_mac_addr_set_get,
|
|
RpcReqIfaceMacAddrSetGet, req_iface_mac_addr_set_get,
|
|
rpc__resp__iface_mac_addr_set_get__init);
|
|
|
|
// copy the incoming request to the outgoing response
|
|
resp_payload->set = req_payload->set;
|
|
resp_payload->type = req_payload->type;
|
|
|
|
// get the expected len based on the type
|
|
size_t len = esp_mac_addr_len_get(req_payload->type);
|
|
|
|
if (req_payload->set) {
|
|
// set the interface mac address
|
|
if (req_payload->mac.len) {
|
|
if (req_payload->mac.len == len) {
|
|
RPC_RET_FAIL_IF(esp_iface_mac_addr_set(req_payload->mac.data, req_payload->type));
|
|
// copy the mac address that was set in the response
|
|
RPC_RESP_COPY_BYTES_SRC_UNCHECKED(resp_payload->mac, req_payload->mac.data, len);
|
|
} else {
|
|
ESP_LOGE(TAG, "expected mac length %" PRIu32 ", but got %" PRIu32, (uint32_t)len, (uint32_t)req_payload->mac.len);
|
|
resp_payload->resp = ESP_ERR_INVALID_ARG;
|
|
}
|
|
} else {
|
|
// no mac data provided
|
|
ESP_LOGE(TAG, "error: set iface mac address without mac data");
|
|
resp_payload->resp = ESP_ERR_INVALID_ARG;
|
|
}
|
|
} else {
|
|
// get the interface mac address
|
|
uint8_t iface_mac[IFACE_MAC_SIZE] = {0};
|
|
RPC_RET_FAIL_IF(esp_read_mac(iface_mac, req_payload->type));
|
|
|
|
RPC_RESP_COPY_BYTES_SRC_UNCHECKED(resp_payload->mac, iface_mac, len);
|
|
}
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
static esp_err_t req_feature_control(Rpc *req, Rpc *resp, void *priv_data)
|
|
{
|
|
RPC_TEMPLATE(RpcRespFeatureControl, resp_feature_control,
|
|
RpcReqFeatureControl, req_feature_control,
|
|
rpc__resp__feature_control__init);
|
|
|
|
/* Echo request into response */
|
|
resp_payload->feature = req_payload->feature;
|
|
resp_payload->command = req_payload->command;
|
|
resp_payload->option = req_payload->option;
|
|
|
|
switch (req_payload->feature) {
|
|
#ifdef CONFIG_ESP_HOSTED_CP_BT
|
|
case RPC_FEATURE__Feature_Bluetooth:
|
|
switch (req_payload->command) {
|
|
|
|
case RPC_FEATURE_COMMAND__Feature_Command_BT_Init:
|
|
RPC_RET_FAIL_IF(init_bluetooth());
|
|
break;
|
|
|
|
case RPC_FEATURE_COMMAND__Feature_Command_BT_Deinit: {
|
|
bool mem_release =
|
|
(req_payload->option ==
|
|
RPC_FEATURE_OPTION__Feature_Option_BT_Deinit_Release_Memory);
|
|
RPC_RET_FAIL_IF(deinit_bluetooth(mem_release));
|
|
break;
|
|
}
|
|
|
|
case RPC_FEATURE_COMMAND__Feature_Command_BT_Enable:
|
|
RPC_RET_FAIL_IF(enable_bluetooth());
|
|
break;
|
|
|
|
case RPC_FEATURE_COMMAND__Feature_Command_BT_Disable:
|
|
RPC_RET_FAIL_IF(disable_bluetooth());
|
|
break;
|
|
|
|
default:
|
|
ESP_LOGE(TAG, "error: invalid Bluetooth Feature Control");
|
|
resp_payload->resp = ESP_ERR_INVALID_ARG;
|
|
break;
|
|
}
|
|
break;
|
|
#endif /* CONFIG_ESP_HOSTED_CP_BT */
|
|
#if CONFIG_ESP_HOSTED_OT_RCP_ENABLED
|
|
case RPC_FEATURE__Feature_Openthread_Rcp:
|
|
switch(req_payload->command) {
|
|
case RPC_FEATURE_COMMAND__Feature_Command_Init:
|
|
RPC_RET_FAIL_IF(slave_openthread_init());
|
|
break;
|
|
case RPC_FEATURE_COMMAND__Feature_Command_Deinit:
|
|
RPC_RET_FAIL_IF(slave_openthread_deinit());
|
|
break;
|
|
case RPC_FEATURE_COMMAND__Feature_Command_Enable:
|
|
RPC_RET_FAIL_IF(slave_openthread_start());
|
|
break;
|
|
case RPC_FEATURE_COMMAND__Feature_Command_Disable:
|
|
RPC_RET_FAIL_IF(slave_openthread_stop());
|
|
break;
|
|
case RPC_FEATURE_COMMAND__Feature_Command_Query:
|
|
{
|
|
slave_openthread_state_t state = slave_openthread_get_state();
|
|
switch (req_payload->option) {
|
|
case RPC_FEATURE_OPTION__Feature_Option_Query_Configured:
|
|
resp_payload->resp = ESP_OK;
|
|
break;
|
|
case RPC_FEATURE_OPTION__Feature_Option_Query_Inited:
|
|
resp_payload->resp = slave_openthread_state_check(state, SLAVE_OT_STATE_INITED);
|
|
break;
|
|
case RPC_FEATURE_OPTION__Feature_Option_Query_Enabled:
|
|
resp_payload->resp = slave_openthread_state_check(state, SLAVE_OT_STATE_ENABLED);
|
|
break;
|
|
case RPC_FEATURE_OPTION__Feature_Option_Query_Ready:
|
|
resp_payload->resp = slave_openthread_state_check(state, SLAVE_OT_STATE_READY);
|
|
break;
|
|
default:
|
|
ESP_LOGE(TAG, "error: invalid Feature Query Option");
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
ESP_LOGE(TAG, "error: invalid OpenThread Feature Control");
|
|
resp_payload->resp = ESP_ERR_INVALID_ARG;
|
|
break;
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
/* Covers:
|
|
* - BT feature when BT is disabled
|
|
* - Any unsupported / unknown feature
|
|
*/
|
|
ESP_LOGE(TAG, "error: invalid Feature Control");
|
|
resp_payload->resp = ESP_ERR_INVALID_ARG;
|
|
break;
|
|
}
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
static esp_err_t req_app_get_desc(Rpc *req, Rpc *resp, void *priv_data)
|
|
{
|
|
RPC_TEMPLATE_SIMPLE(RpcRespAppGetDesc, resp_app_get_desc,
|
|
RpcReqAppGetDesc, req_app_get_desc,
|
|
rpc__resp__app_get_desc__init);
|
|
|
|
RPC_ALLOC_ELEMENT(EspAppDesc, resp_payload->app_desc, esp_app_desc__init);
|
|
EspAppDesc * p_c = resp_payload->app_desc;
|
|
|
|
const esp_app_desc_t *app_desc = esp_app_get_description();
|
|
if (app_desc) {
|
|
// copy basic info: project name, version, IDF version
|
|
RPC_RESP_COPY_STR(p_c->project_name, app_desc->project_name, sizeof(app_desc->project_name));
|
|
RPC_RESP_COPY_STR(p_c->version, app_desc->version, sizeof(app_desc->version));
|
|
RPC_RESP_COPY_STR(p_c->idf_ver, app_desc->idf_ver, sizeof(app_desc->idf_ver));
|
|
#if H_ALLOW_FULL_APP_DESC
|
|
// copy full info
|
|
p_c->magic_word = app_desc->magic_word;
|
|
p_c->secure_version = app_desc->secure_version;
|
|
|
|
RPC_RESP_COPY_STR(p_c->time, app_desc->time, sizeof(app_desc->time));
|
|
RPC_RESP_COPY_STR(p_c->date, app_desc->date, sizeof(app_desc->date));
|
|
RPC_RESP_COPY_BYTES(p_c->app_elf_sha256, app_desc->app_elf_sha256, sizeof(app_desc->app_elf_sha256));
|
|
|
|
#if H_GOT_EFUSE_BLK_REV_FULL_APP_DESC
|
|
p_c->min_efuse_blk_rev_full = app_desc->min_efuse_blk_rev_full;
|
|
p_c->max_efuse_blk_rev_full = app_desc->max_efuse_blk_rev_full;
|
|
#endif
|
|
#if H_GOT_MMU_PAGE_SIZE_FULL_APP_DESC
|
|
p_c->mmu_page_size = app_desc->mmu_page_size;
|
|
#endif
|
|
#endif
|
|
} else {
|
|
resp_payload->resp = ESP_FAIL;
|
|
}
|
|
err:
|
|
return ESP_OK;
|
|
}
|
|
|
|
#ifdef CONFIG_ESP_HOSTED_MEM_MONITOR
|
|
void mem_monitor_timer_cb(TimerHandle_t xTimer)
|
|
{
|
|
bool threshold_exceeded = false;
|
|
|
|
mem_monitor_params_t current_mem_params = { 0 };
|
|
|
|
// get current params
|
|
current_mem_params.internal_mem_dma = heap_caps_get_free_size(MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
|
|
current_mem_params.internal_mem_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
|
|
current_mem_params.external_mem_dma = heap_caps_get_free_size(MALLOC_CAP_DMA | MALLOC_CAP_SPIRAM);
|
|
current_mem_params.external_mem_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
|
|
|
|
// are current params lower than threshold
|
|
#if CONFIG_SPIRAM
|
|
if ((current_mem_params.internal_mem_dma < mem_monitor_params.internal_mem_dma) ||
|
|
(current_mem_params.internal_mem_8bit < mem_monitor_params.internal_mem_8bit) ||
|
|
(current_mem_params.external_mem_dma < mem_monitor_params.external_mem_dma) ||
|
|
(current_mem_params.external_mem_8bit < mem_monitor_params.external_mem_8bit)) {
|
|
threshold_exceeded = true;
|
|
}
|
|
#else
|
|
// external memory not enabled: only compare internal memory
|
|
if ((current_mem_params.internal_mem_dma < mem_monitor_params.internal_mem_dma) ||
|
|
(current_mem_params.internal_mem_8bit < mem_monitor_params.internal_mem_8bit)) {
|
|
threshold_exceeded = true;
|
|
}
|
|
#endif
|
|
// send an event if the current threshold was exceeded or report_always is true
|
|
if (threshold_exceeded || mem_monitor_report_always) {
|
|
mem_monitor_event_t mem_monitor_event = { 0 };
|
|
mem_monitor_event.total_free_heap_size = esp_get_free_heap_size();
|
|
mem_monitor_event.min_free_heap_size = esp_get_minimum_free_heap_size();
|
|
|
|
// copy the curr heap free sizes
|
|
memcpy(&mem_monitor_event.free_size, ¤t_mem_params, sizeof(current_mem_params));
|
|
|
|
// get the largest free block size
|
|
mem_monitor_event.largest_free_block.internal_mem_dma =
|
|
heap_caps_get_largest_free_block(MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
|
|
mem_monitor_event.largest_free_block.internal_mem_8bit =
|
|
heap_caps_get_largest_free_block(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
|
|
mem_monitor_event.largest_free_block.external_mem_dma =
|
|
heap_caps_get_largest_free_block(MALLOC_CAP_DMA | MALLOC_CAP_SPIRAM);
|
|
mem_monitor_event.largest_free_block.external_mem_8bit =
|
|
heap_caps_get_largest_free_block(MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
|
|
|
|
send_event_data_to_host(RPC_ID__Event_MemMonitor, &mem_monitor_event, sizeof(mem_monitor_event));
|
|
}
|
|
}
|
|
|
|
static esp_err_t mem_monitor_check_params(RpcReqMemMonitor *req_payload)
|
|
{
|
|
// check for missing allocated params in request
|
|
if (!req_payload->internal || !req_payload->external) {
|
|
ESP_LOGW(TAG, "%s: missing internal / external params in request", __func__);
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
// checks when enabling mem monitor
|
|
if (req_payload->config == RPC__MEM_MONITOR_CONFIG__MEMMONITOR_ENABLE) {
|
|
// interval cannot be zero
|
|
if (!req_payload->interval_sec) {
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
// thresholds should be valid if report_always is not set
|
|
if (!req_payload->report_always) {
|
|
if (!req_payload->internal->threshold_mem_dma &&
|
|
!req_payload->internal->threshold_mem_8bit &&
|
|
!req_payload->external->threshold_mem_dma &&
|
|
!req_payload->external->threshold_mem_8bit) {
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
static esp_err_t mem_monitor_setup(RpcReqMemMonitor *req_payload)
|
|
{
|
|
if ((req_payload->config == RPC__MEM_MONITOR_CONFIG__MEMMONITOR_ENABLE) ||
|
|
(req_payload->config == RPC__MEM_MONITOR_CONFIG__MEMMONITOR_DISABLE)) {
|
|
// destroy current timer if config is disable or (re)enable
|
|
if (mem_monitor_timer_handle) {
|
|
if (xTimerIsTimerActive(mem_monitor_timer_handle)) {
|
|
xTimerStop(mem_monitor_timer_handle, portMAX_DELAY);
|
|
}
|
|
xTimerDelete(mem_monitor_timer_handle, portMAX_DELAY);
|
|
mem_monitor_timer_handle = NULL;
|
|
}
|
|
}
|
|
|
|
// do we start a new timer
|
|
if (req_payload->config == RPC__MEM_MONITOR_CONFIG__MEMMONITOR_ENABLE) {
|
|
// set up params before enabling
|
|
memset(&mem_monitor_params, 0, sizeof(mem_monitor_params));
|
|
|
|
mem_monitor_params.internal_mem_dma = req_payload->internal->threshold_mem_dma;
|
|
mem_monitor_params.internal_mem_8bit = req_payload->internal->threshold_mem_8bit;
|
|
|
|
mem_monitor_params.external_mem_dma = req_payload->external->threshold_mem_dma;
|
|
mem_monitor_params.external_mem_8bit = req_payload->external->threshold_mem_8bit;
|
|
|
|
mem_monitor_report_always = req_payload->report_always;
|
|
mem_monitor_interval_sec = req_payload->interval_sec;
|
|
|
|
// create monitor timer
|
|
mem_monitor_timer_handle = xTimerCreate("MemMonitorTimer",
|
|
pdMS_TO_TICKS(mem_monitor_interval_sec * 1000),
|
|
pdTRUE,
|
|
0,
|
|
mem_monitor_timer_cb);
|
|
if (!mem_monitor_timer_handle) {
|
|
ESP_LOGE(TAG, "failed to create mem monitor timer");
|
|
return ESP_FAIL;
|
|
}
|
|
|
|
// start the timer
|
|
if (!xTimerStart(mem_monitor_timer_handle, portMAX_DELAY)) {
|
|
ESP_LOGE(TAG, "failed to start mem monitor timer");
|
|
xTimerDelete(mem_monitor_timer_handle, portMAX_DELAY);
|
|
mem_monitor_timer_handle = NULL;
|
|
return ESP_FAIL;
|
|
}
|
|
}
|
|
return ESP_OK;
|
|
}
|
|
|
|
static void mem_monitor_fill_resp_stats(RpcRespMemMonitor *resp_payload)
|
|
{
|
|
// fill the response with the memory statistics
|
|
resp_payload->curr_total_heap_size = esp_get_free_heap_size();
|
|
|
|
resp_payload->curr_internal->mem_dma->free_size = heap_caps_get_free_size(MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
|
|
resp_payload->curr_internal->mem_dma->largest_free_block = heap_caps_get_largest_free_block(MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
|
|
resp_payload->curr_internal->mem_8bit->free_size = heap_caps_get_free_size(MALLOC_CAP_8BIT|MALLOC_CAP_INTERNAL);
|
|
resp_payload->curr_internal->mem_8bit->largest_free_block = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
|
|
|
|
resp_payload->curr_external->mem_dma->free_size = heap_caps_get_free_size(MALLOC_CAP_DMA | MALLOC_CAP_SPIRAM);
|
|
resp_payload->curr_external->mem_dma->largest_free_block = heap_caps_get_largest_free_block(MALLOC_CAP_DMA | MALLOC_CAP_SPIRAM);
|
|
resp_payload->curr_external->mem_8bit->free_size = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
|
|
resp_payload->curr_external->mem_8bit->largest_free_block = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
|
|
|
|
ESP_LOGD(TAG, "Total heap size: %"PRIu32, resp_payload->curr_total_heap_size);
|
|
ESP_LOGD(TAG, "Internal->DMA->free_size: %"PRIu32, resp_payload->curr_internal->mem_dma->free_size);
|
|
ESP_LOGD(TAG, "Internal->DMA->largest_free_block: %"PRIu32, resp_payload->curr_internal->mem_dma->largest_free_block);
|
|
ESP_LOGD(TAG, "Internal->8bit->free_size: %"PRIu32, resp_payload->curr_internal->mem_8bit->free_size);
|
|
ESP_LOGD(TAG, "Internal->8bit->largest_free_block: %"PRIu32, resp_payload->curr_internal->mem_8bit->largest_free_block);
|
|
ESP_LOGD(TAG, "External->DMA->free_size: %"PRIu32, resp_payload->curr_external->mem_dma->free_size);
|
|
ESP_LOGD(TAG, "External->DMA->largest_free_block: %"PRIu32, resp_payload->curr_external->mem_dma->largest_free_block);
|
|
ESP_LOGD(TAG, "External->8bit->free_size: %"PRIu32, resp_payload->curr_external->mem_8bit->free_size);
|
|
ESP_LOGD(TAG, "External->8bit->largest_free_block: %"PRIu32, resp_payload->curr_external->mem_8bit->largest_free_block);
|
|
}
|
|
|
|
static esp_err_t req_mem_monitor(Rpc *req, Rpc *resp, void *priv_data)
|
|
{
|
|
RPC_TEMPLATE(RpcRespMemMonitor, resp_mem_monitor,
|
|
RpcReqMemMonitor, req_mem_monitor,
|
|
rpc__resp__mem_monitor__init);
|
|
|
|
esp_err_t res = mem_monitor_check_params(req_payload);
|
|
if (res != ESP_OK) {
|
|
resp_payload->resp = res;
|
|
goto err;
|
|
}
|
|
|
|
res = mem_monitor_setup(req_payload);
|
|
if (res != ESP_OK) {
|
|
resp_payload->resp = res;
|
|
goto err;
|
|
}
|
|
|
|
// prepare the response
|
|
resp_payload->config = req_payload->config;
|
|
// return current settings
|
|
resp_payload->report_always = mem_monitor_report_always;
|
|
resp_payload->interval_sec = mem_monitor_interval_sec;
|
|
|
|
RPC_ALLOC_ELEMENT(HeapInfo, resp_payload->curr_internal, heap_info__init);
|
|
RPC_ALLOC_ELEMENT(MemInfo, resp_payload->curr_internal->mem_dma, mem_info__init);
|
|
RPC_ALLOC_ELEMENT(MemInfo, resp_payload->curr_internal->mem_8bit, mem_info__init);
|
|
RPC_ALLOC_ELEMENT(HeapInfo, resp_payload->curr_external, heap_info__init);
|
|
RPC_ALLOC_ELEMENT(MemInfo, resp_payload->curr_external->mem_dma, mem_info__init);
|
|
RPC_ALLOC_ELEMENT(MemInfo, resp_payload->curr_external->mem_8bit, mem_info__init);
|
|
|
|
mem_monitor_fill_resp_stats(resp_payload);
|
|
|
|
err:
|
|
return ESP_OK;
|
|
}
|
|
#endif // CONFIG_ESP_HOSTED_MEM_MONITOR
|
|
|
|
#ifdef CONFIG_ESP_HOSTED_ENABLE_PEER_DATA_TRANSFER
|
|
/* Internal RPC bridge - delegates to registered handler */
|
|
static esp_err_t handle_custom_rpc_request(uint32_t msg_id, uint8_t *req_data, uint32_t req_len)
|
|
{
|
|
/* --------- Caution ----------
|
|
* Keep this function as simple, small and fast as possible
|
|
* This function is as callback in the Rx thread.
|
|
* Do not use any blocking calls here
|
|
* ----------------------------
|
|
*/
|
|
|
|
if (msg_id == (uint32_t)-1) {
|
|
ESP_LOGE(TAG, "Invalid message ID 0xFFFFFFFF received");
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
/* Find callback under mutex protection */
|
|
void (*cb)(uint32_t, const uint8_t *, size_t, void *) = NULL;
|
|
void *cb_local_context = NULL;
|
|
if (custom_callbacks_mutex && xSemaphoreTake(custom_callbacks_mutex, portMAX_DELAY) == pdTRUE) {
|
|
for (int i = 0; i < CONFIG_ESP_HOSTED_MAX_CUSTOM_MSG_HANDLERS; i++) {
|
|
if (custom_msg_callbacks[i].msg_id == msg_id && custom_msg_callbacks[i].callback) {
|
|
cb = custom_msg_callbacks[i].callback;
|
|
cb_local_context = custom_msg_callbacks[i].local_context;
|
|
break;
|
|
}
|
|
}
|
|
xSemaphoreGive(custom_callbacks_mutex);
|
|
}
|
|
|
|
/* Invoke callback outside mutex to avoid deadlock */
|
|
if (cb) {
|
|
cb(msg_id, req_data, req_len, cb_local_context);
|
|
return ESP_OK;
|
|
}
|
|
|
|
/* No handler registered for this message ID */
|
|
ESP_LOGW(TAG, "No custom handler registered for message ID %" PRIu32, msg_id);
|
|
return ESP_ERR_NOT_FOUND;
|
|
}
|
|
|
|
esp_err_t esp_hosted_send_custom_data(uint32_t msg_id_to_send, const uint8_t *data_to_send, size_t data_len_to_send)
|
|
{
|
|
if ((!data_to_send && data_len_to_send != 0) || (data_to_send && data_len_to_send == 0)) {
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
/* Validate payload size */
|
|
if (data_len_to_send > 8166) {
|
|
/* Why 8166?
|
|
* pserial r.data has max 8192 bytes size.
|
|
* We want to get rid of this static buffer later.
|
|
* to restrict the data size, 8192 - (serial header + esp hosted header + headroom)
|
|
* we keep it 8166, as part of r.data[8192] removal, this code would be changed.
|
|
*/
|
|
return ESP_ERR_INVALID_SIZE;
|
|
}
|
|
|
|
/* Allocate buffer for [msg_id (4 bytes)][data...] */
|
|
size_t total_len = sizeof(msg_id_to_send) + data_len_to_send;
|
|
uint8_t *buf = malloc(total_len);
|
|
if (!buf) {
|
|
ESP_LOGE(TAG, "Failed to allocate %zu bytes", total_len);
|
|
return ESP_ERR_NO_MEM;
|
|
}
|
|
|
|
/* Pack msg_id as little-endian uint32_t */
|
|
memcpy(buf, &msg_id_to_send, sizeof(msg_id_to_send));
|
|
|
|
/* Copy user data after msg_id */
|
|
if (data_len_to_send > 0) {
|
|
memcpy(buf + sizeof(msg_id_to_send), data_to_send, data_len_to_send);
|
|
}
|
|
|
|
/* Send to RPC layer - rpc_evt_custom_rpc will unpack and wrap in protobuf */
|
|
send_event_data_to_host(RPC_ID__Event_CustomRpc, buf, (int)total_len);
|
|
|
|
free(buf);
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t esp_hosted_register_custom_callback(uint32_t msg_id_exp,
|
|
void (*callback)(uint32_t msg_id_recvd, const uint8_t *data_recvd, size_t data_len_recvd, void *local_context),
|
|
void *local_context)
|
|
{
|
|
/* Validate message ID (-1/0xFFFFFFFF is invalid) */
|
|
if (msg_id_exp == (uint32_t)-1) {
|
|
ESP_LOGE(TAG, "Invalid message ID 0xFFFFFFFF");
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
/* Initialize mutex on first use */
|
|
if (!custom_callbacks_mutex) {
|
|
custom_callbacks_mutex = xSemaphoreCreateMutex();
|
|
if (!custom_callbacks_mutex) {
|
|
ESP_LOGE(TAG, "Failed to create mutex");
|
|
return ESP_ERR_NO_MEM;
|
|
}
|
|
}
|
|
|
|
if (xSemaphoreTake(custom_callbacks_mutex, portMAX_DELAY) != pdTRUE) {
|
|
ESP_LOGE(TAG, "Failed to lock mutex");
|
|
return ESP_FAIL;
|
|
}
|
|
|
|
/* Search for existing registration */
|
|
for (int i = 0; i < CONFIG_ESP_HOSTED_MAX_CUSTOM_MSG_HANDLERS; i++) {
|
|
if (custom_msg_callbacks[i].msg_id == msg_id_exp) {
|
|
/* Found existing registration */
|
|
if (callback == NULL) {
|
|
/* Deregister: clean up entry */
|
|
custom_msg_callbacks[i].msg_id = (uint32_t)-1; /* Mark as invalid */
|
|
custom_msg_callbacks[i].callback = NULL;
|
|
custom_msg_callbacks[i].local_context = NULL;
|
|
ESP_LOGI(TAG, "Deregistered callback for message ID %" PRIu32, msg_id_exp);
|
|
} else {
|
|
/* Update existing callback */
|
|
custom_msg_callbacks[i].callback = callback;
|
|
custom_msg_callbacks[i].local_context = local_context;
|
|
ESP_LOGI(TAG, "Updated callback for message ID %" PRIu32, msg_id_exp);
|
|
}
|
|
xSemaphoreGive(custom_callbacks_mutex);
|
|
return ESP_OK;
|
|
}
|
|
}
|
|
|
|
/* msg_id_exp not found */
|
|
if (callback == NULL) {
|
|
/* Cannot deregister what doesn't exist */
|
|
ESP_LOGW(TAG, "Cannot deregister message ID %" PRIu32 " - not registered", msg_id_exp);
|
|
xSemaphoreGive(custom_callbacks_mutex);
|
|
return ESP_ERR_NOT_FOUND;
|
|
}
|
|
|
|
/* Find empty slot for new registration */
|
|
for (int i = 0; i < CONFIG_ESP_HOSTED_MAX_CUSTOM_MSG_HANDLERS; i++) {
|
|
if (custom_msg_callbacks[i].callback == NULL) {
|
|
custom_msg_callbacks[i].msg_id = msg_id_exp;
|
|
custom_msg_callbacks[i].callback = callback;
|
|
custom_msg_callbacks[i].local_context = local_context;
|
|
ESP_LOGI(TAG, "Registered callback for message ID %" PRIu32, msg_id_exp);
|
|
xSemaphoreGive(custom_callbacks_mutex);
|
|
return ESP_OK;
|
|
}
|
|
}
|
|
|
|
ESP_LOGW(TAG, "No space for callback (max %d)", CONFIG_ESP_HOSTED_MAX_CUSTOM_MSG_HANDLERS);
|
|
xSemaphoreGive(custom_callbacks_mutex);
|
|
return ESP_ERR_NO_MEM;
|
|
}
|
|
|
|
static esp_err_t req_custom_rpc_handler(Rpc *req, Rpc *resp, void *priv_data)
|
|
{
|
|
RPC_TEMPLATE(RpcRespCustomRpc, resp_custom_rpc,
|
|
RpcReqCustomRpc, req_custom_rpc,
|
|
rpc__resp__custom_rpc__init);
|
|
|
|
/* Call the internal handler with message ID */
|
|
esp_err_t ret = handle_custom_rpc_request(
|
|
req_payload->custom_msg_id,
|
|
req_payload->data.data,
|
|
req_payload->data.len
|
|
);
|
|
|
|
/* Fill response with just status */
|
|
resp_payload->custom_msg_id = 0; /* Not used */
|
|
resp_payload->resp = ret;
|
|
|
|
return ESP_OK;
|
|
}
|
|
#endif /* CONFIG_ESP_HOSTED_ENABLE_PEER_DATA_TRANSFER */
|
|
|
|
|
|
static esp_rpc_req_t req_table[] = {
|
|
{
|
|
.req_num = RPC_ID__Req_OTABegin,
|
|
.command_handler = req_ota_begin_handler
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_OTAWrite,
|
|
.command_handler = req_ota_write_handler
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_OTAEnd,
|
|
.command_handler = req_ota_end_handler
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_OTAActivate,
|
|
.command_handler = req_ota_activate_handler
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_ConfigHeartbeat,
|
|
.command_handler = req_config_heartbeat
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_GetCoprocessorFwVersion,
|
|
.command_handler = req_get_coprocessor_fw_version
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_IfaceMacAddrSetGet,
|
|
.command_handler = req_iface_mac_addr_set_get
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_IfaceMacAddrLenGet,
|
|
.command_handler = req_iface_mac_addr_len_get
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_FeatureControl,
|
|
.command_handler = req_feature_control
|
|
},
|
|
|
|
{
|
|
.req_num = RPC_ID__Req_AppGetDesc,
|
|
.command_handler = req_app_get_desc
|
|
},
|
|
|
|
#ifdef CONFIG_ESP_HOSTED_CP_WIFI
|
|
{
|
|
.req_num = RPC_ID__Req_GetMACAddress ,
|
|
.command_handler = req_wifi_get_mac
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_GetWifiMode,
|
|
.command_handler = req_wifi_get_mode
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_SetWifiMode,
|
|
.command_handler = req_wifi_set_mode
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_SetMacAddress,
|
|
.command_handler = req_wifi_set_mac
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiSetPs,
|
|
.command_handler = req_wifi_set_ps
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiGetPs,
|
|
.command_handler = req_wifi_get_ps
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiSetMaxTxPower,
|
|
.command_handler = req_wifi_set_max_tx_power
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiGetMaxTxPower,
|
|
.command_handler = req_wifi_get_max_tx_power
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiInit,
|
|
.command_handler = req_wifi_init
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiDeinit,
|
|
.command_handler = req_wifi_deinit
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiStart,
|
|
.command_handler = req_wifi_start
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiStop,
|
|
.command_handler = req_wifi_stop
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiConnect,
|
|
.command_handler = req_wifi_connect
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiDisconnect,
|
|
.command_handler = req_wifi_disconnect
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiSetConfig,
|
|
.command_handler = req_wifi_set_config
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiScanParams,
|
|
.command_handler = req_wifi_scan_params
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiGetConfig,
|
|
.command_handler = req_wifi_get_config
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiScanStart,
|
|
.command_handler = req_wifi_scan_start
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiScanStop,
|
|
.command_handler = req_wifi_scan_stop
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiScanGetApNum,
|
|
.command_handler = req_wifi_scan_get_ap_num
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiScanGetApRecord,
|
|
.command_handler = req_wifi_scan_get_ap_record
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiScanGetApRecords,
|
|
.command_handler = req_wifi_scan_get_ap_records
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiClearApList,
|
|
.command_handler = req_wifi_clear_ap_list
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiRestore,
|
|
.command_handler = req_wifi_restore
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiClearFastConnect,
|
|
.command_handler = req_wifi_clear_fast_connect
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiStaGetApInfo,
|
|
.command_handler = req_wifi_sta_get_ap_info
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiDeauthSta,
|
|
.command_handler = req_wifi_deauth_sta
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiSetStorage,
|
|
.command_handler = req_wifi_set_storage
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiSetProtocol,
|
|
.command_handler = req_wifi_set_protocol
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiGetProtocol,
|
|
.command_handler = req_wifi_get_protocol
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiSetBandwidth,
|
|
.command_handler = req_wifi_set_bandwidth
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiGetBandwidth,
|
|
.command_handler = req_wifi_get_bandwidth
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiSetChannel,
|
|
.command_handler = req_wifi_set_channel
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiGetChannel,
|
|
.command_handler = req_wifi_get_channel
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiSetCountryCode,
|
|
.command_handler = req_wifi_set_country_code
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiGetCountryCode,
|
|
.command_handler = req_wifi_get_country_code
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiSetCountry,
|
|
.command_handler = req_wifi_set_country
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiGetCountry,
|
|
.command_handler = req_wifi_get_country
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiApGetStaList,
|
|
.command_handler = req_wifi_ap_get_sta_list
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiApGetStaAid,
|
|
.command_handler = req_wifi_ap_get_sta_aid
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiStaGetRssi,
|
|
.command_handler = req_wifi_sta_get_rssi
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiStaGetAid,
|
|
.command_handler = req_wifi_sta_get_aid
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiStaGetNegotiatedPhymode,
|
|
.command_handler = req_wifi_sta_get_negotiated_phymode
|
|
},
|
|
#if H_PRESENT_IN_ESP_IDF_5_4_0
|
|
{
|
|
.req_num = RPC_ID__Req_WifiSetProtocols,
|
|
.command_handler = req_wifi_set_protocols
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiGetProtocols,
|
|
.command_handler = req_wifi_get_protocols
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiSetBandwidths,
|
|
.command_handler = req_wifi_set_bandwidths
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiGetBandwidths,
|
|
.command_handler = req_wifi_get_bandwidths
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiSetBand,
|
|
.command_handler = req_wifi_set_band
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiGetBand,
|
|
.command_handler = req_wifi_get_band
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiSetBandMode,
|
|
.command_handler = req_wifi_set_band_mode
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiGetBandMode,
|
|
.command_handler = req_wifi_get_band_mode
|
|
},
|
|
#endif
|
|
{
|
|
.req_num = RPC_ID__Req_WifiSetInactiveTime,
|
|
.command_handler = req_wifi_set_inactive_time
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiGetInactiveTime,
|
|
.command_handler = req_wifi_get_inactive_time
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiDisablePmfConfig,
|
|
.command_handler = req_wifi_disable_pmf_config
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_SetDhcpDnsStatus,
|
|
.command_handler = req_set_dhcp_dns_status
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_GetDhcpDnsStatus,
|
|
.command_handler = req_get_dhcp_dns_status
|
|
},
|
|
|
|
#if CONFIG_SOC_WIFI_HE_SUPPORT
|
|
#if H_WIFI_HE_GREATER_THAN_ESP_IDF_5_3
|
|
{
|
|
.req_num = RPC_ID__Req_WifiStaTwtConfig,
|
|
.command_handler = req_wifi_sta_twt_config
|
|
},
|
|
#endif
|
|
{
|
|
.req_num = RPC_ID__Req_WifiStaItwtSetup,
|
|
.command_handler = req_wifi_sta_itwt_setup
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiStaItwtTeardown,
|
|
.command_handler = req_wifi_sta_itwt_teardown
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiStaItwtSuspend,
|
|
.command_handler = req_wifi_sta_itwt_suspend
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiStaItwtGetFlowIdStatus,
|
|
.command_handler = req_wifi_sta_itwt_get_flow_id_status
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiStaItwtSendProbeReq,
|
|
.command_handler = req_wifi_sta_itwt_send_probe_req
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiStaItwtSetTargetWakeTimeOffset,
|
|
.command_handler = req_wifi_sta_itwt_set_target_wake_time_offset
|
|
},
|
|
#endif // CONFIG_SOC_WIFI_HE_SUPPORT
|
|
#endif // CONFIG_ESP_HOSTED_CP_WIFI
|
|
|
|
#if H_WIFI_ENTERPRISE_SUPPORT
|
|
{
|
|
.req_num = RPC_ID__Req_WifiStaEnterpriseEnable,
|
|
.command_handler = req_wifi_sta_enterprise_enable
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_WifiStaEnterpriseDisable,
|
|
.command_handler = req_wifi_sta_enterprise_disable
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_EapSetIdentity,
|
|
.command_handler = req_eap_set_identity
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_EapClearIdentity,
|
|
.command_handler = req_eap_clear_identity
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_EapSetUsername,
|
|
.command_handler = req_eap_set_username
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_EapClearUsername,
|
|
.command_handler = req_eap_clear_username
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_EapSetPassword,
|
|
.command_handler = req_eap_set_password
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_EapClearPassword,
|
|
.command_handler = req_eap_clear_password
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_EapSetNewPassword,
|
|
.command_handler = req_eap_set_new_password
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_EapClearNewPassword,
|
|
.command_handler = req_eap_clear_new_password
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_EapSetCaCert,
|
|
.command_handler = req_eap_set_ca_cert
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_EapClearCaCert,
|
|
.command_handler = req_eap_clear_ca_cert
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_EapSetCertificateAndKey,
|
|
.command_handler = req_eap_set_certificate_and_key
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_EapClearCertificateAndKey,
|
|
.command_handler = req_eap_clear_certificate_and_key
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_EapGetDisableTimeCheck,
|
|
.command_handler = req_eap_get_disable_time_check
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_EapSetTtlsPhase2Method,
|
|
.command_handler = req_eap_set_ttls_phase2_method
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_EapSetSuitebCertification,
|
|
.command_handler = req_eap_set_suiteb_certification
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_EapSetPacFile,
|
|
.command_handler = req_eap_set_pac_file
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_EapSetFastParams,
|
|
.command_handler = req_eap_set_fast_params
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_EapUseDefaultCertBundle,
|
|
.command_handler = req_eap_use_default_cert_bundle
|
|
},
|
|
#if H_GOT_EAP_OKC_SUPPORT
|
|
{
|
|
.req_num = RPC_ID__Req_WifiSetOkcSupport,
|
|
.command_handler = req_wifi_set_okc_support
|
|
},
|
|
#endif
|
|
#if H_GOT_EAP_SET_DOMAIN_NAME
|
|
{
|
|
.req_num = RPC_ID__Req_EapSetDomainName,
|
|
.command_handler = req_eap_set_domain_name
|
|
},
|
|
#endif
|
|
{
|
|
.req_num = RPC_ID__Req_EapSetDisableTimeCheck,
|
|
.command_handler = req_eap_set_disable_time_check
|
|
},
|
|
#if H_GOT_SET_EAP_METHODS_API
|
|
{
|
|
.req_num = RPC_ID__Req_EapSetEapMethods,
|
|
.command_handler = req_eap_set_eap_methods
|
|
},
|
|
#endif
|
|
#endif // #if H_WIFI_ENTERPRISE_SUPPORT
|
|
|
|
#if H_DPP_SUPPORT
|
|
{
|
|
.req_num = RPC_ID__Req_SuppDppInit,
|
|
.command_handler = req_supp_dpp_init
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_SuppDppDeinit,
|
|
.command_handler = req_supp_dpp_deinit
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_SuppDppDeinit,
|
|
.command_handler = req_supp_dpp_deinit
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_SuppDppBootstrapGen,
|
|
.command_handler = req_supp_dpp_bootstrap_gen
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_SuppDppStartListen,
|
|
.command_handler = req_supp_dpp_start_listen,
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_SuppDppStopListen,
|
|
.command_handler = req_supp_dpp_stop_listen,
|
|
},
|
|
#endif // H_DPP_SUPPORT
|
|
|
|
#ifdef CONFIG_ESP_HOSTED_MEM_MONITOR
|
|
{
|
|
.req_num = RPC_ID__Req_MemMonitor,
|
|
.command_handler = req_mem_monitor
|
|
},
|
|
#endif
|
|
|
|
#ifdef CONFIG_ESP_HOSTED_ENABLE_PEER_DATA_TRANSFER
|
|
{
|
|
.req_num = RPC_ID__Req_CustomRpc,
|
|
.command_handler = req_custom_rpc_handler
|
|
},
|
|
#endif
|
|
|
|
#if H_GPIO_EXPANDER_SUPPORT
|
|
{
|
|
.req_num = RPC_ID__Req_GpioConfig,
|
|
.command_handler = req_gpio_config
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_GpioResetPin,
|
|
.command_handler = req_gpio_reset
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_GpioSetLevel,
|
|
.command_handler = req_gpio_set_level
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_GpioGetLevel,
|
|
.command_handler = req_gpio_get_level
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_GpioSetDirection,
|
|
.command_handler = req_gpio_set_direction
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_GpioInputEnable,
|
|
.command_handler = req_gpio_input_enable
|
|
},
|
|
{
|
|
.req_num = RPC_ID__Req_GpioSetPullMode,
|
|
.command_handler = req_gpio_set_pull_mode
|
|
},
|
|
#endif // H_GPIO_EXPANDER_SUPPORT
|
|
|
|
#if H_EXT_COEX_SUPPORT
|
|
{
|
|
.req_num = RPC_ID__Req_ExtCoex,
|
|
.command_handler = req_ext_coex
|
|
},
|
|
#endif
|
|
};
|
|
|
|
static int lookup_req_handler(int req_id)
|
|
{
|
|
for (int i = 0; i < sizeof(req_table)/sizeof(esp_rpc_req_t); i++) {
|
|
if (req_table[i].req_num == req_id) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static esp_err_t esp_rpc_command_dispatcher(
|
|
Rpc *req, Rpc *resp,
|
|
void *priv_data)
|
|
{
|
|
esp_err_t ret = ESP_OK;
|
|
int req_index = 0;
|
|
|
|
if (!req || !resp) {
|
|
ESP_LOGE(TAG, "Invalid parameters in command");
|
|
return ESP_FAIL;
|
|
}
|
|
|
|
if ((req->msg_id <= RPC_ID__Req_Base) ||
|
|
(req->msg_id >= RPC_ID__Req_Max)) {
|
|
ESP_LOGE(TAG, "RPC Req [0x%x] is out-of-range", req->msg_id);
|
|
goto err_not_supported;
|
|
}
|
|
|
|
if (req->msg_id != RPC_ID__Req_OTAWrite) {
|
|
ESP_LOGI(TAG, "RPC Req [0x%x] received", req->msg_id);
|
|
}
|
|
|
|
req_index = lookup_req_handler(req->msg_id);
|
|
if (req_index < 0) {
|
|
ESP_LOGW(TAG, "RPC Req [0x%x] is not supported, return failure", req->msg_id);
|
|
goto err_not_supported;
|
|
} else {
|
|
ESP_LOGI(TAG, "RPC Req [0x%x] is supported, index %d", req->msg_id, req_index);
|
|
}
|
|
|
|
ret = req_table[req_index].command_handler(req, resp, priv_data);
|
|
if (ret) {
|
|
ESP_LOGE(TAG, "RPC Req [0x%x] execution failed", req->msg_id);
|
|
goto err_cmd_error;
|
|
}
|
|
|
|
return ESP_OK;
|
|
err_not_supported:
|
|
// response ID Resp_Base means RPC Request was not supported
|
|
resp->msg_id = RPC_ID__Resp_Base;
|
|
err_cmd_error:
|
|
return ESP_OK;
|
|
}
|
|
|
|
/* use rpc__free_unpacked to free memory
|
|
* For RPC structure to be freed correctly with no memory leaks:
|
|
* - n_xxx must be set to number of 'repeated xxx' structures in RPC msg
|
|
* - xxx_case must be set for 'oneof xxx' structures in RPC msg
|
|
* - xxx.len must be set for 'bytes xxx' or 'string xxx' in RPC msg
|
|
*/
|
|
static void esp_rpc_cleanup(Rpc *resp)
|
|
{
|
|
if (resp) {
|
|
rpc__free_unpacked(resp, NULL);
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
Rpc *req = NULL;
|
|
esp_err_t ret = ESP_OK;
|
|
|
|
Rpc *resp = (Rpc *)calloc(1, sizeof(Rpc)); // resp deallocated in esp_rpc_cleanup()
|
|
if (!resp) {
|
|
ESP_LOGE(TAG, "%s calloc failed", __func__);
|
|
return ESP_FAIL;
|
|
}
|
|
|
|
if (!inbuf || !outbuf || !outlen) {
|
|
ESP_LOGE(TAG,"Buffers are NULL");
|
|
return ESP_FAIL;
|
|
}
|
|
|
|
req = rpc__unpack(NULL, inlen, inbuf);
|
|
if (!req) {
|
|
ESP_LOGE(TAG, "Unable to unpack config data");
|
|
return ESP_FAIL;
|
|
}
|
|
|
|
rpc__init (resp);
|
|
resp->msg_type = RPC_TYPE__Resp;
|
|
resp->msg_id = req->msg_id - RPC_ID__Req_Base + RPC_ID__Resp_Base;
|
|
resp->uid = req->uid;
|
|
resp->payload_case = resp->msg_id;
|
|
|
|
if (resp->msg_id != RPC_ID__Resp_OTAWrite) {
|
|
ESP_LOGI(TAG, "Resp_MSGId for req[0x%x] is [0x%x], uid %ld", req->msg_id, resp->msg_id, resp->uid);
|
|
}
|
|
ret = esp_rpc_command_dispatcher(req,resp,NULL);
|
|
if (ret) {
|
|
ESP_LOGE(TAG, "Command dispatching not happening");
|
|
goto err;
|
|
}
|
|
|
|
rpc__free_unpacked(req, NULL);
|
|
|
|
*outlen = rpc__get_packed_size (resp);
|
|
if (*outlen <= 0) {
|
|
ESP_LOGE(TAG, "Invalid encoding for response");
|
|
goto err;
|
|
}
|
|
|
|
// ESP_LOGE(TAG, "len %" PRIi16, *outlen);
|
|
|
|
*outbuf = (uint8_t *)calloc(1, *outlen);
|
|
if (!*outbuf) {
|
|
ESP_LOGE(TAG, "No memory allocated for outbuf");
|
|
esp_rpc_cleanup(resp);
|
|
return ESP_ERR_NO_MEM;
|
|
}
|
|
|
|
rpc__pack (resp, *outbuf);
|
|
|
|
//printf("Resp outbuf:\n");
|
|
//ESP_LOG_BUFFER_HEXDUMP("Resp outbuf", *outbuf, *outlen, ESP_LOG_INFO);
|
|
|
|
esp_rpc_cleanup(resp);
|
|
return ESP_OK;
|
|
|
|
err:
|
|
esp_rpc_cleanup(resp);
|
|
return ESP_FAIL;
|
|
}
|
|
|
|
/* Function ESPInit Notification */
|
|
static esp_err_t rpc_evt_ESPInit(Rpc *ntfy)
|
|
{
|
|
RpcEventESPInit *ntfy_payload = NULL;
|
|
|
|
ESP_LOGI(TAG,"event ESPInit");
|
|
ntfy_payload = (RpcEventESPInit *)
|
|
calloc(1,sizeof(RpcEventESPInit));
|
|
if (!ntfy_payload) {
|
|
ESP_LOGE(TAG,"Failed to allocate memory");
|
|
return ESP_ERR_NO_MEM;
|
|
}
|
|
rpc__event__espinit__init(ntfy_payload);
|
|
ntfy->payload_case = RPC__PAYLOAD_EVENT_ESP_INIT;
|
|
ntfy->event_esp_init = ntfy_payload;
|
|
|
|
ntfy_payload->cp_reset_reason = esp_reset_reason();
|
|
return ESP_OK;
|
|
}
|
|
|
|
static esp_err_t rpc_evt_heartbeat(Rpc *ntfy)
|
|
{
|
|
RpcEventHeartbeat *ntfy_payload = NULL;
|
|
|
|
ntfy_payload = (RpcEventHeartbeat*)
|
|
calloc(1,sizeof(RpcEventHeartbeat));
|
|
if (!ntfy_payload) {
|
|
ESP_LOGE(TAG,"Failed to allocate memory");
|
|
return ESP_ERR_NO_MEM;
|
|
}
|
|
rpc__event__heartbeat__init(ntfy_payload);
|
|
|
|
ntfy_payload->hb_num = hb_num;
|
|
|
|
ntfy->payload_case = RPC__PAYLOAD_EVENT_HEARTBEAT;
|
|
ntfy->event_heartbeat = ntfy_payload;
|
|
|
|
return ESP_OK;
|
|
|
|
}
|
|
|
|
static esp_err_t rpc_evt_Event_WifiEventNoArgs(Rpc *ntfy,
|
|
const uint8_t *data, ssize_t len)
|
|
{
|
|
NTFY_TEMPLATE(RPC_ID__Event_WifiEventNoArgs,
|
|
RpcEventWifiEventNoArgs, event_wifi_event_no_args,
|
|
rpc__event__wifi_event_no_args__init);
|
|
|
|
int32_t event_id = (int32_t)*data;
|
|
ESP_LOGI(TAG, "Sending Wi-Fi event [%ld]", event_id);
|
|
|
|
ntfy_payload->event_id = event_id;
|
|
|
|
ntfy_payload->resp = SUCCESS;
|
|
return ESP_OK;
|
|
}
|
|
|
|
#ifdef CONFIG_ESP_HOSTED_ENABLE_PEER_DATA_TRANSFER
|
|
/* Custom RPC event handler - converts raw data to protobuf */
|
|
static esp_err_t rpc_evt_custom_rpc(Rpc *ntfy, const uint8_t *data, ssize_t len)
|
|
{
|
|
|
|
NTFY_TEMPLATE(RPC_ID__Event_CustomRpc,
|
|
RpcEventCustomRpc, event_custom_rpc,
|
|
rpc__event__custom_rpc__init);
|
|
|
|
ntfy_payload->resp = SUCCESS;
|
|
|
|
/* Extract msg_id from first 4 bytes */
|
|
uint32_t msg_id;
|
|
memcpy(&msg_id, data, sizeof(msg_id));
|
|
ntfy_payload->custom_event_id = msg_id;
|
|
|
|
/* Copy user data (skip msg_id at start) */
|
|
ssize_t user_data_len = len - sizeof(msg_id);
|
|
if (user_data_len > 0) {
|
|
NTFY_COPY_BYTES(ntfy_payload->data, data + sizeof(msg_id), user_data_len);
|
|
}
|
|
|
|
return ESP_OK;
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_ESP_HOSTED_MEM_MONITOR
|
|
static esp_err_t rpc_evt_mem_monitor(Rpc *ntfy, const uint8_t *data, ssize_t len)
|
|
{
|
|
NTFY_TEMPLATE(RPC_ID__Event_MemMonitor,
|
|
RpcEventMemMonitor, event_mem_monitor,
|
|
rpc__event__mem_monitor__init);
|
|
|
|
mem_monitor_event_t *ptr = (mem_monitor_event_t *)data;
|
|
|
|
NTFY_ALLOC_ELEMENT(HeapInfo, ntfy_payload->curr_internal, heap_info__init);
|
|
NTFY_ALLOC_ELEMENT(MemInfo, ntfy_payload->curr_internal->mem_dma, mem_info__init);
|
|
NTFY_ALLOC_ELEMENT(MemInfo, ntfy_payload->curr_internal->mem_8bit, mem_info__init);
|
|
|
|
NTFY_ALLOC_ELEMENT(HeapInfo, ntfy_payload->curr_external, heap_info__init);
|
|
NTFY_ALLOC_ELEMENT(MemInfo, ntfy_payload->curr_external->mem_dma, mem_info__init);
|
|
NTFY_ALLOC_ELEMENT(MemInfo, ntfy_payload->curr_external->mem_8bit, mem_info__init);
|
|
|
|
ntfy_payload->curr_total_free_heap_size = ptr->total_free_heap_size;
|
|
ntfy_payload->curr_min_free_heap_size = ptr->min_free_heap_size;
|
|
|
|
ntfy_payload->curr_internal->mem_dma->free_size = ptr->free_size.internal_mem_dma;
|
|
ntfy_payload->curr_internal->mem_8bit->free_size = ptr->free_size.internal_mem_8bit;
|
|
ntfy_payload->curr_internal->mem_dma->largest_free_block = ptr->largest_free_block.internal_mem_dma;
|
|
ntfy_payload->curr_internal->mem_8bit->largest_free_block = ptr->largest_free_block.internal_mem_8bit;
|
|
|
|
ntfy_payload->curr_external->mem_dma->free_size = ptr->free_size.external_mem_dma;
|
|
ntfy_payload->curr_external->mem_8bit->free_size = ptr->free_size.external_mem_8bit;
|
|
ntfy_payload->curr_external->mem_dma->largest_free_block = ptr->largest_free_block.external_mem_dma;
|
|
ntfy_payload->curr_external->mem_8bit->largest_free_block = ptr->largest_free_block.external_mem_8bit;
|
|
return ESP_OK;
|
|
err:
|
|
return ESP_FAIL;
|
|
}
|
|
#endif // CONFIG_ESP_HOSTED_MEM_MONITOR
|
|
|
|
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)
|
|
{
|
|
int ret = SUCCESS;
|
|
|
|
Rpc *ntfy = (Rpc *)calloc(1, sizeof(Rpc)); // ntfy deallocated in esp_rpc_cleanup()
|
|
if (!ntfy) {
|
|
ESP_LOGE(TAG, "%s calloc failed", __func__);
|
|
return ESP_FAIL;
|
|
}
|
|
|
|
if (!outbuf || !outlen) {
|
|
ESP_LOGE(TAG,"Buffers are NULL");
|
|
return ESP_FAIL;
|
|
}
|
|
|
|
rpc__init (ntfy);
|
|
ntfy->msg_id = session_id;
|
|
ntfy->msg_type = RPC_TYPE__Event;
|
|
|
|
switch ((int)ntfy->msg_id) {
|
|
case RPC_ID__Event_ESPInit : {
|
|
ret = rpc_evt_ESPInit(ntfy);
|
|
break;
|
|
} case RPC_ID__Event_Heartbeat: {
|
|
ret = rpc_evt_heartbeat(ntfy);
|
|
break;
|
|
#ifdef CONFIG_ESP_HOSTED_CP_WIFI
|
|
} case RPC_ID__Event_AP_StaConnected: {
|
|
ret = rpc_evt_ap_staconn_conn_disconn(ntfy, inbuf, inlen, WIFI_EVENT_AP_STACONNECTED);
|
|
break;
|
|
} case RPC_ID__Event_AP_StaDisconnected: {
|
|
ret = rpc_evt_ap_staconn_conn_disconn(ntfy, inbuf, inlen, WIFI_EVENT_AP_STADISCONNECTED);
|
|
break;
|
|
} case RPC_ID__Event_StaScanDone: {
|
|
ret = rpc_evt_sta_scan_done(ntfy, inbuf, inlen, WIFI_EVENT_SCAN_DONE);
|
|
break;
|
|
} case RPC_ID__Event_StaConnected: {
|
|
ret = rpc_evt_sta_connected(ntfy, inbuf, inlen, WIFI_EVENT_STA_CONNECTED);
|
|
break;
|
|
} case RPC_ID__Event_StaDisconnected: {
|
|
ret = rpc_evt_sta_disconnected(ntfy, inbuf, inlen, WIFI_EVENT_STA_DISCONNECTED);
|
|
break;
|
|
#if CONFIG_SOC_WIFI_HE_SUPPORT
|
|
} case RPC_ID__Event_StaItwtSetup: {
|
|
ret = rpc_evt_itwt_setup(ntfy, inbuf, inlen, WIFI_EVENT_ITWT_SETUP);
|
|
break;
|
|
} case RPC_ID__Event_StaItwtTeardown: {
|
|
ret = rpc_evt_itwt_teardown(ntfy, inbuf, inlen, WIFI_EVENT_ITWT_TEARDOWN);
|
|
break;
|
|
} case RPC_ID__Event_StaItwtSuspend: {
|
|
ret = rpc_evt_itwt_suspend(ntfy, inbuf, inlen, WIFI_EVENT_ITWT_SUSPEND);
|
|
break;
|
|
} case RPC_ID__Event_StaItwtProbe: {
|
|
ret = rpc_evt_itwt_probe(ntfy, inbuf, inlen, WIFI_EVENT_ITWT_PROBE);
|
|
break;
|
|
#endif
|
|
#endif // CONFIG_ESP_HOSTED_CP_WIFI
|
|
} case RPC_ID__Event_WifiEventNoArgs: {
|
|
ret = rpc_evt_Event_WifiEventNoArgs(ntfy, inbuf, inlen);
|
|
break;
|
|
#ifdef CONFIG_ESP_HOSTED_NETWORK_SPLIT_ENABLED
|
|
} case RPC_ID__Event_DhcpDnsStatus: {
|
|
ret = rpc_evt_Event_DhcpDnsStatus(ntfy, inbuf, inlen);
|
|
break;
|
|
#endif
|
|
#if H_SUPP_DPP_SUPPORT
|
|
} case RPC_ID__Event_SuppDppUriReady: {
|
|
ret = rpc_evt_supp_dpp_uri_ready(ntfy, inbuf, inlen);
|
|
break;
|
|
} case RPC_ID__Event_SuppDppCfgRecvd: {
|
|
ret = rpc_evt_supp_dpp_cfg_recvd(ntfy, inbuf, inlen);
|
|
break;
|
|
} case RPC_ID__Event_SuppDppFail: {
|
|
ret = rpc_evt_supp_dpp_fail(ntfy, inbuf, inlen);
|
|
break;
|
|
#endif // H_SUPP_DPP_SUPPORT
|
|
#if H_WIFI_DPP_SUPPORT
|
|
} case RPC_ID__Event_WifiDppUriReady: {
|
|
ret = rpc_evt_wifi_dpp_uri_ready(ntfy, inbuf, inlen);
|
|
break;
|
|
} case RPC_ID__Event_WifiDppCfgRecvd: {
|
|
ret = rpc_evt_wifi_dpp_cfg_recvd(ntfy, inbuf, inlen);
|
|
break;
|
|
} case RPC_ID__Event_WifiDppFail: {
|
|
ret = rpc_evt_wifi_dpp_fail(ntfy, inbuf, inlen);
|
|
break;
|
|
#endif // H_WIFI_DPP_SUPPORT
|
|
#ifdef CONFIG_ESP_HOSTED_ENABLE_PEER_DATA_TRANSFER
|
|
} case RPC_ID__Event_CustomRpc: {
|
|
ret = rpc_evt_custom_rpc(ntfy, inbuf, inlen);
|
|
break;
|
|
#endif
|
|
#ifdef CONFIG_ESP_HOSTED_MEM_MONITOR
|
|
} case RPC_ID__Event_MemMonitor: {
|
|
ret = rpc_evt_mem_monitor(ntfy, inbuf, inlen);
|
|
break;
|
|
#endif // CONFIG_ESP_HOSTED_MEM_MONITOR
|
|
} default: {
|
|
ESP_LOGE(TAG, "Incorrect/unsupported Ctrl Notification[%u]\n",ntfy->msg_id);
|
|
goto err;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ret) {
|
|
ESP_LOGI(TAG, "notification[%u] not sent\n", ntfy->msg_id);
|
|
goto err;
|
|
}
|
|
|
|
*outlen = rpc__get_packed_size (ntfy);
|
|
if (*outlen <= 0) {
|
|
ESP_LOGE(TAG, "Invalid encoding for notify");
|
|
goto err;
|
|
}
|
|
|
|
*outbuf = (uint8_t *)calloc(1, *outlen);
|
|
if (!*outbuf) {
|
|
ESP_LOGE(TAG, "No memory allocated for outbuf");
|
|
esp_rpc_cleanup(ntfy);
|
|
return ESP_ERR_NO_MEM;
|
|
}
|
|
|
|
rpc__pack (ntfy, *outbuf);
|
|
|
|
//printf("event outbuf:\n");
|
|
//ESP_LOG_BUFFER_HEXDUMP("event outbuf", *outbuf, *outlen, ESP_LOG_INFO);
|
|
|
|
esp_rpc_cleanup(ntfy);
|
|
return ESP_OK;
|
|
|
|
err:
|
|
if (!*outbuf) {
|
|
free(*outbuf);
|
|
*outbuf = NULL;
|
|
}
|
|
esp_rpc_cleanup(ntfy);
|
|
return ESP_FAIL;
|
|
}
|