Merge branch 'backport5.1/openthread_backport' into 'release/v5.1'

some openthread changes backport to release/v5.1

See merge request espressif/esp-idf!28638
This commit is contained in:
Shu Chen
2024-01-26 13:37:02 +08:00
26 changed files with 1748 additions and 41 deletions

View File

@ -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;

View File

@ -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

View File

@ -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, "+ + +--------------------------------------------------+");

View File

@ -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,19 @@ 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);
ieee802154_sleep();
}
}
}
@ -509,19 +527,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);
@ -802,7 +820,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 +837,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);

View File

@ -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)

View File

@ -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

View File

@ -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(

View File

@ -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
@ -253,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
@ -362,4 +369,29 @@ 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.
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

View File

@ -0,0 +1,395 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <sys/select.h>
#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.
*
* @return
* - ESP_OK on success
* - ESP_FAIL on failures
*
*/
esp_err_t 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.
*
* @return
* - ESP_OK on success
* - ESP_FAIL on failures
*
*/
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.
*
* @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
*
*/
esp_err_t 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.
*
* @return
* - ESP_OK on success
* - ESP_FAIL on failures
*
*/
esp_err_t 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.
*
* @return
* - ESP_OK on success
* - ESP_FAIL on failures
*
*/
esp_err_t 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.
*
* @return
* - ESP_OK on success
* - ESP_FAIL on failures
*
*/
esp_err_t 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.
*
* @return
* - ESP_OK on success
* - ESP_FAIL on failures
*
*/
esp_err_t 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.
*
* @return
* - ESP_OK on success
* - ESP_FAIL on failures
*
*/
esp_err_t 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.
*
* @return
* - ESP_OK on success
* - ESP_FAIL on failures
*
*/
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.
*
* @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.
*
* @return
* - ESP_OK on success
* - ESP_FAIL on failures
*
*/
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
*
*/
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.
*
* @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.
*
* @return
* - ESP_OK on success
* - ESP_FAIL on failures
*
*/
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.
*
* @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_extended_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.
*
* @return
* - ESP_OK on success
* - ESP_FAIL on failures
*
*/
esp_err_t 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.
*
* @return
* - ESP_OK on success
* - ESP_FAIL on failures
*
*/
esp_err_t 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.
*
* @return
* - ESP_OK on success
* - ESP_FAIL on failures
*
*/
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.
*
* @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
*
*/
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.
*
* @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

View File

@ -0,0 +1,15 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <spinel.h>
#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

View File

@ -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 <typename InterfaceType> class SpinelInterfaceAdapter {
public:
SpinelInterfaceAdapter(void) {}
~SpinelInterfaceAdapter(void) {}
InterfaceType &GetSpinelInterface(void) { return mSpinelInterface; }
private:
InterfaceType mSpinelInterface;
};
} // namespace radio_spinel
} // namespace esp

View File

@ -0,0 +1,206 @@
/*
* 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;
ot::Spinel::FrameBuffer<kMaxFrameSize> encoder_buffer;
};
} // namespace radio_spinel
} // namespace esp

View File

@ -195,6 +195,8 @@ private:
UartSpinelInterface &operator=(const UartSpinelInterface &);
esp_openthread_rcp_failure_handler mRcpFailureHandler;
ot::Spinel::FrameBuffer<kMaxFrameSize> encoder_buffer;
};
} // namespace openthread

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,16 +1,10 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
*
* 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
@ -43,4 +38,11 @@
#define OPENTHREAD_SPINEL_CONFIG_RCP_CUSTOM_RESTORATION 0
#endif
#endif // OPENTHREAD_SPINEL_CONFIG_H_
/**
* @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

View File

@ -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);
}

View File

@ -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<esp_ieee802154_pending_mode_t>(pending_mode));
break;
}
default:
error = OT_ERROR_NOT_FOUND;
break;
}
return error;
}
} // namespace Ncp
} // namespace ot
#endif // #if OPENTHREAD_ENABLE_NCP_VENDOR_HOOK

View File

@ -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<Instance *>(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

View File

@ -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);
}

View File

@ -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
*/
@ -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);
}
@ -410,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);
}

View File

@ -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<kMaxFrameSize> encoder_buffer;
encoder_buffer.Clear();
ot::Hdlc::Encoder hdlc_encoder(encoder_buffer);
SuccessOrExit(error = hdlc_encoder.BeginFrame());

