hal_nordic/drivers/nrf_802154/driver/src/mac_features/ack_generator/nrf_802154_ack_data.c

752 lines
25 KiB
C

/*
* Copyright (c) 2018 - 2021, Nordic Semiconductor ASA
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
/**
* @file
* This file implements procedures to set pending bit and 802.15.4-2015 information
* elements in nRF 802.15.4 radio driver.
*
*/
#include "nrf_802154_ack_data.h"
#include <assert.h>
#include <string.h>
#include "mac_features/nrf_802154_frame_parser.h"
#include "nrf_802154_config.h"
#include "nrf_802154_const.h"
/// Maximum number of Short Addresses of nodes for which there is ACK data to set.
#define NUM_SHORT_ADDRESSES NRF_802154_PENDING_SHORT_ADDRESSES
/// Maximum number of Extended Addresses of nodes for which there is ACK data to set.
#define NUM_EXTENDED_ADDRESSES NRF_802154_PENDING_EXTENDED_ADDRESSES
/// Structure representing pending bit setting variables.
typedef struct
{
bool enabled; /// If setting pending bit is enabled.
uint8_t short_addr[NUM_SHORT_ADDRESSES][SHORT_ADDRESS_SIZE]; /// Array of short addresses of nodes for which there is pending data in the buffer.
uint8_t extended_addr[NUM_EXTENDED_ADDRESSES][EXTENDED_ADDRESS_SIZE]; /// Array of extended addresses of nodes for which there is pending data in the buffer.
uint32_t num_of_short_addr; /// Current number of short addresses of nodes for which there is pending data in the buffer.
uint32_t num_of_ext_addr; /// Current number of extended addresses of nodes for which there is pending data in the buffer.
} pending_bit_arrays_t;
// Structure representing a single IE record.
typedef struct
{
uint8_t p_data[NRF_802154_MAX_ACK_IE_SIZE]; /// Pointer to IE data buffer.
uint8_t len; /// Length of the buffer.
} ie_data_t;
// Structure representing IE records sent in an ACK message to a given short address.
typedef struct
{
uint8_t addr[SHORT_ADDRESS_SIZE]; /// Short address of peer node.
ie_data_t ie_data; /// Pointer to IE records.
} ack_short_ie_data_t;
// Structure representing IE records sent in an ACK message to a given extended address.
typedef struct
{
uint8_t addr[EXTENDED_ADDRESS_SIZE]; /// Extended address of peer node.
ie_data_t ie_data; /// Pointer to IE records.
} ack_ext_ie_data_t;
// Structure representing IE data setting variables.
typedef struct
{
ack_short_ie_data_t short_data[NUM_SHORT_ADDRESSES]; /// Array of short addresses and IE records sent to these addresses.
ack_ext_ie_data_t ext_data[NUM_EXTENDED_ADDRESSES]; /// Array of extended addresses and IE records sent to these addresses.
uint32_t num_of_short_data; /// Current number of short addresses stored in @p short_data.
uint32_t num_of_ext_data; /// Current number of extended addresses stored in @p ext_data.
} ie_arrays_t;
// TODO: Combine below arrays to perform binary search only once per Ack generation.
static pending_bit_arrays_t m_pending_bit;
static ie_arrays_t m_ie;
static nrf_802154_src_addr_match_t m_src_matching_method;
/***************************************************************************************************
* @section Array handling helper functions
**************************************************************************************************/
/**
* @brief Compare two extended addresses.
*
* @param[in] p_first_addr Pointer to a first address that should be compared.
* @param[in] p_second_addr Pointer to a second address that should be compared.
*
* @retval -1 First address is less than the second address.
* @retval 0 First address is equal to the second address.
* @retval 1 First address is greater than the second address.
*/
static int8_t extended_addr_compare(const uint8_t * p_first_addr, const uint8_t * p_second_addr)
{
uint32_t first_addr;
uint32_t second_addr;
// Compare extended address in two steps to prevent unaligned access error.
for (uint32_t i = 0; i < EXTENDED_ADDRESS_SIZE / sizeof(uint32_t); i++)
{
first_addr = *(uint32_t *)(p_first_addr + (i * sizeof(uint32_t)));
second_addr = *(uint32_t *)(p_second_addr + (i * sizeof(uint32_t)));
if (first_addr < second_addr)
{
return -1;
}
else if (first_addr > second_addr)
{
return 1;
}
}
return 0;
}
/**
* @brief Compare two short addresses.
*
* @param[in] p_first_addr Pointer to a first address that should be compared.
* @param[in] p_second_addr Pointer to a second address that should be compared.
*
* @retval -1 First address is less than the second address.
* @retval 0 First address is equal to the second address.
* @retval 1 First address is greater than the second address.
*/
static int8_t short_addr_compare(const uint8_t * p_first_addr, const uint8_t * p_second_addr)
{
uint16_t first_addr = *(uint16_t *)(p_first_addr);
uint16_t second_addr = *(uint16_t *)(p_second_addr);
if (first_addr < second_addr)
{
return -1;
}
else if (first_addr > second_addr)
{
return 1;
}
else
{
return 0;
}
}
/**
* @brief Compare two addresses.
*
* @param[in] p_first_addr Pointer to a first address that should be compared.
* @param[in] p_second_addr Pointer to a second address that should be compared.
* @param[in] extended Indication if @p p_first_addr and @p p_second_addr are extended or short addresses.
*
* @retval -1 First address is less than the second address.
* @retval 0 First address is equal to the second address.
* @retval 1 First address is greater than the second address.
*/
static int8_t addr_compare(const uint8_t * p_first_addr,
const uint8_t * p_second_addr,
bool extended)
{
if (extended)
{
return extended_addr_compare(p_first_addr, p_second_addr);
}
else
{
return short_addr_compare(p_first_addr, p_second_addr);
}
}
/**
* @brief Perform a binary search for an address in a list of addresses.
*
* @param[in] p_addr Pointer to an address that is searched for.
* @param[in] p_addr_array Pointer to a list of addresses to be searched.
* @param[out] p_location If the address @p p_addr appears in the list, this is its index in the address list.
* Otherwise, it is the index which @p p_addr would have if it was placed in the list
* (ascending order assumed).
* @param[in] extended Indication if @p p_addr is an extended or a short addresses.
*
* @retval true Address @p p_addr is in the list.
* @retval false Address @p p_addr is not in the list.
*/
static bool addr_binary_search(const uint8_t * p_addr,
const uint8_t * p_addr_array,
uint32_t * p_location,
nrf_802154_ack_data_t data_type,
bool extended)
{
uint32_t addr_array_len = 0;
uint8_t entry_size = 0;
switch (data_type)
{
case NRF_802154_ACK_DATA_PENDING_BIT:
entry_size = extended ? EXTENDED_ADDRESS_SIZE : SHORT_ADDRESS_SIZE;
addr_array_len = extended ?
m_pending_bit.num_of_ext_addr : m_pending_bit.num_of_short_addr;
break;
case NRF_802154_ACK_DATA_IE:
entry_size = extended ? sizeof(ack_ext_ie_data_t) : sizeof(ack_short_ie_data_t);
addr_array_len = extended ? m_ie.num_of_ext_data : m_ie.num_of_short_data;
break;
default:
assert(false);
break;
}
// The actual algorithm
int32_t low = 0;
uint32_t midpoint = 0;
int32_t high = addr_array_len;
while (high >= low)
{
midpoint = (uint32_t)(low + (high - low) / 2);
if (midpoint >= addr_array_len)
{
break;
}
switch (addr_compare(p_addr, p_addr_array + entry_size * midpoint, extended))
{
case -1:
high = (int32_t)(midpoint - 1);
break;
case 0:
*p_location = midpoint;
return true;
case 1:
low = (int32_t)(midpoint + 1);
break;
default:
break;
}
}
/* If in the last iteration of the loop the last case was utilized, it means that the midpoint
* found by the algorithm is less than the address to be added. The midpoint should be therefore
* shifted to the next position. As a simplified example, a { 1, 3, 4 } array can be considered.
* Suppose that a number equal to 2 is about to be added to the array. At the beginning of the
* last iteration, midpoint is equal to 1 and low and high are equal to 0. Midpoint is then set
* to 0 and with last case being utilized, low is set to 1. However, midpoint equal to 0 is
* incorrect, as in the last iteration first element of the array proves to be less than the
* element to be added to the array. With the below code, midpoint is then shifted to 1. */
if ((uint32_t)low == midpoint + 1)
{
midpoint++;
}
*p_location = midpoint;
return false;
}
/**
* @brief Find an address in a list of addresses.
*
* @param[in] p_addr Pointer to an address that is searched for.
* @param[out] p_location If the address @p p_addr appears in the list, this is its index in the address list.
* Otherwise, it is the index which @p p_addr would have if it was placed in the list
* (ascending order assumed).
* @param[in] extended Indication if @p p_addr is an extended or a short addresses.
*
* @retval true Address @p p_addr is in the list.
* @retval false Address @p p_addr is not in the list.
*/
static bool addr_index_find(const uint8_t * p_addr,
uint32_t * p_location,
nrf_802154_ack_data_t data_type,
bool extended)
{
uint8_t * p_addr_array;
bool valid_data_type = true;
switch (data_type)
{
case NRF_802154_ACK_DATA_PENDING_BIT:
p_addr_array = extended ? (uint8_t *)m_pending_bit.extended_addr :
(uint8_t *)m_pending_bit.short_addr;
break;
case NRF_802154_ACK_DATA_IE:
p_addr_array = extended ? (uint8_t *)m_ie.ext_data : (uint8_t *)m_ie.short_data;
break;
default:
valid_data_type = false;
assert(false);
break;
}
if (!valid_data_type)
{
return false;
}
return addr_binary_search(p_addr, p_addr_array, p_location, data_type, extended);
}
/**
* @brief Thread implementation of the address matching algorithm.
*
* @param[in] p_frame Pointer to the frame for which the ACK frame is being prepared.
*
* @retval true Pending bit is to be set.
* @retval false Pending bit is to be cleared.
*/
static bool addr_match_thread(const uint8_t * p_frame)
{
bool extended;
uint32_t location;
const uint8_t * p_src_addr = nrf_802154_frame_parser_src_addr_get(p_frame, &extended);
// The pending bit is set by default.
if (!m_pending_bit.enabled || (NULL == p_src_addr))
{
return true;
}
return addr_index_find(p_src_addr, &location, NRF_802154_ACK_DATA_PENDING_BIT, extended);
}
/**
* @brief Zigbee implementation of the address matching algorithm.
*
* @param[in] p_frame Pointer to the frame for which the ACK frame is being prepared.
*
* @retval true Pending bit is to be set.
* @retval false Pending bit is to be cleared.
*/
static bool addr_match_zigbee(const uint8_t * p_frame)
{
uint8_t frame_type;
nrf_802154_frame_parser_mhr_data_t mhr_fields;
uint32_t location;
const uint8_t * p_cmd = p_frame;
bool ret = false;
// If ack data generator module is disabled do not perform check, return true by default.
if (!m_pending_bit.enabled)
{
return true;
}
// Check the frame type.
frame_type = (p_frame[FRAME_TYPE_OFFSET] & FRAME_TYPE_MASK);
// Parse the MAC header and retrieve the command type.
if (nrf_802154_frame_parser_mhr_parse(p_frame, &mhr_fields))
{
// Note: Security header is not included in the offset.
// If security is to be used at any point, additional calculation
// in nrf_802154_frame_parser_mhr_parse needs to be implemented.
p_cmd += mhr_fields.addressing_end_offset;
}
else
{
// If invalid source or destination addressing mode is detected, assume unknown device.
// Command type cannot be checked, as addressing_end_offset value will be invalid.
return true;
}
// Check frame type and command type.
if ((frame_type == FRAME_TYPE_COMMAND) && (*p_cmd == MAC_CMD_DATA_REQ))
{
// Check addressing type - in long case address, pb should always be 1.
if (mhr_fields.src_addr_size == SHORT_ADDRESS_SIZE)
{
// Return true if address is not found on the m_pending_bits list.
ret = !addr_index_find(mhr_fields.p_src_addr,
&location,
NRF_802154_ACK_DATA_PENDING_BIT,
false);
}
else
{
ret = true;
}
}
return ret;
}
/**
* @brief Standard-compliant implementation of the address matching algorithm.
*
* Function always returns true. It is IEEE 802.15.4 compliant, as per 6.7.3.
* Higher layer should ensure empty data frame with no AR is sent afterwards.
*
* @param[in] p_frame Pointer to the frame for which the ACK frame is being prepared.
*
* @retval true Pending bit is to be set.
*/
static bool addr_match_standard_compliant(const uint8_t * p_frame)
{
(void)p_frame;
return true;
}
/**
* @brief Add an address to the address list in ascending order.
*
* @param[in] p_addr Pointer to the address to be added.
* @param[in] location Index of the location where @p p_addr should be added.
* @param[in] extended Indication if @p p_addr is an extended or a short addresses.
*
* @retval true Address @p p_addr has been added to the list successfully.
* @retval false Address @p p_addr could not be added to the list.
*/
static bool addr_add(const uint8_t * p_addr,
uint32_t location,
nrf_802154_ack_data_t data_type,
bool extended)
{
uint32_t * p_addr_array_len;
uint32_t max_addr_array_len;
uint8_t * p_addr_array;
uint8_t entry_size;
bool valid_data_type = true;
switch (data_type)
{
case NRF_802154_ACK_DATA_PENDING_BIT:
if (extended)
{
p_addr_array = (uint8_t *)m_pending_bit.extended_addr;
max_addr_array_len = NUM_EXTENDED_ADDRESSES;
p_addr_array_len = &m_pending_bit.num_of_ext_addr;
entry_size = EXTENDED_ADDRESS_SIZE;
}
else
{
p_addr_array = (uint8_t *)m_pending_bit.short_addr;
max_addr_array_len = NUM_SHORT_ADDRESSES;
p_addr_array_len = &m_pending_bit.num_of_short_addr;
entry_size = SHORT_ADDRESS_SIZE;
}
break;
case NRF_802154_ACK_DATA_IE:
if (extended)
{
p_addr_array = (uint8_t *)m_ie.ext_data;
max_addr_array_len = NUM_EXTENDED_ADDRESSES;
p_addr_array_len = &m_ie.num_of_ext_data;
entry_size = sizeof(ack_ext_ie_data_t);
}
else
{
p_addr_array = (uint8_t *)m_ie.short_data;
max_addr_array_len = NUM_SHORT_ADDRESSES;
p_addr_array_len = &m_ie.num_of_short_data;
entry_size = sizeof(ack_short_ie_data_t);
}
break;
default:
valid_data_type = false;
assert(false);
break;
}
if (!valid_data_type || (*p_addr_array_len == max_addr_array_len))
{
return false;
}
memmove(p_addr_array + entry_size * (location + 1),
p_addr_array + entry_size * (location),
(*p_addr_array_len - location) * entry_size);
memcpy(p_addr_array + entry_size * location,
p_addr,
extended ? EXTENDED_ADDRESS_SIZE : SHORT_ADDRESS_SIZE);
(*p_addr_array_len)++;
return true;
}
/**
* @brief Remove an address from the address list keeping it in ascending order.
*
* @param[in] location Index of the element to be removed from the list.
* @param[in] extended Indication if address to remove is an extended or a short address.
*
* @retval true Address @p p_addr has been removed from the list successfully.
* @retval false Address @p p_addr could not removed from the list.
*/
static bool addr_remove(uint32_t location, nrf_802154_ack_data_t data_type, bool extended)
{
uint32_t * p_addr_array_len;
uint8_t * p_addr_array;
uint8_t entry_size;
bool valid_data_type = true;
switch (data_type)
{
case NRF_802154_ACK_DATA_PENDING_BIT:
if (extended)
{
p_addr_array = (uint8_t *)m_pending_bit.extended_addr;
p_addr_array_len = &m_pending_bit.num_of_ext_addr;
entry_size = EXTENDED_ADDRESS_SIZE;
}
else
{
p_addr_array = (uint8_t *)m_pending_bit.short_addr;
p_addr_array_len = &m_pending_bit.num_of_short_addr;
entry_size = SHORT_ADDRESS_SIZE;
}
break;
case NRF_802154_ACK_DATA_IE:
if (extended)
{
p_addr_array = (uint8_t *)m_ie.ext_data;
p_addr_array_len = &m_ie.num_of_ext_data;
entry_size = sizeof(ack_ext_ie_data_t);
}
else
{
p_addr_array = (uint8_t *)m_ie.short_data;
p_addr_array_len = &m_ie.num_of_short_data;
entry_size = sizeof(ack_short_ie_data_t);
}
break;
default:
valid_data_type = false;
assert(false);
break;
}
if (!valid_data_type || (*p_addr_array_len == 0))
{
return false;
}
memmove(p_addr_array + entry_size * location,
p_addr_array + entry_size * (location + 1),
(*p_addr_array_len - location - 1) * entry_size);
(*p_addr_array_len)--;
return true;
}
static void ie_data_add(uint32_t location, bool extended, const uint8_t * p_data, uint8_t data_len)
{
if (extended)
{
memcpy(m_ie.ext_data[location].ie_data.p_data, p_data, data_len);
m_ie.ext_data[location].ie_data.len = data_len;
}
else
{
memcpy(m_ie.short_data[location].ie_data.p_data, p_data, data_len);
m_ie.short_data[location].ie_data.len = data_len;
}
}
/***************************************************************************************************
* @section Public API
**************************************************************************************************/
void nrf_802154_ack_data_init(void)
{
memset(&m_pending_bit, 0, sizeof(m_pending_bit));
memset(&m_ie, 0, sizeof(m_ie));
m_pending_bit.enabled = true;
m_src_matching_method = NRF_802154_SRC_ADDR_MATCH_THREAD;
}
void nrf_802154_ack_data_enable(bool enabled)
{
m_pending_bit.enabled = enabled;
}
bool nrf_802154_ack_data_for_addr_set(const uint8_t * p_addr,
bool extended,
nrf_802154_ack_data_t data_type,
const void * p_data,
uint8_t data_len)
{
uint32_t location = 0;
if (addr_index_find(p_addr, &location, data_type, extended) ||
addr_add(p_addr, location, data_type, extended))
{
if (data_type == NRF_802154_ACK_DATA_IE)
{
ie_data_add(location, extended, p_data, data_len);
}
return true;
}
else
{
return false;
}
}
bool nrf_802154_ack_data_for_addr_clear(const uint8_t * p_addr,
bool extended,
nrf_802154_ack_data_t data_type)
{
uint32_t location = 0;
if (addr_index_find(p_addr, &location, data_type, extended))
{
return addr_remove(location, data_type, extended);
}
else
{
return false;
}
}
void nrf_802154_ack_data_reset(bool extended, nrf_802154_ack_data_t data_type)
{
switch (data_type)
{
case NRF_802154_ACK_DATA_PENDING_BIT:
if (extended)
{
m_pending_bit.num_of_ext_addr = 0;
}
else
{
m_pending_bit.num_of_short_addr = 0;
}
break;
case NRF_802154_ACK_DATA_IE:
if (extended)
{
m_ie.num_of_ext_data = 0;
}
else
{
m_ie.num_of_short_data = 0;
}
break;
default:
break;
}
}
void nrf_802154_ack_data_src_addr_matching_method_set(nrf_802154_src_addr_match_t match_method)
{
switch (match_method)
{
case NRF_802154_SRC_ADDR_MATCH_THREAD:
case NRF_802154_SRC_ADDR_MATCH_ZIGBEE:
case NRF_802154_SRC_ADDR_MATCH_ALWAYS_1:
m_src_matching_method = match_method;
break;
default:
assert(false);
}
}
bool nrf_802154_ack_data_pending_bit_should_be_set(const uint8_t * p_frame)
{
bool ret;
switch (m_src_matching_method)
{
case NRF_802154_SRC_ADDR_MATCH_THREAD:
ret = addr_match_thread(p_frame);
break;
case NRF_802154_SRC_ADDR_MATCH_ZIGBEE:
ret = addr_match_zigbee(p_frame);
break;
case NRF_802154_SRC_ADDR_MATCH_ALWAYS_1:
ret = addr_match_standard_compliant(p_frame);
break;
default:
ret = false;
assert(false);
}
return ret;
}
const uint8_t * nrf_802154_ack_data_ie_get(const uint8_t * p_src_addr,
bool src_addr_extended,
uint8_t * p_ie_length)
{
uint32_t location;
if (NULL == p_src_addr)
{
return NULL;
}
if (addr_index_find(p_src_addr, &location, NRF_802154_ACK_DATA_IE, src_addr_extended))
{
if (src_addr_extended)
{
*p_ie_length = m_ie.ext_data[location].ie_data.len;
return m_ie.ext_data[location].ie_data.p_data;
}
else
{
*p_ie_length = m_ie.short_data[location].ie_data.len;
return m_ie.short_data[location].ie_data.p_data;
}
}
else
{
*p_ie_length = 0;
return NULL;
}
}