فهرست منبع

openthread: add SPI support in Radio Co-Processor

xieqinan 3 سال پیش
والد
کامیت
5a1578e692

+ 7 - 5
components/openthread/CMakeLists.txt

@@ -111,9 +111,11 @@ if(CONFIG_OPENTHREAD_ENABLED)
 
     if(CONFIG_OPENTHREAD_RADIO_NATIVE)
         list(APPEND exclude_srcs
-            "port/esp_openthread_radio_uart.cpp"
-            "port/esp_uart_spinel_interface.cpp")
-    elseif(CONFIG_OPENTHREAD_RADIO_SPINEL_UART)
+            "port/esp_openthread_radio_spinel.cpp"
+            "port/esp_spi_spinel_interface.cpp"
+            "port/esp_uart_spinel_interface.cpp"
+            )
+    elseif(CONFIG_OPENTHREAD_RADIO_SPINEL_UART OR CONFIG_OPENTHREAD_RADIO_SPINEL_SPI)
         list(APPEND exclude_srcs
             "port/esp_openthread_radio.c")
     endif()
@@ -166,9 +168,9 @@ idf_component_register(SRC_DIRS "${src_dirs}"
                        EXCLUDE_SRCS "${exclude_srcs}"
                        INCLUDE_DIRS "${public_include_dirs}"
                        PRIV_INCLUDE_DIRS "${private_include_dirs}"
-                       REQUIRES esp_netif lwip
+                       REQUIRES esp_netif lwip driver
                        LDFRAGMENTS linker.lf
-                       PRIV_REQUIRES console driver esp_event esp_partition esp_timer
+                       PRIV_REQUIRES console esp_event esp_partition esp_timer
                                      ieee802154 mbedtls spi_flash)
 
 if(CONFIG_OPENTHREAD_ENABLED)

+ 22 - 0
components/openthread/Kconfig

@@ -61,6 +61,12 @@ menu "OpenThread"
             bool "Connect via UART"
             help
                 Select this to connect to a Radio Co-Processor via UART.
+
+        config OPENTHREAD_RADIO_SPINEL_SPI
+            bool "Connect via SPI"
+            help
+                Select this to connect to a Radio Co-Processor via SPI.
+
     endchoice
 
     choice OPENTHREAD_DEVICE_TYPE
@@ -89,6 +95,22 @@ menu "OpenThread"
                 radio only device.
     endchoice
 
+    choice OPENTHREAD_RCP_TRANSPORT
+        prompt "The RCP transport type"
+        depends on OPENTHREAD_RADIO
+        default OPENTHREAD_RCP_UART
+
+        config OPENTHREAD_RCP_UART
+            bool "UART RCP"
+            help
+                Select this to enable UART connection to host.
+
+        config OPENTHREAD_RCP_SPI
+            bool "SPI RCP"
+            help
+                Select this to enable SPI connection to host.
+    endchoice
+
     config OPENTHREAD_CLI
         bool "Enable Openthread Command-Line Interface"
         depends on OPENTHREAD_ENABLED

+ 39 - 5
components/openthread/include/esp_openthread_types.h

@@ -11,6 +11,10 @@
 #include <sys/select.h>
 
 #include "esp_event_base.h"
+#include "driver/gpio.h"
+#include "driver/spi_master.h"
+#include "driver/spi_slave.h"
+#include "hal/gpio_types.h"
 #include "hal/uart_types.h"
 
 #ifdef __cplusplus
