548 lines
13 KiB
C
548 lines
13 KiB
C
/*
|
|
/ _____) _ | |
|
|
( (____ _____ ____ _| |_ _____ ____| |__
|
|
\____ \| ___ | (_ _) ___ |/ ___) _ \
|
|
_____) ) ____| | | || |_| ____( (___| | | |
|
|
(______/|_____)_|_|_| \__)_____)\____)_| |_|
|
|
(C)2013 Semtech
|
|
___ _____ _ ___ _ _____ ___ ___ ___ ___
|
|
/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
|
|
\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
|
|
|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
|
|
embedded.connectivity.solutions===============
|
|
|
|
Description: LoRa MAC commands
|
|
|
|
License: Revised BSD License, see LICENSE.TXT file include in the project
|
|
|
|
Maintainer: Miguel Luis ( Semtech ), Daniel Jaeckle ( STACKFORCE ), Johannes Bruder ( STACKFORCE )
|
|
*/
|
|
|
|
#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 0;
|
|
}
|
|
}
|
|
|
|
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 == 0 )
|
|
{
|
|
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 == 0 )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
list->First = 0;
|
|
list->Last = 0;
|
|
|
|
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 == 0 ) && ( element == 0 ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Check if this is the first entry to enter the list.
|
|
if( list->First == 0 )
|
|
{
|
|
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 = 0;
|
|
|
|
// 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 == 0 ) && ( element == 0 ) )
|
|
{
|
|
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 == 0 ) && ( element == 0 ) )
|
|
{
|
|
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:
|
|
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 == 0 )
|
|
{
|
|
return LORAMAC_COMMANDS_ERROR_NPE;
|
|
}
|
|
MacCommand_t* newCmd;
|
|
|
|
// Allocate a memory slot
|
|
newCmd = MallocNewMacCommandSlot( );
|
|
|
|
if( newCmd == 0 )
|
|
{
|
|
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;
|
|
}
|