feat(openthread): support multipan feature for RCP

This commit is contained in:
Xu Si Yu
2026-08-14 15:26:00 +08:00
parent 7f333e0763
commit d497516a87
17 changed files with 758 additions and 223 deletions

View File

@@ -1,4 +1,4 @@
[codespell]
skip = build,*.yuv,components/fatfs/src/*,alice.txt,*.rgb,components/wpa_supplicant/*,components/esp_wifi/*
ignore-words-list = ser,dout,rsource,fram,inout,shs,ans,aci,unstall,unstalling,hart,wheight,ot,wel,parms,ehen,pre-empt,EMAC
ignore-words-list = ser,dout,rsource,fram,inout,shs,ans,aci,unstall,unstalling,hart,wheight,ot,wel,parms,ehen,pre-empt,EMAC,acount
write-changes = true

View File

@@ -12,44 +12,46 @@
#include "esp_ieee802154_pib.h"
#include "esp_ieee802154_util.h"
static uint8_t s_rx_when_idle_mask = 0;
uint16_t esp_ieee802154_get_multipan_panid(esp_ieee802154_multipan_index_t index)
{
assert(index < CONFIG_IEEE802154_INTERFACE_NUM);
IEEE802154_ASSERT(index < CONFIG_IEEE802154_INTERFACE_NUM);
return ieee802154_ll_get_multipan_panid(index);
}
esp_err_t esp_ieee802154_set_multipan_panid(esp_ieee802154_multipan_index_t index, uint16_t panid)
{
assert(index < CONFIG_IEEE802154_INTERFACE_NUM);
IEEE802154_ASSERT(index < CONFIG_IEEE802154_INTERFACE_NUM);
ieee802154_ll_set_multipan_panid(index, panid);
return ESP_OK;
}
uint16_t esp_ieee802154_get_multipan_short_address(esp_ieee802154_multipan_index_t index)
{
assert(index < CONFIG_IEEE802154_INTERFACE_NUM);
IEEE802154_ASSERT(index < CONFIG_IEEE802154_INTERFACE_NUM);
return ieee802154_ll_get_multipan_short_addr(index);
}
esp_err_t esp_ieee802154_set_multipan_short_address(esp_ieee802154_multipan_index_t index, uint16_t short_address)
{
assert(index < CONFIG_IEEE802154_INTERFACE_NUM);
IEEE802154_ASSERT(index < CONFIG_IEEE802154_INTERFACE_NUM);
ieee802154_ll_set_multipan_short_addr(index, short_address);
return ESP_OK;
}
esp_err_t esp_ieee802154_get_multipan_extended_address(esp_ieee802154_multipan_index_t index, uint8_t *ext_addr)
{
assert(index < CONFIG_IEEE802154_INTERFACE_NUM);
assert(ext_addr != NULL);
IEEE802154_ASSERT(index < CONFIG_IEEE802154_INTERFACE_NUM);
IEEE802154_ASSERT(ext_addr != NULL);
ieee802154_ll_get_multipan_ext_addr(index, ext_addr);
return ESP_OK;
}
esp_err_t esp_ieee802154_set_multipan_extended_address(esp_ieee802154_multipan_index_t index, const uint8_t *ext_addr)
{
assert(index < CONFIG_IEEE802154_INTERFACE_NUM);
assert(ext_addr != NULL);
IEEE802154_ASSERT(index < CONFIG_IEEE802154_INTERFACE_NUM);
IEEE802154_ASSERT(ext_addr != NULL);
ieee802154_ll_set_multipan_ext_addr(index, ext_addr);
return ESP_OK;
}
@@ -61,39 +63,79 @@ uint8_t esp_ieee802154_get_multipan_enable(void)
esp_err_t esp_ieee802154_set_multipan_enable(uint8_t mask)
{
assert(mask < (1 << CONFIG_IEEE802154_INTERFACE_NUM));
IEEE802154_ASSERT(mask < (1 << CONFIG_IEEE802154_INTERFACE_NUM));
ieee802154_ll_set_multipan_enable_mask(mask);
return ESP_OK;
}
esp_ieee802154_pending_mode_t esp_ieee802154_multipan_get_pending_mode(esp_ieee802154_multipan_index_t index)
{
assert(index < CONFIG_IEEE802154_INTERFACE_NUM);
IEEE802154_ASSERT(index < CONFIG_IEEE802154_INTERFACE_NUM);
return ieee802154_pib_get_pending_mode(index);
}
esp_err_t esp_ieee802154_multipan_set_pending_mode(esp_ieee802154_multipan_index_t inf_index, esp_ieee802154_pending_mode_t pending_mode)
{
assert(inf_index < CONFIG_IEEE802154_INTERFACE_NUM);
IEEE802154_ASSERT(inf_index < CONFIG_IEEE802154_INTERFACE_NUM);
ieee802154_pib_set_pending_mode(inf_index, pending_mode);
return ESP_OK;
}
esp_err_t esp_ieee802154_multipan_add_pending_addr(esp_ieee802154_multipan_index_t inf_index, const uint8_t *addr, bool is_short)
{
assert(inf_index < CONFIG_IEEE802154_INTERFACE_NUM);
IEEE802154_ASSERT(inf_index < CONFIG_IEEE802154_INTERFACE_NUM);
return ieee802154_add_pending_addr(inf_index, addr, is_short);
}
esp_err_t esp_ieee802154_multipan_clear_pending_addr(esp_ieee802154_multipan_index_t inf_index, const uint8_t *addr, bool is_short)
{
assert(inf_index < CONFIG_IEEE802154_INTERFACE_NUM);
IEEE802154_ASSERT(inf_index < CONFIG_IEEE802154_INTERFACE_NUM);
return ieee802154_clear_pending_addr(inf_index, addr, is_short);
}
esp_err_t esp_ieee802154_multipan_reset_pending_table(esp_ieee802154_multipan_index_t inf_index, bool is_short)
{
assert(inf_index < CONFIG_IEEE802154_INTERFACE_NUM);
IEEE802154_ASSERT(inf_index < CONFIG_IEEE802154_INTERFACE_NUM);
ieee802154_reset_pending_table(inf_index, is_short);
return ESP_OK;
}
esp_err_t esp_ieee802154_multipan_sleep(int8_t idx)
{
IEEE802154_ASSERT(idx >= 0 && idx < CONFIG_IEEE802154_INTERFACE_NUM);
uint8_t enable_mask = ieee802154_ll_get_multipan_enable_mask();
enable_mask &= ~(1 << idx);
IEEE802154_ASSERT(enable_mask < (1 << CONFIG_IEEE802154_INTERFACE_NUM));
ieee802154_ll_set_multipan_enable_mask(enable_mask);
if (enable_mask == 0) {
return ieee802154_sleep();
}
return ESP_OK;
}
esp_err_t esp_ieee802154_multipan_receive(int8_t idx)
{
IEEE802154_ASSERT(idx >= 0 && idx < CONFIG_IEEE802154_INTERFACE_NUM);
uint8_t enable_mask = ieee802154_ll_get_multipan_enable_mask();
enable_mask |= (1 << idx);
IEEE802154_ASSERT(enable_mask < (1 << CONFIG_IEEE802154_INTERFACE_NUM));
ieee802154_ll_set_multipan_enable_mask(enable_mask);
return ieee802154_receive();
}
esp_err_t esp_ieee802154_multipan_set_rx_when_idle(int8_t idx, bool enable)
{
IEEE802154_ASSERT(idx >= 0 && idx < CONFIG_IEEE802154_INTERFACE_NUM);
if (enable) {
s_rx_when_idle_mask |= (1 << idx);
ieee802154_pib_set_rx_when_idle(true);
} else {
s_rx_when_idle_mask &= ~(1 << idx);
if (s_rx_when_idle_mask == 0) {
ieee802154_pib_set_rx_when_idle(false);
}
}
return ESP_OK;
}
// Note: multipan receive_at is not supported yet.

View File

@@ -182,6 +182,43 @@ esp_ieee802154_pending_mode_t esp_ieee802154_multipan_get_pending_mode(esp_ieee8
*/
esp_err_t esp_ieee802154_multipan_set_pending_mode(esp_ieee802154_multipan_index_t inf_index, esp_ieee802154_pending_mode_t pending_mode);
/**
* @brief Set the IEEE 802.15.4 Radio to sleep state for the specified multipan interface.
*
* @param[in] idx Index of the multipan interface.
*
* @return
* - ESP_OK on success.
* - ESP_FAIL on failure.
*/
esp_err_t esp_ieee802154_multipan_sleep(int8_t idx);
/**
* @brief Set the IEEE 802.15.4 Radio to receive state for the specified multipan interface.
*
* @param[in] idx Index of the multipan interface.
*
* @return
* - ESP_OK on success.
* - ESP_FAIL on failure.
*/
esp_err_t esp_ieee802154_multipan_receive(int8_t idx);
/**
* @brief Set the RxOnWhenIdle mode for the specified multipan interface.
*
* Each interface owns one bit in an internal mask. The hardware RxOnWhenIdle
* setting is enabled when any interface requests it, and is disabled only
* when all interface bits are cleared.
*
* @param[in] idx Index of the multipan interface.
* @param[in] enable True to enable RxOnWhenIdle for this interface.
*
* @return
* - ESP_OK on success.
*/
esp_err_t esp_ieee802154_multipan_set_rx_when_idle(int8_t idx, bool enable);
#ifdef __cplusplus
}
#endif

