From 8c87790e47b6aeccb6ec94cd77534cb0564442c8 Mon Sep 17 00:00:00 2001 From: Soh Kam Yung Date: Thu, 21 May 2026 10:26:27 +0800 Subject: [PATCH] feat(openthread) Add custom transport to OpenThread - add a custom transport with Tx() and Rx() callbacks in OpenThread config - OpenThread calls Tx() to transmit, Rx() to receive data - external driver provides the transport for sending data between OT RCP and Host - add an OpenThread call used by external driver to send a signal to OpenThread when there is incoming data --- components/openthread/Kconfig | 17 ++ .../include/esp_openthread_transport.h | 33 ++ .../openthread/include/esp_openthread_types.h | 33 +- .../esp_openthread_common_macro.h | 3 +- .../esp_openthread_transport_priv.h | 37 +++ .../esp_radio_spinel_custom.hpp | 193 ++++++++++++ .../openthread-core-esp32x-ftd-config.h | 4 +- .../openthread-core-esp32x-radio-config.h | 4 +- .../src/esp_openthread_platform.cpp | 14 + .../openthread/src/ncp/esp_openthread_ncp.cpp | 8 +- .../src/port/esp_openthread_radio_spinel.cpp | 44 ++- .../src/port/esp_openthread_state.c | 2 +- .../src/port/esp_openthread_transport_rcp.c | 143 +++++++++ .../src/spinel/esp_radio_spinel_custom.cpp | 281 ++++++++++++++++++ components/openthread/srcs_ftd_mtd.cmake | 18 +- components/openthread/srcs_radio.cmake | 7 + 16 files changed, 818 insertions(+), 23 deletions(-) create mode 100644 components/openthread/include/esp_openthread_transport.h create mode 100644 components/openthread/private_include/esp_openthread_transport_priv.h create mode 100644 components/openthread/private_include/esp_radio_spinel_custom.hpp create mode 100644 components/openthread/src/port/esp_openthread_transport_rcp.c create mode 100644 components/openthread/src/spinel/esp_radio_spinel_custom.cpp diff --git a/components/openthread/Kconfig b/components/openthread/Kconfig index 8e0d219e663..2438fc80f1c 100644 --- a/components/openthread/Kconfig +++ b/components/openthread/Kconfig @@ -201,6 +201,11 @@ menu "OpenThread" help Select this to connect to a Radio Co-Processor via SPI. + config OPENTHREAD_RADIO_SPINEL_CUSTOM + bool "Connect via Custom Transport" + help + Select this to connect to a Radio Co-Processor via a provided Custom Transport. + config OPENTHREAD_RADIO_154_NONE bool "Disable the Thread radio based on 15.4 link" help @@ -232,6 +237,11 @@ menu "OpenThread" bool "USB RCP" help Select this to enable connection to host over USB JTAG serial. + + config OPENTHREAD_RCP_CUSTOM + bool "Custom Transport RCP" + help + Select this to enable a custom transport connection to host endchoice config OPENTHREAD_NCP_VENDOR_HOOK @@ -500,6 +510,13 @@ menu "OpenThread" help Set the OpenThread UART buffer size. + config OPENTHREAD_TRANSPORT_BUFFER_SIZE + depends on OPENTHREAD_RCP_CUSTOM || OPENTHREAD_RADIO_SPINEL_CUSTOM + int "The custom transport received buffer size of openthread" + default 2048 + help + Set the OpenThread Custom transport buffer size. + config OPENTHREAD_MAC_MAX_CSMA_BACKOFFS_DIRECT int "Maximum backoffs times before declaring a channel access failure." default 4 diff --git a/components/openthread/include/esp_openthread_transport.h b/components/openthread/include/esp_openthread_transport.h new file mode 100644 index 00000000000..a0946f101bd --- /dev/null +++ b/components/openthread/include/esp_openthread_transport.h @@ -0,0 +1,33 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Wakes the OpenThread main loop on RCP when custom transport RX data is available. + * + * Call this from another task or ISR (after RX data has been queued for transport_rx) when using + * @ref esp_openthread_transport_config_t so select() returns without waiting for the full timeout. + * + */ +void esp_openthread_transport_notify_rcp_rx(void); + +/** + * @brief Wakes the OpenThread main loop on Host when custom transport RX data is available. + * + * Call this from another task or ISR (after RX data has been queued for transport_rx) when using + * @ref esp_openthread_transport_config_t so select() returns without waiting for the full timeout. + * + */ +void esp_openthread_transport_notify_host_rx(void); + +#ifdef __cplusplus +} // end of extern "C" +#endif diff --git a/components/openthread/include/esp_openthread_types.h b/components/openthread/include/esp_openthread_types.h index e384603b736..da4b9d3c63e 100644 --- a/components/openthread/include/esp_openthread_types.h +++ b/components/openthread/include/esp_openthread_types.h @@ -1,11 +1,12 @@ /* - * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #pragma once +#include #include #include @@ -129,6 +130,28 @@ typedef struct { gpio_num_t intr_pin; /*!< SPI interrupt pin */ } esp_openthread_spi_slave_config_t; +/** + * @brief The Transport config for OpenThread. + * + * @note An external transport implements the callback functions that OpenThread will call when it has data to send or receive. The data to be transported are HDLC frames, not raw Spinel Packets. + * + * - transport_tx: Send exactly @p len bytes from @p buf. Should complete synchronously before returning ESP_OK. + * + * - transport_rx: Non-blocking read of up to @p buf_size bytes into @p buf. On ESP_OK, set @p out_len to the number + * of bytes written (1 .. buf_size). Return ESP_ERR_TIMEOUT or another non-OK code when no data is available now. + * + * When RX data becomes available from another context (ISR or task), the external transport calls + * - `esp_openthread_transport_notify_rcp_rx()` (on RCP) or + * - `esp_openthread_transport_notify_host_rx()` (on Host) + * so the OpenThread task wakes promptly from select(). + * + */ +typedef struct { + esp_err_t (*transport_tx)(const void *buf, size_t len); /*!< Transmit */ + esp_err_t (*transport_rx)(void *buf, size_t buf_size, size_t *out_len); /*!< Non-blocking receive */ + uint32_t bus_speed; /*!< Transport speed */ +} esp_openthread_transport_config_t; + /** * @brief The radio mode of OpenThread. * @@ -138,6 +161,7 @@ typedef enum { RADIO_MODE_UART_RCP, /*!< UART connection to a 15.4 capable radio co-processor (RCP) */ RADIO_MODE_SPI_RCP, /*!< SPI connection to a 15.4 capable radio co-processor (RCP) */ RADIO_MODE_TREL, /*!< Use the Thread Radio Encapsulation Link (TREL) */ + RADIO_MODE_TRANSPORT, /*!< Use the custom transport to connect to a 15.4 capable radio co-processor (RCP) */ RADIO_MODE_MAX, /*!< Using for parameter check */ } esp_openthread_radio_mode_t; @@ -151,7 +175,8 @@ typedef enum { HOST_CONNECTION_MODE_CLI_USB, /*!< CLI USB connection to the host */ HOST_CONNECTION_MODE_RCP_UART, /*!< RCP UART connection to the host */ HOST_CONNECTION_MODE_RCP_SPI, /*!< RCP SPI connection to the host */ - HOST_CONNECTION_MODE_RCP_USB, /*!< RCP USB Serial JTAG connection to the host */ + HOST_CONNECTION_MODE_RCP_USB, /*!< RCP USB Serial JTAG connection to the host */ + HOST_CONNECTION_MODE_RCP_TRANSPORT, /*!< RCP Custom Transport to connect to the host */ HOST_CONNECTION_MODE_MAX, /*!< Using for parameter check */ } esp_openthread_host_connection_mode_t; @@ -164,7 +189,8 @@ typedef struct { 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_transport_config_t radio_transport_config; /*!< The custom transport configuration to RCP */ + }; } esp_openthread_radio_config_t; /** @@ -177,6 +203,7 @@ typedef struct { esp_openthread_uart_config_t host_uart_config; /*!< The uart configuration to host */ usb_serial_jtag_driver_config_t host_usb_config; /*!< The usb configuration to host */ esp_openthread_spi_slave_config_t spi_slave_config; /*!< The spi configuration to host */ + esp_openthread_transport_config_t host_transport_config; /*!< The custom transport configuration to the host */ }; } esp_openthread_host_connection_config_t; diff --git a/components/openthread/private_include/esp_openthread_common_macro.h b/components/openthread/private_include/esp_openthread_common_macro.h index 8595bc7684b..579d2492fc1 100644 --- a/components/openthread/private_include/esp_openthread_common_macro.h +++ b/components/openthread/private_include/esp_openthread_common_macro.h @@ -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 */ @@ -23,6 +23,7 @@ #endif #define ESP_OPENTHREAD_UART_BUFFER_SIZE CONFIG_OPENTHREAD_UART_BUFFER_SIZE +#define ESP_OPENTHREAD_TRANSPORT_BUFFER_SIZE CONFIG_OPENTHREAD_TRANSPORT_BUFFER_SIZE #if CONFIG_OPENTHREAD_DEBUG diff --git a/components/openthread/private_include/esp_openthread_transport_priv.h b/components/openthread/private_include/esp_openthread_transport_priv.h new file mode 100644 index 00000000000..81c4c6c20dd --- /dev/null +++ b/components/openthread/private_include/esp_openthread_transport_priv.h @@ -0,0 +1,37 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "esp_err.h" +#include "esp_openthread_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes the custom transport for OpenThread host connection. + * + * @param[in] config The platform configuration. + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if radio mode is invalid + * - ESP_FAIL or other error codes on failure + * + */ +esp_err_t esp_openthread_host_rcp_transport_init(const esp_openthread_platform_config_t *config); + +/** + * @brief Deinitializes the custom transport for OpenThread host connection. + * + */ +void esp_openthread_host_rcp_transport_deinit(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/openthread/private_include/esp_radio_spinel_custom.hpp b/components/openthread/private_include/esp_radio_spinel_custom.hpp new file mode 100644 index 00000000000..4b2366204db --- /dev/null +++ b/components/openthread/private_include/esp_radio_spinel_custom.hpp @@ -0,0 +1,193 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "esp_radio_spinel.h" +#include "esp_openthread_types.h" +#include "lib/hdlc/hdlc.hpp" +#include "lib/spinel/spinel_interface.hpp" +#include "openthread/error.h" + +namespace esp { +namespace radio_spinel { + +/** + * This class defines a custom Spinel transport using + * esp_openthread_transport_config_t (HDLC-framed). + * + */ +class CustomSpinelInterface : public ot::Spinel::SpinelInterface { +public: + /** + * @brief This constructor of object. + */ + CustomSpinelInterface(void); + + /** + * @brief This destructor of the object. + * + */ + ~CustomSpinelInterface(void); + + /** + * Initializes the interface to the Radio Co-processor (RCP). + * + * @note This method should be called before reading and sending spinel frames to the interface. + * + * @param[in] aCallback Callback on frame received + * @param[in] aCallbackContext Callback context + * @param[in] aFrameBuffer A reference to a `RxFrameBuffer` object. + * + * @retval OT_ERROR_NONE The interface is initialized successfully + * @retval OT_ERROR_ALREADY The interface is already initialized. + * @retval OT_ERROR_FAILED Failed to initialize the interface. + * + */ + otError Init(ReceiveFrameCallback aCallback, void *aCallbackContext, RxFrameBuffer &aFrameBuffer); + + /** + * Deinitializes the interface to the RCP. + * + */ + void Deinit(void); + + /** + * Encodes and sends a spinel frame to Radio Co-processor (RCP) over the socket. + * + * @param[in] aFrame A pointer to buffer containing the spinel frame to send. + * @param[in] aLength The length (number of bytes) in the frame. + * + * @retval OT_ERROR_NONE Successfully encoded and sent the spinel frame. + * @retval OT_ERROR_BUSY Failed due to another operation is on going. + * @retval OT_ERROR_NO_BUFS Insufficient buffer space available to encode the frame. + * @retval OT_ERROR_FAILED Failed to call the SPI driver to send the frame. + * + */ + otError SendFrame(const uint8_t *aFrame, uint16_t aLength); + + /** + * Waits for receiving part or all of spinel frame within specified interval. + * + * @param[in] aTimeout The timeout value in microseconds. + * + * @retval OT_ERROR_NONE Part or all of spinel frame is received. + * @retval OT_ERROR_RESPONSE_TIMEOUT No spinel frame is received within @p aTimeout. + * + */ + otError WaitForFrame(uint64_t aTimeoutUs); + + /** + * Updates the file descriptor sets with file descriptors used by the radio driver. + * + * @param[in,out] aMainloopContext A pointer to the mainloop context. + * + */ + void UpdateFdSet(void *aMainloopContext); + + /** + * Performs radio driver processing. + * + * @param[in] aMainloopContext A pointer to the mainloop context. + * + */ + void Process(const void *aMainloopContext); + + /** + * Returns the bus speed between the host and the radio. + * + * @returns Bus speed in bits/second. + * + */ + uint32_t GetBusSpeed(void) const; + + /** + * Hardware resets the RCP. + * + * @retval OT_ERROR_NONE Successfully reset the RCP. + * @retval OT_ERROR_NOT_IMPLEMENT The hardware reset is not implemented. + * + */ + otError HardwareReset(void); + + /** + * Returns the RCP interface metrics. + * + * @returns The RCP interface metrics. + * + */ + const otRcpInterfaceMetrics *GetRcpInterfaceMetrics(void) const { return &mInterfaceMetrics; } + + /** + * This methods registers the callback for RCP failure. + * + * @param[in] handler The RCP failure handler. + * + */ + void RegisterRcpFailureHandler(esp_radio_spinel_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; } + + /** + * @brief This method enable the HDLC interface. + * + * @return + * - ESP_OK on success + * - ESP_ERR_NO_MEM if allocation has failed + * - ESP_ERROR on failure + */ + esp_err_t Enable(const esp_openthread_transport_config_t &transport_config); + + /** + * @brief This method disable the HDLC interface. + * + */ + esp_err_t Disable(void); + +private: + enum { + /** + * Maximum wait time in Milliseconds for socket to become writable (see `SendFrame`). + * + */ + kMaxWaitTime = 2000, + }; + + int TryReadAndDecode(void); + + otError Write(const uint8_t *frame, uint16_t length); + + static void HandleHdlcFrame(void *context, otError error); + void HandleHdlcFrame(otError error); + + ReceiveFrameCallback m_receiver_frame_callback; + void * m_receiver_frame_context; + RxFrameBuffer * m_receive_frame_buffer; + + ot::Hdlc::Decoder m_hdlc_decoder; + uint8_t *m_rx_buffer; + + esp_openthread_transport_config_t m_transport_cfg; + + int m_notify_fd; + + otRcpInterfaceMetrics mInterfaceMetrics; + + CustomSpinelInterface(const CustomSpinelInterface &); + CustomSpinelInterface &operator=(const CustomSpinelInterface &); + + esp_radio_spinel_rcp_failure_handler mRcpFailureHandler; + + ot::Spinel::FrameBuffer encoder_buffer; +}; + +} // namespace radio_spinel +} // namespace esp 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 32a3a337955..45899d8595f 100644 --- a/components/openthread/private_include/openthread-core-esp32x-ftd-config.h +++ b/components/openthread/private_include/openthread-core-esp32x-ftd-config.h @@ -602,7 +602,7 @@ #define OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE 1 #endif -#if (CONFIG_OPENTHREAD_RADIO_SPINEL_UART || CONFIG_OPENTHREAD_RADIO_SPINEL_SPI) +#if (CONFIG_OPENTHREAD_RADIO_SPINEL_UART || CONFIG_OPENTHREAD_RADIO_SPINEL_SPI || CONFIG_OPENTHREAD_RADIO_SPINEL_CUSTOM) /** * @def OPENTHREAD_SPINEL_CONFIG_COMPATIBILITY_ERROR_CALLBACK_ENABLE * @@ -644,7 +644,7 @@ #define OPENTHREAD_SPINEL_CONFIG_COPROCESSOR_RESET_FAILURE_CALLBACK_ENABLE 1 #endif -#endif // CONFIG_OPENTHREAD_RADIO_SPINEL_UART || CONFIG_OPENTHREAD_RADIO_SPINEL_SPI +#endif // CONFIG_OPENTHREAD_RADIO_SPINEL_UART || CONFIG_OPENTHREAD_RADIO_SPINEL_SPI || CONFIG_OPENTHREAD_RADIO_SPINEL_CUSTOM #if CONFIG_OPENTHREAD_LINK_METRICS /** 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 215d82f9203..784738211d7 100644 --- a/components/openthread/private_include/openthread-core-esp32x-radio-config.h +++ b/components/openthread/private_include/openthread-core-esp32x-radio-config.h @@ -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 */ @@ -91,7 +91,7 @@ #ifdef OPENTHREAD_CONFIG_NCP_HDLC_ENABLE #error `OPENTHREAD_CONFIG_NCP_HDLC_ENABLE` is redefined. #endif -#define OPENTHREAD_CONFIG_NCP_HDLC_ENABLE (CONFIG_OPENTHREAD_RCP_UART || CONFIG_OPENTHREAD_RCP_USB_SERIAL_JTAG) +#define OPENTHREAD_CONFIG_NCP_HDLC_ENABLE (CONFIG_OPENTHREAD_RCP_UART || CONFIG_OPENTHREAD_RCP_USB_SERIAL_JTAG || CONFIG_OPENTHREAD_RCP_CUSTOM) /** * @def OPENTHREAD_LIB_SPINEL_RX_FRAME_BUFFER_SIZE diff --git a/components/openthread/src/esp_openthread_platform.cpp b/components/openthread/src/esp_openthread_platform.cpp index c00c48ed557..df26532eea3 100644 --- a/components/openthread/src/esp_openthread_platform.cpp +++ b/components/openthread/src/esp_openthread_platform.cpp @@ -11,6 +11,9 @@ #include "esp_log.h" #include "esp_openthread_alarm.h" #include "esp_openthread_common_macro.h" +#if CONFIG_OPENTHREAD_RCP_CUSTOM +#include "esp_openthread_transport_priv.h" +#endif #include "esp_openthread_lock.h" #include "esp_openthread_radio.h" #include "esp_openthread_spi_slave.h" @@ -112,6 +115,12 @@ static esp_err_t esp_openthread_host_interface_init(const esp_openthread_platfor "esp_openthread_host_rcp_usb_init failed"); break; #endif +#if CONFIG_OPENTHREAD_RCP_CUSTOM + case HOST_CONNECTION_MODE_RCP_TRANSPORT: + ESP_RETURN_ON_ERROR(esp_openthread_host_rcp_transport_init(config), OT_PLAT_LOG_TAG, + "esp_openthread_host_rcp_transport_init failed"); + break; +#endif #if CONFIG_OPENTHREAD_CONSOLE_TYPE_UART case HOST_CONNECTION_MODE_CLI_UART: ESP_RETURN_ON_ERROR(esp_openthread_host_cli_uart_init(config), OT_PLAT_LOG_TAG, @@ -191,6 +200,11 @@ esp_err_t esp_openthread_platform_deinit(void) case HOST_CONNECTION_MODE_CLI_UART: esp_openthread_uart_deinit(); break; +#endif +#if CONFIG_OPENTHREAD_RCP_CUSTOM + case HOST_CONNECTION_MODE_RCP_TRANSPORT: + esp_openthread_host_rcp_transport_deinit(); + break; #endif default: break; diff --git a/components/openthread/src/ncp/esp_openthread_ncp.cpp b/components/openthread/src/ncp/esp_openthread_ncp.cpp index 3852c9a8349..d621b228330 100644 --- a/components/openthread/src/ncp/esp_openthread_ncp.cpp +++ b/components/openthread/src/ncp/esp_openthread_ncp.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -14,7 +14,7 @@ #include "esp_coex_i154.h" #endif -#if (CONFIG_OPENTHREAD_RCP_UART || CONFIG_OPENTHREAD_RCP_USB_SERIAL_JTAG) +#if (CONFIG_OPENTHREAD_RCP_UART || CONFIG_OPENTHREAD_RCP_USB_SERIAL_JTAG || CONFIG_OPENTHREAD_RCP_CUSTOM) #include "utils/uart.h" #endif @@ -36,7 +36,7 @@ struct ConsoleCmdMsg }; #endif // CONFIG_OPENTHREAD_RCP_SPINEL_CONSOLE -#if (CONFIG_OPENTHREAD_RCP_UART || CONFIG_OPENTHREAD_RCP_USB_SERIAL_JTAG) +#if (CONFIG_OPENTHREAD_RCP_UART || CONFIG_OPENTHREAD_RCP_USB_SERIAL_JTAG || CONFIG_OPENTHREAD_RCP_CUSTOM) extern "C" { static int NcpSend(const uint8_t *aBuf, uint16_t aBufLength) { @@ -94,7 +94,7 @@ static esp_err_t init_console_command_worker() extern "C" void otAppNcpInit(otInstance *aInstance) { -#if (CONFIG_OPENTHREAD_RCP_UART || CONFIG_OPENTHREAD_RCP_USB_SERIAL_JTAG) +#if (CONFIG_OPENTHREAD_RCP_UART || CONFIG_OPENTHREAD_RCP_USB_SERIAL_JTAG || CONFIG_OPENTHREAD_RCP_CUSTOM) IgnoreError(otPlatUartEnable()); otNcpHdlcInit(aInstance, NcpSend); #else diff --git a/components/openthread/src/port/esp_openthread_radio_spinel.cpp b/components/openthread/src/port/esp_openthread_radio_spinel.cpp index fa8c2e3f5a0..6516131e2f0 100644 --- a/components/openthread/src/port/esp_openthread_radio_spinel.cpp +++ b/components/openthread/src/port/esp_openthread_radio_spinel.cpp @@ -17,8 +17,13 @@ #include "esp_system.h" #include "esp_spinel_interface.hpp" #include "esp_spinel_ncp_vendor_macro.h" -#include "esp_spi_spinel_interface.hpp" +#if CONFIG_OPENTHREAD_RADIO_SPINEL_UART #include "esp_radio_spinel_uart_interface.hpp" +#elif CONFIG_OPENTHREAD_RADIO_SPINEL_SPI +#include "esp_spi_spinel_interface.hpp" +#elif CONFIG_OPENTHREAD_RADIO_SPINEL_CUSTOM +#include "esp_radio_spinel_custom.hpp" +#endif #include "openthread-core-config.h" #include "lib/spinel/radio_spinel.hpp" #include "lib/spinel/spinel.h" @@ -38,9 +43,12 @@ using ot::Spinel::SpinelDriver; #if CONFIG_OPENTHREAD_RADIO_SPINEL_UART // CONFIG_OPENTHREAD_RADIO_SPINEL_UART using esp::radio_spinel::UartSpinelInterface; static SpinelInterfaceAdapter s_spinel_interface; -#else // CONFIG_OPENTHREAD_RADIO_SPINEL_SPI +#elif CONFIG_OPENTHREAD_RADIO_SPINEL_SPI // CONFIG_OPENTHREAD_RADIO_SPINEL_SPI using esp::openthread::SpiSpinelInterface; static SpinelInterfaceAdapter s_spinel_interface; +#elif CONFIG_OPENTHREAD_RADIO_SPINEL_CUSTOM // CONFIG_OPENTHREAD_RADIO_SPINEL_CUSTOM +using esp::radio_spinel::CustomSpinelInterface; +static SpinelInterfaceAdapter s_spinel_interface; #endif static SpinelDriver s_spinel_driver; @@ -99,6 +107,8 @@ void esp_openthread_set_coprocessor_reset_failure_callback(esp_openthread_coproc esp_err_t esp_openthread_radio_init(const esp_openthread_platform_config_t *config) { + ESP_RETURN_ON_FALSE(config != NULL, ESP_ERR_INVALID_ARG, OT_PLAT_LOG_TAG, "incoming config is NULL"); + spinel_iid_t iidList[ot::Spinel::kSpinelHeaderMaxNumIid]; iidList[0] = 0; @@ -111,12 +121,29 @@ esp_err_t esp_openthread_radio_init(const esp_openthread_platform_config_t *conf s_radio.SetCallbacks(callbacks); esp_openthread_radio_config_set(&config->radio_config); + // validate incoming RCP Radio Mode +#if CONFIG_OPENTHREAD_RADIO_SPINEL_UART + ESP_RETURN_ON_FALSE(config->radio_config.radio_mode == RADIO_MODE_UART_RCP, ESP_ERR_INVALID_ARG, OT_PLAT_LOG_TAG, + "radio_mode must be RADIO_MODE_UART_RCP"); +#elif CONFIG_OPENTHREAD_RADIO_SPINEL_SPI + ESP_RETURN_ON_FALSE(config->radio_config.radio_mode == RADIO_MODE_SPI_RCP, ESP_ERR_INVALID_ARG, OT_PLAT_LOG_TAG, + "radio_mode must be RADIO_MODE_SPI_RCP"); +#elif CONFIG_OPENTHREAD_RADIO_SPINEL_CUSTOM + ESP_RETURN_ON_FALSE(config->radio_config.radio_mode == RADIO_MODE_TRANSPORT, ESP_ERR_INVALID_ARG, OT_PLAT_LOG_TAG, + "radio_mode must be RADIO_MODE_TRANSPORT"); + ESP_RETURN_ON_FALSE(config->radio_config.radio_transport_config.transport_tx != NULL && + config->radio_config.radio_transport_config.transport_rx != NULL, ESP_ERR_INVALID_ARG, OT_PLAT_LOG_TAG, + "transport callbacks must be non-NULL"); +#endif #if CONFIG_OPENTHREAD_RADIO_SPINEL_UART // CONFIG_OPENTHREAD_RADIO_SPINEL_UART ESP_RETURN_ON_ERROR(s_spinel_interface.GetSpinelInterface().Enable(config->radio_config.radio_uart_config), OT_PLAT_LOG_TAG, "Spinel interface init failed"); -#else // CONFIG_OPENTHREAD_RADIO_SPINEL_SPI +#elif CONFIG_OPENTHREAD_RADIO_SPINEL_SPI // CONFIG_OPENTHREAD_RADIO_SPINEL_SPI ESP_RETURN_ON_ERROR(s_spinel_interface.GetSpinelInterface().Enable(config->radio_config.radio_spi_config), OT_PLAT_LOG_TAG, "Spinel interface init failed"); +#elif CONFIG_OPENTHREAD_RADIO_SPINEL_CUSTOM // CONFIG_OPENTHREAD_RADIO_SPINEL_CUSTOM + ESP_RETURN_ON_ERROR(s_spinel_interface.GetSpinelInterface().Enable(config->radio_config.radio_transport_config), OT_PLAT_LOG_TAG, + "Spinel custom transport init failed"); #endif s_spinel_driver.SetCoprocessorResetFailureCallback(ot_spinel_coprocessor_reset_failure_callback, esp_openthread_get_instance()); s_spinel_driver.Init(s_spinel_interface.GetSpinelInterface(), true, iidList, ot::Spinel::kSpinelHeaderMaxNumIid); @@ -164,10 +191,13 @@ esp_err_t esp_openthread_rcp_init(void) #if CONFIG_OPENTHREAD_RADIO_SPINEL_UART ESP_RETURN_ON_ERROR(s_spinel_interface.GetSpinelInterface().Enable(radio_config->radio_uart_config), OT_PLAT_LOG_TAG, "Spinel interface init failed"); -#else // CONFIG_OPENTHREAD_RADIO_SPINEL_SPI +#elif CONFIG_OPENTHREAD_RADIO_SPINEL_SPI ESP_RETURN_ON_ERROR(s_spinel_interface.GetSpinelInterface().Enable(radio_config->radio_spi_config), OT_PLAT_LOG_TAG, "Spinel interface init failed"); -#endif // CONFIG_OPENTHREAD_RADIO_SPINEL_UART +#elif CONFIG_OPENTHREAD_RADIO_SPINEL_CUSTOM + ESP_RETURN_ON_ERROR(s_spinel_interface.GetSpinelInterface().Enable(radio_config->radio_transport_config), OT_PLAT_LOG_TAG, + "Spinel interface init failed"); +#endif ESP_RETURN_ON_FALSE(s_radio.Enable(esp_openthread_get_instance()) == OT_ERROR_NONE, ESP_FAIL, OT_PLAT_LOG_TAG, "Fail to enable radio"); #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0 @@ -547,6 +577,8 @@ uint32_t otPlatRadioGetBusSpeed(otInstance *aInstance) return s_esp_openthread_radio_config->radio_uart_config.uart_config.baud_rate; #elif CONFIG_OPENTHREAD_RADIO_SPINEL_SPI return s_esp_openthread_radio_config->radio_spi_config.spi_device.clock_speed_hz; +#elif CONFIG_OPENTHREAD_RADIO_SPINEL_CUSTOM + return s_esp_openthread_radio_config->radio_transport_config.bus_speed; #else return 0; #endif @@ -556,7 +588,7 @@ uint32_t otPlatRadioGetBusLatency(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); -#if CONFIG_OPENTHREAD_RADIO_SPINEL_UART || CONFIG_OPENTHREAD_RADIO_SPINEL_SPI +#if CONFIG_OPENTHREAD_RADIO_SPINEL_UART || CONFIG_OPENTHREAD_RADIO_SPINEL_SPI || CONFIG_OPENTHREAD_RADIO_SPINEL_CUSTOM return CONFIG_OPENTHREAD_BUS_LATENCY; #else return 0; diff --git a/components/openthread/src/port/esp_openthread_state.c b/components/openthread/src/port/esp_openthread_state.c index d106d397782..9510253dc25 100644 --- a/components/openthread/src/port/esp_openthread_state.c +++ b/components/openthread/src/port/esp_openthread_state.c @@ -50,7 +50,7 @@ static void handle_ot_netif_state_change(otInstance* instance) } } -#if (CONFIG_OPENTHREAD_RADIO_SPINEL_UART || CONFIG_OPENTHREAD_RADIO_SPINEL_SPI) +#if (CONFIG_OPENTHREAD_RADIO_SPINEL_UART || CONFIG_OPENTHREAD_RADIO_SPINEL_SPI || CONFIG_OPENTHREAD_RADIO_SPINEL_CUSTOM) esp_openthread_handle_netif_state_change(otIp6IsEnabled(instance)); #endif } diff --git a/components/openthread/src/port/esp_openthread_transport_rcp.c b/components/openthread/src/port/esp_openthread_transport_rcp.c new file mode 100644 index 00000000000..b508b43d198 --- /dev/null +++ b/components/openthread/src/port/esp_openthread_transport_rcp.c @@ -0,0 +1,143 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sdkconfig.h" + +#if CONFIG_OPENTHREAD_RCP_CUSTOM + +#include "esp_openthread_transport_priv.h" + +#include +#include +#include +#include + +#include "esp_check.h" +#include "esp_openthread_common_macro.h" +#include "esp_openthread_platform.h" +#include "esp_openthread_types.h" +#include "esp_vfs_eventfd.h" +#include "common/code_utils.hpp" +#include "openthread/instance.h" +#include "utils/uart.h" + +static esp_openthread_transport_config_t s_transport_config; +static int s_notify_eventfd = -1; +static const char s_transport_rcp_workflow[] = "transport_rcp"; + +static esp_err_t transport_rcp_notify_eventfd_init(void) +{ + ESP_RETURN_ON_FALSE(s_notify_eventfd < 0, ESP_ERR_INVALID_STATE, OT_PLAT_LOG_TAG, "custom transport RCP eventfd already initialized"); + s_notify_eventfd = eventfd(0, EFD_SUPPORT_ISR); + ESP_RETURN_ON_FALSE(s_notify_eventfd >= 0, ESP_FAIL, OT_PLAT_LOG_TAG, "custom transport RCP eventfd create failed"); + return ESP_OK; +} + +static void transport_rcp_notify_eventfd_deinit(void) +{ + if (s_notify_eventfd >= 0) { + close(s_notify_eventfd); + s_notify_eventfd = -1; + } +} + +void esp_openthread_transport_notify_rcp_rx(void) +{ + if (s_notify_eventfd >= 0) { + uint64_t one = 1; + ssize_t w = write(s_notify_eventfd, &one, sizeof(one)); + (void)w; + } +} + +void esp_openthread_transport_rcp_update(esp_openthread_mainloop_context_t *mainloop) +{ + if (s_notify_eventfd >= 0) { + FD_SET(s_notify_eventfd, &mainloop->read_fds); + if (s_notify_eventfd > mainloop->max_fd) { + mainloop->max_fd = s_notify_eventfd; + } + } +} + +static uint8_t s_rx_chunk[ESP_OPENTHREAD_TRANSPORT_BUFFER_SIZE]; + +esp_err_t esp_openthread_transport_rcp_process(otInstance *instance, const esp_openthread_mainloop_context_t *mainloop) +{ + OT_UNUSED_VARIABLE(instance); + + if (s_notify_eventfd >= 0 && FD_ISSET(s_notify_eventfd, &mainloop->read_fds)) { + uint64_t v; + ssize_t r = read(s_notify_eventfd, &v, sizeof(v)); + (void)r; + } + + size_t n = 0; + esp_err_t err = s_transport_config.transport_rx(s_rx_chunk, sizeof(s_rx_chunk), &n); + if (err == ESP_OK) { + if (n <= sizeof(s_rx_chunk)) { + otPlatUartReceived(s_rx_chunk, (uint16_t)n); + } else { + ESP_LOGE(OT_PLAT_LOG_TAG, "invalid Rx data length"); + } + } + return ESP_OK; +} + +esp_err_t esp_openthread_host_rcp_transport_init(const esp_openthread_platform_config_t *config) +{ + ESP_RETURN_ON_FALSE(config != NULL, ESP_ERR_INVALID_ARG, OT_PLAT_LOG_TAG, "config is NULL"); + ESP_RETURN_ON_FALSE(config->host_config.host_connection_mode == HOST_CONNECTION_MODE_RCP_TRANSPORT, ESP_ERR_INVALID_ARG, + OT_PLAT_LOG_TAG, "host connection mode must be RCP Transport"); + ESP_RETURN_ON_FALSE(config->host_config.host_transport_config.transport_tx != NULL, ESP_ERR_INVALID_ARG, + OT_PLAT_LOG_TAG, "transport_tx is NULL"); + ESP_RETURN_ON_FALSE(config->host_config.host_transport_config.transport_rx != NULL, ESP_ERR_INVALID_ARG, + OT_PLAT_LOG_TAG, "transport_rx is NULL"); + + memcpy(&s_transport_config, &config->host_config.host_transport_config, sizeof(s_transport_config)); + + ESP_RETURN_ON_ERROR(transport_rcp_notify_eventfd_init(), OT_PLAT_LOG_TAG, "custom transport RCP notify init failed"); + + return esp_openthread_platform_workflow_register(&esp_openthread_transport_rcp_update, &esp_openthread_transport_rcp_process, + s_transport_rcp_workflow); +} + +void esp_openthread_host_rcp_transport_deinit(void) +{ + esp_openthread_platform_workflow_unregister(s_transport_rcp_workflow); + memset(&s_transport_config, 0, sizeof(s_transport_config)); + transport_rcp_notify_eventfd_deinit(); +} + +otError otPlatUartEnable(void) +{ + return OT_ERROR_NONE; +} + +otError otPlatUartDisable(void) +{ + return OT_ERROR_NONE; +} + +otError otPlatUartFlush(void) +{ + return OT_ERROR_NONE; +} + +otError otPlatUartSend(const uint8_t *buf, uint16_t buf_length) +{ + if (!s_transport_config.transport_tx) { + return OT_ERROR_FAILED; + } + esp_err_t err = s_transport_config.transport_tx(buf, buf_length); + otPlatUartSendDone(); + if (err != ESP_OK) { + return OT_ERROR_FAILED; + } + return OT_ERROR_NONE; +} + +#endif /* CONFIG_OPENTHREAD_RCP_CUSTOM */ diff --git a/components/openthread/src/spinel/esp_radio_spinel_custom.cpp b/components/openthread/src/spinel/esp_radio_spinel_custom.cpp new file mode 100644 index 00000000000..77d0286acaf --- /dev/null +++ b/components/openthread/src/spinel/esp_radio_spinel_custom.cpp @@ -0,0 +1,281 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_radio_spinel_custom.hpp" + +#include +#include +#include + +#include "esp_check.h" +#include "esp_openthread_common_macro.h" +#include "esp_radio_spinel.h" +#include "esp_vfs_eventfd.h" +#include "common/code_utils.hpp" +#include "hdlc.hpp" +#include "openthread/error.h" +#include "openthread/platform/time.h" +#include "platform/exit_code.h" + +namespace esp { +namespace radio_spinel { + +namespace { +static int s_transport_spinel_notify_fd = -1; +} + +extern "C" void esp_openthread_transport_notify_host_rx(void) +{ + if (s_transport_spinel_notify_fd >= 0) { + uint64_t one = 1; + ssize_t w = write(s_transport_spinel_notify_fd, &one, sizeof(one)); + (void)w; + } +} + +CustomSpinelInterface::CustomSpinelInterface(void) + : m_receiver_frame_callback(nullptr) + , m_receiver_frame_context(nullptr) + , m_receive_frame_buffer(nullptr) + , m_rx_buffer(nullptr) + , m_notify_fd(-1) + , mRcpFailureHandler(nullptr) +{ + memset(&m_transport_cfg, 0, sizeof(m_transport_cfg)); + memset(&mInterfaceMetrics, 0, sizeof(mInterfaceMetrics)); +} + +CustomSpinelInterface::~CustomSpinelInterface(void) +{ + Disable(); + Deinit(); +} + +otError CustomSpinelInterface::Init(ReceiveFrameCallback aCallback, void *aCallbackContext, RxFrameBuffer &aFrameBuffer) +{ + m_receiver_frame_callback = aCallback; + m_receiver_frame_context = aCallbackContext; + m_receive_frame_buffer = &aFrameBuffer; + m_hdlc_decoder.Init(aFrameBuffer, HandleHdlcFrame, this); + return OT_ERROR_NONE; +} + +void CustomSpinelInterface::Deinit(void) +{ + m_receiver_frame_callback = nullptr; + m_receiver_frame_context = nullptr; + m_receive_frame_buffer = nullptr; +} + +esp_err_t CustomSpinelInterface::Enable(const esp_openthread_transport_config_t &transport_config) +{ + ESP_RETURN_ON_FALSE((m_notify_fd < 0) && (m_rx_buffer == NULL), ESP_ERR_INVALID_STATE, ESP_SPINEL_LOG_TAG, "custom transport spinel already enabled"); + ESP_RETURN_ON_FALSE((transport_config.transport_tx != nullptr) && (transport_config.transport_rx != nullptr), + ESP_ERR_INVALID_ARG, ESP_SPINEL_LOG_TAG, "transport callbacks invalid"); + + m_rx_buffer = static_cast(heap_caps_malloc(ESP_OPENTHREAD_TRANSPORT_BUFFER_SIZE, MALLOC_CAP_8BIT)); + if (m_rx_buffer == NULL) { + return ESP_ERR_NO_MEM; + } + + m_notify_fd = eventfd(0, EFD_SUPPORT_ISR); + if (m_notify_fd < 0) { + ESP_LOGE(ESP_SPINEL_LOG_TAG, "custom transport spinel eventfd failed"); + heap_caps_free(m_rx_buffer); + m_rx_buffer = NULL; + return ESP_FAIL; + } + + s_transport_spinel_notify_fd = m_notify_fd; + + memcpy(&m_transport_cfg, &transport_config, sizeof(m_transport_cfg)); + + return ESP_OK; +} + +esp_err_t CustomSpinelInterface::Disable(void) +{ + if (m_rx_buffer) { + heap_caps_free(m_rx_buffer); + } + m_rx_buffer = NULL; + + if (s_transport_spinel_notify_fd == m_notify_fd) { + s_transport_spinel_notify_fd = -1; + } + + if (m_notify_fd >= 0) { + close(m_notify_fd); + m_notify_fd = -1; + } + + memset(&m_transport_cfg, 0, sizeof(m_transport_cfg)); + return ESP_OK; +} + +otError CustomSpinelInterface::SendFrame(const uint8_t *frame, uint16_t length) +{ + otError error = OT_ERROR_NONE; + encoder_buffer.Clear(); + ot::Hdlc::Encoder hdlc_encoder(encoder_buffer); + + SuccessOrExit(error = hdlc_encoder.BeginFrame()); + SuccessOrExit(error = hdlc_encoder.Encode(frame, length)); + SuccessOrExit(error = hdlc_encoder.EndFrame()); + + SuccessOrExit(error = Write(encoder_buffer.GetFrame(), encoder_buffer.GetLength())); + +exit: + if (error != OT_ERROR_NONE) { + ESP_LOGE(ESP_SPINEL_LOG_TAG, "send radio frame failed"); + } else { + ESP_LOGD(ESP_SPINEL_LOG_TAG, "sent radio frame"); + } + return error; +} + +void CustomSpinelInterface::Process(const void *aMainloopContext) +{ + if (m_notify_fd < 0) { + return; + } + auto *ctx = (esp_radio_spinel_mainloop_context_t *)aMainloopContext; + + if (m_notify_fd >= 0 && FD_ISSET(m_notify_fd, &ctx->read_fds)) { + uint64_t v; + ssize_t r = read(m_notify_fd, &v, sizeof(v)); + (void)r; + } + + TryReadAndDecode(); +} + +int CustomSpinelInterface::TryReadAndDecode(void) +{ + if (!m_transport_cfg.transport_rx) { + return 0; + } + + for (;;) { + size_t n = 0; + esp_err_t err = m_transport_cfg.transport_rx(m_rx_buffer, CONFIG_OPENTHREAD_TRANSPORT_BUFFER_SIZE, &n); + if ((err != ESP_OK) || (n == 0) || (n > CONFIG_OPENTHREAD_TRANSPORT_BUFFER_SIZE)) { + break; + } + m_hdlc_decoder.Decode(m_rx_buffer, static_cast(n)); + } + return 0; +} + +otError CustomSpinelInterface::Write(const uint8_t *aFrame, uint16_t length) +{ + if (!m_transport_cfg.transport_tx) { + return OT_ERROR_FAILED; + } + esp_err_t err = m_transport_cfg.transport_tx(aFrame, length); + return (err == ESP_OK) ? OT_ERROR_NONE : OT_ERROR_FAILED; +} + +otError CustomSpinelInterface::WaitForFrame(uint64_t timeout_us) +{ + if (m_notify_fd < 0) { + return OT_ERROR_FAILED; + } + + otError error = OT_ERROR_NONE; + struct timeval timeout; + fd_set read_fds; + fd_set error_fds; + int rval; + uint64_t const deadline = otPlatTimeGet() + timeout_us; + + TryReadAndDecode(); + + while (true) { + uint64_t now = otPlatTimeGet(); + if (now >= deadline) { + ExitNow(error = OT_ERROR_RESPONSE_TIMEOUT); + } + uint64_t remain = deadline - now; + + FD_ZERO(&read_fds); + FD_ZERO(&error_fds); + FD_SET(m_notify_fd, &read_fds); + FD_SET(m_notify_fd, &error_fds); + + timeout.tv_sec = static_cast(remain / US_PER_S); + timeout.tv_usec = static_cast(remain % US_PER_S); + + rval = select(m_notify_fd + 1, &read_fds, NULL, &error_fds, &timeout); + + if (rval > 0) { + if (FD_ISSET(m_notify_fd, &read_fds)) { + uint64_t v; + ssize_t r = read(m_notify_fd, &v, sizeof(v)); + (void)r; + TryReadAndDecode(); + ExitNow(error = OT_ERROR_NONE); + } + if (FD_ISSET(m_notify_fd, &error_fds)) { + ExitNow(error = OT_ERROR_FAILED); + } + } else if (rval == 0) { + ExitNow(error = OT_ERROR_RESPONSE_TIMEOUT); + } else if (errno != EINTR) { + ExitNow(error = OT_ERROR_FAILED); + } + } + +exit: + return error; +} + +void CustomSpinelInterface::HandleHdlcFrame(void *context, otError error) +{ + static_cast(context)->HandleHdlcFrame(error); +} + +void CustomSpinelInterface::HandleHdlcFrame(otError error) +{ + if (error == OT_ERROR_NONE) { + if (m_receiver_frame_callback != nullptr) { + m_receiver_frame_callback(m_receiver_frame_context); + } + } else { + ESP_LOGE(ESP_SPINEL_LOG_TAG, "dropping radio frame: %s", otThreadErrorToString(error)); + if (m_receive_frame_buffer != nullptr) { + m_receive_frame_buffer->DiscardFrame(); + } + } +} + +void CustomSpinelInterface::UpdateFdSet(void *aMainloopContext) +{ + if (m_notify_fd < 0) { + return; + } + auto *ctx = (esp_radio_spinel_mainloop_context_t *)aMainloopContext; + FD_SET(m_notify_fd, &ctx->read_fds); + if (m_notify_fd > ctx->max_fd) { + ctx->max_fd = m_notify_fd; + } +} + +uint32_t CustomSpinelInterface::GetBusSpeed(void) const +{ + return m_transport_cfg.bus_speed; +} + +otError CustomSpinelInterface::HardwareReset(void) +{ + if (mRcpFailureHandler) { + mRcpFailureHandler(); + } + return OT_ERROR_NONE; +} + +} // namespace radio_spinel +} // namespace esp diff --git a/components/openthread/srcs_ftd_mtd.cmake b/components/openthread/srcs_ftd_mtd.cmake index bae3a44fa88..dc0319d028b 100644 --- a/components/openthread/srcs_ftd_mtd.cmake +++ b/components/openthread/srcs_ftd_mtd.cmake @@ -50,21 +50,31 @@ if(CONFIG_OPENTHREAD_RADIO_NATIVE) "src/port/esp_openthread_radio_spinel.cpp" "src/port/esp_spi_spinel_interface.cpp" "src/spinel/esp_radio_spinel.cpp" - "src/spinel/esp_radio_spinel_uart_interface.cpp") -elseif(CONFIG_OPENTHREAD_RADIO_SPINEL_UART OR CONFIG_OPENTHREAD_RADIO_SPINEL_SPI) + "src/spinel/esp_radio_spinel_uart_interface.cpp" + "src/spinel/esp_radio_spinel_custom.cpp") +elseif(CONFIG_OPENTHREAD_RADIO_SPINEL_UART OR + CONFIG_OPENTHREAD_RADIO_SPINEL_SPI OR + CONFIG_OPENTHREAD_RADIO_SPINEL_CUSTOM) list(APPEND exclude_srcs "src/port/esp_openthread_radio.c" "src/port/esp_openthread_sleep.c" "src/spinel/esp_radio_spinel.cpp") - if(CONFIG_OPENTHREAD_RADIO_SPINEL_SPI) + if(CONFIG_OPENTHREAD_RADIO_SPINEL_SPI OR CONFIG_OPENTHREAD_RADIO_SPINEL_CUSTOM) list(APPEND exclude_srcs "src/spinel/esp_radio_spinel_uart_interface.cpp") endif() + if(CONFIG_OPENTHREAD_RADIO_SPINEL_UART OR CONFIG_OPENTHREAD_RADIO_SPINEL_CUSTOM) + list(APPEND exclude_srcs "src/port/esp_spi_spinel_interface.cpp") + endif() + if(CONFIG_OPENTHREAD_RADIO_SPINEL_UART OR CONFIG_OPENTHREAD_RADIO_SPINEL_SPI) + list(APPEND exclude_srcs "src/spinel/esp_radio_spinel_custom.cpp") + endif() elseif(CONFIG_OPENTHREAD_RADIO_154_NONE) list(APPEND exclude_srcs "src/port/esp_openthread_radio_spinel.cpp" "src/port/esp_spi_spinel_interface.cpp" "src/spinel/esp_radio_spinel.cpp" "src/spinel/esp_radio_spinel_uart_interface.cpp" + "src/spinel/esp_radio_spinel_custom.cpp" "src/port/esp_openthread_radio.c" "src/port/esp_openthread_sleep.c") endif() @@ -92,7 +102,7 @@ endif() if(CONFIG_OPENTHREAD_NCP_VENDOR_HOOK) list(APPEND src_dirs "src/ncp") - if(CONFIG_OPENTHREAD_RCP_UART OR CONFIG_OPENTHREAD_RCP_USB_SERIAL_JTAG) + if(CONFIG_OPENTHREAD_RCP_UART OR CONFIG_OPENTHREAD_RCP_USB_SERIAL_JTAG OR CONFIG_OPENTHREAD_RCP_CUSTOM) list(APPEND exclude_srcs "src/ncp/esp_openthread_ncp_spi.cpp") elseif(CONFIG_OPENTHREAD_RCP_SPI) diff --git a/components/openthread/srcs_radio.cmake b/components/openthread/srcs_radio.cmake index 9d929c10059..16043763751 100644 --- a/components/openthread/srcs_radio.cmake +++ b/components/openthread/srcs_radio.cmake @@ -90,6 +90,13 @@ elseif(CONFIG_OPENTHREAD_RCP_SPI) if(CONFIG_OPENTHREAD_NCP_VENDOR_HOOK) list(APPEND rcp_srcs src/ncp/esp_openthread_ncp_spi.cpp) endif() +elseif(CONFIG_OPENTHREAD_RCP_CUSTOM) + list(APPEND rcp_srcs + openthread/src/ncp/ncp_hdlc.cpp + src/port/esp_openthread_transport_rcp.c) + if(CONFIG_OPENTHREAD_NCP_VENDOR_HOOK) + list(APPEND rcp_srcs src/ncp/esp_openthread_ncp_hdlc.cpp) + endif() endif() if(CONFIG_OPENTHREAD_NCP_VENDOR_HOOK AND CONFIG_OPENTHREAD_RCP_SPINEL_CONSOLE)