STM32CubeWL/Middlewares/Third_Party/LoRaWAN/Mac/LoRaMacCommands.c

661 lines
15 KiB
C

/*!
* \file LoRaMacCommands.c
*
* \brief LoRa MAC commands
*
* \copyright Revised BSD License, see section \ref LICENSE.
*
* \code
* ______ _
* / _____) _ | |
* ( (____ _____ ____ _| |_ _____ ____| |__
* \____ \| ___ | (_ _) ___ |/ ___) _ \
* _____) ) ____| | | || |_| ____( (___| | | |
* (______/|_____)_|_|_| \__)_____)\____)_| |_|
* (C)2013 Semtech
*
* ___ _____ _ ___ _ _____ ___ ___ ___ ___
* / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
* \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
* |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
* embedded.connectivity.solutions===============
*
* \endcode
*
* \author Miguel Luis ( Semtech )
*
* \author Daniel Jaeckle ( STACKFORCE )
*
* \author Johannes Bruder ( STACKFORCE )
*/
#include <stddef.h>
#include "utilities.h"
#include "LoRaMacCommands.h"
#include "LoRaMacConfirmQueue.h"
/*!
* Number of MAC Command slots
*/
#define NUM_OF_MAC_COMMANDS 15
/*!
* Size of the CID field of MAC commands
*/
#define CID_FIELD_SIZE 1
/*!
* Mac Commands list structure
*/
typedef struct sMacCommandsList
{
/*
* First element of MAC command list.
*/
MacCommand_t* First;
/*
* Last element of MAC command list.
*/
MacCommand_t* Last;
} MacCommandsList_t;
/*!
* LoRaMac Commands Context structure
*/
typedef struct sLoRaMacCommandsCtx
{
/*
* List of MAC command elements
*/
MacCommandsList_t MacCommandList;
/*
* Buffer to store MAC command elements
*/
MacCommand_t MacCommandSlots[NUM_OF_MAC_COMMANDS];
/*
* Size of all MAC commands serialized as buffer
*/
size_t SerializedCmdsSize;
} LoRaMacCommandsCtx_t;
/*!
* Callback function to notify the upper layer about context change
*/
static LoRaMacCommandsNvmEvent CommandsNvmCtxChanged;
/*!
* Non-volatile module context.
*/
static LoRaMacCommandsCtx_t NvmCtx;
/* Memory management functions */
/*!
* \brief Determines if a MAC command slot is free
*
* \param[IN] slot - Slot to check
* \retval - Status of the operation
*/
static bool IsSlotFree( const MacCommand_t* slot )
{
uint8_t* mem = ( uint8_t* )slot;
for( uint16_t size = 0; size < sizeof( MacCommand_t ); size++ )
{
if( mem[size] != 0x00 )
{
return false;
}
}
return true;
}
/*!
* \brief Allocates a new MAC command memory slot
*
* \retval - Pointer to slot
*/
static MacCommand_t* MallocNewMacCommandSlot( void )
{
uint8_t itr = 0;
while( IsSlotFree( ( const MacCommand_t* )&NvmCtx.MacCommandSlots[itr] ) == false )
{
itr++;
if( itr == NUM_OF_MAC_COMMANDS )
{
return NULL;
}
}
return &NvmCtx.MacCommandSlots[itr];
}
/*!
* \brief Free memory slot
*
* \param[IN] slot - Slot to free
*
* \retval - Status of the operation
*/
static bool FreeMacCommandSlot( MacCommand_t* slot )
{
if( slot == NULL )
{
return false;
}
memset1( ( uint8_t* )slot, 0x00, sizeof( MacCommand_t ) );
return true;
}
/* Linked list functions */
/*!
* \brief Initialize list
*
* \param[IN] list - List that shall be initialized
* \retval - Status of the operation
*/
static bool LinkedListInit( MacCommandsList_t* list )
{
if( list == NULL )
{
return false;
}
list->First = NULL;
list->Last = NULL;
return true;
}
/*!
* \brief Add an element to the list
*
* \param[IN] list - List where the element shall be added.
* \param[IN] element - Element to add
* \retval - Status of the operation
*/
static bool LinkedListAdd( MacCommandsList_t* list, MacCommand_t* element )
{
if( ( list == NULL ) || ( element == NULL ) )
{
return false;
}
// Check if this is the first entry to enter the list.
if( list->First == NULL )
{
list->First = element;
}
// Check if the last entry exists and update its next point.
if( list->Last )
{
list->Last->Next = element;
}
// Update the next point of this entry.
element->Next = NULL;
// Update the last entry of the list.
list->Last = element;
return true;
}
/*!
* \brief Return the previous element in the list.
*
* \param[IN] list - List
* \param[IN] element - Element where the previous element shall be searched
* \retval - Status of the operation
*/
static MacCommand_t* LinkedListGetPrevious( MacCommandsList_t* list, MacCommand_t* element )
{
if( ( list == NULL ) || ( element == NULL ) )
{
return NULL;
}
MacCommand_t* curElement;
// Start at the head of the list
curElement = list->First;
// When current element is the first of the list, there's no previous element so we can return NULL immediately.
if( element != curElement )
{
// Loop through all elements until the end is reached or the next of current is the current element.
while( ( curElement != NULL ) && ( curElement->Next != element ) )
{
curElement = curElement->Next;
}
}
else
{
curElement = NULL;
}
return curElement;
}
/*!
* \brief Remove an element from the list
*
* \param[IN] list - List where the element shall be removed from.
* \param[IN] element - Element to remove
* \retval - Status of the operation
*/
static bool LinkedListRemove( MacCommandsList_t* list, MacCommand_t* element )
{
if( ( list == NULL ) || ( element == NULL ) )
{
return false;
}
MacCommand_t* PrevElement = LinkedListGetPrevious( list, element );
if( list->First == element )
{
list->First = element->Next;
}
if( list->Last == element )
{
list->Last = PrevElement;
}
if( PrevElement != NULL )
{
PrevElement->Next = element->Next;
}
element->Next = NULL;
return true;
}
/*
* \brief Determines if a MAC command is sticky or not
*
* \param[IN] cid - MAC command identifier
*
* \retval - Status of the operation
*/
static bool IsSticky( uint8_t cid )
{
switch( cid )
{
case MOTE_MAC_DL_CHANNEL_ANS:
case MOTE_MAC_RX_PARAM_SETUP_ANS:
case MOTE_MAC_RX_TIMING_SETUP_ANS:
case MOTE_MAC_TX_PARAM_SETUP_ANS:
return true;
default:
return false;
}
}
/*
* \brief Wrapper function for the NvmCtx
*/
static void NvmCtxCallback( void )
{
if( CommandsNvmCtxChanged != NULL )
{
CommandsNvmCtxChanged( );
}
}
LoRaMacCommandStatus_t LoRaMacCommandsInit( LoRaMacCommandsNvmEvent commandsNvmCtxChanged )
{
// Initialize with default
memset1( ( uint8_t* )&NvmCtx, 0, sizeof( NvmCtx ) );
LinkedListInit( &NvmCtx.MacCommandList );
// Assign callback
CommandsNvmCtxChanged = commandsNvmCtxChanged;
return LORAMAC_COMMANDS_SUCCESS;
}
LoRaMacCommandStatus_t LoRaMacCommandsRestoreNvmCtx( void* commandsNvmCtx )
{
// Restore module context
if( commandsNvmCtx != NULL )
{
memcpy1( ( uint8_t* )&NvmCtx, ( uint8_t* )commandsNvmCtx, sizeof( NvmCtx ) );
return LORAMAC_COMMANDS_SUCCESS;
}
else
{
return LORAMAC_COMMANDS_ERROR_NPE;
}
}
void* LoRaMacCommandsGetNvmCtx( size_t* commandsNvmCtxSize )
{
*commandsNvmCtxSize = sizeof( NvmCtx );
return &NvmCtx;
}
LoRaMacCommandStatus_t LoRaMacCommandsAddCmd( uint8_t cid, uint8_t* payload, size_t payloadSize )
{
if( payload == NULL )
{
return LORAMAC_COMMANDS_ERROR_NPE;
}
MacCommand_t* newCmd;
// Allocate a memory slot
newCmd = MallocNewMacCommandSlot( );
if( newCmd == NULL )
{
return LORAMAC_COMMANDS_ERROR_MEMORY;
}
// Add it to the list of Mac commands
if( LinkedListAdd( &NvmCtx.MacCommandList, newCmd ) == false )
{
return LORAMAC_COMMANDS_ERROR;
}
// Set Values
newCmd->CID = cid;
newCmd->PayloadSize = payloadSize;
memcpy1( ( uint8_t* )newCmd->Payload, payload, payloadSize );
newCmd->IsSticky = IsSticky( cid );
NvmCtx.SerializedCmdsSize += ( CID_FIELD_SIZE + payloadSize );
NvmCtxCallback( );
return LORAMAC_COMMANDS_SUCCESS;
}
LoRaMacCommandStatus_t LoRaMacCommandsRemoveCmd( MacCommand_t* macCmd )
{
if( macCmd == NULL )
{
return LORAMAC_COMMANDS_ERROR_NPE;
}
// Remove the Mac command element from MacCommandList
if( LinkedListRemove( &NvmCtx.MacCommandList, macCmd ) == false )
{
return LORAMAC_COMMANDS_ERROR_CMD_NOT_FOUND;
}
NvmCtx.SerializedCmdsSize -= ( CID_FIELD_SIZE + macCmd->PayloadSize );
// Free the MacCommand Slot
if( FreeMacCommandSlot( macCmd ) == false )
{
return LORAMAC_COMMANDS_ERROR;
}
NvmCtxCallback( );
return LORAMAC_COMMANDS_SUCCESS;
}
LoRaMacCommandStatus_t LoRaMacCommandsGetCmd( uint8_t cid, MacCommand_t** macCmd )
{
MacCommand_t* curElement;
// Start at the head of the list
curElement = NvmCtx.MacCommandList.First;
// Loop through all elements until we find the element with the given CID
while( ( curElement != NULL ) && ( curElement->CID != cid ) )
{
curElement = curElement->Next;
}
// Update the pointer anyway
*macCmd = curElement;
// Handle error in case if we reached the end without finding it.
if( curElement == NULL )
{
return LORAMAC_COMMANDS_ERROR_CMD_NOT_FOUND;
}
return LORAMAC_COMMANDS_SUCCESS;
}
LoRaMacCommandStatus_t LoRaMacCommandsRemoveNoneStickyCmds( void )
{
MacCommand_t* curElement;
MacCommand_t* nexElement;
// Start at the head of the list
curElement = NvmCtx.MacCommandList.First;
// Loop through all elements
while( curElement != NULL )
{
if( curElement->IsSticky == false )
{
nexElement = curElement->Next;
LoRaMacCommandsRemoveCmd( curElement );
curElement = nexElement;
}
else
{
curElement = curElement->Next;
}
}
NvmCtxCallback( );
return LORAMAC_COMMANDS_SUCCESS;
}
LoRaMacCommandStatus_t LoRaMacCommandsRemoveStickyAnsCmds( void )
{
MacCommand_t* curElement;
MacCommand_t* nexElement;
// Start at the head of the list
curElement = NvmCtx.MacCommandList.First;
// Loop through all elements
while( curElement != NULL )
{
nexElement = curElement->Next;
if( IsSticky( curElement->CID ) == true )
{
LoRaMacCommandsRemoveCmd( curElement );
}
curElement = nexElement;
}
NvmCtxCallback( );
return LORAMAC_COMMANDS_SUCCESS;
}
LoRaMacCommandStatus_t LoRaMacCommandsGetSizeSerializedCmds( size_t* size )
{
if( size == NULL )
{
return LORAMAC_COMMANDS_ERROR_NPE;
}
*size = NvmCtx.SerializedCmdsSize;
return LORAMAC_COMMANDS_SUCCESS;
}
LoRaMacCommandStatus_t LoRaMacCommandsSerializeCmds( size_t availableSize, size_t* effectiveSize, uint8_t* buffer )
{
MacCommand_t* curElement = NvmCtx.MacCommandList.First;
MacCommand_t* nextElement;
uint8_t itr = 0;
if( ( buffer == NULL ) || ( effectiveSize == NULL ) )
{
return LORAMAC_COMMANDS_ERROR_NPE;
}
// Loop through all elements which fits into the buffer
while( curElement != NULL )
{
// If the next MAC command still fits into the buffer, add it.
if( ( availableSize - itr ) >= ( CID_FIELD_SIZE + curElement->PayloadSize ) )
{
buffer[itr++] = curElement->CID;
memcpy1( &buffer[itr], curElement->Payload, curElement->PayloadSize );
itr += curElement->PayloadSize;
}
else
{
break;
}
curElement = curElement->Next;
}
// Remove all commands which do not fit into the buffer
while( curElement != NULL )
{
// Store the next element before removing the current one
nextElement = curElement->Next;
LoRaMacCommandsRemoveCmd( curElement );
curElement = nextElement;
}
// Fetch the effective size of the mac commands
LoRaMacCommandsGetSizeSerializedCmds( effectiveSize );
return LORAMAC_COMMANDS_SUCCESS;
}
LoRaMacCommandStatus_t LoRaMacCommandsStickyCmdsPending( bool* cmdsPending )
{
if( cmdsPending == NULL )
{
return LORAMAC_COMMANDS_ERROR_NPE;
}
MacCommand_t* curElement;
curElement = NvmCtx.MacCommandList.First;
*cmdsPending = false;
// Loop through all elements
while( curElement != NULL )
{
if( curElement->IsSticky == true )
{
// Found one sticky MAC command
*cmdsPending = true;
return LORAMAC_COMMANDS_SUCCESS;
}
curElement = curElement->Next;
}
return LORAMAC_COMMANDS_SUCCESS;
}
uint8_t LoRaMacCommandsGetCmdSize( uint8_t cid )
{
uint8_t cidSize = 0;
// Decode Frame MAC commands
switch( cid )
{
case SRV_MAC_LINK_CHECK_ANS:
{
// cid + Margin + GwCnt
cidSize = 3;
break;
}
case SRV_MAC_LINK_ADR_REQ:
{
// cid + DataRate_TXPower + ChMask (2) + Redundancy
cidSize = 5;
break;
}
case SRV_MAC_DUTY_CYCLE_REQ:
{
// cid + DutyCyclePL
cidSize = 2;
break;
}
case SRV_MAC_RX_PARAM_SETUP_REQ:
{
// cid + DLsettings + Frequency (3)
cidSize = 5;
break;
}
case SRV_MAC_DEV_STATUS_REQ:
{
// cid
cidSize = 1;
break;
}
case SRV_MAC_NEW_CHANNEL_REQ:
{
// cid + ChIndex + Frequency (3) + DrRange
cidSize = 6;
break;
}
case SRV_MAC_RX_TIMING_SETUP_REQ:
{
// cid + Settings
cidSize = 2;
break;
}
case SRV_MAC_TX_PARAM_SETUP_REQ:
{
// cid + EIRP_DwellTime
cidSize = 2;
break;
}
case SRV_MAC_DL_CHANNEL_REQ:
{
// cid + ChIndex + Frequency (3)
cidSize = 5;
break;
}
case SRV_MAC_DEVICE_TIME_ANS:
{
// cid + Seconds (4) + Fractional seconds (1)
cidSize = 6;
break;
}
case SRV_MAC_PING_SLOT_INFO_ANS:
{
// cid
cidSize = 1;
break;
}
case SRV_MAC_PING_SLOT_CHANNEL_REQ:
{
// cid + Frequency (3) + DR
cidSize = 5;
break;
}
case SRV_MAC_BEACON_TIMING_ANS:
{
// cid + TimingDelay (2) + Channel
cidSize = 4;
break;
}
case SRV_MAC_BEACON_FREQ_REQ:
{
// cid + Frequency (3)
cidSize = 4;
break;
}
default:
{
// Unknown command. ABORT MAC commands processing
break;
}
}
return cidSize;
}