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
parent 0281aa844b
commit e88dc25226
12 changed files with 404 additions and 157 deletions

View File

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

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
*/
@ -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
}

@ -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
*/

View File

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

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).
*
*/
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 &);

View File

@ -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"

View File

@ -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<UartSpinelInterface> s_radio;
static SpinelInterfaceAdapter<UartSpinelInterface> s_spinel_interface;
#else // CONFIG_OPENTHREAD_RADIO_SPINEL_SPI
using esp::openthread::SpiSpinelInterface;
static RadioSpinel<SpiSpinelInterface> s_radio;
#endif // CONFIG_OPENTHREAD_RADIO_SPINEL_UART
static SpinelInterfaceAdapter<SpiSpinelInterface> 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)

View File

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

View File

@ -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<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;
}
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