diff --git a/components/openthread/CMakeLists.txt b/components/openthread/CMakeLists.txt index 2290df57b24d..53eb0e607832 100644 --- a/components/openthread/CMakeLists.txt +++ b/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) diff --git a/components/openthread/Kconfig b/components/openthread/Kconfig index f8376b52bd9b..ffd461169b1f 100644 --- a/components/openthread/Kconfig +++ b/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 diff --git a/components/openthread/include/esp_openthread_types.h b/components/openthread/include/esp_openthread_types.h index 9c22a06edc46..a6903280ab34 100644 --- a/components/openthread/include/esp_openthread_types.h +++ b/components/openthread/include/esp_openthread_types.h @@ -11,6 +11,10 @@ #include #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; /** diff --git a/components/openthread/linker.lf b/components/openthread/linker.lf index 2704ecbe09c0..aa59cc670159 100644 --- a/components/openthread/linker.lf +++ b/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) diff --git a/components/openthread/port/esp_openthread_platform.cpp b/components/openthread/port/esp_openthread_platform.cpp index f8d79269bc44..2197807aee64 100644 --- a/components/openthread/port/esp_openthread_platform.cpp +++ b/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; diff --git a/components/openthread/port/esp_openthread_radio.c b/components/openthread/port/esp_openthread_radio.c index 2472da8b1434..1d376ec48d76 100644 --- a/components/openthread/port/esp_openthread_radio.c +++ b/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; +} diff --git a/components/openthread/port/esp_openthread_radio_uart.cpp b/components/openthread/port/esp_openthread_radio_spinel.cpp similarity index 90% rename from components/openthread/port/esp_openthread_radio_uart.cpp rename to components/openthread/port/esp_openthread_radio_spinel.cpp index cccf9bd1b151..392aee4307b2 100644 --- a/components/openthread/port/esp_openthread_radio_uart.cpp +++ b/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 s_radio; -static const char *radiouart_workflow = "radio_uart"; +#else // CONFIG_OPENTHREAD_RADIO_SPINEL_SPI +using esp::openthread::SpiSpinelInterface; +static RadioSpinel 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) diff --git a/components/openthread/port/esp_openthread_spi_slave.c b/components/openthread/port/esp_openthread_spi_slave.c new file mode 100644 index 000000000000..eb864d7f9ad2 --- /dev/null +++ b/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 + +#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 +#include +#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; + } +} diff --git a/components/openthread/port/esp_openthread_task_queue.c b/components/openthread/port/esp_openthread_task_queue.c index 659a829e6c63..4acfaad722b8 100644 --- a/components/openthread/port/esp_openthread_task_queue.c +++ b/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; } diff --git a/components/openthread/port/esp_openthread_uart.c b/components/openthread/port/esp_openthread_uart.c index 2400f24ee620..e3bbe75dd2bc 100644 --- a/components/openthread/port/esp_openthread_uart.c +++ b/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) { diff --git a/components/openthread/port/esp_spi_spinel_interface.cpp b/components/openthread/port/esp_spi_spinel_interface.cpp new file mode 100644 index 000000000000..b616b6ae75ed --- /dev/null +++ b/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 +#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(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 +} diff --git a/components/openthread/port/esp_uart_spinel_interface.cpp b/components/openthread/port/esp_uart_spinel_interface.cpp index e992665d05d8..061bc8044b7f 100644 --- a/components/openthread/port/esp_uart_spinel_interface.cpp +++ b/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(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 diff --git a/components/openthread/private_include/esp_openthread_spi_slave.h b/components/openthread/private_include/esp_openthread_spi_slave.h new file mode 100644 index 000000000000..c776ef083e73 --- /dev/null +++ b/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 diff --git a/components/openthread/private_include/esp_spi_spinel_interface.hpp b/components/openthread/private_include/esp_spi_spinel_interface.hpp new file mode 100644 index 000000000000..3cbb34f0de99 --- /dev/null +++ b/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 diff --git a/components/openthread/private_include/esp_uart_spinel_interface.hpp b/components/openthread/private_include/esp_uart_spinel_interface.hpp index d1d0e7a938e6..c5087b2c7b9b 100644 --- a/components/openthread/private_include/esp_uart_spinel_interface.hpp +++ b/components/openthread/private_include/esp_uart_spinel_interface.hpp @@ -108,12 +108,7 @@ class UartSpinelInterface { */ 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; } diff --git a/components/openthread/private_include/openthread-core-esp32x-ftd-config.h b/components/openthread/private_include/openthread-core-esp32x-ftd-config.h index 4f26e4dc2669..5b1b97ef011e 100644 --- a/components/openthread/private_include/openthread-core-esp32x-ftd-config.h +++ b/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 /** diff --git a/components/openthread/private_include/openthread-core-esp32x-radio-config.h b/components/openthread/private_include/openthread-core-esp32x-radio-config.h index 6e063415c98a..2d5424c37823 100644 --- a/components/openthread/private_include/openthread-core-esp32x-radio-config.h +++ b/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 diff --git a/examples/openthread/ot_br/main/esp_ot_config.h b/examples/openthread/ot_br/main/esp_ot_config.h index 3641f0a307a9..9e9953966a49 100644 --- a/examples/openthread/ot_br/main/esp_ot_config.h +++ b/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 diff --git a/examples/openthread/ot_rcp/main/esp_ot_config.h b/examples/openthread/ot_rcp/main/esp_ot_config.h index 89e25f62f463..24c434c12df5 100644 --- a/examples/openthread/ot_rcp/main/esp_ot_config.h +++ b/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() \ { \ diff --git a/examples/openthread/ot_rcp/main/esp_ot_rcp.c b/examples/openthread/ot_rcp/main/esp_ot_rcp.c index 67a17f3fd508..0ed8887ce77b 100644 --- a/examples/openthread/ot_rcp/main/esp_ot_rcp.c +++ b/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 #include @@ -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); }