mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-01 18:50:34 +03:00
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:
@@ -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
|
||||
Reference in New Issue
Block a user