@@ -60,10 +64,33 @@ typedef struct {
 typedef struct {
     uart_port_t   port;        /*!< UART port number */
     uart_config_t uart_config; /*!< UART configuration, see uart_config_t docs */
-    int           rx_pin;      /*!< UART RX pin */
-    int           tx_pin;      /*!< UART TX pin */
+    gpio_num_t    rx_pin;      /*!< UART RX pin */
+    gpio_num_t    tx_pin;      /*!< UART TX pin */
 } esp_openthread_uart_config_t;
 
+/**
+ * @brief The spi port config for OpenThread.
+ *
+ */
+typedef struct {
+    spi_host_device_t             host_device;      /*!< SPI host device */
+    spi_dma_chan_t                dma_channel;      /*!< DMA channel */
+    spi_bus_config_t              spi_interface;    /*!< SPI bus */
+    spi_device_interface_config_t spi_device;       /*!< SPI peripheral device */
+    gpio_num_t                    intr_pin;         /*!< SPI interrupt pin */
+} esp_openthread_spi_host_config_t;
+
+/**
+ * @brief The spi slave config for OpenThread.
+ *
+ */
+typedef struct {
+    spi_host_device_t            host_device;  /*!< SPI host device */
+    spi_bus_config_t             bus_config;   /*!< SPI bus config */
+    spi_slave_interface_config_t slave_config; /*!< SPI slave config */
+    gpio_num_t                   intr_pin;     /*!< SPI interrupt pin */
+} esp_openthread_spi_slave_config_t;
+
 /**
  * @brief The radio mode of OpenThread.
  *
@@ -82,6 +109,7 @@ typedef enum {
     HOST_CONNECTION_MODE_NONE = 0x0,     /*!< Disable host connection */
     HOST_CONNECTION_MODE_CLI_UART = 0x1, /*!< CLI UART connection to the host */
     HOST_CONNECTION_MODE_RCP_UART = 0x2, /*!< RCP UART connection to the host */
+    HOST_CONNECTION_MODE_RCP_SPI = 0x3,  /*!< RCP SPI connection to the host */
 } esp_openthread_host_connection_mode_t;
 
 /**
@@ -89,8 +117,11 @@ typedef enum {
  *
  */
 typedef struct {
-    esp_openthread_radio_mode_t  radio_mode;        /*!< The radio mode */
-    esp_openthread_uart_config_t radio_uart_config; /*!< The uart configuration to RCP */
+    esp_openthread_radio_mode_t radio_mode; /*!< The radio mode */
+    union {
+        esp_openthread_uart_config_t     radio_uart_config; /*!< The uart configuration to RCP */
+        esp_openthread_spi_host_config_t radio_spi_config;  /*!< The spi configuration to RCP */
+    };
 } esp_openthread_radio_config_t;
 
 /**
@@ -99,7 +130,10 @@ typedef struct {
  */
 typedef struct {
     esp_openthread_host_connection_mode_t host_connection_mode; /*!< The host connection mode */
-    esp_openthread_uart_config_t          host_uart_config;     /*!< The uart configuration to host */
+    union {
+        esp_openthread_uart_config_t      host_uart_config; /*!< The uart configuration to host */
+        esp_openthread_spi_slave_config_t spi_slave_config; /*!< The spi configuration to host */
+    };
 } esp_openthread_host_connection_config_t;
 
 /**

+ 3 - 0
components/openthread/linker.lf

@@ -8,3 +8,6 @@ entries:
     link_metrics (noflash_text)
     mac (noflash_text)
     sub_mac (noflash_text)
+
+  if OPENTHREAD_RCP_SPI = y:
+    ncp_spi (noflash_text)

+ 22 - 6
components/openthread/port/esp_openthread_platform.cpp

@@ -14,6 +14,7 @@
 #include "esp_openthread_flash.h"
 #include "esp_openthread_lock.h"
 #include "esp_openthread_radio.h"
+#include "esp_openthread_spi_slave.h"
 #include "esp_openthread_task_queue.h"
 #include "esp_openthread_types.h"
 #include "esp_openthread_uart.h"
@@ -87,30 +88,40 @@ void esp_openthread_platform_workflow_unregister(const char *name)
 esp_err_t esp_openthread_platform_init(const esp_openthread_platform_config_t *config)
 {
     ESP_RETURN_ON_FALSE(config->radio_config.radio_mode == RADIO_MODE_NATIVE ||
-                            config->radio_config.radio_mode == RADIO_MODE_UART_RCP,
+                            config->radio_config.radio_mode == RADIO_MODE_UART_RCP ||
+                            config->radio_config.radio_mode == RADIO_MODE_SPI_RCP,
                         ESP_ERR_INVALID_ARG, OT_PLAT_LOG_TAG, "Radio mode not supported");
     ESP_RETURN_ON_FALSE(config->host_config.host_connection_mode == HOST_CONNECTION_MODE_NONE ||
                             config->host_config.host_connection_mode == HOST_CONNECTION_MODE_CLI_UART ||
-                            config->host_config.host_connection_mode == HOST_CONNECTION_MODE_RCP_UART,
+                            config->host_config.host_connection_mode == HOST_CONNECTION_MODE_RCP_UART ||
+                            config->host_config.host_connection_mode == HOST_CONNECTION_MODE_RCP_SPI,
                         ESP_ERR_INVALID_ARG, OT_PLAT_LOG_TAG, "Host connection mode not supported");
     ESP_RETURN_ON_FALSE(!s_openthread_platform_initialized, ESP_ERR_INVALID_STATE, OT_PLAT_LOG_TAG,
                         "OpenThread platform already initialized");
 
     s_openthread_platform_initialized = true;
     esp_err_t ret = ESP_OK;
+
+/* Avoid to compile flash in RADIO type device */
+#if !CONFIG_OPENTHREAD_RADIO
     const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY,
                                                                 config->port_config.storage_partition_name);
-
     ESP_RETURN_ON_FALSE(partition, ESP_ERR_INVALID_ARG, OT_PLAT_LOG_TAG, "OpenThread storage partition not found");
+    esp_openthread_flash_set_partition(partition);
+#endif
 
     s_platform_config = *config;
-    esp_openthread_flash_set_partition(partition);
     ESP_GOTO_ON_ERROR(esp_openthread_lock_init(), exit, OT_PLAT_LOG_TAG, "esp_openthread_lock_init failed");
     ESP_GOTO_ON_ERROR(esp_openthread_alarm_init(), exit, OT_PLAT_LOG_TAG, "esp_openthread_alarm_init failed");
-    if (config->host_config.host_connection_mode == HOST_CONNECTION_MODE_CLI_UART ||
+
+    if (config->host_config.host_connection_mode == HOST_CONNECTION_MODE_RCP_SPI) {
+        ESP_GOTO_ON_ERROR(esp_openthread_spi_slave_init(config), exit, OT_PLAT_LOG_TAG,
+                          "esp_openthread_spi_slave_init failed");
+    }else if (config->host_config.host_connection_mode == HOST_CONNECTION_MODE_CLI_UART ||
         config->host_config.host_connection_mode == HOST_CONNECTION_MODE_RCP_UART) {
         ESP_GOTO_ON_ERROR(esp_openthread_uart_init(config), exit, OT_PLAT_LOG_TAG, "esp_openthread_uart_init failed");
     }
+
     ESP_GOTO_ON_ERROR(esp_openthread_task_queue_init(config), exit, OT_PLAT_LOG_TAG,
                       "esp_openthread_task_queue_init failed");
     ESP_GOTO_ON_ERROR(esp_openthread_radio_init(config), exit, OT_PLAT_LOG_TAG, "esp_openthread_radio_init failed");
@@ -135,9 +146,14 @@ esp_err_t esp_openthread_platform_deinit(void)
     s_openthread_platform_initialized = false;
     esp_openthread_task_queue_deinit();
     esp_openthread_radio_deinit();
-    if (s_platform_config.host_config.host_connection_mode == HOST_CONNECTION_MODE_CLI_UART) {
+
+    if (s_platform_config.host_config.host_connection_mode == HOST_CONNECTION_MODE_RCP_SPI){
+        esp_openthread_spi_slave_deinit();
+    }else if (s_platform_config.host_config.host_connection_mode == HOST_CONNECTION_MODE_CLI_UART ||
+        s_platform_config.host_config.host_connection_mode == HOST_CONNECTION_MODE_RCP_UART) {
         esp_openthread_uart_deinit();
     }
+
     esp_openthread_lock_deinit();
     esp_openthread_alarm_deinit();
     return ESP_OK;

+ 9 - 0
components/openthread/port/esp_openthread_radio.c

@@ -736,3 +736,12 @@ otError otPlatEntropyGet(uint8_t *aOutput, uint16_t aOutputLength)
 
     return OT_ERROR_NONE;
 }
+
+otError otPlatRadioSetChannelMaxTransmitPower(otInstance *aInstance, uint8_t aChannel, int8_t aMaxPower)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    OT_UNUSED_VARIABLE(aChannel);
+    OT_UNUSED_VARIABLE(aMaxPower);
+
+    return OT_ERROR_NONE;
+}

+ 17 - 5
components/openthread/port/esp_openthread_radio_uart.cpp → components/openthread/port/esp_openthread_radio_spinel.cpp