View File

@@ -517,6 +517,22 @@ menu "OpenThread"
OpenThread esp_netif, attaches the esp_netif glue, and enables
lwIP-specific DNS/netif handling.
config OPENTHREAD_MULTIPAN_RCP_ENABLE
bool "Enable Multipan RCP"
depends on (OPENTHREAD_RADIO && IEEE802154_MULTI_PAN_ENABLE)
default n
help
Enable MULTI-PAN RCP support. This also enables multiple OpenThread
instances. Currently only radio devices are supported.
config OPENTHREAD_MULTIPLE_INTERFACES_COUNT
int "The number of multiple interfaces"
depends on OPENTHREAD_MULTIPAN_RCP_ENABLE
default 2
range 2 3
help
Number of multiple interfaces.
endmenu
menu "Thread Log"

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -42,3 +42,9 @@
#else
#define ESP_OPENTHREAD_ASSERT(a) assert(a)
#endif
#if CONFIG_OPENTHREAD_MULTIPAN_RCP_ENABLE
#define OT_INSTANCE_COUNT CONFIG_OPENTHREAD_MULTIPLE_INTERFACES_COUNT
#else
#define OT_INSTANCE_COUNT 1
#endif

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -10,8 +10,21 @@
extern "C" {
#endif
/**
* @brief This function initializes the NCP for a single instance
*
* @param[in] aInstance The OpenThread instance.
*/
void otAppNcpInit(otInstance *aInstance);
/**
* @brief This function initializes the NCP for multiple instances
*
* @param[in] aInstances The array of OpenThread instances.
* @param[in] aCount The number of instances.
*/
void otAppNcpInitMulti(otInstance **aInstances, uint8_t aCount);
#if CONFIG_OPENTHREAD_RCP_SPINEL_CONSOLE
esp_err_t esp_console_redirect_to_otlog(void);
#endif // CONFIG_OPENTHREAD_RCP_SPINEL_CONSOLE

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -155,6 +155,37 @@ void esp_openthread_set_storage_name(const char *name);
*/
uint32_t esp_openthread_get_alloc_caps(void);
/**
* @brief This function gets the index of the instance
*
* @param[in] instance The OpenThread instance.
*
* @note Invalid input is logged and asserted.
*
* @return
* - The index of the instance
*/
int8_t esp_openthread_get_idx_from_instance(otInstance *instance);
/**
* @brief This function gets the instance from the index
*
* @param[in] idx The index of the instance.
*
* @note Invalid index is logged and asserted. A valid index always has an instance.
*
* @return
* - The instance
*/
otInstance *esp_openthread_get_instance_from_idx(int8_t idx);
/**
* @brief This function sets the active instance
*
* @param[in] instance The OpenThread instance.
*/
void esp_openthread_set_active_instance(otInstance *instance);
#ifdef __cplusplus
} // end of extern "C"
#endif

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -165,6 +165,62 @@
#define OPENTHREAD_CONFIG_TIME_SYNC_ENABLE 0
#endif
/**
* @def OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE
*
* Define to 1 to enable multiple instance support.
*/
#ifdef OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE
#error `OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE` is redefined.
#endif
#if CONFIG_OPENTHREAD_MULTIPAN_RCP_ENABLE
#define OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE 1
#else
#define OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE 0
#endif
/**
* @def OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
*
* Define to 1 to enable multipan RCP support.
*/
#ifdef OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
#error `OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE` is redefined.
#endif
#if CONFIG_OPENTHREAD_MULTIPAN_RCP_ENABLE
#define OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE 1
#else
#define OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE 0
#endif
/**
* @def OPENTHREAD_CONFIG_MULTIPLE_STATIC_INSTANCE_ENABLE
*
* Define to 1 to enable multiple static instance support.
*/
#ifdef OPENTHREAD_CONFIG_MULTIPLE_STATIC_INSTANCE_ENABLE
#error `OPENTHREAD_CONFIG_MULTIPLE_STATIC_INSTANCE_ENABLE` is redefined.
#endif
#if CONFIG_OPENTHREAD_MULTIPAN_RCP_ENABLE
#define OPENTHREAD_CONFIG_MULTIPLE_STATIC_INSTANCE_ENABLE 1
#else
#define OPENTHREAD_CONFIG_MULTIPLE_STATIC_INSTANCE_ENABLE 0
#endif
/**
* @def OPENTHREAD_SPINEL_CONFIG_BROADCAST_IID
*
* Define broadcast IID for spinel frames dedicated to all hosts in multipan configuration.
* Host interfaces use IID 0 .. COUNT-1; broadcast takes the next slot.
*
*/
#ifdef OPENTHREAD_SPINEL_CONFIG_BROADCAST_IID
#error `OPENTHREAD_SPINEL_CONFIG_BROADCAST_IID` is redefined.
#endif
#if CONFIG_OPENTHREAD_MULTIPAN_RCP_ENABLE
#define OPENTHREAD_SPINEL_CONFIG_BROADCAST_IID SPINEL_HEADER_IID(CONFIG_OPENTHREAD_MULTIPLE_INTERFACES_COUNT)
#endif
/*----The following options set fixed default values but can be overridden by the user header file.----*/
#if CONFIG_OPENTHREAD_LINK_METRICS

View File

@@ -5,7 +5,7 @@ supplier: 'Organization: Espressif Systems (Shanghai) CO LTD'
originator: 'Organization: Google LLC'
description: OpenThread released by Google is an open-source implementation of the Thread networking
url: https://github.com/espressif/openthread
hash: b678a4f63b6f9397d1a0fa8f31e5b8e0271a4d00
hash: 43cc05a9bcf780bd758bad53d897e2d88cf8cb75
cve-exclude-list:
- cve: CVE-2026-8369
reason: We use Espressifs NAT64 implementation and hence this CVE from the upstream NAT64 implementation is not applicable.

View File

