Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.
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

/* 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);
}
}