@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
+ * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
  *
  * SPDX-License-Identifier: Apache-2.0
  */
@@ -13,6 +13,7 @@
 #include "esp_openthread_platform.h"
 #include "esp_openthread_types.h"
 #include "esp_system.h"
+#include "esp_spi_spinel_interface.hpp"
 #include "esp_uart_spinel_interface.hpp"
 #include "openthread-core-config.h"
 #include "lib/spinel/radio_spinel.hpp"
@@ -20,19 +21,30 @@
 #include "openthread/platform/diag.h"
 #include "openthread/platform/radio.h"
 
-using esp::openthread::UartSpinelInterface;
 using ot::Spinel::RadioSpinel;
 
+#if CONFIG_OPENTHREAD_RADIO_SPINEL_UART
+using esp::openthread::UartSpinelInterface;
 static RadioSpinel<UartSpinelInterface, esp_openthread_mainloop_context_t> s_radio;
-static const char *radiouart_workflow = "radio_uart";
+#else // CONFIG_OPENTHREAD_RADIO_SPINEL_SPI
+using esp::openthread::SpiSpinelInterface;
+static RadioSpinel<SpiSpinelInterface, esp_openthread_mainloop_context_t> s_radio;
+#endif // CONFIG_OPENTHREAD_RADIO_SPINEL_UART
+
+static const char *radiospinel_workflow = "radio_spinel";
 
 esp_err_t esp_openthread_radio_init(const esp_openthread_platform_config_t *config)
 {
+#if CONFIG_OPENTHREAD_RADIO_SPINEL_UART
     ESP_RETURN_ON_ERROR(s_radio.GetSpinelInterface().Init(config->radio_config.radio_uart_config), OT_PLAT_LOG_TAG,
                         "Spinel interface init falied");
+#else   // CONFIG_OPENTHREAD_RADIO_SPINEL_SPI
+    ESP_RETURN_ON_ERROR(s_radio.GetSpinelInterface().Init(config->radio_config.radio_spi_config), OT_PLAT_LOG_TAG,
+                        "Spinel interface init failed");
+#endif  // CONFIG_OPENTHREAD_RADIO_SPINEL_UART
     s_radio.Init(/*reset_radio=*/true, /*restore_dataset_from_ncp=*/false, /*skip_rcp_compatibility_check=*/false);
     return esp_openthread_platform_workflow_register(&esp_openthread_radio_update, &esp_openthread_radio_process,
-                                                     radiouart_workflow);
+                                                     radiospinel_workflow);
 }
 
 void esp_openthread_register_rcp_failure_handler(esp_openthread_rcp_failure_handler handler)
@@ -48,7 +60,7 @@ void esp_openthread_rcp_deinit(void)
 void esp_openthread_radio_deinit(void)
 {
     s_radio.Deinit();
-    esp_openthread_platform_workflow_unregister(radiouart_workflow);
+    esp_openthread_platform_workflow_unregister(radiospinel_workflow);
 }
 
 esp_err_t esp_openthread_radio_process(otInstance *instance, const esp_openthread_mainloop_context_t *mainloop)

+ 162 - 0
components/openthread/port/esp_openthread_spi_slave.c

