mirror of
https://github.com/espressif/esp-idf.git
synced 2026-08-18 06:35:35 +03:00
Merge branch 'review_ot_code_using_cursor_v5.3' into 'release/v5.3'
fix(openthread): make the code more robust (v5.3) See merge request espressif/esp-idf!48085
This commit is contained in:
@@ -168,6 +168,7 @@ private:
|
||||
|
||||
esp_openthread_spi_host_config_t m_spi_config;
|
||||
uint8_t m_tx_buffer[kSPIFrameSize];
|
||||
uint8_t *m_rx_dma_buf; ///< DMA-aligned RX buffer; avoids cache-coherency issues with unaligned frame buffer
|
||||
int m_event_fd;
|
||||
volatile uint16_t m_pending_data_len;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -80,19 +80,20 @@ esp_err_t esp_openthread_init(const esp_openthread_platform_config_t *config)
|
||||
ESP_RETURN_ON_ERROR(esp_openthread_platform_init(config), OT_PLAT_LOG_TAG,
|
||||
"Failed to initialize OpenThread platform driver");
|
||||
esp_openthread_lock_acquire(portMAX_DELAY);
|
||||
ESP_RETURN_ON_FALSE(otInstanceInitSingle() != NULL, ESP_FAIL, OT_PLAT_LOG_TAG,
|
||||
"Failed to initialize OpenThread instance");
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_GOTO_ON_FALSE(otInstanceInitSingle() != NULL, ESP_FAIL, exit, OT_PLAT_LOG_TAG,
|
||||
"Failed to initialize OpenThread instance");
|
||||
#if CONFIG_OPENTHREAD_DNS64_CLIENT
|
||||
ESP_RETURN_ON_ERROR(esp_openthread_dns64_client_init(), OT_PLAT_LOG_TAG,
|
||||
"Failed to initialize OpenThread dns64 client");
|
||||
ESP_GOTO_ON_ERROR(esp_openthread_dns64_client_init(), exit, OT_PLAT_LOG_TAG,
|
||||
"Failed to initialize OpenThread dns64 client");
|
||||
#endif
|
||||
#if !CONFIG_OPENTHREAD_RADIO
|
||||
ESP_RETURN_ON_ERROR(esp_openthread_state_event_init(esp_openthread_get_instance()), OT_PLAT_LOG_TAG,
|
||||
"Failed to initialize OpenThread state event");
|
||||
ESP_GOTO_ON_ERROR(esp_openthread_state_event_init(esp_openthread_get_instance()), exit, OT_PLAT_LOG_TAG,
|
||||
"Failed to initialize OpenThread state event");
|
||||
#endif
|
||||
exit:
|
||||
esp_openthread_lock_release();
|
||||
|
||||
return ESP_OK;
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t esp_openthread_auto_start(otOperationalDatasetTlvs *datasetTlvs)
|
||||
@@ -140,7 +141,7 @@ esp_err_t esp_openthread_auto_start(otOperationalDatasetTlvs *datasetTlvs)
|
||||
memcpy(dataset.mMeshLocalPrefix.m8, prefix.mPrefix.mFields.m8, sizeof(dataset.mMeshLocalPrefix.m8));
|
||||
dataset.mComponents.mIsMeshLocalPrefixPresent = true;
|
||||
} else {
|
||||
ESP_LOGE("Failed to parse mesh local prefix", CONFIG_OPENTHREAD_MESH_LOCAL_PREFIX);
|
||||
ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to parse mesh local prefix: %s", CONFIG_OPENTHREAD_MESH_LOCAL_PREFIX);
|
||||
}
|
||||
|
||||
// Network Key
|
||||
|
||||
@@ -31,8 +31,12 @@ static int cli_output_callback(void *context, const char *format, va_list args)
|
||||
{
|
||||
char prompt_check[3];
|
||||
int ret = 0;
|
||||
va_list args_copy;
|
||||
|
||||
va_copy(args_copy, args);
|
||||
vsnprintf(prompt_check, sizeof(prompt_check), format, args_copy);
|
||||
va_end(args_copy);
|
||||
|
||||
vsnprintf(prompt_check, sizeof(prompt_check), format, args);
|
||||
if (!strncmp(prompt_check, "> ", sizeof(prompt_check)) && s_cli_task) {
|
||||
xTaskNotifyGive(s_cli_task);
|
||||
} else {
|
||||
@@ -60,7 +64,11 @@ esp_err_t esp_openthread_cli_input(const char *line)
|
||||
|
||||
ESP_RETURN_ON_FALSE(line_copy != NULL, ESP_ERR_NO_MEM, OT_PLAT_LOG_TAG, "Failed to copy OpenThread CLI line input");
|
||||
|
||||
return esp_openthread_task_queue_post(line_handle_task, line_copy);
|
||||
esp_err_t ret = esp_openthread_task_queue_post(line_handle_task, line_copy);
|
||||
if (ret != ESP_OK) {
|
||||
free(line_copy);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ot_cli_console_callback(int argc, char **argv)
|
||||
@@ -102,6 +110,11 @@ static void ot_cli_loop(void *context)
|
||||
|
||||
console_config.hint_color = -1;
|
||||
ret = esp_console_init(&console_config);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to initialize console: %s", esp_err_to_name(ret));
|
||||
vTaskDelete(NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
linenoiseSetMultiLine(true);
|
||||
linenoiseHistorySetMaxLen(100);
|
||||
@@ -141,9 +154,8 @@ static void ot_cli_loop(void *context)
|
||||
}
|
||||
}
|
||||
|
||||
void esp_openthread_cli_create_task()
|
||||
void esp_openthread_cli_create_task(void)
|
||||
{
|
||||
xTaskCreate(ot_cli_loop, "ot_cli", 4096, xTaskGetCurrentTaskHandle(), 4, &s_cli_task);
|
||||
|
||||
return;
|
||||
BaseType_t ret = xTaskCreate(ot_cli_loop, "ot_cli", 4096, xTaskGetCurrentTaskHandle(), 4, &s_cli_task);
|
||||
ESP_RETURN_ON_FALSE(ret == pdPASS, , OT_PLAT_LOG_TAG, "Failed to create OpenThread CLI task");
|
||||
}
|
||||
|
||||
@@ -107,6 +107,7 @@ static void dns_found_handler(const char *name, const ip_addr_t *ipaddr, void *c
|
||||
if (resolve_entry && resolve_entry->found) {
|
||||
if (!ipaddr) {
|
||||
resolve_entry->found(name, NULL, resolve_entry->callback_arg);
|
||||
resolve_entry->is_using = false;
|
||||
} else if (lwip_strnicmp(name, resolve_entry->name, sizeof(resolve_entry->name)) == 0) {
|
||||
ip_addr_t ipaddr_copy = *ipaddr;
|
||||
ip6_addr_t nat64_prefix;
|
||||
@@ -117,8 +118,8 @@ static void dns_found_handler(const char *name, const ip_addr_t *ipaddr, void *c
|
||||
ipaddr_copy.u_addr.ip6.zone = IP6_NO_ZONE;
|
||||
}
|
||||
resolve_entry->found(name, &ipaddr_copy, resolve_entry->callback_arg);
|
||||
resolve_entry->is_using = false;
|
||||
}
|
||||
resolve_entry->is_using = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -20,9 +20,14 @@ bool esp_openthread_lock_acquire(TickType_t block_ticks)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(s_openthread_mutex && s_openthread_task_mutex, false, OT_PLAT_LOG_TAG,
|
||||
"Failed to acquire the lock because the mutex is not ready");
|
||||
BaseType_t ret = xSemaphoreTakeRecursive(s_openthread_mutex, block_ticks) &&
|
||||
xSemaphoreTakeRecursive(s_openthread_task_mutex, block_ticks);
|
||||
return (ret == pdTRUE);
|
||||
if (xSemaphoreTakeRecursive(s_openthread_mutex, block_ticks) != pdTRUE) {
|
||||
return false;
|
||||
}
|
||||
if (xSemaphoreTakeRecursive(s_openthread_task_mutex, block_ticks) != pdTRUE) {
|
||||
xSemaphoreGiveRecursive(s_openthread_mutex);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void esp_openthread_lock_release(void)
|
||||
@@ -61,6 +66,7 @@ esp_err_t esp_openthread_lock_init(void)
|
||||
s_openthread_mutex = xSemaphoreCreateRecursiveMutex();
|
||||
s_openthread_task_mutex = xSemaphoreCreateRecursiveMutex();
|
||||
if (s_openthread_mutex == NULL || s_openthread_task_mutex == NULL) {
|
||||
esp_openthread_lock_deinit();
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
return ESP_OK;
|
||||
|
||||
@@ -241,10 +241,8 @@ void esp_openthread_register_meshcop_e_handler(esp_event_handler_t handler, bool
|
||||
{
|
||||
if (for_publish) {
|
||||
meshcop_e_publish_handler = handler;
|
||||
} else if (!for_publish) {
|
||||
meshcop_e_remove_handler = handler;
|
||||
} else {
|
||||
ESP_ERROR_CHECK(ESP_FAIL);
|
||||
meshcop_e_remove_handler = handler;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,6 +341,8 @@ void *esp_openthread_netif_glue_init(const esp_openthread_platform_config_t *con
|
||||
s_openthread_netif_glue.event_fd = eventfd(0, 0);
|
||||
if (s_openthread_netif_glue.event_fd < 0) {
|
||||
ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to create event fd for Thread netif");
|
||||
vQueueDelete(s_packet_queue);
|
||||
s_packet_queue = NULL;
|
||||
ExitNow(error = ESP_FAIL);
|
||||
}
|
||||
s_openthread_netif_glue.base.post_attach = openthread_netif_post_attach;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -34,8 +34,12 @@ esp_err_t esp_openthread_task_queue_init(const esp_openthread_platform_config_t
|
||||
ESP_RETURN_ON_FALSE(s_task_queue_event_fd >= 0, ESP_FAIL, OT_PLAT_LOG_TAG,
|
||||
"Failed to create OpenThread task queue event fd");
|
||||
s_task_queue = xQueueCreate(config->port_config.task_queue_size, sizeof(task_storage_t));
|
||||
ESP_RETURN_ON_FALSE(s_task_queue != NULL, ESP_ERR_NO_MEM, OT_PLAT_LOG_TAG,
|
||||
"Failed to create OpenThread task queue");
|
||||
if (s_task_queue == NULL) {
|
||||
close(s_task_queue_event_fd);
|
||||
s_task_queue_event_fd = -1;
|
||||
ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to create OpenThread task queue");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
return esp_openthread_platform_workflow_register(&esp_openthread_task_queue_update,
|
||||
&esp_openthread_task_queue_process, task_queue_workflow);
|
||||
}
|
||||
|
||||
@@ -18,11 +18,14 @@
|
||||
#define OT_MSGPOOL_NUM_BUFFERS_NO_PSRAM 65
|
||||
|
||||
static int s_buffer_pool_head = -1;
|
||||
static uint16_t s_buffer_pool_size = 0;
|
||||
static otMessageBuffer **s_buffer_pool_pointer = NULL;
|
||||
static otMessageBuffer *s_buffer_pool = NULL;
|
||||
|
||||
void otPlatMessagePoolInit(otInstance *aInstance, uint16_t aMinNumFreeBuffers, size_t aBufferSize)
|
||||
{
|
||||
assert(aBufferSize % sizeof(otMessageBuffer) == 0);
|
||||
|
||||
uint16_t num_buffers = aMinNumFreeBuffers;
|
||||
|
||||
if (!esp_psram_is_initialized() && num_buffers > OT_MSGPOOL_NUM_BUFFERS_NO_PSRAM) {
|
||||
@@ -41,6 +44,7 @@ void otPlatMessagePoolInit(otInstance *aInstance, uint16_t aMinNumFreeBuffers, s
|
||||
s_buffer_pool_pointer[i] = buffer_pool + i * aBufferSize / sizeof(otMessageBuffer);
|
||||
}
|
||||
s_buffer_pool_head = num_buffers - 1;
|
||||
s_buffer_pool_size = num_buffers;
|
||||
s_buffer_pool = buffer_pool;
|
||||
ESP_LOGI(OT_PLAT_LOG_TAG, "Create message buffer pool successfully, size %d", num_buffers * aBufferSize);
|
||||
}
|
||||
@@ -57,6 +61,7 @@ otMessageBuffer *otPlatMessagePoolNew(otInstance *aInstance)
|
||||
|
||||
void otPlatMessagePoolFree(otInstance *aInstance, otMessageBuffer *aBuffer)
|
||||
{
|
||||
assert(s_buffer_pool_head + 1 < s_buffer_pool_size);
|
||||
s_buffer_pool_head++;
|
||||
s_buffer_pool_pointer[s_buffer_pool_head] = aBuffer;
|
||||
}
|
||||
@@ -77,4 +82,5 @@ void otPlatMessagePoolDeinit(otInstance *aInstance)
|
||||
s_buffer_pool = NULL;
|
||||
}
|
||||
s_buffer_pool_head = -1;
|
||||
s_buffer_pool_size = 0;
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ static uint32_t s_csl_sample_time;
|
||||
#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
|
||||
static uint32_t s_mac_frame_counter;
|
||||
static uint8_t s_key_id;
|
||||
static struct otMacKeyMaterial s_pervious_key;
|
||||
static struct otMacKeyMaterial s_previous_key;
|
||||
static struct otMacKeyMaterial s_current_key;
|
||||
static struct otMacKeyMaterial s_next_key;
|
||||
static bool s_with_security_enh_ack = false;
|
||||
@@ -140,7 +140,7 @@ esp_err_t esp_openthread_radio_init(const esp_openthread_platform_config_t *conf
|
||||
|
||||
void esp_openthread_radio_deinit(void)
|
||||
{
|
||||
if (s_radio_event_fd > 0) {
|
||||
if (s_radio_event_fd != -1) {
|
||||
close(s_radio_event_fd);
|
||||
s_radio_event_fd = -1;
|
||||
}
|
||||
@@ -492,7 +492,7 @@ void otPlatRadioSetMacKey(otInstance *aInstance, uint8_t aKeyIdMode, uint8_t aKe
|
||||
assert(aPrevKey != NULL && aCurrKey != NULL && aNextKey != NULL);
|
||||
|
||||
s_key_id = aKeyId;
|
||||
s_pervious_key = *aPrevKey;
|
||||
s_previous_key = *aPrevKey;
|
||||
s_current_key = *aCurrKey;
|
||||
s_next_key = *aNextKey;
|
||||
}
|
||||
@@ -619,7 +619,7 @@ static esp_err_t IRAM_ATTR enh_ack_set_security_addr_and_key(otRadioFrame *ack_f
|
||||
if (key_id == s_key_id) {
|
||||
key = &s_current_key;
|
||||
} else if (key_id == s_key_id - 1) {
|
||||
key = &s_pervious_key;
|
||||
key = &s_previous_key;
|
||||
} else if (key_id == s_key_id + 1) {
|
||||
key = &s_next_key;
|
||||
} else {
|
||||
@@ -692,6 +692,7 @@ void IRAM_ATTR esp_ieee802154_receive_done(uint8_t *data, esp_ieee802154_frame_i
|
||||
|
||||
if (atomic_load(&s_recv_queue.used) == CONFIG_IEEE802154_RX_BUFFER_SIZE) {
|
||||
ESP_EARLY_LOGE(OT_PLAT_LOG_TAG, "radio receive buffer full!");
|
||||
esp_ieee802154_receive_handle_done(data);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -414,7 +414,11 @@ otError otPlatDiagProcess(otInstance *aInstance, uint8_t aArgsLength, char *aArg
|
||||
char *end = cmd + sizeof(cmd);
|
||||
|
||||
for (int index = 0; index < aArgsLength; index++) {
|
||||
cur += snprintf(cur, static_cast<size_t>(end - cur), "%s ", aArgs[index]);
|
||||
if (end > cur + strlen(aArgs[index])) {
|
||||
cur += snprintf(cur, static_cast<size_t>(end - cur), "%s ", aArgs[index]);
|
||||
} else {
|
||||
return OT_ERROR_INVALID_ARGS;
|
||||
}
|
||||
}
|
||||
|
||||
return s_radio.PlatDiagProcess(cmd);
|
||||
|
||||
@@ -223,4 +223,5 @@ otError otPlatSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex)
|
||||
void otPlatSettingsWipe(otInstance *aInstance)
|
||||
{
|
||||
nvs_erase_all(s_ot_nvs_handle);
|
||||
nvs_commit(s_ot_nvs_handle);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ esp_err_t esp_openthread_sleep_init(void)
|
||||
|
||||
void esp_openthread_sleep_process(void)
|
||||
{
|
||||
assert(s_pm_lock != NULL);
|
||||
if (s_ot_sleep == false && esp_ieee802154_get_state() == ESP_IEEE802154_RADIO_SLEEP) {
|
||||
esp_pm_lock_release(s_pm_lock);
|
||||
s_ot_sleep = true;
|
||||
|
||||
@@ -1,17 +1,8 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/* SPI Slave example, receiver (uses SPI Slave driver to communicate with sender)
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
#include <openthread/platform/spi-slave.h>
|
||||
|
||||
#include "esp_attr.h"
|
||||
@@ -42,6 +33,15 @@ typedef struct {
|
||||
uint16_t input_buf_len;
|
||||
} pending_transaction_t;
|
||||
|
||||
// DMA bounce buffer for RX — always sized to max(input, output) so MISO is
|
||||
// driven for the full output even when NcpSpi passes a small input buffer.
|
||||
#define SPI_SLAVE_RX_DMA_BUF_SIZE OPENTHREAD_CONFIG_NCP_SPI_BUFFER_SIZE
|
||||
static DRAM_ATTR uint8_t *s_rx_dma_buf = NULL;
|
||||
|
||||
// Guards the BUSY path: only return OT_ERROR_BUSY when a transaction is truly
|
||||
// queued in the driver, so post_trans_cb is guaranteed to fire and re-queue.
|
||||
static volatile DRAM_ATTR bool s_transaction_in_flight = false;
|
||||
|
||||
static otPlatSpiSlaveTransactionProcessCallback s_process_callback = NULL;
|
||||
static otPlatSpiSlaveTransactionCompleteCallback s_complete_callback = NULL;
|
||||
|
||||
@@ -59,54 +59,78 @@ static void IRAM_ATTR handle_spi_setup_done(spi_slave_transaction_t *trans)
|
||||
static void IRAM_ATTR handle_spi_transaction_done(spi_slave_transaction_t *trans)
|
||||
{
|
||||
gpio_set_level(s_spi_config->intr_pin, 1);
|
||||
s_transaction_in_flight = false;
|
||||
pending_transaction_t *pending_transaction = (pending_transaction_t *)(trans->user);
|
||||
trans->trans_len /= CHAR_BIT;
|
||||
|
||||
// Cap trans_len: HW reports actual clock count which may exceed slave buffer.
|
||||
uint16_t max_buf_len = pending_transaction->output_buf_len > pending_transaction->input_buf_len
|
||||
? pending_transaction->output_buf_len : pending_transaction->input_buf_len;
|
||||
if (trans->trans_len > max_buf_len) {
|
||||
trans->trans_len = max_buf_len;
|
||||
}
|
||||
|
||||
// Copy RX bounce buffer back to the actual NcpSpi input buffer.
|
||||
if (s_input_buf && s_rx_dma_buf && s_rx_dma_buf != s_input_buf) {
|
||||
memcpy(s_input_buf, s_rx_dma_buf, pending_transaction->input_buf_len);
|
||||
}
|
||||
|
||||
if (s_complete_callback &&
|
||||
s_complete_callback(s_context, (void*)trans->tx_buffer, pending_transaction->output_buf_len,
|
||||
trans->rx_buffer, pending_transaction->input_buf_len, trans->trans_len)) {
|
||||
s_input_buf, pending_transaction->input_buf_len, trans->trans_len)) {
|
||||
esp_openthread_task_queue_post(s_process_callback, s_context);
|
||||
}
|
||||
trans = NULL;
|
||||
}
|
||||
|
||||
esp_err_t esp_openthread_host_rcp_spi_init(const esp_openthread_platform_config_t *config)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
s_spi_config = heap_caps_malloc(sizeof(esp_openthread_spi_slave_config_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||||
ESP_RETURN_ON_FALSE(s_spi_config != NULL, ESP_ERR_NO_MEM, OT_PLAT_LOG_TAG,
|
||||
"failed to allocate memory for SPI transaction on internal heap");
|
||||
ESP_GOTO_ON_FALSE(s_spi_config != NULL, ESP_ERR_NO_MEM, err, OT_PLAT_LOG_TAG,
|
||||
"failed to allocate memory for SPI transaction on internal heap");
|
||||
memcpy(s_spi_config, &(config->host_config.spi_slave_config), sizeof(esp_openthread_spi_slave_config_t));
|
||||
gpio_config_t io_conf = {
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = (1 << s_spi_config->intr_pin),
|
||||
.pin_bit_mask = (1ULL << s_spi_config->intr_pin),
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(gpio_config(&io_conf), OT_PLAT_LOG_TAG, "fail to configure SPI gpio");
|
||||
ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, OT_PLAT_LOG_TAG, "fail to configure SPI gpio");
|
||||
// Deassert INT: GPIO latch resets to LOW after chip reset, which would
|
||||
// cause a spurious NEGEDGE before the slave is ready.
|
||||
gpio_set_level(s_spi_config->intr_pin, 1);
|
||||
|
||||
gpio_set_pull_mode(s_spi_config->bus_config.mosi_io_num, GPIO_PULLUP_ONLY);
|
||||
gpio_set_pull_mode(s_spi_config->bus_config.sclk_io_num, GPIO_PULLUP_ONLY);
|
||||
gpio_set_pull_mode(s_spi_config->slave_config.spics_io_num, GPIO_PULLUP_ONLY);
|
||||
|
||||
s_spi_transaction = heap_caps_malloc(sizeof(spi_slave_transaction_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||||
ESP_GOTO_ON_FALSE(s_spi_transaction != NULL, ESP_ERR_NO_MEM, err, OT_PLAT_LOG_TAG, "failed to allocate memory for SPI transaction on internal heap");
|
||||
s_pending_transaction = heap_caps_malloc(sizeof(pending_transaction_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||||
if (s_spi_transaction == NULL || s_pending_transaction == NULL) {
|
||||
heap_caps_free(s_spi_config);
|
||||
heap_caps_free(s_spi_transaction);
|
||||
heap_caps_free(s_pending_transaction);
|
||||
ESP_LOGE(OT_PLAT_LOG_TAG, "failed to allocate memory for SPI transaction on internal heap");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
ESP_GOTO_ON_FALSE(s_pending_transaction != NULL, ESP_ERR_NO_MEM, err, OT_PLAT_LOG_TAG, "failed to allocate memory for pending transaction on internal heap");
|
||||
s_rx_dma_buf = heap_caps_malloc(SPI_SLAVE_RX_DMA_BUF_SIZE, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
|
||||
ESP_GOTO_ON_FALSE(s_rx_dma_buf != NULL, ESP_ERR_NO_MEM, err, OT_PLAT_LOG_TAG, "failed to allocate memory for RX DMA buffer on internal heap");
|
||||
|
||||
s_spi_transaction->user = (void *)s_pending_transaction;
|
||||
|
||||
/* Initialize SPI slave interface */
|
||||
s_spi_config->slave_config.post_setup_cb = handle_spi_setup_done;
|
||||
s_spi_config->slave_config.post_trans_cb = handle_spi_transaction_done;
|
||||
ESP_RETURN_ON_ERROR(spi_slave_initialize(s_spi_config->host_device, &s_spi_config->bus_config,
|
||||
&s_spi_config->slave_config, SPI_DMA_CH_AUTO),
|
||||
OT_PLAT_LOG_TAG, "fail to initialize SPI slave");
|
||||
ESP_GOTO_ON_ERROR(spi_slave_initialize(s_spi_config->host_device, &s_spi_config->bus_config,
|
||||
&s_spi_config->slave_config, SPI_DMA_CH_AUTO),
|
||||
err, OT_PLAT_LOG_TAG, "fail to initialize SPI slave");
|
||||
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
heap_caps_free(s_spi_config);
|
||||
s_spi_config = NULL;
|
||||
heap_caps_free(s_spi_transaction);
|
||||
s_spi_transaction = NULL;
|
||||
heap_caps_free(s_pending_transaction);
|
||||
s_pending_transaction = NULL;
|
||||
heap_caps_free(s_rx_dma_buf);
|
||||
s_rx_dma_buf = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void esp_openthread_spi_slave_deinit(void)
|
||||
@@ -117,9 +141,11 @@ void esp_openthread_spi_slave_deinit(void)
|
||||
heap_caps_free(s_spi_config);
|
||||
heap_caps_free(s_spi_transaction);
|
||||
heap_caps_free(s_pending_transaction);
|
||||
heap_caps_free(s_rx_dma_buf);
|
||||
s_spi_config = NULL;
|
||||
s_spi_transaction = NULL;
|
||||
s_pending_transaction = NULL;
|
||||
s_rx_dma_buf = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -147,14 +173,21 @@ otError IRAM_ATTR otPlatSpiSlavePrepareTransaction(uint8_t *aOutputBuf, uint16_t
|
||||
s_input_len = aInputBufLen;
|
||||
}
|
||||
|
||||
trans_length = s_output_len > s_input_len ? s_output_len : s_input_len;
|
||||
trans_length *= CHAR_BIT;
|
||||
if ((gpio_get_level(s_spi_config->slave_config.spics_io_num) == 0)) {
|
||||
// Use max(input, output) so MISO is driven for the full output frame;
|
||||
// s_rx_dma_buf absorbs extra RX bytes to avoid overflowing the NcpSpi buffer.
|
||||
uint16_t trans_data_len = (s_input_len > s_output_len) ? s_input_len : s_output_len;
|
||||
trans_length = trans_data_len * CHAR_BIT;
|
||||
|
||||
// In task context, return BUSY only when a transaction is already in flight
|
||||
// AND CS is asserted — ensures post_trans_cb will fire to re-queue.
|
||||
// In ISR context (post_trans_cb) we always queue unconditionally.
|
||||
if (xPortCanYield() && s_transaction_in_flight &&
|
||||
(gpio_get_level(s_spi_config->slave_config.spics_io_num) == 0)) {
|
||||
ESP_EARLY_LOGE(SPI_SLAVE_TAG, "SPI busy");
|
||||
return OT_ERROR_BUSY;
|
||||
}
|
||||
s_spi_transaction->length = trans_length;
|
||||
s_spi_transaction->rx_buffer = s_input_buf;
|
||||
s_spi_transaction->rx_buffer = s_rx_dma_buf;
|
||||
s_spi_transaction->tx_buffer = s_output_buf;
|
||||
|
||||
pending_transaction_t *pending_transaction = (pending_transaction_t *)s_spi_transaction->user;
|
||||
@@ -171,6 +204,7 @@ otError IRAM_ATTR otPlatSpiSlavePrepareTransaction(uint8_t *aOutputBuf, uint16_t
|
||||
}
|
||||
|
||||
if (trans_state == ESP_OK) {
|
||||
s_transaction_in_flight = true;
|
||||
return OT_ERROR_NONE;
|
||||
} else {
|
||||
return OT_ERROR_FAILED;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -191,6 +191,8 @@ esp_err_t esp_openthread_trel_process(otInstance *aInstance, const esp_openthrea
|
||||
}
|
||||
if (should_handle) {
|
||||
otPlatTrelHandleReceived(aInstance, data_buf, length, source_addr);
|
||||
s_trel_counters.mRxPackets++;
|
||||
s_trel_counters.mRxBytes += length;
|
||||
}
|
||||
pbuf_free(recv_buf);
|
||||
free(data_buf_to_free);
|
||||
@@ -255,7 +257,13 @@ void otPlatTrelSend(otInstance *aInstance,
|
||||
esp_openthread_task_switching_lock_release();
|
||||
err = esp_netif_tcpip_exec(trel_send_task, &task);
|
||||
esp_openthread_task_switching_lock_acquire(portMAX_DELAY);
|
||||
ESP_RETURN_ON_FALSE(err == ESP_OK, , OT_PLAT_LOG_TAG, "Failed to send TREL message");
|
||||
if (err == ESP_OK) {
|
||||
s_trel_counters.mTxPackets++;
|
||||
s_trel_counters.mTxBytes += aUdpPayloadLen;
|
||||
} else {
|
||||
s_trel_counters.mTxFailure++;
|
||||
ESP_LOGW(OT_PLAT_LOG_TAG, "Failed to send TREL message");
|
||||
}
|
||||
}
|
||||
|
||||
void otPlatTrelNotifyPeerSocketAddressDifference(otInstance *aInstance,
|
||||
@@ -284,8 +292,10 @@ void otPlatTrelRegisterService(otInstance *aInstance, uint16_t aPort, const uint
|
||||
s_is_service_registered = true;
|
||||
uint16_t index = 0;
|
||||
while (index < aTxtLength) {
|
||||
const uint8_t *item_header = aTxtData + index + 1;
|
||||
uint8_t item_len = aTxtData[index];
|
||||
ESP_GOTO_ON_FALSE(index + 1 + item_len <= aTxtLength, ESP_FAIL, exit, OT_PLAT_LOG_TAG,
|
||||
"Malformed _trel._udp TXT record: item exceeds data length");
|
||||
const uint8_t *item_header = aTxtData + index + 1;
|
||||
|
||||
char key[UINT8_MAX + 1];
|
||||
for (uint16_t i = 0; i < item_len; i++) {
|
||||
@@ -348,6 +358,7 @@ void otPlatTrelDisable(otInstance *aInstance)
|
||||
free_all_buffer();
|
||||
close(s_trel_event_fd);
|
||||
s_trel_event_fd = -1;
|
||||
s_trel_netif = NULL;
|
||||
}
|
||||
|
||||
const otPlatTrelCounters *otPlatTrelGetCounters(otInstance *aInstance)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -30,7 +30,8 @@
|
||||
#endif
|
||||
|
||||
static int s_uart_port;
|
||||
static int s_uart_fd;
|
||||
static int s_uart_fd = -1;
|
||||
static bool s_uart_driver_installed = false;
|
||||
static uint8_t s_uart_buffer[ESP_OPENTHREAD_UART_BUFFER_SIZE];
|
||||
static const char *uart_workflow = "uart";
|
||||
|
||||
@@ -89,6 +90,7 @@ esp_err_t esp_openthread_uart_init_port(const esp_openthread_uart_config_t *conf
|
||||
OT_PLAT_LOG_TAG, "uart_set_pin failed");
|
||||
ESP_RETURN_ON_ERROR(uart_driver_install(config->port, ESP_OPENTHREAD_UART_BUFFER_SIZE, 0, 0, NULL, 0),
|
||||
OT_PLAT_LOG_TAG, "uart_driver_install failed");
|
||||
s_uart_driver_installed = true;
|
||||
uart_vfs_dev_use_driver(config->port);
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -173,7 +175,10 @@ void esp_openthread_uart_deinit()
|
||||
close(s_uart_fd);
|
||||
s_uart_fd = -1;
|
||||
}
|
||||
uart_driver_delete(s_uart_port);
|
||||
if (s_uart_driver_installed) {
|
||||
uart_driver_delete(s_uart_port);
|
||||
s_uart_driver_installed = false;
|
||||
}
|
||||
esp_openthread_platform_workflow_unregister(uart_workflow);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -135,8 +135,12 @@ static void udp_recv_task(void *ctx)
|
||||
ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to copy OpenThread message when receiving OpenThread plat UDP"));
|
||||
task->socket->mHandler(task->socket->mContext, message, &message_info);
|
||||
otMessageFree(message);
|
||||
message = NULL;
|
||||
|
||||
exit:
|
||||
if (message != NULL) {
|
||||
otMessageFree(message);
|
||||
}
|
||||
free(task);
|
||||
if (data_buf_to_free) {
|
||||
free(data_buf_to_free);
|
||||
@@ -156,6 +160,8 @@ static void handle_udp_recv(void *ctx, struct udp_pcb *pcb, struct pbuf *p, cons
|
||||
|
||||
if (task == NULL) {
|
||||
ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to allocate recv task when receiving OpenThread plat UDP");
|
||||
pbuf_free(p);
|
||||
return;
|
||||
}
|
||||
task->socket = (otUdpSocket *)ctx;
|
||||
task->recv_buf = p;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "openthread/error.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_openthread_common_macro.h"
|
||||
#include "esp_rom_sys.h"
|
||||
#include "esp_vfs.h"
|
||||
@@ -17,6 +18,7 @@
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "hal/gpio_types.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "ncp/ncp_spi.hpp"
|
||||
|
||||
using ot::Spinel::SpiFrame;
|
||||
@@ -26,7 +28,8 @@ namespace esp {
|
||||
namespace openthread {
|
||||
|
||||
SpiSpinelInterface::SpiSpinelInterface(void)
|
||||
: m_event_fd(-1)
|
||||
: m_rx_dma_buf(nullptr)
|
||||
, m_event_fd(-1)
|
||||
, m_receiver_frame_callback(nullptr)
|
||||
, m_receiver_frame_context(nullptr)
|
||||
, m_receive_frame_buffer(nullptr)
|
||||
@@ -73,7 +76,7 @@ esp_err_t SpiSpinelInterface::Enable(const esp_openthread_spi_host_config_t &spi
|
||||
io_conf.mode = GPIO_MODE_INPUT;
|
||||
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
|
||||
ESP_RETURN_ON_ERROR(gpio_config(&io_conf), OT_PLAT_LOG_TAG, "fail to config spi gpio");
|
||||
gpio_install_isr_service(0); // The gpio isr service may has been installed.
|
||||
gpio_install_isr_service(0);
|
||||
ESP_RETURN_ON_ERROR(gpio_isr_handler_add(spi_config.intr_pin, GpioIntrHandler, this), OT_PLAT_LOG_TAG,
|
||||
"fail to add gpio isr handler");
|
||||
m_has_pending_device_frame = false;
|
||||
@@ -82,6 +85,9 @@ esp_err_t SpiSpinelInterface::Enable(const esp_openthread_spi_host_config_t &spi
|
||||
|
||||
ESP_RETURN_ON_FALSE(m_event_fd >= 0, ESP_FAIL, OT_PLAT_LOG_TAG, "fail to get event fd");
|
||||
|
||||
m_rx_dma_buf = (uint8_t *)heap_caps_malloc(kSPIFrameSize, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
|
||||
ESP_RETURN_ON_FALSE(m_rx_dma_buf != nullptr, ESP_ERR_NO_MEM, OT_PLAT_LOG_TAG, "fail to alloc SPI RX DMA buffer");
|
||||
|
||||
ESP_LOGI(OT_PLAT_LOG_TAG, "spinel SPI interface initialization completed");
|
||||
|
||||
return ESP_OK;
|
||||
@@ -105,6 +111,8 @@ esp_err_t SpiSpinelInterface::Disable(void)
|
||||
ESP_RETURN_ON_ERROR(spi_bus_free(m_spi_config.host_device), OT_PLAT_LOG_TAG, "fail to free spi bus");
|
||||
gpio_uninstall_isr_service();
|
||||
}
|
||||
heap_caps_free(m_rx_dma_buf);
|
||||
m_rx_dma_buf = nullptr;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -115,8 +123,7 @@ otError SpiSpinelInterface::SendFrame(const uint8_t *frame, uint16_t length)
|
||||
"send frame is too long");
|
||||
|
||||
memcpy(&m_tx_buffer[kSPIFrameHeaderSize], frame, length);
|
||||
uint16_t rx_data_size =
|
||||
length < kSmallPacketSize ? kSmallPacketSize : length; // We'll use tx_size to receive small packets piggybacked
|
||||
uint16_t rx_data_size = length < kSmallPacketSize ? kSmallPacketSize : length;
|
||||
if (ConductSPITransaction(false, length, rx_data_size) == ESP_OK) {
|
||||
return OT_ERROR_NONE;
|
||||
} else {
|
||||
@@ -152,9 +159,12 @@ esp_err_t SpiSpinelInterface::ConductSPITransaction(bool reset, uint16_t tx_data
|
||||
transaction.length = data_size * CHAR_BIT;
|
||||
transaction.rxlength = (rx_data_size + kSPIFrameHeaderSize) * CHAR_BIT;
|
||||
transaction.tx_buffer = m_tx_buffer;
|
||||
transaction.rx_buffer = rx_buffer;
|
||||
// Use aligned DMA buffer: MultiFrameBuffer's internal array is not
|
||||
// cache-line-aligned, causing stale D-cache reads after DMA completes.
|
||||
transaction.rx_buffer = m_rx_dma_buf;
|
||||
|
||||
ESP_RETURN_ON_ERROR(spi_device_polling_transmit(m_device, &transaction), OT_PLAT_LOG_TAG, "SPI transaction failed");
|
||||
memcpy(rx_buffer, m_rx_dma_buf, data_size);
|
||||
SpiFrame rx_frame(rx_buffer);
|
||||
|
||||
if (!rx_frame.IsValid() || rx_frame.GetHeaderAcceptLen() > kSPIFrameSize ||
|
||||
@@ -162,17 +172,14 @@ esp_err_t SpiSpinelInterface::ConductSPITransaction(bool reset, uint16_t tx_data
|
||||
vTaskDelay(pdMS_TO_TICKS(15));
|
||||
ESP_RETURN_ON_ERROR(spi_device_polling_transmit(m_device, &transaction), OT_PLAT_LOG_TAG,
|
||||
"fail to retry SPI invalid transaction");
|
||||
memcpy(rx_buffer, m_rx_dma_buf, data_size);
|
||||
}
|
||||
|
||||
if (rx_frame.IsResetFlagSet()) {
|
||||
ESP_LOGW(OT_PLAT_LOG_TAG, "RCP Reset");
|
||||
m_receive_frame_buffer->DiscardFrame();
|
||||
return ESP_OK;
|
||||
}
|
||||
if (rx_frame.GetHeaderDataLen() == 0 && rx_frame.GetHeaderAcceptLen() == 0) {
|
||||
vTaskDelay(pdMS_TO_TICKS(15));
|
||||
ESP_RETURN_ON_ERROR(spi_device_polling_transmit(m_device, &transaction), OT_PLAT_LOG_TAG,
|
||||
"fail to retry SPI empty transaction");
|
||||
memcpy(rx_buffer, m_rx_dma_buf, data_size);
|
||||
}
|
||||
|
||||
if (rx_frame.GetHeaderDataLen() > 0 && rx_frame.GetHeaderDataLen() < tx_frame.GetHeaderAcceptLen()) {
|
||||
@@ -267,7 +274,20 @@ otError SpiSpinelInterface::HardwareReset(void)
|
||||
{
|
||||
if (mRcpFailureHandler) {
|
||||
mRcpFailureHandler();
|
||||
ConductSPITransaction(true, 0, 0); // clear
|
||||
ConductSPITransaction(true, 0, 0);
|
||||
// Drain stale GPIO interrupt events accumulated before reset, otherwise
|
||||
// WaitForFrame() would fire immediately on a stale event and fail.
|
||||
uint64_t event;
|
||||
struct timeval zero_timeout = {0, 0};
|
||||
fd_set read_fds;
|
||||
FD_ZERO(&read_fds);
|
||||
FD_SET(m_event_fd, &read_fds);
|
||||
if (select(m_event_fd + 1, &read_fds, NULL, NULL, &zero_timeout) > 0 &&
|
||||
FD_ISSET(m_event_fd, &read_fds)) {
|
||||
read(m_event_fd, &event, sizeof(event));
|
||||
}
|
||||
m_pending_data_len = 0;
|
||||
|
||||
}
|
||||
return OT_ERROR_NONE;
|
||||
}
|
||||
|
||||
@@ -93,6 +93,7 @@ void app_main(void)
|
||||
ESP_ERROR_CHECK(mdns_hostname_set("esp-ot-br"));
|
||||
#if CONFIG_OPENTHREAD_SUPPORT_HW_RESET_RCP
|
||||
esp_openthread_register_rcp_failure_handler(rcp_failure_hardware_reset_handler);
|
||||
esp_openthread_set_coprocessor_reset_failure_callback(rcp_failure_hardware_reset_handler);
|
||||
#endif
|
||||
|
||||
#if CONFIG_OPENTHREAD_CLI
|
||||
|
||||
Reference in New Issue
Block a user