/* ieee802154_mcxw_utils.c - NXP MCXW 802.15.4 driver utils*/ /* * Copyright 2025 NXP * * SPDX-License-Identifier: Apache-2.0 * */ #include #include #include #include #include #include "ieee802154_mcxw_utils.h" /* TODO IEEE 802.15.4 MAC Multipurpose frame format */ /* TODO add function checks */ enum offset_fcf_fields { OffsetFrameType = 0x00, OffsetSecurityEnabled = 0x03, OffsetFramePending = 0x04, OffsetAR = 0x05, OffsetPanIdCompression = 0x06, OffsetSeqNumberSuppression = 0x08, OffsetIEPresent = 0x09, OffsetDstAddrMode = 0x0A, OffsetFrameVersion = 0x0C, OffsetSrcAddrMode = 0x0E, }; enum mask_fcf_fields { MaskFrameType = (0x7 << OffsetFrameType), MaskSecurityEnabled = (0x01 << OffsetSecurityEnabled), MaskFramePending = (0x01 << OffsetFramePending), MaskAR = (0x01 << OffsetAR), MaskPanIdCompression = (0x01 << OffsetPanIdCompression), MaskSeqNumberSuppression = (0x01 << OffsetSeqNumberSuppression), MaskIEPresent = (0x01 << OffsetIEPresent), MaskDstAddrMode = (0x03 << OffsetDstAddrMode), MaskFrameVersion = (0x03 << OffsetFrameVersion), MaskSrcAddrMode = (0x03 << OffsetSrcAddrMode), }; enum modes_dst_addr { ModeDstAddrNone = 0x00, ModeDstAddrShort = (0x02 << OffsetDstAddrMode), ModeDstAddrExt = (0x03 << OffsetDstAddrMode), }; enum version_frame { VersionIeee2003 = 0x00, VersionIeee2006 = 0x01, VersionIeee2015 = 0x02, }; enum modes_src_addr { ModeSrcAddrNone = 0x00, ModeSrcAddrShort = (0x02 << OffsetSrcAddrMode), ModeSrcAddrExt = (0x03 << OffsetSrcAddrMode), }; enum offset_scf_fields { OffsetSecurityLevel = 0x00, OffsetKeyIdMode = 0x03, OffsetFrameCntSuppression = 0x05, OffsetASNinNonce = 0x06, }; enum mask_scf_fields { MaskSecurityLevel = (0x07 << OffsetSecurityLevel), MaskKeyIdMode = (0x03 << OffsetKeyIdMode), MaskFrameCntSuppression = (0x1 << OffsetFrameCntSuppression), MaskASNinNonce = (0x01 << OffsetASNinNonce), }; static uint16_t get_frame_control_field(uint8_t *pdu, uint16_t length) { if ((pdu == NULL) || (length < 3)) { return 0x00; } return (uint16_t)(pdu[0] | (pdu[1] << 8)); } static bool is_security_enabled(uint16_t fcf) { if (fcf) { return (bool)(fcf & MaskSecurityEnabled); } return false; } static bool is_ie_present(uint16_t fcf) { if (fcf) { return (bool)(fcf & MaskIEPresent); } return false; } static uint8_t get_frame_version(uint16_t fcf) { if (fcf) { return (uint8_t)((fcf & MaskFrameVersion) >> OffsetFrameVersion); } return 0xFF; } static bool is_frame_version_2015_fcf(uint16_t fcf) { if (fcf) { return get_frame_version(fcf) == VersionIeee2015; } return false; } bool is_frame_version_2015(uint8_t *pdu, uint16_t length) { uint16_t fcf = get_frame_control_field(pdu, length); if (fcf) { return get_frame_version(fcf) == VersionIeee2015; } return false; } static bool is_sequence_number_suppression(uint16_t fcf) { if (fcf) { return (bool)(fcf & MaskSeqNumberSuppression); } return false; } static bool is_dst_panid_present(uint16_t fcf) { bool present; if (!fcf) { return false; } if (is_frame_version_2015_fcf(fcf)) { switch (fcf & (MaskDstAddrMode | MaskSrcAddrMode | MaskPanIdCompression)) { case (ModeDstAddrNone | ModeSrcAddrNone): case (ModeDstAddrShort | ModeSrcAddrNone | MaskPanIdCompression): case (ModeDstAddrExt | ModeSrcAddrNone | MaskPanIdCompression): case (ModeDstAddrNone | ModeSrcAddrShort): case (ModeDstAddrNone | ModeSrcAddrExt): case (ModeDstAddrNone | ModeSrcAddrShort | MaskPanIdCompression): case (ModeDstAddrNone | ModeSrcAddrExt | MaskPanIdCompression): case (ModeDstAddrExt | ModeSrcAddrExt | MaskPanIdCompression): present = false; break; default: present = true; } } else { present = (bool)(fcf & MaskDstAddrMode); } return present; } static bool is_src_panid_present(uint16_t fcf) { bool present; if (!fcf) { return false; } if (is_frame_version_2015_fcf(fcf)) { switch (fcf & (MaskDstAddrMode | MaskSrcAddrMode | MaskPanIdCompression)) { case (ModeDstAddrNone | ModeSrcAddrShort): case (ModeDstAddrNone | ModeSrcAddrExt): case (ModeDstAddrShort | ModeSrcAddrShort): case (ModeDstAddrShort | ModeSrcAddrExt): case (ModeDstAddrExt | ModeSrcAddrShort): present = true; break; default: present = false; } } else { present = ((fcf & MaskSrcAddrMode) != 0) && ((fcf & MaskPanIdCompression) == 0); } return present; } static uint8_t calculate_addr_field_size(uint16_t fcf) { uint8_t size = 2; if (!fcf) { return 0; } if (!is_sequence_number_suppression(fcf)) { size += 1; } if (is_dst_panid_present(fcf)) { size += 2; } /* destination addressing mode */ switch (fcf & MaskDstAddrMode) { case ModeDstAddrShort: size += 2; break; case ModeDstAddrExt: size += 8; break; default: break; } if (is_src_panid_present(fcf)) { size += 2; } /* source addressing mode */ switch (fcf & MaskSrcAddrMode) { case ModeSrcAddrShort: size += 2; break; case ModeSrcAddrExt: size += 8; break; default: break; } return size; } static uint8_t get_keyid_mode(uint8_t *pdu, uint16_t length) { uint16_t fcf = get_frame_control_field(pdu, length); uint8_t ash_start; if (is_security_enabled(fcf)) { ash_start = calculate_addr_field_size(fcf); return (uint8_t)((pdu[ash_start] & MaskKeyIdMode) >> OffsetKeyIdMode); } return 0xFF; } bool is_keyid_mode_1(uint8_t *pdu, uint16_t length) { uint8_t key_mode = get_keyid_mode(pdu, length); if (key_mode == 0x01) { return true; } return false; } void set_frame_counter(uint8_t *pdu, uint16_t length, uint32_t fc) { uint16_t fcf = get_frame_control_field(pdu, length); if (is_security_enabled(fcf)) { uint8_t ash_start = calculate_addr_field_size(fcf); uint8_t scf = pdu[ash_start]; /* check that Frame Counter Suppression is not set */ if (!(scf & MaskFrameCntSuppression)) { sys_put_le32(fc, &pdu[ash_start + 1]); } } } static uint8_t get_asn_size(uint8_t *pdu, uint16_t length) { uint16_t fcf = get_frame_control_field(pdu, length); if (is_security_enabled(fcf)) { uint8_t ash_start = calculate_addr_field_size(fcf); uint8_t scf = pdu[ash_start]; uint8_t size = 1; /* Frame Counter Suppression is not set */ if (!(scf & MaskFrameCntSuppression)) { size += 4; } uint8_t key_mode = get_keyid_mode(pdu, length); switch (key_mode) { case 0x01: size += 1; break; case 0x02: size += 5; case 0x03: size += 9; default: break; } return size; } return 0; } static uint8_t *get_csl_ie_content_start(uint8_t *pdu, uint16_t length) { uint16_t fcf = get_frame_control_field(pdu, length); if (is_ie_present(fcf)) { uint8_t ie_start_idx = calculate_addr_field_size(fcf) + get_asn_size(pdu, length); uint8_t *cur_ie = &pdu[ie_start_idx]; uint8_t ie_header = (uint16_t)(cur_ie[0] | (cur_ie[1] << 8)); uint8_t ie_length = ie_header & 0x7F; uint8_t ie_el_id = ie_header & 0x7F80; while ((ie_el_id != 0x7e) && (ie_el_id != 0x7f)) { if (ie_el_id == 0x1a) { return (cur_ie + 2); } cur_ie += (2 + ie_length); ie_header = (uint16_t)(cur_ie[0] | (cur_ie[1] << 8)); ie_length = ie_header & 0x7F; ie_el_id = ie_header & 0x7F80; } } return NULL; } void set_csl_ie(uint8_t *pdu, uint16_t length, uint16_t period, uint16_t phase) { uint8_t *csl_ie_content = get_csl_ie_content_start(pdu, length); if (csl_ie_content) { sys_put_le16(phase, csl_ie_content); sys_put_le16(period, csl_ie_content + 2); } }