@@ -0,0 +1,162 @@
+/*
+ * SPDX-FileCopyrightText: 2022-2023 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"
+#include "esp_check.h"
+#include "esp_err.h"
+#include "esp_openthread_common_macro.h"
+#include "esp_openthread_task_queue.h"
+#include "esp_openthread_types.h"
+#include "esp_rom_sys.h"
+#include <stdint.h>
+#include <string.h>
+#include "driver/gpio.h"
+#include "driver/spi_slave.h"
+#include "esp_private/cache_utils.h"
+#include "esp_private/spi_slave_internal.h"
+#include "ncp/ncp_config.h"
+#include "openthread/error.h"
+
+static const char *SPI_SLAVE_TAG = "spi_slave";
+
+static void *s_context = NULL;
+static uint8_t *s_prev_output_buf;
+static uint16_t s_prev_output_len;
+static uint8_t *s_prev_input_buf;
+static uint16_t s_prev_input_len;
+static bool s_request_transaction = false;
+
+static esp_openthread_spi_slave_config_t s_spi_config;
+static otPlatSpiSlaveTransactionProcessCallback s_process_callback = NULL;
+static otPlatSpiSlaveTransactionCompleteCallback s_complete_callback = NULL;
+static spi_slave_transaction_t s_spi_transaction;
+
+typedef struct {
+    uint16_t output_buf_len;
+    uint16_t input_buf_len;
+} pending_transaction_t;
+
+static void IRAM_ATTR handle_spi_setup_done(spi_slave_transaction_t *trans)
+{
+    if (s_request_transaction) {
+        gpio_set_level(s_spi_config.intr_pin, 0);
+    }
+}
+
+static void IRAM_ATTR handle_spi_transaction_done(spi_slave_transaction_t *trans)
+{
+    gpio_set_level(s_spi_config.intr_pin, 1);
+    pending_transaction_t *pending_transaction = (pending_transaction_t *)&(trans->user);
+    trans->trans_len /= CHAR_BIT;
+
+    if (s_complete_callback &&
+        s_complete_callback(s_context, (uint8_t *)trans->tx_buffer, pending_transaction->output_buf_len,
+                            trans->rx_buffer, 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_spi_slave_init(const esp_openthread_platform_config_t *config)
+{
+    s_spi_config = config->host_config.spi_slave_config;
+    gpio_config_t io_conf = {
+        .intr_type = GPIO_INTR_DISABLE,
+        .mode = GPIO_MODE_OUTPUT,
+        .pin_bit_mask = (1 << s_spi_config.intr_pin),
+    };
+    ESP_RETURN_ON_ERROR(gpio_config(&io_conf), OT_PLAT_LOG_TAG, "fail to configure SPI gpio");
+
+    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);
+
+    /* 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");
+
+    return ESP_OK;
+}
+
+void esp_openthread_spi_slave_deinit(void)
+{
+    spi_slave_free(s_spi_config.host_device);
+    s_spi_config.slave_config.post_setup_cb = NULL;
+    s_spi_config.slave_config.post_trans_cb = NULL;
+    return;
+}
+
+otError otPlatSpiSlaveEnable(otPlatSpiSlaveTransactionCompleteCallback aCompleteCallback,
+                             otPlatSpiSlaveTransactionProcessCallback aProcessCallback, void *aContext)
+{
+    s_process_callback = aProcessCallback;
+    s_complete_callback = aCompleteCallback;
+    s_context = aContext;
+    return OT_ERROR_NONE;
+}
+
+otError IRAM_ATTR otPlatSpiSlavePrepareTransaction(uint8_t *aOutputBuf, uint16_t aOutputBufLen, uint8_t *aInputBuf,
+                                                   uint16_t aInputBufLen, bool aRequestTransactionFlag)
+{
+    esp_err_t trans_state = ESP_OK;
+    pending_transaction_t *pending_transaction = NULL;
+    if (aOutputBuf == NULL) {
+        aOutputBuf = s_prev_output_buf;
+        aOutputBufLen = s_prev_output_len;
+    }
+    if (aInputBuf == NULL) {
+        aInputBuf = s_prev_input_buf;
+        aInputBufLen = s_prev_input_len;
+    }
+    s_prev_output_buf = aOutputBuf;
+    s_prev_output_len = aOutputBufLen;
+    s_prev_input_buf = aInputBuf;
+    s_prev_input_len = aInputBufLen;
+
+    s_spi_transaction.length = aOutputBufLen > aInputBufLen ? aOutputBufLen : aInputBufLen;
+    s_spi_transaction.length *= CHAR_BIT;
+    s_spi_transaction.rx_buffer = aInputBuf;
+    s_spi_transaction.tx_buffer = aOutputBuf;
+
+    assert(sizeof(s_spi_transaction.user) >= sizeof(pending_transaction_t));
+    pending_transaction = (pending_transaction_t *)&(s_spi_transaction.user);
+    pending_transaction->input_buf_len = aInputBufLen;
+    pending_transaction->output_buf_len = aOutputBufLen;
+    s_spi_transaction.user = pending_transaction;
+    s_request_transaction = aRequestTransactionFlag;
+
+    if ((gpio_get_level(s_spi_config.slave_config.spics_io_num) == 0)) {
+        ESP_EARLY_LOGE(SPI_SLAVE_TAG, "SPI busy");
+        return OT_ERROR_BUSY;
+    }
+
+    if (xPortCanYield()) {
+        spi_slave_queue_reset(s_spi_config.host_device);
+        trans_state = spi_slave_queue_trans(s_spi_config.host_device, &s_spi_transaction, 0);
+    } else {
+        spi_slave_queue_reset_isr(s_spi_config.host_device);
+        trans_state = spi_slave_queue_trans_isr(s_spi_config.host_device, &s_spi_transaction);
+    }
+
+    if (trans_state == ESP_OK) {
+        return OT_ERROR_NONE;
+    } else {
+        return OT_ERROR_FAILED;
+    }
+}

+ 13 - 3
components/openthread/port/esp_openthread_task_queue.c

@@ -13,6 +13,7 @@
 #include "esp_vfs.h"
 #include "esp_vfs_eventfd.h"
 #include "freertos/FreeRTOS.h"
+#include "freertos/portmacro.h"
 #include "freertos/queue.h"
 
 static QueueHandle_t s_task_queue = NULL;
@@ -36,7 +37,7 @@ esp_err_t esp_openthread_task_queue_init(const esp_openthread_platform_config_t
                                                      &esp_openthread_task_queue_process, task_queue_workflow);
 }
 
-esp_err_t esp_openthread_task_queue_post(esp_openthread_task_t task, void *arg)
+esp_err_t IRAM_ATTR esp_openthread_task_queue_post(esp_openthread_task_t task, void *arg)
 {
     task_storage_t task_storage = {
         .task = task,
@@ -44,12 +45,21 @@ esp_err_t esp_openthread_task_queue_post(esp_openthread_task_t task, void *arg)
     };
     uint64_t val = 1;
     ssize_t ret;
+    BaseType_t task_woken = pdFALSE;
 
-    ESP_RETURN_ON_FALSE(xQueueSend(s_task_queue, &task_storage, portMAX_DELAY), ESP_FAIL, OT_PLAT_LOG_TAG,
-                        "Failed to post task to OpenThread task queue");
+    if (!xPortCanYield()) {
+        ESP_RETURN_ON_FALSE_ISR(xQueueSendFromISR(s_task_queue, &task_storage, &task_woken), ESP_FAIL, OT_PLAT_LOG_TAG,
+                                "Failed to post task to OpenThread task queue");
+    } else {
+        ESP_RETURN_ON_FALSE(xQueueSend(s_task_queue, &task_storage, portMAX_DELAY), ESP_FAIL, OT_PLAT_LOG_TAG,
+                            "Failed to post task to OpenThread task queue");
+    }
     ret = write(s_task_queue_event_fd, &val, sizeof(val));
     assert(ret == sizeof(val));
 
+    if (task_woken) {
+        portYIELD_FROM_ISR_NO_ARG();
+    }
     return ESP_OK;
 }
 

+ 3 - 1
components/openthread/port/esp_openthread_uart.c

@@ -27,6 +27,7 @@ static int s_uart_fd;
 static uint8_t s_uart_buffer[ESP_OPENTHREAD_UART_BUFFER_SIZE];
 static const char *uart_workflow = "uart";
 
+#if (CONFIG_OPENTHREAD_CLI || (CONFIG_OPENTHREAD_RADIO && CONFIG_OPENTHREAD_RCP_UART))
 otError otPlatUartEnable(void)
 {
     return OT_ERROR_NONE;
@@ -54,6 +55,7 @@ otError otPlatUartSend(const uint8_t *buf, uint16_t buf_length)
 
     return OT_ERROR_NONE;
 }
+#endif
 
 esp_err_t esp_openthread_uart_init_port(const esp_openthread_uart_config_t *config)
 {
@@ -117,7 +119,7 @@ esp_err_t esp_openthread_uart_process(otInstance *instance, const esp_openthread
     int rval = read(s_uart_fd, s_uart_buffer, sizeof(s_uart_buffer));
 
     if (rval > 0) {
-#if CONFIG_OPENTHREAD_CLI || CONFIG_OPENTHREAD_RADIO
+#if (CONFIG_OPENTHREAD_CLI || (CONFIG_OPENTHREAD_RADIO && CONFIG_OPENTHREAD_RCP_UART))
         otPlatUartReceived(s_uart_buffer, (uint16_t)rval);
 #endif
     } else if (rval < 0) {

+ 252 - 0
components/openthread/port/esp_spi_spinel_interface.cpp

@@ -0,0 +1,252 @@
+/*
+ * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "esp_spi_spinel_interface.hpp"
+
+#include "error.h"
+#include "esp_check.h"
+#include "esp_openthread_common_macro.h"
+#include "esp_rom_sys.h"
+#include "esp_vfs.h"
+#include "esp_vfs_eventfd.h"
+#include <stdint.h>
+#include "common/logging.hpp"
+#include "driver/gpio.h"
+#include "driver/spi_master.h"
+#include "hal/gpio_types.h"
+#include "ncp/ncp_spi.hpp"
+
+using ot::Ncp::SpiFrame;
+using ot::Spinel::SpinelInterface;
+
+namespace esp {
+namespace openthread {
+
+SpiSpinelInterface::SpiSpinelInterface(SpinelInterface::ReceiveFrameCallback callback, void *callback_context,
+                                       SpinelInterface::RxFrameBuffer &frame_buffer)
+    : m_event_fd(-1)
+    , m_receiver_frame_callback(callback)
+    , m_receiver_frame_context(callback_context)
+    , m_receive_frame_buffer(frame_buffer)
+    , mRcpFailureHandler(nullptr)
+{
+}
+
+esp_err_t SpiSpinelInterface::Init(const esp_openthread_spi_host_config_t &spi_config)
+{
+    ESP_RETURN_ON_FALSE(m_event_fd < 0, ESP_ERR_INVALID_STATE, OT_PLAT_LOG_TAG, "event fd was initialized");
+    m_spi_config = spi_config;
+    ESP_RETURN_ON_ERROR(spi_bus_initialize(spi_config.host_device, &spi_config.spi_interface, SPI_DMA_CH_AUTO),
+                        OT_PLAT_LOG_TAG, "fail to initialize spi bus");
+    ESP_RETURN_ON_ERROR(spi_bus_add_device(spi_config.host_device, &spi_config.spi_device, &m_device), OT_PLAT_LOG_TAG,
+                        "fail to add spi bus device");
+
+    gpio_config_t io_conf;
+    memset(&io_conf, 0, sizeof(io_conf));
+    io_conf.intr_type = GPIO_INTR_NEGEDGE;
+    io_conf.pin_bit_mask = (1ULL << spi_config.intr_pin);
+    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");
+    ESP_RETURN_ON_ERROR(gpio_install_isr_service(0), OT_PLAT_LOG_TAG, "fail to install gpio isr service");
+    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;
+    m_event_fd = eventfd(0, EFD_SUPPORT_ISR);
+    m_pending_data_len = 0;
+
+    ESP_RETURN_ON_FALSE(m_event_fd >= 0, ESP_FAIL, OT_PLAT_LOG_TAG, "fail to get event fd");
+
+    ESP_LOGI(OT_PLAT_LOG_TAG, "spinel SPI interface initialization completed");
+
+    return ConductSPITransaction(true, 0, 0);
+}
+
+esp_err_t SpiSpinelInterface::Deinit(void)
+{
+    if (m_event_fd >= 0) {
+        close(m_event_fd);
+        m_event_fd = -1;
+
+        ESP_RETURN_ON_ERROR(gpio_isr_handler_remove(m_spi_config.intr_pin), OT_PLAT_LOG_TAG,
+                            "fail to remove gpio isr handler");
+        ESP_RETURN_ON_ERROR(spi_bus_remove_device(m_device), OT_PLAT_LOG_TAG, "fail to remove spi bus device");
+        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();
+    }
+    return ESP_OK;
+}
+
+SpiSpinelInterface::~SpiSpinelInterface(void)
+{
+    Deinit();
+}
+
+otError SpiSpinelInterface::SendFrame(const uint8_t *frame, uint16_t length)
+{
+    ESP_RETURN_ON_FALSE(frame, OT_ERROR_INVALID_ARGS, OT_PLAT_LOG_TAG, "empty frame");
+    ESP_RETURN_ON_FALSE(length <= SpinelInterface::kMaxFrameSize, OT_ERROR_NO_BUFS, OT_PLAT_LOG_TAG,
+                        "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
+    if (ConductSPITransaction(false, length, rx_data_size) == ESP_OK) {
+        return OT_ERROR_NONE;
+    } else {
+        return OT_ERROR_FAILED;
+    }
+}
+
+esp_err_t SpiSpinelInterface::ConductSPITransaction(bool reset, uint16_t tx_data_size, uint16_t rx_data_size)
+{
+    ESP_RETURN_ON_FALSE(tx_data_size <= kSPIFrameSize && rx_data_size <= kSPIFrameSize, ESP_ERR_INVALID_ARG,
+                        OT_PLAT_LOG_TAG, "invalid arguments");
+
+    SpiFrame tx_frame(m_tx_buffer);
+
+    tx_frame.SetHeaderFlagByte(reset);
+    tx_frame.SetHeaderDataLen(tx_data_size);
+    tx_frame.SetHeaderAcceptLen(rx_data_size);
+
+    uint8_t *rx_buffer;
+    otError err = m_receive_frame_buffer.SetSkipLength(kSPIFrameHeaderSize);
+
+    ESP_RETURN_ON_FALSE(err == OT_ERROR_NONE, ESP_ERR_NO_MEM, OT_PLAT_LOG_TAG, "buffer space is insufficient");
+
+    rx_buffer = m_receive_frame_buffer.GetFrame() - kSPIFrameHeaderSize;
+    if (m_receive_frame_buffer.GetFrameMaxLength() < rx_data_size) {
+        rx_data_size = m_receive_frame_buffer.GetFrameMaxLength();
+    }
+    uint16_t data_size = tx_data_size > rx_data_size ? tx_data_size : rx_data_size;
+    data_size += kSPIFrameHeaderSize;
+
+    spi_transaction_t transaction;
+    memset(&transaction, 0, sizeof(transaction));
+    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;
+
+    ESP_RETURN_ON_ERROR(spi_device_polling_transmit(m_device, &transaction), OT_PLAT_LOG_TAG, "SPI transaction failed");
+    SpiFrame rx_frame(rx_buffer);
+
+    if (!rx_frame.IsValid() || rx_frame.GetHeaderAcceptLen() > kSPIFrameSize ||
+        rx_frame.GetHeaderDataLen() > kSPIFrameSize) {
+        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");
+    }
+
+    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");
+    }
+
+    if (rx_frame.GetHeaderDataLen() > 0 && rx_frame.GetHeaderDataLen() < tx_frame.GetHeaderAcceptLen()) {
+        if (gpio_get_level(m_spi_config.intr_pin) == 1) {
+            m_pending_data_len = 0;
+        }
+        if (m_receive_frame_buffer.SetLength(rx_frame.GetHeaderDataLen()) != OT_ERROR_NONE) {
+            ESP_LOGW(OT_PLAT_LOG_TAG, "insufficient buffer space to hold a frame of length %d...",
+                     rx_frame.GetHeaderDataLen());
+            m_receive_frame_buffer.DiscardFrame();
+            return ESP_ERR_NO_MEM;
+        }
+        m_receiver_frame_callback(m_receiver_frame_context);
+    } else {
+        m_pending_data_len = 0;
+        m_receive_frame_buffer.DiscardFrame();
+    }
+    m_pending_data_len = 0;
+
+    return ESP_OK;
+}
+
+void SpiSpinelInterface::GpioIntrHandler(void *arg)
+{
+    SpiSpinelInterface *instance = static_cast<SpiSpinelInterface *>(arg);
+    instance->m_pending_data_len = SpinelInterface::kMaxFrameSize;
+    uint64_t event = SpinelInterface::kMaxFrameSize;
+    write(instance->m_event_fd, &event, sizeof(event));
+}
+
+void SpiSpinelInterface::Update(esp_openthread_mainloop_context_t &mainloop)
+{
+    if (m_pending_data_len > 0) {
+        mainloop.timeout.tv_sec = 0;
+        mainloop.timeout.tv_usec = 0;
+    }
+    FD_SET(m_event_fd, &mainloop.read_fds);
+    FD_SET(m_event_fd, &mainloop.error_fds);
+    if (m_event_fd > mainloop.max_fd) {
+        mainloop.max_fd = m_event_fd;
+    }
+}
+
+void SpiSpinelInterface::Process(const esp_openthread_mainloop_context_t &mainloop)
+{
+    if (FD_ISSET(m_event_fd, &mainloop.error_fds)) {
+        ESP_LOGE(OT_PLAT_LOG_TAG, "SPI INTR GPIO error event");
+        return;
+    }
+    if (FD_ISSET(m_event_fd, &mainloop.read_fds)) {
+        uint64_t event;
+        read(m_event_fd, &event, sizeof(event));
+        m_pending_data_len = SpinelInterface::kMaxFrameSize;
+
+        if (ConductSPITransaction(false, 0, m_pending_data_len) != ESP_OK) {
+            ESP_LOGW(OT_PLAT_LOG_TAG, "fail to process SPI transaction");
+        }
+    }
+    return;
+}
+
+otError SpiSpinelInterface::WaitForFrame(uint64_t timeout_us)
+{
+    fd_set read_fds, error_fds;
+    struct timeval timeout;
+    uint64_t event = 0;
+    if (m_pending_data_len == 0) {
+        FD_ZERO(&read_fds);
+        FD_ZERO(&error_fds);
+        FD_SET(m_event_fd, &read_fds);
+        FD_SET(m_event_fd, &error_fds);
+        timeout.tv_sec = timeout_us / US_PER_S;
+        timeout.tv_usec = timeout_us % US_PER_S;
+
+        int ret = select(m_event_fd + 1, &read_fds, NULL, &error_fds, &timeout);
+        if (ret <= 0 || !FD_ISSET(m_event_fd, &read_fds)) {
+            if (FD_ISSET(m_event_fd, &error_fds)) {
+                ESP_LOGW(OT_PLAT_LOG_TAG, "FD error!\n");
+            }
+            ESP_LOGW(OT_PLAT_LOG_TAG, "SPI transaction timeout for %llu us, result %d\n", timeout_us, ret);
+            return OT_ERROR_RESPONSE_TIMEOUT;
+        }
+        read(m_event_fd, &event, sizeof(event));
+    }
+
+    ESP_RETURN_ON_FALSE(ConductSPITransaction(false, 0, SpinelInterface::kMaxFrameSize) == ESP_OK, OT_ERROR_FAILED,
+                        OT_PLAT_LOG_TAG, "fail to complete SPI transaction during wait for frame");
+    return OT_ERROR_NONE;
+}
+
+void SpiSpinelInterface::OnRcpReset(void)
+{
+    if (mRcpFailureHandler) {
+        mRcpFailureHandler();
+        ConductSPITransaction(true, 0, 0); // clear
+    }
+
+} // namespace openthread
+} // namespace esp
+}

+ 12 - 1
components/openthread/port/esp_uart_spinel_interface.cpp

@@ -45,12 +45,15 @@ UartSpinelInterface::~UartSpinelInterface(void)
 
 esp_err_t UartSpinelInterface::Init(const esp_openthread_uart_config_t &radio_uart_config)
 {
+    esp_err_t error = ESP_OK;
     m_uart_rx_buffer = static_cast<uint8_t *>(heap_caps_malloc(kMaxFrameSize, MALLOC_CAP_8BIT));
     if (m_uart_rx_buffer == NULL) {
         return ESP_ERR_NO_MEM;
     }
 
-    return InitUart(radio_uart_config);
+    error = InitUart(radio_uart_config);
+    ESP_LOGI(OT_PLAT_LOG_TAG, "spinel UART interface initialization completed");
+    return error;
 }
 
 esp_err_t UartSpinelInterface::Deinit(void)
@@ -282,5 +285,13 @@ esp_err_t UartSpinelInterface::TryRecoverUart(void)
     return ESP_OK;
 }
 
+void UartSpinelInterface::OnRcpReset(void)
+{
+    if (mRcpFailureHandler) {
+        mRcpFailureHandler();
+        TryRecoverUart();
+    }
+}
+
 } // namespace openthread
 } // namespace esp

+ 31 - 0
components/openthread/private_include/esp_openthread_spi_slave.h

@@ -0,0 +1,31 @@
+/*
+ * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+
+#pragma once
+
+#include "esp_openthread.h"
+#include "esp_openthread_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief This function initializes the OpenThread spinel SPI slave.
+ *
+ */
+esp_err_t esp_openthread_spi_slave_init(const esp_openthread_platform_config_t *config);
+
+/**
+ * @brief This function deinitializes the OpenThread spinel SPI slave.
+ *
+ */
+void esp_openthread_spi_slave_deinit(void);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif

