diff --git a/.codespellrc b/.codespellrc index 3e7c39a06b6..99b5f7ceec6 100644 --- a/.codespellrc +++ b/.codespellrc @@ -1,4 +1,4 @@ [codespell] skip = build,*.yuv,components/fatfs/src/*,alice.txt,*.rgb,components/wpa_supplicant/*,components/esp_wifi/*,*.pem,components/newlib/COPYING.* -ignore-words-list = ser,dout,rsource,fram,inout,shs,ans,aci,unstall,unstalling,hart,wheight,wel,ot,fane,assertIn,registr,oen,parms +ignore-words-list = ser,dout,rsource,fram,inout,shs,ans,aci,unstall,unstalling,hart,wheight,wel,ot,fane,assertIn,registr,oen,parms,aCount write-changes = true diff --git a/components/ieee802154/esp_ieee802154_multipan.c b/components/ieee802154/esp_ieee802154_multipan.c index 3c15f8fc2a2..5a144cbd457 100644 --- a/components/ieee802154/esp_ieee802154_multipan.c +++ b/components/ieee802154/esp_ieee802154_multipan.c @@ -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. diff --git a/components/ieee802154/include/esp_ieee802154_multipan.h b/components/ieee802154/include/esp_ieee802154_multipan.h index f49da2a4b87..58766f8585a 100644 --- a/components/ieee802154/include/esp_ieee802154_multipan.h +++ b/components/ieee802154/include/esp_ieee802154_multipan.h @@ -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 diff --git a/components/openthread/Kconfig b/components/openthread/Kconfig index 8e0d219e663..04a48aabe3b 100644 --- a/components/openthread/Kconfig +++ b/components/openthread/Kconfig @@ -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" diff --git a/components/openthread/openthread b/components/openthread/openthread index b678a4f63b6..43cc05a9bcf 160000 --- a/components/openthread/openthread +++ b/components/openthread/openthread @@ -1 +1 @@ -Subproject commit b678a4f63b6f9397d1a0fa8f31e5b8e0271a4d00 +Subproject commit 43cc05a9bcf780bd758bad53d897e2d88cf8cb75 diff --git a/components/openthread/private_include/esp_openthread_common_macro.h b/components/openthread/private_include/esp_openthread_common_macro.h index 8595bc7684b..4893e7fb3cc 100644 --- a/components/openthread/private_include/esp_openthread_common_macro.h +++ b/components/openthread/private_include/esp_openthread_common_macro.h @@ -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 diff --git a/components/openthread/private_include/esp_openthread_ncp.h b/components/openthread/private_include/esp_openthread_ncp.h index 2d529dd7704..05026cd766a 100644 --- a/components/openthread/private_include/esp_openthread_ncp.h +++ b/components/openthread/private_include/esp_openthread_ncp.h @@ -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 diff --git a/components/openthread/private_include/esp_openthread_platform.h b/components/openthread/private_include/esp_openthread_platform.h index 582599f5d7f..faa78ec1d65 100644 --- a/components/openthread/private_include/esp_openthread_platform.h +++ b/components/openthread/private_include/esp_openthread_platform.h @@ -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 diff --git a/components/openthread/private_include/openthread-core-esp32x-radio-config.h b/components/openthread/private_include/openthread-core-esp32x-radio-config.h index 3df6677e2e1..133c7b7c215 100644 --- a/components/openthread/private_include/openthread-core-esp32x-radio-config.h +++ b/components/openthread/private_include/openthread-core-esp32x-radio-config.h @@ -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 diff --git a/components/openthread/sbom_openthread.yml b/components/openthread/sbom_openthread.yml index 6d40f289e71..52b620afc19 100644 --- a/components/openthread/sbom_openthread.yml +++ b/components/openthread/sbom_openthread.yml @@ -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 Espressif’s NAT64 implementation and hence this CVE from the upstream NAT64 implementation is not applicable. diff --git a/components/openthread/src/multiple_instances/esp_openthread_instance.cpp b/components/openthread/src/multiple_instances/esp_openthread_instance.cpp index c52daf509c7..e19e5f7b44e 100644 --- a/components/openthread/src/multiple_instances/esp_openthread_instance.cpp +++ b/components/openthread/src/multiple_instances/esp_openthread_instance.cpp @@ -5,16 +5,27 @@ */ #include "sdkconfig.h" -#include "core/instance/instance.hpp" +#include +#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(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(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; } diff --git a/components/openthread/src/multiple_instances/port/esp_openthread_alarm.c b/components/openthread/src/multiple_instances/port/esp_openthread_alarm.c index 091e557211a..82f3f4ba562 100644 --- a/components/openthread/src/multiple_instances/port/esp_openthread_alarm.c +++ b/components/openthread/src/multiple_instances/port/esp_openthread_alarm.c @@ -11,6 +11,7 @@ #include #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; } diff --git a/components/openthread/src/multiple_instances/port/esp_openthread_radio.c b/components/openthread/src/multiple_instances/port/esp_openthread_radio.c index e99ea7034df..45c38754edd 100644 --- a/components/openthread/src/multiple_instances/port/esp_openthread_radio.c +++ b/components/openthread/src/multiple_instances/port/esp_openthread_radio.c @@ -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; +} diff --git a/components/openthread/src/ncp/esp_openthread_ncp.cpp b/components/openthread/src/ncp/esp_openthread_ncp.cpp index eb0bad9c508..67f87df3eaa 100644 --- a/components/openthread/src/ncp/esp_openthread_ncp.cpp +++ b/components/openthread/src/ncp/esp_openthread_ncp.cpp @@ -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 { diff --git a/components/openthread/src/ncp/esp_openthread_ncp_hdlc.cpp b/components/openthread/src/ncp/esp_openthread_ncp_hdlc.cpp index 6b86b6d9359..db8cd395acb 100644 --- a/components/openthread/src/ncp/esp_openthread_ncp_hdlc.cpp +++ b/components/openthread/src/ncp/esp_openthread_ncp_hdlc.cpp @@ -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(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 diff --git a/components/openthread/srcs_radio.cmake b/components/openthread/srcs_radio.cmake index 0fc4756d5a2..c9fdf754991 100644 --- a/components/openthread/srcs_radio.cmake +++ b/components/openthread/srcs_radio.cmake @@ -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() diff --git a/examples/openthread/ot_rcp/sdkconfig.ci.multipan b/examples/openthread/ot_rcp/sdkconfig.ci.multipan new file mode 100644 index 00000000000..2a5ee0a278c --- /dev/null +++ b/examples/openthread/ot_rcp/sdkconfig.ci.multipan @@ -0,0 +1,3 @@ +CONFIG_IEEE802154_MULTI_PAN_ENABLE=y +CONFIG_OPENTHREAD_MULTIPAN_RCP_ENABLE=y +CONFIG_OPENTHREAD_MULTIPLE_INTERFACES_COUNT=2