You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
356 lines
7.4 KiB
356 lines
7.4 KiB
/* ieee802154_mcxw_utils.c - NXP MCXW 802.15.4 driver utils*/ |
|
|
|
/* |
|
* Copyright 2025 NXP |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
* |
|
*/ |
|
|
|
#include <zephyr/kernel.h> |
|
#include <zephyr/arch/cpu.h> |
|
#include <zephyr/sys/util.h> |
|
#include <zephyr/sys/byteorder.h> |
|
|
|
#include <errno.h> |
|
|
|
#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); |
|
} |
|
}
|
|
|