From 01808d0cfbaa50b2ea227a6b4ae000c7e2ba5402 Mon Sep 17 00:00:00 2001 From: Xu Si Yu Date: Wed, 22 Nov 2023 19:15:55 +0800 Subject: [PATCH] feat(openthread): modification of uart and spi spinel based on openthread 41ef807 --- components/openthread/CMakeLists.txt | 4 +- .../include/esp_openthread_border_router.h | 18 ++- components/openthread/lib | 2 +- components/openthread/openthread | 2 +- .../private_include/esp_openthread_radio.h | 2 +- .../esp_spi_spinel_interface.hpp | 103 +++++++++---- .../private_include/esp_spinel_interface.hpp | 50 +++++++ .../esp_uart_spinel_interface.hpp | 136 +++++++++++------- .../src/esp_openthread_platform.cpp | 2 +- .../src/port/esp_openthread_radio_spinel.cpp | 88 ++++++++++-- .../src/port/esp_spi_spinel_interface.cpp | 88 ++++++++---- .../src/port/esp_uart_spinel_interface.cpp | 66 ++++++--- 12 files changed, 404 insertions(+), 157 deletions(-) create mode 100644 components/openthread/private_include/esp_spinel_interface.hpp diff --git a/components/openthread/CMakeLists.txt b/components/openthread/CMakeLists.txt index e0030687ab..b878ce0e6d 100644 --- a/components/openthread/CMakeLists.txt +++ b/components/openthread/CMakeLists.txt @@ -18,6 +18,7 @@ if(CONFIG_OPENTHREAD_ENABLED) "openthread/include/openthread" "openthread/src" "openthread/src/core" + "openthread/src/lib" "openthread/src/lib/hdlc" "openthread/src/lib/spinel" "openthread/src/ncp" @@ -31,6 +32,7 @@ if(CONFIG_OPENTHREAD_ENABLED) "openthread/src/core/common" "openthread/src/core/crypto" "openthread/src/core/diags" + "openthread/src/core/instance" "openthread/src/core/mac" "openthread/src/core/radio" "openthread/src/core/thread" @@ -41,7 +43,7 @@ if(CONFIG_OPENTHREAD_ENABLED) set(exclude_srcs "openthread/examples/platforms/utils/logging_rtt.c" "openthread/examples/platforms/utils/soft_source_match_table.c" - "openthread/src/core/common/extension_example.cpp") + "openthread/src/core/instance/extension_example.cpp") if(CONFIG_OPENTHREAD_FTD OR CONFIG_OPENTHREAD_MTD) list(APPEND src_dirs diff --git a/components/openthread/include/esp_openthread_border_router.h b/components/openthread/include/esp_openthread_border_router.h index 33f9244eb7..3a6c6e9638 100644 --- a/components/openthread/include/esp_openthread_border_router.h +++ b/components/openthread/include/esp_openthread_border_router.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -69,8 +69,22 @@ void esp_openthread_register_rcp_failure_handler(esp_openthread_rcp_failure_hand /** * @brief Deinitializes the conneciton to RCP. * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if fail to deinitialize RCP + * */ -void esp_openthread_rcp_deinit(void); +esp_err_t esp_openthread_rcp_deinit(void); + +/** + * @brief Initializes the conneciton to RCP. + * + * @return + * - ESP_OK on success + * - ESP_FAIL if fail to initialize RCP + * + */ +esp_err_t esp_openthread_rcp_init(void); #ifdef __cplusplus } diff --git a/components/openthread/lib b/components/openthread/lib index 12f563ee49..648c28e792 160000 --- a/components/openthread/lib +++ b/components/openthread/lib @@ -1 +1 @@ -Subproject commit 12f563ee490236f7332eb22f568e71c7c1d4a3b7 +Subproject commit 648c28e792567bc00602c92e43518c1784599251 diff --git a/components/openthread/openthread b/components/openthread/openthread index af5938e389..41ef80717f 160000 --- a/components/openthread/openthread +++ b/components/openthread/openthread @@ -1 +1 @@ -Subproject commit af5938e389be40650507748272bb6c6b3a2de2cf +Subproject commit 41ef80717f4b757440125932723cc8721ef42f7f diff --git a/components/openthread/private_include/esp_openthread_radio.h b/components/openthread/private_include/esp_openthread_radio.h index 7cf41a559b..d3fa70aa2c 100644 --- a/components/openthread/private_include/esp_openthread_radio.h +++ b/components/openthread/private_include/esp_openthread_radio.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/components/openthread/private_include/esp_spi_spinel_interface.hpp b/components/openthread/private_include/esp_spi_spinel_interface.hpp index 6b8ae2833e..e5b0976e1f 100644 --- a/components/openthread/private_include/esp_spi_spinel_interface.hpp +++ b/components/openthread/private_include/esp_spi_spinel_interface.hpp @@ -14,18 +14,12 @@ namespace esp { namespace openthread { -class SpiSpinelInterface { +class SpiSpinelInterface : public ot::Spinel::SpinelInterface { 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); + SpiSpinelInterface(); /** * @brief This destructor of the object. @@ -34,24 +28,26 @@ public: ~SpiSpinelInterface(void); /** - * @brief This method initializes the spinel interface. + * 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. * - * @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); + otError Init(ReceiveFrameCallback aCallback, void *aCallbackContext, RxFrameBuffer &aFrameBuffer); /** - * @brief This method deinitializes the HDLC interface. + * Deinitializes the interface to the RCP. * - * @return - * - ESP_OK on success - * - ESP_FAIL on failure */ - esp_err_t Deinit(void); + void Deinit(void); /** * @brief This method encodes and sends a spinel frame to Radio Co-processor (RCP) over the socket. @@ -80,20 +76,42 @@ public: otError WaitForFrame(uint64_t timeout_us); /** - * This method performs spi processing to the RCP. + * Updates the file descriptor sets with file descriptors used by the radio driver. * - * @param[in] mainloop The mainloop context + * @param[in,out] aMainloopContext A pointer to the mainloop context. * */ - void Process(const void *mainloop); + void UpdateFdSet(void *aMainloopContext); /** - * This methods updates the mainloop context. + * Performs radio driver processing. * - * @param[inout] mainloop The mainloop context. + * @param[in] aMainloopContext A pointer to the mainloop context. * */ - void Update(void *mainloop); + 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; + + /** + * This method is called when RCP failure detected and resets internal states of the interface. + * + */ + 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. @@ -111,10 +129,33 @@ public: otError ResetConnection(void) { return OT_ERROR_NONE; } /** - * This method is called when RCP failure detected and resets internal states of the interface. + * @brief This method enable 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 */ - otError HardwareReset(void); + esp_err_t Enable(const esp_openthread_spi_host_config_t &spi_config); + + /** + * @brief This method disable the spinel interface. + * + * @return + * - ESP_OK on success + * - ESP_FAIL on failure + */ + esp_err_t Disable(void); + + /** + * @brief This method should be called after radio is initialized. + * + * @return + * - ESP_OK on success + * - ESP_FAIL on failure + */ + esp_err_t AfterRadioInit(void); private: static constexpr uint8_t kSPIFrameHeaderSize = 5; @@ -130,14 +171,16 @@ private: int m_event_fd; volatile uint16_t m_pending_data_len; - ot::Spinel::SpinelInterface::ReceiveFrameCallback m_receiver_frame_callback; + ReceiveFrameCallback m_receiver_frame_callback; void *m_receiver_frame_context; - ot::Spinel::SpinelInterface::RxFrameBuffer &m_receive_frame_buffer; + RxFrameBuffer *m_receive_frame_buffer; bool m_has_pending_device_frame; spi_device_handle_t m_device; esp_openthread_rcp_failure_handler mRcpFailureHandler; + + otRcpInterfaceMetrics mInterfaceMetrics; }; } // namespace openthread diff --git a/components/openthread/private_include/esp_spinel_interface.hpp b/components/openthread/private_include/esp_spinel_interface.hpp new file mode 100644 index 0000000000..7a393e570d --- /dev/null +++ b/components/openthread/private_include/esp_spinel_interface.hpp @@ -0,0 +1,50 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "esp_err.h" +#include "esp_openthread.h" +#include "esp_openthread_types.h" +#include "hal/uart_types.h" +#include "lib/spinel/spinel_interface.hpp" +#include "lib/hdlc/hdlc.hpp" +#include "openthread/error.h" + +namespace esp { +namespace openthread { + +/** + * This class defines an template to adapt both UartSpinelInterface and SpiSpinelInterface. + * + */ +template class SpinelInterfaceAdapter { +public: + /** + * @brief This constructor of object. + */ + SpinelInterfaceAdapter(void) {} + + /** + * @brief This destructor of the object. + * + */ + ~SpinelInterfaceAdapter(void) {} + + /** + * @brief This method return the underlying spinel interface. + * + * @return The underlying spinel interface. + * + */ + InterfaceType &GetSpinelInterface(void) { return mSpinelInterface; } + +private: + InterfaceType mSpinelInterface; +}; + +} // 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 d21c16a23b..8926ab7fc1 100644 --- a/components/openthread/private_include/esp_uart_spinel_interface.hpp +++ b/components/openthread/private_include/esp_uart_spinel_interface.hpp @@ -21,18 +21,12 @@ namespace openthread { * This class defines an UART interface to the Radio Co-processor (RCP). * */ -class UartSpinelInterface { +class UartSpinelInterface : public ot::Spinel::SpinelInterface { 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. - * */ - UartSpinelInterface(ot::Spinel::SpinelInterface::ReceiveFrameCallback callback, void *callback_context, - ot::Spinel::SpinelInterface::RxFrameBuffer &frame_buffer); + UartSpinelInterface(void); /** * @brief This destructor of the object. @@ -41,65 +35,92 @@ public: ~UartSpinelInterface(void); /** - * @brief This method initializes the HDLC interface. + * 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. * - * @return - * - ESP_OK on success - * - ESP_ERR_NO_MEM if allocation has failed - * - ESP_ERROR on failure */ - esp_err_t Init(const esp_openthread_uart_config_t &radio_uart_config); + otError Init(ReceiveFrameCallback aCallback, void *aCallbackContext, RxFrameBuffer &aFrameBuffer); /** - * @brief This method deinitializes the HDLC interface. + * Deinitializes the interface to the RCP. * */ - esp_err_t Deinit(void); + void Deinit(void); /** - * @brief This method encodes and sends a spinel frame to Radio Co-processor (RCP) over the socket. + * Encodes and sends a spinel frame to Radio Co-processor (RCP) over the socket. * - * @note This is blocking call, i.e., if the socket is not writable, this method waits for it to become writable - * for up to `kMaxWaitTime` interval. + * @param[in] aFrame A pointer to buffer containing the spinel frame to send. + * @param[in] aLength The length (number of bytes) in the frame. * - * @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`. + * @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 *frame, uint16_t length); + otError SendFrame(const uint8_t *aFrame, uint16_t aLength); /** - * This method waits for receiving part or all of spinel frame within specified timeout. + * Waits for receiving part or all of spinel frame within specified interval. * - * @param[in] timeout_us The timeout value in microseconds. + * @param[in] aTimeout 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. + * @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 timeout_us); + otError WaitForFrame(uint64_t aTimeoutUs); /** - * This method performs uart processing to the RCP. + * Updates the file descriptor sets with file descriptors used by the radio driver. * - * @param[in] mainloop The mainloop context + * @param[in,out] aMainloopContext A pointer to the mainloop context. * */ - void Process(const void *mainloop); + void UpdateFdSet(void *aMainloopContext); /** - * This methods updates the mainloop context. + * Performs radio driver processing. * - * @param[inout] mainloop The mainloop context. + * @param[in] aMainloopContext A pointer to the mainloop context. * */ - void Update(void *mainloop); + 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. @@ -109,12 +130,6 @@ public: */ void RegisterRcpFailureHandler(esp_openthread_rcp_failure_handler handler) { mRcpFailureHandler = handler; } - /** - * This method is called when RCP failure detected and resets internal states of the interface. - * - */ - otError HardwareReset(void); - /** * This method is called when RCP is reset to recreate the connection with it. * Intentionally empty. @@ -122,14 +137,25 @@ public: */ otError ResetConnection(void) { return OT_ERROR_NONE; } -private: - enum { - /** - * Maximum spinel frame size. - * - */ - kMaxFrameSize = ot::Spinel::SpinelInterface::kMaxFrameSize, + /** + * @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_uart_config_t &radio_uart_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`). * @@ -152,9 +178,9 @@ private: static void HandleHdlcFrame(void *context, otError error); void HandleHdlcFrame(otError error); - ot::Spinel::SpinelInterface::ReceiveFrameCallback m_receiver_frame_callback; + ReceiveFrameCallback m_receiver_frame_callback; void *m_receiver_frame_context; - ot::Spinel::SpinelInterface::RxFrameBuffer &m_receive_frame_buffer; + RxFrameBuffer *m_receive_frame_buffer; ot::Hdlc::Decoder m_hdlc_decoder; uint8_t *m_uart_rx_buffer; @@ -162,6 +188,8 @@ private: esp_openthread_uart_config_t m_uart_config; int m_uart_fd; + otRcpInterfaceMetrics mInterfaceMetrics; + // Non-copyable, intentionally not implemented. UartSpinelInterface(const UartSpinelInterface &); UartSpinelInterface &operator=(const UartSpinelInterface &); diff --git a/components/openthread/src/esp_openthread_platform.cpp b/components/openthread/src/esp_openthread_platform.cpp index ef46800a78..f6f29e278e 100644 --- a/components/openthread/src/esp_openthread_platform.cpp +++ b/components/openthread/src/esp_openthread_platform.cpp @@ -20,7 +20,7 @@ #include "esp_partition.h" #include "common/code_utils.hpp" #include "common/logging.hpp" -#include "core/common/instance.hpp" +#include "core/instance/instance.hpp" #include "freertos/FreeRTOS.h" #include "freertos/queue.h" #include "openthread/cli.h" diff --git a/components/openthread/src/port/esp_openthread_radio_spinel.cpp b/components/openthread/src/port/esp_openthread_radio_spinel.cpp index 2e0cab92f7..83fdf9b006 100644 --- a/components/openthread/src/port/esp_openthread_radio_spinel.cpp +++ b/components/openthread/src/port/esp_openthread_radio_spinel.cpp @@ -13,6 +13,7 @@ #include "esp_openthread_platform.h" #include "esp_openthread_types.h" #include "esp_system.h" +#include "esp_spinel_interface.hpp" #include "esp_spi_spinel_interface.hpp" #include "esp_uart_spinel_interface.hpp" #include "openthread-core-config.h" @@ -20,41 +21,100 @@ #include "lib/spinel/spinel.h" #include "openthread/platform/diag.h" #include "openthread/platform/radio.h" +#include "platform/exit_code.h" using ot::Spinel::RadioSpinel; +using esp::openthread::SpinelInterfaceAdapter; -#if CONFIG_OPENTHREAD_RADIO_SPINEL_UART +#if CONFIG_OPENTHREAD_RADIO_SPINEL_UART // CONFIG_OPENTHREAD_RADIO_SPINEL_UART using esp::openthread::UartSpinelInterface; -static RadioSpinel s_radio; +static SpinelInterfaceAdapter s_spinel_interface; #else // CONFIG_OPENTHREAD_RADIO_SPINEL_SPI using esp::openthread::SpiSpinelInterface; -static RadioSpinel s_radio; -#endif // CONFIG_OPENTHREAD_RADIO_SPINEL_UART +static SpinelInterfaceAdapter s_spinel_interface; +#endif + +static RadioSpinel s_radio; static const char *radiospinel_workflow = "radio_spinel"; +static const esp_openthread_radio_config_t *s_esp_openthread_radio_config = NULL; + +static void esp_openthread_radio_config_set(const esp_openthread_radio_config_t *config) +{ + s_esp_openthread_radio_config = config; +} + +static const esp_openthread_radio_config_t *esp_openthread_radio_config_get(void) +{ + return s_esp_openthread_radio_config; +} + 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_iid_t iidList[ot::Spinel::kSpinelHeaderMaxNumIid]; + iidList[0] = 0; + + ot::Spinel::RadioSpinelCallbacks callbacks; +#if CONFIG_OPENTHREAD_DIAG + callbacks.mDiagReceiveDone = otPlatDiagRadioReceiveDone; + callbacks.mDiagTransmitDone = otPlatDiagRadioTransmitDone; +#endif // OPENTHREAD_CONFIG_DIAG_ENABLE + callbacks.mEnergyScanDone = otPlatRadioEnergyScanDone; + callbacks.mReceiveDone = otPlatRadioReceiveDone; + callbacks.mTransmitDone = otPlatRadioTxDone; + callbacks.mTxStarted = otPlatRadioTxStarted; + s_radio.SetCallbacks(callbacks); + + esp_openthread_radio_config_set(&config->radio_config); +#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 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, +#else // 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"); -#endif // CONFIG_OPENTHREAD_RADIO_SPINEL_UART - s_radio.Init(/*reset_radio=*/true, /*skip_rcp_compatibility_check=*/false); +#endif + s_radio.Init(s_spinel_interface.GetSpinelInterface(), /*reset_radio=*/true, /*skip_rcp_compatibility_check=*/false, iidList, ot::Spinel::kSpinelHeaderMaxNumIid); +#if CONFIG_OPENTHREAD_RADIO_SPINEL_SPI // CONFIG_OPENTHREAD_RADIO_SPINEL_SPI + ESP_RETURN_ON_ERROR(s_spinel_interface.GetSpinelInterface().AfterRadioInit(), OT_PLAT_LOG_TAG, "Spinel interface init falied"); +#endif return esp_openthread_platform_workflow_register(&esp_openthread_radio_update, &esp_openthread_radio_process, radiospinel_workflow); } void esp_openthread_register_rcp_failure_handler(esp_openthread_rcp_failure_handler handler) { - s_radio.GetSpinelInterface().RegisterRcpFailureHandler(handler); + s_spinel_interface.GetSpinelInterface().RegisterRcpFailureHandler(handler); } -void esp_openthread_rcp_deinit(void) +esp_err_t esp_openthread_rcp_deinit(void) { - s_radio.GetSpinelInterface().Deinit(); + ESP_RETURN_ON_FALSE(otThreadGetDeviceRole(esp_openthread_get_instance()) == OT_DEVICE_ROLE_DISABLED, ESP_ERR_INVALID_STATE, OT_PLAT_LOG_TAG, "Thread is enabled, failed to deinitialize RCP"); + ESP_RETURN_ON_FALSE(!otIp6IsEnabled(esp_openthread_get_instance()), ESP_ERR_INVALID_STATE, OT_PLAT_LOG_TAG, "OT interface is up, failed to deinitialize RCP"); + if (s_radio.IsEnabled()) { + ESP_RETURN_ON_FALSE(s_radio.Sleep() == OT_ERROR_NONE, ESP_ERR_INVALID_STATE, OT_PLAT_LOG_TAG, "Radio fails to sleep"); + ESP_RETURN_ON_FALSE(s_radio.Disable() == OT_ERROR_NONE, ESP_ERR_INVALID_STATE, OT_PLAT_LOG_TAG, "Fail to disable radio"); + } + ESP_RETURN_ON_FALSE(s_spinel_interface.GetSpinelInterface().Disable() == OT_ERROR_NONE, ESP_ERR_INVALID_STATE, OT_PLAT_LOG_TAG, "Fail to deinitialize UART"); + esp_openthread_platform_workflow_unregister(radiospinel_workflow); + return ESP_OK; +} + +esp_err_t esp_openthread_rcp_init(void) +{ + const esp_openthread_radio_config_t *radio_config = esp_openthread_radio_config_get(); +#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 falied"); +#else // 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 + + ESP_RETURN_ON_FALSE(s_radio.Enable(esp_openthread_get_instance()) == OT_ERROR_NONE, ESP_FAIL, OT_PLAT_LOG_TAG, "Fail to enable radio"); + s_radio.RestoreProperties(); + return esp_openthread_platform_workflow_register(&esp_openthread_radio_update, &esp_openthread_radio_process, + radiospinel_workflow); } void esp_openthread_radio_deinit(void) @@ -72,7 +132,7 @@ esp_err_t esp_openthread_radio_process(otInstance *instance, const esp_openthrea void esp_openthread_radio_update(esp_openthread_mainloop_context_t *mainloop) { - s_radio.GetSpinelInterface().Update((void *)mainloop); + s_spinel_interface.GetSpinelInterface().UpdateFdSet((void *)mainloop); } void otPlatRadioGetIeeeEui64(otInstance *instance, uint8_t *ieee_eui64) diff --git a/components/openthread/src/port/esp_spi_spinel_interface.cpp b/components/openthread/src/port/esp_spi_spinel_interface.cpp index bc5cd7e52c..b89eff6779 100644 --- a/components/openthread/src/port/esp_spi_spinel_interface.cpp +++ b/components/openthread/src/port/esp_spi_spinel_interface.cpp @@ -25,17 +25,39 @@ using ot::Spinel::SpinelInterface; namespace esp { namespace openthread { -SpiSpinelInterface::SpiSpinelInterface(SpinelInterface::ReceiveFrameCallback callback, void *callback_context, - SpinelInterface::RxFrameBuffer &frame_buffer) +SpiSpinelInterface::SpiSpinelInterface(void) : m_event_fd(-1) - , m_receiver_frame_callback(callback) - , m_receiver_frame_context(callback_context) - , m_receive_frame_buffer(frame_buffer) + , m_receiver_frame_callback(nullptr) + , m_receiver_frame_context(nullptr) + , m_receive_frame_buffer(nullptr) , mRcpFailureHandler(nullptr) { } -esp_err_t SpiSpinelInterface::Init(const esp_openthread_spi_host_config_t &spi_config) +SpiSpinelInterface::~SpiSpinelInterface(void) +{ + Deinit(); +} + +otError SpiSpinelInterface::Init(ReceiveFrameCallback aCallback, void *aCallbackContext, RxFrameBuffer &aFrameBuffer) +{ + otError error = OT_ERROR_NONE; + + m_receiver_frame_callback = aCallback; + m_receiver_frame_context = aCallbackContext; + m_receive_frame_buffer = &aFrameBuffer; + + return error; +} + +void SpiSpinelInterface::Deinit(void) +{ + m_receiver_frame_callback = nullptr; + m_receiver_frame_context = nullptr; + m_receive_frame_buffer = nullptr; +} + +esp_err_t SpiSpinelInterface::Enable(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; @@ -62,10 +84,16 @@ esp_err_t SpiSpinelInterface::Init(const esp_openthread_spi_host_config_t &spi_c ESP_LOGI(OT_PLAT_LOG_TAG, "spinel SPI interface initialization completed"); + return ESP_OK; +} + +esp_err_t SpiSpinelInterface::AfterRadioInit(void) +{ return ConductSPITransaction(true, 0, 0); } -esp_err_t SpiSpinelInterface::Deinit(void) + +esp_err_t SpiSpinelInterface::Disable(void) { if (m_event_fd >= 0) { close(m_event_fd); @@ -80,11 +108,6 @@ esp_err_t SpiSpinelInterface::Deinit(void) 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"); @@ -113,13 +136,13 @@ esp_err_t SpiSpinelInterface::ConductSPITransaction(bool reset, uint16_t tx_data tx_frame.SetHeaderAcceptLen(rx_data_size); uint8_t *rx_buffer; - otError err = m_receive_frame_buffer.SetSkipLength(kSPIFrameHeaderSize); + 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(); + 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; @@ -143,7 +166,7 @@ esp_err_t SpiSpinelInterface::ConductSPITransaction(bool reset, uint16_t tx_data if (rx_frame.IsResetFlagSet()) { ESP_LOGW(OT_PLAT_LOG_TAG, "RCP Reset"); - m_receive_frame_buffer.DiscardFrame(); + m_receive_frame_buffer->DiscardFrame(); return ESP_OK; } if (rx_frame.GetHeaderDataLen() == 0 && rx_frame.GetHeaderAcceptLen() == 0) { @@ -156,16 +179,16 @@ esp_err_t SpiSpinelInterface::ConductSPITransaction(bool reset, uint16_t tx_data 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) { + 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(); + 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_receive_frame_buffer->DiscardFrame(); } m_pending_data_len = 0; @@ -180,26 +203,26 @@ void SpiSpinelInterface::GpioIntrHandler(void *arg) write(instance->m_event_fd, &event, sizeof(event)); } -void SpiSpinelInterface::Update(void *mainloop) +void SpiSpinelInterface::UpdateFdSet(void *aMainloopContext) { if (m_pending_data_len > 0) { - ((esp_openthread_mainloop_context_t *)mainloop)->timeout.tv_sec = 0; - ((esp_openthread_mainloop_context_t *)mainloop)->timeout.tv_usec = 0; + ((esp_openthread_mainloop_context_t *)aMainloopContext)->timeout.tv_sec = 0; + ((esp_openthread_mainloop_context_t *)aMainloopContext)->timeout.tv_usec = 0; } - FD_SET(m_event_fd, &((esp_openthread_mainloop_context_t *)mainloop)->read_fds); - FD_SET(m_event_fd, &((esp_openthread_mainloop_context_t *)mainloop)->error_fds); - if (m_event_fd > ((esp_openthread_mainloop_context_t *)mainloop)->max_fd) { - ((esp_openthread_mainloop_context_t *)mainloop)->max_fd = m_event_fd; + FD_SET(m_event_fd, &((esp_openthread_mainloop_context_t *)aMainloopContext)->read_fds); + FD_SET(m_event_fd, &((esp_openthread_mainloop_context_t *)aMainloopContext)->error_fds); + if (m_event_fd > ((esp_openthread_mainloop_context_t *)aMainloopContext)->max_fd) { + ((esp_openthread_mainloop_context_t *)aMainloopContext)->max_fd = m_event_fd; } } -void SpiSpinelInterface::Process(const void *mainloop) +void SpiSpinelInterface::Process(const void *aMainloopContext) { - if (FD_ISSET(m_event_fd, &((esp_openthread_mainloop_context_t *)mainloop)->error_fds)) { + if (FD_ISSET(m_event_fd, &((esp_openthread_mainloop_context_t *)aMainloopContext)->error_fds)) { ESP_LOGE(OT_PLAT_LOG_TAG, "SPI INTR GPIO error event"); return; } - if (FD_ISSET(m_event_fd, &((esp_openthread_mainloop_context_t *)mainloop)->read_fds)) { + if (FD_ISSET(m_event_fd, &((esp_openthread_mainloop_context_t *)aMainloopContext)->read_fds)) { uint64_t event; read(m_event_fd, &event, sizeof(event)); m_pending_data_len = SpinelInterface::kMaxFrameSize; @@ -249,5 +272,10 @@ otError SpiSpinelInterface::HardwareReset(void) return OT_ERROR_NONE; } +uint32_t SpiSpinelInterface::GetBusSpeed(void) const +{ + return m_spi_config.spi_device.clock_speed_hz; +} + } // namespace openthread } // namespace esp diff --git a/components/openthread/src/port/esp_uart_spinel_interface.cpp b/components/openthread/src/port/esp_uart_spinel_interface.cpp index fc32043ed1..ec07bd6d80 100644 --- a/components/openthread/src/port/esp_uart_spinel_interface.cpp +++ b/components/openthread/src/port/esp_uart_spinel_interface.cpp @@ -27,13 +27,10 @@ namespace esp { namespace openthread { -UartSpinelInterface::UartSpinelInterface(ot::Spinel::SpinelInterface::ReceiveFrameCallback callback, - void *callback_context, - ot::Spinel::SpinelInterface::RxFrameBuffer &frame_buffer) - : m_receiver_frame_callback(callback) - , m_receiver_frame_context(callback_context) - , m_receive_frame_buffer(frame_buffer) - , m_hdlc_decoder(frame_buffer, HandleHdlcFrame, this) +UartSpinelInterface::UartSpinelInterface(void) + : m_receiver_frame_callback(nullptr) + , m_receiver_frame_context(nullptr) + , m_receive_frame_buffer(nullptr) , m_uart_fd(-1) , mRcpFailureHandler(nullptr) { @@ -41,9 +38,29 @@ UartSpinelInterface::UartSpinelInterface(ot::Spinel::SpinelInterface::ReceiveFra UartSpinelInterface::~UartSpinelInterface(void) { + Deinit(); } -esp_err_t UartSpinelInterface::Init(const esp_openthread_uart_config_t &radio_uart_config) +otError UartSpinelInterface::Init(ReceiveFrameCallback aCallback, void *aCallbackContext, RxFrameBuffer &aFrameBuffer) +{ + otError error = OT_ERROR_NONE; + + m_receiver_frame_callback = aCallback; + m_receiver_frame_context = aCallbackContext; + m_receive_frame_buffer = &aFrameBuffer; + m_hdlc_decoder.Init(aFrameBuffer, HandleHdlcFrame, this); + + return error; +} + +void UartSpinelInterface::Deinit(void) +{ + m_receiver_frame_callback = nullptr; + m_receiver_frame_context = nullptr; + m_receive_frame_buffer = nullptr; +} + +esp_err_t UartSpinelInterface::Enable(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)); @@ -56,7 +73,7 @@ esp_err_t UartSpinelInterface::Init(const esp_openthread_uart_config_t &radio_ua return error; } -esp_err_t UartSpinelInterface::Deinit(void) +esp_err_t UartSpinelInterface::Disable(void) { if (m_uart_rx_buffer) { heap_caps_free(m_uart_rx_buffer); @@ -88,24 +105,14 @@ exit: return error; } -void UartSpinelInterface::Process(const void *mainloop) +void UartSpinelInterface::Process(const void *aMainloopContext) { - if (FD_ISSET(m_uart_fd, &((esp_openthread_mainloop_context_t *)mainloop)->read_fds)) { + if (FD_ISSET(m_uart_fd, &((esp_openthread_mainloop_context_t *)aMainloopContext)->read_fds)) { ESP_LOGD(OT_PLAT_LOG_TAG, "radio uart read event"); TryReadAndDecode(); } } -void UartSpinelInterface::Update(void *mainloop) -{ - // Register only READ events for radio UART and always wait - // for a radio WRITE to complete. - FD_SET(m_uart_fd, &((esp_openthread_mainloop_context_t *)mainloop)->read_fds); - if (m_uart_fd > ((esp_openthread_mainloop_context_t *)mainloop)->max_fd) { - ((esp_openthread_mainloop_context_t *)mainloop)->max_fd = m_uart_fd; - } -} - int UartSpinelInterface::TryReadAndDecode(void) { uint8_t buffer[UART_HW_FIFO_LEN(m_uart_config.port)]; @@ -246,7 +253,7 @@ void UartSpinelInterface::HandleHdlcFrame(otError error) m_receiver_frame_callback(m_receiver_frame_context); } else { ESP_LOGE(OT_PLAT_LOG_TAG, "dropping radio frame: %s", otThreadErrorToString(error)); - m_receive_frame_buffer.DiscardFrame(); + m_receive_frame_buffer->DiscardFrame(); } } @@ -294,5 +301,20 @@ otError UartSpinelInterface::HardwareReset(void) return OT_ERROR_NONE; } +void UartSpinelInterface::UpdateFdSet(void *aMainloopContext) +{ + // Register only READ events for radio UART and always wait + // for a radio WRITE to complete. + FD_SET(m_uart_fd, &((esp_openthread_mainloop_context_t *)aMainloopContext)->read_fds); + if (m_uart_fd > ((esp_openthread_mainloop_context_t *)aMainloopContext)->max_fd) { + ((esp_openthread_mainloop_context_t *)aMainloopContext)->max_fd = m_uart_fd; + } +} + +uint32_t UartSpinelInterface::GetBusSpeed(void) const +{ + return m_uart_config.uart_config.baud_rate; +} + } // namespace openthread } // namespace esp