+ 144 - 0
components/openthread/private_include/esp_spi_spinel_interface.hpp

@@ -0,0 +1,144 @@
+/*
+ * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#pragma once
+
+#include "esp_err.h"
+#include "esp_openthread_types.h"
+#include "driver/spi_master.h"
+#include "lib/spinel/spinel_interface.hpp"
+
+namespace esp {
+namespace openthread {
+
+class SpiSpinelInterface {
+public:
+    /**
+     * @brief   This constructor of object.
+     *
+     * @param[in] callback         Callback on frame received
+     * @param[in] callback_context  Callback context
+     * @param[in] frame_buffer      A reference to a `RxFrameBuffer` object.
+     *
+     */
+    SpiSpinelInterface(ot::Spinel::SpinelInterface::ReceiveFrameCallback callback, void *callback_context,
+                       ot::Spinel::SpinelInterface::RxFrameBuffer &frame_buffer);
+
+    /**
+     * @brief   This destructor of the object.
+     *
+     */
+    ~SpiSpinelInterface(void);
+
+    /**
+     * @brief   This method initializes the spinel interface.
+     *
+     * @return
+     *      - ESP_OK on success
+     *      - ESP_ERR_INVALID_STATE if already initialized
+     *      - ESP_ERR_NO_MEM if allocation has failed
+     *      - ESP_FAIL on failure
+     */
+    esp_err_t Init(const esp_openthread_spi_host_config_t &spi_config);
+
+    /**
+     * @brief  This method deinitializes the HDLC interface.
+     *
+     * @return
+     *      - ESP_OK on success
+     *      - ESP_FAIL on failure
+     */
+    esp_err_t Deinit(void);
+
+    /**
+     * @brief   This method encodes and sends a spinel frame to Radio Co-processor (RCP) over the socket.
+     *
+     * @param[in] frame     A pointer to buffer containing the spinel frame to send.
+     * @param[in] length    The length (number of bytes) in the frame.
+     *
+     * @return
+     *      -OT_ERROR_NONE     Successfully encoded and sent the spinel frame.
+     *      -OT_ERROR_NO_BUFS  Insufficient buffer space available to encode the frame.
+     *      -OT_ERROR_FAILED   Failed to send due to socket not becoming writable within `kMaxWaitTime`.
+     *
+     */
+    otError SendFrame(const uint8_t *frame, uint16_t length);
+
+    /**
+     * This method waits for receiving part or all of spinel frame within specified timeout.
+     *
+     * @param[in]  timeout_us  The timeout value in microseconds.
+     *
+     * @return
+     *      -OT_ERROR_NONE             Part or all of spinel frame is received.
+     *      -OT_ERROR_RESPONSE_TIMEOUT No spinel frame is received within @p timeout_us.
+     *
+     */
+    otError WaitForFrame(uint64_t timeout_us);
+
+    /**
+     * This method performs spi processing to the RCP.
+     *
+     * @param[in]  mainloop     The mainloop context
+     *
+     */
+    void Process(const esp_openthread_mainloop_context_t &mainloop);
+
+    /**
+     * This methods updates the mainloop context.
+     *
+     * @param[inout] mainloop   The mainloop context.
+     *
+     */
+    void Update(esp_openthread_mainloop_context_t &mainloop);
+
+    /**
+     * This methods registers the callback for RCP failure.
+     *
+     * @param[in] handler   The RCP failure handler.
+     *
+     */
+    void RegisterRcpFailureHandler(esp_openthread_rcp_failure_handler handler) { mRcpFailureHandler = handler; }
+
+    /**
+     * This method is called when RCP is reset to recreate the connection with it.
+     * Intentionally empty.
+     *
+     */
+    otError ResetConnection(void) { return OT_ERROR_NONE; }
+
+    /**
+     * This method is called when RCP failure detected and resets internal states of the interface.
+     *
+     */
+    void OnRcpReset(void);
+
+private:
+    static constexpr uint8_t kSPIFrameHeaderSize = 5;
+    static constexpr uint16_t kSPIFrameSize = ot::Spinel::SpinelInterface::kMaxFrameSize + kSPIFrameHeaderSize;
+    static constexpr uint8_t kSmallPacketSize = 32;
+    static constexpr uint16_t kSPIDataEvent = 1;
+
+    static void GpioIntrHandler(void *arg);
+    esp_err_t ConductSPITransaction(bool reset, uint16_t tx_data_size, uint16_t rx_data_size);
+
+    esp_openthread_spi_host_config_t m_spi_config;
+    uint8_t m_tx_buffer[kSPIFrameSize];
+    int m_event_fd;
+    volatile uint16_t m_pending_data_len;
+
+    ot::Spinel::SpinelInterface::ReceiveFrameCallback m_receiver_frame_callback;
+    void *m_receiver_frame_context;
+    ot::Spinel::SpinelInterface::RxFrameBuffer &m_receive_frame_buffer;
+    bool m_has_pending_device_frame;
+
+    spi_device_handle_t m_device;
+
+    esp_openthread_rcp_failure_handler mRcpFailureHandler;
+};
+
+} // namespace openthread
+} // namespace esp

