mbed-os/features/nanostack/sal-stack-nanostack/source/6LoWPAN/ws/ws_ie_lib.c

1449 lines
46 KiB
C

/*
* Copyright (c) 2018-2020, Pelion and affiliates.
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nsconfig.h"
#include <string.h>
#include "ns_types.h"
#include "ns_list.h"
#include "ns_trace.h"
#include "common_functions.h"
#include "mac_common_defines.h"
#include "6LoWPAN/MAC/mac_ie_lib.h"
#include "6LoWPAN/ws/ws_common_defines.h"
#include "6LoWPAN/ws/ws_ie_lib.h"
#include "ws_management_api.h"
static uint8_t *ws_wh_header_base_write(uint8_t *ptr, uint16_t length, uint8_t type)
{
ptr = mac_ie_header_base_write(ptr, MAC_HEADER_ASSIGNED_EXTERNAL_ORG_IE_ID, length + 1);
*ptr++ = type;
return ptr;
}
static uint16_t ws_channel_plan_length(uint8_t channel_plan)
{
switch (channel_plan) {
case 0:
//Regulator domain and operationg class inline
return 2;
case 1:
//CHo, Channel spasing and number of channel's inline
return 6;
case 2:
//Regulator domain and channel plan ID inline
return 2;
default:
return 0;
}
}
static uint16_t ws_channel_function_length(uint8_t channel_function, uint16_t hop_channel_count)
{
switch (channel_function) {
case 0:
//Fixed channel inline
return 2;
case 1:
case 2:
return 0;
case 3:
//Hop count + channel hop list
return (1 + hop_channel_count);
default:
return 0;
}
}
static uint16_t ws_excluded_channel_length(ws_generic_channel_info_t *generic_channel_info)
{
uint16_t length;
if (generic_channel_info->excluded_channel_ctrl == WS_EXC_CHAN_CTRL_RANGE) {
length = (generic_channel_info->excluded_channels.range.excluded_range_length * 4) + 1;
} else if (generic_channel_info->excluded_channel_ctrl == WS_EXC_CHAN_CTRL_BITMASK) {
length = generic_channel_info->excluded_channels.mask.channel_mask_bytes_inline;
} else {
length = 0;
}
return length;
}
static void ws_generic_channel_info_init(struct ws_hopping_schedule_s *hopping_schedule, ws_generic_channel_info_t *generic_channel_info, bool unicast_schedule)
{
generic_channel_info->channel_plan = hopping_schedule->channel_plan;
if (unicast_schedule) {
generic_channel_info->channel_function = hopping_schedule->uc_channel_function;
generic_channel_info->excluded_channel_ctrl = hopping_schedule->excluded_channels.excuded_channel_ctrl;
if (generic_channel_info->excluded_channel_ctrl == WS_EXC_CHAN_CTRL_RANGE) {
generic_channel_info->excluded_channels.range.excluded_range_length = hopping_schedule->excluded_channels.excluded_range_length;
generic_channel_info->excluded_channels.range.exluded_range = hopping_schedule->excluded_channels.exluded_range;
} else if (generic_channel_info->excluded_channel_ctrl == WS_EXC_CHAN_CTRL_BITMASK) {
generic_channel_info->excluded_channels.mask.channel_mask_bytes_inline = hopping_schedule->excluded_channels.channel_mask_bytes_inline;
generic_channel_info->excluded_channels.mask.excluded_channel_count = hopping_schedule->excluded_channels.excluded_channel_count;
generic_channel_info->excluded_channels.mask.channel_mask = hopping_schedule->excluded_channels.channel_mask;
}
} else {
generic_channel_info->channel_function = hopping_schedule->bc_channel_function;
generic_channel_info->excluded_channel_ctrl = WS_EXC_CHAN_CTRL_NONE;
}
}
static void ws_wp_channel_plan_set(ws_generic_channel_info_t *generic_channel_info, struct ws_hopping_schedule_s *hopping_schedule)
{
switch (generic_channel_info->channel_plan) {
case 0:
//Regulator domain and operationg class inline
generic_channel_info->plan.zero.regulator_domain = hopping_schedule->regulatory_domain;
generic_channel_info->plan.zero.operation_class = hopping_schedule->operating_class;
break;
case 1:
//CHo, Channel spasing and number of channel's inline
generic_channel_info->plan.one.ch0 = hopping_schedule->ch0_freq;
generic_channel_info->plan.one.channel_spacing = hopping_schedule->channel_spacing;
generic_channel_info->plan.one.number_of_channel = hopping_schedule->number_of_channels;
break;
case 2:
generic_channel_info->plan.two.regulator_domain = hopping_schedule->regulatory_domain;
generic_channel_info->plan.two.channel_plan_id = hopping_schedule->channel_plan_id;
break;
default:
break;
}
}
static void ws_wp_channel_function_set(ws_generic_channel_info_t *generic_channel_info, struct ws_hopping_schedule_s *hopping_schedule, bool unicast_schedule)
{
switch (generic_channel_info->channel_function) {
case 0:
//Fixed channel inline
if (unicast_schedule) {
generic_channel_info->function.zero.fixed_channel = hopping_schedule->uc_fixed_channel;
} else {
generic_channel_info->function.zero.fixed_channel = hopping_schedule->bc_fixed_channel;
}
break;
case 1:
case 2:
//No Inline
break;
case 3:
//TODO add list to possible to set
//Force 1 channel 0
generic_channel_info->function.three.channel_hop_count = 1;
generic_channel_info->function.three.channel_list = NULL;
break;
default:
break;
}
}
static uint16_t ws_wp_generic_shedule_length_get(ws_generic_channel_info_t *generic_channel_info)
{
uint16_t length = 1;
length += ws_channel_plan_length(generic_channel_info->channel_plan);
uint16_t number_of_channels = 1;
if (generic_channel_info->channel_plan == 3) {
number_of_channels = generic_channel_info->function.three.channel_hop_count;
} else {
number_of_channels = 1;
}
length += ws_channel_function_length(generic_channel_info->channel_function, number_of_channels);
length += ws_excluded_channel_length(generic_channel_info);
return length;
}
uint16_t ws_wp_nested_hopping_schedule_length(struct ws_hopping_schedule_s *hopping_schedule, bool unicast_schedule)
{
ws_generic_channel_info_t generic_channel_info;
ws_generic_channel_info_init(hopping_schedule, &generic_channel_info, unicast_schedule);
ws_wp_channel_function_set(&generic_channel_info, hopping_schedule, unicast_schedule);
uint16_t length;
if (unicast_schedule) {
length = 3;
} else {
length = 9;
}
length += ws_wp_generic_shedule_length_get(&generic_channel_info);
return length;
}
uint8_t *ws_wh_utt_write(uint8_t *ptr, uint8_t message_type)
{
ptr = ws_wh_header_base_write(ptr, 4, WH_IE_UTT_TYPE);
*ptr++ = message_type;
memset(ptr, 0, 3);
ptr += 3;
return ptr;
}
uint8_t *ws_wh_bt_write(uint8_t *ptr)
{
ptr = ws_wh_header_base_write(ptr, 5, WH_IE_BT_TYPE);
memset(ptr, 0, 5);
ptr += 5;
return ptr;
}
uint8_t *ws_wh_fc_write(uint8_t *ptr, ws_fc_ie_t *fc_ie)
{
ptr = ws_wh_header_base_write(ptr, 2, WH_IE_FC_TYPE);
*ptr++ = fc_ie->tx_flow_ctrl;
*ptr++ = fc_ie->rx_flow_ctrl;
return ptr;
}
uint8_t *ws_wh_rsl_write(uint8_t *ptr, uint8_t rsl)
{
ptr = ws_wh_header_base_write(ptr, 1, WH_IE_RSL_TYPE);
*ptr++ = rsl;
return ptr;
}
uint8_t *ws_wh_ea_write(uint8_t *ptr, uint8_t *eui64)
{
ptr = ws_wh_header_base_write(ptr, 8, WH_IE_EA_TYPE);
memcpy(ptr, eui64, 8);
ptr += 8;
return ptr;
}
uint8_t *ws_wh_vh_write(uint8_t *ptr, uint8_t *vendor_header, uint8_t vendor_header_length)
{
ptr = ws_wh_header_base_write(ptr, vendor_header_length, WH_IE_VH_TYPE);
if (vendor_header_length) {
memcpy(ptr, vendor_header, vendor_header_length);
ptr += vendor_header_length;
}
return ptr;
}
uint8_t *ws_wh_lutt_write(uint8_t *ptr, uint8_t message_type)
{
ptr = ws_wh_header_base_write(ptr, ws_wh_lutt_length(), WH_IE_LUTT_TYPE);
*ptr++ = message_type;
//Set 0 for next 5 bytes which will be initializwed by FHSS
memset(ptr, 0, 5); /* Unicast Slot Number 2 bytes, UFSI 3 bytes */
ptr += 5;
return ptr;
}
uint8_t *ws_wh_lus_write(uint8_t *ptr, struct ws_lus_ie *lus_ptr)
{
ptr = ws_wh_header_base_write(ptr, ws_wh_lus_length(), WH_IE_LUS_TYPE);
ptr = common_write_24_bit_inverse(lus_ptr->listen_interval, ptr);
*ptr++ = lus_ptr->channel_plan_tag;
return ptr;
}
uint8_t *ws_wh_flus_write(uint8_t *ptr, struct ws_flus_ie *flus_ptr)
{
ptr = ws_wh_header_base_write(ptr, ws_wh_flus_length(), WH_IE_FLUS_TYPE);
*ptr++ = flus_ptr->dwell_interval;
*ptr++ = flus_ptr->channel_plan_tag;
return ptr;
}
uint8_t *ws_wh_lbt_write(uint8_t *ptr)
{
ptr = ws_wh_header_base_write(ptr, ws_wh_lbt_length(), WH_IE_LBT_TYPE);
//Set 0 for next 5 bytes which will be initializwed by FHSS
memset(ptr, 0, ws_wh_lbt_length()); /* LFN Broadcast Slot Number 2 bytes, LFN Broadcast Interval Offset 3 bytes */
ptr += ws_wh_lbt_length();
return ptr;
}
uint8_t *ws_wh_lbs_write(uint8_t *ptr, struct ws_lbs_ie *lbs_ptr)
{
ptr = ws_wh_header_base_write(ptr, ws_wh_lbs_length(), WH_IE_LBS_TYPE);
ptr = common_write_24_bit_inverse(lbs_ptr->broadcast_interval, ptr);
ptr = common_write_16_bit_inverse(lbs_ptr->broadcast_secheduler_id, ptr);
*ptr++ = lbs_ptr->channel_plan_tag;
return ptr;
}
uint16_t ws_wh_nr_length(struct ws_nr_ie *nr_ptr)
{
uint16_t length;
if (nr_ptr->node_role == WS_NR_ROLE_LFN) {
length = 9;
} else {
length = 3;
}
return length;
}
uint8_t *ws_wh_nr_write(uint8_t *ptr, struct ws_nr_ie *nr_ptr)
{
ptr = ws_wh_header_base_write(ptr, ws_wh_nr_length(nr_ptr), WH_IE_NR_TYPE);
*ptr++ = nr_ptr->node_role;
*ptr++ = nr_ptr->clock_drift;
*ptr++ = nr_ptr->timing_accurancy;
if (nr_ptr->node_role == WS_NR_ROLE_LFN) {
ptr = common_write_24_bit_inverse(nr_ptr->listen_interval_min, ptr);
ptr = common_write_24_bit_inverse(nr_ptr->listen_interval_max, ptr);
}
return ptr;
}
uint8_t *ws_wh_lnd_write(uint8_t *ptr, struct ws_lnd_ie *lnd_ptr)
{
ptr = ws_wh_header_base_write(ptr, ws_wh_lnd_length(), WH_IE_LND_TYPE);
*ptr++ = lnd_ptr->response_threshold;
ptr = common_write_24_bit_inverse(lnd_ptr->response_delay, ptr);
*ptr++ = lnd_ptr->discovery_slot_time;
*ptr++ = lnd_ptr->discovery_slots;
ptr = common_write_16_bit_inverse(lnd_ptr->discovery_first_slot, ptr);
return ptr;
}
uint8_t *ws_wh_lto_write(uint8_t *ptr, struct ws_lto_ie *lto_ptr)
{
ptr = ws_wh_header_base_write(ptr, ws_wh_lto_length(), WH_IE_LTO_TYPE);
ptr = common_write_24_bit_inverse(lto_ptr->offset, ptr);
ptr = common_write_24_bit_inverse(lto_ptr->adjusted_listening_interval, ptr);
return ptr;
}
uint8_t *ws_wh_panid_write(uint8_t *ptr, uint16_t pana_id)
{
ptr = ws_wh_header_base_write(ptr, ws_wh_panid_length(), WH_IE_PANID_TYPE);
//TODO need to be check byte order
ptr = common_write_16_bit_inverse(pana_id, ptr);
return ptr;
}
uint8_t *ws_wp_base_write(uint8_t *ptr, uint16_t length)
{
return mac_ie_payload_base_write(ptr, WS_WP_NESTED_IE, length);
}
static uint8_t ws_wp_channel_info_base_get(ws_generic_channel_info_t *generic_channel_info)
{
uint8_t channel_info_base = 0;
channel_info_base = generic_channel_info->channel_plan;
channel_info_base |= (generic_channel_info->channel_function << 3);
//Set Excluded Channel control part
channel_info_base |= (generic_channel_info->excluded_channel_ctrl << 6);
return channel_info_base;
}
static uint8_t *ws_wp_channel_plan_write(uint8_t *ptr, ws_generic_channel_info_t *generic_channel_info)
{
switch (generic_channel_info->channel_plan) {
case 0:
//Regulator domain and operationg class inline
*ptr++ = generic_channel_info->plan.zero.regulator_domain;
*ptr++ = generic_channel_info->plan.zero.operation_class;
break;
case 1:
//CHo, Channel spasing and number of channel's inline
ptr = common_write_24_bit_inverse(generic_channel_info->plan.one.ch0 * 100, ptr);
*ptr++ = generic_channel_info->plan.one.channel_spacing;
ptr = common_write_16_bit_inverse(generic_channel_info->plan.one.number_of_channel, ptr);
break;
case 2:
*ptr++ = generic_channel_info->plan.two.regulator_domain;
*ptr++ = generic_channel_info->plan.two.channel_plan_id;
break;
default:
break;
}
return ptr;
}
static uint8_t *ws_wp_channel_function_write(uint8_t *ptr, ws_generic_channel_info_t *generic_channel_info)
{
switch (generic_channel_info->channel_function) {
case 0:
//Fixed channel inline
ptr = common_write_16_bit_inverse(generic_channel_info->function.zero.fixed_channel, ptr);
break;
case 1:
case 2:
//No Inline
break;
case 3:
//TODO do this properly
//Hop count + channel hop list
if (generic_channel_info->function.three.channel_list && generic_channel_info->function.three.channel_hop_count) {
*ptr++ = generic_channel_info->function.three.channel_hop_count;
memcpy(ptr, generic_channel_info->function.three.channel_list, generic_channel_info->function.three.channel_hop_count);
ptr += generic_channel_info->function.three.channel_hop_count;
} else {
*ptr++ = 1;
*ptr++ = 0;
}
break;
default:
break;
}
return ptr;
}
static uint8_t *ws_wp_nested_excluded_channel_write(uint8_t *ptr, ws_generic_channel_info_t *generic_channel_info)
{
if (generic_channel_info->excluded_channel_ctrl == WS_EXC_CHAN_CTRL_RANGE) {
uint8_t range_length = generic_channel_info->excluded_channels.range.excluded_range_length;
ws_excluded_channel_range_data_t *range_ptr = generic_channel_info->excluded_channels.range.exluded_range;
*ptr++ = range_length;
while (range_length) {
ptr = common_write_16_bit_inverse(range_ptr->range_start, ptr);
ptr = common_write_16_bit_inverse(range_ptr->range_end, ptr);
range_length--;
range_ptr++;
}
} else if (generic_channel_info->excluded_channel_ctrl == WS_EXC_CHAN_CTRL_BITMASK) {
//Set Mask
uint16_t channel_mask_length = generic_channel_info->excluded_channels.mask.channel_mask_bytes_inline * 8;
for (uint8_t i = 0; i < 8; i++) {
uint32_t mask_value = generic_channel_info->excluded_channels.mask.channel_mask[i];
if (channel_mask_length >= 32) {
ptr = common_write_32_bit(mask_value, ptr);
channel_mask_length -= 32;
} else {
//Write MSB Bits from mask 24-8 top bits
uint8_t move_mask = 0;
while (channel_mask_length) {
*ptr++ = (uint8_t)(mask_value >> (24 - move_mask));
channel_mask_length -= 8;
move_mask += 8;
}
}
if (channel_mask_length == 0) {
break;
}
}
}
return ptr;
}
uint8_t *ws_wp_nested_hopping_schedule_write(uint8_t *ptr, struct ws_hopping_schedule_s *hopping_schedule, bool unicast_schedule)
{
//Calculate length
ws_generic_channel_info_t generic_channel_info;
ws_generic_channel_info_init(hopping_schedule, &generic_channel_info, unicast_schedule);
ws_wp_channel_plan_set(&generic_channel_info, hopping_schedule);
ws_wp_channel_function_set(&generic_channel_info, hopping_schedule, unicast_schedule);
uint16_t length;
if (unicast_schedule) {
length = 3;
} else {
length = 9;
}
length += ws_wp_generic_shedule_length_get(&generic_channel_info);
if (!unicast_schedule) {
ptr = mac_ie_nested_ie_long_base_write(ptr, WP_PAYLOAD_IE_BS_TYPE, length);
ptr = common_write_32_bit_inverse(hopping_schedule->fhss_broadcast_interval, ptr);
ptr = common_write_16_bit_inverse(hopping_schedule->fhss_bsi, ptr);
*ptr++ = hopping_schedule->fhss_bc_dwell_interval;
} else {
ptr = mac_ie_nested_ie_long_base_write(ptr, WP_PAYLOAD_IE_US_TYPE, length);
*ptr++ = hopping_schedule->fhss_uc_dwell_interval;
}
*ptr++ = hopping_schedule->clock_drift;
*ptr++ = hopping_schedule->timing_accurancy;
// Write a generic part of shedule
*ptr++ = ws_wp_channel_info_base_get(&generic_channel_info);
ptr = ws_wp_channel_plan_write(ptr, &generic_channel_info);
ptr = ws_wp_channel_function_write(ptr, &generic_channel_info);
ptr = ws_wp_nested_excluded_channel_write(ptr, &generic_channel_info);
return ptr;
}
uint8_t *ws_wp_nested_vp_write(uint8_t *ptr, uint8_t *vendor_payload, uint16_t vendor_payload_length)
{
if (vendor_payload_length) {
ptr = mac_ie_nested_ie_long_base_write(ptr, WP_PAYLOAD_IE_VP_TYPE, vendor_payload_length);
memcpy(ptr, vendor_payload, vendor_payload_length);
ptr += vendor_payload_length;
}
return ptr;
}
uint8_t *ws_wp_nested_pan_info_write(uint8_t *ptr, struct ws_pan_information_s *pan_congiguration)
{
if (!pan_congiguration) {
return mac_ie_nested_ie_short_base_write(ptr, WP_PAYLOAD_IE_PAN_TYPE, 0);
}
ptr = mac_ie_nested_ie_short_base_write(ptr, WP_PAYLOAD_IE_PAN_TYPE, 5);
ptr = common_write_16_bit_inverse(pan_congiguration->pan_size, ptr);
ptr = common_write_16_bit_inverse(pan_congiguration->routing_cost, ptr);
uint8_t temp8 = 0;
temp8 |= (pan_congiguration->use_parent_bs << 0);
temp8 |= (pan_congiguration->rpl_routing_method << 1);
/* FAN 1.1 specific write */
if (pan_congiguration->version > WS_FAN_VERSION_1_0) {
temp8 |= (pan_congiguration->lfn_window_style << 2);
}
temp8 |= pan_congiguration->version << 5;
*ptr++ = temp8;
return ptr;
}
uint8_t *ws_wp_nested_netname_write(uint8_t *ptr, uint8_t *network_name, uint8_t network_name_length)
{
ptr = mac_ie_nested_ie_short_base_write(ptr, WP_PAYLOAD_IE_NETNAME_TYPE, network_name_length);
if (network_name_length) {
memcpy(ptr, network_name, network_name_length);
ptr += network_name_length;
}
return ptr;
}
uint8_t *ws_wp_nested_pan_ver_write(uint8_t *ptr, struct ws_pan_information_s *pan_congiguration)
{
if (!pan_congiguration) {
return ptr;
}
ptr = mac_ie_nested_ie_short_base_write(ptr, WP_PAYLOAD_IE_PAN_VER_TYPE, 2);
return common_write_16_bit_inverse(pan_congiguration->pan_version, ptr);
}
uint8_t *ws_wp_nested_gtkhash_write(uint8_t *ptr, uint8_t *gtkhash, uint8_t gtkhash_length)
{
ptr = mac_ie_nested_ie_short_base_write(ptr, WP_PAYLOAD_IE_GTKHASH_TYPE, gtkhash_length);
if (gtkhash_length) {
memcpy(ptr, gtkhash, 32);
ptr += 32;
}
return ptr;
}
uint16_t ws_wp_nested_pcap_length(uint8_t list_length)
{
uint16_t lenght = (list_length * 3) + 1;
return lenght;
}
void ws_ie_lib_phy_cap_list_update(struct ws_phy_cap_info *phy_pap, struct ws_pcap_ie *pcap)
{
for (int i = 0; i < phy_pap->length_of_list; i++) {
if (phy_pap->pcap[i].phy_type == pcap->phy_type) {
phy_pap->pcap[i].operating_mode |= pcap->operating_mode;
return;
}
}
if (phy_pap->length_of_list == 7) {
return;
}
phy_pap->pcap[phy_pap->length_of_list].phy_type = pcap->phy_type;
phy_pap->pcap[phy_pap->length_of_list].operating_mode |= pcap->operating_mode;
phy_pap->length_of_list++;
}
ws_pcap_ie_t ws_ie_lib_generate_phy_cap_from_phy_mode_id(uint8_t phy_mode_id)
{
ws_pcap_ie_t pcap;
if (phy_mode_id > 96) {
pcap.operating_mode = 0;
return pcap;
}
if (phy_mode_id < 16) {
pcap.phy_type = WS_PHY_TYPE_ID_FSK;
} else if (phy_mode_id < 32) {
phy_mode_id -= 16;
pcap.phy_type = WS_PHY_TYPE_ID_FSK_FEC;
} else if (phy_mode_id < 48) {
phy_mode_id -= 32;
pcap.phy_type = WS_PHY_TYPE_ID_OFDM1;
} else if (phy_mode_id < 64) {
phy_mode_id -= 48;
pcap.phy_type = WS_PHY_TYPE_ID_OFDM2;
} else if (phy_mode_id < 80) {
phy_mode_id -= 64;
pcap.phy_type = WS_PHY_TYPE_ID_OFDM3;
} else {
phy_mode_id -= 80;
pcap.phy_type = WS_PHY_TYPE_ID_OFDM4;
}
pcap.operating_mode = 1 << phy_mode_id;
return pcap;
}
uint8_t ws_ie_lib_phy_mode_id_get_from_phy_cap(ws_pcap_ie_t *phy_cap)
{
if (!phy_cap->operating_mode) {
return 0;
}
uint8_t phy_mode_id = 0;
for (uint8_t i = 0; i < 16; i++) {
if (phy_cap->operating_mode & (1 << (15 - i))) {
phy_mode_id = 15 - i;
break;
}
}
switch (phy_cap->phy_type) {
case WS_PHY_TYPE_ID_FSK:
break;
case WS_PHY_TYPE_ID_FSK_FEC:
phy_mode_id += 16;
break;
case WS_PHY_TYPE_ID_OFDM1:
phy_mode_id += 32;
break;
case WS_PHY_TYPE_ID_OFDM2:
phy_mode_id += 48;
break;
case WS_PHY_TYPE_ID_OFDM3:
phy_mode_id += 64;
break;
case WS_PHY_TYPE_ID_OFDM4:
phy_mode_id += 80;
break;
default:
break;
}
return phy_mode_id;
}
uint8_t *ws_wp_nested_pcap_write(uint8_t *ptr, struct ws_phy_cap_info *pcap_list)
{
uint16_t lenght = ws_wp_nested_pcap_length(pcap_list->length_of_list);
ptr = mac_ie_nested_ie_short_base_write(ptr, WP_PAYLOAD_IE_PCAP_TYPE, lenght);
*ptr++ = pcap_list->length_of_list;
for (int i = 0; i < pcap_list->length_of_list; i++) {
*ptr++ = pcap_list->pcap[i].phy_type;
ptr = common_write_16_bit_inverse(pcap_list->pcap[i].operating_mode, ptr);
}
return ptr;
}
uint8_t *ws_wp_nested_lfn_version_write(uint8_t *ptr, struct ws_lfnver_ie *ws_lfnver)
{
ptr = mac_ie_nested_ie_short_base_write(ptr, WP_PAYLOAD_IE_LFNVER_TYPE, ws_wp_nested_lfn_version_length());
ptr = common_write_16_bit_inverse(ws_lfnver->lfn_version, ptr);
return ptr;
}
uint16_t ws_wp_lgtk_hash_length(struct ws_lgtkhash_ie *ws_lgtkhash)
{
uint16_t length = 1;
if (ws_lgtkhash->lgtk0) {
length += 8;
}
if (ws_lgtkhash->lgtk1) {
length += 8;
}
if (ws_lgtkhash->lgtk2) {
length += 8;
}
return length;
}
uint8_t *ws_wp_nested_lgtk_hash_write(uint8_t *ptr, struct ws_lgtkhash_ie *ws_lgtkhash)
{
uint16_t length = ws_wp_lgtk_hash_length(ws_lgtkhash);
ptr = mac_ie_nested_ie_short_base_write(ptr, WP_PAYLOAD_IE_LGTKHASH_TYPE, length);
uint8_t temp8 = 0;
temp8 |= (ws_lgtkhash->lgtk0 << 0);
temp8 |= (ws_lgtkhash->lgtk1 << 1);
temp8 |= (ws_lgtkhash->lgtk2 << 2);
temp8 |= (ws_lgtkhash->active_lgtk_index << 3);
*ptr++ = temp8;
if (ws_lgtkhash->lgtk0) {
memcpy(ptr, ws_lgtkhash->lgtk0_hash, 8);
ptr += 8;
}
if (ws_lgtkhash->lgtk1) {
memcpy(ptr, ws_lgtkhash->lgtk1_hash, 8);
ptr += 8;
}
if (ws_lgtkhash->lgtk2) {
memcpy(ptr, ws_lgtkhash->lgtk2_hash, 8);
ptr += 8;
}
return ptr;
}
uint16_t ws_wp_nested_lfn_channel_plan_length(ws_generic_channel_info_t *ws_lcp)
{
uint16_t length = 1; //Channel Plan Tag
length += ws_wp_generic_shedule_length_get(ws_lcp);
return length;
}
uint8_t *ws_wp_nested_lfn_channel_plan_write(uint8_t *ptr, ws_generic_channel_info_t *ws_lcp, uint8_t plan_tag_id)
{
uint16_t length = ws_wp_nested_lfn_channel_plan_length(ws_lcp);
ptr = mac_ie_nested_ie_long_base_write(ptr, WP_PAYLOAD_IE_LFN_CHANNEL_PLAN_TYPE, length);
*ptr++ = plan_tag_id;
*ptr++ = ws_wp_channel_info_base_get(ws_lcp);
ptr = ws_wp_channel_plan_write(ptr, ws_lcp);
ptr = ws_wp_channel_function_write(ptr, ws_lcp);
ptr = ws_wp_nested_excluded_channel_write(ptr, ws_lcp);
return ptr;
}
bool ws_wh_utt_read(uint8_t *data, uint16_t length, struct ws_utt_ie *utt_ie)
{
mac_header_IE_t utt_ie_data;
utt_ie_data.id = MAC_HEADER_ASSIGNED_EXTERNAL_ORG_IE_ID;
if (4 > mac_ie_header_sub_id_discover(data, length, &utt_ie_data, WH_IE_UTT_TYPE)) {
// NO UTT header
return false;
}
data = utt_ie_data.content_ptr;
utt_ie->message_type = *data++;
utt_ie->ufsi = common_read_24_bit_inverse(data);
return true;
}
bool ws_wh_bt_read(uint8_t *data, uint16_t length, struct ws_bt_ie *bt_ie)
{
mac_header_IE_t btt_ie_data;
btt_ie_data.id = MAC_HEADER_ASSIGNED_EXTERNAL_ORG_IE_ID;
if (5 > mac_ie_header_sub_id_discover(data, length, &btt_ie_data, WH_IE_BT_TYPE)) {
return false;
}
data = btt_ie_data.content_ptr;
bt_ie->broadcast_slot_number = common_read_16_bit_inverse(data);
bt_ie->broadcast_interval_offset = common_read_24_bit_inverse(data + 2);
return true;
}
bool ws_wh_fc_read(uint8_t *data, uint16_t length, struct ws_fc_ie *fc_ie)
{
mac_header_IE_t fc_ie_data;
fc_ie_data.id = MAC_HEADER_ASSIGNED_EXTERNAL_ORG_IE_ID;
if (2 > mac_ie_header_sub_id_discover(data, length, &fc_ie_data, WH_IE_FC_TYPE)) {
return false;
}
data = fc_ie_data.content_ptr;
fc_ie->tx_flow_ctrl = *data++;
fc_ie->rx_flow_ctrl = *data;
return true;
}
bool ws_wh_rsl_read(uint8_t *data, uint16_t length, int8_t *rsl)
{
mac_header_IE_t rsl_ie_data;
rsl_ie_data.id = MAC_HEADER_ASSIGNED_EXTERNAL_ORG_IE_ID;
if (1 > mac_ie_header_sub_id_discover(data, length, &rsl_ie_data, WH_IE_RSL_TYPE)) {
return false;
}
*rsl = *rsl_ie_data.content_ptr;
return true;
}
bool ws_wh_ea_read(uint8_t *data, uint16_t length, uint8_t *eui64)
{
mac_header_IE_t rsl_ie_data;
rsl_ie_data.id = MAC_HEADER_ASSIGNED_EXTERNAL_ORG_IE_ID;
if (8 > mac_ie_header_sub_id_discover(data, length, &rsl_ie_data, WH_IE_EA_TYPE)) {
return false;
}
memcpy(eui64, rsl_ie_data.content_ptr, 8);
return true;
}
bool ws_wh_lutt_read(uint8_t *data, uint16_t length, struct ws_lutt_ie *ws_lutt)
{
mac_header_IE_t lutt_ie_data;
lutt_ie_data.id = MAC_HEADER_ASSIGNED_EXTERNAL_ORG_IE_ID;
if (ws_wh_lutt_length() > mac_ie_header_sub_id_discover(data, length, &lutt_ie_data, WH_IE_LUTT_TYPE)) {
return false;
}
data = lutt_ie_data.content_ptr;
ws_lutt->message_type = *data++;
ws_lutt->slot_number = common_read_16_bit_inverse(data);
ws_lutt->interval_offset = common_read_24_bit_inverse(data + 2);
return true;
}
bool ws_wh_lus_read(uint8_t *data, uint16_t length, struct ws_lus_ie *lus_ptr)
{
mac_header_IE_t lus_ie_data;
lus_ie_data.id = MAC_HEADER_ASSIGNED_EXTERNAL_ORG_IE_ID;
if (ws_wh_lus_length() > mac_ie_header_sub_id_discover(data, length, &lus_ie_data, WH_IE_LUS_TYPE)) {
return false;
}
data = lus_ie_data.content_ptr;
lus_ptr->listen_interval = common_read_24_bit_inverse(data);
data += 3;
lus_ptr->channel_plan_tag = *data;
return true;
}
bool ws_wh_flus_read(uint8_t *data, uint16_t length, struct ws_flus_ie *flus_ptr)
{
mac_header_IE_t flus_ie_data;
flus_ie_data.id = MAC_HEADER_ASSIGNED_EXTERNAL_ORG_IE_ID;
if (ws_wh_flus_length() > mac_ie_header_sub_id_discover(data, length, &flus_ie_data, WH_IE_FLUS_TYPE)) {
return false;
}
data = flus_ie_data.content_ptr;
flus_ptr->dwell_interval = *data++;
flus_ptr->channel_plan_tag = *data;
return true;
}
bool ws_wh_lbt_read(uint8_t *data, uint16_t length, struct ws_lbt_ie *ws_lbt)
{
mac_header_IE_t lbt_ie_data;
lbt_ie_data.id = MAC_HEADER_ASSIGNED_EXTERNAL_ORG_IE_ID;
if (ws_wh_lbt_length() > mac_ie_header_sub_id_discover(data, length, &lbt_ie_data, WH_IE_LBT_TYPE)) {
return false;
}
data = lbt_ie_data.content_ptr;
ws_lbt->slot_number = common_read_16_bit_inverse(data);
ws_lbt->interval_offset = common_read_24_bit_inverse(data + 2);
return true;
}
bool ws_wh_lbs_read(uint8_t *data, uint16_t length, struct ws_lbs_ie *lbs_ptr)
{
mac_header_IE_t lbs_ie_data;
lbs_ie_data.id = MAC_HEADER_ASSIGNED_EXTERNAL_ORG_IE_ID;
if (ws_wh_lbs_length() > mac_ie_header_sub_id_discover(data, length, &lbs_ie_data, WH_IE_LBS_TYPE)) {
return false;
}
data = lbs_ie_data.content_ptr;
lbs_ptr->broadcast_interval = common_read_24_bit_inverse(data);
data += 3;
lbs_ptr->broadcast_secheduler_id = common_read_16_bit_inverse(data);
data += 2;
lbs_ptr->channel_plan_tag = *data;
return true;
}
bool ws_wh_nr_read(uint8_t *data, uint16_t length, struct ws_nr_ie *nr_ptr)
{
mac_header_IE_t nr_ie_data;
nr_ie_data.id = MAC_HEADER_ASSIGNED_EXTERNAL_ORG_IE_ID;
if (3 > mac_ie_header_sub_id_discover(data, length, &nr_ie_data, WH_IE_NR_TYPE)) {
return false;
}
data = nr_ie_data.content_ptr;
nr_ptr->node_role = *data++ & 7;
nr_ptr->clock_drift = *data++;
nr_ptr->timing_accurancy = *data++;
switch (nr_ptr->node_role) {
case WS_NR_ROLE_BR:
break;
case WS_NR_ROLE_ROUTER:
break;
case WS_NR_ROLE_LFN:
if (9 > nr_ie_data.length) {
return false;
}
nr_ptr->listen_interval_min = common_read_24_bit_inverse(data);
nr_ptr->listen_interval_max = common_read_24_bit_inverse(data + 3);
break;
default:
return false;
}
return true;
}
bool ws_wh_lnd_read(uint8_t *data, uint16_t length, struct ws_lnd_ie *lnd_ptr)
{
mac_header_IE_t lnd_ie_data;
lnd_ie_data.id = MAC_HEADER_ASSIGNED_EXTERNAL_ORG_IE_ID;
if (ws_wh_lnd_length() > mac_ie_header_sub_id_discover(data, length, &lnd_ie_data, WH_IE_LND_TYPE)) {
return false;
}
data = lnd_ie_data.content_ptr;
lnd_ptr->response_threshold = *data++;
lnd_ptr->response_delay = common_read_24_bit_inverse(data);
data += 3;
lnd_ptr->discovery_slot_time = *data++;
lnd_ptr->discovery_slots = *data++;
lnd_ptr->discovery_first_slot = common_read_16_bit_inverse(data);
return true;
}
bool ws_wh_lto_read(uint8_t *data, uint16_t length, struct ws_lto_ie *lto_ptr)
{
mac_header_IE_t lto_ie_data;
lto_ie_data.id = MAC_HEADER_ASSIGNED_EXTERNAL_ORG_IE_ID;
if (ws_wh_lto_length() > mac_ie_header_sub_id_discover(data, length, &lto_ie_data, WH_IE_LTO_TYPE)) {
return false;
}
data = lto_ie_data.content_ptr;
lto_ptr->offset = common_read_24_bit_inverse(data);
lto_ptr->adjusted_listening_interval = common_read_24_bit_inverse(data + 3);
return true;
}
bool ws_wh_panid_read(uint8_t *data, uint16_t length, struct ws_panid_ie *ws_panid)
{
mac_header_IE_t panid_ie_data;
panid_ie_data.id = MAC_HEADER_ASSIGNED_EXTERNAL_ORG_IE_ID;
if (ws_wh_panid_length() > mac_ie_header_sub_id_discover(data, length, &panid_ie_data, WH_IE_PANID_TYPE)) {
return false;
}
ws_panid->panid = common_read_16_bit_inverse(panid_ie_data.content_ptr);
return true;
}
static uint8_t *ws_channel_plan_zero_read(uint8_t *ptr, ws_channel_plan_zero_t *plan)
{
plan->regulator_domain = *ptr++;
plan->operation_class = *ptr++;
return ptr;
}
static uint8_t *ws_channel_plan_one_read(uint8_t *ptr, ws_channel_plan_one_t *plan)
{
plan->ch0 = common_read_24_bit_inverse(ptr);
plan->ch0 /= 100;
ptr += 3;
plan->channel_spacing = *ptr++;
plan->number_of_channel = common_read_16_bit_inverse(ptr);
ptr += 2;
return ptr;
}
static uint8_t *ws_channel_plan_two_read(uint8_t *ptr, ws_channel_plan_two_t *plan)
{
plan->regulator_domain = *ptr++;
plan->channel_plan_id = *ptr++;
return ptr;
}
static uint8_t *ws_channel_function_zero_read(uint8_t *ptr, ws_channel_function_zero_t *plan)
{
plan->fixed_channel = common_read_16_bit_inverse(ptr);
return ptr + 2;
}
static uint8_t *ws_channel_function_three_read(uint8_t *ptr, ws_channel_function_three_t *plan)
{
plan->channel_hop_count = *ptr++;
plan->channel_list = ptr++;
return ptr;
}
bool ws_wp_nested_us_read(uint8_t *data, uint16_t length, struct ws_us_ie *us_ie)
{
mac_nested_payload_IE_t nested_payload_ie;
nested_payload_ie.id = WP_PAYLOAD_IE_US_TYPE;
nested_payload_ie.type_long = true;
if (4 > mac_ie_nested_discover(data, length, &nested_payload_ie)) {
return false;
}
data = nested_payload_ie.content_ptr;
us_ie->dwell_interval = *data++;
us_ie->clock_drift = *data++;
us_ie->timing_accurancy = *data++;
us_ie->channel_plan = (*data & 3);
us_ie->channel_function = (*data & 0x38) >> 3;
us_ie->excluded_channel_ctrl = (*data & 0xc0) >> 6;
data++;
uint16_t info_length = 0;
nested_payload_ie.length -= 4;
info_length = ws_channel_plan_length(us_ie->channel_plan);
if (nested_payload_ie.length < info_length) {
return false;
}
nested_payload_ie.length -= info_length;
switch (us_ie->channel_plan) {
case 0:
data = ws_channel_plan_zero_read(data, &us_ie->plan.zero);
break;
case 1:
data = ws_channel_plan_one_read(data, &us_ie->plan.one);
break;
case 2:
data = ws_channel_plan_two_read(data, &us_ie->plan.two);
break;
default:
return false;
}
info_length = ws_channel_function_length(us_ie->channel_function, 0);
if (nested_payload_ie.length < info_length) {
return false;
}
nested_payload_ie.length -= info_length;
switch (us_ie->channel_function) {
case 0:
data = ws_channel_function_zero_read(data, &us_ie->function.zero);
break;
case 1:
case 2:
break;
case 3:
data = ws_channel_function_three_read(data, &us_ie->function.three);
info_length = us_ie->function.three.channel_hop_count;
if (nested_payload_ie.length < info_length) {
return false;
}
nested_payload_ie.length -= info_length;
data += info_length;
break;
default:
return false;
}
switch (us_ie->excluded_channel_ctrl) {
case WS_EXC_CHAN_CTRL_NONE:
break;
case WS_EXC_CHAN_CTRL_RANGE:
us_ie->excluded_channels.range.number_of_range = *data;
if (nested_payload_ie.length < (us_ie->excluded_channels.range.number_of_range * 4) + 1) {
return false;
}
//Set Range start after validation
us_ie->excluded_channels.range.range_start = data + 1;
break;
case WS_EXC_CHAN_CTRL_BITMASK:
if (us_ie->channel_plan == 1) {
us_ie->excluded_channels.mask.mask_len_inline = ((us_ie->plan.one.number_of_channel + 7) / 8);
if (us_ie->excluded_channels.mask.mask_len_inline != nested_payload_ie.length) {
//Channel mask length is not correct
return false;
}
} else {
us_ie->excluded_channels.mask.mask_len_inline = nested_payload_ie.length;
}
us_ie->excluded_channels.mask.channel_mask = data;
break;
default:
return false;
}
return true;
}
bool ws_wp_nested_bs_read(uint8_t *data, uint16_t length, struct ws_bs_ie *bs_ie)
{
mac_nested_payload_IE_t nested_payload_ie;
nested_payload_ie.id = WP_PAYLOAD_IE_BS_TYPE;
nested_payload_ie.type_long = true;
if (10 > mac_ie_nested_discover(data, length, &nested_payload_ie)) {
return false;
}
data = nested_payload_ie.content_ptr;
bs_ie->broadcast_interval = common_read_32_bit_inverse(data);
bs_ie->broadcast_schedule_identifier = common_read_16_bit_inverse(data + 4);
data += 6;
bs_ie->dwell_interval = *data++;
bs_ie->clock_drift = *data++;
bs_ie->timing_accurancy = *data++;
bs_ie->channel_plan = (*data & 3);
bs_ie->channel_function = (*data & 0x38) >> 3;
bs_ie->excluded_channel_ctrl = (*data & 0xc0) >> 6;
data++;
nested_payload_ie.length -= 10;
uint16_t info_length = 0;
info_length = ws_channel_plan_length(bs_ie->channel_plan);
if (nested_payload_ie.length < info_length) {
return false;
}
nested_payload_ie.length -= info_length;
switch (bs_ie->channel_plan) {
case 0:
data = ws_channel_plan_zero_read(data, &bs_ie->plan.zero);
break;
case 1:
data = ws_channel_plan_one_read(data, &bs_ie->plan.one);
break;
case 2:
data = ws_channel_plan_two_read(data, &bs_ie->plan.two);
break;
default:
return false;
}
info_length = ws_channel_function_length(bs_ie->channel_function, 0);
if (nested_payload_ie.length < info_length) {
return false;
}
nested_payload_ie.length -= info_length;
switch (bs_ie->channel_function) {
case 0:
data = ws_channel_function_zero_read(data, &bs_ie->function.zero);
break;
case 1:
case 2:
break;
case 3:
data = ws_channel_function_three_read(data, &bs_ie->function.three);
info_length = bs_ie->function.three.channel_hop_count;
if (nested_payload_ie.length < info_length) {
return false;
}
nested_payload_ie.length -= info_length;
data += info_length;
break;
default:
return false;
}
return true;
}
bool ws_wp_nested_pan_read(uint8_t *data, uint16_t length, struct ws_pan_information_s *pan_congiguration)
{
mac_nested_payload_IE_t nested_payload_ie;
nested_payload_ie.id = WP_PAYLOAD_IE_PAN_TYPE;
nested_payload_ie.type_long = false;
if (5 > mac_ie_nested_discover(data, length, &nested_payload_ie)) {
return false;
}
pan_congiguration->pan_size = common_read_16_bit_inverse(nested_payload_ie.content_ptr);
pan_congiguration->routing_cost = common_read_16_bit_inverse(nested_payload_ie.content_ptr + 2);
pan_congiguration->use_parent_bs = (nested_payload_ie.content_ptr[4] & 0x01) == 0x01;
pan_congiguration->rpl_routing_method = (nested_payload_ie.content_ptr[4] & 0x02) == 0x02;
pan_congiguration->version = (nested_payload_ie.content_ptr[4] & 0xe0) >> 5;
if (pan_congiguration->version > WS_FAN_VERSION_1_0) {
/* FAN 1.1 specific read */
pan_congiguration->lfn_window_style = (nested_payload_ie.content_ptr[4] & 0x04) == 0x04;
} else {
pan_congiguration->lfn_window_style = false; //Set false for FAN 1.0
}
return true;
}
bool ws_wp_nested_pan_version_read(uint8_t *data, uint16_t length, uint16_t *pan_version)
{
mac_nested_payload_IE_t nested_payload_ie;
nested_payload_ie.id = WP_PAYLOAD_IE_PAN_VER_TYPE;
nested_payload_ie.type_long = false;
if (2 > mac_ie_nested_discover(data, length, &nested_payload_ie)) {
return false;
}
*pan_version = common_read_16_bit_inverse(nested_payload_ie.content_ptr);
return true;
}
uint8_t *ws_wp_nested_gtkhash_read(uint8_t *data, uint16_t length)
{
mac_nested_payload_IE_t nested_payload_ie;
nested_payload_ie.id = WP_PAYLOAD_IE_GTKHASH_TYPE;
nested_payload_ie.type_long = false;
if (mac_ie_nested_discover(data, length, &nested_payload_ie) != 32) {
return NULL;
}
return nested_payload_ie.content_ptr;
}
bool ws_wp_nested_network_name_read(uint8_t *data, uint16_t length, ws_wp_network_name_t *network_name)
{
mac_nested_payload_IE_t nested_payload_ie;
nested_payload_ie.id = WP_PAYLOAD_IE_NETNAME_TYPE;
nested_payload_ie.type_long = false;
if (0 == mac_ie_nested_discover(data, length, &nested_payload_ie)) {
return false;
} else if (nested_payload_ie.length > 32) {
//Too long name
return false;
}
network_name->network_name = nested_payload_ie.content_ptr;
network_name->network_name_length = nested_payload_ie.length;
return true;
}
bool ws_wp_nested_pcap_read(uint8_t *data, uint16_t length, struct ws_phy_cap_info *ws_pcap_list)
{
#ifdef HAVE_WS_VERSION_1_1
mac_nested_payload_IE_t nested_payload_ie;
nested_payload_ie.id = WP_PAYLOAD_IE_PCAP_TYPE;
nested_payload_ie.type_long = false;
if (4 > mac_ie_nested_discover(data, length, &nested_payload_ie)) {
return false;
}
//Read & Validate length
data = nested_payload_ie.content_ptr;
uint8_t length_of_cap = *data++ & 0x07;
if (nested_payload_ie.length < length_of_cap * 3) {
return false;
}
ws_pcap_list->length_of_list = length_of_cap;
for (uint8_t i = 0; i < length_of_cap; i++) {
ws_pcap_list->pcap[i].phy_type = *data++ & 7;
ws_pcap_list->pcap[i].operating_mode = common_read_16_bit_inverse(data);
data += 2;
}
return true;
#else
(void) data;
(void) length;
(void) ws_pcap_list;
return false;
#endif
}
bool ws_wp_nested_lfn_version_read(uint8_t *data, uint16_t length, struct ws_lfnver_ie *ws_lfnver)
{
mac_nested_payload_IE_t nested_payload_ie;
nested_payload_ie.id = WP_PAYLOAD_IE_LFNVER_TYPE;
nested_payload_ie.type_long = false;
if (ws_wp_nested_lfn_version_length() > mac_ie_nested_discover(data, length, &nested_payload_ie)) {
return false;
}
ws_lfnver->lfn_version = common_read_16_bit_inverse(nested_payload_ie.content_ptr);
return true;
}
bool ws_wp_nested_lgtk_hash_read(uint8_t *data, uint16_t length, struct ws_lgtkhash_ie *ws_lgtkhash)
{
mac_nested_payload_IE_t nested_payload_ie;
nested_payload_ie.id = WP_PAYLOAD_IE_LGTKHASH_TYPE;
nested_payload_ie.type_long = false;
if (1 > mac_ie_nested_discover(data, length, &nested_payload_ie)) {
return false;
}
data = nested_payload_ie.content_ptr;
ws_lgtkhash->lgtk0 = (*data & 0x01) == 0x01;
ws_lgtkhash->lgtk1 = (*data & 0x02) == 0x02;
ws_lgtkhash->lgtk2 = (*data & 0x04) == 0x04;
ws_lgtkhash->active_lgtk_index = (*data & 0x18) >> 3;
if (ws_wp_lgtk_hash_length(ws_lgtkhash) > nested_payload_ie.length) {
return false;
}
data++;
if (ws_lgtkhash->lgtk0) {
ws_lgtkhash->lgtk0_hash = data;
data += 8;
} else {
ws_lgtkhash->lgtk0_hash = NULL;
}
if (ws_lgtkhash->lgtk1) {
ws_lgtkhash->lgtk1_hash = data;
data += 8;
} else {
ws_lgtkhash->lgtk1_hash = NULL;
}
if (ws_lgtkhash->lgtk2) {
ws_lgtkhash->lgtk2_hash = data;
} else {
ws_lgtkhash->lgtk2_hash = NULL;
}
return true;
}
bool ws_wp_nested_lfn_channel_plan_read(uint8_t *data, uint16_t length, ws_generic_channel_info_t *ws_lcp, uint8_t plan_tag_id)
{
mac_nested_payload_IE_t nested_payload_ie;
nested_payload_ie.id = WP_PAYLOAD_IE_LFN_CHANNEL_PLAN_TYPE;
nested_payload_ie.type_long = true;
if (1 < mac_ie_nested_tagged_discover(data, length, &nested_payload_ie, plan_tag_id)) {
return false;
}
//Parse Channel Plan, function and excluded channel
data = nested_payload_ie.content_ptr;
ws_lcp->channel_plan = (*data & 3);
ws_lcp->channel_function = (*data & 0x38) >> 3;
ws_lcp->excluded_channel_ctrl = (*data & 0xc0) >> 6;
data++;
nested_payload_ie.length--;
uint16_t info_length = 0;
info_length = ws_channel_plan_length(ws_lcp->channel_plan);
if (nested_payload_ie.length < info_length) {
return false;
}
nested_payload_ie.length -= info_length;
switch (ws_lcp->channel_plan) {
case 0:
data = ws_channel_plan_zero_read(data, &ws_lcp->plan.zero);
break;
case 1:
data = ws_channel_plan_one_read(data, &ws_lcp->plan.one);
break;
case 2:
data = ws_channel_plan_two_read(data, &ws_lcp->plan.two);
break;
default:
return false;
}
info_length = ws_channel_function_length(ws_lcp->channel_function, 0);
if (nested_payload_ie.length < info_length) {
return false;
}
nested_payload_ie.length -= info_length;
switch (ws_lcp->channel_function) {
case 0:
data = ws_channel_function_zero_read(data, &ws_lcp->function.zero);
break;
case 1:
case 2:
break;
case 3:
data = ws_channel_function_three_read(data, &ws_lcp->function.three);
info_length = ws_lcp->function.three.channel_hop_count;
if (nested_payload_ie.length < info_length) {
return false;
}
nested_payload_ie.length -= info_length;
data += info_length;
break;
default:
return false;
}
switch (ws_lcp->excluded_channel_ctrl) {
case WS_EXC_CHAN_CTRL_NONE:
break;
case WS_EXC_CHAN_CTRL_RANGE:
ws_lcp->excluded_channels.range_in.number_of_range = *data;
if (nested_payload_ie.length < (ws_lcp->excluded_channels.range_in.number_of_range * 4) + 1) {
return false;
}
//Set Range start after validation
ws_lcp->excluded_channels.range_in.range_start = data + 1;
break;
case WS_EXC_CHAN_CTRL_BITMASK:
if (ws_lcp->channel_plan == 1) {
ws_lcp->excluded_channels.mask_in.mask_len_inline = ((ws_lcp->plan.one.number_of_channel + 7) / 8);
if (ws_lcp->excluded_channels.mask_in.mask_len_inline != nested_payload_ie.length) {
//Channel mask length is not correct
return false;
}
} else {
ws_lcp->excluded_channels.mask_in.mask_len_inline = nested_payload_ie.length;
}
ws_lcp->excluded_channels.mask_in.channel_mask = data;
break;
default:
return false;
}
return true;
}