View File

@ -0,0 +1,354 @@
/*
* 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<UartSpinelInterface> 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);
}
esp_err_t esp_radio_spinel_enable(esp_radio_spinel_idx_t idx)
{
otInstance *instance = get_instance_from_index(idx);
return (s_radio[idx].Enable(instance) == OT_ERROR_NONE) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_radio_spinel_set_pending_mode(esp_ieee802154_pending_mode_t pending_mode, esp_radio_spinel_idx_t idx)
{
return (s_radio[idx].Set(SPINEL_PROP_VENDOR_ESP_SET_PENDINGMODE, SPINEL_DATATYPE_INT32_S, static_cast<int32_t>(pending_mode)) == OT_ERROR_NONE) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_radio_spinel_get_eui64(uint8_t *eui64, esp_radio_spinel_idx_t idx)
{
return (s_radio[idx].GetIeeeEui64(eui64) == OT_ERROR_NONE) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_radio_spinel_set_panid(uint16_t panid, esp_radio_spinel_idx_t idx)
{
return (s_radio[idx].SetPanId(panid) == OT_ERROR_NONE) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_radio_spinel_set_short_address(uint16_t short_address, esp_radio_spinel_idx_t idx)
{
return (s_radio[idx].SetShortAddress(short_address) == OT_ERROR_NONE) ? ESP_OK : ESP_FAIL;
}
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);
return (s_radio[idx].SetExtendedAddress(aExtAddress) == OT_ERROR_NONE) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_radio_spinel_set_pan_coord(bool enable, esp_radio_spinel_idx_t idx)
{
return (s_radio[idx].Set(SPINEL_PROP_VENDOR_ESP_SET_COORDINATOR, SPINEL_DATATYPE_BOOL_S, enable) == OT_ERROR_NONE) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_radio_spinel_receive(uint8_t channel, esp_radio_spinel_idx_t idx)
{
return (s_radio[idx].Receive(channel) == OT_ERROR_NONE) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_radio_spinel_energy_scan(uint8_t scan_channel, uint16_t scan_duration, esp_radio_spinel_idx_t idx)
{
return (s_radio[idx].EnergyScan(scan_channel, scan_duration) == OT_ERROR_NONE) ? ESP_OK : ESP_FAIL;
}
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;
return (s_radio[idx].Transmit(s_transmit_frame) == OT_ERROR_NONE) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_radio_spinel_clear_short_entries(esp_radio_spinel_idx_t idx)
{
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)
{
return (s_radio[idx].AddSrcMatchShortEntry(short_address) == OT_ERROR_NONE) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_radio_spinel_clear_extended_entries(esp_radio_spinel_idx_t idx)
{
return (s_radio[idx].ClearSrcMatchExtEntries() == OT_ERROR_NONE) ? ESP_OK : ESP_FAIL;
}
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;
}
esp_err_t esp_radio_spinel_set_promiscuous_mode(bool enable, esp_radio_spinel_idx_t idx)
{
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)
{
s_spinel_interface[idx].GetSpinelInterface().UpdateFdSet(static_cast<void *>(mainloop_context));
}
void esp_radio_spinel_radio_process(esp_radio_spinel_mainloop_context_t *mainloop_context, esp_radio_spinel_idx_t idx)
{
s_radio[idx].Process(static_cast<void *>(mainloop_context));
}
esp_err_t esp_radio_spinel_sleep(esp_radio_spinel_idx_t idx)
{
return (s_radio[idx].Sleep() == OT_ERROR_NONE) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_radio_spinel_set_tx_power(int8_t power, esp_radio_spinel_idx_t idx)
{
return (s_radio[idx].SetTransmitPower(power) == OT_ERROR_NONE) ? ESP_OK : ESP_FAIL;
}
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;
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)
{
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;
}

View File

@ -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 <errno.h>
#include <sys/unistd.h>
#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<uint8_t *>(heap_caps_malloc(kMaxFrameSize, MALLOC_CAP_8BIT));
if (m_uart_rx_buffer == NULL) {
return ESP_ERR_NO_MEM;
}
error = InitUart(radio_uart_config);
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;
encoder_buffer.Clear();
ot::Hdlc::Encoder hdlc_encoder(encoder_buffer);
SuccessOrExit(error = hdlc_encoder.BeginFrame());
SuccessOrExit(error = hdlc_encoder.Encode(frame, length));
SuccessOrExit(error = hdlc_encoder.EndFrame());
SuccessOrExit(error = Write(encoder_buffer.GetFrame(), encoder_buffer.GetLength()));
exit:
if (error != OT_ERROR_NONE) {
ESP_LOGE(ESP_SPINEL_LOG_TAG, "send radio frame failed");
} else {
ESP_LOGD(ESP_SPINEL_LOG_TAG, "sent radio frame");
}
return error;
}
void 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<uint16_t>(rval));
}
} while (rval > 0);
if ((rval < 0) && (errno != EAGAIN) && (errno != EWOULDBLOCK)) {
ESP_ERROR_CHECK(TryRecoverUart());
}
return rval;
}
otError UartSpinelInterface::WaitForWritable(void)
{
otError error = OT_ERROR_NONE;
struct timeval timeout = {kMaxWaitTime / MS_PER_S, (kMaxWaitTime % MS_PER_S) * US_PER_MS};
uint64_t now = otPlatTimeGet();
uint64_t end = now + kMaxWaitTime * US_PER_MS;
fd_set write_fds;
fd_set error_fds;
int rval;
while (true) {
FD_ZERO(&write_fds);
FD_ZERO(&error_fds);
FD_SET(m_uart_fd, &write_fds);
FD_SET(m_uart_fd, &error_fds);
rval = select(m_uart_fd + 1, NULL, &write_fds, &error_fds, &timeout);
if (rval > 0) {
if (FD_ISSET(m_uart_fd, &write_fds)) {
ExitNow();
} else if (FD_ISSET(m_uart_fd, &error_fds)) {
ExitNow(error = OT_ERROR_FAILED);
}
} else if ((rval < 0) && (errno != EINTR)) {
ESP_ERROR_CHECK(TryRecoverUart());
ExitNow(error = OT_ERROR_FAILED);
}
now = otPlatTimeGet();
if (end > now) {
uint64_t remain = end - now;
timeout.tv_sec = static_cast<time_t>(remain / 1000000);
timeout.tv_usec = static_cast<suseconds_t>(remain % 1000000);
} else {
break;
}
}
error = OT_ERROR_FAILED;
exit:
return error;
}
otError UartSpinelInterface::Write(const uint8_t *aFrame, uint16_t length)
{
otError error = OT_ERROR_NONE;
while (length) {
ssize_t rval;
rval = write(m_uart_fd, aFrame, length);
if (rval > 0) {
assert(rval <= length);
length -= static_cast<uint16_t>(rval);
aFrame += static_cast<uint16_t>(rval);
continue;
} else if (rval < 0) {
ESP_ERROR_CHECK(TryRecoverUart());
ExitNow(error = OT_ERROR_FAILED);
}
SuccessOrExit(error = WaitForWritable());
}
exit:
return error;
}
otError UartSpinelInterface::WaitForFrame(uint64_t timeout_us)
{
otError error = OT_ERROR_NONE;
struct timeval timeout;
fd_set read_fds;
fd_set error_fds;
int rval;
FD_ZERO(&read_fds);
FD_ZERO(&error_fds);
FD_SET(m_uart_fd, &read_fds);
FD_SET(m_uart_fd, &error_fds);
timeout.tv_sec = static_cast<time_t>(timeout_us / US_PER_S);
timeout.tv_usec = static_cast<suseconds_t>(timeout_us % US_PER_S);
rval = select(m_uart_fd + 1, &read_fds, NULL, &error_fds, &timeout);
if (rval > 0) {
if (FD_ISSET(m_uart_fd, &read_fds)) {
TryReadAndDecode();
} else if (FD_ISSET(m_uart_fd, &error_fds)) {
ESP_ERROR_CHECK(TryRecoverUart());
ExitNow(error = OT_ERROR_FAILED);
}
} else if (rval == 0) {
ExitNow(error = OT_ERROR_RESPONSE_TIMEOUT);
} else {
ESP_ERROR_CHECK(TryRecoverUart());
ExitNow(error = OT_ERROR_FAILED);
}
exit:
return error;
}
void UartSpinelInterface::HandleHdlcFrame(void *context, otError error)
{
static_cast<UartSpinelInterface *>(context)->HandleHdlcFrame(error);
}
void UartSpinelInterface::HandleHdlcFrame(otError error)
{
if (error == OT_ERROR_NONE) {
ESP_LOGD(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

View File

@ -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"