feat(openthread): optimize radio spinel uart interface

This commit is contained in:
Xu Si Yu
2026-03-04 15:24:41 +08:00
committed by BOT
parent 2994fca5ba
commit 2d1cbe418c
9 changed files with 150 additions and 552 deletions
+7
View File
@@ -604,6 +604,13 @@ menu "OpenThread"
help
The maximum number of backoffs the CSMA-CA algorithm will attempt before declaring a channel access
failure.
config OPENTHREAD_SPINEL_UART_DRIVER_BUFFER_SIZE
int "UART driver buffer size for radio spinel interface"
depends on OPENTHREAD_SPINEL_ONLY || OPENTHREAD_RADIO_SPINEL_UART
default 1024
help
RX buffer size passed to uart_driver_install() for the radio spinel UART interface.
endmenu
menuconfig OPENTHREAD_DEBUG
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -72,12 +72,23 @@ void esp_radio_spinel_set_callbacks(const esp_radio_spinel_callbacks_t aCallback
* @note This function should be called before `esp_radio_spinel_init`.
*
* @param[in] radio_uart_config The config of UART for radio spinel.
* @param[in] aUartInitHandler The function for UART initialization
* @param[in] aUartDeinitHandler The function for UART deinitialization
* Must not be nullptr.
*
* @param[in] aUartInitHandler Optional UART initialization handler.
* If provided (not nullptr), this handler will be used
* to initialize the UART. Otherwise, a default
* internal implementation will be used.
*
* @param[in] aUartDeinitHandler Optional UART deinitialization handler.
* If provided (not nullptr), this handler will be used
* to deinitialize the UART. Otherwise, a default
* internal implementation will be used.
*
* @param[in] idx The index of 802.15.4 related protocol stack.
*
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_ARG if `radio_uart_config` is nullptr
* - ESP_FAIL on failures
*
*/
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -10,6 +10,9 @@
#include "lib/spinel/spinel_interface.hpp"
#include "lib/hdlc/hdlc.hpp"
#include "openthread/error.h"
#if CONFIG_OPENTHREAD_RADIO_SPINEL_UART
#include "esp_openthread_types.h"
#endif
namespace esp {
namespace radio_spinel {
@@ -143,6 +146,9 @@ public:
* - ESP_ERROR on failure
*/
esp_err_t Enable(const esp_radio_spinel_uart_config_t &radio_uart_config);
#if CONFIG_OPENTHREAD_RADIO_SPINEL_UART
esp_err_t Enable(const esp_openthread_uart_config_t &radio_uart_config);
#endif
/**
* @brief This method disable the HDLC interface.
@@ -150,7 +156,13 @@ public:
*/
esp_err_t Disable(void);
void RegisterUartInitHandler(esp_radio_spinel_uart_init_handler handler) { mUartInitHandler = handler; }
void RegisterUartInitHandler(esp_radio_spinel_uart_init_handler handler)
{
if (mUartInitHandler != NULL) {
ESP_LOGW(ESP_SPINEL_LOG_TAG, "UartInitHandler already registered, will overwrite (prev=%p, new=%p)", mUartInitHandler, handler);
}
mUartInitHandler = handler;
}
void RegisterUartDeinitHandler(esp_radio_spinel_uart_deinit_handler handler) { mUartDeinitHandler = handler; }
@@ -1,203 +0,0 @@
/*
* 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 UART interface to the Radio Co-processor (RCP).
*
*/
class UartSpinelInterface : public ot::Spinel::SpinelInterface {
public:
/**
* @brief This constructor of object.
*/
UartSpinelInterface(void);
/**
* @brief This destructor of the object.
*
*/
~UartSpinelInterface(void);
/**
* Initializes the interface to the Radio Co-processor (RCP).
*
* @note This method should be called before reading and sending spinel frames to the interface.
*
* @param[in] aCallback Callback on frame received
* @param[in] aCallbackContext Callback context
* @param[in] aFrameBuffer A reference to a `RxFrameBuffer` object.
*
* @retval OT_ERROR_NONE The interface is initialized successfully
* @retval OT_ERROR_ALREADY The interface is already initialized.
* @retval OT_ERROR_FAILED Failed to initialize the interface.
*
*/
otError Init(ReceiveFrameCallback aCallback, void *aCallbackContext, RxFrameBuffer &aFrameBuffer);
/**
* Deinitializes the interface to the RCP.
*
*/
void Deinit(void);
/**
* Encodes and sends a spinel frame to Radio Co-processor (RCP) over the socket.
*
* @param[in] aFrame A pointer to buffer containing the spinel frame to send.
* @param[in] aLength The length (number of bytes) in the frame.
*
* @retval OT_ERROR_NONE Successfully encoded and sent the spinel frame.
* @retval OT_ERROR_BUSY Failed due to another operation is on going.
* @retval OT_ERROR_NO_BUFS Insufficient buffer space available to encode the frame.
* @retval OT_ERROR_FAILED Failed to call the SPI driver to send the frame.
*
*/
otError SendFrame(const uint8_t *aFrame, uint16_t aLength);
/**
* Waits for receiving part or all of spinel frame within specified interval.
*
* @param[in] aTimeout The timeout value in microseconds.
*
* @retval OT_ERROR_NONE Part or all of spinel frame is received.
* @retval OT_ERROR_RESPONSE_TIMEOUT No spinel frame is received within @p aTimeout.
*
*/
otError WaitForFrame(uint64_t aTimeoutUs);
/**
* Updates the file descriptor sets with file descriptors used by the radio driver.
*
* @param[in,out] aMainloopContext A pointer to the mainloop context.
*
*/
void UpdateFdSet(void *aMainloopContext);
/**
* Performs radio driver processing.
*
* @param[in] aMainloopContext A pointer to the mainloop context.
*
*/
void Process(const void *aMainloopContext);
/**
* Returns the bus speed between the host and the radio.
*
* @returns Bus speed in bits/second.
*
*/
uint32_t GetBusSpeed(void) const;
/**
* Hardware resets the RCP.
*
* @retval OT_ERROR_NONE Successfully reset the RCP.
* @retval OT_ERROR_NOT_IMPLEMENT The hardware reset is not implemented.
*
*/
otError HardwareReset(void);
/**
* Returns the RCP interface metrics.
*
* @returns The RCP interface metrics.
*
*/
const otRcpInterfaceMetrics *GetRcpInterfaceMetrics(void) const { return &mInterfaceMetrics; }
/**
* This methods registers the callback for RCP failure.
*
* @param[in] handler The RCP failure handler.
*
*/
void RegisterRcpFailureHandler(esp_openthread_rcp_failure_handler handler) { mRcpFailureHandler = handler; }
/**
* This method is called when RCP is reset to recreate the connection with it.
* Intentionally empty.
*
*/
otError ResetConnection(void) { return OT_ERROR_NONE; }
/**
* @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`).
*
*/
kMaxWaitTime = 2000,
};
esp_err_t InitUart(const esp_openthread_uart_config_t &radio_uart_config);
esp_err_t DeinitUart(void);
int TryReadAndDecode(void);
otError WaitForWritable(void);
otError Write(const uint8_t *frame, uint16_t length);
esp_err_t TryRecoverUart(void);
static void HandleHdlcFrame(void *context, otError error);
void HandleHdlcFrame(otError error);
ReceiveFrameCallback m_receiver_frame_callback;
void *m_receiver_frame_context;
RxFrameBuffer *m_receive_frame_buffer;
ot::Hdlc::Decoder m_hdlc_decoder;
uint8_t *m_uart_rx_buffer;
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 &);
esp_openthread_rcp_failure_handler mRcpFailureHandler;
ot::Spinel::FrameBuffer<kMaxFrameSize> encoder_buffer;
};
} // namespace openthread
} // namespace esp
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -18,7 +18,7 @@
#include "esp_spinel_interface.hpp"
#include "esp_spinel_ncp_vendor_macro.h"
#include "esp_spi_spinel_interface.hpp"
#include "esp_uart_spinel_interface.hpp"
#include "esp_radio_spinel_uart_interface.hpp"
#include "openthread-core-config.h"
#include "lib/spinel/radio_spinel.hpp"
#include "lib/spinel/spinel.h"
@@ -36,7 +36,7 @@ using esp::openthread::SpinelInterfaceAdapter;
using ot::Spinel::SpinelDriver;
#if CONFIG_OPENTHREAD_RADIO_SPINEL_UART // CONFIG_OPENTHREAD_RADIO_SPINEL_UART
using esp::openthread::UartSpinelInterface;
using esp::radio_spinel::UartSpinelInterface;
static SpinelInterfaceAdapter<UartSpinelInterface> s_spinel_interface;
#else // CONFIG_OPENTHREAD_RADIO_SPINEL_SPI
using esp::openthread::SpiSpinelInterface;
@@ -150,7 +150,8 @@ esp_err_t esp_openthread_rcp_deinit(void)
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_RETURN_ON_ERROR(s_spinel_interface.GetSpinelInterface().Disable(), OT_PLAT_LOG_TAG,
"Fail to deinitialize UART");
esp_openthread_platform_workflow_unregister(radiospinel_workflow);
return ESP_OK;
}
@@ -1,320 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_uart_spinel_interface.hpp"
#include <errno.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/unistd.h>
#include "esp_check.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_openthread_common_macro.h"
#include "esp_openthread_types.h"
#include "esp_openthread_uart.h"
#include "driver/uart_vfs.h"
#include "common/code_utils.hpp"
#include "common/logging.hpp"
#include "driver/uart.h"
#include "lib/platform/exit_code.h"
#include "openthread/platform/time.h"
namespace esp {
namespace openthread {
UartSpinelInterface::UartSpinelInterface(void)
: m_receiver_frame_callback(nullptr)
, m_receiver_frame_context(nullptr)
, m_receive_frame_buffer(nullptr)
, m_uart_fd(-1)
, mRcpFailureHandler(nullptr)
{
}
UartSpinelInterface::~UartSpinelInterface(void)
{
Deinit();
}
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));
if (m_uart_rx_buffer == NULL) {
return ESP_ERR_NO_MEM;
}
error = InitUart(radio_uart_config);
ESP_LOGI(OT_PLAT_LOG_TAG, "spinel UART interface initialization completed");
return error;
}
esp_err_t UartSpinelInterface::Disable(void)
{
if (m_uart_rx_buffer) {
heap_caps_free(m_uart_rx_buffer);
}
m_uart_rx_buffer = NULL;
return DeinitUart();
}
otError UartSpinelInterface::SendFrame(const uint8_t *frame, uint16_t length)
{
otError error = OT_ERROR_NONE;
encoder_buffer.Clear();
ot::Hdlc::Encoder hdlc_encoder(encoder_buffer);
SuccessOrExit(error = hdlc_encoder.BeginFrame());
SuccessOrExit(error = hdlc_encoder.Encode(frame, length));
SuccessOrExit(error = hdlc_encoder.EndFrame());
SuccessOrExit(error = Write(encoder_buffer.GetFrame(), encoder_buffer.GetLength()));
exit:
if (error != OT_ERROR_NONE) {
ESP_LOGE(OT_PLAT_LOG_TAG, "send radio frame failed");
} else {
ESP_LOGD(OT_PLAT_LOG_TAG, "sent radio frame");
}
return error;
}
void UartSpinelInterface::Process(const void *aMainloopContext)
{
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();
}
}
int UartSpinelInterface::TryReadAndDecode(void)
{
uint8_t buffer[UART_HW_FIFO_LEN(m_uart_config.port)];
ssize_t rval;
do {
rval = read(m_uart_fd, buffer, sizeof(buffer));
if (rval > 0) {
m_hdlc_decoder.Decode(buffer, static_cast<uint16_t>(rval));
}
} while (rval > 0);
if ((rval < 0) && (errno != EAGAIN) && (errno != EWOULDBLOCK)) {
ESP_ERROR_CHECK(TryRecoverUart());
}
return rval;
}
otError UartSpinelInterface::WaitForWritable(void)
{
otError error = OT_ERROR_NONE;
struct timeval timeout = {kMaxWaitTime / MS_PER_S, (kMaxWaitTime % MS_PER_S) * US_PER_MS};
uint64_t now = otPlatTimeGet();
uint64_t end = now + kMaxWaitTime * US_PER_MS;
fd_set write_fds;
fd_set error_fds;
int rval;
while (true) {
FD_ZERO(&write_fds);
FD_ZERO(&error_fds);
FD_SET(m_uart_fd, &write_fds);
FD_SET(m_uart_fd, &error_fds);
rval = select(m_uart_fd + 1, NULL, &write_fds, &error_fds, &timeout);
if (rval > 0) {
if (FD_ISSET(m_uart_fd, &write_fds)) {
ExitNow();
} else if (FD_ISSET(m_uart_fd, &error_fds)) {
ExitNow(error = OT_ERROR_FAILED);
}
} else if ((rval < 0) && (errno != EINTR)) {
ESP_ERROR_CHECK(TryRecoverUart());
ExitNow(error = OT_ERROR_FAILED);
}
now = otPlatTimeGet();
if (end > now) {
uint64_t remain = end - now;
timeout.tv_sec = static_cast<time_t>(remain / 1000000);
timeout.tv_usec = static_cast<suseconds_t>(remain % 1000000);
} else {
break;
}
}
error = OT_ERROR_FAILED;
exit:
return error;
}
otError UartSpinelInterface::Write(const uint8_t *aFrame, uint16_t length)
{
otError error = OT_ERROR_NONE;
while (length) {
ssize_t rval;
rval = write(m_uart_fd, aFrame, length);
if (rval > 0) {
assert(rval <= length);
length -= static_cast<uint16_t>(rval);
aFrame += static_cast<uint16_t>(rval);
continue;
} else if (rval < 0) {
ESP_ERROR_CHECK(TryRecoverUart());
ExitNow(error = OT_ERROR_FAILED);
}
SuccessOrExit(error = WaitForWritable());
}
exit:
return error;
}
otError UartSpinelInterface::WaitForFrame(uint64_t timeout_us)
{
otError error = OT_ERROR_NONE;
struct timeval timeout;
fd_set read_fds;
fd_set error_fds;
int rval;
FD_ZERO(&read_fds);
FD_ZERO(&error_fds);
FD_SET(m_uart_fd, &read_fds);
FD_SET(m_uart_fd, &error_fds);
timeout.tv_sec = static_cast<time_t>(timeout_us / US_PER_S);
timeout.tv_usec = static_cast<suseconds_t>(timeout_us % US_PER_S);
rval = select(m_uart_fd + 1, &read_fds, NULL, &error_fds, &timeout);
if (rval > 0) {
if (FD_ISSET(m_uart_fd, &read_fds)) {
TryReadAndDecode();
} else if (FD_ISSET(m_uart_fd, &error_fds)) {
ESP_ERROR_CHECK(TryRecoverUart());
ExitNow(error = OT_ERROR_FAILED);
}
} else if (rval == 0) {
ExitNow(error = OT_ERROR_RESPONSE_TIMEOUT);
} else {
ESP_ERROR_CHECK(TryRecoverUart());
ExitNow(error = OT_ERROR_FAILED);
}
exit:
return error;
}
void UartSpinelInterface::HandleHdlcFrame(void *context, otError error)
{
static_cast<UartSpinelInterface *>(context)->HandleHdlcFrame(error);
}
void UartSpinelInterface::HandleHdlcFrame(otError error)
{
if (error == OT_ERROR_NONE) {
ESP_LOGD(OT_PLAT_LOG_TAG, "received hdlc radio frame");
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();
}
}
esp_err_t UartSpinelInterface::InitUart(const esp_openthread_uart_config_t &radio_uart_config)
{
char uart_path[16];
m_uart_config = radio_uart_config;
ESP_RETURN_ON_ERROR(esp_openthread_uart_init_port(&radio_uart_config), OT_PLAT_LOG_TAG,
"esp_openthread_uart_init_port failed");
// We have a driver now installed so set up the read/write functions to use driver also.
uart_vfs_dev_port_set_tx_line_endings(m_uart_config.port, ESP_LINE_ENDINGS_LF);
uart_vfs_dev_port_set_rx_line_endings(m_uart_config.port, ESP_LINE_ENDINGS_LF);
snprintf(uart_path, sizeof(uart_path), "/dev/uart/%d", radio_uart_config.port);
m_uart_fd = open(uart_path, O_RDWR | O_NONBLOCK);
return m_uart_fd >= 0 ? ESP_OK : ESP_FAIL;
}
esp_err_t UartSpinelInterface::DeinitUart(void)
{
if (m_uart_fd != -1) {
close(m_uart_fd);
m_uart_fd = -1;
return uart_driver_delete(m_uart_config.port);
} else {
return ESP_ERR_INVALID_STATE;
}
}
esp_err_t UartSpinelInterface::TryRecoverUart(void)
{
ESP_RETURN_ON_ERROR(DeinitUart(), OT_PLAT_LOG_TAG, "DeInitUart failed");
ESP_RETURN_ON_ERROR(InitUart(m_uart_config), OT_PLAT_LOG_TAG, "InitUart failed");
return ESP_OK;
}
otError UartSpinelInterface::HardwareReset(void)
{
if (mRcpFailureHandler) {
TryRecoverUart();
mRcpFailureHandler();
}
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
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -29,7 +29,7 @@ using esp::radio_spinel::SpinelInterfaceAdapter;
using esp::radio_spinel::UartSpinelInterface;
using ot::Spinel::SpinelDriver;
static SpinelInterfaceAdapter<UartSpinelInterface> s_spinel_interface[ot::Spinel::kSpinelHeaderMaxNumIid];
static SpinelInterfaceAdapter<UartSpinelInterface> s_spinel_interface;
static RadioSpinel s_radio[ot::Spinel::kSpinelHeaderMaxNumIid];
static esp_radio_spinel_callbacks_t s_esp_radio_spinel_callbacks[ot::Spinel::kSpinelHeaderMaxNumIid];
static SpinelDriver s_spinel_driver[ot::Spinel::kSpinelHeaderMaxNumIid];
@@ -205,11 +205,11 @@ esp_err_t esp_radio_spinel_uart_interface_enable(const esp_radio_spinel_uart_con
esp_radio_spinel_uart_deinit_handler aUartDeinitHandler,
esp_radio_spinel_idx_t idx)
{
ESP_RETURN_ON_FALSE(aUartInitHandler != nullptr, ESP_FAIL, ESP_SPINEL_LOG_TAG, "UartInitHandler can not be set to NULL");
ESP_RETURN_ON_FALSE(aUartDeinitHandler != nullptr, ESP_FAIL, ESP_SPINEL_LOG_TAG, "UartDeinitHandler can not be set to NULL");
s_spinel_interface[idx].GetSpinelInterface().RegisterUartInitHandler(aUartInitHandler);
s_spinel_interface[idx].GetSpinelInterface().RegisterUartDeinitHandler(aUartDeinitHandler);
ESP_RETURN_ON_FALSE(s_spinel_interface[idx].GetSpinelInterface().Enable(*radio_uart_config) == OT_ERROR_NONE, ESP_FAIL, ESP_SPINEL_LOG_TAG, "Spinel UART interface failed to enable");
s_spinel_interface.GetSpinelInterface().RegisterUartInitHandler(aUartInitHandler);
s_spinel_interface.GetSpinelInterface().RegisterUartDeinitHandler(aUartDeinitHandler);
ESP_RETURN_ON_FALSE(radio_uart_config != nullptr, ESP_ERR_INVALID_ARG, ESP_SPINEL_LOG_TAG, "radio_uart_config can not be NULL");
ESP_RETURN_ON_ERROR(s_spinel_interface.GetSpinelInterface().Enable(*radio_uart_config), ESP_SPINEL_LOG_TAG,
"Spinel UART interface failed to enable");
ESP_LOGI(ESP_SPINEL_LOG_TAG, "Spinel UART interface has been successfully enabled");
return ESP_OK;
}
@@ -222,7 +222,7 @@ void esp_radio_spinel_init(esp_radio_spinel_idx_t idx)
// Multipan is not currently supported
iidList[0] = 0;
s_spinel_driver[idx].SetCoprocessorResetFailureCallback(radio_spinel_coprocessor_reset_failure_callback, instance);
s_spinel_driver[idx].Init(s_spinel_interface[idx].GetSpinelInterface(), true, iidList, ot::Spinel::kSpinelHeaderMaxNumIid);
s_spinel_driver[idx].Init(s_spinel_interface.GetSpinelInterface(), true, iidList, ot::Spinel::kSpinelHeaderMaxNumIid);
s_radio[idx].SetCompatibilityErrorCallback(radio_spinel_compatibility_error_callback, instance);
s_radio[idx].Init(/*skip_rcp_compatibility_check=*/false, /*reset_radio=*/true, &s_spinel_driver[idx], s_radio_caps, false);
s_radio[idx].SetVendorRestorePropertiesCallback(esp_radio_spinel_restore_vendor_properities, instance);
@@ -324,7 +324,7 @@ esp_err_t esp_radio_spinel_set_pan_coord(bool enable, esp_radio_spinel_idx_t idx
void esp_radio_spinel_radio_update(esp_radio_spinel_mainloop_context_t *mainloop_context, esp_radio_spinel_idx_t idx)
{
s_spinel_interface[idx].GetSpinelInterface().UpdateFdSet(static_cast<void *>(mainloop_context));
s_spinel_interface.GetSpinelInterface().UpdateFdSet(static_cast<void *>(mainloop_context));
}
void esp_radio_spinel_radio_process(esp_radio_spinel_mainloop_context_t *mainloop_context, esp_radio_spinel_idx_t idx)
@@ -354,7 +354,7 @@ esp_err_t esp_radio_spinel_get_tx_power(int8_t *power, esp_radio_spinel_idx_t id
void esp_radio_spinel_register_rcp_failure_handler(esp_radio_spinel_rcp_failure_handler handler, esp_radio_spinel_idx_t idx)
{
s_spinel_interface[idx].GetSpinelInterface().RegisterRcpFailureHandler(handler);
s_spinel_interface.GetSpinelInterface().RegisterRcpFailureHandler(handler);
}
esp_err_t esp_radio_spinel_rcp_deinit(esp_radio_spinel_idx_t idx)
@@ -363,7 +363,8 @@ esp_err_t esp_radio_spinel_rcp_deinit(esp_radio_spinel_idx_t idx)
ESP_RETURN_ON_FALSE(s_radio[idx].Sleep() == OT_ERROR_NONE, ESP_ERR_INVALID_STATE, ESP_SPINEL_LOG_TAG, "Radio fails to sleep");
ESP_RETURN_ON_FALSE(s_radio[idx].Disable() == OT_ERROR_NONE, ESP_ERR_INVALID_STATE, ESP_SPINEL_LOG_TAG, "Fail to disable radio");
}
ESP_RETURN_ON_FALSE(s_spinel_interface[idx].GetSpinelInterface().Disable() == OT_ERROR_NONE, ESP_ERR_INVALID_STATE, ESP_SPINEL_LOG_TAG, "Fail to deinitialize UART");
ESP_RETURN_ON_ERROR(s_spinel_interface.GetSpinelInterface().Disable(), ESP_SPINEL_LOG_TAG,
"Fail to deinitialize UART");
return ESP_OK;
}
@@ -1,32 +1,65 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_radio_spinel_uart_interface.hpp"
#include <errno.h>
#include <fcntl.h>
#include <sys/unistd.h>
#include "esp_check.h"
#include "esp_openthread_common_macro.h"
#include "openthread/platform/time.h"
#include "hdlc.hpp"
#include "common/code_utils.hpp"
#include "esp_vfs_dev.h"
#include "driver/uart.h"
#include "driver/uart_vfs.h"
#if CONFIG_OPENTHREAD_RADIO_SPINEL_UART
#include "esp_openthread_types.h"
#endif
namespace esp {
namespace radio_spinel {
static esp_err_t RadioSpinelUartInitPort(const esp_radio_spinel_uart_config_t *config)
{
char uart_path[16];
snprintf(uart_path, sizeof(uart_path), "/dev/uart/%d", config->port);
bool is_uart_registered = (access(uart_path, F_OK) == 0);
if (!is_uart_registered) {
// Register UART VFS devices before opening /dev/uart/x if not already present.
uart_vfs_dev_register();
}
ESP_RETURN_ON_ERROR(uart_param_config(config->port, &config->uart_config), ESP_SPINEL_LOG_TAG,
"uart_param_config failed");
ESP_RETURN_ON_ERROR(
uart_set_pin(config->port, config->tx_pin, config->rx_pin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE),
ESP_SPINEL_LOG_TAG, "uart_set_pin failed");
ESP_RETURN_ON_ERROR(uart_driver_install(config->port, CONFIG_OPENTHREAD_SPINEL_UART_DRIVER_BUFFER_SIZE, 0, 0, NULL, 0),
ESP_SPINEL_LOG_TAG, "uart_driver_install failed");
uart_vfs_dev_use_driver(config->port);
return ESP_OK;
}
UartSpinelInterface::UartSpinelInterface(void)
: m_receiver_frame_callback(nullptr)
, m_receiver_frame_context(nullptr)
, m_receive_frame_buffer(nullptr)
, m_uart_rx_buffer(nullptr)
, m_uart_fd(-1)
, mRcpFailureHandler(nullptr)
, mUartInitHandler(nullptr)
, mUartDeinitHandler(nullptr)
{
}
UartSpinelInterface::~UartSpinelInterface(void)
{
// Ensure UART resources are released even if caller forgets Disable().
Disable();
Deinit();
}
@@ -52,6 +85,11 @@ void UartSpinelInterface::Deinit(void)
esp_err_t UartSpinelInterface::Enable(const esp_radio_spinel_uart_config_t &radio_uart_config)
{
esp_err_t error = ESP_OK;
if (m_uart_fd != -1 || m_uart_rx_buffer != NULL) {
return ESP_ERR_INVALID_STATE;
}
m_uart_rx_buffer = static_cast<uint8_t *>(heap_caps_malloc(kMaxFrameSize, MALLOC_CAP_8BIT));
if (m_uart_rx_buffer == NULL) {
return ESP_ERR_NO_MEM;
@@ -60,10 +98,27 @@ esp_err_t UartSpinelInterface::Enable(const esp_radio_spinel_uart_config_t &radi
error = InitUart(radio_uart_config);
if (error == ESP_OK) {
ESP_LOGI(ESP_SPINEL_LOG_TAG, "spinel UART interface initialization completed");
} else {
heap_caps_free(m_uart_rx_buffer);
m_uart_rx_buffer = NULL;
}
return error;
}
#if CONFIG_OPENTHREAD_RADIO_SPINEL_UART
// NOTE: This overload bridges config types; keep field-wise copy and avoid storing references/pointers to the input to prevent potential lifetime-related memory risks.
esp_err_t UartSpinelInterface::Enable(const esp_openthread_uart_config_t &radio_uart_config)
{
esp_radio_spinel_uart_config_t spinel_uart_config = {
.port = radio_uart_config.port,
.uart_config = radio_uart_config.uart_config,
.rx_pin = radio_uart_config.rx_pin,
.tx_pin = radio_uart_config.tx_pin,
};
return Enable(spinel_uart_config);
}
#endif
esp_err_t UartSpinelInterface::Disable(void)
{
if (m_uart_rx_buffer) {
@@ -71,6 +126,10 @@ esp_err_t UartSpinelInterface::Disable(void)
}
m_uart_rx_buffer = NULL;
if (m_uart_fd == -1) {
return ESP_OK;
}
return DeinitUart();
}
@@ -108,8 +167,9 @@ int UartSpinelInterface::TryReadAndDecode(void)
{
uint8_t buffer[UART_HW_FIFO_LEN(m_uart_config.port)];
ssize_t rval;
do {
rval = read(m_uart_fd, buffer, sizeof(buffer));
rval = read(m_uart_fd, buffer, sizeof(buffer));
if (rval > 0) {
m_hdlc_decoder.Decode(buffer, static_cast<uint16_t>(rval));
}
@@ -252,8 +312,25 @@ esp_err_t UartSpinelInterface::InitUart(const esp_radio_spinel_uart_config_t &ra
m_uart_config = radio_uart_config;
return mUartInitHandler(&m_uart_config, &m_uart_fd);
} else {
ESP_LOGE(ESP_SPINEL_LOG_TAG, "None mUartInitHandler");
return ESP_FAIL;
char uart_path[16];
esp_err_t err = ESP_OK;
m_uart_config = radio_uart_config;
ESP_RETURN_ON_ERROR(RadioSpinelUartInitPort(&radio_uart_config), ESP_SPINEL_LOG_TAG,
"RadioSpinelUartInitPort failed");
// We have a driver now installed so set up the read/write functions to use driver also.
uart_vfs_dev_port_set_tx_line_endings(m_uart_config.port, ESP_LINE_ENDINGS_LF);
uart_vfs_dev_port_set_rx_line_endings(m_uart_config.port, ESP_LINE_ENDINGS_LF);
snprintf(uart_path, sizeof(uart_path), "/dev/uart/%d", radio_uart_config.port);
m_uart_fd = open(uart_path, O_RDWR | O_NONBLOCK);
if (m_uart_fd < 0) {
err = uart_driver_delete(m_uart_config.port);
ESP_RETURN_ON_ERROR(err, ESP_SPINEL_LOG_TAG, "uart_driver_delete failed after open");
return ESP_FAIL;
}
return ESP_OK;
}
}
@@ -262,8 +339,13 @@ esp_err_t UartSpinelInterface::DeinitUart(void)
if (mUartDeinitHandler) {
return mUartDeinitHandler(&m_uart_config, &m_uart_fd);
} else {
ESP_LOGE(ESP_SPINEL_LOG_TAG, "None mUartDeinitHandler");
return ESP_FAIL;
if (m_uart_fd != -1) {
close(m_uart_fd);
m_uart_fd = -1;
return uart_driver_delete(m_uart_config.port);
} else {
return ESP_ERR_INVALID_STATE;
}
}
}
+10 -3
View File
@@ -7,6 +7,7 @@
set(src_dirs
"src"
"src/port"
"src/spinel"
"openthread/examples/platforms/utils"
"openthread/src/core/api"
"openthread/src/core/common"
@@ -48,16 +49,22 @@ if(CONFIG_OPENTHREAD_RADIO_NATIVE)
list(APPEND exclude_srcs
"src/port/esp_openthread_radio_spinel.cpp"
"src/port/esp_spi_spinel_interface.cpp"
"src/port/esp_uart_spinel_interface.cpp")
"src/spinel/esp_radio_spinel.cpp"
"src/spinel/esp_radio_spinel_uart_interface.cpp")
elseif(CONFIG_OPENTHREAD_RADIO_SPINEL_UART OR CONFIG_OPENTHREAD_RADIO_SPINEL_SPI)
list(APPEND exclude_srcs
"src/port/esp_openthread_radio.c"
"src/port/esp_openthread_sleep.c")
"src/port/esp_openthread_sleep.c"
"src/spinel/esp_radio_spinel.cpp")
if(CONFIG_OPENTHREAD_RADIO_SPINEL_SPI)
list(APPEND exclude_srcs "src/spinel/esp_radio_spinel_uart_interface.cpp")
endif()
elseif(CONFIG_OPENTHREAD_RADIO_154_NONE)
list(APPEND exclude_srcs
"src/port/esp_openthread_radio_spinel.cpp"
"src/port/esp_spi_spinel_interface.cpp"
"src/port/esp_uart_spinel_interface.cpp"
"src/spinel/esp_radio_spinel.cpp"
"src/spinel/esp_radio_spinel_uart_interface.cpp"
"src/port/esp_openthread_radio.c"
"src/port/esp_openthread_sleep.c")
endif()