mirror of
https://github.com/espressif/esp-idf.git
synced 2026-08-18 06:35:35 +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:
@@ -201,6 +201,11 @@ menu "OpenThread"
|
||||
help
|
||||
Select this to connect to a Radio Co-Processor via SPI.
|
||||
|
||||
config OPENTHREAD_RADIO_SPINEL_CUSTOM
|
||||
bool "Connect via Custom Transport"
|
||||
help
|
||||
Select this to connect to a Radio Co-Processor via a provided Custom Transport.
|
||||
|
||||
config OPENTHREAD_RADIO_154_NONE
|
||||
bool "Disable the Thread radio based on 15.4 link"
|
||||
help
|
||||
@@ -232,6 +237,11 @@ menu "OpenThread"
|
||||
bool "USB RCP"
|
||||
help
|
||||
Select this to enable connection to host over USB JTAG serial.
|
||||
|
||||
config OPENTHREAD_RCP_CUSTOM
|
||||
bool "Custom Transport RCP"
|
||||
help
|
||||
Select this to enable a custom transport connection to host
|
||||
endchoice
|
||||
|
||||
config OPENTHREAD_NCP_VENDOR_HOOK
|
||||
@@ -500,6 +510,13 @@ menu "OpenThread"
|
||||
help
|
||||
Set the OpenThread UART buffer size.
|
||||
|
||||
config OPENTHREAD_TRANSPORT_BUFFER_SIZE
|
||||
depends on OPENTHREAD_RCP_CUSTOM || OPENTHREAD_RADIO_SPINEL_CUSTOM
|
||||
int "The custom transport received buffer size of openthread"
|
||||
default 2048
|
||||
help
|
||||
Set the OpenThread Custom transport buffer size.
|
||||
|
||||
config OPENTHREAD_MAC_MAX_CSMA_BACKOFFS_DIRECT
|
||||
int "Maximum backoffs times before declaring a channel access failure."
|
||||
default 4
|
||||
|
||||
33
components/openthread/include/esp_openthread_transport.h
Normal file
33
components/openthread/include/esp_openthread_transport.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Wakes the OpenThread main loop on RCP when custom transport RX data is available.
|
||||
*
|
||||
* Call this from another task or ISR (after RX data has been queued for transport_rx) when using
|
||||
* @ref esp_openthread_transport_config_t so select() returns without waiting for the full timeout.
|
||||
*
|
||||
*/
|
||||
void esp_openthread_transport_notify_rcp_rx(void);
|
||||
|
||||
/**
|
||||
* @brief Wakes the OpenThread main loop on Host when custom transport RX data is available.
|
||||
*
|
||||
* Call this from another task or ISR (after RX data has been queued for transport_rx) when using
|
||||
* @ref esp_openthread_transport_config_t so select() returns without waiting for the full timeout.
|
||||
*
|
||||
*/
|
||||
void esp_openthread_transport_notify_host_rx(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // end of extern "C"
|
||||
#endif
|
||||
@@ -1,11 +1,12 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <sys/select.h>
|
||||
@@ -129,6 +130,28 @@ typedef struct {
|
||||
gpio_num_t intr_pin; /*!< SPI interrupt pin */
|
||||
} esp_openthread_spi_slave_config_t;
|
||||
|
||||
/**
|
||||
* @brief The Transport config for OpenThread.
|
||||
*
|
||||
* @note An external transport implements the callback functions that OpenThread will call when it has data to send or receive. The data to be transported are HDLC frames, not raw Spinel Packets.
|
||||
*
|
||||
* - transport_tx: Send exactly @p len bytes from @p buf. Should complete synchronously before returning ESP_OK.
|
||||
*
|
||||
* - transport_rx: Non-blocking read of up to @p buf_size bytes into @p buf. On ESP_OK, set @p out_len to the number
|
||||
* of bytes written (1 .. buf_size). Return ESP_ERR_TIMEOUT or another non-OK code when no data is available now.
|
||||
*
|
||||
* When RX data becomes available from another context (ISR or task), the external transport calls
|
||||
* - `esp_openthread_transport_notify_rcp_rx()` (on RCP) or
|
||||
* - `esp_openthread_transport_notify_host_rx()` (on Host)
|
||||
* so the OpenThread task wakes promptly from select().
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
esp_err_t (*transport_tx)(const void *buf, size_t len); /*!< Transmit */
|
||||
esp_err_t (*transport_rx)(void *buf, size_t buf_size, size_t *out_len); /*!< Non-blocking receive */
|
||||
uint32_t bus_speed; /*!< Transport speed */
|
||||
} esp_openthread_transport_config_t;
|
||||
|
||||
/**
|
||||
* @brief The radio mode of OpenThread.
|
||||
*
|
||||
@@ -138,6 +161,7 @@ typedef enum {
|
||||
RADIO_MODE_UART_RCP, /*!< UART connection to a 15.4 capable radio co-processor (RCP) */
|
||||
RADIO_MODE_SPI_RCP, /*!< SPI connection to a 15.4 capable radio co-processor (RCP) */
|
||||
RADIO_MODE_TREL, /*!< Use the Thread Radio Encapsulation Link (TREL) */
|
||||
RADIO_MODE_TRANSPORT, /*!< Use the custom transport to connect to a 15.4 capable radio co-processor (RCP) */
|
||||
RADIO_MODE_MAX, /*!< Using for parameter check */
|
||||
} esp_openthread_radio_mode_t;
|
||||
|
||||
@@ -151,7 +175,8 @@ typedef enum {
|
||||
HOST_CONNECTION_MODE_CLI_USB, /*!< CLI USB connection to the host */
|
||||
HOST_CONNECTION_MODE_RCP_UART, /*!< RCP UART connection to the host */
|
||||
HOST_CONNECTION_MODE_RCP_SPI, /*!< RCP SPI connection to the host */
|
||||
HOST_CONNECTION_MODE_RCP_USB, /*!< RCP USB Serial JTAG connection to the host */
|
||||
HOST_CONNECTION_MODE_RCP_USB, /*!< RCP USB Serial JTAG connection to the host */
|
||||
HOST_CONNECTION_MODE_RCP_TRANSPORT, /*!< RCP Custom Transport to connect to the host */
|
||||
HOST_CONNECTION_MODE_MAX, /*!< Using for parameter check */
|
||||
} esp_openthread_host_connection_mode_t;
|
||||
|
||||
@@ -164,7 +189,8 @@ typedef struct {
|
||||
union {
|
||||
esp_openthread_uart_config_t radio_uart_config; /*!< The uart configuration to RCP */
|
||||
esp_openthread_spi_host_config_t radio_spi_config; /*!< The spi configuration to RCP */
|
||||
};
|
||||
esp_openthread_transport_config_t radio_transport_config; /*!< The custom transport configuration to RCP */
|
||||
};
|
||||
} esp_openthread_radio_config_t;
|
||||
|
||||
/**
|
||||
@@ -177,6 +203,7 @@ typedef struct {
|
||||
esp_openthread_uart_config_t host_uart_config; /*!< The uart configuration to host */
|
||||
usb_serial_jtag_driver_config_t host_usb_config; /*!< The usb configuration to host */
|
||||
esp_openthread_spi_slave_config_t spi_slave_config; /*!< The spi configuration to host */
|
||||
esp_openthread_transport_config_t host_transport_config; /*!< The custom transport configuration to the host */
|
||||
};
|
||||
} esp_openthread_host_connection_config_t;
|
||||
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
@@ -23,6 +23,7 @@
|
||||
#endif
|
||||
|
||||
#define ESP_OPENTHREAD_UART_BUFFER_SIZE CONFIG_OPENTHREAD_UART_BUFFER_SIZE
|
||||
#define ESP_OPENTHREAD_TRANSPORT_BUFFER_SIZE CONFIG_OPENTHREAD_TRANSPORT_BUFFER_SIZE
|
||||
|
||||
#if CONFIG_OPENTHREAD_DEBUG
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_openthread_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Initializes the custom transport for OpenThread host connection.
|
||||
*
|
||||
* @param[in] config The platform configuration.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_ARG if radio mode is invalid
|
||||
* - ESP_FAIL or other error codes on failure
|
||||
*
|
||||
*/
|
||||
esp_err_t esp_openthread_host_rcp_transport_init(const esp_openthread_platform_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Deinitializes the custom transport for OpenThread host connection.
|
||||
*
|
||||
*/
|
||||
void esp_openthread_host_rcp_transport_deinit(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_radio_spinel.h"
|
||||
#include "esp_openthread_types.h"
|
||||
#include "lib/hdlc/hdlc.hpp"
|
||||
#include "lib/spinel/spinel_interface.hpp"
|
||||
#include "openthread/error.h"
|
||||
|
||||
namespace esp {
|
||||
namespace radio_spinel {
|
||||
|
||||
/**
|
||||
* This class defines a custom Spinel transport using
|
||||
* esp_openthread_transport_config_t (HDLC-framed).
|
||||
*
|
||||
*/
|
||||
class CustomSpinelInterface : public ot::Spinel::SpinelInterface {
|
||||
public:
|
||||
/**
|
||||
* @brief This constructor of object.
|
||||
*/
|
||||
CustomSpinelInterface(void);
|
||||
|
||||
/**
|
||||
* @brief This destructor of the object.
|
||||
*
|
||||
*/
|
||||
~CustomSpinelInterface(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_radio_spinel_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_transport_config_t &transport_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,
|
||||
};
|
||||
|
||||
int TryReadAndDecode(void);
|
||||
|
||||
otError Write(const uint8_t *frame, uint16_t length);
|
||||
|
||||
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_rx_buffer;
|
||||
|
||||
esp_openthread_transport_config_t m_transport_cfg;
|
||||
|
||||
int m_notify_fd;
|
||||
|
||||
otRcpInterfaceMetrics mInterfaceMetrics;
|
||||
|
||||
CustomSpinelInterface(const CustomSpinelInterface &);
|
||||
CustomSpinelInterface &operator=(const CustomSpinelInterface &);
|
||||
|
||||
esp_radio_spinel_rcp_failure_handler mRcpFailureHandler;
|
||||
|
||||
ot::Spinel::FrameBuffer<kMaxFrameSize> encoder_buffer;
|
||||
};
|
||||
|
||||
} // namespace radio_spinel
|
||||
} // namespace esp
|
||||
@@ -602,7 +602,7 @@
|
||||
#define OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE 1
|
||||
#endif
|
||||
|
||||
#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)
|
||||
/**
|
||||
* @def OPENTHREAD_SPINEL_CONFIG_COMPATIBILITY_ERROR_CALLBACK_ENABLE
|
||||
*
|
||||
@@ -644,7 +644,7 @@
|
||||
#define OPENTHREAD_SPINEL_CONFIG_COPROCESSOR_RESET_FAILURE_CALLBACK_ENABLE 1
|
||||
#endif
|
||||
|
||||
#endif // CONFIG_OPENTHREAD_RADIO_SPINEL_UART || CONFIG_OPENTHREAD_RADIO_SPINEL_SPI
|
||||
#endif // CONFIG_OPENTHREAD_RADIO_SPINEL_UART || CONFIG_OPENTHREAD_RADIO_SPINEL_SPI || CONFIG_OPENTHREAD_RADIO_SPINEL_CUSTOM
|
||||
|
||||
#if CONFIG_OPENTHREAD_LINK_METRICS
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -91,7 +91,7 @@
|
||||
#ifdef OPENTHREAD_CONFIG_NCP_HDLC_ENABLE
|
||||
#error `OPENTHREAD_CONFIG_NCP_HDLC_ENABLE` is redefined.
|
||||
#endif
|
||||
#define OPENTHREAD_CONFIG_NCP_HDLC_ENABLE (CONFIG_OPENTHREAD_RCP_UART || CONFIG_OPENTHREAD_RCP_USB_SERIAL_JTAG)
|
||||
#define OPENTHREAD_CONFIG_NCP_HDLC_ENABLE (CONFIG_OPENTHREAD_RCP_UART || CONFIG_OPENTHREAD_RCP_USB_SERIAL_JTAG || CONFIG_OPENTHREAD_RCP_CUSTOM)
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_LIB_SPINEL_RX_FRAME_BUFFER_SIZE
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
143
components/openthread/src/port/esp_openthread_transport_rcp.c
Normal file
143
components/openthread/src/port/esp_openthread_transport_rcp.c
Normal file
@@ -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 */
|
||||
281
components/openthread/src/spinel/esp_radio_spinel_custom.cpp
Normal file
281
components/openthread/src/spinel/esp_radio_spinel_custom.cpp
Normal file
@@ -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
|
||||
@@ -50,21 +50,31 @@ if(CONFIG_OPENTHREAD_RADIO_NATIVE)
|
||||
"src/port/esp_openthread_radio_spinel.cpp"
|
||||
"src/port/esp_spi_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)
|
||||
"src/spinel/esp_radio_spinel_uart_interface.cpp"
|
||||
"src/spinel/esp_radio_spinel_custom.cpp")
|
||||
elseif(CONFIG_OPENTHREAD_RADIO_SPINEL_UART OR
|
||||
CONFIG_OPENTHREAD_RADIO_SPINEL_SPI OR
|
||||
CONFIG_OPENTHREAD_RADIO_SPINEL_CUSTOM)
|
||||
list(APPEND exclude_srcs
|
||||
"src/port/esp_openthread_radio.c"
|
||||
"src/port/esp_openthread_sleep.c"
|
||||
"src/spinel/esp_radio_spinel.cpp")
|
||||
if(CONFIG_OPENTHREAD_RADIO_SPINEL_SPI)
|
||||
if(CONFIG_OPENTHREAD_RADIO_SPINEL_SPI OR CONFIG_OPENTHREAD_RADIO_SPINEL_CUSTOM)
|
||||
list(APPEND exclude_srcs "src/spinel/esp_radio_spinel_uart_interface.cpp")
|
||||
endif()
|
||||
if(CONFIG_OPENTHREAD_RADIO_SPINEL_UART OR CONFIG_OPENTHREAD_RADIO_SPINEL_CUSTOM)
|
||||
list(APPEND exclude_srcs "src/port/esp_spi_spinel_interface.cpp")
|
||||
endif()
|
||||
if(CONFIG_OPENTHREAD_RADIO_SPINEL_UART OR CONFIG_OPENTHREAD_RADIO_SPINEL_SPI)
|
||||
list(APPEND exclude_srcs "src/spinel/esp_radio_spinel_custom.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/spinel/esp_radio_spinel.cpp"
|
||||
"src/spinel/esp_radio_spinel_uart_interface.cpp"
|
||||
"src/spinel/esp_radio_spinel_custom.cpp"
|
||||
"src/port/esp_openthread_radio.c"
|
||||
"src/port/esp_openthread_sleep.c")
|
||||
endif()
|
||||
@@ -92,7 +102,7 @@ endif()
|
||||
if(CONFIG_OPENTHREAD_NCP_VENDOR_HOOK)
|
||||
list(APPEND src_dirs
|
||||
"src/ncp")
|
||||
if(CONFIG_OPENTHREAD_RCP_UART OR CONFIG_OPENTHREAD_RCP_USB_SERIAL_JTAG)
|
||||
if(CONFIG_OPENTHREAD_RCP_UART OR CONFIG_OPENTHREAD_RCP_USB_SERIAL_JTAG OR CONFIG_OPENTHREAD_RCP_CUSTOM)
|
||||
list(APPEND exclude_srcs
|
||||
"src/ncp/esp_openthread_ncp_spi.cpp")
|
||||
elseif(CONFIG_OPENTHREAD_RCP_SPI)
|
||||
|
||||
@@ -90,6 +90,13 @@ elseif(CONFIG_OPENTHREAD_RCP_SPI)
|
||||
if(CONFIG_OPENTHREAD_NCP_VENDOR_HOOK)
|
||||
list(APPEND rcp_srcs src/ncp/esp_openthread_ncp_spi.cpp)
|
||||
endif()
|
||||
elseif(CONFIG_OPENTHREAD_RCP_CUSTOM)
|
||||
list(APPEND rcp_srcs
|
||||
openthread/src/ncp/ncp_hdlc.cpp
|
||||
src/port/esp_openthread_transport_rcp.c)
|
||||
if(CONFIG_OPENTHREAD_NCP_VENDOR_HOOK)
|
||||
list(APPEND rcp_srcs src/ncp/esp_openthread_ncp_hdlc.cpp)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CONFIG_OPENTHREAD_NCP_VENDOR_HOOK AND CONFIG_OPENTHREAD_RCP_SPINEL_CONSOLE)
|
||||
|
||||
Reference in New Issue
Block a user