@@ -5,16 +5,27 @@
*/
#include "sdkconfig.h"
#include "core/instance/instance.hpp"
#include <assert.h>
#include "esp_log.h"
#include "esp_openthread.h"
#include "esp_openthread_common_macro.h"
#include "esp_openthread_instance.h"
#include "esp_openthread_ncp.h"
#include "esp_openthread_platform.h"
#include "openthread/instance.h"
#include "openthread/tasklet.h"
static otInstance *s_active_instance = NULL;
static otInstance *s_instances[OT_INSTANCE_COUNT] = {NULL};
bool esp_openthread_instances_init(void)
{
return otInstanceInitSingle() != NULL;
for (int i = 0; i < OT_INSTANCE_COUNT; i++) {
s_instances[i] = otInstanceInitMultiple(i);
assert(s_instances[i]);
}
esp_openthread_set_active_instance(s_instances[0]);
return true;
}
void esp_openthread_instances_deinit(otInstance *instance)
@@ -24,24 +35,79 @@ void esp_openthread_instances_deinit(otInstance *instance)
bool esp_openthread_tasklets_are_pending(otInstance *instance)
{
return otTaskletsArePending(instance);
(void)instance;
for (int idx = 0; idx < OT_INSTANCE_COUNT; idx++) {
if (otTaskletsArePending(esp_openthread_get_instance_from_idx(idx))) {
return true;
}
}
return false;
}
void esp_openthread_tasklets_process(otInstance *instance)
{
while (otTaskletsArePending(instance)) {
otTaskletsProcess(instance);
(void)instance;
for (int idx = 0; idx < OT_INSTANCE_COUNT; idx++) {
otInstance *inst = esp_openthread_get_instance_from_idx(idx);
while (otTaskletsArePending(inst)) {
otTaskletsProcess(inst);
}
}
}
#if CONFIG_OPENTHREAD_RADIO
void esp_openthread_ncp_app_init(otInstance *instance)
{
otAppNcpInit(instance);
(void)instance;
otInstance *instances[OT_INSTANCE_COUNT] = {nullptr};
for (int idx = 0; idx < OT_INSTANCE_COUNT; idx++) {
instances[idx] = esp_openthread_get_instance_from_idx(idx);
}
otAppNcpInitMulti(instances, OT_INSTANCE_COUNT);
}
#endif
otInstance *esp_openthread_get_instance(void)
{
return (otInstance *)&ot::Instance::Get();
return s_active_instance;
}
int8_t esp_openthread_get_idx_from_instance(otInstance *instance)
{
if (instance == nullptr) {
ESP_LOGE(OT_PLAT_LOG_TAG, "Instance is NULL");
assert(instance != nullptr);
}
uint8_t idx = otInstanceGetIndex(instance);
if (idx >= OT_INSTANCE_COUNT) {
ESP_LOGE(OT_PLAT_LOG_TAG, "Index %u is out of range", idx);
assert(idx < OT_INSTANCE_COUNT);
}
return static_cast<int8_t>(idx);
}
otInstance *esp_openthread_get_instance_from_idx(int8_t idx)
{
if (idx < 0 || idx >= OT_INSTANCE_COUNT) {
ESP_LOGE(OT_PLAT_LOG_TAG, "Index %d is out of range", idx);
assert(idx >= 0 && idx < OT_INSTANCE_COUNT);
}
otInstance *instance = otInstanceGetInstance(static_cast<uint8_t>(idx));
if (instance == nullptr) {
ESP_LOGE(OT_PLAT_LOG_TAG, "No instance for index %d", idx);
assert(instance != nullptr);
}
return instance;
}
void esp_openthread_set_active_instance(otInstance *instance)
{
s_active_instance = instance;
}

View File

@@ -11,6 +11,7 @@
#include <sys/time.h>
#include "esp_log.h"
#include "esp_openthread.h"
#include "esp_openthread_common_macro.h"
#include "esp_openthread_platform.h"
#include "esp_timer.h"
@@ -23,10 +24,15 @@
#include "openthread/platform/radio.h"
#include "openthread/platform/time.h"
static uint32_t s_alarm_ms = 0;
static bool s_is_ms_running = false;
static uint32_t s_alarm_us = 0;
static bool s_is_us_running = false;
typedef struct {
uint32_t alarm_ms;
bool is_ms_running;
uint32_t alarm_us;
bool is_us_running;
otInstance *instance;
} esp_openthread_alarm_t;
static esp_openthread_alarm_t s_alarms[OT_INSTANCE_COUNT];
static const char *alarm_workflow = "alarm";
static inline bool is_expired(uint32_t target, uint32_t now)
@@ -46,19 +52,19 @@ uint64_t otPlatTimeGet(void)
void otPlatAlarmMilliStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt)
{
OT_UNUSED_VARIABLE(aInstance);
int8_t idx = esp_openthread_get_idx_from_instance(aInstance);
s_alarm_ms = aT0 + aDt;
s_is_ms_running = true;
s_alarms[idx].alarm_ms = aT0 + aDt;
s_alarms[idx].is_ms_running = true;
ESP_LOGD(OT_PLAT_LOG_TAG, "Millisecond timer alarm start running, t0=%"PRIu32", dt=%"PRIu32"", aT0, aDt);
}
void otPlatAlarmMilliStop(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
int8_t idx = esp_openthread_get_idx_from_instance(aInstance);
s_is_ms_running = false;
s_alarms[idx].is_ms_running = false;
}
uint32_t inline otPlatAlarmMilliGetNow(void)
@@ -68,18 +74,19 @@ uint32_t inline otPlatAlarmMilliGetNow(void)
void otPlatAlarmMicroStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt)
{
OT_UNUSED_VARIABLE(aInstance);
int8_t idx = esp_openthread_get_idx_from_instance(aInstance);
s_alarm_us = aT0 + aDt;
s_is_us_running = true;
s_alarms[idx].alarm_us = aT0 + aDt;
s_alarms[idx].is_us_running = true;
ESP_LOGD(OT_PLAT_LOG_TAG, "Microsecond timer alarm start running, t0=%"PRIu32", dt=%"PRIu32"", aT0, aDt);
}
void otPlatAlarmMicroStop(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
s_is_us_running = false;
int8_t idx = esp_openthread_get_idx_from_instance(aInstance);
s_alarms[idx].is_us_running = false;
}
uint32_t inline otPlatAlarmMicroGetNow(void)
@@ -89,6 +96,9 @@ uint32_t inline otPlatAlarmMicroGetNow(void)
esp_err_t esp_openthread_alarm_init(void)
{
for (int idx = 0; idx < OT_INSTANCE_COUNT; idx++) {
memset((void *)(&s_alarms[idx]), 0, sizeof(esp_openthread_alarm_t));
}
return esp_openthread_platform_workflow_register(&esp_openthread_alarm_update, &esp_openthread_alarm_process,
alarm_workflow);
}
@@ -103,19 +113,22 @@ void esp_openthread_alarm_update(esp_openthread_mainloop_context_t *mainloop)
struct timeval *timeout = &mainloop->timeout;
int64_t remain_min_time_us = INT64_MAX;
int64_t remaining_us = 0;
if (s_is_ms_running) {
remaining_us = calculate_duration(s_alarm_ms, otPlatAlarmMilliGetNow()) * US_PER_MS;
if (remain_min_time_us > remaining_us) {
remain_min_time_us = remaining_us;
for (int idx = 0; idx < OT_INSTANCE_COUNT; idx++) {
if (s_alarms[idx].is_ms_running) {
remaining_us = calculate_duration(s_alarms[idx].alarm_ms, otPlatAlarmMilliGetNow()) * US_PER_MS;
if (remain_min_time_us > remaining_us) {
remain_min_time_us = remaining_us;
}
}
if (s_alarms[idx].is_us_running) {
remaining_us = calculate_duration(s_alarms[idx].alarm_us, otPlatAlarmMicroGetNow());
if (remain_min_time_us > remaining_us) {
remain_min_time_us = remaining_us;
}
}
}
if (s_is_us_running) {
remaining_us = calculate_duration(s_alarm_us, otPlatAlarmMicroGetNow());
if (remain_min_time_us > remaining_us) {
remain_min_time_us = remaining_us;
}
}
if (remain_min_time_us > 0) {
if (remain_min_time_us != INT64_MAX && remain_min_time_us > 0) {
timeout->tv_sec = remain_min_time_us / US_PER_S;
timeout->tv_usec = remain_min_time_us % US_PER_S;
} else {
@@ -126,24 +139,29 @@ void esp_openthread_alarm_update(esp_openthread_mainloop_context_t *mainloop)
esp_err_t esp_openthread_alarm_process(otInstance *aInstance, const esp_openthread_mainloop_context_t *mainloop)
{
if (s_is_ms_running && is_expired(s_alarm_ms, otPlatAlarmMilliGetNow())) {
s_is_ms_running = false;
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(mainloop);
for (int idx = 0; idx < OT_INSTANCE_COUNT; idx++) {
otInstance *instance = esp_openthread_get_instance_from_idx(idx);
if (s_alarms[idx].is_ms_running && is_expired(s_alarms[idx].alarm_ms, otPlatAlarmMilliGetNow())) {
s_alarms[idx].is_ms_running = false;
#if OPENTHREAD_CONFIG_DIAG_ENABLE
if (otPlatDiagModeGet()) {
otPlatDiagAlarmFired(aInstance);
} else
if (otPlatDiagModeGet()) {
otPlatDiagAlarmFired(instance);
} else
#endif
{
otPlatAlarmMilliFired(aInstance);
}
{
otPlatAlarmMilliFired(instance);
}
ESP_LOGD(OT_PLAT_LOG_TAG, "Millisecond timer alarm fired");
}
if (s_is_us_running && is_expired(s_alarm_us, otPlatAlarmMicroGetNow())) {
s_is_us_running = false;
otPlatAlarmMicroFired(aInstance);
ESP_LOGD(OT_PLAT_LOG_TAG, "Microsecond timer alarm fired");
ESP_LOGD(OT_PLAT_LOG_TAG, "Millisecond timer alarm fired");
}
if (s_alarms[idx].is_us_running && is_expired(s_alarms[idx].alarm_us, otPlatAlarmMicroGetNow())) {
s_alarms[idx].is_us_running = false;
otPlatAlarmMicroFired(instance);
ESP_LOGD(OT_PLAT_LOG_TAG, "Microsecond timer alarm fired");
}
}
return ESP_OK;
}

View File

@@ -10,7 +10,10 @@
#include "sdkconfig.h"
#include "esp_check.h"
#include "esp_ieee802154.h"
#include "esp_ieee802154_multipan.h"
#include "esp_ieee802154_types.h"
#include "esp_mac.h"
#include "esp_openthread.h"
#include "esp_openthread_common.h"
#include "esp_openthread_common_macro.h"
#include "esp_openthread_platform.h"
@@ -26,6 +29,7 @@
#include "openthread/error.h"
#include "openthread/link.h"
#include "openthread/platform/diag.h"
#include "openthread/platform/multipan.h"
#include "openthread/platform/radio.h"
#include "openthread/platform/time.h"
#include "utils/link_metrics.h"
@@ -51,11 +55,20 @@ typedef struct {
uint8_t psdu[OT_RADIO_FRAME_MAX_SIZE];
} esp_openthread_radio_tx_psdu;
static otRadioFrame s_transmit_frame;
typedef struct esp_openthread_radio_transmit_context {
int8_t index;
otRadioFrame *aFrame;
} esp_openthread_radio_transmit_context_t;
static esp_openthread_radio_tx_psdu s_transmit_psdu;
typedef struct esp_openthread_radio_receive_context {
int8_t index;
otRadioFrame frame;
} esp_openthread_radio_receive_context_t;
static otRadioFrame s_transmit_frame[OT_INSTANCE_COUNT];
static esp_openthread_radio_tx_psdu s_transmit_psdu[OT_INSTANCE_COUNT];
static uint8_t *s_enhack;
static otRadioFrame s_receive_frame[CONFIG_IEEE802154_RX_BUFFER_SIZE];
static esp_openthread_radio_receive_context_t s_receive_frame[CONFIG_IEEE802154_RX_BUFFER_SIZE];
static otRadioFrame s_ack_frame;
static int s_ed_power;
static esp_ieee802154_tx_error_t s_tx_error;
@@ -68,7 +81,7 @@ static bool s_diag_mode = false;
#endif
#if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT
static otRadioIeInfo s_transmit_ie_info;
static otRadioIeInfo s_transmit_ie_info[OT_INSTANCE_COUNT];
#endif // OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
@@ -77,44 +90,107 @@ static uint32_t s_csl_sample_time;
#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
static uint32_t s_mac_frame_counter;
static uint8_t s_key_id;
static struct otMacKeyMaterial s_previous_key;
static struct otMacKeyMaterial s_current_key;
static struct otMacKeyMaterial s_next_key;
typedef struct {
uint32_t mac_frame_counter;
uint8_t key_id;
struct otMacKeyMaterial previous_key;
struct otMacKeyMaterial current_key;
struct otMacKeyMaterial next_key;
#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
static uint8_t s_pervious_key_bytes[16];
static uint8_t s_current_key_bytes[16];
static uint8_t s_next_key_bytes[16];
uint8_t previous_key_bytes[16];
uint8_t current_key_bytes[16];
uint8_t next_key_bytes[16];
#endif
static bool s_with_security_enh_ack = false;
static uint32_t s_ack_frame_counter;
static uint8_t s_ack_key_id;
static uint8_t s_security_key[16];
static uint8_t s_security_addr[8];
bool with_security_enh_ack;
uint32_t ack_frame_counter;
uint8_t ack_key_id;
uint8_t security_key[16];
uint8_t security_addr[8];
} esp_openthread_radio_security_ctx_t;
static void ot_set_security_key_from_key_material(struct otMacKeyMaterial a_key_material)
static esp_openthread_radio_security_ctx_t s_sec_ctx[OT_INSTANCE_COUNT];
#endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
/* Head is the in-flight TX; remaining entries are pending.
* Only touched in OpenThread task context (ISR only reads head/index). */
typedef struct {
uint8_t head;
uint8_t tail;
uint8_t used;
} esp_openthread_tx_queue_info_t;
static esp_openthread_radio_transmit_context_t s_tx_queue[OT_INSTANCE_COUNT];
static esp_openthread_tx_queue_info_t s_tx_queue_info = {.head = 0, .tail = 0, .used = 0};
#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
static void ot_set_security_key_from_key_material(int8_t idx, struct otMacKeyMaterial a_key_material)
{
if (idx < 0 || idx >= OT_INSTANCE_COUNT) {
return;
}
#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
/* Key bytes are pre-exported in task context (otPlatRadioSetMacKey).
* Identify which cached buffer to use by comparing the key reference.
* Jira TZ-2472 */
if (a_key_material.mKeyMaterial.mKeyRef == s_previous_key.mKeyMaterial.mKeyRef) {
memcpy(s_security_key, s_pervious_key_bytes, 16);
} else if (a_key_material.mKeyMaterial.mKeyRef == s_next_key.mKeyMaterial.mKeyRef) {
memcpy(s_security_key, s_next_key_bytes, 16);
if (a_key_material.mKeyMaterial.mKeyRef == s_sec_ctx[idx].previous_key.mKeyMaterial.mKeyRef) {
memcpy(s_sec_ctx[idx].security_key, s_sec_ctx[idx].previous_key_bytes, 16);
} else if (a_key_material.mKeyMaterial.mKeyRef == s_sec_ctx[idx].next_key.mKeyMaterial.mKeyRef) {
memcpy(s_sec_ctx[idx].security_key, s_sec_ctx[idx].next_key_bytes, 16);
} else {
memcpy(s_security_key, s_current_key_bytes, 16);
memcpy(s_sec_ctx[idx].security_key, s_sec_ctx[idx].current_key_bytes, 16);
}
#else
memcpy(s_security_key, a_key_material.mKeyMaterial.mKey.m8, sizeof(a_key_material.mKeyMaterial.mKey.m8));
memcpy(s_sec_ctx[idx].security_key, a_key_material.mKeyMaterial.mKey.m8,
sizeof(a_key_material.mKeyMaterial.mKey.m8));
#endif
}
#endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
static esp_openthread_circular_queue_info_t s_recv_queue = {.head = 0, .tail = 0, .used = 0};
static void radio_tx_queue_pop_head(void)
{
assert(s_tx_queue_info.used > 0);
s_tx_queue_info.head = (s_tx_queue_info.head + 1) % OT_INSTANCE_COUNT;
s_tx_queue_info.used--;
}
static void radio_start_transmit(otInstance *aInstance, otRadioFrame *aFrame, int8_t idx)
{
esp_ieee802154_set_channel(aFrame->mChannel);
aFrame->mPsdu[-1] = aFrame->mLength; // length locates one byte before the psdu (esp_openthread_radio_tx_psdu);
if (otMacFrameIsSecurityEnabled(aFrame) && !aFrame->mInfo.mTxInfo.mIsSecurityProcessed) {
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
if (!s_transmit_frame[idx].mInfo.mTxInfo.mIsARetx || s_csl_period > 0) {
#else
if (!s_transmit_frame[idx].mInfo.mTxInfo.mIsARetx) {
#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
otMacFrameSetFrameCounter(aFrame, s_sec_ctx[idx].mac_frame_counter++);
}
if (otMacFrameIsKeyIdMode1(aFrame)) {
s_transmit_frame[idx].mInfo.mTxInfo.mAesKey = &s_sec_ctx[idx].current_key;
if (!s_transmit_frame[idx].mInfo.mTxInfo.mIsARetx) {
otMacFrameSetKeyId(aFrame, s_sec_ctx[idx].key_id);
}
esp_ieee802154_get_multipan_extended_address((esp_ieee802154_multipan_index_t)idx, s_sec_ctx[idx].security_addr);
}
ot_set_security_key_from_key_material(idx, s_sec_ctx[idx].current_key);
esp_ieee802154_set_transmit_security(&aFrame->mPsdu[-1], s_sec_ctx[idx].security_key,
s_sec_ctx[idx].security_addr);
}
if (aFrame->mInfo.mTxInfo.mTxDelay != 0) {
esp_ieee802154_transmit_at(&aFrame->mPsdu[-1], aFrame->mInfo.mTxInfo.mCsmaCaEnabled,
(aFrame->mInfo.mTxInfo.mTxDelayBaseTime + aFrame->mInfo.mTxInfo.mTxDelay));
} else {
esp_ieee802154_transmit(&aFrame->mPsdu[-1], aFrame->mInfo.mTxInfo.mCsmaCaEnabled);
}
otPlatRadioTxStarted(aInstance, aFrame);
}
static void set_event(uint8_t event)
{
uint64_t event_write = event;
@@ -136,15 +212,40 @@ static inline bool get_event(uint8_t event)
static void ot_radio_receive_done(uint8_t *data, esp_ieee802154_frame_info_t *frame_info);
static void ot_radio_receive_sfd_done(void);
static void ot_radio_transmit_done(const uint8_t *frame, const uint8_t *ack,
esp_ieee802154_frame_info_t *ack_frame_info);
esp_ieee802154_frame_info_t *ack_frame_info);
static void ot_radio_transmit_failed(const uint8_t *frame, esp_ieee802154_tx_error_t error);
static void ot_radio_transmit_sfd_done(uint8_t *frame);
static void ot_radio_energy_detect_done(int8_t power);
static esp_err_t ot_radio_enh_ack_generator(uint8_t *frame, esp_ieee802154_frame_info_t *frame_info,
uint8_t *enhack_frame);
uint8_t *enhack_frame);
static void radio_init_software_context(void)
{
for (int idx = 0; idx < OT_INSTANCE_COUNT; idx++) {
s_transmit_frame[idx].mPsdu = s_transmit_psdu[idx].psdu;
}
for (uint8_t i = 0; i < CONFIG_IEEE802154_RX_BUFFER_SIZE; i++) {
s_receive_frame[i].frame.mPsdu = NULL;
}
s_ack_frame.mPsdu = NULL;
memset(&s_recv_queue, 0, sizeof(esp_openthread_circular_queue_info_t));
memset(&s_tx_queue_info, 0, sizeof(esp_openthread_tx_queue_info_t));
s_txrx_events = 0;
#if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT
for (int idx = 0; idx < OT_INSTANCE_COUNT; idx++) {
s_transmit_frame[idx].mInfo.mTxInfo.mIeInfo = &s_transmit_ie_info[idx];
}
#endif
}
esp_err_t esp_openthread_radio_init(const esp_openthread_platform_config_t *config)
{
esp_err_t ret = ESP_OK;
const uint8_t multipan_mask = (uint8_t)((1U << CONFIG_IEEE802154_INTERFACE_NUM) - 1U);
ESP_RETURN_ON_FALSE(s_radio_event_fd == -1, ESP_ERR_INVALID_STATE, OT_PLAT_LOG_TAG,
"Radio was initialized already!");
@@ -161,26 +262,37 @@ esp_err_t esp_openthread_radio_init(const esp_openthread_platform_config_t *conf
"Failed to register ieee802154 event callbacks");
s_radio_event_fd = eventfd(0, EFD_SUPPORT_ISR);
ESP_GOTO_ON_FALSE(s_radio_event_fd != -1, ESP_FAIL, exit_unregister, OT_PLAT_LOG_TAG, "Failed to create eventfd");
s_transmit_frame.mPsdu = s_transmit_psdu.psdu;
for (uint8_t i = 0; i < CONFIG_IEEE802154_RX_BUFFER_SIZE; i++) {
s_receive_frame[i].mPsdu = NULL;
}
s_ack_frame.mPsdu = NULL;
memset(&s_recv_queue, 0, sizeof(esp_openthread_circular_queue_info_t));
#if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT
s_transmit_frame.mInfo.mTxInfo.mIeInfo = &s_transmit_ie_info;
#endif
radio_init_software_context();
esp_ieee802154_enable();
esp_ieee802154_set_promiscuous(false);
esp_ieee802154_set_rx_when_idle(true);
ESP_GOTO_ON_ERROR(esp_ieee802154_set_multipan_enable(multipan_mask), exit, OT_PLAT_LOG_TAG,
"Fail to set multipan mask");
return esp_openthread_platform_workflow_register(&esp_openthread_radio_update, &esp_openthread_radio_process,
s_radio_workflow);
for (int i = 0; i < CONFIG_IEEE802154_INTERFACE_NUM; i++) {
if (multipan_mask & (1U << i)) {
esp_ieee802154_multipan_set_rx_when_idle(i, true);
}
}
esp_ieee802154_set_promiscuous(false);
ESP_GOTO_ON_ERROR(esp_openthread_platform_workflow_register(&esp_openthread_radio_update,
&esp_openthread_radio_process,
s_radio_workflow),
exit, OT_PLAT_LOG_TAG, "Fail to register radio workflow");
return ESP_OK;
exit:
esp_ieee802154_disable();
if (s_radio_event_fd != -1) {
close(s_radio_event_fd);
s_radio_event_fd = -1;
}
exit_unregister:
esp_ieee802154_event_callback_list_unregister();
return ret;
}
void esp_openthread_radio_deinit(void)
@@ -190,6 +302,10 @@ void esp_openthread_radio_deinit(void)
s_radio_event_fd = -1;
}
memset(&s_recv_queue, 0, sizeof(esp_openthread_circular_queue_info_t));
memset(&s_tx_queue_info, 0, sizeof(esp_openthread_tx_queue_info_t));
s_txrx_events = 0;
esp_ieee802154_disable();
esp_ieee802154_event_callback_list_unregister();
esp_openthread_platform_workflow_unregister(s_radio_workflow);
@@ -208,21 +324,32 @@ esp_err_t esp_openthread_radio_process(otInstance *aInstance, const esp_openthre
uint64_t event_read;
int ret = read(s_radio_event_fd, &event_read, sizeof(event_read));
assert(ret == sizeof(event_read));
bool start_pending_tx = false;
if (get_event(EVENT_TX_DONE)) {
clr_event(EVENT_TX_DONE);
assert(s_tx_queue_info.used > 0);
int8_t tx_idx = s_tx_queue[s_tx_queue_info.head].index;
assert(tx_idx >= 0 && tx_idx < OT_INSTANCE_COUNT);
otInstance *tx_instance = esp_openthread_get_instance_from_idx(tx_idx);
if (s_ack_frame.mPsdu == NULL) {
otPlatRadioTxDone(aInstance, &s_transmit_frame, NULL, OT_ERROR_NONE);
otPlatRadioTxDone(tx_instance, &s_transmit_frame[tx_idx], NULL, OT_ERROR_NONE);
} else {
otPlatRadioTxDone(aInstance, &s_transmit_frame, &s_ack_frame, OT_ERROR_NONE);
otPlatRadioTxDone(tx_instance, &s_transmit_frame[tx_idx], &s_ack_frame, OT_ERROR_NONE);
esp_ieee802154_receive_handle_done(s_ack_frame.mPsdu - 1);
s_ack_frame.mPsdu = NULL;
}
radio_tx_queue_pop_head();
start_pending_tx = true;
}
if (get_event(EVENT_TX_FAILED)) {
clr_event(EVENT_TX_FAILED);
assert(s_tx_queue_info.used > 0);
int8_t tx_idx = s_tx_queue[s_tx_queue_info.head].index;
assert(tx_idx >= 0 && tx_idx < OT_INSTANCE_COUNT);
otInstance *tx_instance = esp_openthread_get_instance_from_idx(tx_idx);
otError err = OT_ERROR_NONE;
@@ -243,7 +370,17 @@ esp_err_t esp_openthread_radio_process(otInstance *aInstance, const esp_openthre
break;
}
otPlatRadioTxDone(aInstance, &s_transmit_frame, NULL, err);
otPlatRadioTxDone(tx_instance, &s_transmit_frame[tx_idx], NULL, err);
radio_tx_queue_pop_head();
start_pending_tx = true;
}
// After in-flight TX finishes, start the next frame already in the queue
// before processing RX, to reduce TX idle time.
if (start_pending_tx && s_tx_queue_info.used > 0) {
esp_openthread_radio_transmit_context_t *packet = &s_tx_queue[s_tx_queue_info.head];
otInstance *instance = esp_openthread_get_instance_from_idx(packet->index);
radio_start_transmit(instance, packet->aFrame, packet->index);
}
if (get_event(EVENT_ENERGY_DETECT_DONE)) {
@@ -252,10 +389,20 @@ esp_err_t esp_openthread_radio_process(otInstance *aInstance, const esp_openthre
}
while (atomic_load(&s_recv_queue.used)) {
if (s_receive_frame[s_recv_queue.head].mPsdu != NULL) {
otPlatRadioReceiveDone(aInstance, &s_receive_frame[s_recv_queue.head], OT_ERROR_NONE);
esp_ieee802154_receive_handle_done(s_receive_frame[s_recv_queue.head].mPsdu - 1);
s_receive_frame[s_recv_queue.head].mPsdu = NULL;
if (s_receive_frame[s_recv_queue.head].frame.mPsdu != NULL) {
if (s_receive_frame[s_recv_queue.head].index == ESP_IEEE802154_MULTIPAN_MAX) {
// should check all used instances and receive packets for multicast packet.
// TZ-2585
for (int i = 0; i < OT_INSTANCE_COUNT; i++) {
otInstance *ins = esp_openthread_get_instance_from_idx(i);
otPlatRadioReceiveDone(ins, &s_receive_frame[s_recv_queue.head].frame, OT_ERROR_NONE);
}
} else {
otInstance *ins = esp_openthread_get_instance_from_idx(s_receive_frame[s_recv_queue.head].index);
otPlatRadioReceiveDone(ins, &s_receive_frame[s_recv_queue.head].frame, OT_ERROR_NONE);
}
esp_ieee802154_receive_handle_done(s_receive_frame[s_recv_queue.head].frame.mPsdu - 1);
s_receive_frame[s_recv_queue.head].frame.mPsdu = NULL;
s_recv_queue.head = (s_recv_queue.head + 1) % CONFIG_IEEE802154_RX_BUFFER_SIZE;
atomic_fetch_sub(&s_recv_queue.used, 1);
}
@@ -278,17 +425,20 @@ void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64)
void otPlatRadioSetPanId(otInstance *aInstance, uint16_t panid)
{
esp_ieee802154_set_panid(panid);
esp_ieee802154_multipan_index_t idx = (esp_ieee802154_multipan_index_t)esp_openthread_get_idx_from_instance(aInstance);
esp_ieee802154_set_multipan_panid(idx, panid);
}
void otPlatRadioSetExtendedAddress(otInstance *aInstance, const otExtAddress *aAddress)
{
esp_ieee802154_set_extended_address(aAddress->m8);
esp_ieee802154_multipan_index_t idx = (esp_ieee802154_multipan_index_t)esp_openthread_get_idx_from_instance(aInstance);
esp_ieee802154_set_multipan_extended_address(idx, aAddress->m8);
}
void otPlatRadioSetShortAddress(otInstance *aInstance, uint16_t aAddress)
{
esp_ieee802154_set_short_address(aAddress);
esp_ieee802154_multipan_index_t idx = (esp_ieee802154_multipan_index_t)esp_openthread_get_idx_from_instance(aInstance);
esp_ieee802154_set_multipan_short_address(idx, aAddress);
}
void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable)
@@ -317,60 +467,47 @@ otError otPlatRadioDisable(otInstance *aInstance)
otError otPlatRadioSleep(otInstance *aInstance)
{
esp_ieee802154_sleep();
int8_t idx = esp_openthread_get_idx_from_instance(aInstance);
esp_ieee802154_multipan_sleep(idx);
return OT_ERROR_NONE;
}
otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel)
{
int8_t idx = esp_openthread_get_idx_from_instance(aInstance);
esp_ieee802154_set_channel(aChannel);
esp_ieee802154_receive();
esp_ieee802154_multipan_receive(idx);
return OT_ERROR_NONE;
}
otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aFrame)
{
esp_ieee802154_set_channel(aFrame->mChannel);
int8_t idx = esp_openthread_get_idx_from_instance(aInstance);
aFrame->mPsdu[-1] = aFrame->mLength; // length locates one byte before the psdu (esp_openthread_radio_tx_psdu);
// One in-flight + pending entries share this queue; under normal OT scheduling
// there is at most one outstanding TX per instance.
assert(s_tx_queue_info.used < OT_INSTANCE_COUNT);
if (otMacFrameIsSecurityEnabled(aFrame) && !aFrame->mInfo.mTxInfo.mIsSecurityProcessed) {
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
if (!s_transmit_frame.mInfo.mTxInfo.mIsARetx || s_csl_period > 0) {
#else
if (!s_transmit_frame.mInfo.mTxInfo.mIsARetx) {
#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
otMacFrameSetFrameCounter(aFrame, s_mac_frame_counter++);
}
if (otMacFrameIsKeyIdMode1(aFrame)) {
s_transmit_frame.mInfo.mTxInfo.mAesKey = &s_current_key;
if (!s_transmit_frame.mInfo.mTxInfo.mIsARetx) {
otMacFrameSetKeyId(aFrame, s_key_id);
}
esp_ieee802154_get_extended_address(s_security_addr);
}
ot_set_security_key_from_key_material(s_current_key);
esp_ieee802154_set_transmit_security(&aFrame->mPsdu[-1], s_security_key, s_security_addr);
bool was_idle = (s_tx_queue_info.used == 0);
esp_openthread_radio_transmit_context_t *packet = &s_tx_queue[s_tx_queue_info.tail];
packet->aFrame = aFrame;
packet->index = idx;
s_tx_queue_info.tail = (s_tx_queue_info.tail + 1) % OT_INSTANCE_COUNT;
s_tx_queue_info.used++;
if (was_idle) {
radio_start_transmit(aInstance, aFrame, idx);
}
if (aFrame->mInfo.mTxInfo.mTxDelay != 0) {
esp_ieee802154_transmit_at(&aFrame->mPsdu[-1], aFrame->mInfo.mTxInfo.mCsmaCaEnabled,
(aFrame->mInfo.mTxInfo.mTxDelayBaseTime + aFrame->mInfo.mTxInfo.mTxDelay));
} else
{
esp_ieee802154_transmit(&aFrame->mPsdu[-1], aFrame->mInfo.mTxInfo.mCsmaCaEnabled);
}
otPlatRadioTxStarted(aInstance, aFrame);
return OT_ERROR_NONE;
}
otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance)
{
return &s_transmit_frame;
int8_t idx = esp_openthread_get_idx_from_instance(aInstance);
return &s_transmit_frame[idx];
}
int8_t otPlatRadioGetRssi(otInstance *aInstance)
@@ -391,6 +528,7 @@ otRadioCaps otPlatRadioGetCaps(otInstance *aInstance)
otError otPlatRadioReceiveAt(otInstance *aInstance, uint8_t aChannel, uint32_t aStart, uint32_t aDuration)
{
// Multipan RCP does not support receive_at yet.
esp_ieee802154_set_channel(aChannel);
esp_ieee802154_receive_at(aStart, aDuration);
return OT_ERROR_NONE;
@@ -412,40 +550,47 @@ void otPlatRadioEnableSrcMatch(otInstance *aInstance, bool aEnable)
otError otPlatRadioAddSrcMatchShortEntry(otInstance *aInstance, uint16_t aShortAddress)
{
esp_ieee802154_add_pending_addr((uint8_t *)&aShortAddress, true);
int8_t idx = esp_openthread_get_idx_from_instance(aInstance);
esp_ieee802154_multipan_add_pending_addr(idx, (uint8_t *)&aShortAddress, true);
return OT_ERROR_NONE;
}
otError otPlatRadioAddSrcMatchExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress)
{
esp_ieee802154_add_pending_addr(aExtAddress->m8, false);
int8_t idx = esp_openthread_get_idx_from_instance(aInstance);
esp_ieee802154_multipan_add_pending_addr(idx, aExtAddress->m8, false);
return OT_ERROR_NONE;
}
otError otPlatRadioClearSrcMatchShortEntry(otInstance *aInstance, uint16_t aShortAddress)
{
esp_ieee802154_clear_pending_addr((uint8_t *)&aShortAddress, true);
int8_t idx = esp_openthread_get_idx_from_instance(aInstance);
esp_ieee802154_multipan_clear_pending_addr(idx, (uint8_t *)&aShortAddress, true);
return OT_ERROR_NONE;
}
otError otPlatRadioClearSrcMatchExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress)
{
esp_ieee802154_clear_pending_addr(aExtAddress->m8, false);
int8_t idx = esp_openthread_get_idx_from_instance(aInstance);
esp_ieee802154_multipan_clear_pending_addr(idx, aExtAddress->m8, false);
return OT_ERROR_NONE;
}
void otPlatRadioClearSrcMatchShortEntries(otInstance *aInstance)
{
esp_ieee802154_reset_pending_table(true);
int8_t idx = esp_openthread_get_idx_from_instance(aInstance);
esp_ieee802154_multipan_reset_pending_table(idx, true);
}
void otPlatRadioClearSrcMatchExtEntries(otInstance *aInstance)
{
esp_ieee802154_reset_pending_table(false);
int8_t idx = esp_openthread_get_idx_from_instance(aInstance);
esp_ieee802154_multipan_reset_pending_table(idx, false);
}
otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint16_t aScanDuration)
{
// TZ-2587
esp_ieee802154_set_channel(aScanChannel);
esp_ieee802154_energy_detect(aScanDuration * US_PER_MS / US_PER_SYMBOL);
@@ -532,7 +677,6 @@ void otPlatDiagAlarmCallback(otInstance *aInstance)
void otPlatRadioSetMacKey(otInstance *aInstance, uint8_t aKeyIdMode, uint8_t aKeyId, const otMacKeyMaterial *aPrevKey,
const otMacKeyMaterial *aCurrKey, const otMacKeyMaterial *aNextKey, otRadioKeyType aKeyType)
{
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aKeyIdMode);
#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
assert(aKeyType == OT_KEY_TYPE_KEY_REF);
@@ -541,33 +685,33 @@ void otPlatRadioSetMacKey(otInstance *aInstance, uint8_t aKeyIdMode, uint8_t aKe
#endif // OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
assert(aPrevKey != NULL && aCurrKey != NULL && aNextKey != NULL);
s_key_id = aKeyId;
s_previous_key = *aPrevKey;
s_current_key = *aCurrKey;
s_next_key = *aNextKey;
int8_t idx = esp_openthread_get_idx_from_instance(aInstance);
s_sec_ctx[idx].key_id = aKeyId;
s_sec_ctx[idx].previous_key = *aPrevKey;
s_sec_ctx[idx].current_key = *aCurrKey;
s_sec_ctx[idx].next_key = *aNextKey;
#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
/* Pre-export raw key bytes in task context to avoid calling psa_export_key()
* from ISR context (enh_ack_generator), which would attempt to take a mutex. */
size_t keyLength = 0;
psa_export_key(aPrevKey->mKeyMaterial.mKeyRef, s_pervious_key_bytes, 16, &keyLength);
psa_export_key(aCurrKey->mKeyMaterial.mKeyRef, s_current_key_bytes, 16, &keyLength);
psa_export_key(aNextKey->mKeyMaterial.mKeyRef, s_next_key_bytes, 16, &keyLength);
psa_export_key(aPrevKey->mKeyMaterial.mKeyRef, s_sec_ctx[idx].previous_key_bytes, 16, &keyLength);
psa_export_key(aCurrKey->mKeyMaterial.mKeyRef, s_sec_ctx[idx].current_key_bytes, 16, &keyLength);
psa_export_key(aNextKey->mKeyMaterial.mKeyRef, s_sec_ctx[idx].next_key_bytes, 16, &keyLength);
#endif
}
void otPlatRadioSetMacFrameCounter(otInstance *aInstance, uint32_t aMacFrameCounter)
{
OT_UNUSED_VARIABLE(aInstance);
s_mac_frame_counter = aMacFrameCounter;
int8_t idx = esp_openthread_get_idx_from_instance(aInstance);
s_sec_ctx[idx].mac_frame_counter = aMacFrameCounter;
}
void otPlatRadioSetMacFrameCounterIfLarger(otInstance *aInstance, uint32_t aMacFrameCounter)
{
OT_UNUSED_VARIABLE(aInstance);
if (aMacFrameCounter > s_mac_frame_counter) {
s_mac_frame_counter = aMacFrameCounter;
int8_t idx = esp_openthread_get_idx_from_instance(aInstance);
if (aMacFrameCounter > s_sec_ctx[idx].mac_frame_counter) {
s_sec_ctx[idx].mac_frame_counter = aMacFrameCounter;
}
}
#endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
@@ -635,9 +779,11 @@ uint8_t otPlatRadioGetCslUncertainty(otInstance *aInstance)
// events
static void IRAM_ATTR ot_radio_transmit_done(const uint8_t *frame, const uint8_t *ack,
esp_ieee802154_frame_info_t *ack_frame_info)
esp_ieee802154_frame_info_t *ack_frame_info)
{
ETS_ASSERT(frame == (uint8_t *)&s_transmit_psdu);
int8_t tx_idx = s_tx_queue[s_tx_queue_info.head].index;
ETS_ASSERT(tx_idx >= 0 && tx_idx < OT_INSTANCE_COUNT);
ETS_ASSERT(frame == (uint8_t *)&s_transmit_psdu[tx_idx]);
if (ack != NULL) {
s_ack_frame.mLength = (uint16_t)(*ack);
@@ -663,40 +809,48 @@ static void IRAM_ATTR convert_to_ot_frame(uint8_t *data, esp_ieee802154_frame_in
radio_frame->mInfo.mRxInfo.mTimestamp = frame_info->timestamp;
}
static esp_err_t IRAM_ATTR enh_ack_set_security_addr_and_key(otRadioFrame *ack_frame)
static esp_err_t IRAM_ATTR enh_ack_set_security_addr_and_key(otRadioFrame *ack_frame, int8_t idx)
{
struct otMacKeyMaterial *key = NULL;
uint8_t key_id;
ETS_ASSERT(otMacFrameIsSecurityEnabled(ack_frame));
if (idx < 0 || idx >= OT_INSTANCE_COUNT) {
return ESP_FAIL;
}
key_id = otMacFrameGetKeyId(ack_frame);
if(!(otMacFrameIsKeyIdMode1(ack_frame) && key_id != 0)) {
return ESP_FAIL;
}
if (key_id == s_key_id) {
key = &s_current_key;
} else if (key_id == s_key_id - 1) {
key = &s_previous_key;
} else if (key_id == s_key_id + 1) {
key = &s_next_key;
if (key_id == s_sec_ctx[idx].key_id) {
key = &s_sec_ctx[idx].current_key;
} else if (key_id == s_sec_ctx[idx].key_id - 1) {
key = &s_sec_ctx[idx].previous_key;
} else if (key_id == s_sec_ctx[idx].key_id + 1) {
key = &s_sec_ctx[idx].next_key;
} else {
return ESP_FAIL;
}
s_ack_frame_counter = s_mac_frame_counter;
s_ack_key_id = key_id;
s_with_security_enh_ack = true;
s_sec_ctx[idx].ack_frame_counter = s_sec_ctx[idx].mac_frame_counter;
s_sec_ctx[idx].ack_key_id = key_id;
s_sec_ctx[idx].with_security_enh_ack = true;
if (otMacFrameIsKeyIdMode1(ack_frame)) {
esp_ieee802154_get_extended_address(s_security_addr);
ot_set_security_key_from_key_material(*key);
#if CONFIG_IEEE802154_MULTI_PAN_ENABLE
esp_ieee802154_get_multipan_extended_address((esp_ieee802154_multipan_index_t)idx, s_sec_ctx[idx].security_addr);
#else
esp_ieee802154_get_extended_address(s_sec_ctx[idx].security_addr);
#endif
ot_set_security_key_from_key_material(idx, *key);
}
esp_ieee802154_set_transmit_security(&ack_frame->mPsdu[-1], s_security_key, s_security_addr);
esp_ieee802154_set_transmit_security(&ack_frame->mPsdu[-1], s_sec_ctx[idx].security_key,
s_sec_ctx[idx].security_addr);
return ESP_OK;
}
static esp_err_t IRAM_ATTR ot_radio_enh_ack_generator(uint8_t *frame, esp_ieee802154_frame_info_t *frame_info,
uint8_t *enhack_frame)
uint8_t *enhack_frame)
{
otRadioFrame ack_frame;
otRadioFrame ot_frame;
@@ -735,8 +889,12 @@ static esp_err_t IRAM_ATTR ot_radio_enh_ack_generator(uint8_t *frame, esp_ieee80
s_enhack = enhack_frame;
if (otMacFrameIsSecurityEnabled(&ack_frame) && !ack_frame.mInfo.mTxInfo.mIsSecurityProcessed) {
otMacFrameSetFrameCounter(&ack_frame, s_mac_frame_counter++);
if (enh_ack_set_security_addr_and_key(&ack_frame) != ESP_OK) {
int8_t ack_idx = (int8_t)frame_info->mpf_index;
if (ack_idx < 0 || ack_idx >= OT_INSTANCE_COUNT) {
return ESP_FAIL;
}
otMacFrameSetFrameCounter(&ack_frame, s_sec_ctx[ack_idx].mac_frame_counter++);
if (enh_ack_set_security_addr_and_key(&ack_frame, ack_idx) != ESP_OK) {
return ESP_FAIL;
}
}
@@ -747,6 +905,8 @@ static void IRAM_ATTR ot_radio_receive_done(uint8_t *data, esp_ieee802154_frame_
{
otRadioFrame ot_frame;
ot_frame.mPsdu = data + 1;
int8_t rx_idx = (int8_t)frame_info->mpf_index;
assert((rx_idx >= 0 && rx_idx < OT_INSTANCE_COUNT) || rx_idx == ESP_IEEE802154_MULTIPAN_MAX);
if (atomic_load(&s_recv_queue.used) == CONFIG_IEEE802154_RX_BUFFER_SIZE) {
ESP_EARLY_LOGE(OT_PLAT_LOG_TAG, "radio receive buffer full!");
@@ -754,17 +914,20 @@ static void IRAM_ATTR ot_radio_receive_done(uint8_t *data, esp_ieee802154_frame_
return;
}
convert_to_ot_frame(data, frame_info, &(s_receive_frame[s_recv_queue.tail]));
convert_to_ot_frame(data, frame_info, &(s_receive_frame[s_recv_queue.tail].frame));
s_receive_frame[s_recv_queue.tail].index = rx_idx;
#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
// Inform if this frame was acknowledged with secured Enh-ACK.
if (otMacFrameIsAckRequested(&ot_frame) && otMacFrameIsVersion2015(&ot_frame)) {
s_receive_frame[s_recv_queue.tail].mInfo.mRxInfo.mAckedWithSecEnhAck = s_with_security_enh_ack;
s_receive_frame[s_recv_queue.tail].mInfo.mRxInfo.mAckFrameCounter = s_ack_frame_counter;
s_receive_frame[s_recv_queue.tail].mInfo.mRxInfo.mAckKeyId = s_ack_key_id;
} else {
s_receive_frame[s_recv_queue.tail].mInfo.mRxInfo.mAckedWithSecEnhAck = false;
// Default to false for broadcast/unmatched frames or non-2015/non-AR data frames.
s_receive_frame[s_recv_queue.tail].frame.mInfo.mRxInfo.mAckedWithSecEnhAck = false;
if (rx_idx >= 0 && rx_idx < OT_INSTANCE_COUNT &&
otMacFrameIsAckRequested(&ot_frame) && otMacFrameIsVersion2015(&ot_frame)) {
s_receive_frame[s_recv_queue.tail].frame.mInfo.mRxInfo.mAckedWithSecEnhAck = s_sec_ctx[rx_idx].with_security_enh_ack;
s_receive_frame[s_recv_queue.tail].frame.mInfo.mRxInfo.mAckFrameCounter = s_sec_ctx[rx_idx].ack_frame_counter;
s_receive_frame[s_recv_queue.tail].frame.mInfo.mRxInfo.mAckKeyId = s_sec_ctx[rx_idx].ack_key_id;
}
if (rx_idx >= 0 && rx_idx < OT_INSTANCE_COUNT) {
s_sec_ctx[rx_idx].with_security_enh_ack = false;
}
s_with_security_enh_ack = false;
#endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
s_recv_queue.tail = (s_recv_queue.tail + 1) % CONFIG_IEEE802154_RX_BUFFER_SIZE;
atomic_fetch_add(&s_recv_queue.used, 1);
@@ -773,7 +936,9 @@ static void IRAM_ATTR ot_radio_receive_done(uint8_t *data, esp_ieee802154_frame_
static void IRAM_ATTR ot_radio_transmit_failed(const uint8_t *frame, esp_ieee802154_tx_error_t error)
{
ETS_ASSERT(frame == (uint8_t *)&s_transmit_psdu);
int8_t tx_idx = s_tx_queue[s_tx_queue_info.head].index;
ETS_ASSERT(tx_idx >= 0 && tx_idx < OT_INSTANCE_COUNT);
ETS_ASSERT(frame == (uint8_t *)&s_transmit_psdu[tx_idx]);
s_tx_error = error;
@@ -786,7 +951,16 @@ static void IRAM_ATTR ot_radio_receive_sfd_done(void)
static void IRAM_ATTR ot_radio_transmit_sfd_done(uint8_t *frame)
{
assert(frame == (uint8_t *)&s_transmit_psdu || frame == s_enhack);
int8_t idx = -1;
if (frame != s_enhack) {
idx = s_tx_queue[s_tx_queue_info.head].index;
assert(idx >= 0 && idx < OT_INSTANCE_COUNT);
assert(frame == (uint8_t *)&s_transmit_psdu[idx]);
}
#if !OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
OT_UNUSED_VARIABLE(idx);
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
if (s_csl_period > 0) {
@@ -799,12 +973,12 @@ static void IRAM_ATTR ot_radio_transmit_sfd_done(uint8_t *frame)
#endif
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
if (frame == (uint8_t *)&s_transmit_psdu && s_transmit_frame.mInfo.mTxInfo.mIeInfo->mTimeIeOffset != 0)
if ((idx >= 0) && s_transmit_frame[idx].mInfo.mTxInfo.mIeInfo->mTimeIeOffset != 0)
{
uint8_t *p_time_ie = s_transmit_frame.mPsdu + s_transmit_frame.mInfo.mTxInfo.mIeInfo->mTimeIeOffset;
uint64_t time = (uint64_t)((int64_t)otPlatTimeGet() + s_transmit_frame.mInfo.mTxInfo.mIeInfo->mNetworkTimeOffset);
uint8_t *p_time_ie = s_transmit_frame[idx].mPsdu + s_transmit_frame[idx].mInfo.mTxInfo.mIeInfo->mTimeIeOffset;
uint64_t time = (uint64_t)((int64_t)otPlatTimeGet() + s_transmit_frame[idx].mInfo.mTxInfo.mIeInfo->mNetworkTimeOffset);
*p_time_ie = s_transmit_frame.mInfo.mTxInfo.mIeInfo->mTimeSyncSeq;
*p_time_ie = s_transmit_frame[idx].mInfo.mTxInfo.mIeInfo->mTimeSyncSeq;
*(++p_time_ie) = (uint8_t)(time & 0xff);
for (uint8_t i = 1; i < sizeof(uint64_t); i++)
@@ -842,8 +1016,8 @@ otError otPlatRadioSetChannelMaxTransmitPower(otInstance *aInstance, uint8_t aCh
#if CONFIG_OPENTHREAD_RX_ON_WHEN_IDLE
void otPlatRadioSetRxOnWhenIdle(otInstance *aInstance, bool aEnable)
{
OT_UNUSED_VARIABLE(aInstance);
esp_ieee802154_set_rx_when_idle(aEnable);
int8_t idx = esp_openthread_get_idx_from_instance(aInstance);
esp_ieee802154_multipan_set_rx_when_idle(idx, aEnable);
}
#endif
@@ -875,3 +1049,16 @@ void esp_ieee802154_receive_at_done(void)
{
set_event(EVENT_SLEEP);
}
otError otPlatMultipanGetActiveInstance(otInstance **aInstance)
{
*aInstance = esp_openthread_get_instance();
return OT_ERROR_NONE;
}
otError otPlatMultipanSetActiveInstance(otInstance *aInstance, bool aCompletePending)
{
OT_UNUSED_VARIABLE(aCompletePending);
esp_openthread_set_active_instance(aInstance);
return OT_ERROR_NONE;
}

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -93,6 +93,31 @@ static esp_err_t init_console_command_worker()
}
#endif // CONFIG_OPENTHREAD_RCP_SPINEL_CONSOLE
static void ncp_init_console(void)
{
#if CONFIG_OPENTHREAD_RCP_SPINEL_CONSOLE
esp_err_t err = esp_console_redirect_to_otlog();
if (err != ESP_OK) {
ESP_LOGE(NO_LOG_TAG, "Failed to redirect console to otPlatLog: %s", esp_err_to_name(err));
}
init_console_command_worker();
#endif
}
#if CONFIG_OPENTHREAD_MULTIPAN_RCP_ENABLE
extern "C" void otAppNcpInitMulti(otInstance **aInstances, uint8_t aCount)
{
#if (CONFIG_OPENTHREAD_RCP_UART || CONFIG_OPENTHREAD_RCP_USB_SERIAL_JTAG || CONFIG_OPENTHREAD_RCP_CUSTOM)
IgnoreError(otPlatUartEnable());
otNcpHdlcInitMulti(aInstances, aCount, NcpSend);
#else
#error "Multiple instances based on SPI are not supported yet"
#endif
ncp_init_console();
}
#else
extern "C" void otAppNcpInit(otInstance *aInstance)
{
#if (CONFIG_OPENTHREAD_RCP_UART || CONFIG_OPENTHREAD_RCP_USB_SERIAL_JTAG)
@@ -102,15 +127,9 @@ extern "C" void otAppNcpInit(otInstance *aInstance)
otNcpSpiInit(aInstance);
#endif
#if CONFIG_OPENTHREAD_RCP_SPINEL_CONSOLE
esp_err_t err = esp_console_redirect_to_otlog();
if (err != ESP_OK) {
ESP_LOGE(NO_LOG_TAG, "Failed to redirect console to otPlatLog: %s", esp_err_to_name(err));
}
init_console_command_worker();
#endif // CONFIG_OPENTHREAD_RCP_SPINEL_CONSOLE
ncp_init_console();
}
#endif
namespace ot {
namespace Ncp {

View File

@@ -1,9 +1,10 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
#include "common/new.hpp"
#include "ncp_hdlc.hpp"
@@ -12,6 +13,30 @@ namespace Ncp {
static OT_DEFINE_ALIGNED_VAR(sNcpRaw, sizeof(NcpHdlc), uint64_t);
#if CONFIG_OPENTHREAD_MULTIPAN_RCP_ENABLE
extern "C" void otNcpHdlcInitMulti(otInstance **aInstances, uint8_t aCount, otNcpHdlcSendCallback aSendCallback)
{
NcpHdlc *ncpHdlc = nullptr;
ot::Instance *instances[SPINEL_HEADER_IID_MAX];
OT_ASSERT(aCount < SPINEL_HEADER_IID_MAX + 1);
OT_ASSERT(aCount > 0);
OT_ASSERT(aInstances[0] != nullptr);
for (int i = 0; i < aCount; i++)
{
OT_ASSERT(aInstances[i] != nullptr);
instances[i] = static_cast<ot::Instance *>(aInstances[i]);
}
ncpHdlc = new (&sNcpRaw) NcpHdlc(instances, aCount, aSendCallback);
if (ncpHdlc == nullptr || ncpHdlc != NcpBase::GetNcpInstance())
{
OT_ASSERT(false);
}
}
#else
extern "C" void otNcpHdlcInit(otInstance *aInstance, otNcpHdlcSendCallback aSendCallback)
{
NcpHdlc *ncpHdlc = nullptr;
@@ -24,6 +49,7 @@ extern "C" void otNcpHdlcInit(otInstance *aInstance, otNcpHdlcSendCallback aSend
OT_ASSERT(false);
}
}
#endif
} // namespace Ncp
} // namespace ot

View File

@@ -60,18 +60,31 @@ set(rcp_srcs
openthread/src/ncp/ncp_base_dispatcher.cpp
openthread/src/ncp/ncp_base_radio.cpp
openthread/examples/apps/ncp/ncp.c
# ESP port
src/port/esp_openthread_alarm.c
# ESP port (logging/misc/settings stay at the original path)
src/port/esp_openthread_logging.c
src/port/esp_openthread_misc.c
src/port/esp_openthread_radio.c
src/port/esp_openthread_settings.c
# ESP sources
src/esp_openthread.cpp
src/esp_openthread_lock.c
src/esp_openthread_platform.cpp
src/esp_openthread_task_queue.c
# ESP NCP
)
# Single-instance files stay at the original src/ paths. Multipan RCP swaps in
# radio/alarm/instance under src/multiple_instances/. NCP is shared in src/ncp/.
if(CONFIG_OPENTHREAD_MULTIPAN_RCP_ENABLE)
set(OT_PORT_DIR "src/multiple_instances/port")
set(OT_INSTANCE_SRC "src/multiple_instances/esp_openthread_instance.cpp")
else()
set(OT_PORT_DIR "src/port")
set(OT_INSTANCE_SRC "src/esp_openthread_instance.cpp")
endif()
list(APPEND rcp_srcs
${OT_PORT_DIR}/esp_openthread_alarm.c
${OT_PORT_DIR}/esp_openthread_radio.c
${OT_INSTANCE_SRC}
src/ncp/esp_openthread_ncp.cpp
)
@@ -87,7 +100,9 @@ elseif(CONFIG_OPENTHREAD_RCP_SPI)
list(APPEND rcp_srcs
openthread/src/ncp/ncp_spi.cpp
src/port/esp_openthread_spi_slave.c)
if(CONFIG_OPENTHREAD_NCP_VENDOR_HOOK)
# Multipan SPI NCP is not supported; the compile-time check lives in
# src/ncp/esp_openthread_ncp.cpp.
if(CONFIG_OPENTHREAD_NCP_VENDOR_HOOK AND NOT CONFIG_OPENTHREAD_MULTIPAN_RCP_ENABLE)
list(APPEND rcp_srcs src/ncp/esp_openthread_ncp_spi.cpp)
endif()
endif()

View File

@@ -0,0 +1,3 @@
CONFIG_IEEE802154_MULTI_PAN_ENABLE=y
CONFIG_OPENTHREAD_MULTIPAN_RCP_ENABLE=y
CONFIG_OPENTHREAD_MULTIPLE_INTERFACES_COUNT=2