Merge branch 'feat/openthread_generic_if' into 'master'

feat(openthread) Add generic interface to OpenThread

See merge request espressif/esp-idf!48773
This commit is contained in:
Shu Chen
2026-06-25 11:23:20 +00:00
16 changed files with 818 additions and 23 deletions
@@ -11,6 +11,9 @@
#include "esp_log.h"
#include "esp_openthread_alarm.h"
#include "esp_openthread_common_macro.h"
#if CONFIG_OPENTHREAD_RCP_CUSTOM
#include "esp_openthread_transport_priv.h"
#endif
#include "esp_openthread_lock.h"
#include "esp_openthread_radio.h"
#include "esp_openthread_spi_slave.h"
@@ -112,6 +115,12 @@ static esp_err_t esp_openthread_host_interface_init(const esp_openthread_platfor
"esp_openthread_host_rcp_usb_init failed");
break;
#endif
#if CONFIG_OPENTHREAD_RCP_CUSTOM
case HOST_CONNECTION_MODE_RCP_TRANSPORT:
ESP_RETURN_ON_ERROR(esp_openthread_host_rcp_transport_init(config), OT_PLAT_LOG_TAG,
"esp_openthread_host_rcp_transport_init failed");
break;
#endif
#if CONFIG_OPENTHREAD_CONSOLE_TYPE_UART
case HOST_CONNECTION_MODE_CLI_UART:
ESP_RETURN_ON_ERROR(esp_openthread_host_cli_uart_init(config), OT_PLAT_LOG_TAG,
@@ -191,6 +200,11 @@ esp_err_t esp_openthread_platform_deinit(void)
case HOST_CONNECTION_MODE_CLI_UART:
esp_openthread_uart_deinit();
break;
#endif
#if CONFIG_OPENTHREAD_RCP_CUSTOM
case HOST_CONNECTION_MODE_RCP_TRANSPORT:
esp_openthread_host_rcp_transport_deinit();
break;
#endif
default:
break;
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -14,7 +14,7 @@
#include "esp_coex_i154.h"
#endif
#if (CONFIG_OPENTHREAD_RCP_UART || CONFIG_OPENTHREAD_RCP_USB_SERIAL_JTAG)
#if (CONFIG_OPENTHREAD_RCP_UART || CONFIG_OPENTHREAD_RCP_USB_SERIAL_JTAG || CONFIG_OPENTHREAD_RCP_CUSTOM)
#include "utils/uart.h"
#endif
@@ -36,7 +36,7 @@ struct ConsoleCmdMsg
};
#endif // CONFIG_OPENTHREAD_RCP_SPINEL_CONSOLE
#if (CONFIG_OPENTHREAD_RCP_UART || CONFIG_OPENTHREAD_RCP_USB_SERIAL_JTAG)
#if (CONFIG_OPENTHREAD_RCP_UART || CONFIG_OPENTHREAD_RCP_USB_SERIAL_JTAG || CONFIG_OPENTHREAD_RCP_CUSTOM)
extern "C" {
static int NcpSend(const uint8_t *aBuf, uint16_t aBufLength)
{
@@ -94,7 +94,7 @@ static esp_err_t init_console_command_worker()
extern "C" void otAppNcpInit(otInstance *aInstance)
{
#if (CONFIG_OPENTHREAD_RCP_UART || CONFIG_OPENTHREAD_RCP_USB_SERIAL_JTAG)
#if (CONFIG_OPENTHREAD_RCP_UART || CONFIG_OPENTHREAD_RCP_USB_SERIAL_JTAG || CONFIG_OPENTHREAD_RCP_CUSTOM)
IgnoreError(otPlatUartEnable());
otNcpHdlcInit(aInstance, NcpSend);
#else
@@ -17,8 +17,13 @@
#include "esp_system.h"
#include "esp_spinel_interface.hpp"
#include "esp_spinel_ncp_vendor_macro.h"
#include "esp_spi_spinel_interface.hpp"
#if CONFIG_OPENTHREAD_RADIO_SPINEL_UART
#include "esp_radio_spinel_uart_interface.hpp"
#elif CONFIG_OPENTHREAD_RADIO_SPINEL_SPI
#include "esp_spi_spinel_interface.hpp"
#elif CONFIG_OPENTHREAD_RADIO_SPINEL_CUSTOM
#include "esp_radio_spinel_custom.hpp"
#endif
#include "openthread-core-config.h"
#include "lib/spinel/radio_spinel.hpp"
#include "lib/spinel/spinel.h"
@@ -38,9 +43,12 @@ using ot::Spinel::SpinelDriver;
#if CONFIG_OPENTHREAD_RADIO_SPINEL_UART // CONFIG_OPENTHREAD_RADIO_SPINEL_UART
using esp::radio_spinel::UartSpinelInterface;
static SpinelInterfaceAdapter<UartSpinelInterface> s_spinel_interface;
#else // CONFIG_OPENTHREAD_RADIO_SPINEL_SPI
#elif CONFIG_OPENTHREAD_RADIO_SPINEL_SPI // CONFIG_OPENTHREAD_RADIO_SPINEL_SPI
using esp::openthread::SpiSpinelInterface;
static SpinelInterfaceAdapter<SpiSpinelInterface> s_spinel_interface;
#elif CONFIG_OPENTHREAD_RADIO_SPINEL_CUSTOM // CONFIG_OPENTHREAD_RADIO_SPINEL_CUSTOM
using esp::radio_spinel::CustomSpinelInterface;
static SpinelInterfaceAdapter<CustomSpinelInterface> s_spinel_interface;
#endif
static SpinelDriver s_spinel_driver;
@@ -99,6 +107,8 @@ void esp_openthread_set_coprocessor_reset_failure_callback(esp_openthread_coproc
esp_err_t esp_openthread_radio_init(const esp_openthread_platform_config_t *config)
{
ESP_RETURN_ON_FALSE(config != NULL, ESP_ERR_INVALID_ARG, OT_PLAT_LOG_TAG, "incoming config is NULL");
spinel_iid_t iidList[ot::Spinel::kSpinelHeaderMaxNumIid];
iidList[0] = 0;
@@ -111,12 +121,29 @@ esp_err_t esp_openthread_radio_init(const esp_openthread_platform_config_t *conf
s_radio.SetCallbacks(callbacks);
esp_openthread_radio_config_set(&config->radio_config);
// validate incoming RCP Radio Mode
#if CONFIG_OPENTHREAD_RADIO_SPINEL_UART
ESP_RETURN_ON_FALSE(config->radio_config.radio_mode == RADIO_MODE_UART_RCP, ESP_ERR_INVALID_ARG, OT_PLAT_LOG_TAG,
"radio_mode must be RADIO_MODE_UART_RCP");
#elif CONFIG_OPENTHREAD_RADIO_SPINEL_SPI
ESP_RETURN_ON_FALSE(config->radio_config.radio_mode == RADIO_MODE_SPI_RCP, ESP_ERR_INVALID_ARG, OT_PLAT_LOG_TAG,
"radio_mode must be RADIO_MODE_SPI_RCP");
#elif CONFIG_OPENTHREAD_RADIO_SPINEL_CUSTOM
ESP_RETURN_ON_FALSE(config->radio_config.radio_mode == RADIO_MODE_TRANSPORT, ESP_ERR_INVALID_ARG, OT_PLAT_LOG_TAG,
"radio_mode must be RADIO_MODE_TRANSPORT");
ESP_RETURN_ON_FALSE(config->radio_config.radio_transport_config.transport_tx != NULL &&
config->radio_config.radio_transport_config.transport_rx != NULL, ESP_ERR_INVALID_ARG, OT_PLAT_LOG_TAG,
"transport callbacks must be non-NULL");
#endif
#if CONFIG_OPENTHREAD_RADIO_SPINEL_UART // CONFIG_OPENTHREAD_RADIO_SPINEL_UART
ESP_RETURN_ON_ERROR(s_spinel_interface.GetSpinelInterface().Enable(config->radio_config.radio_uart_config), OT_PLAT_LOG_TAG,
"Spinel interface init failed");
#else // CONFIG_OPENTHREAD_RADIO_SPINEL_SPI
#elif CONFIG_OPENTHREAD_RADIO_SPINEL_SPI // CONFIG_OPENTHREAD_RADIO_SPINEL_SPI
ESP_RETURN_ON_ERROR(s_spinel_interface.GetSpinelInterface().Enable(config->radio_config.radio_spi_config), OT_PLAT_LOG_TAG,
"Spinel interface init failed");
#elif CONFIG_OPENTHREAD_RADIO_SPINEL_CUSTOM // CONFIG_OPENTHREAD_RADIO_SPINEL_CUSTOM
ESP_RETURN_ON_ERROR(s_spinel_interface.GetSpinelInterface().Enable(config->radio_config.radio_transport_config), OT_PLAT_LOG_TAG,
"Spinel custom transport init failed");
#endif
s_spinel_driver.SetCoprocessorResetFailureCallback(ot_spinel_coprocessor_reset_failure_callback, esp_openthread_get_instance());
s_spinel_driver.Init(s_spinel_interface.GetSpinelInterface(), true, iidList, ot::Spinel::kSpinelHeaderMaxNumIid);
@@ -164,10 +191,13 @@ esp_err_t esp_openthread_rcp_init(void)
#if CONFIG_OPENTHREAD_RADIO_SPINEL_UART
ESP_RETURN_ON_ERROR(s_spinel_interface.GetSpinelInterface().Enable(radio_config->radio_uart_config), OT_PLAT_LOG_TAG,
"Spinel interface init failed");
#else // CONFIG_OPENTHREAD_RADIO_SPINEL_SPI
#elif CONFIG_OPENTHREAD_RADIO_SPINEL_SPI
ESP_RETURN_ON_ERROR(s_spinel_interface.GetSpinelInterface().Enable(radio_config->radio_spi_config), OT_PLAT_LOG_TAG,
"Spinel interface init failed");
#endif // CONFIG_OPENTHREAD_RADIO_SPINEL_UART
#elif CONFIG_OPENTHREAD_RADIO_SPINEL_CUSTOM
ESP_RETURN_ON_ERROR(s_spinel_interface.GetSpinelInterface().Enable(radio_config->radio_transport_config), OT_PLAT_LOG_TAG,
"Spinel interface init failed");
#endif
ESP_RETURN_ON_FALSE(s_radio.Enable(esp_openthread_get_instance()) == OT_ERROR_NONE, ESP_FAIL, OT_PLAT_LOG_TAG, "Fail to enable radio");
#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
@@ -547,6 +577,8 @@ uint32_t otPlatRadioGetBusSpeed(otInstance *aInstance)
return s_esp_openthread_radio_config->radio_uart_config.uart_config.baud_rate;
#elif CONFIG_OPENTHREAD_RADIO_SPINEL_SPI
return s_esp_openthread_radio_config->radio_spi_config.spi_device.clock_speed_hz;
#elif CONFIG_OPENTHREAD_RADIO_SPINEL_CUSTOM
return s_esp_openthread_radio_config->radio_transport_config.bus_speed;
#else
return 0;
#endif
@@ -556,7 +588,7 @@ uint32_t otPlatRadioGetBusLatency(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
#if CONFIG_OPENTHREAD_RADIO_SPINEL_UART || CONFIG_OPENTHREAD_RADIO_SPINEL_SPI
#if CONFIG_OPENTHREAD_RADIO_SPINEL_UART || CONFIG_OPENTHREAD_RADIO_SPINEL_SPI || CONFIG_OPENTHREAD_RADIO_SPINEL_CUSTOM
return CONFIG_OPENTHREAD_BUS_LATENCY;
#else
return 0;
@@ -50,7 +50,7 @@ static void handle_ot_netif_state_change(otInstance* instance)
}
}
#if (CONFIG_OPENTHREAD_RADIO_SPINEL_UART || CONFIG_OPENTHREAD_RADIO_SPINEL_SPI)
#if (CONFIG_OPENTHREAD_RADIO_SPINEL_UART || CONFIG_OPENTHREAD_RADIO_SPINEL_SPI || CONFIG_OPENTHREAD_RADIO_SPINEL_CUSTOM)
esp_openthread_handle_netif_state_change(otIp6IsEnabled(instance));
#endif
}
@@ -0,0 +1,143 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
#if CONFIG_OPENTHREAD_RCP_CUSTOM
#include "esp_openthread_transport_priv.h"
#include <errno.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include "esp_check.h"
#include "esp_openthread_common_macro.h"
#include "esp_openthread_platform.h"
#include "esp_openthread_types.h"
#include "esp_vfs_eventfd.h"
#include "common/code_utils.hpp"
#include "openthread/instance.h"
#include "utils/uart.h"
static esp_openthread_transport_config_t s_transport_config;
static int s_notify_eventfd = -1;
static const char s_transport_rcp_workflow[] = "transport_rcp";
static esp_err_t transport_rcp_notify_eventfd_init(void)
{
ESP_RETURN_ON_FALSE(s_notify_eventfd < 0, ESP_ERR_INVALID_STATE, OT_PLAT_LOG_TAG, "custom transport RCP eventfd already initialized");
s_notify_eventfd = eventfd(0, EFD_SUPPORT_ISR);
ESP_RETURN_ON_FALSE(s_notify_eventfd >= 0, ESP_FAIL, OT_PLAT_LOG_TAG, "custom transport RCP eventfd create failed");
return ESP_OK;
}
static void transport_rcp_notify_eventfd_deinit(void)
{
if (s_notify_eventfd >= 0) {
close(s_notify_eventfd);
s_notify_eventfd = -1;
}
}
void esp_openthread_transport_notify_rcp_rx(void)
{
if (s_notify_eventfd >= 0) {
uint64_t one = 1;
ssize_t w = write(s_notify_eventfd, &one, sizeof(one));
(void)w;
}
}
void esp_openthread_transport_rcp_update(esp_openthread_mainloop_context_t *mainloop)
{
if (s_notify_eventfd >= 0) {
FD_SET(s_notify_eventfd, &mainloop->read_fds);
if (s_notify_eventfd > mainloop->max_fd) {
mainloop->max_fd = s_notify_eventfd;
}
}
}
static uint8_t s_rx_chunk[ESP_OPENTHREAD_TRANSPORT_BUFFER_SIZE];
esp_err_t esp_openthread_transport_rcp_process(otInstance *instance, const esp_openthread_mainloop_context_t *mainloop)
{
OT_UNUSED_VARIABLE(instance);
if (s_notify_eventfd >= 0 && FD_ISSET(s_notify_eventfd, &mainloop->read_fds)) {
uint64_t v;
ssize_t r = read(s_notify_eventfd, &v, sizeof(v));
(void)r;
}
size_t n = 0;
esp_err_t err = s_transport_config.transport_rx(s_rx_chunk, sizeof(s_rx_chunk), &n);
if (err == ESP_OK) {
if (n <= sizeof(s_rx_chunk)) {
otPlatUartReceived(s_rx_chunk, (uint16_t)n);
} else {
ESP_LOGE(OT_PLAT_LOG_TAG, "invalid Rx data length");
}
}
return ESP_OK;
}
esp_err_t esp_openthread_host_rcp_transport_init(const esp_openthread_platform_config_t *config)
{
ESP_RETURN_ON_FALSE(config != NULL, ESP_ERR_INVALID_ARG, OT_PLAT_LOG_TAG, "config is NULL");
ESP_RETURN_ON_FALSE(config->host_config.host_connection_mode == HOST_CONNECTION_MODE_RCP_TRANSPORT, ESP_ERR_INVALID_ARG,
OT_PLAT_LOG_TAG, "host connection mode must be RCP Transport");
ESP_RETURN_ON_FALSE(config->host_config.host_transport_config.transport_tx != NULL, ESP_ERR_INVALID_ARG,
OT_PLAT_LOG_TAG, "transport_tx is NULL");
ESP_RETURN_ON_FALSE(config->host_config.host_transport_config.transport_rx != NULL, ESP_ERR_INVALID_ARG,
OT_PLAT_LOG_TAG, "transport_rx is NULL");
memcpy(&s_transport_config, &config->host_config.host_transport_config, sizeof(s_transport_config));
ESP_RETURN_ON_ERROR(transport_rcp_notify_eventfd_init(), OT_PLAT_LOG_TAG, "custom transport RCP notify init failed");
return esp_openthread_platform_workflow_register(&esp_openthread_transport_rcp_update, &esp_openthread_transport_rcp_process,
s_transport_rcp_workflow);
}
void esp_openthread_host_rcp_transport_deinit(void)
{
esp_openthread_platform_workflow_unregister(s_transport_rcp_workflow);
memset(&s_transport_config, 0, sizeof(s_transport_config));
transport_rcp_notify_eventfd_deinit();
}
otError otPlatUartEnable(void)
{
return OT_ERROR_NONE;
}
otError otPlatUartDisable(void)
{
return OT_ERROR_NONE;
}
otError otPlatUartFlush(void)
{
return OT_ERROR_NONE;
}
otError otPlatUartSend(const uint8_t *buf, uint16_t buf_length)
{
if (!s_transport_config.transport_tx) {
return OT_ERROR_FAILED;
}
esp_err_t err = s_transport_config.transport_tx(buf, buf_length);
otPlatUartSendDone();
if (err != ESP_OK) {
return OT_ERROR_FAILED;
}
return OT_ERROR_NONE;
}
#endif /* CONFIG_OPENTHREAD_RCP_CUSTOM */
@@ -0,0 +1,281 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_radio_spinel_custom.hpp"
#include <cstring>
#include <errno.h>
#include <unistd.h>
#include "esp_check.h"
#include "esp_openthread_common_macro.h"
#include "esp_radio_spinel.h"
#include "esp_vfs_eventfd.h"
#include "common/code_utils.hpp"
#include "hdlc.hpp"
#include "openthread/error.h"
#include "openthread/platform/time.h"
#include "platform/exit_code.h"
namespace esp {
namespace radio_spinel {
namespace {
static int s_transport_spinel_notify_fd = -1;
}
extern "C" void esp_openthread_transport_notify_host_rx(void)
{
if (s_transport_spinel_notify_fd >= 0) {
uint64_t one = 1;
ssize_t w = write(s_transport_spinel_notify_fd, &one, sizeof(one));
(void)w;
}
}
CustomSpinelInterface::CustomSpinelInterface(void)
: m_receiver_frame_callback(nullptr)
, m_receiver_frame_context(nullptr)
, m_receive_frame_buffer(nullptr)
, m_rx_buffer(nullptr)
, m_notify_fd(-1)
, mRcpFailureHandler(nullptr)
{
memset(&m_transport_cfg, 0, sizeof(m_transport_cfg));
memset(&mInterfaceMetrics, 0, sizeof(mInterfaceMetrics));
}
CustomSpinelInterface::~CustomSpinelInterface(void)
{
Disable();
Deinit();
}
otError CustomSpinelInterface::Init(ReceiveFrameCallback aCallback, void *aCallbackContext, RxFrameBuffer &aFrameBuffer)
{
m_receiver_frame_callback = aCallback;
m_receiver_frame_context = aCallbackContext;
m_receive_frame_buffer = &aFrameBuffer;
m_hdlc_decoder.Init(aFrameBuffer, HandleHdlcFrame, this);
return OT_ERROR_NONE;
}
void CustomSpinelInterface::Deinit(void)
{
m_receiver_frame_callback = nullptr;
m_receiver_frame_context = nullptr;
m_receive_frame_buffer = nullptr;
}
esp_err_t CustomSpinelInterface::Enable(const esp_openthread_transport_config_t &transport_config)
{
ESP_RETURN_ON_FALSE((m_notify_fd < 0) && (m_rx_buffer == NULL), ESP_ERR_INVALID_STATE, ESP_SPINEL_LOG_TAG, "custom transport spinel already enabled");
ESP_RETURN_ON_FALSE((transport_config.transport_tx != nullptr) && (transport_config.transport_rx != nullptr),
ESP_ERR_INVALID_ARG, ESP_SPINEL_LOG_TAG, "transport callbacks invalid");
m_rx_buffer = static_cast<uint8_t *>(heap_caps_malloc(ESP_OPENTHREAD_TRANSPORT_BUFFER_SIZE, MALLOC_CAP_8BIT));
if (m_rx_buffer == NULL) {
return ESP_ERR_NO_MEM;
}
m_notify_fd = eventfd(0, EFD_SUPPORT_ISR);
if (m_notify_fd < 0) {
ESP_LOGE(ESP_SPINEL_LOG_TAG, "custom transport spinel eventfd failed");
heap_caps_free(m_rx_buffer);
m_rx_buffer = NULL;
return ESP_FAIL;
}
s_transport_spinel_notify_fd = m_notify_fd;
memcpy(&m_transport_cfg, &transport_config, sizeof(m_transport_cfg));
return ESP_OK;
}
esp_err_t CustomSpinelInterface::Disable(void)
{
if (m_rx_buffer) {
heap_caps_free(m_rx_buffer);
}
m_rx_buffer = NULL;
if (s_transport_spinel_notify_fd == m_notify_fd) {
s_transport_spinel_notify_fd = -1;
}
if (m_notify_fd >= 0) {
close(m_notify_fd);
m_notify_fd = -1;
}
memset(&m_transport_cfg, 0, sizeof(m_transport_cfg));
return ESP_OK;
}
otError CustomSpinelInterface::SendFrame(const uint8_t *frame, uint16_t length)
{
otError error = OT_ERROR_NONE;
encoder_buffer.Clear();
ot::Hdlc::Encoder hdlc_encoder(encoder_buffer);
SuccessOrExit(error = hdlc_encoder.BeginFrame());
SuccessOrExit(error = hdlc_encoder.Encode(frame, length));
SuccessOrExit(error = hdlc_encoder.EndFrame());
SuccessOrExit(error = Write(encoder_buffer.GetFrame(), encoder_buffer.GetLength()));
exit:
if (error != OT_ERROR_NONE) {
ESP_LOGE(ESP_SPINEL_LOG_TAG, "send radio frame failed");
} else {
ESP_LOGD(ESP_SPINEL_LOG_TAG, "sent radio frame");
}
return error;
}
void CustomSpinelInterface::Process(const void *aMainloopContext)
{
if (m_notify_fd < 0) {
return;
}
auto *ctx = (esp_radio_spinel_mainloop_context_t *)aMainloopContext;
if (m_notify_fd >= 0 && FD_ISSET(m_notify_fd, &ctx->read_fds)) {
uint64_t v;
ssize_t r = read(m_notify_fd, &v, sizeof(v));
(void)r;
}
TryReadAndDecode();
}
int CustomSpinelInterface::TryReadAndDecode(void)
{
if (!m_transport_cfg.transport_rx) {
return 0;
}
for (;;) {
size_t n = 0;
esp_err_t err = m_transport_cfg.transport_rx(m_rx_buffer, CONFIG_OPENTHREAD_TRANSPORT_BUFFER_SIZE, &n);
if ((err != ESP_OK) || (n == 0) || (n > CONFIG_OPENTHREAD_TRANSPORT_BUFFER_SIZE)) {
break;
}
m_hdlc_decoder.Decode(m_rx_buffer, static_cast<uint16_t>(n));
}
return 0;
}
otError CustomSpinelInterface::Write(const uint8_t *aFrame, uint16_t length)
{
if (!m_transport_cfg.transport_tx) {
return OT_ERROR_FAILED;
}
esp_err_t err = m_transport_cfg.transport_tx(aFrame, length);
return (err == ESP_OK) ? OT_ERROR_NONE : OT_ERROR_FAILED;
}
otError CustomSpinelInterface::WaitForFrame(uint64_t timeout_us)
{
if (m_notify_fd < 0) {
return OT_ERROR_FAILED;
}
otError error = OT_ERROR_NONE;
struct timeval timeout;
fd_set read_fds;
fd_set error_fds;
int rval;
uint64_t const deadline = otPlatTimeGet() + timeout_us;
TryReadAndDecode();
while (true) {
uint64_t now = otPlatTimeGet();
if (now >= deadline) {
ExitNow(error = OT_ERROR_RESPONSE_TIMEOUT);
}
uint64_t remain = deadline - now;
FD_ZERO(&read_fds);
FD_ZERO(&error_fds);
FD_SET(m_notify_fd, &read_fds);
FD_SET(m_notify_fd, &error_fds);
timeout.tv_sec = static_cast<time_t>(remain / US_PER_S);
timeout.tv_usec = static_cast<suseconds_t>(remain % US_PER_S);
rval = select(m_notify_fd + 1, &read_fds, NULL, &error_fds, &timeout);
if (rval > 0) {
if (FD_ISSET(m_notify_fd, &read_fds)) {
uint64_t v;
ssize_t r = read(m_notify_fd, &v, sizeof(v));
(void)r;
TryReadAndDecode();
ExitNow(error = OT_ERROR_NONE);
}
if (FD_ISSET(m_notify_fd, &error_fds)) {
ExitNow(error = OT_ERROR_FAILED);
}
} else if (rval == 0) {
ExitNow(error = OT_ERROR_RESPONSE_TIMEOUT);
} else if (errno != EINTR) {
ExitNow(error = OT_ERROR_FAILED);
}
}
exit:
return error;
}
void CustomSpinelInterface::HandleHdlcFrame(void *context, otError error)
{
static_cast<CustomSpinelInterface *>(context)->HandleHdlcFrame(error);
}
void CustomSpinelInterface::HandleHdlcFrame(otError error)
{
if (error == OT_ERROR_NONE) {
if (m_receiver_frame_callback != nullptr) {
m_receiver_frame_callback(m_receiver_frame_context);
}
} else {
ESP_LOGE(ESP_SPINEL_LOG_TAG, "dropping radio frame: %s", otThreadErrorToString(error));
if (m_receive_frame_buffer != nullptr) {
m_receive_frame_buffer->DiscardFrame();
}
}
}
void CustomSpinelInterface::UpdateFdSet(void *aMainloopContext)
{
if (m_notify_fd < 0) {
return;
}
auto *ctx = (esp_radio_spinel_mainloop_context_t *)aMainloopContext;
FD_SET(m_notify_fd, &ctx->read_fds);
if (m_notify_fd > ctx->max_fd) {
ctx->max_fd = m_notify_fd;
}
}
uint32_t CustomSpinelInterface::GetBusSpeed(void) const
{
return m_transport_cfg.bus_speed;
}
otError CustomSpinelInterface::HardwareReset(void)
{
if (mRcpFailureHandler) {
mRcpFailureHandler();
}
return OT_ERROR_NONE;
}
} // namespace radio_spinel
} // namespace esp