feat(openthread): modification of uart and spi spinel based on openthread 41ef807

This commit is contained in:
Xu Si Yu 2023-11-22 19:15:55 +08:00 committed by zwx
parent 6221119c2f
commit 7e469f1330
12 changed files with 404 additions and 157 deletions

View File

@ -10,6 +10,7 @@ if(CONFIG_OPENTHREAD_ENABLED)
"openthread/include/openthread" "openthread/include/openthread"
"openthread/src" "openthread/src"
"openthread/src/core" "openthread/src/core"
"openthread/src/lib"
"openthread/src/lib/hdlc" "openthread/src/lib/hdlc"
"openthread/src/lib/spinel" "openthread/src/lib/spinel"
"openthread/src/ncp" "openthread/src/ncp"
@ -23,6 +24,7 @@ if(CONFIG_OPENTHREAD_ENABLED)
"openthread/src/core/common" "openthread/src/core/common"
"openthread/src/core/crypto" "openthread/src/core/crypto"
"openthread/src/core/diags" "openthread/src/core/diags"
"openthread/src/core/instance"
"openthread/src/core/mac" "openthread/src/core/mac"
"openthread/src/core/radio" "openthread/src/core/radio"
"openthread/src/core/thread" "openthread/src/core/thread"
@ -33,7 +35,7 @@ if(CONFIG_OPENTHREAD_ENABLED)
set(exclude_srcs set(exclude_srcs
"openthread/examples/platforms/utils/logging_rtt.c" "openthread/examples/platforms/utils/logging_rtt.c"
"openthread/examples/platforms/utils/soft_source_match_table.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) if(CONFIG_OPENTHREAD_FTD OR CONFIG_OPENTHREAD_MTD)
list(APPEND src_dirs list(APPEND src_dirs

View File

@ -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 * 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. * @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 #ifdef __cplusplus
} }

@ -1 +1 @@
Subproject commit 12f563ee490236f7332eb22f568e71c7c1d4a3b7 Subproject commit 648c28e792567bc00602c92e43518c1784599251

@ -1 +1 @@
Subproject commit af5938e389be40650507748272bb6c6b3a2de2cf Subproject commit 41ef80717f4b757440125932723cc8721ef42f7f

View File

@ -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 * SPDX-License-Identifier: Apache-2.0
*/ */

View File

@ -14,18 +14,12 @@
namespace esp { namespace esp {
namespace openthread { namespace openthread {
class SpiSpinelInterface { class SpiSpinelInterface : public ot::Spinel::SpinelInterface {
public: public:
/** /**
* @brief This constructor of object. * @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, SpiSpinelInterface();
ot::Spinel::SpinelInterface::RxFrameBuffer &frame_buffer);
/** /**
* @brief This destructor of the object. * @brief This destructor of the object.
@ -34,24 +28,26 @@ public:
~SpiSpinelInterface(void); ~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. * @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); 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. * This methods registers the callback for RCP failure.
@ -111,10 +129,33 @@ public:
otError ResetConnection(void) { return OT_ERROR_NONE; } 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: private:
static constexpr uint8_t kSPIFrameHeaderSize = 5; static constexpr uint8_t kSPIFrameHeaderSize = 5;
@ -130,14 +171,16 @@ private:
int m_event_fd; int m_event_fd;
volatile uint16_t m_pending_data_len; 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; void *m_receiver_frame_context;
ot::Spinel::SpinelInterface::RxFrameBuffer &m_receive_frame_buffer; RxFrameBuffer *m_receive_frame_buffer;
bool m_has_pending_device_frame; bool m_has_pending_device_frame;
spi_device_handle_t m_device; spi_device_handle_t m_device;
esp_openthread_rcp_failure_handler mRcpFailureHandler; esp_openthread_rcp_failure_handler mRcpFailureHandler;
otRcpInterfaceMetrics mInterfaceMetrics;
}; };
} // namespace openthread } // namespace openthread

View File

@ -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 <typename InterfaceType> 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

View File

@ -21,18 +21,12 @@ namespace openthread {
* This class defines an UART interface to the Radio Co-processor (RCP). * This class defines an UART interface to the Radio Co-processor (RCP).
* *
*/ */
class UartSpinelInterface { class UartSpinelInterface : public ot::Spinel::SpinelInterface {
public: public:
/** /**
* @brief This constructor of object. * @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, UartSpinelInterface(void);
ot::Spinel::SpinelInterface::RxFrameBuffer &frame_buffer);
/** /**
* @brief This destructor of the object. * @brief This destructor of the object.
@ -41,65 +35,92 @@ public:
~UartSpinelInterface(void); ~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 * @param[in] aFrame A pointer to buffer containing the spinel frame to send.
* for up to `kMaxWaitTime` interval. * @param[in] aLength The length (number of bytes) in the frame.
* *
* @param[in] frame A pointer to buffer containing the spinel frame to send. * @retval OT_ERROR_NONE Successfully encoded and sent the spinel frame.
* @param[in] length The length (number of bytes) in the 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.
* @return * @retval OT_ERROR_FAILED Failed to call the SPI driver to send the frame.
* -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); 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 * @retval OT_ERROR_NONE Part or all of spinel frame is received.
* -OT_ERROR_NONE Part or all of spinel frame is received. * @retval OT_ERROR_RESPONSE_TIMEOUT No spinel frame is received within @p aTimeout.
* -OT_ERROR_RESPONSE_TIMEOUT No spinel frame is received within @p timeout_us.
* *
*/ */
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. * This methods registers the callback for RCP failure.
@ -109,12 +130,6 @@ public:
*/ */
void RegisterRcpFailureHandler(esp_openthread_rcp_failure_handler handler) { mRcpFailureHandler = handler; } 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. * This method is called when RCP is reset to recreate the connection with it.
* Intentionally empty. * Intentionally empty.
@ -122,14 +137,25 @@ public:
*/ */
otError ResetConnection(void) { return OT_ERROR_NONE; } otError ResetConnection(void) { return OT_ERROR_NONE; }
private: /**
enum { * @brief This method enable the HDLC interface.
/** *
* Maximum spinel frame size. * @return
* * - ESP_OK on success
*/ * - ESP_ERR_NO_MEM if allocation has failed
kMaxFrameSize = ot::Spinel::SpinelInterface::kMaxFrameSize, * - 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`). * Maximum wait time in Milliseconds for socket to become writable (see `SendFrame`).
* *
@ -152,9 +178,9 @@ private:
static void HandleHdlcFrame(void *context, otError error); static void HandleHdlcFrame(void *context, otError error);
void HandleHdlcFrame(otError error); void HandleHdlcFrame(otError error);
ot::Spinel::SpinelInterface::ReceiveFrameCallback m_receiver_frame_callback; ReceiveFrameCallback m_receiver_frame_callback;
void *m_receiver_frame_context; void *m_receiver_frame_context;
ot::Spinel::SpinelInterface::RxFrameBuffer &m_receive_frame_buffer; RxFrameBuffer *m_receive_frame_buffer;
ot::Hdlc::Decoder m_hdlc_decoder; ot::Hdlc::Decoder m_hdlc_decoder;
uint8_t *m_uart_rx_buffer; uint8_t *m_uart_rx_buffer;
@ -162,6 +188,8 @@ private:
esp_openthread_uart_config_t m_uart_config; esp_openthread_uart_config_t m_uart_config;
int m_uart_fd; int m_uart_fd;
otRcpInterfaceMetrics mInterfaceMetrics;
// Non-copyable, intentionally not implemented. // Non-copyable, intentionally not implemented.
UartSpinelInterface(const UartSpinelInterface &); UartSpinelInterface(const UartSpinelInterface &);
UartSpinelInterface &operator=(const UartSpinelInterface &); UartSpinelInterface &operator=(const UartSpinelInterface &);

View File

@ -20,7 +20,7 @@
#include "esp_partition.h" #include "esp_partition.h"
#include "common/code_utils.hpp" #include "common/code_utils.hpp"
#include "common/logging.hpp" #include "common/logging.hpp"
#include "core/common/instance.hpp" #include "core/instance/instance.hpp"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/queue.h" #include "freertos/queue.h"
#include "openthread/cli.h" #include "openthread/cli.h"

View File

@ -13,6 +13,7 @@
#include "esp_openthread_platform.h" #include "esp_openthread_platform.h"
#include "esp_openthread_types.h" #include "esp_openthread_types.h"
#include "esp_system.h" #include "esp_system.h"
#include "esp_spinel_interface.hpp"
#include "esp_spi_spinel_interface.hpp" #include "esp_spi_spinel_interface.hpp"
#include "esp_uart_spinel_interface.hpp" #include "esp_uart_spinel_interface.hpp"
#include "openthread-core-config.h" #include "openthread-core-config.h"
@ -20,41 +21,100 @@
#include "lib/spinel/spinel.h" #include "lib/spinel/spinel.h"
#include "openthread/platform/diag.h" #include "openthread/platform/diag.h"
#include "openthread/platform/radio.h" #include "openthread/platform/radio.h"
#include "platform/exit_code.h"
using ot::Spinel::RadioSpinel; 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; using esp::openthread::UartSpinelInterface;
static RadioSpinel<UartSpinelInterface> s_radio; static SpinelInterfaceAdapter<UartSpinelInterface> s_spinel_interface;
#else // CONFIG_OPENTHREAD_RADIO_SPINEL_SPI #else // CONFIG_OPENTHREAD_RADIO_SPINEL_SPI
using esp::openthread::SpiSpinelInterface; using esp::openthread::SpiSpinelInterface;
static RadioSpinel<SpiSpinelInterface> s_radio; static SpinelInterfaceAdapter<SpiSpinelInterface> s_spinel_interface;
#endif // CONFIG_OPENTHREAD_RADIO_SPINEL_UART #endif
static RadioSpinel s_radio;
static const char *radiospinel_workflow = "radio_spinel"; 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) esp_err_t esp_openthread_radio_init(const esp_openthread_platform_config_t *config)
{ {
#if CONFIG_OPENTHREAD_RADIO_SPINEL_UART spinel_iid_t iidList[ot::Spinel::kSpinelHeaderMaxNumIid];
ESP_RETURN_ON_ERROR(s_radio.GetSpinelInterface().Init(config->radio_config.radio_uart_config), OT_PLAT_LOG_TAG, 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"); "Spinel interface init falied");
#else // CONFIG_OPENTHREAD_RADIO_SPINEL_SPI #else // CONFIG_OPENTHREAD_RADIO_SPINEL_SPI
ESP_RETURN_ON_ERROR(s_radio.GetSpinelInterface().Init(config->radio_config.radio_spi_config), OT_PLAT_LOG_TAG, ESP_RETURN_ON_ERROR(s_spinel_interface.GetSpinelInterface().Enable(config->radio_config.radio_spi_config), OT_PLAT_LOG_TAG,
"Spinel interface init failed"); "Spinel interface init failed");
#endif // CONFIG_OPENTHREAD_RADIO_SPINEL_UART #endif
s_radio.Init(/*reset_radio=*/true, /*skip_rcp_compatibility_check=*/false); 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, return esp_openthread_platform_workflow_register(&esp_openthread_radio_update, &esp_openthread_radio_process,
radiospinel_workflow); radiospinel_workflow);
} }
void esp_openthread_register_rcp_failure_handler(esp_openthread_rcp_failure_handler handler) 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) 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) 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) void otPlatRadioGetIeeeEui64(otInstance *instance, uint8_t *ieee_eui64)

View File

@ -25,17 +25,39 @@ using ot::Spinel::SpinelInterface;
namespace esp { namespace esp {
namespace openthread { namespace openthread {
SpiSpinelInterface::SpiSpinelInterface(SpinelInterface::ReceiveFrameCallback callback, void *callback_context, SpiSpinelInterface::SpiSpinelInterface(void)
SpinelInterface::RxFrameBuffer &frame_buffer)
: m_event_fd(-1) : m_event_fd(-1)
, m_receiver_frame_callback(callback) , m_receiver_frame_callback(nullptr)
, m_receiver_frame_context(callback_context) , m_receiver_frame_context(nullptr)
, m_receive_frame_buffer(frame_buffer) , m_receive_frame_buffer(nullptr)
, mRcpFailureHandler(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"); 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; 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"); 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); return ConductSPITransaction(true, 0, 0);
} }
esp_err_t SpiSpinelInterface::Deinit(void)
esp_err_t SpiSpinelInterface::Disable(void)
{ {
if (m_event_fd >= 0) { if (m_event_fd >= 0) {
close(m_event_fd); close(m_event_fd);
@ -80,11 +108,6 @@ esp_err_t SpiSpinelInterface::Deinit(void)
return ESP_OK; return ESP_OK;
} }
SpiSpinelInterface::~SpiSpinelInterface(void)
{
Deinit();
}
otError SpiSpinelInterface::SendFrame(const uint8_t *frame, uint16_t length) 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(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); tx_frame.SetHeaderAcceptLen(rx_data_size);
uint8_t *rx_buffer; 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"); 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; rx_buffer = m_receive_frame_buffer->GetFrame() - kSPIFrameHeaderSize;
if (m_receive_frame_buffer.GetFrameMaxLength() < rx_data_size) { if (m_receive_frame_buffer->GetFrameMaxLength() < rx_data_size) {
rx_data_size = m_receive_frame_buffer.GetFrameMaxLength(); rx_data_size = m_receive_frame_buffer->GetFrameMaxLength();
} }
uint16_t data_size = tx_data_size > rx_data_size ? tx_data_size : rx_data_size; uint16_t data_size = tx_data_size > rx_data_size ? tx_data_size : rx_data_size;
data_size += kSPIFrameHeaderSize; data_size += kSPIFrameHeaderSize;
@ -143,7 +166,7 @@ esp_err_t SpiSpinelInterface::ConductSPITransaction(bool reset, uint16_t tx_data
if (rx_frame.IsResetFlagSet()) { if (rx_frame.IsResetFlagSet()) {
ESP_LOGW(OT_PLAT_LOG_TAG, "RCP Reset"); ESP_LOGW(OT_PLAT_LOG_TAG, "RCP Reset");
m_receive_frame_buffer.DiscardFrame(); m_receive_frame_buffer->DiscardFrame();
return ESP_OK; return ESP_OK;
} }
if (rx_frame.GetHeaderDataLen() == 0 && rx_frame.GetHeaderAcceptLen() == 0) { 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) { if (gpio_get_level(m_spi_config.intr_pin) == 1) {
m_pending_data_len = 0; 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...", ESP_LOGW(OT_PLAT_LOG_TAG, "insufficient buffer space to hold a frame of length %d...",
rx_frame.GetHeaderDataLen()); rx_frame.GetHeaderDataLen());
m_receive_frame_buffer.DiscardFrame(); m_receive_frame_buffer->DiscardFrame();
return ESP_ERR_NO_MEM; return ESP_ERR_NO_MEM;
} }
m_receiver_frame_callback(m_receiver_frame_context); m_receiver_frame_callback(m_receiver_frame_context);
} else { } else {
m_pending_data_len = 0; m_pending_data_len = 0;
m_receive_frame_buffer.DiscardFrame(); m_receive_frame_buffer->DiscardFrame();
} }
m_pending_data_len = 0; m_pending_data_len = 0;
@ -180,26 +203,26 @@ void SpiSpinelInterface::GpioIntrHandler(void *arg)
write(instance->m_event_fd, &event, sizeof(event)); write(instance->m_event_fd, &event, sizeof(event));
} }
void SpiSpinelInterface::Update(void *mainloop) void SpiSpinelInterface::UpdateFdSet(void *aMainloopContext)
{ {
if (m_pending_data_len > 0) { if (m_pending_data_len > 0) {
((esp_openthread_mainloop_context_t *)mainloop)->timeout.tv_sec = 0; ((esp_openthread_mainloop_context_t *)aMainloopContext)->timeout.tv_sec = 0;
((esp_openthread_mainloop_context_t *)mainloop)->timeout.tv_usec = 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 *)aMainloopContext)->read_fds);
FD_SET(m_event_fd, &((esp_openthread_mainloop_context_t *)mainloop)->error_fds); FD_SET(m_event_fd, &((esp_openthread_mainloop_context_t *)aMainloopContext)->error_fds);
if (m_event_fd > ((esp_openthread_mainloop_context_t *)mainloop)->max_fd) { if (m_event_fd > ((esp_openthread_mainloop_context_t *)aMainloopContext)->max_fd) {
((esp_openthread_mainloop_context_t *)mainloop)->max_fd = m_event_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"); ESP_LOGE(OT_PLAT_LOG_TAG, "SPI INTR GPIO error event");
return; 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; uint64_t event;
read(m_event_fd, &event, sizeof(event)); read(m_event_fd, &event, sizeof(event));
m_pending_data_len = SpinelInterface::kMaxFrameSize; m_pending_data_len = SpinelInterface::kMaxFrameSize;
@ -249,5 +272,10 @@ otError SpiSpinelInterface::HardwareReset(void)
return OT_ERROR_NONE; return OT_ERROR_NONE;
} }
uint32_t SpiSpinelInterface::GetBusSpeed(void) const
{
return m_spi_config.spi_device.clock_speed_hz;
}
} // namespace openthread } // namespace openthread
} // namespace esp } // namespace esp

View File

@ -27,13 +27,10 @@
namespace esp { namespace esp {
namespace openthread { namespace openthread {
UartSpinelInterface::UartSpinelInterface(ot::Spinel::SpinelInterface::ReceiveFrameCallback callback, UartSpinelInterface::UartSpinelInterface(void)
void *callback_context, : m_receiver_frame_callback(nullptr)
ot::Spinel::SpinelInterface::RxFrameBuffer &frame_buffer) , m_receiver_frame_context(nullptr)
: m_receiver_frame_callback(callback) , m_receive_frame_buffer(nullptr)
, m_receiver_frame_context(callback_context)
, m_receive_frame_buffer(frame_buffer)
, m_hdlc_decoder(frame_buffer, HandleHdlcFrame, this)
, m_uart_fd(-1) , m_uart_fd(-1)
, mRcpFailureHandler(nullptr) , mRcpFailureHandler(nullptr)
{ {
@ -41,9 +38,29 @@ UartSpinelInterface::UartSpinelInterface(ot::Spinel::SpinelInterface::ReceiveFra
UartSpinelInterface::~UartSpinelInterface(void) 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; esp_err_t error = ESP_OK;
m_uart_rx_buffer = static_cast<uint8_t *>(heap_caps_malloc(kMaxFrameSize, MALLOC_CAP_8BIT)); m_uart_rx_buffer = static_cast<uint8_t *>(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; return error;
} }
esp_err_t UartSpinelInterface::Deinit(void) esp_err_t UartSpinelInterface::Disable(void)
{ {
if (m_uart_rx_buffer) { if (m_uart_rx_buffer) {
heap_caps_free(m_uart_rx_buffer); heap_caps_free(m_uart_rx_buffer);
@ -88,24 +105,14 @@ exit:
return error; 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"); ESP_LOGD(OT_PLAT_LOG_TAG, "radio uart read event");
TryReadAndDecode(); 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) int UartSpinelInterface::TryReadAndDecode(void)
{ {
uint8_t buffer[UART_FIFO_LEN]; uint8_t buffer[UART_FIFO_LEN];
@ -246,7 +253,7 @@ void UartSpinelInterface::HandleHdlcFrame(otError error)
m_receiver_frame_callback(m_receiver_frame_context); m_receiver_frame_callback(m_receiver_frame_context);
} else { } else {
ESP_LOGE(OT_PLAT_LOG_TAG, "dropping radio frame: %s", otThreadErrorToString(error)); 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; 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 openthread
} // namespace esp } // namespace esp