+ 1 - 6
components/openthread/private_include/esp_uart_spinel_interface.hpp

@@ -108,12 +108,7 @@ public:
      */
     void RegisterRcpFailureHandler(esp_openthread_rcp_failure_handler handler) { mRcpFailureHandler = handler; }
 
-    void OnRcpReset(void)
-    {
-        if (mRcpFailureHandler) {
-            mRcpFailureHandler();
-        }
-    }
+    void OnRcpReset(void);
 
     otError ResetConnection(void) { return OT_ERROR_NONE; }
 

+ 1 - 1
components/openthread/private_include/openthread-core-esp32x-ftd-config.h

@@ -224,7 +224,7 @@
  *
  */
 #ifndef OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT
-#define OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT 1
+#define OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT 3
 #endif
 
 /**

+ 2 - 2
components/openthread/private_include/openthread-core-esp32x-radio-config.h

@@ -88,7 +88,7 @@
  * Define to 1 to enable NCP SPI support.
  *
  */
-#define OPENTHREAD_CONFIG_NCP_SPI_ENABLE 0
+#define OPENTHREAD_CONFIG_NCP_SPI_ENABLE CONFIG_OPENTHREAD_RCP_SPI
 
 /**
  * @def OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
@@ -104,7 +104,7 @@
  * Define to 1 to enable NCP HDLC support.
  *
  */
