diff --git a/components/esp_hal_ieee802154/esp32c5/include/hal/ieee802154_ll.h b/components/esp_hal_ieee802154/esp32c5/include/hal/ieee802154_ll.h index d867995893..4f94a5224b 100644 --- a/components/esp_hal_ieee802154/esp32c5/include/hal/ieee802154_ll.h +++ b/components/esp_hal_ieee802154/esp32c5/include/hal/ieee802154_ll.h @@ -8,8 +8,4 @@ #include "hal/ieee802154_common_ll.h" -#define IEEE802154_TXPOWER_VALUE_MAX 20 -#define IEEE802154_TXPOWER_VALUE_MIN -15 -#define IEEE802154_TXPOWER_INDEX_MIN 3 - #define IEEE802154_RSSI_COMPENSATION_VALUE 0 diff --git a/components/esp_hal_ieee802154/esp32c6/include/hal/ieee802154_ll.h b/components/esp_hal_ieee802154/esp32c6/include/hal/ieee802154_ll.h index d867995893..4f94a5224b 100644 --- a/components/esp_hal_ieee802154/esp32c6/include/hal/ieee802154_ll.h +++ b/components/esp_hal_ieee802154/esp32c6/include/hal/ieee802154_ll.h @@ -8,8 +8,4 @@ #include "hal/ieee802154_common_ll.h" -#define IEEE802154_TXPOWER_VALUE_MAX 20 -#define IEEE802154_TXPOWER_VALUE_MIN -15 -#define IEEE802154_TXPOWER_INDEX_MIN 3 - #define IEEE802154_RSSI_COMPENSATION_VALUE 0 diff --git a/components/esp_hal_ieee802154/esp32h2/include/hal/ieee802154_ll.h b/components/esp_hal_ieee802154/esp32h2/include/hal/ieee802154_ll.h index ab1ad15e2a..6fc4a17ef6 100644 --- a/components/esp_hal_ieee802154/esp32h2/include/hal/ieee802154_ll.h +++ b/components/esp_hal_ieee802154/esp32h2/include/hal/ieee802154_ll.h @@ -14,10 +14,6 @@ extern "C" { extern uint32_t bt_bb_get_rssi_comp(void); -#define IEEE802154_TXPOWER_VALUE_MAX 20 -#define IEEE802154_TXPOWER_VALUE_MIN -24 -#define IEEE802154_TXPOWER_INDEX_MIN 0 - #define IEEE802154_RSSI_COMPENSATION_VALUE bt_bb_get_rssi_comp() #ifdef __cplusplus diff --git a/components/esp_hal_ieee802154/include/hal/ieee802154_common_ll.h b/components/esp_hal_ieee802154/include/hal/ieee802154_common_ll.h index 07a419e32e..d0faf3b622 100644 --- a/components/esp_hal_ieee802154/include/hal/ieee802154_common_ll.h +++ b/components/esp_hal_ieee802154/include/hal/ieee802154_common_ll.h @@ -414,6 +414,11 @@ static inline void ieee802154_ll_set_pending_mode(bool enable) IEEE802154.conf.pending_enhance = enable; } +static inline bool ieee802154_ll_get_pending_mode(void) +{ + return IEEE802154.conf.pending_enhance; +} + FORCE_INLINE_ATTR void ieee802154_ll_set_pending_bit(bool pending) { IEEE802154.pending_cfg.pending = pending; diff --git a/components/esp_phy/lib b/components/esp_phy/lib index 3d57415af6..ac744ff2c5 160000 --- a/components/esp_phy/lib +++ b/components/esp_phy/lib @@ -1 +1 @@ -Subproject commit 3d57415af6e4c92eff2c4c3463e20a51d7340aba +Subproject commit ac744ff2c5c39c63f8cdd503d4074905647fdbb6 diff --git a/components/ieee802154/Kconfig b/components/ieee802154/Kconfig index 54e7229aff..0948408932 100644 --- a/components/ieee802154/Kconfig +++ b/components/ieee802154/Kconfig @@ -72,6 +72,16 @@ menu "IEEE 802.15.4" help Enable IEEE802154 multi-pan + config IEEE802154_INTERFACE_NUM + int "Number of IEEE802154 interfaces" + depends on IEEE802154_ENABLED + range 1 1 if !IEEE802154_MULTI_PAN_ENABLE + range 1 4 if IEEE802154_MULTI_PAN_ENABLE + default 1 if !IEEE802154_MULTI_PAN_ENABLE + default 2 if IEEE802154_MULTI_PAN_ENABLE + help + Number of IEEE802154 interfaces + config IEEE802154_TIMING_OPTIMIZATION bool "Enable throughput optimization" depends on IEEE802154_ENABLED diff --git a/components/ieee802154/driver/esp_ieee802154_ack.c b/components/ieee802154/driver/esp_ieee802154_ack.c index cb21dd72a0..ce15b44597 100644 --- a/components/ieee802154/driver/esp_ieee802154_ack.c +++ b/components/ieee802154/driver/esp_ieee802154_ack.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -15,7 +15,7 @@ #include "esp_ieee802154_types.h" #include "esp_ieee802154_util.h" -static ieee802154_pending_table_t ieee802154_pending_table; +static ieee802154_pending_table_t ieee802154_pending_table[CONFIG_IEEE802154_INTERFACE_NUM]; #define GET_MASK_ITEM_FROM_TABLE(mask, pos) (mask[(pos) / IEEE802154_PENDING_TABLE_MASK_BITS]) @@ -23,21 +23,21 @@ static ieee802154_pending_table_t ieee802154_pending_table; #define BIT_CLR(mask, pos) (GET_MASK_ITEM_FROM_TABLE(mask, pos) &= ~(1UL << (pos % IEEE802154_PENDING_TABLE_MASK_BITS))) #define BIT_IST(mask, pos) (GET_MASK_ITEM_FROM_TABLE(mask, pos) & (1UL << (pos % IEEE802154_PENDING_TABLE_MASK_BITS))) -static IRAM_ATTR bool ieee802154_addr_in_pending_table(const uint8_t *addr, bool is_short) +static IRAM_ATTR bool ieee802154_addr_in_pending_table(esp_ieee802154_multipan_index_t inf_index, const uint8_t *addr, bool is_short) { bool ret = false; if (is_short) { for (uint8_t index = 0; index < CONFIG_IEEE802154_PENDING_TABLE_SIZE; index++) { - if (BIT_IST(ieee802154_pending_table.short_addr_mask, index) && - memcmp(addr, ieee802154_pending_table.short_addr[index], IEEE802154_FRAME_SHORT_ADDR_SIZE) == 0) { + if (BIT_IST(ieee802154_pending_table[inf_index].short_addr_mask, index) && + memcmp(addr, ieee802154_pending_table[inf_index].short_addr[index], IEEE802154_FRAME_SHORT_ADDR_SIZE) == 0) { ret = true; break; } } } else { for (uint8_t index = 0; index < CONFIG_IEEE802154_PENDING_TABLE_SIZE; index++) { - if (BIT_IST(ieee802154_pending_table.ext_addr_mask, index) && - memcmp(addr, ieee802154_pending_table.ext_addr[index], IEEE802154_FRAME_EXT_ADDR_SIZE) == 0) { + if (BIT_IST(ieee802154_pending_table[inf_index].ext_addr_mask, index) && + memcmp(addr, ieee802154_pending_table[inf_index].ext_addr[index], IEEE802154_FRAME_EXT_ADDR_SIZE) == 0) { ret = true; break; } @@ -46,63 +46,63 @@ static IRAM_ATTR bool ieee802154_addr_in_pending_table(const uint8_t *addr, bool return ret; } -esp_err_t ieee802154_add_pending_addr(const uint8_t *addr, bool is_short) +esp_err_t ieee802154_add_pending_addr(esp_ieee802154_multipan_index_t inf_index, const uint8_t *addr, bool is_short) { esp_err_t ret = ESP_FAIL; int8_t first_empty_index = -1; if (is_short) { for (uint8_t index = 0; index < CONFIG_IEEE802154_PENDING_TABLE_SIZE; index++) { - if (!BIT_IST(ieee802154_pending_table.short_addr_mask, index)) { + if (!BIT_IST(ieee802154_pending_table[inf_index].short_addr_mask, index)) { // record the first empty index first_empty_index = (first_empty_index == -1 ? index : first_empty_index); - } else if (memcmp(addr, ieee802154_pending_table.short_addr[index], IEEE802154_FRAME_SHORT_ADDR_SIZE) == 0) { + } else if (memcmp(addr, ieee802154_pending_table[inf_index].short_addr[index], IEEE802154_FRAME_SHORT_ADDR_SIZE) == 0) { // The address is in the table already. ret = ESP_OK; return ret; } } if (first_empty_index != -1) { - memcpy(ieee802154_pending_table.short_addr[first_empty_index], addr, IEEE802154_FRAME_SHORT_ADDR_SIZE); - BIT_SET(ieee802154_pending_table.short_addr_mask, first_empty_index); + memcpy(ieee802154_pending_table[inf_index].short_addr[first_empty_index], addr, IEEE802154_FRAME_SHORT_ADDR_SIZE); + BIT_SET(ieee802154_pending_table[inf_index].short_addr_mask, first_empty_index); ret = ESP_OK; } } else { for (uint8_t index = 0; index < CONFIG_IEEE802154_PENDING_TABLE_SIZE; index++) { - if (!BIT_IST(ieee802154_pending_table.ext_addr_mask, index)) { + if (!BIT_IST(ieee802154_pending_table[inf_index].ext_addr_mask, index)) { first_empty_index = (first_empty_index == -1 ? index : first_empty_index); - } else if (memcmp(addr, ieee802154_pending_table.ext_addr[index], IEEE802154_FRAME_EXT_ADDR_SIZE) == 0) { + } else if (memcmp(addr, ieee802154_pending_table[inf_index].ext_addr[index], IEEE802154_FRAME_EXT_ADDR_SIZE) == 0) { // The address is already in the pending table. ret = ESP_OK; return ret; } } if (first_empty_index != -1) { - memcpy(ieee802154_pending_table.ext_addr[first_empty_index], addr, IEEE802154_FRAME_EXT_ADDR_SIZE); - BIT_SET(ieee802154_pending_table.ext_addr_mask, first_empty_index); + memcpy(ieee802154_pending_table[inf_index].ext_addr[first_empty_index], addr, IEEE802154_FRAME_EXT_ADDR_SIZE); + BIT_SET(ieee802154_pending_table[inf_index].ext_addr_mask, first_empty_index); ret = ESP_OK; } } return ret; } -esp_err_t ieee802154_clear_pending_addr(const uint8_t *addr, bool is_short) +esp_err_t ieee802154_clear_pending_addr(esp_ieee802154_multipan_index_t inf_index, const uint8_t *addr, bool is_short) { esp_err_t ret = ESP_FAIL; // Consider this function may be called in ISR, only clear the mask bits for finishing the process quickly. if (is_short) { for (uint8_t index = 0; index < CONFIG_IEEE802154_PENDING_TABLE_SIZE; index++) { - if (BIT_IST(ieee802154_pending_table.short_addr_mask, index) && - memcmp(addr, ieee802154_pending_table.short_addr[index], IEEE802154_FRAME_SHORT_ADDR_SIZE) == 0) { - BIT_CLR(ieee802154_pending_table.short_addr_mask, index); + if (BIT_IST(ieee802154_pending_table[inf_index].short_addr_mask, index) && + memcmp(addr, ieee802154_pending_table[inf_index].short_addr[index], IEEE802154_FRAME_SHORT_ADDR_SIZE) == 0) { + BIT_CLR(ieee802154_pending_table[inf_index].short_addr_mask, index); ret = ESP_OK; break; } } } else { for (uint8_t index = 0; index < CONFIG_IEEE802154_PENDING_TABLE_SIZE; index++) { - if (BIT_IST(ieee802154_pending_table.ext_addr_mask, index) && - memcmp(addr, ieee802154_pending_table.ext_addr[index], IEEE802154_FRAME_EXT_ADDR_SIZE) == 0) { - BIT_CLR(ieee802154_pending_table.ext_addr_mask, index); + if (BIT_IST(ieee802154_pending_table[inf_index].ext_addr_mask, index) && + memcmp(addr, ieee802154_pending_table[inf_index].ext_addr[index], IEEE802154_FRAME_EXT_ADDR_SIZE) == 0) { + BIT_CLR(ieee802154_pending_table[inf_index].ext_addr_mask, index); ret = ESP_OK; break; } @@ -112,26 +112,42 @@ esp_err_t ieee802154_clear_pending_addr(const uint8_t *addr, bool is_short) return ret; } -void ieee802154_reset_pending_table(bool is_short) +void ieee802154_reset_pending_table(esp_ieee802154_multipan_index_t inf_index, bool is_short) { // Consider this function may be called in ISR, only clear the mask bits for finishing the process quickly. if (is_short) { - memset(ieee802154_pending_table.short_addr_mask, 0, IEEE802154_PENDING_TABLE_MASK_SIZE); + memset(ieee802154_pending_table[inf_index].short_addr_mask, 0, IEEE802154_PENDING_TABLE_MASK_SIZE); } else { - memset(ieee802154_pending_table.ext_addr_mask, 0, IEEE802154_PENDING_TABLE_MASK_SIZE); + memset(ieee802154_pending_table[inf_index].ext_addr_mask, 0, IEEE802154_PENDING_TABLE_MASK_SIZE); } } -bool ieee802154_ack_config_pending_bit(const uint8_t *frame) +bool ieee802154_ack_config_pending_bit(const uint8_t *frame, const esp_ieee802154_frame_info_t *frame_info) { + esp_ieee802154_multipan_index_t inf_index = 0; +#if CONFIG_IEEE802154_MULTI_PAN_ENABLE + inf_index = frame_info->mpf_index; + if (inf_index >= ESP_IEEE802154_MULTIPAN_MAX || inf_index < ESP_IEEE802154_MULTIPAN_0) { + return false; + } +#endif bool pending_bit = false; uint8_t addr[IEEE802154_FRAME_EXT_ADDR_SIZE] = {0}; uint8_t src_mode = 0; // Only set the HW pending bit for the frames with version 0b00 or 0b01. bool set_to_hw = (ieee802154_frame_get_version(frame) <= IEEE802154_FRAME_VERSION_1); - ieee802154_ll_pending_mode_t pending_mode = ieee802154_pib_get_pending_mode(); + // Check whether the frame is a Data Request. + if (ieee802154_ll_get_pending_mode()) { + pending_bit = ieee802154_is_data_request(frame); + } + ieee802154_ll_pending_mode_t pending_mode = ieee802154_pib_get_pending_mode(inf_index); + + src_mode = ieee802154_frame_get_src_addr(frame, addr); + if (src_mode == IEEE802154_FRAME_INVALID_VALUE) { + goto exit; + } switch (pending_mode) { case IEEE802154_AUTO_PENDING_DISABLE: // HW will check whether the frame is data request or not @@ -139,19 +155,14 @@ bool ieee802154_ack_config_pending_bit(const uint8_t *frame) break; case IEEE802154_AUTO_PENDING_ENABLE: case IEEE802154_AUTO_PENDING_ENHANCED: - src_mode = ieee802154_frame_get_src_addr(frame, addr); - if (src_mode == IEEE802154_FRAME_SRC_MODE_SHORT || src_mode == IEEE802154_FRAME_SRC_MODE_EXT) { - if (ieee802154_addr_in_pending_table(addr, src_mode == IEEE802154_FRAME_SRC_MODE_SHORT)) { - pending_bit = true; - } + pending_bit = ieee802154_addr_in_pending_table(inf_index, addr, src_mode == IEEE802154_FRAME_SRC_MODE_SHORT); } break; case IEEE802154_AUTO_PENDING_ZIGBEE: // If the address type is short and in pending table, set 'pending_bit' false, otherwise set true. - src_mode = ieee802154_frame_get_src_addr(frame, addr); pending_bit = true; - if (src_mode == IEEE802154_FRAME_SRC_MODE_SHORT && ieee802154_addr_in_pending_table(addr, src_mode == IEEE802154_FRAME_SRC_MODE_SHORT)) { + if (src_mode == IEEE802154_FRAME_SRC_MODE_SHORT && ieee802154_addr_in_pending_table(inf_index, addr, src_mode == IEEE802154_FRAME_SRC_MODE_SHORT)) { pending_bit = false; } break; @@ -159,6 +170,7 @@ bool ieee802154_ack_config_pending_bit(const uint8_t *frame) IEEE802154_ASSERT(false); } +exit: if (set_to_hw) { ieee802154_ll_set_pending_bit(pending_bit); } diff --git a/components/ieee802154/driver/esp_ieee802154_debug.c b/components/ieee802154/driver/esp_ieee802154_debug.c index 8aa30f26cc..5979c0483f 100644 --- a/components/ieee802154/driver/esp_ieee802154_debug.c +++ b/components/ieee802154/driver/esp_ieee802154_debug.c @@ -77,6 +77,16 @@ static char *ieee802154_state_string[] = { "ED", "CCA", }; + +static char *ieee802154_state_to_string(ieee802154_state_t state) +{ + char *state_string = "INVALID"; + if (state < sizeof(ieee802154_state_string) / sizeof(ieee802154_state_string[0])) { + state_string = ieee802154_state_string[state]; + } + return state_string; +} + #endif // CONFIG_IEEE802154_RECORD_STATE #if CONFIG_IEEE802154_RECORD_CMD @@ -126,7 +136,7 @@ static char *ieee802154_get_cmd_string(ieee802154_ll_cmd_t cmd) #endif // CONFIG_IEEE802154_RECORD_CMD #if CONFIG_IEEE802154_RECORD_EVENT || CONFIG_IEEE802154_RECORD_ABORT -static char *ieee80154_rx_abort_reason_string[] = { +static char *ieee802154_rx_abort_reason_string[] = { "RSVD", // = 0, "RX_STOP", // = 1, "SFD_TIMEOUT", // = 2, @@ -148,7 +158,7 @@ static char *ieee80154_rx_abort_reason_string[] = { "ED_COEX_REJECT", // = 26, }; -static char *ieee80154_tx_abort_reason_string[] = { +static char *ieee802154_tx_abort_reason_string[] = { "RSVD", // = 0, "RX_ACK_STOP", // = 1, "RX_ACK_SFD_TIMEOUT", // = 2, @@ -169,17 +179,47 @@ static char *ieee80154_tx_abort_reason_string[] = { "CCA_BUSY", // = 25, }; +static char *ieee802154_abort_reason_to_string(ieee802154_ll_rx_abort_reason_t reason, bool is_tx_abort) +{ + char *abort_reason_string = "UNKNOWN"; + if (is_tx_abort) { + if (reason < sizeof(ieee802154_tx_abort_reason_string) / sizeof(ieee802154_tx_abort_reason_string[0])) { + abort_reason_string = ieee802154_tx_abort_reason_string[reason]; + } + } else { + if (reason < sizeof(ieee802154_rx_abort_reason_string) / sizeof(ieee802154_rx_abort_reason_string[0])) { + abort_reason_string = ieee802154_rx_abort_reason_string[reason]; + } + } + return abort_reason_string; +} + #endif // CONFIG_IEEE802154_RECORD_EVENT #if CONFIG_IEEE802154_RECORD #if CONFIG_IEEE802154_RECORD_TXRX_FRAME static void ieee802154_dump_frame_print(const uint8_t *frame) { + if (frame == NULL) { + ESP_EARLY_LOGW(IEEE802154_TAG, "Invalid frame, frame is NULL"); + return; + } + if (frame[0] < IEEE802154_FRAME_MIN_LEN) { + ESP_EARLY_LOGW(IEEE802154_TAG, "Invalid frame, frame length is %d", frame[0]); + return; + } for (uint8_t i = 1; i < frame[0]; i+=8) { + uint8_t end = (i + 7 < frame[0]) ? i + 7 : frame[0] - 1; ESP_EARLY_LOGW(IEEE802154_TAG, "frag(%03d:%03d~%03d): %02x %02x %02x %02x %02x %02x %02x %02x", - frame[0], i, i+7, - frame[i], frame[i+1], frame[i+2], frame[i+3], - frame[i+4], frame[i+5], frame[i+6], frame[i+7]); + frame[0], i, end, + frame[i], + (i+1 < frame[0]) ? frame[i+1] : 0, + (i+2 < frame[0]) ? frame[i+2] : 0, + (i+3 < frame[0]) ? frame[i+3] : 0, + (i+4 < frame[0]) ? frame[i+4] : 0, + (i+5 < frame[0]) ? frame[i+5] : 0, + (i+6 < frame[0]) ? frame[i+6] : 0, + (i+7 < frame[0]) ? frame[i+7] : 0); } } #endif // CONFIG_IEEE802154_RECORD_TXRX_FRAME @@ -193,14 +233,14 @@ void ieee802154_record_print(void) char abort_log[100] = { 0 }; snprintf(event_log, 200,"index %2d: event: 0x%4x, %15s, state:%10s, timestamp: %lld", i, g_ieee802154_probe.event[i].event, ieee802154_get_event_string(g_ieee802154_probe.event[i].event), - ieee802154_state_string[g_ieee802154_probe.event[i].state], + ieee802154_state_to_string(g_ieee802154_probe.event[i].state), g_ieee802154_probe.event[i].timestamp); if (g_ieee802154_probe.event[i].event == IEEE802154_EVENT_RX_ABORT) { snprintf(abort_log, 100, "rx abort reason: %4x, %20s", g_ieee802154_probe.event[i].abort_reason.rx, - ieee80154_rx_abort_reason_string[g_ieee802154_probe.event[i].abort_reason.rx]); + ieee802154_abort_reason_to_string(g_ieee802154_probe.event[i].abort_reason.rx, false)); } else if (g_ieee802154_probe.event[i].event == IEEE802154_EVENT_TX_ABORT) { snprintf(abort_log, 100, "tx abort reason: %4x, %20s", g_ieee802154_probe.event[i].abort_reason.tx, - ieee80154_tx_abort_reason_string[g_ieee802154_probe.event[i].abort_reason.tx]); + ieee802154_abort_reason_to_string(g_ieee802154_probe.event[i].abort_reason.tx, true)); } ESP_EARLY_LOGW(IEEE802154_TAG, "%s %s", event_log, abort_log); } @@ -212,7 +252,7 @@ void ieee802154_record_print(void) for (uint8_t i = 0; i < IEEE802154_ASSERT_RECORD_STATE_SIZE; i++) { ESP_EARLY_LOGW(IEEE802154_TAG, "index %2d: line:%5lu, state:%10s, timestamp: %lld", i, g_ieee802154_probe.state[i].line, - ieee802154_state_string[g_ieee802154_probe.state[i].state], + ieee802154_state_to_string(g_ieee802154_probe.state[i].state), g_ieee802154_probe.state[i].timestamp); } ESP_EARLY_LOGW(IEEE802154_TAG,"Print the record state done."); @@ -235,12 +275,12 @@ void ieee802154_record_print(void) if (g_ieee802154_probe.abort[i].is_tx_abort) { ESP_EARLY_LOGW(IEEE802154_TAG, "index %2d: tx abort: %4x, %15s, timestamp: %lld", i, g_ieee802154_probe.abort[i].abort_reason.tx, - ieee80154_tx_abort_reason_string[g_ieee802154_probe.abort[i].abort_reason.tx], + ieee802154_abort_reason_to_string(g_ieee802154_probe.abort[i].abort_reason.tx, true), g_ieee802154_probe.abort[i].timestamp); } else { ESP_EARLY_LOGW(IEEE802154_TAG, "index %2d: rx abort: %4x, %15s, timestamp: %lld", i, g_ieee802154_probe.abort[i].abort_reason.rx, - ieee80154_rx_abort_reason_string[g_ieee802154_probe.abort[i].abort_reason.rx], + ieee802154_abort_reason_to_string(g_ieee802154_probe.abort[i].abort_reason.rx, false), g_ieee802154_probe.abort[i].timestamp); } } @@ -418,14 +458,19 @@ static uint64_t s_rx_buffer_used_water_level[IEEE802154_RX_BUFFER_USED_TOTAL_LEV void ieee802154_rx_buffer_statistic_is_free(bool is_free) { if (is_free) { - s_rx_buffer_used_nums--; + if (s_rx_buffer_used_nums > 0) { + s_rx_buffer_used_nums--; + } } else { s_rx_buffer_used_nums++; // (CONFIG_IEEE802154_RX_BUFFER_SIZE + 1) means buffer full. if (s_rx_buffer_used_nums > (CONFIG_IEEE802154_RX_BUFFER_SIZE + 1)) { s_rx_buffer_used_nums = CONFIG_IEEE802154_RX_BUFFER_SIZE + 1; } - s_rx_buffer_used_water_level[IEEE802154_RX_BUFFER_GET_USED_LEVEL(s_rx_buffer_used_nums)]++; + uint8_t level = IEEE802154_RX_BUFFER_GET_USED_LEVEL(s_rx_buffer_used_nums); + if (level <= IEEE802154_RX_BUFFER_USED_TOTAL_LEVEL) { + s_rx_buffer_used_water_level[level]++; + } } } @@ -437,6 +482,7 @@ void ieee802154_rx_buffer_statistic_clear(void) void ieee802154_rx_buffer_statistic_print(void) { uint64_t total_times = 0; + float percentage = 0.0f; for (uint8_t i = 0; i < (IEEE802154_RX_BUFFER_USED_TOTAL_LEVEL + 1); i++) { total_times += s_rx_buffer_used_water_level[i]; } @@ -445,9 +491,15 @@ void ieee802154_rx_buffer_statistic_print(void) ESP_LOGW(IEEE802154_TAG, "|%25s|%-25llu|", "buffer alloc times:", total_times); ESP_LOGW(IEEE802154_TAG, "+-------------------------+-------------------------+"); for (uint8_t i = 0; i < (IEEE802154_RX_BUFFER_USED_TOTAL_LEVEL); i++) { - ESP_LOGW(IEEE802154_TAG, "|%4d%%%5s%4d%%%10s|%-15llu%9.2f%%|", ((i) * 100 / IEEE802154_RX_BUFFER_USED_TOTAL_LEVEL), "~", ((i + 1) * 100 / IEEE802154_RX_BUFFER_USED_TOTAL_LEVEL), " used:", s_rx_buffer_used_water_level[i], ((float)s_rx_buffer_used_water_level[i] / (float)total_times)*100); + percentage = (total_times > 0) ? ((float)s_rx_buffer_used_water_level[i] / (float)total_times) * 100 : 0.0f; + ESP_LOGW(IEEE802154_TAG, "|%4d%%%5s%4d%%%10s|%-15llu%9.2f%%|", + ((i) * 100 / IEEE802154_RX_BUFFER_USED_TOTAL_LEVEL), "~", + ((i + 1) * 100 / IEEE802154_RX_BUFFER_USED_TOTAL_LEVEL), " used:", + s_rx_buffer_used_water_level[i], percentage); } - ESP_LOGW(IEEE802154_TAG, "|%25s|%-15llu%9.2f%%|", "full used:", s_rx_buffer_used_water_level[IEEE802154_RX_BUFFER_USED_TOTAL_LEVEL], ((float)s_rx_buffer_used_water_level[IEEE802154_RX_BUFFER_USED_TOTAL_LEVEL] / (float)total_times)*100); + percentage = (total_times > 0) ? ((float)s_rx_buffer_used_water_level[IEEE802154_RX_BUFFER_USED_TOTAL_LEVEL] / (float)total_times) * 100 : 0.0f; + ESP_LOGW(IEEE802154_TAG, "|%25s|%-15llu%9.2f%%|", "full used:", + s_rx_buffer_used_water_level[IEEE802154_RX_BUFFER_USED_TOTAL_LEVEL], percentage); ESP_LOGW(IEEE802154_TAG, "+-------------------------+-------------------------+"); } diff --git a/components/ieee802154/driver/esp_ieee802154_dev.c b/components/ieee802154/driver/esp_ieee802154_dev.c index e1cf461a66..b4abc8f507 100644 --- a/components/ieee802154/driver/esp_ieee802154_dev.c +++ b/components/ieee802154/driver/esp_ieee802154_dev.c @@ -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 */ @@ -48,7 +48,12 @@ extern uint32_t bt_bb_get_cur_rx_info(void); IEEE802154_STATIC volatile ieee802154_state_t s_ieee802154_state; static uint8_t *s_tx_frame = NULL; + +#if CONFIG_IEEE802154_TEST #define IEEE802154_RX_FRAME_SIZE (127 + 1 + 1) // +1: len, +1: for dma test +#else +#define IEEE802154_RX_FRAME_SIZE (127 + 1) // +1: len +#endif // CONFIG_IEEE802154_TEST // +1: for the stub buffer when the valid buffers are full. // @@ -144,11 +149,101 @@ static IRAM_ATTR void receive_ack_timeout_timer_start(uint32_t duration) } #endif +#if CONFIG_IEEE802154_MULTI_PAN_ENABLE +IEEE802154_STATIC IEEE802154_NOINLINE bool is_broadcast_panid(uint8_t *target_panid) +{ + if (target_panid[0] == 0xff && target_panid[1] == 0xff) { + return true; + } + return false; +} + +static IEEE802154_NOINLINE bool is_broadcast_addr(uint8_t *dest_addr, uint8_t addr_mode) +{ + uint8_t target[IEEE802154_FRAME_EXT_ADDR_SIZE] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + if (addr_mode == IEEE802154_FRAME_DST_MODE_NONE) { + return false; + } + + size_t addr_size = (addr_mode == IEEE802154_FRAME_DST_MODE_SHORT) ? IEEE802154_FRAME_SHORT_ADDR_SIZE : IEEE802154_FRAME_EXT_ADDR_SIZE; + if (memcmp(dest_addr, target, addr_size) == 0) { + return true; + } + return false; +} + +IEEE802154_STATIC IEEE802154_NOINLINE void update_mpf_index(void) +{ + uint8_t *frame = s_rx_frame[s_rx_index]; + uint8_t frame_type = ieee802154_frame_get_type(frame); + s_rx_frame_info[s_rx_index].mpf_index = ESP_IEEE802154_MULTIPAN_MAX; + bool is_target_panid_present = false; + uint8_t dest_addr_mode = IEEE802154_FRAME_DST_MODE_NONE; + uint8_t dest_addr[IEEE802154_FRAME_EXT_ADDR_SIZE] = {0}; + uint8_t target_panid[IEEE802154_FRAME_PANID_SIZE] = {0}; + + // Get dest addr and panid from the raw packet. + if (frame_type == IEEE802154_FRAME_TYPE_BEACON) { + is_target_panid_present = (ieee802154_frame_get_src_panid(frame, target_panid) == ESP_OK) ? true : false; + } else { + is_target_panid_present = (ieee802154_frame_get_dest_panid(frame, target_panid) == ESP_OK) ? true : false; + } + dest_addr_mode = ieee802154_frame_get_dst_addr(frame, dest_addr); + // Check is this packet is Broadcast + if (is_broadcast_addr(dest_addr, dest_addr_mode) || (is_target_panid_present && is_broadcast_panid(target_panid))) { + return; + } + + for (esp_ieee802154_multipan_index_t index = 0; index < CONFIG_IEEE802154_INTERFACE_NUM; index++) { + if (is_target_panid_present == true) { + uint16_t panid = target_panid[1]; + panid = (panid << 8) | target_panid[0]; + if (panid != esp_ieee802154_get_multipan_panid(index)) { + continue; + } + } + + if (dest_addr_mode == IEEE802154_FRAME_DST_MODE_SHORT) { + uint16_t short_addr = dest_addr[1]; + short_addr = (short_addr << 8) | dest_addr[0]; + if (short_addr != esp_ieee802154_get_multipan_short_address(index)) { + continue; + } else { + s_rx_frame_info[s_rx_index].mpf_index = index; + return; + } + } else if (dest_addr_mode == IEEE802154_FRAME_DST_MODE_EXT) { + uint8_t ext_addr[IEEE802154_FRAME_EXT_ADDR_SIZE] = {0}; + esp_ieee802154_get_multipan_extended_address(index, ext_addr); + if (memcmp(dest_addr, ext_addr, IEEE802154_FRAME_EXT_ADDR_SIZE) != 0) { + continue; + } else { + s_rx_frame_info[s_rx_index].mpf_index = index; + return; + } + } + } +} +#endif + static IEEE802154_NOINLINE void ieee802154_rx_frame_info_update(void) { - uint8_t len = s_rx_frame[s_rx_index][0]; - int8_t rssi = s_rx_frame[s_rx_index][len - 1]; // crc is not written to rx buffer - uint8_t lqi = s_rx_frame[s_rx_index][len]; + uint8_t len = s_rx_frame[s_rx_index][0] & 0x7f; + int8_t rssi = 0; + uint8_t lqi = 0; + + if (len < IEEE802154_FRAME_MIN_LEN) { + s_rx_frame_info[s_rx_index].channel = 0; + s_rx_frame_info[s_rx_index].rssi = 0; + s_rx_frame_info[s_rx_index].lqi = 0; + return; + } + rssi = s_rx_frame[s_rx_index][len - 1]; // crc is not written to rx buffer + lqi = s_rx_frame[s_rx_index][len]; + +#if CONFIG_IEEE802154_MULTI_PAN_ENABLE + update_mpf_index(); +#endif s_rx_frame_info[s_rx_index].channel = ieee802154_freq_to_channel(ieee802154_ll_get_freq()); s_rx_frame_info[s_rx_index].rssi = rssi + IEEE802154_RSSI_COMPENSATION_VALUE; @@ -436,12 +531,12 @@ static IRAM_ATTR void isr_handle_rx_done(void) && ieee802154_ll_get_tx_auto_ack()) { extcoex_tx_stage_start(); // auto tx ack only works for the frame with version 0b00 and 0b01 - s_rx_frame_info[s_rx_index].pending = ieee802154_ack_config_pending_bit(s_rx_frame[s_rx_index]); + s_rx_frame_info[s_rx_index].pending = ieee802154_ack_config_pending_bit(s_rx_frame[s_rx_index], &s_rx_frame_info[s_rx_index]); ieee802154_set_state(IEEE802154_STATE_TX_ACK); NEEDS_NEXT_OPT(false); } else if (ieee802154_frame_is_ack_required(s_rx_frame[s_rx_index]) && ieee802154_frame_get_version(s_rx_frame[s_rx_index]) == IEEE802154_FRAME_VERSION_2 && ieee802154_ll_get_tx_enhance_ack()) { - s_rx_frame_info[s_rx_index].pending = ieee802154_ack_config_pending_bit(s_rx_frame[s_rx_index]); + s_rx_frame_info[s_rx_index].pending = ieee802154_ack_config_pending_bit(s_rx_frame[s_rx_index], &s_rx_frame_info[s_rx_index]); // For 2015 enh-ack, SW should generate an enh-ack then send it manually if (ieee802154_inner_enh_ack_generator(s_rx_frame[s_rx_index], &s_rx_frame_info[s_rx_index], s_enh_ack_frame) == ESP_OK) { extcoex_tx_stage_start(); @@ -888,7 +983,6 @@ static inline esp_err_t ieee802154_transmit_internal(const uint8_t *frame, bool esp_err_t ieee802154_transmit(const uint8_t *frame, bool cca) { - ESP_RETURN_ON_FALSE(frame[0] <= 127, ESP_ERR_INVALID_ARG, IEEE802154_TAG, "Invalid frame length."); #if !CONFIG_IEEE802154_TEST ieee802154_enter_critical(); if ((s_ieee802154_state == IEEE802154_STATE_RX && ieee802154_ll_is_current_rx_frame()) @@ -910,32 +1004,23 @@ esp_err_t ieee802154_transmit(const uint8_t *frame, bool cca) esp_err_t ieee802154_transmit_at(const uint8_t *frame, bool cca, uint32_t time) { - ESP_RETURN_ON_FALSE(frame[0] <= 127, ESP_ERR_INVALID_ARG, IEEE802154_TAG, "Invalid frame length."); - uint32_t tx_target_time; + uint32_t rampup_time = cca ? IEEE802154_ED_TRIG_TX_RAMPUP_TIME_US : IEEE802154_TX_RAMPUP_TIME_US; + uint32_t tx_target_time = (time >= rampup_time) ? time - rampup_time : 0; IEEE802154_RF_ENABLE(); tx_init(frame); IEEE802154_SET_TXRX_PTI(IEEE802154_SCENE_TX_AT); if (cca) { ieee802154_ll_set_ed_duration(CCA_DETECTION_TIME); - tx_target_time = time - IEEE802154_ED_TRIG_TX_RAMPUP_TIME_US; - ieee802154_set_state(IEEE802154_STATE_TX_CCA); - ieee802154_enter_critical(); - ieee802154_etm_set_event_task(IEEE802154_ETM_CHANNEL0, ETM_EVENT_TIMER0_OVERFLOW, ETM_TASK_ED_TRIG_TX); - ieee802154_timer0_fire_at(tx_target_time); - ieee802154_exit_critical(); - } else { - tx_target_time = time - IEEE802154_TX_RAMPUP_TIME_US; - if (ieee802154_frame_get_type(frame) == IEEE802154_FRAME_TYPE_ACK && ieee802154_frame_get_version(frame) == IEEE802154_FRAME_VERSION_2) { - ieee802154_set_state(IEEE802154_STATE_TX_ENH_ACK); - } else { - ieee802154_set_state(IEEE802154_STATE_TX); - } - ieee802154_enter_critical(); - ieee802154_etm_set_event_task(IEEE802154_ETM_CHANNEL0, ETM_EVENT_TIMER0_OVERFLOW, ETM_TASK_TX_START); - ieee802154_timer0_fire_at(tx_target_time); - ieee802154_exit_critical(); } - + ieee802154_set_state(cca ? IEEE802154_STATE_TX_CCA : IEEE802154_STATE_TX); + ieee802154_enter_critical(); + ieee802154_etm_set_event_task(IEEE802154_ETM_CHANNEL0, ETM_EVENT_TIMER0_OVERFLOW, cca ? ETM_TASK_ED_TRIG_TX : ETM_TASK_TX_START); + ieee802154_timer0_fire_at(tx_target_time); + ieee802154_exit_critical(); + if (time < rampup_time) { + // First start the transmit at and then print some logs. + ESP_EARLY_LOGE(IEEE802154_TAG, "Time should be longer than %d us to account for the TX ramp-up", rampup_time); + } return ESP_OK; } @@ -976,6 +1061,7 @@ IEEE802154_NOINLINE static void ieee802154_start_receive_at(void* ctx) esp_err_t ieee802154_receive_at(uint32_t time, uint32_t duration) { // TODO: Light sleep current optimization, TZ-1613. + uint32_t target_time = (time >= IEEE802154_RX_RAMPUP_TIME_US) ? time - IEEE802154_RX_RAMPUP_TIME_US : 0; IEEE802154_RF_ENABLE(); ieee802154_enter_critical(); rx_init(); @@ -984,11 +1070,15 @@ esp_err_t ieee802154_receive_at(uint32_t time, uint32_t duration) ieee802154_set_state(IEEE802154_STATE_RX); ieee802154_etm_set_event_task(IEEE802154_ETM_CHANNEL1, ETM_EVENT_TIMER1_OVERFLOW, ETM_TASK_RX_START); if (duration) { - ieee802154_timer1_fire_at_with_callback(time - IEEE802154_RX_RAMPUP_TIME_US, ieee802154_start_receive_at, (void*)(time + duration)); + ieee802154_timer1_fire_at_with_callback(target_time, ieee802154_start_receive_at, (void*)(time + duration)); } else { - ieee802154_timer1_fire_at(time - IEEE802154_RX_RAMPUP_TIME_US); + ieee802154_timer1_fire_at(target_time); } ieee802154_exit_critical(); + if (time < IEEE802154_RX_RAMPUP_TIME_US) { + // First start the receive at and then print some logs. + ESP_EARLY_LOGE(IEEE802154_TAG, "Time should be longer than %d us to account for the RX ramp-up", IEEE802154_RX_RAMPUP_TIME_US); + } return ESP_OK; } diff --git a/components/ieee802154/driver/esp_ieee802154_frame.c b/components/ieee802154/driver/esp_ieee802154_frame.c index 8a970b3350..4eda910637 100644 --- a/components/ieee802154/driver/esp_ieee802154_frame.c +++ b/components/ieee802154/driver/esp_ieee802154_frame.c @@ -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 */ @@ -10,6 +10,7 @@ #include "esp_ieee802154_dev.h" #include "esp_ieee802154_frame.h" #include "esp_ieee802154_util.h" +#include "esp_rom_sys.h" bool ieee802154_frame_is_security_enabled(const uint8_t *frame) { @@ -42,14 +43,29 @@ IEEE802154_STATIC IEEE802154_INLINE bool is_panid_compression(const uint8_t *fra return frame[IEEE802154_FRAME_PANID_COMP_OFFSET] & IEEE802154_FRAME_PANID_COMP_BIT; } +IEEE802154_STATIC IEEE802154_INLINE bool is_valid_dst_mode(uint8_t dst_mode) +{ + return (dst_mode == IEEE802154_FRAME_DST_MODE_NONE || + dst_mode == IEEE802154_FRAME_DST_MODE_SHORT || + dst_mode == IEEE802154_FRAME_DST_MODE_EXT); +} + +IEEE802154_STATIC IEEE802154_INLINE bool is_valid_src_mode(uint8_t src_mode) +{ + return (src_mode == IEEE802154_FRAME_SRC_MODE_NONE || + src_mode == IEEE802154_FRAME_SRC_MODE_SHORT || + src_mode == IEEE802154_FRAME_SRC_MODE_EXT); +} + IEEE802154_STATIC IEEE802154_NOINLINE bool is_dst_panid_present(const uint8_t *frame) { uint8_t dst_mode = dst_addr_mode(frame); + uint8_t src_mode = src_addr_mode(frame); bool dst_panid_present = false; + bool panid_compression = is_panid_compression(frame); + IEEE802154_RETURN_ON_FALSE_SILENTLY(is_valid_dst_mode(dst_mode) && is_valid_src_mode(src_mode), false); if (ieee802154_frame_get_version(frame) == IEEE802154_FRAME_VERSION_2) { - uint8_t src_mode = src_addr_mode(frame); - bool panid_compression = is_panid_compression(frame); if (dst_mode != IEEE802154_FRAME_DST_MODE_NONE) { // dest address is present/short/extended if ((src_mode == IEEE802154_FRAME_SRC_MODE_NONE && panid_compression) || @@ -74,13 +90,13 @@ IEEE802154_STATIC IEEE802154_NOINLINE bool is_dst_panid_present(const uint8_t *f IEEE802154_STATIC IEEE802154_NOINLINE bool is_src_panid_present(const uint8_t *frame) { + uint8_t dst_mode = dst_addr_mode(frame); uint8_t src_mode = src_addr_mode(frame); - bool panid_compression = is_panid_compression(frame); bool src_panid_present = false; + bool panid_compression = is_panid_compression(frame); + IEEE802154_RETURN_ON_FALSE_SILENTLY(is_valid_dst_mode(dst_mode) && is_valid_src_mode(src_mode), false); if (ieee802154_frame_get_version(frame) == IEEE802154_FRAME_VERSION_2) { - uint8_t dst_mode = dst_addr_mode(frame); - if (src_mode != IEEE802154_FRAME_SRC_MODE_NONE) { if (dst_mode == IEEE802154_FRAME_DST_MODE_EXT && src_mode == IEEE802154_FRAME_SRC_MODE_EXT && !panid_compression) { src_panid_present = false; @@ -101,6 +117,16 @@ IEEE802154_STATIC IEEE802154_NOINLINE bool is_src_panid_present(const uint8_t *f return src_panid_present; } +uint8_t IEEE802154_INLINE ieee802154_frame_get_type(const uint8_t *frame) +{ + return frame[IEEE802154_FRAME_TYPE_OFFSET] & IEEE802154_FRAME_TYPE_MASK; +} + +uint8_t IEEE802154_INLINE ieee802154_frame_get_version(const uint8_t *frame) +{ + return frame[IEEE802154_FRAME_VERSION_OFFSET] & IEEE802154_FRAME_VERSION_MASK; +} + IEEE802154_STATIC uint8_t IEEE802154_INLINE ieee802154_frame_address_offset(const uint8_t *frame) { return IEEE802154_FRAME_PHR_SIZE + IEEE802154_FRAME_FCF_SIZE + (is_dsn_present(frame) ? IEEE802154_FRAME_DSN_SIZE : 0); @@ -109,12 +135,17 @@ IEEE802154_STATIC uint8_t IEEE802154_INLINE ieee802154_frame_address_offset(cons IEEE802154_STATIC IRAM_ATTR uint8_t ieee802154_frame_address_size(const uint8_t *frame) { uint8_t address_size = 0; + uint8_t dst_mode = dst_addr_mode(frame); + uint8_t src_mode = src_addr_mode(frame); + + ESP_RETURN_ON_FALSE_ISR(is_valid_src_mode(src_mode) && is_valid_dst_mode(dst_mode), IEEE802154_FRAME_INVALID_VALUE, IEEE802154_TAG, "invalid address mode"); + if (is_dst_panid_present(frame)) { address_size += IEEE802154_FRAME_PANID_SIZE; } - switch (dst_addr_mode(frame)) { + switch (dst_mode) { case IEEE802154_FRAME_DST_MODE_NONE: break; @@ -127,14 +158,14 @@ IEEE802154_STATIC IRAM_ATTR uint8_t ieee802154_frame_address_size(const uint8_t break; default: - return IEEE802154_FRAME_INVALID_OFFSET; + break; } if (is_src_panid_present(frame)) { address_size += IEEE802154_FRAME_PANID_SIZE; } - switch (src_addr_mode(frame)) { + switch (src_mode) { case IEEE802154_FRAME_SRC_MODE_NONE: break; @@ -147,7 +178,7 @@ IEEE802154_STATIC IRAM_ATTR uint8_t ieee802154_frame_address_size(const uint8_t break; default: - return IEEE802154_FRAME_INVALID_OFFSET; + break; } return address_size; @@ -155,12 +186,11 @@ IEEE802154_STATIC IRAM_ATTR uint8_t ieee802154_frame_address_size(const uint8_t IEEE802154_STATIC IEEE802154_NOINLINE uint8_t ieee802154_frame_security_header_offset(const uint8_t *frame) { - ESP_RETURN_ON_FALSE_ISR(ieee802154_is_supported_frame_type(ieee802154_frame_get_type(frame)), IEEE802154_FRAME_INVALID_ADDR_MODE, IEEE802154_TAG, "invalid frame type"); + IEEE802154_RETURN_ON_FALSE_SILENTLY(ieee802154_is_supported_frame_type(ieee802154_frame_get_type(frame)), IEEE802154_FRAME_INVALID_VALUE); uint8_t offset = ieee802154_frame_address_offset(frame); + IEEE802154_RETURN_ON_FALSE_SILENTLY(offset != IEEE802154_FRAME_INVALID_VALUE, IEEE802154_FRAME_INVALID_VALUE); uint8_t address_size = ieee802154_frame_address_size(frame); - - ESP_RETURN_ON_FALSE_ISR(offset != IEEE802154_FRAME_INVALID_OFFSET, IEEE802154_FRAME_INVALID_OFFSET, IEEE802154_TAG, "invalid offset"); - ESP_RETURN_ON_FALSE_ISR(address_size != IEEE802154_FRAME_INVALID_OFFSET, IEEE802154_FRAME_INVALID_OFFSET, IEEE802154_TAG, "invalid offset"); + IEEE802154_RETURN_ON_FALSE_SILENTLY(address_size != IEEE802154_FRAME_INVALID_VALUE, IEEE802154_FRAME_INVALID_VALUE); offset += address_size; @@ -169,12 +199,12 @@ IEEE802154_STATIC IEEE802154_NOINLINE uint8_t ieee802154_frame_security_header_o IEEE802154_STATIC IEEE802154_NOINLINE uint8_t ieee802154_frame_get_security_field_len(const uint8_t *frame) { - ESP_RETURN_ON_FALSE_ISR(ieee802154_is_supported_frame_type(ieee802154_frame_get_type(frame)), IEEE802154_FRAME_INVALID_OFFSET, IEEE802154_TAG, "invalid frame type"); + ESP_RETURN_ON_FALSE_ISR(ieee802154_is_supported_frame_type(ieee802154_frame_get_type(frame)), IEEE802154_FRAME_INVALID_VALUE, IEEE802154_TAG, "invalid frame type"); uint8_t security_field_len = 0; uint8_t offset = ieee802154_frame_security_header_offset(frame); - ESP_RETURN_ON_FALSE_ISR(offset != IEEE802154_FRAME_INVALID_OFFSET, IEEE802154_FRAME_INVALID_OFFSET, IEEE802154_TAG, "invalid offset"); + ESP_RETURN_ON_FALSE_ISR(offset != IEEE802154_FRAME_INVALID_VALUE, IEEE802154_FRAME_INVALID_VALUE, IEEE802154_TAG, "invalid offset"); security_field_len += IEEE802154_FRAME_SE_HEAD_SIZE; uint8_t security_header = frame[offset]; @@ -206,7 +236,9 @@ IEEE802154_STATIC IEEE802154_NOINLINE uint8_t ieee802154_frame_get_security_fiel IEEE802154_STATIC uint8_t ieee802154_frame_ie_header_offset(const uint8_t *frame) { uint8_t offset = ieee802154_frame_security_header_offset(frame); + IEEE802154_RETURN_ON_FALSE_SILENTLY(offset != IEEE802154_FRAME_INVALID_VALUE, IEEE802154_FRAME_INVALID_VALUE); uint8_t security_field_len = ieee802154_frame_get_security_field_len(frame); + IEEE802154_RETURN_ON_FALSE_SILENTLY(security_field_len != IEEE802154_FRAME_INVALID_VALUE, IEEE802154_FRAME_INVALID_VALUE); offset += security_field_len; @@ -216,6 +248,7 @@ IEEE802154_STATIC uint8_t ieee802154_frame_ie_header_offset(const uint8_t *frame IEEE802154_STATIC uint8_t ieee802154_frame_get_mic_len(const uint8_t *frame) { uint8_t offset = ieee802154_frame_security_header_offset(frame); + IEEE802154_RETURN_ON_FALSE_SILENTLY(offset != IEEE802154_FRAME_INVALID_VALUE, 0); uint8_t mic_len = 0; uint8_t security_header = frame[offset]; @@ -242,8 +275,10 @@ IEEE802154_STATIC uint8_t ieee802154_frame_get_mic_len(const uint8_t *frame) IEEE802154_STATIC uint8_t ieee802154_frame_get_ie_field_len(const uint8_t *frame) { uint8_t offset = ieee802154_frame_ie_header_offset(frame); + IEEE802154_RETURN_ON_FALSE_SILENTLY(offset != IEEE802154_FRAME_INVALID_VALUE, IEEE802154_FRAME_INVALID_VALUE); uint8_t ie_field_len = 0; uint8_t frame_footer_len = ieee802154_frame_get_mic_len(frame) + IEEE802154_FRAME_FCS_SIZE; + uint8_t frame_len = frame[0]; /* If the `offset + frame_footer_len == frame_len`, we exit the `while()` loop. This covers the case where frame contains one or more Header IEs @@ -253,6 +288,9 @@ IEEE802154_STATIC uint8_t ieee802154_frame_get_ie_field_len(const uint8_t *frame footer length. (for details, please reference 2015 - spec table 7 - 6 in page 169) */ while (frame[0] > offset + ie_field_len + frame_footer_len) { + if (offset + ie_field_len + 1 >= frame_len) { + break; + } uint16_t ie_header = frame[offset + ie_field_len + 1] << 8 | frame[offset + ie_field_len]; // Header Termination IE 2 is used in to signal end of the MHR and beginning of the MAC Payload. if ((ie_header & IEEE802154_FRAME_IE_HEAD_ID_MASK) == IEEE802154_IE_TYPE_HT2) { @@ -269,9 +307,12 @@ IEEE802154_STATIC uint8_t ieee802154_frame_get_ie_field_len(const uint8_t *frame IEEE802154_STATIC IRAM_ATTR uint8_t ieee802154_frame_payload_offset(const uint8_t *frame) { uint8_t offset = ieee802154_frame_security_header_offset(frame); + IEEE802154_RETURN_ON_FALSE_SILENTLY(offset != IEEE802154_FRAME_INVALID_VALUE, IEEE802154_FRAME_INVALID_VALUE); if (ieee802154_frame_is_security_enabled(frame)) { // skip security field. - offset += ieee802154_frame_get_security_field_len(frame); + uint8_t security_field_len = ieee802154_frame_get_security_field_len(frame); + IEEE802154_RETURN_ON_FALSE_SILENTLY(security_field_len != IEEE802154_FRAME_INVALID_VALUE, IEEE802154_FRAME_INVALID_VALUE); + offset += security_field_len; } if (ieee802154_frame_get_version(frame) == IEEE802154_FRAME_VERSION_2 && is_ie_present(frame)) { @@ -290,31 +331,44 @@ IEEE802154_STATIC IRAM_ATTR uint8_t ieee802154_frame_payload_offset(const uint8_ return offset - 1; } -uint8_t IEEE802154_INLINE ieee802154_frame_get_type(const uint8_t *frame) +bool ieee802154_is_data_request(const uint8_t *frame) { - return frame[IEEE802154_FRAME_TYPE_OFFSET] & IEEE802154_FRAME_TYPE_MASK; -} + if (ieee802154_frame_get_type(frame) != IEEE802154_FRAME_TYPE_COMMAND) { + return false; + } + uint8_t offset = ieee802154_frame_security_header_offset(frame); + if (ieee802154_frame_is_security_enabled(frame)) { + // skip security field. + offset += ieee802154_frame_get_security_field_len(frame); + } -uint8_t IEEE802154_INLINE ieee802154_frame_get_version(const uint8_t *frame) -{ - return frame[IEEE802154_FRAME_VERSION_OFFSET] & IEEE802154_FRAME_VERSION_MASK; + if (ieee802154_frame_get_version(frame) == IEEE802154_FRAME_VERSION_2 && is_ie_present(frame)) { + // skip IE fields. + offset += ieee802154_frame_get_ie_field_len(frame); + } + if (frame[offset] == IEEE802154_CMD_DATA_REQ) { + return true; + } + + return false; } bool IEEE802154_INLINE ieee802154_frame_is_ack_required(const uint8_t *frame) { - return (ieee802154_is_supported_frame_type(ieee802154_frame_get_type(frame))) && (frame[IEEE802154_FRAME_AR_OFFSET] & IEEE802154_FRAME_AR_BIT); + IEEE802154_RETURN_ON_FALSE_SILENTLY(ieee802154_is_supported_frame_type(ieee802154_frame_get_type(frame)), false); + return (frame[IEEE802154_FRAME_AR_OFFSET] & IEEE802154_FRAME_AR_BIT); } uint8_t ieee802154_frame_get_dst_addr(const uint8_t *frame, uint8_t *addr) { - ESP_RETURN_ON_FALSE_ISR(ieee802154_is_supported_frame_type(ieee802154_frame_get_type(frame)), IEEE802154_FRAME_INVALID_ADDR_MODE, IEEE802154_TAG, "invalid frame type"); + ESP_RETURN_ON_FALSE_ISR(ieee802154_is_supported_frame_type(ieee802154_frame_get_type(frame)), IEEE802154_FRAME_INVALID_VALUE, IEEE802154_TAG, "invalid frame type"); uint8_t offset = ieee802154_frame_address_offset(frame); uint8_t dst_mode = dst_addr_mode(frame); - uint8_t addr_size; - - ESP_RETURN_ON_FALSE_ISR(dst_mode == IEEE802154_FRAME_DST_MODE_SHORT || dst_mode == IEEE802154_FRAME_DST_MODE_EXT, dst_mode, IEEE802154_TAG, "invalid address mode"); + uint8_t addr_size = 0; + ESP_RETURN_ON_FALSE_ISR(is_valid_dst_mode(dst_mode), IEEE802154_FRAME_INVALID_VALUE, IEEE802154_TAG, "invalid destination address mode"); + IEEE802154_RETURN_ON_FALSE_SILENTLY(dst_mode != IEEE802154_FRAME_DST_MODE_NONE, IEEE802154_FRAME_DST_MODE_NONE); addr_size = (dst_mode == IEEE802154_FRAME_DST_MODE_SHORT) ? IEEE802154_FRAME_SHORT_ADDR_SIZE : IEEE802154_FRAME_EXT_ADDR_SIZE; if (is_dst_panid_present(frame)) { @@ -328,15 +382,16 @@ uint8_t ieee802154_frame_get_dst_addr(const uint8_t *frame, uint8_t *addr) uint8_t ieee802154_frame_get_src_addr(const uint8_t *frame, uint8_t *addr) { - ESP_RETURN_ON_FALSE_ISR(ieee802154_is_supported_frame_type(ieee802154_frame_get_type(frame)), IEEE802154_FRAME_INVALID_ADDR_MODE, IEEE802154_TAG, "invalid frame type"); + ESP_RETURN_ON_FALSE_ISR(ieee802154_is_supported_frame_type(ieee802154_frame_get_type(frame)), IEEE802154_FRAME_INVALID_VALUE, IEEE802154_TAG, "invalid frame type"); uint8_t offset = ieee802154_frame_address_offset(frame); uint8_t dst_mode = dst_addr_mode(frame); uint8_t src_mode = src_addr_mode(frame); - uint8_t addr_size; + uint8_t addr_size = 0; - ESP_RETURN_ON_FALSE_ISR(src_mode == IEEE802154_FRAME_SRC_MODE_SHORT || src_mode == IEEE802154_FRAME_SRC_MODE_EXT, src_mode, IEEE802154_TAG, "invalid address mode"); + ESP_RETURN_ON_FALSE_ISR(is_valid_src_mode(src_mode) && is_valid_dst_mode(dst_mode), IEEE802154_FRAME_INVALID_VALUE, IEEE802154_TAG, "invalid address mode"); + IEEE802154_RETURN_ON_FALSE_SILENTLY(src_mode != IEEE802154_FRAME_SRC_MODE_NONE, IEEE802154_FRAME_SRC_MODE_NONE); addr_size = (src_mode == IEEE802154_FRAME_SRC_MODE_SHORT) ? IEEE802154_FRAME_SHORT_ADDR_SIZE : IEEE802154_FRAME_EXT_ADDR_SIZE; if (is_dst_panid_present(frame)) { @@ -367,7 +422,9 @@ uint8_t ieee802154_frame_get_src_addr(const uint8_t *frame, uint8_t *addr) uint8_t ieee802154_frame_get_security_payload_offset(uint8_t *frame) { - return ieee802154_frame_payload_offset(frame); + uint8_t offset = ieee802154_frame_payload_offset(frame); + ESP_RETURN_ON_FALSE_ISR(offset != IEEE802154_FRAME_INVALID_VALUE, IEEE802154_FRAME_INVALID_VALUE, IEEE802154_TAG, "invalid offset"); + return offset; } esp_err_t ieee802154_frame_get_dest_panid(const uint8_t *frame, uint8_t *panid) @@ -385,6 +442,7 @@ esp_err_t ieee802154_frame_get_src_panid(const uint8_t *frame, uint8_t *panid) { uint8_t offset = ieee802154_frame_address_offset(frame); uint8_t dst_mode = dst_addr_mode(frame); + IEEE802154_RETURN_ON_FALSE_SILENTLY(is_valid_dst_mode(dst_mode), ESP_FAIL); if (is_src_panid_present(frame)) { if (is_dst_panid_present(frame)) { diff --git a/components/ieee802154/driver/esp_ieee802154_pib.c b/components/ieee802154/driver/esp_ieee802154_pib.c index 57204949c1..b8a95369db 100644 --- a/components/ieee802154/driver/esp_ieee802154_pib.c +++ b/components/ieee802154/driver/esp_ieee802154_pib.c @@ -1,11 +1,12 @@ /* - * 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 #include +#include "sdkconfig.h" #include "hal/ieee802154_ll.h" #include "esp_check.h" #include "esp_ieee802154_pib.h" @@ -14,6 +15,30 @@ static ieee802154_pib_t s_ieee802154_pib; static bool is_pending = false; +extern const int8_t* bt_bb_get_tx_pwr_table(uint8_t *length); + +static inline int8_t IEEE802154_TXPOWER_VALUE_MAX(void) +{ + uint8_t length = 0; + const int8_t *tx_pwr_table = bt_bb_get_tx_pwr_table(&length); + if (!tx_pwr_table || length == 0) { + ESP_LOGE(IEEE802154_TAG, "Failed to get tx power table"); + return 0; + } + return tx_pwr_table[length - 1]; +} + +static inline int8_t IEEE802154_TXPOWER_VALUE_MIN(void) +{ + uint8_t length = 0; + const int8_t *tx_pwr_table = bt_bb_get_tx_pwr_table(&length); + if (!tx_pwr_table || length == 0) { + ESP_LOGE(IEEE802154_TAG, "Failed to get tx power table"); + return 0; + } + return tx_pwr_table[0]; +} + static inline void set_pending(void) { is_pending = true; @@ -24,7 +49,7 @@ static inline void clr_pending(void) is_pending = false; } -bool inline ieee802154_pib_is_pending(void) +inline bool ieee802154_pib_is_pending(void) { return is_pending; } @@ -41,22 +66,38 @@ void ieee802154_pib_init(void) s_ieee802154_pib.channel = 11; s_ieee802154_pib.cca_threshold = CONFIG_IEEE802154_CCA_THRESHOLD; s_ieee802154_pib.cca_mode = CONFIG_IEEE802154_CCA_MODE; - memset(&s_ieee802154_pib.power_table, IEEE802154_TXPOWER_VALUE_MAX, sizeof(s_ieee802154_pib.power_table)); + for (int i = 0; i < 16; i++) { + s_ieee802154_pib.power_table.channel[i] = IEEE802154_TXPOWER_VALUE_MAX(); + } set_pending(); } IEEE802154_NOINLINE static uint8_t ieee802154_txpower_convert(int8_t txpower) { - uint8_t ieee820154_txpower_index = 0; - if (txpower >= IEEE802154_TXPOWER_VALUE_MAX) { - ieee820154_txpower_index = 15; - } else if (txpower <= IEEE802154_TXPOWER_VALUE_MIN) { - ieee820154_txpower_index = IEEE802154_TXPOWER_INDEX_MIN; - } else { - ieee820154_txpower_index = (uint8_t)((txpower - IEEE802154_TXPOWER_VALUE_MIN) / 3) + IEEE802154_TXPOWER_INDEX_MIN; + uint8_t ieee802154_txpower_index = 0; + uint8_t length = 0; + const int8_t *tx_pwr_table = bt_bb_get_tx_pwr_table(&length); + if (!tx_pwr_table || length == 0) { + ESP_LOGE(IEEE802154_TAG, "Failed to get tx power table"); + return 0; } - return ieee820154_txpower_index; + + if (txpower <= tx_pwr_table[0]) { + ieee802154_txpower_index = 0; + } else if (txpower >= tx_pwr_table[length - 1]) { + ieee802154_txpower_index = length - 1; + } else { + uint8_t i = 0; + for (i = length-1; i != 0; i--) { + if (tx_pwr_table[i] <= txpower) { + break; + } + } + ieee802154_txpower_index = i; + } + + return ieee802154_txpower_index; } void ieee802154_pib_update(void) @@ -74,7 +115,14 @@ void ieee802154_pib_update(void) ieee802154_ll_set_coordinator(s_ieee802154_pib.coordinator); ieee802154_ll_set_promiscuous(s_ieee802154_pib.promiscuous); - ieee802154_ll_set_pending_mode(s_ieee802154_pib.pending_mode == IEEE802154_AUTO_PENDING_ENHANCED); + bool target_mode = false; + for (int i = 0; i < CONFIG_IEEE802154_INTERFACE_NUM; i++) { + if (s_ieee802154_pib.pending_mode[i] == IEEE802154_AUTO_PENDING_ENHANCED || s_ieee802154_pib.pending_mode[i] == IEEE802154_AUTO_PENDING_ZIGBEE) { + target_mode = true; + break; + } + } + ieee802154_ll_set_pending_mode(target_mode); clr_pending(); } @@ -87,7 +135,7 @@ uint8_t ieee802154_pib_get_channel(void) void ieee802154_pib_set_channel(uint8_t channel) { - ESP_RETURN_ON_FALSE(ieee802154_is_valid_channel(channel), , IEEE802154_TAG, "Failed to set channel, reason: Invalid channel: %d", channel); + ESP_RETURN_VOID_ON_FALSE(ieee802154_is_valid_channel(channel), IEEE802154_TAG, "Failed to set channel, reason: Invalid channel: %d", channel); if (s_ieee802154_pib.channel != channel) { s_ieee802154_pib.channel = channel; set_pending(); @@ -231,15 +279,15 @@ void ieee802154_pib_set_coordinator(bool enable) } } -ieee802154_ll_pending_mode_t ieee802154_pib_get_pending_mode(void) +ieee802154_ll_pending_mode_t ieee802154_pib_get_pending_mode(esp_ieee802154_multipan_index_t inf_index) { - return s_ieee802154_pib.pending_mode; + return s_ieee802154_pib.pending_mode[inf_index]; } -void ieee802154_pib_set_pending_mode(ieee802154_ll_pending_mode_t pending_mode) +void ieee802154_pib_set_pending_mode(esp_ieee802154_multipan_index_t inf_index, ieee802154_ll_pending_mode_t pending_mode) { - if (s_ieee802154_pib.pending_mode != pending_mode) { - s_ieee802154_pib.pending_mode = pending_mode; + if (s_ieee802154_pib.pending_mode[inf_index] != pending_mode) { + s_ieee802154_pib.pending_mode[inf_index] = pending_mode; set_pending(); } } diff --git a/components/ieee802154/driver/esp_ieee802154_timer.c b/components/ieee802154/driver/esp_ieee802154_timer.c index d44249e529..f4c6eb2b79 100644 --- a/components/ieee802154/driver/esp_ieee802154_timer.c +++ b/components/ieee802154/driver/esp_ieee802154_timer.c @@ -30,10 +30,7 @@ void ieee802154_timer0_stop(void) esp_err_t ieee802154_timer0_set_threshold(uint32_t value) { - ESP_RETURN_ON_FALSE((value < IEEE802154_TIMER0_THRESHOLD), ESP_ERR_INVALID_ARG, IEEE802154_TAG, "invalid timer0 threshold"); - ieee802154_ll_timer0_set_threshold(value); - return ESP_OK; } @@ -56,10 +53,7 @@ void ieee802154_timer1_stop(void) esp_err_t ieee802154_timer1_set_threshold(uint32_t value) { - ESP_RETURN_ON_FALSE((value < IEEE802154_TIMER1_THRESHOLD), ESP_ERR_INVALID_ARG, IEEE802154_TAG, "invalid timer1 threshold"); - ieee802154_ll_timer1_set_threshold(value); - return ESP_OK; } diff --git a/components/ieee802154/driver/esp_ieee802154_util.c b/components/ieee802154/driver/esp_ieee802154_util.c index 73f31d2b52..a37cf4b6d8 100644 --- a/components/ieee802154/driver/esp_ieee802154_util.c +++ b/components/ieee802154/driver/esp_ieee802154_util.c @@ -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 */ @@ -8,16 +8,21 @@ #include "soc/soc.h" #include "soc/periph_defs.h" #include "hal/ieee802154_ll.h" +#include "esp_check.h" #include "esp_coex_i154.h" #include "esp_ieee802154_util.h" uint8_t ieee802154_freq_to_channel(uint8_t freq) { - return (freq - 3) / 5 + IEEE802154_OQPSK_2P4G_CHANNEL_MIN; + assert(((freq - 3) % 5) == 0); + uint8_t channel = (freq - 3) / 5 + IEEE802154_OQPSK_2P4G_CHANNEL_MIN; + assert(ieee802154_is_valid_channel(channel)); + return channel; } uint8_t ieee802154_channel_to_freq(uint8_t channel) { + assert(ieee802154_is_valid_channel(channel)); return (channel - IEEE802154_OQPSK_2P4G_CHANNEL_MIN) * 5 + 3; } @@ -43,7 +48,6 @@ esp_ieee802154_coex_config_t ieee802154_get_coex_config(void) void ieee802154_set_txrx_pti(ieee802154_txrx_scene_t txrx_scene) { - switch (txrx_scene) { case IEEE802154_SCENE_IDLE: esp_coex_ieee802154_txrx_pti_set(s_coex_config.idle); @@ -66,8 +70,9 @@ void ieee802154_set_txrx_pti(ieee802154_txrx_scene_t txrx_scene) // TZ-97: implement these two functions using ETM common interface void ieee802154_etm_channel_clear(uint32_t channel) { + assert(channel < 16); if ((REG_READ(ETM_CHEN_AD0_REG) & (1 << channel))) { - REG_WRITE(ETM_CHENCLR_AD0_REG, (REG_READ(ETM_CHENCLR_AD0_REG)) | 1 << channel); + REG_WRITE(ETM_CHENCLR_AD0_REG, (REG_READ(ETM_CHENCLR_AD0_REG)) | (1 << channel)); } } @@ -78,5 +83,11 @@ void ieee802154_etm_set_event_task(uint32_t channel, uint32_t event, uint32_t ta REG_WRITE((ETM_CH0_EVT_ID_REG + ETM_CH_OFFSET * channel), event); REG_WRITE((ETM_CH0_TASK_ID_REG + ETM_CH_OFFSET * channel), task); - REG_WRITE(ETM_CHENSET_AD0_REG, (REG_READ(ETM_CHENSET_AD0_REG) | 1 << channel)); + REG_WRITE(ETM_CHENSET_AD0_REG, (REG_READ(ETM_CHENSET_AD0_REG) | (1 << channel))); +} + +__attribute__((weak)) const int8_t* bt_bb_get_tx_pwr_table(uint8_t *length) +{ + ESP_LOGE(IEEE802154_TAG, "bt_bb_get_tx_pwr_table is not implemented"); + return NULL; } diff --git a/components/ieee802154/esp_ieee802154.c b/components/ieee802154/esp_ieee802154.c index 39342eba8f..1947c73a52 100644 --- a/components/ieee802154/esp_ieee802154.c +++ b/components/ieee802154/esp_ieee802154.c @@ -8,6 +8,7 @@ #include #include "sdkconfig.h" #include "esp_ieee802154.h" +#include "esp_check.h" #include "esp_err.h" #include "esp_phy_init.h" #include "esp_ieee802154_ack.h" @@ -26,6 +27,7 @@ esp_err_t esp_ieee802154_event_callback_list_register(esp_ieee802154_event_cb_li { return ieee802154_event_callback_list_register(cb_list); } + esp_err_t esp_ieee802154_event_callback_list_unregister(void) { return ieee802154_event_callback_list_unregister(); @@ -54,6 +56,7 @@ uint8_t esp_ieee802154_get_channel(void) esp_err_t esp_ieee802154_set_channel(uint8_t channel) { + assert(ieee802154_is_valid_channel(channel)); ieee802154_pib_set_channel(channel); return ESP_OK; } @@ -66,7 +69,9 @@ int8_t esp_ieee802154_get_txpower(void) esp_err_t esp_ieee802154_set_txpower(int8_t power) { esp_ieee802154_txpower_table_t power_table; - memset(&power_table, power, sizeof(power_table)); + for (int i = 0; i < 16; i++) { + power_table.channel[i] = power; + } return ieee802154_pib_set_power_table(power_table); } @@ -77,6 +82,7 @@ esp_err_t esp_ieee802154_set_power_table(esp_ieee802154_txpower_table_t power_ta esp_err_t esp_ieee802154_get_power_table(esp_ieee802154_txpower_table_t *out_power_table) { + assert(out_power_table != NULL); return ieee802154_pib_get_power_table(out_power_table); } @@ -87,6 +93,7 @@ esp_err_t esp_ieee802154_set_power_with_channel(uint8_t channel, int8_t power) esp_err_t esp_ieee802154_get_power_with_channel(uint8_t channel, int8_t *out_power) { + assert(out_power != NULL); return ieee802154_pib_get_power_with_channel(channel, out_power); } @@ -163,40 +170,42 @@ esp_err_t esp_ieee802154_set_coordinator(bool enable) uint16_t esp_ieee802154_get_multipan_panid(esp_ieee802154_multipan_index_t index) { - assert(index < ESP_IEEE802154_MULTIPAN_MAX); + 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 < ESP_IEEE802154_MULTIPAN_MAX); + 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 < ESP_IEEE802154_MULTIPAN_MAX); + 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 < ESP_IEEE802154_MULTIPAN_MAX); + 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 < ESP_IEEE802154_MULTIPAN_MAX); + assert(index < CONFIG_IEEE802154_INTERFACE_NUM); + 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 < ESP_IEEE802154_MULTIPAN_MAX); + assert(index < CONFIG_IEEE802154_INTERFACE_NUM); + assert(ext_addr != NULL); ieee802154_ll_set_multipan_ext_addr(index, ext_addr); return ESP_OK; } @@ -208,17 +217,50 @@ uint8_t esp_ieee802154_get_multipan_enable(void) esp_err_t esp_ieee802154_set_multipan_enable(uint8_t mask) { - assert(mask < (1 << ESP_IEEE802154_MULTIPAN_MAX)); + 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); + 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_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); + 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); + 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_reset_pending_table(inf_index, is_short); + return ESP_OK; +} + #endif // CONFIG_IEEE802154_MULTI_PAN_ENABLE esp_err_t esp_ieee802154_set_ack_timeout(uint32_t timeout) { // Divide by 16 and round it up. uint32_t target_reg_value = (timeout + 15) / 16; - if((timeout % 16) != 0) { + if ((timeout % 16) != 0) { ESP_LOGW(IEEE802154_TAG, "Ack timeout should be a multiple of 16, input %"PRIu32", will be replaced by %"PRIu32"", timeout, (target_reg_value * 16)); } ieee802154_ll_set_ack_timeout(target_reg_value); @@ -254,24 +296,26 @@ esp_err_t esp_ieee802154_set_short_address(uint16_t short_address) esp_err_t esp_ieee802154_get_extended_address(uint8_t *ext_addr) { + assert(ext_addr != NULL); ieee802154_ll_get_multipan_ext_addr(ESP_IEEE802154_MULTIPAN_0, ext_addr); return ESP_OK; } esp_err_t esp_ieee802154_set_extended_address(const uint8_t *ext_addr) { + assert(ext_addr != NULL); ieee802154_ll_set_multipan_ext_addr(ESP_IEEE802154_MULTIPAN_0, ext_addr); return ESP_OK; } esp_ieee802154_pending_mode_t esp_ieee802154_get_pending_mode(void) { - return ieee802154_pib_get_pending_mode(); + return ieee802154_pib_get_pending_mode(ESP_IEEE802154_MULTIPAN_0); } esp_err_t esp_ieee802154_set_pending_mode(esp_ieee802154_pending_mode_t pending_mode) { - ieee802154_pib_set_pending_mode(pending_mode); + ieee802154_pib_set_pending_mode(ESP_IEEE802154_MULTIPAN_0, pending_mode); return ESP_OK; } @@ -288,11 +332,15 @@ bool esp_ieee802154_get_rx_when_idle(void) esp_err_t esp_ieee802154_transmit(const uint8_t *frame, bool cca) { + assert(frame != NULL); + assert(frame[0] <= 127); return ieee802154_transmit(frame, cca); } esp_err_t esp_ieee802154_transmit_at(const uint8_t *frame, bool cca, uint32_t time) { + assert(frame != NULL); + assert(frame[0] <= 127); return ieee802154_transmit_at(frame, cca, time); } @@ -342,10 +390,12 @@ esp_ieee802154_state_t esp_ieee802154_get_state(void) case IEEE802154_STATE_TX_CCA: case IEEE802154_STATE_CCA: case IEEE802154_STATE_TX: + case IEEE802154_STATE_TEST_TX: case IEEE802154_STATE_RX_ACK: return ESP_IEEE802154_RADIO_TRANSMIT; default: + ESP_LOGE(IEEE802154_TAG, "Invalid state: %d", ieee802154_get_state()); assert(false); return ESP_IEEE802154_RADIO_DISABLE; } @@ -353,23 +403,28 @@ esp_ieee802154_state_t esp_ieee802154_get_state(void) esp_err_t esp_ieee802154_set_transmit_security(uint8_t *frame, uint8_t *key, uint8_t *addr) { + assert(frame != NULL); + assert(key != NULL); + assert(addr != NULL); ieee802154_transmit_security_config(frame, key, addr); return ESP_OK; } esp_err_t esp_ieee802154_add_pending_addr(const uint8_t *addr, bool is_short) { - return ieee802154_add_pending_addr(addr, is_short); + assert(addr != NULL); + return ieee802154_add_pending_addr(ESP_IEEE802154_MULTIPAN_0, addr, is_short); } esp_err_t esp_ieee802154_clear_pending_addr(const uint8_t *addr, bool is_short) { - return ieee802154_clear_pending_addr(addr, is_short); + assert(addr != NULL); + return ieee802154_clear_pending_addr(ESP_IEEE802154_MULTIPAN_0, addr, is_short); } esp_err_t esp_ieee802154_reset_pending_table(bool is_short) { - ieee802154_reset_pending_table(is_short); + ieee802154_reset_pending_table(ESP_IEEE802154_MULTIPAN_0, is_short); return ESP_OK; } @@ -432,6 +487,7 @@ __attribute__((weak)) void esp_ieee802154_ed_failed(uint16_t error) { } + __attribute__((weak)) void esp_ieee802154_receive_at_done(void) { @@ -439,7 +495,7 @@ __attribute__((weak)) void esp_ieee802154_receive_at_done(void) __attribute__((weak)) esp_err_t esp_ieee802154_enh_ack_generator(uint8_t *frame, esp_ieee802154_frame_info_t *frame_info, uint8_t* enhack_frame) { - ESP_EARLY_LOGE(IEEE802154_TAG, "Not implement for the enh-ack generating handler"); + ESP_EARLY_LOGE(IEEE802154_TAG, "Not implemented for the enh-ack generating handler"); return ESP_FAIL; } diff --git a/components/ieee802154/include/esp_ieee802154.h b/components/ieee802154/include/esp_ieee802154.h index 2971cc22c6..56c45dbd16 100644 --- a/components/ieee802154/include/esp_ieee802154.h +++ b/components/ieee802154/include/esp_ieee802154.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 */ @@ -257,10 +257,11 @@ esp_err_t esp_ieee802154_set_short_address(uint16_t short_address); /** * @brief Get the device extended address. * - * @param[out] ext_addr The pointer to the device extended address. + * @param[out] ext_addr The pointer to the device extended address. Must not be NULL. * * @return * - ESP_OK on success. + * - ESP_ERR_INVALID_ARG if ext_addr is NULL. * - ESP_FAIL on failure. */ esp_err_t esp_ieee802154_get_extended_address(uint8_t *ext_addr); @@ -268,10 +269,11 @@ esp_err_t esp_ieee802154_get_extended_address(uint8_t *ext_addr); /** * @brief Set the device extended address. * - * @param[in] ext_addr The pointer to the device extended address. + * @param[in] ext_addr The pointer to the device extended address. Must not be NULL. * * @return * - ESP_OK on success. + * - ESP_ERR_INVALID_ARG if ext_addr is NULL. * - ESP_FAIL on failure. */ esp_err_t esp_ieee802154_set_extended_address(const uint8_t *ext_addr); @@ -324,10 +326,11 @@ esp_err_t esp_ieee802154_set_multipan_short_address(esp_ieee802154_multipan_inde * @brief Get the device extended address for specific interface. * * @param[in] index The interface index. - * @param[out] ext_addr The pointer to the device extended address. + * @param[out] ext_addr The pointer to the device extended address. Must not be NULL. * * @return * - ESP_OK on success. + * - ESP_ERR_INVALID_ARG if ext_addr is NULL. * - ESP_FAIL on failure. */ esp_err_t esp_ieee802154_get_multipan_extended_address(esp_ieee802154_multipan_index_t index, uint8_t *ext_addr); @@ -336,10 +339,11 @@ esp_err_t esp_ieee802154_get_multipan_extended_address(esp_ieee802154_multipan_i * @brief Set the device extended address for specific interface. * * @param[in] index The interface index. - * @param[in] ext_addr The pointer to the device extended address. + * @param[in] ext_addr The pointer to the device extended address. Must not be NULL. * * @return * - ESP_OK on success. + * - ESP_ERR_INVALID_ARG if addr is NULL. * - ESP_FAIL on failure. */ esp_err_t esp_ieee802154_set_multipan_extended_address(esp_ieee802154_multipan_index_t index, const uint8_t *ext_addr); @@ -409,12 +413,13 @@ esp_err_t esp_ieee802154_set_pending_mode(esp_ieee802154_pending_mode_t pending_ /** * @brief Add address to the source matching table. * - * @param[in] addr The pointer to the address. + * @param[in] addr The pointer to the address. Must not be NULL. * @param[in] is_short Short address or Extended address. * * @return * - ESP_OK on success. * - ESP_ERR_NO_MEM if the pending table is full. + * - ESP_ERR_INVALID_ARG if addr is NULL. * */ esp_err_t esp_ieee802154_add_pending_addr(const uint8_t *addr, bool is_short); @@ -422,12 +427,13 @@ esp_err_t esp_ieee802154_add_pending_addr(const uint8_t *addr, bool is_short); /** * @brief Remove address from the source matching table. * - * @param[in] addr The pointer to the address. + * @param[in] addr The pointer to the address. Must not be NULL. * @param[in] is_short Short address or Extended address. * * @return * - ESP_OK on success. * - ESP_ERR_NOT_FOUND if the address was not found from the source matching table. + * - ESP_ERR_INVALID_ARG if addr is NULL. * */ esp_err_t esp_ieee802154_clear_pending_addr(const uint8_t *addr, bool is_short); @@ -440,6 +446,7 @@ esp_err_t esp_ieee802154_clear_pending_addr(const uint8_t *addr, bool is_short); * @return * - ESP_OK on success. * - ESP_FAIL on failure. + * */ esp_err_t esp_ieee802154_reset_pending_table(bool is_short); @@ -459,6 +466,7 @@ int8_t esp_ieee802154_get_cca_threshold(void); * @return * - ESP_OK on success. * - ESP_FAIL on failure. + * */ esp_err_t esp_ieee802154_set_cca_threshold(int8_t cca_threshold); @@ -478,6 +486,7 @@ esp_ieee802154_cca_mode_t esp_ieee802154_get_cca_mode(void); * @return * - ESP_OK on success. * - ESP_FAIL on failure. + * */ esp_err_t esp_ieee802154_set_cca_mode(esp_ieee802154_cca_mode_t cca_mode); @@ -489,6 +498,7 @@ esp_err_t esp_ieee802154_set_cca_mode(esp_ieee802154_cca_mode_t cca_mode); * @return * - ESP_OK on success. * - ESP_FAIL on failure. + * */ esp_err_t esp_ieee802154_set_rx_when_idle(bool enable); @@ -643,12 +653,13 @@ uint8_t esp_ieee802154_get_recent_lqi(void); /** * @brief Set the key and addr for a frame needs to be encrypted by HW. * - * @param[in] frame A frame needs to be encrypted. Refer to `esp_ieee802154_transmit()`. - * @param[in] key A 16-bytes key for encryption. - * @param[in] addr An 8-bytes addr for HW to generate nonce, in general, is the device extended address. + * @param[in] frame A frame needs to be encrypted. Refer to `esp_ieee802154_transmit()`. Must not be NULL. + * @param[in] key A 16-bytes key for encryption. Must not be NULL. + * @param[in] addr An 8-bytes addr for HW to generate nonce, in general, is the device extended address. Must not be NULL. * * @return * - ESP_OK on success. + * - ESP_ERR_INVALID_ARG if frame, key, or addr is NULL. * - ESP_FAIL on failure. */ esp_err_t esp_ieee802154_set_transmit_security(uint8_t *frame, uint8_t *key, uint8_t *addr); @@ -657,7 +668,7 @@ esp_err_t esp_ieee802154_set_transmit_security(uint8_t *frame, uint8_t *key, uin * @brief This function will be called when a received frame needs to be acked with Enh-Ack, the upper * layer should generate the Enh-Ack frame in this callback function. * - * @param[in] frame The received frame. + * @param[in] frame The received frame. Must not be NULL. * @param[in] frame_info The frame information. Refer to `esp_ieee802154_frame_info_t`. * @param[out] enhack_frame The Enh-ack frame need to be generated via this function, HW will send it back after AIFS. * @@ -741,6 +752,7 @@ esp_ieee802154_coex_config_t esp_ieee802154_get_coex_config(void); * @return * - ESP_OK on success. * - ESP_FAIL on failure. + * */ esp_err_t esp_ieee802154_event_callback_list_register(esp_ieee802154_event_cb_list_t cb_list); @@ -753,9 +765,84 @@ esp_err_t esp_ieee802154_event_callback_list_register(esp_ieee802154_event_cb_li * @return * - ESP_OK on success. * - ESP_FAIL on failure. + * */ esp_err_t esp_ieee802154_event_callback_list_unregister(void); +/** + * @brief Add a pending address to the multipan table for the specified interface. + * + * @note This API should be called only when the IEEE 802.15.4 subsystem is enabled. + * + * @param inf_index Index of the interface. + * @param addr Pointer to the address to add. + * @param is_short True if the address is a short address, false if extended. + * + * @return + * - ESP_OK on success. + * - ESP_FAIL on failure. + */ +esp_err_t esp_ieee802154_multipan_add_pending_addr(esp_ieee802154_multipan_index_t inf_index, const uint8_t *addr, bool is_short); + +/** + * @brief Remove a pending address from the multipan table for the specified interface. + * + * @note This API should be called only when the IEEE 802.15.4 subsystem is enabled. + * + * @param inf_index Index of the interface. + * @param addr Pointer to the address to remove. + * @param is_short True if the address is a short address, false if extended. + * + * @return + * - ESP_OK on success. + * - ESP_FAIL on failure. + */ +esp_err_t esp_ieee802154_multipan_clear_pending_addr(esp_ieee802154_multipan_index_t inf_index, const uint8_t *addr, bool is_short); + +/** + * @brief Reset the pending address table for the specified interface. + * + * @note This API clears all pending addresses of the specified type (short or extended) for the interface. + * + * @param inf_index Index of the interface. + * @param is_short True to reset short addresses, false to reset extended addresses. + * + * @return + * - ESP_OK on success. + * - ESP_FAIL on failure. + */ +esp_err_t esp_ieee802154_multipan_reset_pending_table(esp_ieee802154_multipan_index_t inf_index, bool is_short); + +/** + * @brief Get the auto frame pending mode of the specified multipan interface. + * + * @note This API returns the logical pending mode configured for a given multipan + * interface. The value is used by the ACK generation logic together with the + * per-interface pending address table. + * + * @param inf_index Index of the multipan interface. + * + * @return + * - The auto frame pending mode, refer to esp_ieee802154_pending_mode_t. + */ +esp_ieee802154_pending_mode_t esp_ieee802154_multipan_get_pending_mode(esp_ieee802154_multipan_index_t inf_index); + +/** + * @brief Set the auto frame pending mode of the specified multipan interface. + * + * @note This API configures the logical pending mode for a given multipan interface. + * The configured mode is used by the ACK generation logic together with the + * per-interface pending address table. + * + * @param inf_index Index of the multipan interface. + * @param pending_mode Pending mode to set for this interface. + * + * @return + * - ESP_OK on success. + * - ESP_FAIL on failure. + */ +esp_err_t esp_ieee802154_multipan_set_pending_mode(esp_ieee802154_multipan_index_t inf_index, esp_ieee802154_pending_mode_t pending_mode); + #ifdef __cplusplus } #endif diff --git a/components/ieee802154/include/esp_ieee802154_types.h b/components/ieee802154/include/esp_ieee802154_types.h index bf52ed91a4..ddf5d792af 100644 --- a/components/ieee802154/include/esp_ieee802154_types.h +++ b/components/ieee802154/include/esp_ieee802154_types.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -10,7 +10,7 @@ #include #include "esp_err.h" -#define US_PER_SYMBLE 16 +#define US_PER_SYMBOL 16 #ifdef __cplusplus extern "C" { @@ -61,8 +61,8 @@ typedef enum { } esp_ieee802154_pending_mode_t; /** -* @brief The four groups of mac filter interface index. -*/ + * @brief The four groups of mac filter interface index. + */ typedef enum { ESP_IEEE802154_MULTIPAN_0 = 0, ESP_IEEE802154_MULTIPAN_1 = 1, @@ -82,6 +82,7 @@ typedef struct { int8_t rssi; /*!< RSSI */ uint8_t lqi; /*!< LQI */ uint64_t timestamp; /*!< The timestamp when the frame's SFD field was received */ + esp_ieee802154_multipan_index_t mpf_index; /*!< Multipan interface that matches the frame, ESP_IEEE802154_MULTIPAN_MAX if none */ } esp_ieee802154_frame_info_t; /** @@ -142,13 +143,13 @@ typedef esp_err_t (*esp_ieee802154_enh_ack_generator_cb_t)(uint8_t *frame, esp_i * */ typedef struct { - esp_ieee802154_receive_done_cb_t rx_done_cb; /*! #include "sdkconfig.h" #include "esp_err.h" +#include "esp_ieee802154_types.h" #include "esp_ieee802154_frame.h" #ifdef __cplusplus @@ -25,35 +26,37 @@ extern "C" { typedef struct { uint8_t short_addr[CONFIG_IEEE802154_PENDING_TABLE_SIZE][IEEE802154_FRAME_SHORT_ADDR_SIZE]; /*!< Short address table */ uint8_t ext_addr[CONFIG_IEEE802154_PENDING_TABLE_SIZE][IEEE802154_FRAME_EXT_ADDR_SIZE]; /*!< Extend address table */ - uint8_t short_addr_mask[IEEE802154_PENDING_TABLE_MASK_SIZE]; /*!< The mask which the index of short address table is used */ - uint8_t ext_addr_mask[IEEE802154_PENDING_TABLE_MASK_SIZE]; /*!< The mask which the index of extended address table is used */ + uint8_t short_addr_mask[IEEE802154_PENDING_TABLE_MASK_SIZE]; /*!< Bit mask to track which indices of the short address table are used */ + uint8_t ext_addr_mask[IEEE802154_PENDING_TABLE_MASK_SIZE]; /*!< Bit mask to track which indices of the extended address table are used */ } ieee802154_pending_table_t; /** * @brief Add an address to the pending table. * - * @param[in] addr The pointer to the address needs to be added. + * @param[in] addr The pointer to the address needs to be added. Must not be NULL. * @param[in] is_short The type of address, true for short address, false for extended. * * @return * - ESP_OK on success. * - ESP_FAIL on failure due to the table is full. + * - ESP_ERR_INVALID_ARG if addr is NULL. * */ -esp_err_t ieee802154_add_pending_addr(const uint8_t *addr, bool is_short); +esp_err_t ieee802154_add_pending_addr(esp_ieee802154_multipan_index_t inf_index, const uint8_t *addr, bool is_short); /** * @brief Remove an address in pending table. * - * @param[in] addr The pointer to the address needs to be cleared. + * @param[in] addr The pointer to the address needs to be cleared. Must not be NULL. * @param[in] is_short The type of address, true for short address, false for extended. * * @return * - ESP_OK on success. * - ESP_FAIL on failure if the given address is not present in the pending table. + * - ESP_ERR_INVALID_ARG if addr is NULL. * */ -esp_err_t ieee802154_clear_pending_addr(const uint8_t *addr, bool is_short); +esp_err_t ieee802154_clear_pending_addr(esp_ieee802154_multipan_index_t inf_index, const uint8_t *addr, bool is_short); /** * @brief Reset the pending table, only clear the mask bits for finishing the process quickly. @@ -61,18 +64,18 @@ esp_err_t ieee802154_clear_pending_addr(const uint8_t *addr, bool is_short); * @param[in] is_short The type of address, true for resetting short address table, false for extended. * */ -void ieee802154_reset_pending_table(bool is_short); +void ieee802154_reset_pending_table(esp_ieee802154_multipan_index_t inf_index, bool is_short); /** * @brief Check whether the pending bit should be set or not in the ack frame. * - * @param[in] frame The pointer to the received frame. + * @param[in] frame The pointer to the received frame. Must not be NULL. * * @return - * - True The pending bit should be set, otherwise False. + * - True if the pending bit should be set, otherwise false. * */ -bool ieee802154_ack_config_pending_bit(const uint8_t *frame); +bool ieee802154_ack_config_pending_bit(const uint8_t *frame, const esp_ieee802154_frame_info_t *frame_info); #ifdef __cplusplus } diff --git a/components/ieee802154/private_include/esp_ieee802154_dev.h b/components/ieee802154/private_include/esp_ieee802154_dev.h index 1d53af66f0..aaeed472ee 100644 --- a/components/ieee802154/private_include/esp_ieee802154_dev.h +++ b/components/ieee802154/private_include/esp_ieee802154_dev.h @@ -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 */ diff --git a/components/ieee802154/private_include/esp_ieee802154_frame.h b/components/ieee802154/private_include/esp_ieee802154_frame.h index 80cfdc6152..59f2f4c622 100644 --- a/components/ieee802154/private_include/esp_ieee802154_frame.h +++ b/components/ieee802154/private_include/esp_ieee802154_frame.h @@ -15,9 +15,9 @@ extern "C" { #endif #define IEEE802154_FRAME_MAX_LEN 127 +#define IEEE802154_FRAME_MIN_LEN 3 -#define IEEE802154_FRAME_INVALID_OFFSET 0xff -#define IEEE802154_FRAME_INVALID_ADDR_MODE 0xff +#define IEEE802154_FRAME_INVALID_VALUE 0xff #define IEEE802154_FRAME_TYPE_OFFSET 1 #define IEEE802154_FRAME_TYPE_MASK 0x07 @@ -111,7 +111,7 @@ static inline bool ieee802154_is_supported_frame_type(uint8_t frame_type) /** * @brief Get the frame type. * - * @param[in] frame The pointer to the frame. + * @param[in] frame The pointer to the frame. Must not be NULL. * * @return * - The type of the frame. @@ -122,7 +122,7 @@ uint8_t ieee802154_frame_get_type(const uint8_t *frame); /** * @brief Get the frame version. * - * @param[in] frame The pointer to the frame. + * @param[in] frame The pointer to the frame. Must not be NULL. * * @return * - The version of the frame. @@ -133,7 +133,7 @@ uint8_t ieee802154_frame_get_version(const uint8_t *frame); /** * @brief Is the frame ack required. * - * @param[in] frame The pointer to the frame. + * @param[in] frame The pointer to the frame. Must not be NULL. * * @return * - True if the frame is ack required, otherwise false. @@ -154,13 +154,14 @@ bool ieee802154_frame_is_security_enabled(const uint8_t *frame); /** * @brief Get the destination address of the frame. * - * @param[in] frame The pointer to the frame. - * @param[out] addr The pointer to the address. + * @param[in] frame The pointer to the frame. Must not be NULL. + * @param[out] addr The pointer to the address. Must not be NULL. * * @return * - IEEE802154_FRAME_DST_MODE_NONE if destination address mode is none. * - IEEE802154_FRAME_DST_MODE_SHORT if destination address mode is short. * - IEEE802154_FRAME_DST_MODE_EXT if destination address mode is extended. + * - IEEE802154_FRAME_INVALID_VALUE if frame is NULL, addr is NULL, or frame type is invalid. * */ uint8_t ieee802154_frame_get_dst_addr(const uint8_t *frame, uint8_t *addr); @@ -168,51 +169,60 @@ uint8_t ieee802154_frame_get_dst_addr(const uint8_t *frame, uint8_t *addr); /** * @brief Get the source address of the frame. * - * @param[in] frame The pointer to the frame. - * @param[out] addr The pointer to the address. + * @param[in] frame The pointer to the frame. Must not be NULL. + * @param[out] addr The pointer to the address. Must not be NULL. * * @return * - IEEE802154_FRAME_SRC_MODE_NONE if source address mode is none. * - IEEE802154_FRAME_SRC_MODE_SHORT if source address mode is short. * - IEEE802154_FRAME_SRC_MODE_EXT if source address mode is extended. - * + * - IEEE802154_FRAME_INVALID_VALUE if frame is NULL, addr is NULL, or frame type is invalid. */ uint8_t ieee802154_frame_get_src_addr(const uint8_t *frame, uint8_t *addr); /** * @brief Get the offset of the private payload. * - * @param[in] frame The pointer to the frame. + * @param[in] frame The pointer to the frame. Must not be NULL. * * @return * - The offset of the private payload - * + * - IEEE802154_FRAME_INVALID_VALUE if frame is NULL, or frame type is invalid. */ uint8_t ieee802154_frame_get_security_payload_offset(uint8_t *frame); /** * @brief Get the destination PAN ID of the frame. * - * @param[in] frame The pointer to the frame. - * @param[out] panid The pointer to the destination PAN ID. + * @param[in] frame The pointer to the frame. Must not be NULL. + * @param[out] panid The pointer to the destination PAN ID. Must not be NULL. * * @return * - ESP_OK if destination PAN ID is present, otherwise ESP_FAIL. - * + * - ESP_FAIL if frame is NULL, panid is NULL, or frame type is invalid. */ esp_err_t ieee802154_frame_get_dest_panid(const uint8_t *frame, uint8_t *panid); /** * @brief Get the source PAN ID of the frame. * - * @param[in] frame The pointer to the frame. - * @param[out] panid The pointer to the source PAN ID. + * @param[in] frame The pointer to the frame. Must not be NULL. + * @param[out] panid The pointer to the source PAN ID. Must not be NULL. * * @return * - ESP_OK if source PAN ID is present, otherwise ESP_FAIL. - * + * - ESP_FAIL if frame is NULL, panid is NULL, or frame type is invalid. */ esp_err_t ieee802154_frame_get_src_panid(const uint8_t *frame, uint8_t *panid); + +/** + * @brief Check whether the given frame is a MAC Data Request command. + * + * @param[in] frame Pointer to the raw MAC frame buffer. + * + * @return true if the frame is a Data Request command, false otherwise. + */ +bool ieee802154_is_data_request(const uint8_t *frame); #ifdef __cplusplus } #endif diff --git a/components/ieee802154/private_include/esp_ieee802154_pib.h b/components/ieee802154/private_include/esp_ieee802154_pib.h index c6edcde134..2a2063aac5 100644 --- a/components/ieee802154/private_include/esp_ieee802154_pib.h +++ b/components/ieee802154/private_include/esp_ieee802154_pib.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -8,6 +8,7 @@ #include #include +#include "sdkconfig.h" #include "hal/ieee802154_ll.h" #include "esp_ieee802154_frame.h" #include "esp_ieee802154_types.h" @@ -28,7 +29,7 @@ typedef struct { bool rx_when_idle; /*!< A flag indicates the device is rx on when idle or not */ esp_ieee802154_txpower_table_t power_table; /*!< The power table configuration */ uint8_t channel; /*!< Channel configuration */ - ieee802154_ll_pending_mode_t pending_mode; /*!< Pending mode configuration */ + ieee802154_ll_pending_mode_t pending_mode[CONFIG_IEEE802154_INTERFACE_NUM]; /*!< Pending mode configuration */ int8_t cca_threshold; /*!< CCA threshold */ ieee802154_ll_cca_mode_t cca_mode; /*!< CCA mode */ } ieee802154_pib_t; @@ -160,7 +161,7 @@ void ieee802154_pib_set_cca_threshold(int8_t cca_threshold); * @return * - The CCA threshold has been set in the PIB. */ - int8_t ieee802154_pib_get_cca_threshold(void); +int8_t ieee802154_pib_get_cca_threshold(void); /** * @brief Set the CCA mode to the PIB. @@ -248,7 +249,7 @@ bool ieee802154_pib_get_coordinator(void); * @param[in] pending_mode The pending mode. * */ -void ieee802154_pib_set_pending_mode(ieee802154_ll_pending_mode_t pending_mode); +void ieee802154_pib_set_pending_mode(esp_ieee802154_multipan_index_t inf_index, ieee802154_ll_pending_mode_t pending_mode); /** * @brief Get the pending mode from the PIB. @@ -256,21 +257,21 @@ void ieee802154_pib_set_pending_mode(ieee802154_ll_pending_mode_t pending_mode); * @return * - The pending mode has been set in the PIB. */ -ieee802154_ll_pending_mode_t ieee802154_pib_get_pending_mode(void); +ieee802154_ll_pending_mode_t ieee802154_pib_get_pending_mode(esp_ieee802154_multipan_index_t inf_index); /** * @brief Configure the radio mode when the radio is going to enter idle to the PIB. * - * @param[in] enable True for continuing to receive when the radio is going to enter ilde, otherwise false. + * @param[in] enable True for continuing to receive when the radio is going to enter idle, otherwise false. * */ void ieee802154_pib_set_rx_when_idle(bool enable); /** - * @brief Get the radio mode when the radio is going to enter ilde to the PIB. + * @brief Get the radio mode when the radio is going to enter idle to the PIB. * * @return - * - True for continuing to receive when the radio is going to enter ilde, otherwise false. + * - True for continuing to receive when the radio is going to enter idle, otherwise false. * */ bool ieee802154_pib_get_rx_when_idle(void); diff --git a/components/ieee802154/private_include/esp_ieee802154_util.h b/components/ieee802154/private_include/esp_ieee802154_util.h index 5efdac1c26..f2881607c2 100644 --- a/components/ieee802154/private_include/esp_ieee802154_util.h +++ b/components/ieee802154/private_include/esp_ieee802154_util.h @@ -23,6 +23,12 @@ extern "C" { #define IEEE802154_OQPSK_2P4G_CHANNEL_MIN 11 #define IEEE802154_OQPSK_2P4G_CHANNEL_MAX 26 +#define IEEE802154_RETURN_ON_FALSE_SILENTLY(a, ret) do { \ + if (unlikely(!(a))) { \ + return ret; \ + } \ +} while(0) + static inline bool ieee802154_is_valid_channel(uint8_t channel) { return ((channel <= IEEE802154_OQPSK_2P4G_CHANNEL_MAX) && (channel >= IEEE802154_OQPSK_2P4G_CHANNEL_MIN)); diff --git a/components/openthread/CMakeLists.txt b/components/openthread/CMakeLists.txt index f28dd01769..5ec2fa9bcf 100644 --- a/components/openthread/CMakeLists.txt +++ b/components/openthread/CMakeLists.txt @@ -5,14 +5,12 @@ if(${idf_target} STREQUAL "linux") endif() if(CONFIG_OPENTHREAD_ENABLED OR CONFIG_IDF_DOC_BUILD OR CONFIG_OPENTHREAD_SPINEL_ONLY) - set(public_include_dirs "include" "openthread/include") endif() if(CONFIG_OPENTHREAD_ENABLED) - set(private_include_dirs "openthread/examples/platforms" "openthread/src" @@ -24,243 +22,18 @@ if(CONFIG_OPENTHREAD_ENABLED) "openthread/src/ncp" "private_include") - set(src_dirs - "src" - "src/port" - "openthread/examples/platforms/utils" - "openthread/src/core/api" - "openthread/src/core/common" - "openthread/src/core/crypto" - "openthread/src/core/diags" - "openthread/src/core/instance" - "openthread/src/core/mac" - "openthread/src/core/radio" - "openthread/src/core/thread" - "openthread/src/core/utils" - "openthread/src/lib/hdlc" - "openthread/src/lib/spinel") - - set(exclude_srcs - "openthread/examples/platforms/utils/logging_rtt.c" - "openthread/examples/platforms/utils/soft_source_match_table.c" - "openthread/src/core/instance/extension_example.cpp" - "openthread/src/core/crypto/crypto_platform_mbedtls.cpp" - "openthread/src/core/api/random_crypto_api.cpp" - ) - - - if(CONFIG_OPENTHREAD_FTD OR CONFIG_OPENTHREAD_MTD) - list(APPEND src_dirs - "openthread/src/core/backbone_router" - "openthread/src/core/coap" - "openthread/src/core/meshcop" - "openthread/src/core/net" - "openthread/src/lib/platform") - - if(CONFIG_OPENTHREAD_CLI) - list(APPEND src_dirs - "openthread/examples/apps/cli" - "openthread/src/cli") - - list(APPEND exclude_srcs - "openthread/examples/apps/cli/main.c") - endif() - - elseif(CONFIG_OPENTHREAD_RADIO) - list(APPEND src_dirs - "openthread/src/ncp" - "openthread/examples/apps/ncp") - - list(APPEND exclude_srcs - "src/port/esp_openthread_state.c" - "openthread/examples/apps/ncp/main.c" - "openthread/src/core/api/backbone_router_api.cpp" - "openthread/src/core/api/child_supervision_api.cpp" - "openthread/src/core/api/dataset_api.cpp" - "openthread/src/core/api/dns_api.cpp" - "openthread/src/core/api/entropy_api.cpp" - "openthread/src/core/api/heap_api.cpp" - "openthread/src/core/api/icmp6_api.cpp" - "openthread/src/core/api/ip6_api.cpp" - "openthread/src/core/api/link_api.cpp" - "openthread/src/core/api/link_metrics_api.cpp" - "openthread/src/core/api/message_api.cpp" - "openthread/src/core/api/nat64_api.cpp" - "openthread/src/core/api/netdata_api.cpp" - "openthread/src/core/api/netdiag_api.cpp" - "openthread/src/core/api/network_time_api.cpp" - "openthread/src/core/api/random_crypto_api.cpp" - "openthread/src/core/api/tcp_api.cpp" - "openthread/src/core/api/udp_api.cpp" - "openthread/src/core/common/heap.cpp" - "openthread/src/core/common/heap_string.cpp" - "openthread/src/core/common/notifier.cpp" - "openthread/src/core/common/settings.cpp" - "openthread/src/core/common/time_ticker.cpp" - "openthread/src/core/mac/channel_mask.cpp" - "openthread/src/core/mac/data_poll_handler.cpp" - "openthread/src/core/mac/data_poll_sender.cpp" - "openthread/src/core/mac/mac.cpp" - "openthread/src/core/mac/mac_filter.cpp" - "openthread/src/core/mac/mac_links.cpp" - "openthread/src/core/thread/announce_begin_server.cpp" - "openthread/src/core/thread/announce_sender.cpp" - "openthread/src/core/thread/address_resolver.cpp" - "openthread/src/core/thread/child.cpp" - "openthread/src/core/thread/child_supervision.cpp" - "openthread/src/core/thread/csl_tx_scheduler.cpp" - "openthread/src/core/thread/discover_scanner.cpp" - "openthread/src/core/thread/energy_scan_server.cpp" - "openthread/src/core/thread/indirect_sender.cpp" - "openthread/src/core/thread/key_manager.cpp" - "openthread/src/core/thread/link_metrics.cpp" - "openthread/src/core/thread/lowpan.cpp" - "openthread/src/core/thread/mesh_forwarder.cpp" - "openthread/src/core/thread/mesh_forwarder_ftd.cpp" - "openthread/src/core/thread/mesh_forwarder_mtd.cpp" - "openthread/src/core/thread/message_framer.cpp" - "openthread/src/core/thread/mle.cpp" - "openthread/src/core/thread/mle_ftd.cpp" - "openthread/src/core/thread/mle_p2p.cpp" - "openthread/src/core/thread/mle_router.cpp" - "openthread/src/core/thread/mle_types.cpp" - "openthread/src/core/thread/neighbor.cpp" - "openthread/src/core/thread/neighbor_table.cpp" - "openthread/src/core/thread/network_data.cpp" - "openthread/src/core/thread/network_data_leader.cpp" - "openthread/src/core/thread/network_data_leader_ftd.cpp" - "openthread/src/core/thread/network_data_types.cpp" - "openthread/src/core/thread/network_data_service.cpp" - "openthread/src/core/thread/network_diagnostic.cpp" - "openthread/src/core/thread/network_diagnostic_tlvs.cpp" - "openthread/src/core/thread/panid_query_server.cpp" - "openthread/src/core/thread/router.cpp" - "openthread/src/core/thread/thread_netif.cpp" - "openthread/src/core/thread/time_sync_service.cpp" - "openthread/src/core/thread/tmf.cpp" - "openthread/src/core/thread/topology.cpp" - "openthread/src/core/utils/child_supervision.cpp") - endif() - - if(CONFIG_OPENTHREAD_RADIO_NATIVE) - list(APPEND exclude_srcs - "src/port/esp_openthread_radio_spinel.cpp" - "src/port/esp_spi_spinel_interface.cpp" - "src/port/esp_uart_spinel_interface.cpp" - ) - elseif(CONFIG_OPENTHREAD_RADIO_SPINEL_UART OR CONFIG_OPENTHREAD_RADIO_SPINEL_SPI) - list(APPEND exclude_srcs - "src/port/esp_openthread_radio.c" - "src/port/esp_openthread_sleep.c") - elseif(CONFIG_OPENTHREAD_RADIO_154_NONE) - list(APPEND exclude_srcs - "src/port/esp_openthread_radio_spinel.cpp" - "src/port/esp_spi_spinel_interface.cpp" - "src/port/esp_uart_spinel_interface.cpp" - "src/port/esp_openthread_radio.c" - "src/port/esp_openthread_sleep.c" - ) - endif() - - if(NOT CONFIG_OPENTHREAD_RADIO_TREL) - list(APPEND exclude_srcs - "src/port/esp_openthread_trel.c") - endif() - - if(CONFIG_OPENTHREAD_BORDER_ROUTER) - list(APPEND src_dirs - "openthread/src/core/border_router") - endif() - - if(CONFIG_OPENTHREAD_FTD) - set_source_files_properties("openthread/src/core/net/srp_server.cpp" - PROPERTIES COMPILE_FLAGS - -Wno-maybe-uninitialized) - endif() - - if(CONFIG_OPENTHREAD_NCP_VENDOR_HOOK) - list(APPEND src_dirs - "src/ncp") - if(CONFIG_OPENTHREAD_RCP_UART OR CONFIG_OPENTHREAD_RCP_USB_SERIAL_JTAG) - list(APPEND exclude_srcs - "src/ncp/esp_openthread_ncp_spi.cpp") - elseif(CONFIG_OPENTHREAD_RCP_SPI) - list(APPEND exclude_srcs - "src/ncp/esp_openthread_ncp_hdlc.cpp") - endif() - endif() - - if(NOT CONFIG_OPENTHREAD_DNS64_CLIENT) - list(APPEND exclude_srcs - "src/esp_openthread_dns64.c") - endif() - - if(NOT CONFIG_FREERTOS_USE_TICKLESS_IDLE) - list(APPEND exclude_srcs - "src/port/esp_openthread_sleep.c") - endif() - - if(NOT CONFIG_OPENTHREAD_PLATFORM_MSGPOOL_MANAGEMENT) - list(APPEND exclude_srcs - "src/port/esp_openthread_messagepool.c") - endif() - - if(NOT CONFIG_OPENTHREAD_RCP_SPINEL_CONSOLE) - list(APPEND exclude_srcs - "src/ncp/esp_openthread_ncp_console.cpp") - endif() - if(CONFIG_OPENTHREAD_FTD) + include(srcs_ftd_mtd.cmake) set(device_type "OPENTHREAD_FTD=1") elseif(CONFIG_OPENTHREAD_MTD) + include(srcs_ftd_mtd.cmake) set(device_type "OPENTHREAD_MTD=1") elseif(CONFIG_OPENTHREAD_RADIO) + include(srcs_radio.cmake) set(device_type "OPENTHREAD_RADIO=1") endif() elseif(CONFIG_OPENTHREAD_SPINEL_ONLY) - - set(src_dirs - "src/spinel" - "src/port" - "openthread/src/lib/spinel" - "openthread/src/lib/hdlc" - "openthread/src/lib/platform" - "openthread/src/core/api" - "openthread/src/core/common" - "openthread/src/core/mac") - - set(private_include_dirs - "private_include" - "openthread/src" - "openthread/src/core" - "openthread/src/include" - "openthread/src/lib" - "openthread/src/lib/hdlc" - "openthread/src/lib/spinel") - - file(GLOB_RECURSE exclude_srcs_list - "src/port/*" - "openthread/src/core/api/*.cpp" - "openthread/src/core/common/*" - "openthread/src/core/mac/*") - - list(REMOVE_ITEM exclude_srcs_list - "${CMAKE_CURRENT_SOURCE_DIR}/src/port/esp_openthread_alarm.c" - "${CMAKE_CURRENT_SOURCE_DIR}/src/port/esp_openthread_logging.c" - "${CMAKE_CURRENT_SOURCE_DIR}/openthread/src/core/api/error_api.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/openthread/src/core/api/logging_api.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/openthread/src/core/common/error.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/openthread/src/core/common/error.hpp" - "${CMAKE_CURRENT_SOURCE_DIR}/openthread/src/core/common/log.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/openthread/src/core/common/log.hpp" - "${CMAKE_CURRENT_SOURCE_DIR}/openthread/src/core/common/logging.hpp" - "${CMAKE_CURRENT_SOURCE_DIR}/openthread/src/core/common/string.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/openthread/src/core/common/string.hpp" - "${CMAKE_CURRENT_SOURCE_DIR}/openthread/src/core/mac/mac_frame.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/openthread/src/core/mac/mac_frame.hpp") - - list(APPEND exclude_srcs ${exclude_srcs_list}) - + include(srcs_spinel.cmake) endif() if(CONFIG_OPENTHREAD_HEADER_CUSTOM) @@ -287,14 +60,49 @@ string(CONCAT OT_FULL_VERSION_STRING "${IDF_VERSION_FOR_OPENTHREAD_PACKAGE}-${OPENTHREAD_VERSION}\; " "${CONFIG_OPENTHREAD_PLATFORM_INFO}\; ${OT_BUILD_TIMESTAMP}") -idf_component_register(SRC_DIRS "${src_dirs}" - EXCLUDE_SRCS "${exclude_srcs}" - INCLUDE_DIRS "${public_include_dirs}" - PRIV_INCLUDE_DIRS "${private_include_dirs}" - REQUIRES esp_netif lwip esp_driver_uart esp_driver_gpio esp_driver_spi esp_driver_usb_serial_jtag - LDFRAGMENTS linker.lf - PRIV_REQUIRES console esp_coex esp_event esp_partition esp_timer - ieee802154 mbedtls nvs_flash) +# Register component based on mode +if(CONFIG_OPENTHREAD_FTD OR CONFIG_OPENTHREAD_MTD) + # FTD/MTD modes use directory-based collection from srcs_ftd_mtd.cmake + idf_component_register(SRC_DIRS "${src_dirs}" + EXCLUDE_SRCS "${exclude_srcs}" + INCLUDE_DIRS "${public_include_dirs}" + PRIV_INCLUDE_DIRS "${private_include_dirs}" + REQUIRES esp_netif lwip esp_driver_uart esp_driver_gpio esp_driver_spi + esp_driver_usb_serial_jtag + LDFRAGMENTS linker.lf + PRIV_REQUIRES console esp_coex esp_event esp_partition esp_timer + ieee802154 mbedtls nvs_flash esp_psram) +elseif(CONFIG_OPENTHREAD_RADIO) + # RCP mode uses explicit source list from srcs_radio.cmake + idf_component_register(SRCS ${rcp_srcs} + INCLUDE_DIRS "${public_include_dirs}" + PRIV_INCLUDE_DIRS "${private_include_dirs}" + REQUIRES esp_netif lwip esp_driver_uart esp_driver_gpio esp_driver_spi + esp_driver_usb_serial_jtag + LDFRAGMENTS linker.lf + PRIV_REQUIRES console esp_coex esp_event esp_partition esp_timer + ieee802154 mbedtls nvs_flash esp_psram) +elseif(CONFIG_OPENTHREAD_SPINEL_ONLY) + # SPINEL_ONLY mode uses explicit source list from srcs_spinel.cmake + idf_component_register(SRCS ${spinel_srcs} + INCLUDE_DIRS "${public_include_dirs}" + PRIV_INCLUDE_DIRS "${private_include_dirs}" + REQUIRES esp_netif lwip esp_driver_uart esp_driver_gpio esp_driver_spi + esp_driver_usb_serial_jtag + LDFRAGMENTS linker.lf + PRIV_REQUIRES console esp_coex esp_event esp_partition esp_timer + ieee802154 mbedtls nvs_flash esp_psram) +else() + # This is not supposed to happen, but in older versions of IDF components couldn't be selectively + # enabled based on KConfig options so some examples just included openthread for all configs. + idf_component_register(INCLUDE_DIRS "${public_include_dirs}" + PRIV_INCLUDE_DIRS "${private_include_dirs}" + REQUIRES esp_netif lwip esp_driver_uart esp_driver_gpio esp_driver_spi + esp_driver_usb_serial_jtag + LDFRAGMENTS linker.lf + PRIV_REQUIRES console esp_coex esp_event esp_partition esp_timer + ieee802154 mbedtls nvs_flash esp_psram) +endif() if(CONFIG_OPENTHREAD_RADIO_TREL) idf_component_optional_requires(PRIVATE espressif__mdns) diff --git a/components/openthread/Kconfig b/components/openthread/Kconfig index c6bf703d8c..2bdf97730b 100644 --- a/components/openthread/Kconfig +++ b/components/openthread/Kconfig @@ -436,6 +436,7 @@ menu "OpenThread" config OPENTHREAD_PLATFORM_MSGPOOL_MANAGEMENT bool 'Allocate message pool buffer from PSRAM' + depends on OPENTHREAD_PLATFORM_MALLOC_CAP_SPIRAM default y help If enabled, the message pool is managed by platform defined logic. @@ -622,5 +623,27 @@ menu "OpenThread" information whenever an OpenThread assert occurs. This can help developers analyze unexpected failures by providing additional MAC layer context. + config OPENTHREAD_TASK_BLOCK_MONITOR + depends on OPENTHREAD_DEBUG + bool "Enable OpenThread Task Block Monitor" + default n + help + Enable monitoring of OpenThread tasks to detect if a task is blocked + and unable to continue its normal execution loop. Useful for debugging + issues where tasks stop progressing due to deadlocks or resource waits. + On RISC-V targets, in order to get meaningful backtraces when a task is + detected as blocked, ESP_SYSTEM_USE_FRAME_POINTER must be enabled in + ESP System Settings -> Backtracing method. + + config OPENTHREAD_TASK_BLOCK_MONITOR_TIMEOUT + depends on OPENTHREAD_TASK_BLOCK_MONITOR + int "Task Block Monitor Timeout (seconds)" + range 1 60 + default 20 + help + Set the timeout (in seconds) for detecting a blocked task. + The timer is started at the beginning of each task main loop + and stopped at the end. If the timer expires, the task is considered blocked. + endmenu diff --git a/components/openthread/lib b/components/openthread/lib index 43142da1e3..c560c3c990 160000 --- a/components/openthread/lib +++ b/components/openthread/lib @@ -1 +1 @@ -Subproject commit 43142da1e3504cf2a1a5fc5b30bfa49dc1ef26e4 +Subproject commit c560c3c990560e60f835d1bb325e4be3e1cc2e57 diff --git a/components/openthread/linker.lf b/components/openthread/linker.lf index 58c830c8e7..4be6622a08 100644 --- a/components/openthread/linker.lf +++ b/components/openthread/linker.lf @@ -2,13 +2,13 @@ archive: libopenthread.a entries: if OPENTHREAD_TIMING_OPTIMIZATION = y: - link_metrics (noflash) link_quality (noflash) mac_frame (noflash) radio (noflash) sub_mac (noflash) if OPENTHREAD_TIMING_OPTIMIZATION = y && OPENTHREAD_RADIO = n: + link_metrics (noflash) mesh_forwarder (noflash) csl_tx_scheduler (noflash) mac (noflash) diff --git a/components/openthread/openthread b/components/openthread/openthread index 03933d9b76..9b887f6bd1 160000 --- a/components/openthread/openthread +++ b/components/openthread/openthread @@ -1 +1 @@ -Subproject commit 03933d9b76cb7075cc29a6648b42b217971f82a5 +Subproject commit 9b887f6bd1437170fc9bcb726f32185ccb3cb841 diff --git a/components/openthread/private_include/esp_openthread_debug.h b/components/openthread/private_include/esp_openthread_debug.h new file mode 100644 index 0000000000..8e6ea8f520 --- /dev/null +++ b/components/openthread/private_include/esp_openthread_debug.h @@ -0,0 +1,55 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if CONFIG_OPENTHREAD_TASK_BLOCK_MONITOR +/** + * @brief This function creates the OpenThread task block monitor. + * The monitor task periodically checks whether the OpenThread mainloop + * has been blocked for more than `CONFIG_OPENTHREAD_TASK_BLOCK_MONITOR_TIMEOUT` + * seconds and prints backtraces when a block is detected. + * + * @return + * - ESP_OK on success + * - ESP_FAIL if the monitor task cannot be created + * + */ +esp_err_t esp_openthread_task_block_monitor_create(void); + +/** + * @brief This function deletes the OpenThread task block monitor. + * + * @return + * - ESP_OK on success + * + */ +esp_err_t esp_openthread_task_block_monitor_delete(void); + +/** + * @brief This function enables or disables the OpenThread task block monitor. + * + * @note When enabled, the latest mainloop activity timestamp is updated and the + * monitor task starts checking for blocking. When disabled, monitoring is + * temporarily stopped. + * + * @param[in] enable True to enable the monitor, false to disable it. + * + */ +void esp_openthread_task_block_monitor_set(bool enable); +#endif // CONFIG_OPENTHREAD_TASK_BLOCK_MONITOR + +#ifdef __cplusplus +} +#endif diff --git a/components/openthread/private_include/openthread-core-esp32x-ftd-config.h b/components/openthread/private_include/openthread-core-esp32x-ftd-config.h index 0f776479ce..e3548c8a93 100644 --- a/components/openthread/private_include/openthread-core-esp32x-ftd-config.h +++ b/components/openthread/private_include/openthread-core-esp32x-ftd-config.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -99,6 +99,16 @@ #define OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE 0 #endif // CONFIG_OPENTHREAD_RADIO_TREL +#if CONFIG_OPENTHREAD_RADIO_TREL +/** + * @def OPENTHREAD_CONFIG_TREL_USE_HEAP_ENABLE + * + * Set to 1 to allow TREL modules to use heap allocated objects (e.g. for the TREL peer table). + * + */ +#define OPENTHREAD_CONFIG_TREL_USE_HEAP_ENABLE 1 +#endif // CONFIG_OPENTHREAD_RADIO_TREL + /** * @def OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE * @@ -879,7 +889,7 @@ #endif /** - * @OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_MARK_ECN_INTERVAL + * @def OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_MARK_ECN_INTERVAL * * Specifies the time-in-queue threshold interval in milliseconds to mark ECN on a message if it is ECN-capable or * drop the message if not ECN-capable. @@ -888,10 +898,34 @@ #define OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_MARK_ECN_INTERVAL 1000 #endif +/** + * @def OPENTHREAD_CONFIG_PLATFORM_MAC_KEYS_EXPORTABLE_ENABLE + * + * Define to 1 if you want to make MAC keys exportable. + */ +#define OPENTHREAD_CONFIG_PLATFORM_MAC_KEYS_EXPORTABLE_ENABLE 1 + +/** + * @def OPENTHREAD_CONFIG_CRYPTO_LIB + * + * Selects the crypto backend library for OpenThread. + * + * There are several options available + * - @sa OPENTHREAD_CONFIG_CRYPTO_LIB_MBEDTLS + * - @sa OPENTHREAD_CONFIG_CRYPTO_LIB_PSA + * - @sa OPENTHREAD_CONFIG_CRYPTO_LIB_PLATFORM + */ +#define OPENTHREAD_CONFIG_CRYPTO_LIB OPENTHREAD_CONFIG_CRYPTO_LIB_PSA +// TODO: Remove when https://github.com/openthread/openthread/pull/12638 is merged +#define OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE 1 + +/** + * @def OPENTHREAD_CONFIG_MESHCOP_STEERING_DATA_API_ENABLE + * + * Define as 1 to enable the MeshCoP Steering Data public APIs (in `openthread/steering_data.h`). + */ +#define OPENTHREAD_CONFIG_MESHCOP_STEERING_DATA_API_ENABLE 1 + #ifndef OPENTHREAD_CONFIG_THREAD_VERSION #define OPENTHREAD_CONFIG_THREAD_VERSION OT_THREAD_VERSION_1_4 #endif - -#define OPENTHREAD_CONFIG_PLATFORM_MAC_KEYS_EXPORTABLE_ENABLE 1 - -#define OPENTHREAD_CONFIG_CRYPTO_LIB OPENTHREAD_CONFIG_CRYPTO_LIB_PSA diff --git a/components/openthread/private_include/openthread-core-esp32x-mtd-config.h b/components/openthread/private_include/openthread-core-esp32x-mtd-config.h index c278420b7f..27725a7ade 100644 --- a/components/openthread/private_include/openthread-core-esp32x-mtd-config.h +++ b/components/openthread/private_include/openthread-core-esp32x-mtd-config.h @@ -494,6 +494,34 @@ #define OPENTHREAD_CONFIG_PARENT_SEARCH_RSS_THRESHOLD CONFIG_OPENTHREAD_PARENT_SEARCH_RSS_THRESHOLD #endif +/** + * @def OPENTHREAD_CONFIG_PLATFORM_MAC_KEYS_EXPORTABLE_ENABLE + * + * Define to 1 if you want to make MAC keys exportable. + */ #define OPENTHREAD_CONFIG_PLATFORM_MAC_KEYS_EXPORTABLE_ENABLE 1 +/** + * @def OPENTHREAD_CONFIG_CRYPTO_LIB + * + * Selects the crypto backend library for OpenThread. + * + * There are several options available + * - @sa OPENTHREAD_CONFIG_CRYPTO_LIB_MBEDTLS + * - @sa OPENTHREAD_CONFIG_CRYPTO_LIB_PSA + * - @sa OPENTHREAD_CONFIG_CRYPTO_LIB_PLATFORM + */ #define OPENTHREAD_CONFIG_CRYPTO_LIB OPENTHREAD_CONFIG_CRYPTO_LIB_PSA +// TODO: Remove when https://github.com/openthread/openthread/pull/12638 is merged +#define OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE 1 + +/** + * @def OPENTHREAD_CONFIG_MESHCOP_STEERING_DATA_API_ENABLE + * + * Define as 1 to enable the MeshCoP Steering Data public APIs (in `openthread/steering_data.h`). + */ +#define OPENTHREAD_CONFIG_MESHCOP_STEERING_DATA_API_ENABLE 1 + +#ifndef OPENTHREAD_CONFIG_THREAD_VERSION +#define OPENTHREAD_CONFIG_THREAD_VERSION OT_THREAD_VERSION_1_4 +#endif diff --git a/components/openthread/sbom_openthread.yml b/components/openthread/sbom_openthread.yml index d0dc17ff5e..8a79402605 100644 --- a/components/openthread/sbom_openthread.yml +++ b/components/openthread/sbom_openthread.yml @@ -5,4 +5,4 @@ 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: 03933d9b76cb7075cc29a6648b42b217971f82a5 +hash: 9b887f6bd1437170fc9bcb726f32185ccb3cb841 diff --git a/components/openthread/src/esp_openthread.cpp b/components/openthread/src/esp_openthread.cpp index 3379b318d4..b667e9bcad 100644 --- a/components/openthread/src/esp_openthread.cpp +++ b/components/openthread/src/esp_openthread.cpp @@ -18,6 +18,7 @@ #include "esp_openthread_platform.h" #include "esp_openthread_sleep.h" #include "esp_openthread_state.h" +#include "esp_openthread_debug.h" #include "esp_openthread_task_queue.h" #include "esp_openthread_types.h" #include "freertos/FreeRTOS.h" @@ -27,7 +28,6 @@ #include "openthread/netdata.h" #include "openthread/tasklet.h" #include "openthread/thread.h" -#include #if CONFIG_OPENTHREAD_FTD #include "openthread/dataset_ftd.h" @@ -186,6 +186,10 @@ esp_err_t esp_openthread_launch_mainloop(void) esp_err_t error = ESP_OK; s_ot_mainloop_running = true; +#if CONFIG_OPENTHREAD_TASK_BLOCK_MONITOR + ESP_ERROR_CHECK(esp_openthread_task_block_monitor_create()); +#endif + while (s_ot_mainloop_running) { FD_ZERO(&mainloop.read_fds); FD_ZERO(&mainloop.write_fds); @@ -206,8 +210,14 @@ esp_err_t esp_openthread_launch_mainloop(void) #endif /* CONFIG_FREERTOS_USE_TICKLESS_IDLE && CONFIG_OPENTHREAD_RADIO_NATIVE */ esp_openthread_lock_release(); - if (select(mainloop.max_fd + 1, &mainloop.read_fds, &mainloop.write_fds, &mainloop.error_fds, - &mainloop.timeout) >= 0) { +#if CONFIG_OPENTHREAD_TASK_BLOCK_MONITOR + esp_openthread_task_block_monitor_set(false); +#endif + int result = select(mainloop.max_fd + 1, &mainloop.read_fds, &mainloop.write_fds, &mainloop.error_fds, &mainloop.timeout); +#if CONFIG_OPENTHREAD_TASK_BLOCK_MONITOR + esp_openthread_task_block_monitor_set(true); +#endif + if (result >= 0) { esp_openthread_lock_acquire(portMAX_DELAY); error = esp_openthread_platform_process(instance, &mainloop); while (otTaskletsArePending(instance)) { @@ -224,6 +234,9 @@ esp_err_t esp_openthread_launch_mainloop(void) break; } } +#if CONFIG_OPENTHREAD_TASK_BLOCK_MONITOR + ESP_ERROR_CHECK(esp_openthread_task_block_monitor_delete()); +#endif return error; } diff --git a/components/openthread/src/esp_openthread_netif_glue.c b/components/openthread/src/esp_openthread_netif_glue.c index ff37ce1fb3..c49a260407 100644 --- a/components/openthread/src/esp_openthread_netif_glue.c +++ b/components/openthread/src/esp_openthread_netif_glue.c @@ -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 */ @@ -183,7 +183,21 @@ static esp_err_t openthread_netif_transmit(void *handle, void *buffer, size_t le otError ot_error = OT_ERROR_NONE; esp_openthread_task_switching_lock_acquire(portMAX_DELAY); - otMessage *message = otIp6NewMessage(esp_openthread_get_instance(), NULL); + + otMessageSettings settings = {}; + switch (otThreadGetDeviceRole(esp_openthread_get_instance())) + { + case OT_DEVICE_ROLE_DISABLED: + settings.mLinkSecurityEnabled = false; + settings.mPriority = OT_MESSAGE_PRIORITY_LOW; + break; + default: + settings.mLinkSecurityEnabled = true; + settings.mPriority = OT_MESSAGE_PRIORITY_NORMAL; + break; + } + + otMessage *message = otIp6NewMessage(esp_openthread_get_instance(), &settings); if (message == NULL) { ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to allocate OpenThread message"); ExitNow(error = ESP_ERR_NO_MEM); diff --git a/components/openthread/src/esp_openthread_platform.cpp b/components/openthread/src/esp_openthread_platform.cpp index f16891d69c..c00c48ed55 100644 --- a/components/openthread/src/esp_openthread_platform.cpp +++ b/components/openthread/src/esp_openthread_platform.cpp @@ -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 */ @@ -179,11 +179,21 @@ esp_err_t esp_openthread_platform_deinit(void) esp_openthread_task_queue_deinit(); esp_openthread_radio_deinit(); - if (get_host_connection_mode() == HOST_CONNECTION_MODE_RCP_SPI){ + esp_openthread_host_connection_mode_t host_mode = get_host_connection_mode(); + switch (host_mode) { +#if CONFIG_OPENTHREAD_RCP_SPI + case HOST_CONNECTION_MODE_RCP_SPI: esp_openthread_spi_slave_deinit(); - } else if (get_host_connection_mode() == HOST_CONNECTION_MODE_CLI_UART || - get_host_connection_mode() == HOST_CONNECTION_MODE_RCP_UART) { + break; +#endif +#if CONFIG_OPENTHREAD_RCP_UART || CONFIG_OPENTHREAD_CONSOLE_TYPE_UART + case HOST_CONNECTION_MODE_RCP_UART: + case HOST_CONNECTION_MODE_CLI_UART: esp_openthread_uart_deinit(); + break; +#endif + default: + break; } esp_openthread_lock_deinit(); @@ -217,6 +227,6 @@ uint32_t esp_openthread_get_alloc_caps(void) #if CONFIG_OPENTHREAD_PLATFORM_MALLOC_CAP_SPIRAM (MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); #else - (MALLOC_CAP_DEFAULT | MALLOC_CAP_8BIT); + (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); #endif } diff --git a/components/openthread/src/port/esp_openthread_debug.c b/components/openthread/src/port/esp_openthread_debug.c new file mode 100644 index 0000000000..a35d04af4e --- /dev/null +++ b/components/openthread/src/port/esp_openthread_debug.c @@ -0,0 +1,133 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sdkconfig.h" + +#include +#include + +#include "esp_check.h" +#include "esp_log.h" +#include "esp_timer.h" +#include "esp_debug_helpers.h" +#include "esp_openthread_debug.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#if CONFIG_OPENTHREAD_TASK_BLOCK_MONITOR + +#include "esp_private/eh_frame_parser.h" +#include "esp_private/esp_system_attr.h" +#include "esp_private/esp_cpu_internal.h" +#include "esp_private/fp_unwind.h" +#include "esp_private/panic_internal.h" +#include "freertos/freertos_debug.h" + +static const char *TAG = "OT_DEBUG"; +static TaskHandle_t s_ot_task_block_monitor_task = NULL; +static atomic_llong s_ot_task_block_monitor_latest_time = 0; +static atomic_bool s_ot_task_block_monitor_active = false; +static const int64_t s_ot_task_block_monitor_timeout = CONFIG_OPENTHREAD_TASK_BLOCK_MONITOR_TIMEOUT * 1000000; +static bool is_backtrace_printed = false; + +#if CONFIG_IDF_TARGET_ARCH_RISCV +static esp_err_t ESP_SYSTEM_IRAM_ATTR esp_task_backtrace_print(TaskHandle_t target_task) +{ + TaskSnapshot_t snapshot = {}; + void *frame = NULL; + + vTaskGetSnapshot(target_task, &snapshot); + + frame = snapshot.pxTopOfStack; + char *name = pcTaskGetName(target_task); + + ESP_LOGI(TAG, "Target Task Backtrace: %s", name ? name : "No Name"); + +#if CONFIG_ESP_SYSTEM_USE_EH_FRAME + esp_eh_frame_print_backtrace(frame); +#elif CONFIG_ESP_SYSTEM_USE_FRAME_POINTER + esp_fp_print_backtrace(frame); +#else + esp_cpu_frame_t backtrace_frame = {}; + const int current_core = xPortGetCoreID(); + memset(&backtrace_frame, 0, sizeof(esp_cpu_frame_t)); + memcpy(&backtrace_frame, frame, sizeof(esp_cpu_frame_t)); + + panic_prepare_frame_from_ctx(&backtrace_frame); + panic_print_registers(&backtrace_frame, current_core); + + esp_rom_printf("\r\n"); + esp_rom_printf("Please enable CONFIG_ESP_SYSTEM_USE_FRAME_POINTER option to have a full backtrace.\r\n"); +#endif + + return ESP_OK; +} +#endif + +static void ot_debug_monitor_task(void *arg) +{ + (void)arg; + + while (true) { + if (atomic_load(&s_ot_task_block_monitor_active)) { + int64_t elapsed = esp_timer_get_time() - atomic_load(&s_ot_task_block_monitor_latest_time); + if (elapsed > s_ot_task_block_monitor_timeout && !is_backtrace_printed) { + is_backtrace_printed = true; + ESP_LOGW(TAG, + "OpenThread mainloop blocked for more than %d seconds, printing all tasks backtrace", + CONFIG_OPENTHREAD_TASK_BLOCK_MONITOR_TIMEOUT); +#if CONFIG_IDF_TARGET_ARCH_XTENSA + esp_backtrace_print_all_tasks(10); +#elif CONFIG_IDF_TARGET_ARCH_RISCV + TaskHandle_t handle = xTaskGetHandle(CONFIG_OPENTHREAD_TASK_NAME); + if (handle) { + esp_task_backtrace_print(handle); + } +#endif + } else if (elapsed <= s_ot_task_block_monitor_timeout) { + is_backtrace_printed = false; + } + } + + vTaskDelay(pdMS_TO_TICKS(1000)); + } +} + +esp_err_t esp_openthread_task_block_monitor_create(void) +{ + ESP_RETURN_ON_FALSE(s_ot_task_block_monitor_task == NULL, ESP_OK, TAG, "Task block monitor already created"); + + BaseType_t ret = xTaskCreate(ot_debug_monitor_task, "ot_task_monitor", 3072, NULL, CONFIG_OPENTHREAD_TASK_PRIORITY, &s_ot_task_block_monitor_task); + ESP_RETURN_ON_FALSE(ret == pdPASS, ESP_FAIL, TAG, "Failed to create OpenThread task block monitor"); + + ESP_LOGI(TAG, "OpenThread task block monitor created"); + esp_openthread_task_block_monitor_set(true); + return ESP_OK; +} + +esp_err_t esp_openthread_task_block_monitor_delete(void) +{ + ESP_RETURN_ON_FALSE(s_ot_task_block_monitor_task != NULL, ESP_OK, TAG, "Task block monitor not created"); + + esp_openthread_task_block_monitor_set(false); + vTaskDelete(s_ot_task_block_monitor_task); + s_ot_task_block_monitor_task = NULL; + + ESP_LOGI(TAG, "OpenThread task block monitor deleted"); + return ESP_OK; +} + +void esp_openthread_task_block_monitor_set(bool enable) +{ + if (enable) { + atomic_store(&s_ot_task_block_monitor_latest_time, esp_timer_get_time()); + atomic_store(&s_ot_task_block_monitor_active, true); + } else { + atomic_store(&s_ot_task_block_monitor_active, false); + } +} + +#endif // CONFIG_OPENTHREAD_TASK_BLOCK_MONITOR diff --git a/components/openthread/src/port/esp_openthread_memory.c b/components/openthread/src/port/esp_openthread_memory.c index 661e62c70e..6b5aba17f2 100644 --- a/components/openthread/src/port/esp_openthread_memory.c +++ b/components/openthread/src/port/esp_openthread_memory.c @@ -1,10 +1,11 @@ /* - * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include "esp_openthread.h" +#include "esp_openthread_platform.h" #include "openthread/platform/memory.h" @@ -12,10 +13,10 @@ void *otPlatCAlloc(size_t num, size_t size) { - return calloc(num, size); + return heap_caps_calloc_prefer(num, size, 3, esp_openthread_get_alloc_caps(), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); } void otPlatFree(void *ptr) { - free(ptr); + heap_caps_free(ptr); } diff --git a/components/openthread/src/port/esp_openthread_messagepool.c b/components/openthread/src/port/esp_openthread_messagepool.c index 17761b5061..7996af3340 100644 --- a/components/openthread/src/port/esp_openthread_messagepool.c +++ b/components/openthread/src/port/esp_openthread_messagepool.c @@ -1,35 +1,48 @@ /* - * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include "openthread-core-config.h" #include "esp_openthread_common_macro.h" +#include "esp_openthread_platform.h" #include "esp_err.h" #include "esp_log.h" #include "esp_heap_caps.h" +#include "esp_psram.h" #include "openthread/instance.h" #include "openthread/platform/messagepool.h" -int s_buffer_pool_head = -1; -otMessageBuffer **s_buffer_pool_pointer = NULL; -otMessageBuffer *s_buffer_pool = NULL; +// Fallback buffer count at runtime for non-PSRAM targets (matches Kconfig default) +#define OT_MSGPOOL_NUM_BUFFERS_NO_PSRAM 65 + +static int s_buffer_pool_head = -1; +static otMessageBuffer **s_buffer_pool_pointer = NULL; +static otMessageBuffer *s_buffer_pool = NULL; void otPlatMessagePoolInit(otInstance *aInstance, uint16_t aMinNumFreeBuffers, size_t aBufferSize) { - otMessageBuffer *buffer_pool = (otMessageBuffer *)heap_caps_calloc(aMinNumFreeBuffers, aBufferSize, MALLOC_CAP_SPIRAM); - s_buffer_pool_pointer = (otMessageBuffer **)heap_caps_calloc(aMinNumFreeBuffers, sizeof(otMessageBuffer **), MALLOC_CAP_SPIRAM); + uint16_t num_buffers = aMinNumFreeBuffers; + + if (!esp_psram_is_initialized() && num_buffers > OT_MSGPOOL_NUM_BUFFERS_NO_PSRAM) { + ESP_LOGW(OT_PLAT_LOG_TAG, "PSRAM not available, reducing message pool from %u to %u buffers", + num_buffers, OT_MSGPOOL_NUM_BUFFERS_NO_PSRAM); + num_buffers = OT_MSGPOOL_NUM_BUFFERS_NO_PSRAM; + } + + otMessageBuffer *buffer_pool = (otMessageBuffer *)heap_caps_calloc_prefer(num_buffers, aBufferSize, 3, esp_openthread_get_alloc_caps(), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + s_buffer_pool_pointer = (otMessageBuffer **)heap_caps_calloc_prefer(num_buffers, sizeof(otMessageBuffer **), 3, esp_openthread_get_alloc_caps(), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); if (buffer_pool == NULL || s_buffer_pool_pointer == NULL) { ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to create message buffer pool"); assert(false); } - for (uint16_t i = 0; i < aMinNumFreeBuffers; i++) { + for (uint16_t i = 0; i < num_buffers; i++) { s_buffer_pool_pointer[i] = buffer_pool + i * aBufferSize / sizeof(otMessageBuffer); } - s_buffer_pool_head = aMinNumFreeBuffers - 1; + s_buffer_pool_head = num_buffers - 1; s_buffer_pool = buffer_pool; - ESP_LOGI(OT_PLAT_LOG_TAG, "Create message buffer pool successfully, size %d", aMinNumFreeBuffers*aBufferSize); + ESP_LOGI(OT_PLAT_LOG_TAG, "Create message buffer pool successfully, size %d", num_buffers * aBufferSize); } otMessageBuffer *otPlatMessagePoolNew(otInstance *aInstance) diff --git a/components/openthread/src/port/esp_openthread_radio.c b/components/openthread/src/port/esp_openthread_radio.c index 83e5c7aa64..1ef27f0331 100644 --- a/components/openthread/src/port/esp_openthread_radio.c +++ b/components/openthread/src/port/esp_openthread_radio.c @@ -413,7 +413,7 @@ void otPlatRadioClearSrcMatchExtEntries(otInstance *aInstance) otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint16_t aScanDuration) { esp_ieee802154_set_channel(aScanChannel); - esp_ieee802154_energy_detect(aScanDuration * US_PER_MS / US_PER_SYMBLE); + esp_ieee802154_energy_detect(aScanDuration * US_PER_MS / US_PER_SYMBOL); return OT_ERROR_NONE; } diff --git a/components/openthread/srcs_ftd_mtd.cmake b/components/openthread/srcs_ftd_mtd.cmake new file mode 100644 index 0000000000..e8cee70187 --- /dev/null +++ b/components/openthread/srcs_ftd_mtd.cmake @@ -0,0 +1,116 @@ +# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 + +# Source files for FTD (Full Thread Device) and MTD (Minimal Thread Device) modes. +# Uses directory-based collection with exclusions. + +set(src_dirs + "src" + "src/port" + "openthread/examples/platforms/utils" + "openthread/src/core/api" + "openthread/src/core/common" + "openthread/src/core/crypto" + "openthread/src/core/diags" + "openthread/src/core/instance" + "openthread/src/core/mac" + "openthread/src/core/radio" + "openthread/src/core/thread" + "openthread/src/core/utils" + "openthread/src/lib/hdlc" + "openthread/src/lib/spinel" + "openthread/src/core/backbone_router" + "openthread/src/core/coap" + "openthread/src/core/meshcop" + "openthread/src/core/net" + "openthread/src/lib/platform" +) + +set(exclude_srcs + "openthread/examples/platforms/utils/logging_rtt.c" + "openthread/examples/platforms/utils/soft_source_match_table.c" + "openthread/src/core/instance/extension_example.cpp" + "openthread/src/core/crypto/crypto_platform_mbedtls.cpp" + "openthread/src/core/api/random_crypto_api.cpp" +) + +# CLI sources +if(CONFIG_OPENTHREAD_CLI) + list(APPEND src_dirs + "openthread/examples/apps/cli" + "openthread/src/cli") + list(APPEND exclude_srcs + "openthread/examples/apps/cli/main.c") +endif() + +# Radio interface exclusions +if(CONFIG_OPENTHREAD_RADIO_NATIVE) + list(APPEND exclude_srcs + "src/port/esp_openthread_radio_spinel.cpp" + "src/port/esp_spi_spinel_interface.cpp" + "src/port/esp_uart_spinel_interface.cpp") +elseif(CONFIG_OPENTHREAD_RADIO_SPINEL_UART OR CONFIG_OPENTHREAD_RADIO_SPINEL_SPI) + list(APPEND exclude_srcs + "src/port/esp_openthread_radio.c" + "src/port/esp_openthread_sleep.c") +elseif(CONFIG_OPENTHREAD_RADIO_154_NONE) + list(APPEND exclude_srcs + "src/port/esp_openthread_radio_spinel.cpp" + "src/port/esp_spi_spinel_interface.cpp" + "src/port/esp_uart_spinel_interface.cpp" + "src/port/esp_openthread_radio.c" + "src/port/esp_openthread_sleep.c") +endif() + +# TREL +if(NOT CONFIG_OPENTHREAD_RADIO_TREL) + list(APPEND exclude_srcs + "src/port/esp_openthread_trel.c") +endif() + +# Border router +if(CONFIG_OPENTHREAD_BORDER_ROUTER) + list(APPEND src_dirs + "openthread/src/core/border_router") +endif() + +# FTD-specific +if(CONFIG_OPENTHREAD_FTD) + set_source_files_properties("openthread/src/core/net/srp_server.cpp" + PROPERTIES COMPILE_FLAGS + -Wno-maybe-uninitialized) +endif() + +# NCP vendor hook +if(CONFIG_OPENTHREAD_NCP_VENDOR_HOOK) + list(APPEND src_dirs + "src/ncp") + if(CONFIG_OPENTHREAD_RCP_UART OR CONFIG_OPENTHREAD_RCP_USB_SERIAL_JTAG) + list(APPEND exclude_srcs + "src/ncp/esp_openthread_ncp_spi.cpp") + elseif(CONFIG_OPENTHREAD_RCP_SPI) + list(APPEND exclude_srcs + "src/ncp/esp_openthread_ncp_hdlc.cpp") + endif() +endif() + +# Optional features +if(NOT CONFIG_OPENTHREAD_DNS64_CLIENT) + list(APPEND exclude_srcs + "src/esp_openthread_dns64.c") +endif() + +if(NOT CONFIG_FREERTOS_USE_TICKLESS_IDLE) + list(APPEND exclude_srcs + "src/port/esp_openthread_sleep.c") +endif() + +if(NOT CONFIG_OPENTHREAD_PLATFORM_MSGPOOL_MANAGEMENT) + list(APPEND exclude_srcs + "src/port/esp_openthread_messagepool.c") +endif() + +if(NOT CONFIG_OPENTHREAD_RCP_SPINEL_CONSOLE) + list(APPEND exclude_srcs + "src/ncp/esp_openthread_ncp_console.cpp") +endif() diff --git a/components/openthread/srcs_radio.cmake b/components/openthread/srcs_radio.cmake new file mode 100644 index 0000000000..9d929c1005 --- /dev/null +++ b/components/openthread/srcs_radio.cmake @@ -0,0 +1,97 @@ +# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 + +# Source files for RCP (Radio Co-Processor) mode. +# Uses explicit whitelist to avoid including non-RCP-compatible files. +# This prevents new OpenThread files from breaking RCP builds. + +set(rcp_srcs + # OpenThread core - RADIO_COMMON_SOURCES from openthread/src/core/CMakeLists.txt + # No need for crypto/crypto_platform_mbedtls.cpp because we use esp-idf implementation + openthread/src/core/api/diags_api.cpp + openthread/src/core/api/error_api.cpp + openthread/src/core/api/instance_api.cpp + openthread/src/core/api/link_raw_api.cpp + openthread/src/core/api/logging_api.cpp + openthread/src/core/api/random_noncrypto_api.cpp + openthread/src/core/api/tasklet_api.cpp + openthread/src/core/common/binary_search.cpp + openthread/src/core/common/error.cpp + openthread/src/core/common/frame_builder.cpp + openthread/src/core/common/log.cpp + openthread/src/core/common/random.cpp + openthread/src/core/common/string.cpp + openthread/src/core/common/tasklet.cpp + openthread/src/core/common/timer.cpp + openthread/src/core/common/uptime.cpp + openthread/src/core/crypto/aes_ccm.cpp + openthread/src/core/crypto/aes_ecb.cpp + openthread/src/core/crypto/crypto_platform_psa.cpp + openthread/src/core/crypto/storage.cpp + openthread/src/core/diags/factory_diags.cpp + openthread/src/core/instance/instance.cpp + openthread/src/core/mac/link_raw.cpp + openthread/src/core/mac/mac_frame.cpp + openthread/src/core/mac/mac_header_ie.cpp + openthread/src/core/mac/mac_types.cpp + openthread/src/core/mac/sub_mac.cpp + openthread/src/core/mac/sub_mac_callbacks.cpp + openthread/src/core/mac/sub_mac_csl_receiver.cpp + openthread/src/core/mac/sub_mac_wed.cpp + openthread/src/core/radio/radio.cpp + openthread/src/core/radio/radio_callbacks.cpp + openthread/src/core/radio/radio_platform.cpp + openthread/src/core/thread/link_quality.cpp + openthread/src/core/utils/otns.cpp + openthread/src/core/utils/parse_cmdline.cpp + openthread/src/core/utils/power_calibration.cpp + # Platform utils + openthread/examples/platforms/utils/mac_frame.cpp + # HDLC library + openthread/src/lib/hdlc/hdlc.cpp + # Spinel library + openthread/src/lib/spinel/spinel.c + openthread/src/lib/spinel/spinel_buffer.cpp + openthread/src/lib/spinel/spinel_decoder.cpp + openthread/src/lib/spinel/spinel_encoder.cpp + # NCP + openthread/src/ncp/changed_props_set.cpp + openthread/src/ncp/ncp_base.cpp + 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 + 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 + src/ncp/esp_openthread_ncp.cpp +) + +# Conditional sources based on host connection mode +if(CONFIG_OPENTHREAD_RCP_UART OR CONFIG_OPENTHREAD_RCP_USB_SERIAL_JTAG) + list(APPEND rcp_srcs + openthread/src/ncp/ncp_hdlc.cpp + src/port/esp_openthread_uart.c) + if(CONFIG_OPENTHREAD_NCP_VENDOR_HOOK) + list(APPEND rcp_srcs src/ncp/esp_openthread_ncp_hdlc.cpp) + endif() +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) + list(APPEND rcp_srcs src/ncp/esp_openthread_ncp_spi.cpp) + endif() +endif() + +if(CONFIG_OPENTHREAD_NCP_VENDOR_HOOK AND CONFIG_OPENTHREAD_RCP_SPINEL_CONSOLE) + list(APPEND rcp_srcs src/ncp/esp_openthread_ncp_console.cpp) +endif() diff --git a/components/openthread/srcs_spinel.cmake b/components/openthread/srcs_spinel.cmake new file mode 100644 index 0000000000..0dd7daf407 --- /dev/null +++ b/components/openthread/srcs_spinel.cmake @@ -0,0 +1,45 @@ +# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 + +# Source files for SPINEL_ONLY mode. +# Minimal build for host-side spinel communication. +# Uses explicit whitelist for clarity. + +set(private_include_dirs + "private_include" + "openthread/src" + "openthread/src/core" + "openthread/src/include" + "openthread/src/lib" + "openthread/src/lib/hdlc" + "openthread/src/lib/spinel" +) + +set(spinel_srcs + # ESP spinel sources + src/spinel/esp_radio_spinel.cpp + src/spinel/esp_radio_spinel_uart_interface.cpp + # ESP port (minimal) + src/port/esp_openthread_alarm.c + src/port/esp_openthread_logging.c + # OpenThread spinel library + openthread/src/lib/spinel/spinel.c + openthread/src/lib/spinel/spinel_buffer.cpp + openthread/src/lib/spinel/spinel_decoder.cpp + openthread/src/lib/spinel/spinel_encoder.cpp + openthread/src/lib/spinel/spinel_helper.cpp + openthread/src/lib/spinel/radio_spinel.cpp + openthread/src/lib/spinel/spinel_driver.cpp + openthread/src/lib/spinel/logger.cpp + # OpenThread HDLC library + openthread/src/lib/hdlc/hdlc.cpp + # OpenThread platform library + openthread/src/lib/platform/exit_code.c + # OpenThread core (minimal) + openthread/src/core/api/error_api.cpp + openthread/src/core/api/logging_api.cpp + openthread/src/core/common/error.cpp + openthread/src/core/common/log.cpp + openthread/src/core/common/string.cpp + openthread/src/core/mac/mac_frame.cpp +) diff --git a/examples/openthread/.build-test-rules.yml b/examples/openthread/.build-test-rules.yml index 007e0630a4..c05a5b7cfd 100644 --- a/examples/openthread/.build-test-rules.yml +++ b/examples/openthread/.build-test-rules.yml @@ -25,6 +25,8 @@ examples/openthread/ot_br: enable: - if: ((SOC_WIFI_SUPPORTED == 1 and IDF_TARGET != "esp32c61") or IDF_TARGET == "esp32p4") and CONFIG_NAME != "native_radio" - if: SOC_WIFI_SUPPORTED == 1 and (SOC_IEEE802154_SUPPORTED == 1 and CONFIG_NAME == "native_radio") + disable: + - if: IDF_TARGET in ["esp32", "esp32s2", "esp32s3"] and CONFIG_NAME == "br_debug_riscv" disable_test: - if: IDF_TARGET not in ["esp32s3"] reason: only test on esp32s3 diff --git a/examples/openthread/ot_br/partitions.csv b/examples/openthread/ot_br/partitions.csv index 376458ad8b..531e721a29 100644 --- a/examples/openthread/ot_br/partitions.csv +++ b/examples/openthread/ot_br/partitions.csv @@ -1,5 +1,5 @@ # Name, Type, SubType, Offset, Size, Flags # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap -nvs, data, nvs, 0x9000, 0x6000, -phy_init, data, phy, 0xf000, 0x1000, -factory, app, factory, 0x10000, 1900K, +nvs, data, nvs, , 0x6000, +phy_init, data, phy, , 0x1000, +factory, app, factory, , 2000K, diff --git a/examples/openthread/ot_br/sdkconfig.ci.br_debug b/examples/openthread/ot_br/sdkconfig.ci.br_debug new file mode 100644 index 0000000000..85b50225fa --- /dev/null +++ b/examples/openthread/ot_br/sdkconfig.ci.br_debug @@ -0,0 +1,2 @@ +CONFIG_OPENTHREAD_DEBUG=y +CONFIG_OPENTHREAD_TASK_BLOCK_MONITOR=y diff --git a/examples/openthread/ot_br/sdkconfig.ci.br_debug_riscv b/examples/openthread/ot_br/sdkconfig.ci.br_debug_riscv new file mode 100644 index 0000000000..286ce3cc99 --- /dev/null +++ b/examples/openthread/ot_br/sdkconfig.ci.br_debug_riscv @@ -0,0 +1,3 @@ +CONFIG_ESP_SYSTEM_USE_FRAME_POINTER=y +CONFIG_OPENTHREAD_DEBUG=y +CONFIG_OPENTHREAD_TASK_BLOCK_MONITOR=y diff --git a/examples/openthread/ot_br/sdkconfig.defaults b/examples/openthread/ot_br/sdkconfig.defaults index 7fe886d91f..23cd52c6e1 100644 --- a/examples/openthread/ot_br/sdkconfig.defaults +++ b/examples/openthread/ot_br/sdkconfig.defaults @@ -4,7 +4,6 @@ CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" -CONFIG_PARTITION_TABLE_OFFSET=0x8000 CONFIG_PARTITION_TABLE_MD5=y # end of Partition Table @@ -58,3 +57,8 @@ CONFIG_EXAMPLE_CONNECT_THREAD=n CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=3584 CONFIG_ESP_MAIN_TASK_STACK_SIZE=6144 # end of ESP System Settings + +# +# Serial flasher config +# +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y diff --git a/examples/openthread/ot_br/sdkconfig.defaults.esp32p4 b/examples/openthread/ot_br/sdkconfig.defaults.esp32p4 index 3b161864c7..8277f12de5 100644 --- a/examples/openthread/ot_br/sdkconfig.defaults.esp32p4 +++ b/examples/openthread/ot_br/sdkconfig.defaults.esp32p4 @@ -5,3 +5,5 @@ CONFIG_OPENTHREAD_BORDER_ROUTER_AUTO_START=y # Enable PPP support as a workaround to ensure LWIP thread-lib compatibility for Ethernet builds CONFIG_LWIP_PPP_SUPPORT=y CONFIG_LWIP_PPP_SERVER_SUPPORT=y +# Increase size of bootloader due to frame pointer. Only overflowed on P4. +CONFIG_PARTITION_TABLE_OFFSET=0x9000