/* * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include #include "hal/ieee802154_ll.h" #include "esp_attr.h" #include "esp_err.h" #include "esp_ieee802154_ack.h" #include "esp_ieee802154_dev.h" #include "esp_ieee802154_frame.h" #include "esp_ieee802154_pib.h" #include "esp_ieee802154_types.h" static ieee802154_pending_table_t ieee802154_pending_table; #define BIT_SET(mask, pos) ((mask) |= (1UL << (pos))) #define BIT_CLR(mask, pos) ((mask) &= ~(1UL << (pos))) #define BIT_IST(mask, pos) ((mask) & (1UL << (pos))) static IRAM_ATTR bool ieee802154_addr_in_pending_table(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) { 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) { ret = true; break; } } } return ret; } esp_err_t ieee802154_add_pending_addr(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)) { // 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) { // 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); 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)) { 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) { // 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); ret = ESP_OK; } } return ret; } esp_err_t ieee802154_clear_pending_addr(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); 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); ret = ESP_OK; break; } } } return ret; } void ieee802154_reset_pending_table(bool is_short) { // Consider this function may be called in ISR, only clear the mask bits for finishing the process quickly. if (is_short) { ieee802154_pending_table.short_addr_mask = 0; } else { ieee802154_pending_table.ext_addr_mask = 0; } } bool ieee802154_ack_config_pending_bit(const uint8_t *frame) { 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(); switch (pending_mode) { case IEEE802154_AUTO_PENDING_DISABLE: // HW will check whether the frame is data request or not pending_bit = true; 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; } } 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)) { pending_bit = false; } break; default: assert(false); } if (set_to_hw) { ieee802154_ll_set_pending_bit(pending_bit); } return pending_bit; }