-#define OPENTHREAD_CONFIG_NCP_HDLC_ENABLE 1
+#define OPENTHREAD_CONFIG_NCP_HDLC_ENABLE CONFIG_OPENTHREAD_RCP_UART
 
 /**
  * @def PACKAGE_NAME

+ 44 - 19
examples/openthread/ot_br/main/esp_ot_config.h

@@ -21,27 +21,52 @@
     {                                                      \
         .radio_mode = RADIO_MODE_NATIVE,                   \
     }
+#elif CONFIG_OPENTHREAD_RADIO_SPINEL_UART
+#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG()              \
+    {                                                      \
+        .radio_mode = RADIO_MODE_UART_RCP,                 \
+        .radio_uart_config = {                             \
+            .port = 1,                                     \
+            .uart_config =                                 \
+                {                                          \
+                    .baud_rate = 460800,                   \
+                    .data_bits = UART_DATA_8_BITS,         \
+                    .parity = UART_PARITY_DISABLE,         \
+                    .stop_bits = UART_STOP_BITS_1,         \
+                    .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, \
+                    .rx_flow_ctrl_thresh = 0,              \
+                    .source_clk = UART_SCLK_DEFAULT,       \
+                },                                         \
+            .rx_pin = 4,                                   \
+            .tx_pin = 5,                                   \
+        },                                                 \
+    }
 #else
-#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG()                        \
-    {                                                                \
-        .radio_mode = RADIO_MODE_UART_RCP,                           \
-        .radio_uart_config = {                                       \
-            .port = 1,                                               \
-            .uart_config =                                           \
-                {                                                    \
-                    .baud_rate = 460800,                             \
-                    .data_bits = UART_DATA_8_BITS,                   \
-                    .parity = UART_PARITY_DISABLE,                   \
-                    .stop_bits = UART_STOP_BITS_1,                   \
-                    .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,           \
-                    .rx_flow_ctrl_thresh = 0,                        \
-                    .source_clk = UART_SCLK_DEFAULT,                 \
-                },                                                   \
-            .rx_pin = 4,                                             \
-            .tx_pin = 5,                                             \
-        },                                                           \
+#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG()      \
+    {                                              \
+        .radio_mode = RADIO_MODE_SPI_RCP,          \
+        .radio_spi_config = {                      \
+            .host_device = SPI2_HOST,              \
+            .dma_channel = 2,                      \
+            .spi_interface =                       \
+                {                                  \
+                    .mosi_io_num = 11,             \
+                    .sclk_io_num = 12,             \
+                    .miso_io_num = 13,             \
+                },                                 \
+            .spi_device =                          \
+                {                                  \
+                    .cs_ena_pretrans = 2,          \
+                    .input_delay_ns = 100,         \
+                    .mode = 0,                     \
+                    .clock_speed_hz = 2500 * 1000, \
+                    .spics_io_num = 10,            \
+                    .queue_size = 5,               \
+                },                                 \
+            .intr_pin = 8,                         \
+        },                                         \
     }
-#endif
+#endif // CONFIG_OPENTHREAD_RADIO_SPINEL_UART OR  CONFIG_OPENTHREAD_RADIO_SPINEL_SPI
 
 #if CONFIG_IDF_TARGET_ESP32C2 && CONFIG_XTAL_FREQ_26
 #define HOST_BAUD_RATE 74880

+ 25 - 0
examples/openthread/ot_rcp/main/esp_ot_config.h

@@ -20,6 +20,7 @@
         .radio_mode = RADIO_MODE_NATIVE,                        \
     }
 
+#if CONFIG_OPENTHREAD_RCP_UART
 #if CONFIG_OPENTHREAD_UART_PIN_MANUAL
 #define OPENTHREAD_RCP_UART_RX_PIN CONFIG_OPENTHREAD_UART_RX_PIN
 #define OPENTHREAD_RCP_UART_TX_PIN CONFIG_OPENTHREAD_UART_TX_PIN
@@ -47,6 +48,30 @@
             .tx_pin = OPENTHREAD_RCP_UART_TX_PIN,               \
         },                                                      \
     }
+#else // CONFIG_OPENTHREAD_RCP_SPI
+#define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG()                    \
+    {                                                           \
+        .host_connection_mode = HOST_CONNECTION_MODE_RCP_SPI,   \
+        .spi_slave_config = {                                   \
+            .host_device = SPI2_HOST,                           \
+            .bus_config = {                                     \
+                .mosi_io_num = 3,                               \
+                .miso_io_num = 1,                               \
+                .sclk_io_num = 0,                               \
+                .quadhd_io_num = -1,                            \
+                .quadwp_io_num = -1,                            \
+                .isr_cpu_id = INTR_CPU_ID_0,                    \
+            },                                                  \
+            .slave_config = {                                   \
+                .mode = 0,                                      \
+                .spics_io_num = 2,                              \
+                .queue_size = 3,                                \
+                .flags = 0,                                     \
+            },                                                  \
+            .intr_pin = 9,                                      \
+        },                                                      \
+    }
+#endif
 
 #define ESP_OPENTHREAD_DEFAULT_PORT_CONFIG()    \
     {                                           \

+ 2 - 2
examples/openthread/ot_rcp/main/esp_ot_rcp.c

@@ -10,7 +10,7 @@
  * 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 <stdio.h>
 #include <unistd.h>
@@ -64,5 +64,5 @@ void app_main(void)
     ESP_ERROR_CHECK(nvs_flash_init());
     ESP_ERROR_CHECK(esp_event_loop_create_default());
     ESP_ERROR_CHECK(esp_vfs_eventfd_register(&eventfd_config));
-    xTaskCreate(ot_task_worker, "ot_rcp_main", 10240, xTaskGetCurrentTaskHandle(), 5, NULL);
+    xTaskCreate(ot_task_worker, "ot_rcp_main", 3072, xTaskGetCurrentTaskHandle(), 5, NULL);
 }