From d339120bede6550e52d578977b443248aa3abd39 Mon Sep 17 00:00:00 2001 From: Xu Si Yu Date: Wed, 27 Dec 2023 15:12:25 +0800 Subject: [PATCH 1/7] feat(openthread): support esp openthread radio spinel --- components/openthread/CMakeLists.txt | 60 ++- components/openthread/Kconfig | 13 + .../openthread/include/esp_radio_spinel.h | 331 ++++++++++++++++ .../private_include/esp_openthread_ncp.h | 15 + .../esp_radio_spinel_adapter.hpp | 29 ++ .../esp_radio_spinel_uart_interface.hpp | 204 ++++++++++ .../openthread-core-esp32x-radio-config.h | 14 +- ...=> openthread-core-esp32x-spinel-config.h} | 11 +- .../openthread/src/ncp/esp_openthread_ncp.cpp | 111 ++++++ .../src/ncp/esp_openthread_ncp_hdlc.cpp | 33 ++ .../src/spinel/esp_radio_spinel.cpp | 352 ++++++++++++++++++ .../esp_radio_spinel_uart_interface.cpp | 300 +++++++++++++++ 12 files changed, 1460 insertions(+), 13 deletions(-) create mode 100644 components/openthread/include/esp_radio_spinel.h create mode 100644 components/openthread/private_include/esp_openthread_ncp.h create mode 100644 components/openthread/private_include/esp_radio_spinel_adapter.hpp create mode 100644 components/openthread/private_include/esp_radio_spinel_uart_interface.hpp rename components/openthread/private_include/{openthread-spinel-config.h => openthread-core-esp32x-spinel-config.h} (82%) create mode 100644 components/openthread/src/ncp/esp_openthread_ncp.cpp create mode 100644 components/openthread/src/ncp/esp_openthread_ncp_hdlc.cpp create mode 100644 components/openthread/src/spinel/esp_radio_spinel.cpp create mode 100644 components/openthread/src/spinel/esp_radio_spinel_uart_interface.cpp diff --git a/components/openthread/CMakeLists.txt b/components/openthread/CMakeLists.txt index 64025c75db..72d3f9320b 100644 --- a/components/openthread/CMakeLists.txt +++ b/components/openthread/CMakeLists.txt @@ -1,5 +1,10 @@ -if(CONFIG_OPENTHREAD_ENABLED) - idf_build_get_property(idf_target IDF_TARGET) +idf_build_get_property(idf_target IDF_TARGET) + +if(${idf_target} STREQUAL "linux") + return() # This component is not supported by the POSIX/Linux simulator +endif() + +if(CONFIG_OPENTHREAD_ENABLED OR CONFIG_IDF_DOC_BUILD OR CONFIG_OPENTHREAD_SPINEL_ONLY) set(public_include_dirs "include" @@ -141,6 +146,11 @@ if(CONFIG_OPENTHREAD_ENABLED) -Wno-maybe-uninitialized) endif() + if(CONFIG_OPENTHREAD_NCP_VENDOR_HOOK) + list(APPEND src_dirs + "src/ncp") + endif() + if(NOT CONFIG_OPENTHREAD_DNS64_CLIENT) list(APPEND exclude_srcs "src/esp_openthread_dns64.c") @@ -163,6 +173,48 @@ if(CONFIG_OPENTHREAD_ENABLED) elseif(CONFIG_OPENTHREAD_RADIO) set(device_type "OPENTHREAD_RADIO=1") endif() +elseif(CONFIG_OPENTHREAD_SPINEL_ONLY) + + set(src_dirs + "src/spinel" + "src/port" + "openthread/src/lib/spinel" + "openthread/src/lib/hdlc" + "openthread/src/lib/platform" + "openthread/src/core/api" + "openthread/src/core/common" + "openthread/src/core/mac") + + set(private_include_dirs + "private_include" + "openthread/src" + "openthread/src/core" + "openthread/src/lib" + "openthread/src/lib/hdlc" + "openthread/src/lib/spinel") + + file(GLOB_RECURSE exclude_srcs_list + "src/port/*" + "openthread/src/core/api/*.cpp" + "openthread/src/core/common/*" + "openthread/src/core/mac/*") + + list(REMOVE_ITEM exclude_srcs_list + "${CMAKE_CURRENT_SOURCE_DIR}/src/port/esp_openthread_alarm.c" + "${CMAKE_CURRENT_SOURCE_DIR}/src/port/esp_openthread_logging.c" + "${CMAKE_CURRENT_SOURCE_DIR}/openthread/src/core/api/error_api.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/openthread/src/core/api/logging_api.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/openthread/src/core/common/error.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/openthread/src/core/common/error.hpp" + "${CMAKE_CURRENT_SOURCE_DIR}/openthread/src/core/common/log.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/openthread/src/core/common/log.hpp" + "${CMAKE_CURRENT_SOURCE_DIR}/openthread/src/core/common/logging.hpp" + "${CMAKE_CURRENT_SOURCE_DIR}/openthread/src/core/common/string.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/openthread/src/core/common/string.hpp" + "${CMAKE_CURRENT_SOURCE_DIR}/openthread/src/core/mac/mac_frame.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/openthread/src/core/mac/mac_frame.hpp") + + list(APPEND exclude_srcs ${exclude_srcs_list}) endif() @@ -193,13 +245,15 @@ idf_component_register(SRC_DIRS "${src_dirs}" PRIV_REQUIRES console esp_event esp_partition esp_timer ieee802154 mbedtls nvs_flash) -if(CONFIG_OPENTHREAD_ENABLED) +if(CONFIG_OPENTHREAD_ENABLED OR CONFIG_OPENTHREAD_SPINEL_ONLY) if(CONFIG_OPENTHREAD_RADIO) set(CONFIG_FILE_TYPE "radio") elseif(CONFIG_OPENTHREAD_FTD) set(CONFIG_FILE_TYPE "ftd") elseif(CONFIG_OPENTHREAD_MTD) set(CONFIG_FILE_TYPE "mtd") + elseif(CONFIG_OPENTHREAD_SPINEL_ONLY) + set(CONFIG_FILE_TYPE "spinel") endif() target_compile_definitions( diff --git a/components/openthread/Kconfig b/components/openthread/Kconfig index c298ca40e5..fc2fa8115c 100644 --- a/components/openthread/Kconfig +++ b/components/openthread/Kconfig @@ -168,6 +168,13 @@ menu "OpenThread" Select this to enable SPI connection to host. endchoice + config OPENTHREAD_NCP_VENDOR_HOOK + bool "Enable vendor command for RCP" + depends on OPENTHREAD_RADIO + default n + help + Select this to enable OpenThread NCP vendor commands. + config OPENTHREAD_CLI bool "Enable Openthread Command-Line Interface" depends on OPENTHREAD_ENABLED @@ -362,4 +369,10 @@ menu "OpenThread" Select this option to enable the radio statistics feature, you can use radio command to print some radio Statistics informations. + config OPENTHREAD_SPINEL_ONLY + bool "Enable OpenThread External Radio Spinel feature" + default n + help + Select this option to enable the OpenThread Radio Spinel for external protocol stack, such as Zigbee. + endmenu diff --git a/components/openthread/include/esp_radio_spinel.h b/components/openthread/include/esp_radio_spinel.h new file mode 100644 index 0000000000..7a3fda8837 --- /dev/null +++ b/components/openthread/include/esp_radio_spinel.h @@ -0,0 +1,331 @@ +/* + * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "esp_ieee802154_types.h" +#include "driver/uart.h" +#include "soc/gpio_num.h" + +#define ESP_SPINEL_LOG_TAG "ESP_RADIO_SPINEL" + +#define SPINEL_PROP_VENDOR_ESP_SET_COORDINATOR (SPINEL_PROP_VENDOR_ESP__BEGIN + 1) /* Vendor command for coordinator.*/ + +#define SPINEL_PROP_VENDOR_ESP_SET_PENDINGMODE (SPINEL_PROP_VENDOR_ESP__BEGIN + 2) /* Vendor command for pending mode.*/ + +typedef enum { + ESP_RADIO_SPINEL_ZIGBEE = 0x0, /* The index of Zigbee.*/ + ESP_RADIO_SPINEL_OPENTHREAD = 0x1, /* The index of OpenThread.*/ + ESP_RADIO_SPINEL_MAX, +} esp_radio_spinel_idx_t; /* The index of 802.15.4 related protocol stack for ESP radio spinel.*/ + +typedef struct { + fd_set read_fds; /* The read file descriptors.*/ + fd_set write_fds; /* The write file descriptors.*/ + fd_set error_fds; /* The error file descriptors.*/ + int max_fd; /* The max file descriptor.*/ + struct timeval timeout; /* The timeout.*/ +} esp_radio_spinel_mainloop_context_t; + +typedef struct { + uart_port_t port; /*!< UART port number */ + uart_config_t uart_config; /*!< UART configuration, see uart_config_t docs */ + gpio_num_t rx_pin; /*!< UART RX pin */ + gpio_num_t tx_pin; /*!< UART TX pin */ +} esp_radio_spinel_uart_config_t; /*This structure represents a context for ESP radio spinel. */ + +typedef void (*esp_radio_spinel_rcp_failure_handler)(void); /* The handler for rcp failure.*/ +typedef esp_err_t (*esp_radio_spinel_uart_init_handler)(const esp_radio_spinel_uart_config_t *uart_config_t, int *uart_fd); /* The handler for UART initialization.*/ +typedef esp_err_t (*esp_radio_spinel_uart_deinit_handler)(const esp_radio_spinel_uart_config_t *uart_config_t, int *uart_fd); /* The handler for UART deinitialization.*/ + +typedef struct +{ + void (*receive_done)(const uint8_t *frame, esp_ieee802154_frame_info_t *frame_info); /* Callback for Receive Done.*/ + void (*transmit_done)(const uint8_t *frame, const uint8_t *ack, esp_ieee802154_frame_info_t *ack_frame_info); /* Callback for Transmit Done.*/ + void (*transmit_failed)(esp_ieee802154_tx_error_t error); /* Callback for Transmit Failed.*/ + void (*energy_scan_done)(int8_t max_rssi); /* Callback for Energy Scan Done.*/ + void (*transmit_started)(const uint8_t *frame); /* Callback for Transmit Started.*/ + void (*switchover_done)(bool success); /* Callback for Switchover Done.*/ + +#if OPENTHREAD_CONFIG_DIAG_ENABLE + void (*diag_receive_done)(const uint8_t *frame, esp_ieee802154_frame_info_t *frame_info); /* Callback for Receive Done (diag).*/ + void (*diag_transmit_done)(const uint8_t *frame, esp_ieee802154_frame_info_t *frame_info); /* Callback for Transmit Done (diag).*/ + void (*diag_transmit_failed)(esp_ieee802154_tx_error_t error); /* Callback for Transmit Failed (diag).*/ +#endif // OPENTHREAD_CONFIG_DIAG_ENABLE +} esp_radio_spinel_callbacks_t; /* ESP Radio Spinel Callbacks.*/ + +/** + * @brief Set callkbacks of ESP radio spinel. + * + * @note This function should be called before `esp_radio_spinel_init`. + * + * @param[in] aCallbacks The callbacks struct. + * @param[in] idx The index of 802.15.4 related protocol stack. + * + */ +void esp_radio_spinel_set_callbacks(const esp_radio_spinel_callbacks_t aCallbacks, esp_radio_spinel_idx_t idx); + +/** + * @brief Enable the UART interface for ESP radio spinel + * + * @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 + * @param[in] idx The index of 802.15.4 related protocol stack. + * + * @return + * - ESP_OK on success + * - ESP_FAIL on failures + * + */ +esp_err_t esp_radio_spinel_uart_interface_enable(const esp_radio_spinel_uart_config_t *radio_uart_config, + esp_radio_spinel_uart_init_handler aUartInitHandler, + esp_radio_spinel_uart_deinit_handler aUartDeinitHandler, + esp_radio_spinel_idx_t idx); + +/** + * @brief Initialize ESP radio spinel. + * + * @note This function should be called after `esp_radio_spinel_set_callbacks` and `esp_radio_spinel_uart_interface_enable`. + * + * @param[in] idx The index of 802.15.4 related protocol stack. + * + */ +void esp_radio_spinel_init(esp_radio_spinel_idx_t idx); + +/** + * @brief Enavle ESP radio spinel. + * + * @note This function should be called after `esp_radio_spinel_init`. + * + * @param[in] idx The index of 802.15.4 related protocol stack. + * + */ +void esp_radio_spinel_enable(esp_radio_spinel_idx_t idx); + +/** + * @brief Set the pending mode. + * + * @param[in] pending_mode The pending mode. + * @param[in] idx The index of 802.15.4 related protocol stack. + * + */ +void esp_radio_spinel_set_pending_mode(esp_ieee802154_pending_mode_t pending_mode, esp_radio_spinel_idx_t idx); + +/** + * @brief Get the EUI-64. + * + * @param[in] eui64 A pointer to the EUI-64. + * @param[in] idx The index of 802.15.4 related protocol stack. + * + */ +void esp_radio_spinel_get_eui64(uint8_t *eui64, esp_radio_spinel_idx_t idx); + +/** + * @brief Set the panid. + * + * @param[in] panid The panid. + * @param[in] idx The index of 802.15.4 related protocol stack. + * + */ +void esp_radio_spinel_set_panid(uint16_t panid, esp_radio_spinel_idx_t idx); + +/** + * @brief Set the short address. + * + * @param[in] short_address The short address. + * @param[in] idx The index of 802.15.4 related protocol stack. + * + */ +void esp_radio_spinel_set_short_address(uint16_t short_address, esp_radio_spinel_idx_t idx); + +/** + * @brief Set the extended address. + * + * @param[in] ext_address The extended address. + * @param[in] idx The index of 802.15.4 related protocol stack. + * + */ +void esp_radio_spinel_set_extended_address(uint8_t *ext_address, esp_radio_spinel_idx_t idx); + +/** + * @brief Set the coordinator mode. + * + * @param[in] enable Enable or disable the coordinator mode. + * @param[in] idx The index of 802.15.4 related protocol stack. + * + */ +void esp_radio_spinel_set_pan_coord(bool enable, esp_radio_spinel_idx_t idx); + +/** + * @brief Enable the RCP reception. + * + * @param[in] channel The channel of reception. + * @param[in] idx The index of 802.15.4 related protocol stack. + * + */ +void esp_radio_spinel_receive(uint8_t channel, esp_radio_spinel_idx_t idx); + +/** + * @brief Perform the energy scan. + * + * @param[in] scan_channel The channel of energy scan (usually the range is 11~26, scan all channels if it's set to 0). + * @param[in] scan_duration The duration for energy scan. + * @param[in] idx The index of 802.15.4 related protocol stack. + * + */ +void esp_radio_spinel_energy_scan(uint8_t scan_channel, uint16_t scan_duration, esp_radio_spinel_idx_t idx); + +/** + * @brief Perform the transmission. + * + * @param[in] frame A pointer to the frame. + * @param[in] channel The channel to use for transmitting. + * @param[in] cca Perform clear channel assessment(if it's true) or not(if it's false) + * @param[in] idx The index of 802.15.4 related protocol stack. + * + */ +void esp_radio_spinel_transmit(uint8_t *frame, uint8_t channel, bool cca, esp_radio_spinel_idx_t idx); + +/** + * @brief Clear all short addresses from the source address match table. + * + * @param[in] idx The index of 802.15.4 related protocol stack. + * + */ +void esp_radio_spinel_clear_short_entries(esp_radio_spinel_idx_t idx); + +/** + * @brief Add a short address to the source address match table. + * + * @param[in] short_address The short address to be added. + * @param[in] idx The index of 802.15.4 related protocol stack. + * + * @return + * - ESP_OK on success + * - ESP_FAIL on failures + * + */ +esp_err_t esp_radio_spinel_add_short_entry(uint16_t short_address, esp_radio_spinel_idx_t idx); + +/** + * @brief Clear the pending table, remove all extended/long addresses. + * + * @param[in] idx The index of 802.15.4 related protocol stack. + * + */ +void esp_radio_spinel_clear_extened_entries(esp_radio_spinel_idx_t idx); + +/** + * @brief Add an extended address to the source address match table. + * + * @param[in] ext_address The extended address to be added. + * @param[in] idx The index of 802.15.4 related protocol stack. + * + * @return + * - ESP_OK on success + * - ESP_FAIL on failures + * + */ +esp_err_t esp_radio_spinel_add_extened_entry(uint8_t *ext_address, esp_radio_spinel_idx_t idx); + +/** + * @brief Sets the status of promiscuous mode. + * + * @param[in] enable Whether to enable or disable promiscuous mode. + * @param[in] idx The index of 802.15.4 related protocol stack. + * + */ +void esp_radio_spinel_set_promiscuous_mode(bool enable, esp_radio_spinel_idx_t idx); + +/** + * @brief Update the ESP radio spinel. + * + * @param[in] mainloop_context The context for ESP radio spinel. + * @param[in] idx The index of 802.15.4 related protocol stack. + * + */ +void esp_radio_spinel_radio_update(esp_radio_spinel_mainloop_context_t *mainloop_context, esp_radio_spinel_idx_t idx); + +/** + * @brief Process the ESP radio spinel. + * + * @param[in] mainloop_context The context for ESP radio spinel. + * @param[in] idx The index of 802.15.4 related protocol stack. + * + */ +void esp_radio_spinel_radio_process(esp_radio_spinel_mainloop_context_t *mainloop_context, esp_radio_spinel_idx_t idx); + +/** + * @brief Switch the radio state to Sleep. + * + * @param[in] idx The index of 802.15.4 related protocol stack. + * + */ +void esp_radio_spinel_sleep(esp_radio_spinel_idx_t idx); + +/** + * @brief Set the radio's transmit power in dBm. + * + * @param[in] power The transmit power in dBm. + * @param[in] idx The index of 802.15.4 related protocol stack. + * + */ +void esp_radio_spinel_set_tx_power(int8_t power, esp_radio_spinel_idx_t idx); + +/** + * @brief Get the radio's transmit power in dBm. + * + * @param[in] power A pointer to the transmit power. + * @param[in] idx The index of 802.15.4 related protocol stack. + * + */ +void esp_radio_spinel_get_tx_power(int8_t *power, esp_radio_spinel_idx_t idx); + +/** + * @brief Register a handler to process the RCP failure. + * + * @param[in] handler The RCP failure handler. + * @param[in] idx The index of 802.15.4 related protocol stack. + * + */ +void esp_radio_spinel_register_rcp_failure_handler(esp_radio_spinel_rcp_failure_handler handler, esp_radio_spinel_idx_t idx); + +/** + * @brief Deinitialize the RCP. + * + * @param[in] idx The index of 802.15.4 related protocol stack. + * + * @return + * - ESP_OK on success + * - ESP_FAIL on failures + * + */ +esp_err_t esp_radio_spinel_rcp_deinit(esp_radio_spinel_idx_t idx); + +/** + * @brief Get the version of RCP. + * + * @param[in] running_rcp_version A pointer to the RCP version string. + * @param[in] idx The index of 802.15.4 related protocol stack. + * + * @return + * - ESP_OK on success + * - ESP_FAIL on failures + * + */ +esp_err_t esp_radio_spinel_rcp_version_get(char *running_rcp_version, esp_radio_spinel_idx_t idx); + +#ifdef __cplusplus +} +#endif diff --git a/components/openthread/private_include/esp_openthread_ncp.h b/components/openthread/private_include/esp_openthread_ncp.h new file mode 100644 index 0000000000..39f9e9532d --- /dev/null +++ b/components/openthread/private_include/esp_openthread_ncp.h @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#if CONFIG_OPENTHREAD_NCP_VENDOR_HOOK + +#define SPINEL_PROP_VENDOR_ESP_SET_COORDINATOR (SPINEL_PROP_VENDOR_ESP__BEGIN + 1) + +#define SPINEL_PROP_VENDOR_ESP_SET_PENDINGMODE (SPINEL_PROP_VENDOR_ESP__BEGIN + 2) + +#endif diff --git a/components/openthread/private_include/esp_radio_spinel_adapter.hpp b/components/openthread/private_include/esp_radio_spinel_adapter.hpp new file mode 100644 index 0000000000..50ba3c540f --- /dev/null +++ b/components/openthread/private_include/esp_radio_spinel_adapter.hpp @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +namespace esp { +namespace radio_spinel { + +/** + * This class defines an template to adapt both UartSpinelInterface and SpiSpinelInterface. + * + */ +template class SpinelInterfaceAdapter { +public: + SpinelInterfaceAdapter(void) {} + + ~SpinelInterfaceAdapter(void) {} + + InterfaceType &GetSpinelInterface(void) { return mSpinelInterface; } + +private: + InterfaceType mSpinelInterface; +}; + +} // namespace radio_spinel +} // namespace esp diff --git a/components/openthread/private_include/esp_radio_spinel_uart_interface.hpp b/components/openthread/private_include/esp_radio_spinel_uart_interface.hpp new file mode 100644 index 0000000000..b053d48519 --- /dev/null +++ b/components/openthread/private_include/esp_radio_spinel_uart_interface.hpp @@ -0,0 +1,204 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "esp_radio_spinel.h" +#include "lib/spinel/spinel_interface.hpp" +#include "lib/hdlc/hdlc.hpp" +#include "openthread/error.h" + +namespace esp { +namespace radio_spinel { + +/** + * 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_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_radio_spinel_uart_config_t &radio_uart_config); + + /** + * @brief This method disable the HDLC interface. + * + */ + esp_err_t Disable(void); + + void RegisterUartInitHandler(esp_radio_spinel_uart_init_handler handler) { mUartInitHandler = handler; } + + void RegisterUartDeinitHandler(esp_radio_spinel_uart_deinit_handler handler) { mUartDeinitHandler = handler; } + +private: + + enum { + /** + * Maximum wait time in Milliseconds for socket to become writable (see `SendFrame`). + * + */ + kMaxWaitTime = 2000, + }; + + esp_err_t InitUart(const esp_radio_spinel_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_radio_spinel_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_radio_spinel_rcp_failure_handler mRcpFailureHandler; + esp_radio_spinel_uart_init_handler mUartInitHandler; + esp_radio_spinel_uart_deinit_handler mUartDeinitHandler; +}; + +} // namespace radio_spinel +} // namespace esp diff --git a/components/openthread/private_include/openthread-core-esp32x-radio-config.h b/components/openthread/private_include/openthread-core-esp32x-radio-config.h index 6a292f2d6e..2f34b61ef1 100644 --- a/components/openthread/private_include/openthread-core-esp32x-radio-config.h +++ b/components/openthread/private_include/openthread-core-esp32x-radio-config.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -218,3 +218,15 @@ #define OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE 1 #endif #endif //CONFIG_OPENTHREAD_LINK_METRICS + +#if CONFIG_OPENTHREAD_NCP_VENDOR_HOOK +/** + * @def OPENTHREAD_ENABLE_NCP_VENDOR_HOOK + * + * Define as 1 to support ESP OpenThread NCP vendor commands + * + */ +#ifndef OPENTHREAD_ENABLE_NCP_VENDOR_HOOK +#define OPENTHREAD_ENABLE_NCP_VENDOR_HOOK 1 +#endif +#endif //CONFIG_OPENTHREAD_NCP_VENDOR_HOOK diff --git a/components/openthread/private_include/openthread-spinel-config.h b/components/openthread/private_include/openthread-core-esp32x-spinel-config.h similarity index 82% rename from components/openthread/private_include/openthread-spinel-config.h rename to components/openthread/private_include/openthread-core-esp32x-spinel-config.h index 2911ee3df6..65badda57e 100644 --- a/components/openthread/private_include/openthread-spinel-config.h +++ b/components/openthread/private_include/openthread-core-esp32x-spinel-config.h @@ -4,13 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -/** - * @file - * This file includes compile-time configuration constants for OpenThread. - */ - -#ifndef OPENTHREAD_SPINEL_CONFIG_H_ -#define OPENTHREAD_SPINEL_CONFIG_H_ +#pragma once /** * @def OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE @@ -30,6 +24,7 @@ * */ #ifndef OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT +// TZ-567: Set OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT to 3 after adding rcp failure notification mechanism #define OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT 0 #endif @@ -42,5 +37,3 @@ #ifndef OPENTHREAD_SPINEL_CONFIG_RCP_CUSTOM_RESTORATION #define OPENTHREAD_SPINEL_CONFIG_RCP_CUSTOM_RESTORATION 0 #endif - -#endif // OPENTHREAD_SPINEL_CONFIG_H_ diff --git a/components/openthread/src/ncp/esp_openthread_ncp.cpp b/components/openthread/src/ncp/esp_openthread_ncp.cpp new file mode 100644 index 0000000000..7e5127f869 --- /dev/null +++ b/components/openthread/src/ncp/esp_openthread_ncp.cpp @@ -0,0 +1,111 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_ieee802154.h" +#include "esp_openthread_ncp.h" +#include "ncp_base.hpp" + +#if OPENTHREAD_ENABLE_NCP_VENDOR_HOOK + +#if CONFIG_OPENTHREAD_RCP_UART +#include "utils/uart.h" +#endif + +#if CONFIG_OPENTHREAD_RCP_UART +extern "C" { + static int NcpSend(const uint8_t *aBuf, uint16_t aBufLength) + { + IgnoreError(otPlatUartSend(aBuf, aBufLength)); + return aBufLength; + } +} +#endif + +extern "C" void otAppNcpInit(otInstance *aInstance) +{ +#if CONFIG_OPENTHREAD_RCP_SPI + otNcpSpiInit(aInstance); +#else + IgnoreError(otPlatUartEnable()); + + otNcpHdlcInit(aInstance, NcpSend); +#endif +} + +namespace ot { +namespace Ncp { + +otError NcpBase::VendorCommandHandler(uint8_t aHeader, unsigned int aCommand) +{ + otError error = OT_ERROR_NONE; + + otPlatLog(OT_LOG_LEVEL_WARN, OT_LOG_REGION_NCP, "VendorCommandHandler Not Implemented"); + + switch (aCommand) + { + + default: + error = PrepareLastStatusResponse(aHeader, SPINEL_STATUS_INVALID_COMMAND); + break; + } + + return error; +} + +void NcpBase::VendorHandleFrameRemovedFromNcpBuffer(Spinel::Buffer::FrameTag aFrameTag) +{ + OT_UNUSED_VARIABLE(aFrameTag); +} + +otError NcpBase::VendorGetPropertyHandler(spinel_prop_key_t aPropKey) +{ + otError error = OT_ERROR_NONE; + + switch (aPropKey) + { + + default: + error = OT_ERROR_NOT_FOUND; + break; + } + + return error; +} + +otError NcpBase::VendorSetPropertyHandler(spinel_prop_key_t aPropKey) +{ + otError error = OT_ERROR_NONE; + + switch (aPropKey) + { + + // TZ-566: Add mechanism to allow users to register callback functions. + case SPINEL_PROP_VENDOR_ESP_SET_COORDINATOR: { + bool coordinator = false; + mDecoder.ReadBool(coordinator); + esp_ieee802154_set_coordinator(coordinator); + break; + } + case SPINEL_PROP_VENDOR_ESP_SET_PENDINGMODE: { + + int32_t pending_mode = 0; + mDecoder.ReadInt32(pending_mode); + esp_ieee802154_set_pending_mode(static_cast(pending_mode)); + break; + } + + default: + error = OT_ERROR_NOT_FOUND; + break; + } + + return error; +} + +} // namespace Ncp +} // namespace ot + +#endif // #if OPENTHREAD_ENABLE_NCP_VENDOR_HOOK diff --git a/components/openthread/src/ncp/esp_openthread_ncp_hdlc.cpp b/components/openthread/src/ncp/esp_openthread_ncp_hdlc.cpp new file mode 100644 index 0000000000..0e8a3dbb88 --- /dev/null +++ b/components/openthread/src/ncp/esp_openthread_ncp_hdlc.cpp @@ -0,0 +1,33 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "common/new.hpp" +#include "ncp_hdlc.hpp" + +#if OPENTHREAD_ENABLE_NCP_VENDOR_HOOK + +namespace ot { +namespace Ncp { + +static OT_DEFINE_ALIGNED_VAR(sNcpRaw, sizeof(NcpHdlc), uint64_t); + +extern "C" void otNcpHdlcInit(otInstance *aInstance, otNcpHdlcSendCallback aSendCallback) +{ + NcpHdlc *ncpHdlc = nullptr; + Instance *instance = static_cast(aInstance); + + ncpHdlc = new (&sNcpRaw) NcpHdlc(instance, aSendCallback); + + if (ncpHdlc == nullptr || ncpHdlc != NcpBase::GetNcpInstance()) + { + OT_ASSERT(false); + } +} + +} // namespace Ncp +} // namespace ot + +#endif // #if OPENTHREAD_ENABLE_NCP_VENDOR_HOOK diff --git a/components/openthread/src/spinel/esp_radio_spinel.cpp b/components/openthread/src/spinel/esp_radio_spinel.cpp new file mode 100644 index 0000000000..25374d862d --- /dev/null +++ b/components/openthread/src/spinel/esp_radio_spinel.cpp @@ -0,0 +1,352 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_check.h" +#include "esp_log.h" +#include "platform/exit_code.h" +#include "radio_spinel.hpp" +#include "esp_radio_spinel.h" +#include "esp_radio_spinel_adapter.hpp" +#include "esp_radio_spinel_uart_interface.hpp" + +using ot::Spinel::RadioSpinel; +using ot::Spinel::RadioSpinelCallbacks; +using esp::radio_spinel::SpinelInterfaceAdapter; +using esp::radio_spinel::UartSpinelInterface; + +static SpinelInterfaceAdapter s_spinel_interface[ot::Spinel::kSpinelHeaderMaxNumIid]; +static RadioSpinel s_radio[ot::Spinel::kSpinelHeaderMaxNumIid]; +static esp_radio_spinel_callbacks_t s_esp_radio_spinel_callbacks[ot::Spinel::kSpinelHeaderMaxNumIid]; +otRadioFrame s_transmit_frame; + +static esp_radio_spinel_idx_t get_index_from_instance(otInstance *instance) +{ + // TZ-563: Implement the function to get the esp radio spinel idx from otInstance for multipan rcp + return ESP_RADIO_SPINEL_ZIGBEE; +} + +static otInstance* get_instance_from_index(esp_radio_spinel_idx_t idx) +{ + // TZ-563: Implement the function to get otInstance pointer from esp radio spinel idx + return nullptr; +} + +void ReceiveDone(otInstance *aInstance, otRadioFrame *aFrame, otError aError) +{ + esp_radio_spinel_idx_t idx = get_index_from_instance(aInstance); + assert(s_esp_radio_spinel_callbacks[idx].receive_done); + uint8_t *frame = (uint8_t *)malloc(aFrame->mLength + 1); + esp_ieee802154_frame_info_t frame_info; + if (frame) { + frame[0] = aFrame->mLength; + memcpy((void *)(frame + 1), aFrame->mPsdu, frame[0]); + frame_info.rssi = aFrame->mInfo.mRxInfo.mRssi; + frame_info.timestamp = aFrame->mInfo.mRxInfo.mTimestamp; + frame_info.pending = aFrame->mInfo.mRxInfo.mAckedWithFramePending; + s_esp_radio_spinel_callbacks[idx].receive_done(frame, &frame_info); + free(frame); + } else { + ESP_LOGE(ESP_SPINEL_LOG_TAG, "Fail to alloc memory for frame"); + } +} + +void TransmitDone(otInstance *aInstance, otRadioFrame *aFrame, otRadioFrame *aAckFrame, otError aError) +{ + esp_radio_spinel_idx_t idx = get_index_from_instance(aInstance); + assert(s_esp_radio_spinel_callbacks[idx].transmit_done && s_esp_radio_spinel_callbacks[idx].transmit_failed); + if (aError == OT_ERROR_NONE) { + uint8_t *frame = (uint8_t *)malloc(aFrame->mLength + 1); + uint8_t *ack = nullptr; + if (frame) { + esp_ieee802154_frame_info_t ack_info; + frame[0] = aFrame->mLength; + memcpy((void *)(frame + 1), aFrame->mPsdu, frame[0]); + if (aAckFrame) { + ack = (uint8_t *)malloc(aAckFrame->mLength + 1); + if (ack) { + ack[0] = aAckFrame->mLength; + memcpy((void *)(ack + 1), aAckFrame->mPsdu, ack[0]); + } else { + ESP_LOGE(ESP_SPINEL_LOG_TAG, "Fail to alloc memory for ack"); + } + } + s_esp_radio_spinel_callbacks[idx].transmit_done(frame, ack, &ack_info); + free(frame); + free(ack); + } else { + ESP_LOGE(ESP_SPINEL_LOG_TAG, "Fail to alloc memory for frame"); + } + } else { + switch (aError) { + case OT_ERROR_CHANNEL_ACCESS_FAILURE: + s_esp_radio_spinel_callbacks[idx].transmit_failed(ESP_IEEE802154_TX_ERR_CCA_BUSY); + break; + case OT_ERROR_NO_ACK: + s_esp_radio_spinel_callbacks[idx].transmit_failed(ESP_IEEE802154_TX_ERR_NO_ACK); + break; + default: + s_esp_radio_spinel_callbacks[idx].transmit_failed(ESP_IEEE802154_TX_ERR_ABORT); + break; + } + } +} + +void EnergyScanDone(otInstance *aInstance, int8_t aMaxRssi) +{ + esp_radio_spinel_idx_t idx = get_index_from_instance(aInstance); + assert(s_esp_radio_spinel_callbacks[idx].energy_scan_done); + s_esp_radio_spinel_callbacks[idx].energy_scan_done(aMaxRssi); +} + +void TxStarted(otInstance *aInstance, otRadioFrame *aFrame) +{ + esp_radio_spinel_idx_t idx = get_index_from_instance(aInstance); + assert(s_esp_radio_spinel_callbacks[idx].transmit_started); + uint8_t *frame = (uint8_t *)malloc(aFrame->mLength + 1); + if (frame) { + frame[0] = aFrame->mLength; + memcpy((void *)(frame + 1), aFrame->mPsdu, frame[0]); + s_esp_radio_spinel_callbacks[idx].transmit_started(frame); + free(frame); + } else { + ESP_LOGE(ESP_SPINEL_LOG_TAG, "Fail to alloc memory for frame"); + } +} + +void SwitchoverDone(otInstance *aInstance, bool aSuccess) +{ + esp_radio_spinel_idx_t idx = get_index_from_instance(aInstance); + assert(s_esp_radio_spinel_callbacks[idx].switchover_done); + s_esp_radio_spinel_callbacks[idx].switchover_done(aSuccess); +} + +#if OPENTHREAD_CONFIG_DIAG_ENABLE +void DiagReceiveDone(otInstance *aInstance, otRadioFrame *aFrame, otError aError) +{ + esp_radio_spinel_idx_t idx = get_index_from_instance(aInstance); + assert(s_esp_radio_spinel_callbacks[idx].diag_receive_done); + uint8_t *frame = (uint8_t *)malloc(aFrame->mLength + 1); + esp_ieee802154_frame_info_t frame_info; + if (frame) { + frame[0] = aFrame->mLength; + memcpy((void *)(frame + 1), aFrame->mPsdu, frame[0]); + frame_info.rssi = aFrame->mInfo.mRxInfo.mRssi; + frame_info.timestamp = aFrame->mInfo.mRxInfo.mTimestamp; + frame_info.pending = aFrame->mInfo.mRxInfo.mAckedWithFramePending; + s_esp_radio_spinel_callbacks[idx].diag_receive_done(frame, &frame_info); + free(frame); + } else { + ESP_LOGE(ESP_SPINEL_LOG_TAG, "Fail to alloc memory for frame"); + } +} + +void DiagTransmitDone(otInstance *aInstance, otRadioFrame *aFrame, otError aError) +{ + esp_radio_spinel_idx_t idx = get_index_from_instance(aInstance); + assert(s_esp_radio_spinel_callbacks[idx].diag_transmit_done && s_esp_radio_spinel_callbacks[idx].diag_transmit_failed); + if (aError == OT_ERROR_NONE) { + uint8_t *frame = (uint8_t *)malloc(aFrame->mLength + 1); + if (frame) { + esp_ieee802154_frame_info_t ack_info; + frame[0] = aFrame->mLength; + memcpy((void *)(frame + 1), aFrame->mPsdu, frame[0]); + s_esp_radio_spinel_callbacks[idx].diag_transmit_done(frame, &ack_info); + free(frame); + } else { + ESP_LOGE(ESP_SPINEL_LOG_TAG, "Fail to alloc memory for frame"); + } + } else { + switch (aError) { + case OT_ERROR_CHANNEL_ACCESS_FAILURE: + s_esp_radio_spinel_callbacks[idx].diag_transmit_failed(ESP_IEEE802154_TX_ERR_CCA_BUSY); + break; + case OT_ERROR_NO_ACK: + s_esp_radio_spinel_callbacks[idx].diag_transmit_failed(ESP_IEEE802154_TX_ERR_NO_ACK); + break; + default: + s_esp_radio_spinel_callbacks[idx].diag_transmit_failed(ESP_IEEE802154_TX_ERR_CCA_BUSY); + break; + } + } +} +#endif // OPENTHREAD_CONFIG_DIAG_ENABLE + + +void esp_radio_spinel_set_callbacks(const esp_radio_spinel_callbacks_t aCallbacks, esp_radio_spinel_idx_t idx) +{ + s_esp_radio_spinel_callbacks[idx] = aCallbacks; + RadioSpinelCallbacks Callbacks; + Callbacks.mReceiveDone = ReceiveDone; + Callbacks.mTransmitDone = TransmitDone; + Callbacks.mEnergyScanDone = EnergyScanDone; + Callbacks.mTxStarted = TxStarted; + Callbacks.mSwitchoverDone = SwitchoverDone; +#if OPENTHREAD_CONFIG_DIAG_ENABLE + Callbacks.mDiagReceiveDone = DiagReceiveDone; + Callbacks.mDiagTransmitDone = DiagTransmitDone; +#endif // OPENTHREAD_CONFIG_DIAG_ENABLE + + s_radio[idx].SetCallbacks(Callbacks); +} + +esp_err_t esp_radio_spinel_uart_interface_enable(const esp_radio_spinel_uart_config_t *radio_uart_config, + esp_radio_spinel_uart_init_handler aUartInitHandler, + 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"); + ESP_LOGI(ESP_SPINEL_LOG_TAG, "Spinel UART interface has been successfully enabled"); + return ESP_OK; +} + +void esp_radio_spinel_init(esp_radio_spinel_idx_t idx) +{ + spinel_iid_t iidList[ot::Spinel::kSpinelHeaderMaxNumIid]; + + // Multipan is not currently supported + iidList[0] = 0; + s_radio[idx].Init(s_spinel_interface[idx].GetSpinelInterface(), /*reset_radio=*/true, /*skip_rcp_compatibility_check=*/false, iidList, ot::Spinel::kSpinelHeaderMaxNumIid); +} + +void esp_radio_spinel_enable(esp_radio_spinel_idx_t idx) +{ + otInstance *instance = get_instance_from_index(idx); + s_radio[idx].Enable(instance); +} + +void esp_radio_spinel_set_pending_mode(esp_ieee802154_pending_mode_t pending_mode, esp_radio_spinel_idx_t idx) +{ + s_radio[idx].Set(SPINEL_PROP_VENDOR_ESP_SET_PENDINGMODE, SPINEL_DATATYPE_INT32_S, static_cast(pending_mode)); +} + +void esp_radio_spinel_get_eui64(uint8_t *eui64, esp_radio_spinel_idx_t idx) +{ + SuccessOrDie(s_radio[idx].GetIeeeEui64(eui64)); +} + +void esp_radio_spinel_set_panid(uint16_t panid, esp_radio_spinel_idx_t idx) +{ + SuccessOrDie(s_radio[idx].SetPanId(panid)); +} + +void esp_radio_spinel_set_short_address(uint16_t short_address, esp_radio_spinel_idx_t idx) +{ + SuccessOrDie(s_radio[idx].SetShortAddress(short_address)); +} + +void esp_radio_spinel_set_extended_address(uint8_t *ext_address, esp_radio_spinel_idx_t idx) +{ + otExtAddress aExtAddress; + memcpy(aExtAddress.m8, (void *)ext_address, OT_EXT_ADDRESS_SIZE); + SuccessOrDie(s_radio[idx].SetExtendedAddress(aExtAddress)); +} + +void esp_radio_spinel_set_pan_coord(bool enable, esp_radio_spinel_idx_t idx) +{ + s_radio[idx].Set(SPINEL_PROP_VENDOR_ESP_SET_COORDINATOR, SPINEL_DATATYPE_BOOL_S, enable); +} + +void esp_radio_spinel_receive(uint8_t channel, esp_radio_spinel_idx_t idx) +{ + s_radio[idx].Receive(channel); +} + +void esp_radio_spinel_energy_scan(uint8_t scan_channel, uint16_t scan_duration, esp_radio_spinel_idx_t idx) +{ + s_radio[idx].EnergyScan(scan_channel, scan_duration); +} + +void esp_radio_spinel_transmit(uint8_t *frame, uint8_t channel, bool cca, esp_radio_spinel_idx_t idx) +{ + s_transmit_frame.mLength = frame[0]; + s_transmit_frame.mPsdu = frame + 1; + s_transmit_frame.mInfo.mTxInfo.mCsmaCaEnabled = cca; + s_transmit_frame.mChannel = channel; + s_transmit_frame.mInfo.mTxInfo.mRxChannelAfterTxDone = channel; + SuccessOrDie(s_radio[idx].Transmit(s_transmit_frame)); +} + +void esp_radio_spinel_clear_short_entries(esp_radio_spinel_idx_t idx) +{ + SuccessOrDie(s_radio[idx].ClearSrcMatchShortEntries()); +} + +esp_err_t esp_radio_spinel_add_short_entry(uint16_t short_address, esp_radio_spinel_idx_t idx) +{ + return (s_radio[idx].AddSrcMatchShortEntry(short_address) == OT_ERROR_NONE) ? ESP_OK : ESP_FAIL; +} + +void esp_radio_spinel_clear_extened_entries(esp_radio_spinel_idx_t idx) +{ + SuccessOrDie(s_radio[idx].ClearSrcMatchExtEntries()); +} + +esp_err_t esp_radio_spinel_add_extened_entry(uint8_t *ext_address, esp_radio_spinel_idx_t idx) +{ + otExtAddress aExtAddress; + memcpy(aExtAddress.m8, (void *)ext_address, OT_EXT_ADDRESS_SIZE); + return (s_radio[idx].AddSrcMatchExtEntry(aExtAddress) == OT_ERROR_NONE) ? ESP_OK : ESP_FAIL; +} + +void esp_radio_spinel_set_promiscuous_mode(bool enable, esp_radio_spinel_idx_t idx) +{ + SuccessOrDie(s_radio[idx].SetPromiscuous(enable)); +} + +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(mainloop_context)); +} + +void esp_radio_spinel_radio_process(esp_radio_spinel_mainloop_context_t *mainloop_context, esp_radio_spinel_idx_t idx) +{ + s_radio[idx].Process(static_cast(mainloop_context)); +} + +void esp_radio_spinel_sleep(esp_radio_spinel_idx_t idx) +{ + s_radio[idx].Sleep(); +} + +void esp_radio_spinel_set_tx_power(int8_t power, esp_radio_spinel_idx_t idx) +{ + s_radio[idx].SetTransmitPower(power); +} + +void esp_radio_spinel_get_tx_power(int8_t *power, esp_radio_spinel_idx_t idx) +{ + int8_t aPower; + s_radio[idx].GetTransmitPower(aPower); + *power = aPower; +} + +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); +} + +esp_err_t esp_radio_spinel_rcp_deinit(esp_radio_spinel_idx_t idx) +{ + if (s_radio[idx].IsEnabled()) { + 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"); + return ESP_OK; +} + + +esp_err_t esp_radio_spinel_rcp_version_get(char *running_rcp_version, esp_radio_spinel_idx_t idx) +{ + const char *rcp_version = s_radio[idx].GetVersion(); + ESP_RETURN_ON_FALSE(rcp_version != nullptr, ESP_FAIL, ESP_SPINEL_LOG_TAG, "Fail to get rcp version"); + strcpy(running_rcp_version, rcp_version); + return ESP_OK; +} diff --git a/components/openthread/src/spinel/esp_radio_spinel_uart_interface.cpp b/components/openthread/src/spinel/esp_radio_spinel_uart_interface.cpp new file mode 100644 index 0000000000..e633e78266 --- /dev/null +++ b/components/openthread/src/spinel/esp_radio_spinel_uart_interface.cpp @@ -0,0 +1,300 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_radio_spinel_uart_interface.hpp" +#include +#include +#include "esp_check.h" +#include "esp_openthread_common_macro.h" +#include "openthread/platform/time.h" +#include "hdlc.hpp" + +namespace esp { +namespace radio_spinel { + +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_radio_spinel_uart_config_t &radio_uart_config) +{ + esp_err_t error = ESP_OK; + m_uart_rx_buffer = static_cast(heap_caps_malloc(kMaxFrameSize, MALLOC_CAP_8BIT)); + if (m_uart_rx_buffer == NULL) { + return ESP_ERR_NO_MEM; + } + + error = InitUart(radio_uart_config); + if (error == ESP_OK) { + ESP_LOGI(ESP_SPINEL_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; + ot::Spinel::FrameBuffer encoder_buffer; + 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 UartSpinelInterface::Process(const void *aMainloopContext) +{ + if (FD_ISSET(m_uart_fd, &((esp_radio_spinel_mainloop_context_t *)aMainloopContext)->read_fds)) { + ESP_LOGD(ESP_SPINEL_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(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(remain / 1000000); + timeout.tv_usec = static_cast(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(rval); + aFrame += static_cast(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(timeout_us / US_PER_S); + timeout.tv_usec = static_cast(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(context)->HandleHdlcFrame(error); +} + +void UartSpinelInterface::HandleHdlcFrame(otError error) +{ + if (error == OT_ERROR_NONE) { + ESP_LOGD(ESP_SPINEL_LOG_TAG, "received hdlc radio frame"); + m_receiver_frame_callback(m_receiver_frame_context); + } else { + ESP_LOGE(ESP_SPINEL_LOG_TAG, "dropping radio frame: %s", otThreadErrorToString(error)); + m_receive_frame_buffer->DiscardFrame(); + } +} + +esp_err_t UartSpinelInterface::InitUart(const esp_radio_spinel_uart_config_t &radio_uart_config) +{ + if (mUartInitHandler) { + 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; + } +} + +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; + } +} + +esp_err_t UartSpinelInterface::TryRecoverUart(void) +{ + ESP_RETURN_ON_ERROR(DeinitUart(), ESP_SPINEL_LOG_TAG, "DeInitUart failed"); + ESP_RETURN_ON_ERROR(InitUart(m_uart_config), ESP_SPINEL_LOG_TAG, "InitUart failed"); + return ESP_OK; +} + +otError UartSpinelInterface::HardwareReset(void) +{ + if (mRcpFailureHandler) { + mRcpFailureHandler(); + TryRecoverUart(); + } + 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_radio_spinel_mainloop_context_t *)aMainloopContext)->read_fds); + if (m_uart_fd > ((esp_radio_spinel_mainloop_context_t *)aMainloopContext)->max_fd) { + ((esp_radio_spinel_mainloop_context_t *)aMainloopContext)->max_fd = m_uart_fd; + } +} + +uint32_t UartSpinelInterface::GetBusSpeed(void) const +{ + return m_uart_config.uart_config.baud_rate; +} +} // namespace radio_spinel +} // namespace esp From f86a39ad879e8b30e91fd17d074f8c593462df74 Mon Sep 17 00:00:00 2001 From: Xu Si Yu Date: Tue, 16 Jan 2024 19:04:19 +0800 Subject: [PATCH 2/7] fix(openthread): set spinel frame buffer to member variables --- components/openthread/Kconfig | 2 +- .../openthread/include/esp_radio_spinel.h | 98 +++++++++++++++---- .../esp_radio_spinel_uart_interface.hpp | 2 + .../esp_uart_spinel_interface.hpp | 2 + .../openthread-core-esp32x-spinel-config.h | 11 ++- .../src/port/esp_openthread_radio_spinel.cpp | 2 + .../src/port/esp_uart_spinel_interface.cpp | 2 +- .../src/spinel/esp_radio_spinel.cpp | 68 ++++++------- .../esp_radio_spinel_uart_interface.cpp | 2 +- 9 files changed, 135 insertions(+), 54 deletions(-) diff --git a/components/openthread/Kconfig b/components/openthread/Kconfig index fc2fa8115c..93b7246b27 100644 --- a/components/openthread/Kconfig +++ b/components/openthread/Kconfig @@ -260,7 +260,7 @@ menu "OpenThread" config OPENTHREAD_SPINEL_RX_FRAME_BUFFER_SIZE int "The size of openthread spinel rx frame buffer" - depends on OPENTHREAD_ENABLED + depends on OPENTHREAD_ENABLED || OPENTHREAD_SPINEL_ONLY default 1024 range 512 8192 diff --git a/components/openthread/include/esp_radio_spinel.h b/components/openthread/include/esp_radio_spinel.h index 7a3fda8837..e8edef1fa6 100644 --- a/components/openthread/include/esp_radio_spinel.h +++ b/components/openthread/include/esp_radio_spinel.h @@ -110,8 +110,12 @@ void esp_radio_spinel_init(esp_radio_spinel_idx_t idx); * * @param[in] idx The index of 802.15.4 related protocol stack. * + * @return + * - ESP_OK on success + * - ESP_FAIL on failures + * */ -void esp_radio_spinel_enable(esp_radio_spinel_idx_t idx); +esp_err_t esp_radio_spinel_enable(esp_radio_spinel_idx_t idx); /** * @brief Set the pending mode. @@ -119,8 +123,12 @@ void esp_radio_spinel_enable(esp_radio_spinel_idx_t idx); * @param[in] pending_mode The pending mode. * @param[in] idx The index of 802.15.4 related protocol stack. * + * @return + * - ESP_OK on success + * - ESP_FAIL on failures + * */ -void esp_radio_spinel_set_pending_mode(esp_ieee802154_pending_mode_t pending_mode, esp_radio_spinel_idx_t idx); +esp_err_t esp_radio_spinel_set_pending_mode(esp_ieee802154_pending_mode_t pending_mode, esp_radio_spinel_idx_t idx); /** * @brief Get the EUI-64. @@ -128,8 +136,12 @@ void esp_radio_spinel_set_pending_mode(esp_ieee802154_pending_mode_t pending_mod * @param[in] eui64 A pointer to the EUI-64. * @param[in] idx The index of 802.15.4 related protocol stack. * + * @return + * - ESP_OK on success + * - ESP_FAIL on failures + * */ -void esp_radio_spinel_get_eui64(uint8_t *eui64, esp_radio_spinel_idx_t idx); +esp_err_t esp_radio_spinel_get_eui64(uint8_t *eui64, esp_radio_spinel_idx_t idx); /** * @brief Set the panid. @@ -137,8 +149,12 @@ void esp_radio_spinel_get_eui64(uint8_t *eui64, esp_radio_spinel_idx_t idx); * @param[in] panid The panid. * @param[in] idx The index of 802.15.4 related protocol stack. * + * @return + * - ESP_OK on success + * - ESP_FAIL on failures + * */ -void esp_radio_spinel_set_panid(uint16_t panid, esp_radio_spinel_idx_t idx); +esp_err_t esp_radio_spinel_set_panid(uint16_t panid, esp_radio_spinel_idx_t idx); /** * @brief Set the short address. @@ -146,8 +162,12 @@ void esp_radio_spinel_set_panid(uint16_t panid, esp_radio_spinel_idx_t idx); * @param[in] short_address The short address. * @param[in] idx The index of 802.15.4 related protocol stack. * + * @return + * - ESP_OK on success + * - ESP_FAIL on failures + * */ -void esp_radio_spinel_set_short_address(uint16_t short_address, esp_radio_spinel_idx_t idx); +esp_err_t esp_radio_spinel_set_short_address(uint16_t short_address, esp_radio_spinel_idx_t idx); /** * @brief Set the extended address. @@ -155,8 +175,12 @@ void esp_radio_spinel_set_short_address(uint16_t short_address, esp_radio_spinel * @param[in] ext_address The extended address. * @param[in] idx The index of 802.15.4 related protocol stack. * + * @return + * - ESP_OK on success + * - ESP_FAIL on failures + * */ -void esp_radio_spinel_set_extended_address(uint8_t *ext_address, esp_radio_spinel_idx_t idx); +esp_err_t esp_radio_spinel_set_extended_address(uint8_t *ext_address, esp_radio_spinel_idx_t idx); /** * @brief Set the coordinator mode. @@ -164,8 +188,12 @@ void esp_radio_spinel_set_extended_address(uint8_t *ext_address, esp_radio_spine * @param[in] enable Enable or disable the coordinator mode. * @param[in] idx The index of 802.15.4 related protocol stack. * + * @return + * - ESP_OK on success + * - ESP_FAIL on failures + * */ -void esp_radio_spinel_set_pan_coord(bool enable, esp_radio_spinel_idx_t idx); +esp_err_t esp_radio_spinel_set_pan_coord(bool enable, esp_radio_spinel_idx_t idx); /** * @brief Enable the RCP reception. @@ -173,8 +201,12 @@ void esp_radio_spinel_set_pan_coord(bool enable, esp_radio_spinel_idx_t idx); * @param[in] channel The channel of reception. * @param[in] idx The index of 802.15.4 related protocol stack. * + * @return + * - ESP_OK on success + * - ESP_FAIL on failures + * */ -void esp_radio_spinel_receive(uint8_t channel, esp_radio_spinel_idx_t idx); +esp_err_t esp_radio_spinel_receive(uint8_t channel, esp_radio_spinel_idx_t idx); /** * @brief Perform the energy scan. @@ -183,8 +215,12 @@ void esp_radio_spinel_receive(uint8_t channel, esp_radio_spinel_idx_t idx); * @param[in] scan_duration The duration for energy scan. * @param[in] idx The index of 802.15.4 related protocol stack. * + * @return + * - ESP_OK on success + * - ESP_FAIL on failures + * */ -void esp_radio_spinel_energy_scan(uint8_t scan_channel, uint16_t scan_duration, esp_radio_spinel_idx_t idx); +esp_err_t esp_radio_spinel_energy_scan(uint8_t scan_channel, uint16_t scan_duration, esp_radio_spinel_idx_t idx); /** * @brief Perform the transmission. @@ -194,16 +230,24 @@ void esp_radio_spinel_energy_scan(uint8_t scan_channel, uint16_t scan_duration, * @param[in] cca Perform clear channel assessment(if it's true) or not(if it's false) * @param[in] idx The index of 802.15.4 related protocol stack. * + * @return + * - ESP_OK on success + * - ESP_FAIL on failures + * */ -void esp_radio_spinel_transmit(uint8_t *frame, uint8_t channel, bool cca, esp_radio_spinel_idx_t idx); +esp_err_t esp_radio_spinel_transmit(uint8_t *frame, uint8_t channel, bool cca, esp_radio_spinel_idx_t idx); /** * @brief Clear all short addresses from the source address match table. * * @param[in] idx The index of 802.15.4 related protocol stack. * + * @return + * - ESP_OK on success + * - ESP_FAIL on failures + * */ -void esp_radio_spinel_clear_short_entries(esp_radio_spinel_idx_t idx); +esp_err_t esp_radio_spinel_clear_short_entries(esp_radio_spinel_idx_t idx); /** * @brief Add a short address to the source address match table. @@ -223,8 +267,12 @@ esp_err_t esp_radio_spinel_add_short_entry(uint16_t short_address, esp_radio_spi * * @param[in] idx The index of 802.15.4 related protocol stack. * + * @return + * - ESP_OK on success + * - ESP_FAIL on failures + * */ -void esp_radio_spinel_clear_extened_entries(esp_radio_spinel_idx_t idx); +esp_err_t esp_radio_spinel_clear_extended_entries(esp_radio_spinel_idx_t idx); /** * @brief Add an extended address to the source address match table. @@ -237,7 +285,7 @@ void esp_radio_spinel_clear_extened_entries(esp_radio_spinel_idx_t idx); * - ESP_FAIL on failures * */ -esp_err_t esp_radio_spinel_add_extened_entry(uint8_t *ext_address, esp_radio_spinel_idx_t idx); +esp_err_t esp_radio_spinel_add_extended_entry(uint8_t *ext_address, esp_radio_spinel_idx_t idx); /** * @brief Sets the status of promiscuous mode. @@ -245,8 +293,12 @@ esp_err_t esp_radio_spinel_add_extened_entry(uint8_t *ext_address, esp_radio_spi * @param[in] enable Whether to enable or disable promiscuous mode. * @param[in] idx The index of 802.15.4 related protocol stack. * + * @return + * - ESP_OK on success + * - ESP_FAIL on failures + * */ -void esp_radio_spinel_set_promiscuous_mode(bool enable, esp_radio_spinel_idx_t idx); +esp_err_t esp_radio_spinel_set_promiscuous_mode(bool enable, esp_radio_spinel_idx_t idx); /** * @brief Update the ESP radio spinel. @@ -271,8 +323,12 @@ void esp_radio_spinel_radio_process(esp_radio_spinel_mainloop_context_t *mainloo * * @param[in] idx The index of 802.15.4 related protocol stack. * + * @return + * - ESP_OK on success + * - ESP_FAIL on failures + * */ -void esp_radio_spinel_sleep(esp_radio_spinel_idx_t idx); +esp_err_t esp_radio_spinel_sleep(esp_radio_spinel_idx_t idx); /** * @brief Set the radio's transmit power in dBm. @@ -280,8 +336,12 @@ void esp_radio_spinel_sleep(esp_radio_spinel_idx_t idx); * @param[in] power The transmit power in dBm. * @param[in] idx The index of 802.15.4 related protocol stack. * + * @return + * - ESP_OK on success + * - ESP_FAIL on failures + * */ -void esp_radio_spinel_set_tx_power(int8_t power, esp_radio_spinel_idx_t idx); +esp_err_t esp_radio_spinel_set_tx_power(int8_t power, esp_radio_spinel_idx_t idx); /** * @brief Get the radio's transmit power in dBm. @@ -289,8 +349,12 @@ void esp_radio_spinel_set_tx_power(int8_t power, esp_radio_spinel_idx_t idx); * @param[in] power A pointer to the transmit power. * @param[in] idx The index of 802.15.4 related protocol stack. * + * @return + * - ESP_OK on success + * - ESP_FAIL on failures + * */ -void esp_radio_spinel_get_tx_power(int8_t *power, esp_radio_spinel_idx_t idx); +esp_err_t esp_radio_spinel_get_tx_power(int8_t *power, esp_radio_spinel_idx_t idx); /** * @brief Register a handler to process the RCP failure. diff --git a/components/openthread/private_include/esp_radio_spinel_uart_interface.hpp b/components/openthread/private_include/esp_radio_spinel_uart_interface.hpp index b053d48519..70f7440b01 100644 --- a/components/openthread/private_include/esp_radio_spinel_uart_interface.hpp +++ b/components/openthread/private_include/esp_radio_spinel_uart_interface.hpp @@ -198,6 +198,8 @@ private: esp_radio_spinel_rcp_failure_handler mRcpFailureHandler; esp_radio_spinel_uart_init_handler mUartInitHandler; esp_radio_spinel_uart_deinit_handler mUartDeinitHandler; + + ot::Spinel::FrameBuffer encoder_buffer; }; } // namespace radio_spinel diff --git a/components/openthread/private_include/esp_uart_spinel_interface.hpp b/components/openthread/private_include/esp_uart_spinel_interface.hpp index 8926ab7fc1..5cd1de7137 100644 --- a/components/openthread/private_include/esp_uart_spinel_interface.hpp +++ b/components/openthread/private_include/esp_uart_spinel_interface.hpp @@ -195,6 +195,8 @@ private: UartSpinelInterface &operator=(const UartSpinelInterface &); esp_openthread_rcp_failure_handler mRcpFailureHandler; + + ot::Spinel::FrameBuffer encoder_buffer; }; } // namespace openthread diff --git a/components/openthread/private_include/openthread-core-esp32x-spinel-config.h b/components/openthread/private_include/openthread-core-esp32x-spinel-config.h index 65badda57e..ab6da83d84 100644 --- a/components/openthread/private_include/openthread-core-esp32x-spinel-config.h +++ b/components/openthread/private_include/openthread-core-esp32x-spinel-config.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -37,3 +37,12 @@ #ifndef OPENTHREAD_SPINEL_CONFIG_RCP_CUSTOM_RESTORATION #define OPENTHREAD_SPINEL_CONFIG_RCP_CUSTOM_RESTORATION 0 #endif + +/** + * @def OPENTHREAD_CONFIG_PLATFORM_RADIO_SPINEL_RX_FRAME_BUFFER_SIZE + * + * Specifies the rx frame buffer size used by `SpinelInterface` in RCP host code. This is applicable/used when + * `RadioSpinel` platform is used. + * + */ +#define OPENTHREAD_CONFIG_PLATFORM_RADIO_SPINEL_RX_FRAME_BUFFER_SIZE CONFIG_OPENTHREAD_SPINEL_RX_FRAME_BUFFER_SIZE diff --git a/components/openthread/src/port/esp_openthread_radio_spinel.cpp b/components/openthread/src/port/esp_openthread_radio_spinel.cpp index 83fdf9b006..50eb22361d 100644 --- a/components/openthread/src/port/esp_openthread_radio_spinel.cpp +++ b/components/openthread/src/port/esp_openthread_radio_spinel.cpp @@ -112,7 +112,9 @@ esp_err_t esp_openthread_rcp_init(void) #endif // CONFIG_OPENTHREAD_RADIO_SPINEL_UART ESP_RETURN_ON_FALSE(s_radio.Enable(esp_openthread_get_instance()) == OT_ERROR_NONE, ESP_FAIL, OT_PLAT_LOG_TAG, "Fail to enable radio"); +#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0 s_radio.RestoreProperties(); +#endif return esp_openthread_platform_workflow_register(&esp_openthread_radio_update, &esp_openthread_radio_process, radiospinel_workflow); } diff --git a/components/openthread/src/port/esp_uart_spinel_interface.cpp b/components/openthread/src/port/esp_uart_spinel_interface.cpp index bb0dc186b8..68f721ad86 100644 --- a/components/openthread/src/port/esp_uart_spinel_interface.cpp +++ b/components/openthread/src/port/esp_uart_spinel_interface.cpp @@ -86,7 +86,7 @@ esp_err_t UartSpinelInterface::Disable(void) otError UartSpinelInterface::SendFrame(const uint8_t *frame, uint16_t length) { otError error = OT_ERROR_NONE; - ot::Spinel::FrameBuffer encoder_buffer; + encoder_buffer.Clear(); ot::Hdlc::Encoder hdlc_encoder(encoder_buffer); SuccessOrExit(error = hdlc_encoder.BeginFrame()); diff --git a/components/openthread/src/spinel/esp_radio_spinel.cpp b/components/openthread/src/spinel/esp_radio_spinel.cpp index 25374d862d..c9895ff00a 100644 --- a/components/openthread/src/spinel/esp_radio_spinel.cpp +++ b/components/openthread/src/spinel/esp_radio_spinel.cpp @@ -215,67 +215,67 @@ void esp_radio_spinel_init(esp_radio_spinel_idx_t idx) s_radio[idx].Init(s_spinel_interface[idx].GetSpinelInterface(), /*reset_radio=*/true, /*skip_rcp_compatibility_check=*/false, iidList, ot::Spinel::kSpinelHeaderMaxNumIid); } -void esp_radio_spinel_enable(esp_radio_spinel_idx_t idx) +esp_err_t esp_radio_spinel_enable(esp_radio_spinel_idx_t idx) { otInstance *instance = get_instance_from_index(idx); - s_radio[idx].Enable(instance); + return (s_radio[idx].Enable(instance) == OT_ERROR_NONE) ? ESP_OK : ESP_FAIL; } -void esp_radio_spinel_set_pending_mode(esp_ieee802154_pending_mode_t pending_mode, esp_radio_spinel_idx_t idx) +esp_err_t esp_radio_spinel_set_pending_mode(esp_ieee802154_pending_mode_t pending_mode, esp_radio_spinel_idx_t idx) { - s_radio[idx].Set(SPINEL_PROP_VENDOR_ESP_SET_PENDINGMODE, SPINEL_DATATYPE_INT32_S, static_cast(pending_mode)); + return (s_radio[idx].Set(SPINEL_PROP_VENDOR_ESP_SET_PENDINGMODE, SPINEL_DATATYPE_INT32_S, static_cast(pending_mode)) == OT_ERROR_NONE) ? ESP_OK : ESP_FAIL; } -void esp_radio_spinel_get_eui64(uint8_t *eui64, esp_radio_spinel_idx_t idx) +esp_err_t esp_radio_spinel_get_eui64(uint8_t *eui64, esp_radio_spinel_idx_t idx) { - SuccessOrDie(s_radio[idx].GetIeeeEui64(eui64)); + return (s_radio[idx].GetIeeeEui64(eui64) == OT_ERROR_NONE) ? ESP_OK : ESP_FAIL; } -void esp_radio_spinel_set_panid(uint16_t panid, esp_radio_spinel_idx_t idx) +esp_err_t esp_radio_spinel_set_panid(uint16_t panid, esp_radio_spinel_idx_t idx) { - SuccessOrDie(s_radio[idx].SetPanId(panid)); + return (s_radio[idx].SetPanId(panid) == OT_ERROR_NONE) ? ESP_OK : ESP_FAIL; } -void esp_radio_spinel_set_short_address(uint16_t short_address, esp_radio_spinel_idx_t idx) +esp_err_t esp_radio_spinel_set_short_address(uint16_t short_address, esp_radio_spinel_idx_t idx) { - SuccessOrDie(s_radio[idx].SetShortAddress(short_address)); + return (s_radio[idx].SetShortAddress(short_address) == OT_ERROR_NONE) ? ESP_OK : ESP_FAIL; } -void esp_radio_spinel_set_extended_address(uint8_t *ext_address, esp_radio_spinel_idx_t idx) +esp_err_t esp_radio_spinel_set_extended_address(uint8_t *ext_address, esp_radio_spinel_idx_t idx) { otExtAddress aExtAddress; memcpy(aExtAddress.m8, (void *)ext_address, OT_EXT_ADDRESS_SIZE); - SuccessOrDie(s_radio[idx].SetExtendedAddress(aExtAddress)); + return (s_radio[idx].SetExtendedAddress(aExtAddress) == OT_ERROR_NONE) ? ESP_OK : ESP_FAIL; } -void esp_radio_spinel_set_pan_coord(bool enable, esp_radio_spinel_idx_t idx) +esp_err_t esp_radio_spinel_set_pan_coord(bool enable, esp_radio_spinel_idx_t idx) { - s_radio[idx].Set(SPINEL_PROP_VENDOR_ESP_SET_COORDINATOR, SPINEL_DATATYPE_BOOL_S, enable); + return (s_radio[idx].Set(SPINEL_PROP_VENDOR_ESP_SET_COORDINATOR, SPINEL_DATATYPE_BOOL_S, enable) == OT_ERROR_NONE) ? ESP_OK : ESP_FAIL; } -void esp_radio_spinel_receive(uint8_t channel, esp_radio_spinel_idx_t idx) +esp_err_t esp_radio_spinel_receive(uint8_t channel, esp_radio_spinel_idx_t idx) { - s_radio[idx].Receive(channel); + return (s_radio[idx].Receive(channel) == OT_ERROR_NONE) ? ESP_OK : ESP_FAIL; } -void esp_radio_spinel_energy_scan(uint8_t scan_channel, uint16_t scan_duration, esp_radio_spinel_idx_t idx) +esp_err_t esp_radio_spinel_energy_scan(uint8_t scan_channel, uint16_t scan_duration, esp_radio_spinel_idx_t idx) { - s_radio[idx].EnergyScan(scan_channel, scan_duration); + return (s_radio[idx].EnergyScan(scan_channel, scan_duration) == OT_ERROR_NONE) ? ESP_OK : ESP_FAIL; } -void esp_radio_spinel_transmit(uint8_t *frame, uint8_t channel, bool cca, esp_radio_spinel_idx_t idx) +esp_err_t esp_radio_spinel_transmit(uint8_t *frame, uint8_t channel, bool cca, esp_radio_spinel_idx_t idx) { s_transmit_frame.mLength = frame[0]; s_transmit_frame.mPsdu = frame + 1; s_transmit_frame.mInfo.mTxInfo.mCsmaCaEnabled = cca; s_transmit_frame.mChannel = channel; s_transmit_frame.mInfo.mTxInfo.mRxChannelAfterTxDone = channel; - SuccessOrDie(s_radio[idx].Transmit(s_transmit_frame)); + return (s_radio[idx].Transmit(s_transmit_frame) == OT_ERROR_NONE) ? ESP_OK : ESP_FAIL; } -void esp_radio_spinel_clear_short_entries(esp_radio_spinel_idx_t idx) +esp_err_t esp_radio_spinel_clear_short_entries(esp_radio_spinel_idx_t idx) { - SuccessOrDie(s_radio[idx].ClearSrcMatchShortEntries()); + return (s_radio[idx].ClearSrcMatchShortEntries() == OT_ERROR_NONE) ? ESP_OK : ESP_FAIL; } esp_err_t esp_radio_spinel_add_short_entry(uint16_t short_address, esp_radio_spinel_idx_t idx) @@ -283,21 +283,21 @@ esp_err_t esp_radio_spinel_add_short_entry(uint16_t short_address, esp_radio_spi return (s_radio[idx].AddSrcMatchShortEntry(short_address) == OT_ERROR_NONE) ? ESP_OK : ESP_FAIL; } -void esp_radio_spinel_clear_extened_entries(esp_radio_spinel_idx_t idx) +esp_err_t esp_radio_spinel_clear_extended_entries(esp_radio_spinel_idx_t idx) { - SuccessOrDie(s_radio[idx].ClearSrcMatchExtEntries()); + return (s_radio[idx].ClearSrcMatchExtEntries() == OT_ERROR_NONE) ? ESP_OK : ESP_FAIL; } -esp_err_t esp_radio_spinel_add_extened_entry(uint8_t *ext_address, esp_radio_spinel_idx_t idx) +esp_err_t esp_radio_spinel_add_extended_entry(uint8_t *ext_address, esp_radio_spinel_idx_t idx) { otExtAddress aExtAddress; memcpy(aExtAddress.m8, (void *)ext_address, OT_EXT_ADDRESS_SIZE); return (s_radio[idx].AddSrcMatchExtEntry(aExtAddress) == OT_ERROR_NONE) ? ESP_OK : ESP_FAIL; } -void esp_radio_spinel_set_promiscuous_mode(bool enable, esp_radio_spinel_idx_t idx) +esp_err_t esp_radio_spinel_set_promiscuous_mode(bool enable, esp_radio_spinel_idx_t idx) { - SuccessOrDie(s_radio[idx].SetPromiscuous(enable)); + return (s_radio[idx].SetPromiscuous(enable) == OT_ERROR_NONE) ? ESP_OK : ESP_FAIL; } void esp_radio_spinel_radio_update(esp_radio_spinel_mainloop_context_t *mainloop_context, esp_radio_spinel_idx_t idx) @@ -310,21 +310,23 @@ void esp_radio_spinel_radio_process(esp_radio_spinel_mainloop_context_t *mainloo s_radio[idx].Process(static_cast(mainloop_context)); } -void esp_radio_spinel_sleep(esp_radio_spinel_idx_t idx) +esp_err_t esp_radio_spinel_sleep(esp_radio_spinel_idx_t idx) { - s_radio[idx].Sleep(); + return (s_radio[idx].Sleep() == OT_ERROR_NONE) ? ESP_OK : ESP_FAIL; } -void esp_radio_spinel_set_tx_power(int8_t power, esp_radio_spinel_idx_t idx) +esp_err_t esp_radio_spinel_set_tx_power(int8_t power, esp_radio_spinel_idx_t idx) { - s_radio[idx].SetTransmitPower(power); + return (s_radio[idx].SetTransmitPower(power) == OT_ERROR_NONE) ? ESP_OK : ESP_FAIL; } -void esp_radio_spinel_get_tx_power(int8_t *power, esp_radio_spinel_idx_t idx) +esp_err_t esp_radio_spinel_get_tx_power(int8_t *power, esp_radio_spinel_idx_t idx) { + otError error = OT_ERROR_NONE; int8_t aPower; - s_radio[idx].GetTransmitPower(aPower); + error = s_radio[idx].GetTransmitPower(aPower); *power = aPower; + return (error == OT_ERROR_NONE) ? ESP_OK : ESP_FAIL; } void esp_radio_spinel_register_rcp_failure_handler(esp_radio_spinel_rcp_failure_handler handler, esp_radio_spinel_idx_t idx) diff --git a/components/openthread/src/spinel/esp_radio_spinel_uart_interface.cpp b/components/openthread/src/spinel/esp_radio_spinel_uart_interface.cpp index e633e78266..d07a67065b 100644 --- a/components/openthread/src/spinel/esp_radio_spinel_uart_interface.cpp +++ b/components/openthread/src/spinel/esp_radio_spinel_uart_interface.cpp @@ -76,7 +76,7 @@ esp_err_t UartSpinelInterface::Disable(void) otError UartSpinelInterface::SendFrame(const uint8_t *frame, uint16_t length) { otError error = OT_ERROR_NONE; - ot::Spinel::FrameBuffer encoder_buffer; + encoder_buffer.Clear(); ot::Hdlc::Encoder hdlc_encoder(encoder_buffer); SuccessOrExit(error = hdlc_encoder.BeginFrame()); From dd0eeaf7f31c02a7ef19096737fc732f9317e1d0 Mon Sep 17 00:00:00 2001 From: Xu Si Yu Date: Thu, 4 Jan 2024 16:16:50 +0800 Subject: [PATCH 3/7] feat(openthread): add menuconfig for openthread address query --- components/openthread/Kconfig | 19 +++++++++++ .../openthread-core-esp32x-ftd-config.h | 32 ++++++++++++++++++- .../openthread-core-esp32x-mtd-config.h | 32 ++++++++++++++++++- 3 files changed, 81 insertions(+), 2 deletions(-) diff --git a/components/openthread/Kconfig b/components/openthread/Kconfig index 93b7246b27..fd77585b23 100644 --- a/components/openthread/Kconfig +++ b/components/openthread/Kconfig @@ -374,5 +374,24 @@ menu "OpenThread" default n help Select this option to enable the OpenThread Radio Spinel for external protocol stack, such as Zigbee. + menu "Thread Address Query Config" + config OPENTHREAD_ADDRESS_QUERY_TIMEOUT + int "Timeout value (in seconds) for a address notification response after sending an address query." + depends on OPENTHREAD_FTD || OPENTHREAD_MTD + default 3 + range 1 10 + + config OPENTHREAD_ADDRESS_QUERY_RETRY_DELAY + int "Initial retry delay for address query (in seconds)." + depends on OPENTHREAD_FTD || OPENTHREAD_MTD + default 15 + range 1 120 + + config OPENTHREAD_ADDRESS_QUERY_MAX_RETRY_DELAY + int "Maximum retry delay for address query (in seconds)." + depends on OPENTHREAD_FTD || OPENTHREAD_MTD + default 120 + range OPENTHREAD_ADDRESS_QUERY_RETRY_DELAY 960 + endmenu endmenu diff --git a/components/openthread/private_include/openthread-core-esp32x-ftd-config.h b/components/openthread/private_include/openthread-core-esp32x-ftd-config.h index b27bc53963..6043159bf3 100644 --- a/components/openthread/private_include/openthread-core-esp32x-ftd-config.h +++ b/components/openthread/private_include/openthread-core-esp32x-ftd-config.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -590,4 +590,34 @@ #define OPENTHREAD_CONFIG_RADIO_STATS_ENABLE 1 #endif +/** + * @def OPENTHREAD_CONFIG_TMF_ADDRESS_QUERY_TIMEOUT + * + * The timeout value (in seconds) waiting for a address notification response after sending an address query. + * + */ +#ifndef OPENTHREAD_CONFIG_TMF_ADDRESS_QUERY_TIMEOUT +#define OPENTHREAD_CONFIG_TMF_ADDRESS_QUERY_TIMEOUT CONFIG_OPENTHREAD_ADDRESS_QUERY_TIMEOUT +#endif + +/** + * @def OPENTHREAD_CONFIG_TMF_ADDRESS_QUERY_INITIAL_RETRY_DELAY + * + * Initial retry delay for address query (in seconds). + * + */ +#ifndef OPENTHREAD_CONFIG_TMF_ADDRESS_QUERY_INITIAL_RETRY_DELAY +#define OPENTHREAD_CONFIG_TMF_ADDRESS_QUERY_INITIAL_RETRY_DELAY CONFIG_OPENTHREAD_ADDRESS_QUERY_RETRY_DELAY +#endif + +/** + * @def OPENTHREAD_CONFIG_TMF_ADDRESS_QUERY_MAX_RETRY_DELAY + * + * Maximum retry delay for address query (in seconds). + * + */ +#ifndef OPENTHREAD_CONFIG_TMF_ADDRESS_QUERY_MAX_RETRY_DELAY +#define OPENTHREAD_CONFIG_TMF_ADDRESS_QUERY_MAX_RETRY_DELAY CONFIG_OPENTHREAD_ADDRESS_QUERY_MAX_RETRY_DELAY +#endif + #define OPENTHREAD_FTD 1 diff --git a/components/openthread/private_include/openthread-core-esp32x-mtd-config.h b/components/openthread/private_include/openthread-core-esp32x-mtd-config.h index f706d364e2..a008afcca6 100644 --- a/components/openthread/private_include/openthread-core-esp32x-mtd-config.h +++ b/components/openthread/private_include/openthread-core-esp32x-mtd-config.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -274,4 +274,34 @@ #define OPENTHREAD_CONFIG_RADIO_STATS_ENABLE 1 #endif +/** + * @def OPENTHREAD_CONFIG_TMF_ADDRESS_QUERY_TIMEOUT + * + * The timeout value (in seconds) waiting for a address notification response after sending an address query. + * + */ +#ifndef OPENTHREAD_CONFIG_TMF_ADDRESS_QUERY_TIMEOUT +#define OPENTHREAD_CONFIG_TMF_ADDRESS_QUERY_TIMEOUT CONFIG_OPENTHREAD_ADDRESS_QUERY_TIMEOUT +#endif + +/** + * @def OPENTHREAD_CONFIG_TMF_ADDRESS_QUERY_INITIAL_RETRY_DELAY + * + * Initial retry delay for address query (in seconds). + * + */ +#ifndef OPENTHREAD_CONFIG_TMF_ADDRESS_QUERY_INITIAL_RETRY_DELAY +#define OPENTHREAD_CONFIG_TMF_ADDRESS_QUERY_INITIAL_RETRY_DELAY CONFIG_OPENTHREAD_ADDRESS_QUERY_RETRY_DELAY +#endif + +/** + * @def OPENTHREAD_CONFIG_TMF_ADDRESS_QUERY_MAX_RETRY_DELAY + * + * Maximum retry delay for address query (in seconds). + * + */ +#ifndef OPENTHREAD_CONFIG_TMF_ADDRESS_QUERY_MAX_RETRY_DELAY +#define OPENTHREAD_CONFIG_TMF_ADDRESS_QUERY_MAX_RETRY_DELAY CONFIG_OPENTHREAD_ADDRESS_QUERY_MAX_RETRY_DELAY +#endif + #define OPENTHREAD_MTD 1 From 08f462dc0e4de2e2d30aeed94ffe1ca6ce323e4b Mon Sep 17 00:00:00 2001 From: zwx Date: Wed, 27 Dec 2023 19:38:42 +0800 Subject: [PATCH 4/7] feat(openthread): integrate console command with ot command --- .../openthread/src/esp_openthread_cli.c | 32 ++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/components/openthread/src/esp_openthread_cli.c b/components/openthread/src/esp_openthread_cli.c index 947bf892b9..6ce998a430 100644 --- a/components/openthread/src/esp_openthread_cli.c +++ b/components/openthread/src/esp_openthread_cli.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -20,8 +20,10 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "linenoise/linenoise.h" - -#define OT_CLI_MAX_LINE_LENGTH 256 +#include "esp_console.h" +#define OT_CLI_MAX_LINE_LENGTH 256 +#define ESP_CONSOLE_PREFIX "esp " +#define ESP_CONSOLE_PREFIX_LENGTH 4 static TaskHandle_t s_cli_task; @@ -63,7 +65,13 @@ esp_err_t esp_openthread_cli_input(const char *line) static void ot_cli_loop(void *context) { + int ret = 0; const char *prompt = "> "; + esp_console_config_t console_config = ESP_CONSOLE_CONFIG_DEFAULT(); + console_config.max_cmdline_length = OT_CLI_MAX_LINE_LENGTH; + + console_config.hint_color = -1; + ret = esp_console_init(&console_config); linenoiseSetMultiLine(true); linenoiseHistorySetMaxLen(100); @@ -78,9 +86,23 @@ static void ot_cli_loop(void *context) char *line = linenoise(prompt); if (line && strnlen(line, OT_CLI_MAX_LINE_LENGTH)) { printf("\r\n"); - esp_openthread_cli_input(line); + if (memcmp(line, ESP_CONSOLE_PREFIX, ESP_CONSOLE_PREFIX_LENGTH) == 0) { + esp_err_t err = esp_console_run(line + ESP_CONSOLE_PREFIX_LENGTH, &ret); + if (err == ESP_ERR_NOT_FOUND) { + printf("Unrecognized command\n"); + } else if (err == ESP_ERR_INVALID_ARG) { + // command was empty + printf("Command is empty\n"); + } else if (err == ESP_OK && ret != ESP_OK) { + printf("Command returned non-zero error code: 0x%x (%s)\n", ret, esp_err_to_name(ret)); + } else if (err != ESP_OK) { + printf("Internal error: %s\n", esp_err_to_name(err)); + } + } else { + esp_openthread_cli_input(line); + xTaskNotifyWait(0, 0, NULL, portMAX_DELAY); + } linenoiseHistoryAdd(line); - xTaskNotifyWait(0, 0, NULL, portMAX_DELAY); } linenoiseFree(line); } From 2f988d08def26be540a0c1aac95627fabb583ce7 Mon Sep 17 00:00:00 2001 From: zwx Date: Thu, 11 Jan 2024 15:36:44 +0800 Subject: [PATCH 5/7] feat(802.15.4): introduce pending tx while rx feature --- .../hal/include/hal/ieee802154_common_ll.h | 9 +++- components/ieee802154/Kconfig | 6 +-- .../ieee802154/driver/esp_ieee802154_debug.c | 17 ++++++- .../ieee802154/driver/esp_ieee802154_dev.c | 47 ++++++++++++++++--- components/ieee802154/linker.lf | 4 ++ .../private_include/esp_ieee802154_util.h | 9 +++- 6 files changed, 79 insertions(+), 13 deletions(-) diff --git a/components/hal/include/hal/ieee802154_common_ll.h b/components/hal/include/hal/ieee802154_common_ll.h index 60c31b0433..b6314cd337 100644 --- a/components/hal/include/hal/ieee802154_common_ll.h +++ b/components/hal/include/hal/ieee802154_common_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -162,6 +162,8 @@ typedef enum { IEEE802154_ED_SAMPLE_AVG = 0x01, } ieee802154_ll_ed_sample_mode_t; +#define IEEE802154_RX_STATUS_RECEIVE_SFD 0x1 + FORCE_INLINE_ATTR void ieee802154_ll_set_cmd(ieee802154_ll_cmd_t cmd) { IEEE802154.cmd.cmd = cmd; @@ -187,6 +189,11 @@ FORCE_INLINE_ATTR ieee802154_ll_events ieee802154_ll_get_events(void) return IEEE802154.event_status.events; } +FORCE_INLINE_ATTR bool ieee802154_ll_is_current_rx_frame(void) +{ + return (IEEE802154.rx_status.rx_state > IEEE802154_RX_STATUS_RECEIVE_SFD); +} + static inline void ieee802154_ll_enable_rx_abort_events(ieee802154_ll_rx_abort_events events) { IEEE802154.rx_abort_event_en.rx_abort_en |= events; diff --git a/components/ieee802154/Kconfig b/components/ieee802154/Kconfig index 488e8e05d6..305d4f2ecb 100644 --- a/components/ieee802154/Kconfig +++ b/components/ieee802154/Kconfig @@ -45,9 +45,9 @@ menu "IEEE 802.15.4" bool "Enable the receive done handler feature" default n help - configure the receive done handler feature, when enabled, the user must call the - function `esp_ieee802154_receive_handle_done` to inform the 802.15.4 driver that - the received frame has been processed, so the frame space could be freed. + configure the receive done handler feature, when enabled, the user must call the + function `esp_ieee802154_receive_handle_done` to inform the 802.15.4 driver that + the received frame has been processed, so the frame space could be freed. config IEEE802154_CCA_MODE depends on IEEE802154_ENABLED diff --git a/components/ieee802154/driver/esp_ieee802154_debug.c b/components/ieee802154/driver/esp_ieee802154_debug.c index 2b172ad952..c8472c8315 100644 --- a/components/ieee802154/driver/esp_ieee802154_debug.c +++ b/components/ieee802154/driver/esp_ieee802154_debug.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -289,6 +289,11 @@ void ieee802154_tx_nums_update(void) s_ieee802154_txrx_statistic.tx.nums++; } +void ieee802154_tx_deferred_nums_update(void) +{ + s_ieee802154_txrx_statistic.tx.deferred_nums++; +} + void ieee802154_tx_break_coex_nums_update(void) { s_ieee802154_txrx_statistic.tx.abort.tx_coex_break_nums++; @@ -302,9 +307,15 @@ void ieee802154_txrx_statistic_print(void) s_ieee802154_txrx_statistic.tx.abort.cca_failed_nums + s_ieee802154_txrx_statistic.tx.abort.cca_busy_nums; uint64_t tx_nums = s_ieee802154_txrx_statistic.tx.nums; + uint64_t tx_direct_num = tx_nums - s_ieee802154_txrx_statistic.tx.deferred_nums; + float tx_success_ratio = (tx_nums > 0 ? ((float)tx_success_nums / tx_nums) : 0); float tx_done_ratio = (tx_nums > 0 ? ((float)s_ieee802154_txrx_statistic.tx.done_nums / tx_nums) : 0); float tx_abort_ratio = (tx_nums > 0 ? ((float)tx_abort_nums / tx_nums) : 0); + + float tx_direct_num_ratio = (tx_nums > 0 ? ((float)tx_direct_num / tx_nums) : 0); + float tx_deferred_num_ratio = (tx_nums > 0 ? ((float)s_ieee802154_txrx_statistic.tx.deferred_nums / tx_nums) : 0); + float tx_abort_rx_ack_coex_break_ratio = (tx_nums > 0 ? ((float)s_ieee802154_txrx_statistic.tx.abort.rx_ack_coex_break_nums / tx_nums) : 0); float tx_abort_rx_ack_timeout_ratio = (tx_nums > 0 ? ((float)s_ieee802154_txrx_statistic.tx.abort.rx_ack_timeout_nums / tx_nums) : 0); float tx_abort_tx_coex_break_ratio = (tx_nums > 0 ? ((float)s_ieee802154_txrx_statistic.tx.abort.tx_coex_break_nums / tx_nums) : 0); @@ -321,6 +332,10 @@ void ieee802154_txrx_statistic_print(void) ESP_LOGW(TAG, "+--------------------+-----------------------------------+--------------------------------------------------+"); ESP_LOGW(TAG, "|%-20s|%-10s%-15llu%9.2f%%|%-25s%-15llu%9.2f%%|", "", "Done:", s_ieee802154_txrx_statistic.tx.done_nums, tx_done_ratio*100, "Success:", tx_success_nums, tx_success_ratio*100); + ESP_LOGW(TAG, "+ + +--------------------------------------------------+"); + ESP_LOGW(TAG, "|%-20s|%-35s|%-25s%-15llu%9.2f%%|", "", "", "tx_direct_num:", tx_direct_num, tx_direct_num_ratio*100); + ESP_LOGW(TAG, "+ + +--------------------------------------------------+"); + ESP_LOGW(TAG, "|%-20s|%-35s|%-25s%-15llu%9.2f%%|", "", "", "tx_deferred_num:", s_ieee802154_txrx_statistic.tx.deferred_nums, tx_deferred_num_ratio*100); ESP_LOGW(TAG, "+ +-----------------------------------+--------------------------------------------------+"); ESP_LOGW(TAG, "|%-20s|%-35s|%-25s%-15llu%9.2f%%|", "", "", "rx_ack_coex_break:", s_ieee802154_txrx_statistic.tx.abort.rx_ack_coex_break_nums, tx_abort_rx_ack_coex_break_ratio*100); ESP_LOGW(TAG, "+ + +--------------------------------------------------+"); diff --git a/components/ieee802154/driver/esp_ieee802154_dev.c b/components/ieee802154/driver/esp_ieee802154_dev.c index 172f51532d..74d51ff89d 100644 --- a/components/ieee802154/driver/esp_ieee802154_dev.c +++ b/components/ieee802154/driver/esp_ieee802154_dev.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -75,6 +75,15 @@ static intr_handle_t s_ieee802154_isr_handle = NULL; static esp_err_t ieee802154_sleep_init(void); static void next_operation(void); +static esp_err_t ieee802154_transmit_internal(const uint8_t *frame, bool cca); + +#if !CONFIG_IEEE802154_TEST +typedef struct { + const uint8_t *frame; + bool cca; +} pending_tx_t; +static pending_tx_t s_pending_tx = { 0 }; +#endif #if CONFIG_IEEE802154_RECEIVE_DONE_HANDLER static void ieee802154_receive_done(uint8_t *data, esp_ieee802154_frame_info_t *frame_info) @@ -367,10 +376,18 @@ static void enable_rx(void) static IRAM_ATTR void next_operation(void) { - if (ieee802154_pib_get_rx_when_idle()) { - enable_rx(); - } else { - ieee802154_set_state(IEEE802154_STATE_IDLE); +#if !CONFIG_IEEE802154_TEST + if (s_pending_tx.frame) { + ieee802154_transmit_internal(s_pending_tx.frame, s_pending_tx.cca); + s_pending_tx.frame = NULL; + } else +#endif + { + if (ieee802154_pib_get_rx_when_idle()) { + enable_rx(); + } else { + ieee802154_set_state(IEEE802154_STATE_IDLE); + } } } @@ -802,7 +819,7 @@ IEEE802154_STATIC void tx_init(const uint8_t *frame) } } -esp_err_t ieee802154_transmit(const uint8_t *frame, bool cca) +static inline esp_err_t ieee802154_transmit_internal(const uint8_t *frame, bool cca) { IEEE802154_RF_ENABLE(); ieee802154_enter_critical(); @@ -819,10 +836,26 @@ esp_err_t ieee802154_transmit(const uint8_t *frame, bool cca) } ieee802154_exit_critical(); - return ESP_OK; } +esp_err_t ieee802154_transmit(const uint8_t *frame, bool cca) +{ +#if !CONFIG_IEEE802154_TEST + if ((s_ieee802154_state == IEEE802154_STATE_RX && ieee802154_ll_is_current_rx_frame()) + || s_ieee802154_state == IEEE802154_STATE_TX_ACK || s_ieee802154_state == IEEE802154_STATE_TX_ENH_ACK) { + // If the current radio is processing an RX frame or sending an ACK, do not shut down the ongoing process. + // Instead, defer the transmission of the pending TX frame. + // Once the current process is completed, the pending transmit frame will be initiated. + s_pending_tx.frame = frame; + s_pending_tx.cca = cca; + IEEE802154_TX_DEFERRED_NUMS_UPDATE(); + return ESP_OK; + } +#endif + return ieee802154_transmit_internal(frame, cca); +} + static inline bool is_target_time_expired(uint32_t target, uint32_t now) { return (((now - target) & (1 << 31)) == 0); diff --git a/components/ieee802154/linker.lf b/components/ieee802154/linker.lf index 3c32c33352..b0a01b70c0 100644 --- a/components/ieee802154/linker.lf +++ b/components/ieee802154/linker.lf @@ -22,6 +22,10 @@ entries: esp_ieee802154_timer: ieee802154_timer0_stop (noflash) esp_ieee802154_timer: ieee802154_timer1_stop (noflash) esp_ieee802154_util: ieee802154_etm_channel_clear (noflash) + + if IEEE802154_DEBUG = y: + esp_ieee802154_debug (noflash) + if IEEE802154_TIMING_OPTIMIZATION = y: esp_ieee802154_dev: set_next_rx_buffer (noflash) esp_ieee802154_dev: stop_rx (noflash) diff --git a/components/ieee802154/private_include/esp_ieee802154_util.h b/components/ieee802154/private_include/esp_ieee802154_util.h index 3288ac9e80..0d0268f422 100644 --- a/components/ieee802154/private_include/esp_ieee802154_util.h +++ b/components/ieee802154/private_include/esp_ieee802154_util.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -185,6 +185,7 @@ void ieee802154_assert_print(void); typedef struct ieee802154_txrx_statistic{ struct { uint64_t nums; + uint64_t deferred_nums; uint64_t done_nums; struct { uint64_t rx_ack_coex_break_nums; // IEEE802154_RX_ACK_ABORT_COEX_CNT_REG @@ -218,6 +219,10 @@ typedef struct ieee802154_txrx_statistic{ ieee802154_txrx_statistic(a);\ } while(0) +#define IEEE802154_TX_DEFERRED_NUMS_UPDATE() do { \ + ieee802154_tx_deferred_nums_update();\ + } while(0) + #define IEEE802154_TX_NUMS_UPDATE() do { \ ieee802154_tx_nums_update();\ } while(0) @@ -230,10 +235,12 @@ void ieee802154_txrx_statistic_clear(void); void ieee802154_txrx_statistic_print(void); void ieee802154_txrx_statistic(ieee802154_ll_events events); void ieee802154_tx_nums_update(void); +void ieee802154_tx_deferred_nums_update(void); void ieee802154_tx_break_coex_nums_update(void); #else #define IEEE802154_TXRX_STATISTIC(a) #define IEEE802154_TX_NUMS_UPDATE() +#define IEEE802154_TX_DEFERRED_NUMS_UPDATE() #define IEEE802154_TXRX_STATISTIC_CLEAR() #define IEEE802154_TX_BREAK_COEX_NUMS_UPDATE() #endif // CONFIG_IEEE802154_TXRX_STATISTIC From 738145a65e6fca275051beddd1c1e0d5cf2b77e5 Mon Sep 17 00:00:00 2001 From: xiaqilin Date: Wed, 17 Jan 2024 19:40:15 +0800 Subject: [PATCH 6/7] fix(ieee802154): add next operation in ieee802154 test when rx abort by tx ack coex --- components/ieee802154/driver/esp_ieee802154_dev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/ieee802154/driver/esp_ieee802154_dev.c b/components/ieee802154/driver/esp_ieee802154_dev.c index 74d51ff89d..86abf5e3b3 100644 --- a/components/ieee802154/driver/esp_ieee802154_dev.c +++ b/components/ieee802154/driver/esp_ieee802154_dev.c @@ -526,19 +526,19 @@ static IRAM_ATTR void isr_handle_rx_abort(void) IEEE802154_ASSERT(s_ieee802154_state == IEEE802154_STATE_TX_ACK || s_ieee802154_state == IEEE802154_STATE_TX_ENH_ACK); #if !CONFIG_IEEE802154_TEST ieee802154_receive_done((uint8_t *)s_rx_frame[s_rx_index], &s_rx_frame_info[s_rx_index]); - next_operation(); #else esp_ieee802154_receive_failed(rx_status); #endif + next_operation(); break; case IEEE802154_RX_ABORT_BY_ENHACK_SECURITY_ERROR: IEEE802154_ASSERT(s_ieee802154_state == IEEE802154_STATE_TX_ENH_ACK); #if !CONFIG_IEEE802154_TEST ieee802154_receive_done((uint8_t *)s_rx_frame[s_rx_index], &s_rx_frame_info[s_rx_index]); - next_operation(); #else esp_ieee802154_receive_failed(rx_status); #endif + next_operation(); break; default: IEEE802154_ASSERT(false); From e2e54312da03246f9a2e6c2ed53856e07821574f Mon Sep 17 00:00:00 2001 From: zwx Date: Fri, 19 Jan 2024 17:16:49 +0800 Subject: [PATCH 7/7] feat(openthread): support openthread radio caps rx on when idle --- components/ieee802154/driver/esp_ieee802154_dev.c | 1 + components/openthread/src/port/esp_openthread_radio.c | 10 ++++++++-- .../src/port/esp_openthread_radio_spinel.cpp | 7 ++++++- examples/openthread/ot_cli/main/idf_component.yml | 2 +- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/components/ieee802154/driver/esp_ieee802154_dev.c b/components/ieee802154/driver/esp_ieee802154_dev.c index 86abf5e3b3..1e915d4070 100644 --- a/components/ieee802154/driver/esp_ieee802154_dev.c +++ b/components/ieee802154/driver/esp_ieee802154_dev.c @@ -387,6 +387,7 @@ static IRAM_ATTR void next_operation(void) enable_rx(); } else { ieee802154_set_state(IEEE802154_STATE_IDLE); + ieee802154_sleep(); } } } diff --git a/components/openthread/src/port/esp_openthread_radio.c b/components/openthread/src/port/esp_openthread_radio.c index a5013a5c4b..11d2437a95 100644 --- a/components/openthread/src/port/esp_openthread_radio.c +++ b/components/openthread/src/port/esp_openthread_radio.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -347,7 +347,7 @@ int8_t otPlatRadioGetRssi(otInstance *aInstance) otRadioCaps otPlatRadioGetCaps(otInstance *aInstance) { - return (otRadioCaps)(OT_RADIO_CAPS_ENERGY_SCAN | + return (otRadioCaps)(OT_RADIO_CAPS_ENERGY_SCAN | OT_RADIO_CAPS_RX_ON_WHEN_IDLE | OT_RADIO_CAPS_TRANSMIT_SEC | OT_RADIO_CAPS_RECEIVE_TIMING | OT_RADIO_CAPS_TRANSMIT_TIMING | OT_RADIO_CAPS_ACK_TIMEOUT | OT_RADIO_CAPS_SLEEP_TO_TX); } @@ -774,3 +774,9 @@ otError otPlatRadioSetChannelMaxTransmitPower(otInstance *aInstance, uint8_t aCh return OT_ERROR_NONE; } + +void otPlatRadioSetRxOnWhenIdle(otInstance *aInstance, bool aEnable) +{ + OT_UNUSED_VARIABLE(aInstance); + esp_ieee802154_set_rx_when_idle(aEnable); +} diff --git a/components/openthread/src/port/esp_openthread_radio_spinel.cpp b/components/openthread/src/port/esp_openthread_radio_spinel.cpp index 50eb22361d..2574bd2b13 100644 --- a/components/openthread/src/port/esp_openthread_radio_spinel.cpp +++ b/components/openthread/src/port/esp_openthread_radio_spinel.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -412,3 +412,8 @@ otError otPlatRadioConfigureEnhAckProbing(otInstance *aInstance, otLinkMetrics a return s_radio.ConfigureEnhAckProbing(aLinkMetrics, aShortAddress, *aExtAddress); } #endif + +void otPlatRadioSetRxOnWhenIdle(otInstance *aInstance, bool aEnable) +{ + s_radio.SetRxOnWhenIdle(aEnable); +} diff --git a/examples/openthread/ot_cli/main/idf_component.yml b/examples/openthread/ot_cli/main/idf_component.yml index 9d13c3b615..9dd02beb43 100644 --- a/examples/openthread/ot_cli/main/idf_component.yml +++ b/examples/openthread/ot_cli/main/idf_component.yml @@ -1,6 +1,6 @@ ## IDF Component Manager Manifest File dependencies: espressif/esp_ot_cli_extension: - version: "~0.4.0" + version: "~1.0.0" idf: version: ">=4.1.0"