642 lines
27 KiB
C
642 lines
27 KiB
C
/*!
|
|
* \file LmhpFragmentation.c
|
|
*
|
|
* \brief Implements the LoRa-Alliance fragmented data block transport package
|
|
* Specification V1.0.0: https://resources.lora-alliance.org/technical-specifications/lorawan-fragmented-data-block-transport-specification-v1-0-0
|
|
* Specification V2.0.0: https://resources.lora-alliance.org/technical-specifications/ts004-2-0-0-fragmented-data-block-transport
|
|
*
|
|
* \copyright Revised BSD License, see section \ref LICENSE.
|
|
*
|
|
* \code
|
|
* ______ _
|
|
* / _____) _ | |
|
|
* ( (____ _____ ____ _| |_ _____ ____| |__
|
|
* \____ \| ___ | (_ _) ___ |/ ___) _ \
|
|
* _____) ) ____| | | || |_| ____( (___| | | |
|
|
* (______/|_____)_|_|_| \__)_____)\____)_| |_|
|
|
* (C)2013-2018 Semtech
|
|
*
|
|
* \endcode
|
|
*
|
|
* \author Miguel Luis ( Semtech )
|
|
*/
|
|
/**
|
|
******************************************************************************
|
|
*
|
|
* Portions COPYRIGHT 2020 STMicroelectronics
|
|
*
|
|
* @file LmhpFragmentation.c
|
|
* @author MCD Application Team
|
|
* @brief Implements the LoRa-Alliance fragmented data block transport package
|
|
******************************************************************************
|
|
*/
|
|
/* Includes ------------------------------------------------------------------*/
|
|
#include "LoRaMac.h"
|
|
#include "LmHandler.h"
|
|
#include "LmhpFragmentation.h"
|
|
#include "frag_decoder_if.h"
|
|
#include "utilities.h"
|
|
#include "mw_log_conf.h" /* needed for MW_LOG */
|
|
|
|
/*!
|
|
* LoRaWAN Application Layer Fragmented Data Block Transport Specification
|
|
*/
|
|
#define FRAGMENTATION_PORT 201
|
|
|
|
#define FRAGMENTATION_ID 3
|
|
|
|
#if (LORAWAN_PACKAGES_VERSION == 1)
|
|
#define FRAGMENTATION_VERSION 1
|
|
#elif (LORAWAN_PACKAGES_VERSION == 2)
|
|
#define FRAGMENTATION_VERSION 2
|
|
#endif /* LORAWAN_PACKAGES_VERSION */
|
|
|
|
#define FRAGMENTATION_MAX_SESSIONS 4
|
|
|
|
/*!
|
|
* Package current context
|
|
*/
|
|
typedef struct LmhpFragmentationState_s
|
|
{
|
|
bool Initialized;
|
|
bool IsTxPending;
|
|
#if ( FRAGMENTATION_VERSION == 2 )
|
|
bool FragDataBlockAnsRequired;
|
|
#endif /* FRAGMENTATION_VERSION */
|
|
uint8_t DataBufferMaxSize;
|
|
uint8_t DataBufferSize;
|
|
uint8_t *DataBuffer;
|
|
} LmhpFragmentationState_t;
|
|
|
|
typedef enum LmhpFragmentationMoteCmd_e
|
|
{
|
|
FRAGMENTATION_PKG_VERSION_ANS = 0x00,
|
|
FRAGMENTATION_FRAG_STATUS_ANS = 0x01,
|
|
FRAGMENTATION_FRAG_SESSION_SETUP_ANS = 0x02,
|
|
FRAGMENTATION_FRAG_SESSION_DELETE_ANS = 0x03,
|
|
#if ( FRAGMENTATION_VERSION == 2 )
|
|
FRAGMENTATION_FRAG_DATA_BLOCK_RECEIVED_REQ = 0x04,
|
|
#endif /* FRAGMENTATION_VERSION */
|
|
} LmhpFragmentationMoteCmd_t;
|
|
|
|
typedef enum LmhpFragmentationSrvCmd_e
|
|
{
|
|
FRAGMENTATION_PKG_VERSION_REQ = 0x00,
|
|
FRAGMENTATION_FRAG_STATUS_REQ = 0x01,
|
|
FRAGMENTATION_FRAG_SESSION_SETUP_REQ = 0x02,
|
|
FRAGMENTATION_FRAG_SESSION_DELETE_REQ = 0x03,
|
|
#if ( FRAGMENTATION_VERSION == 2 )
|
|
FRAGMENTATION_FRAG_DATA_BLOCK_RECEIVED_ANS = 0x04,
|
|
#endif /* FRAGMENTATION_VERSION */
|
|
FRAGMENTATION_DATA_FRAGMENT = 0x08,
|
|
} LmhpFragmentationSrvCmd_t;
|
|
|
|
/*!
|
|
* LoRaWAN fragmented data block transport handler parameters
|
|
*/
|
|
static LmhpFragmentationParams_t *LmhpFragmentationParams;
|
|
|
|
/*!
|
|
* Initializes the package with provided parameters
|
|
*
|
|
* \param [in] params Pointer to the package parameters
|
|
* \param [in] dataBuffer Pointer to main application buffer
|
|
* \param [in] dataBufferMaxSize Main application buffer maximum size
|
|
*/
|
|
static void LmhpFragmentationInit( void *params, uint8_t *dataBuffer, uint8_t dataBufferMaxSize );
|
|
|
|
/*!
|
|
* Returns the current package initialization status.
|
|
*
|
|
* \retval status Package initialization status
|
|
* [true: Initialized, false: Not initialized]
|
|
*/
|
|
static bool LmhpFragmentationIsInitialized( void );
|
|
|
|
/*!
|
|
* Returns if a package transmission is pending or not.
|
|
*
|
|
* \retval status Package transmission status
|
|
* [true: pending, false: Not pending]
|
|
*/
|
|
static bool LmhpFragmentationIsTxPending( void );
|
|
|
|
/*!
|
|
* Processes the internal package events.
|
|
*/
|
|
static void LmhpFragmentationProcess( void );
|
|
|
|
/*!
|
|
* Processes the MCPS Indication
|
|
*
|
|
* \param [in] mcpsIndication MCPS indication primitive data
|
|
*/
|
|
static void LmhpFragmentationOnMcpsIndication( McpsIndication_t *mcpsIndication );
|
|
|
|
/*!
|
|
* \brief Callback function for Fragment delay timer.
|
|
*/
|
|
static void OnFragmentProcessTimer( void *context );
|
|
|
|
static LmhpFragmentationState_t LmhpFragmentationState =
|
|
{
|
|
.Initialized = false,
|
|
.IsTxPending = false,
|
|
#if ( FRAGMENTATION_VERSION == 2 )
|
|
.FragDataBlockAnsRequired = false,
|
|
#endif /* FRAGMENTATION_VERSION */
|
|
.DataBufferMaxSize = 0,
|
|
.DataBufferSize = 0,
|
|
.DataBuffer = NULL,
|
|
};
|
|
|
|
typedef struct FragGroupData_s
|
|
{
|
|
bool IsActive;
|
|
union
|
|
{
|
|
uint8_t Value;
|
|
struct
|
|
{
|
|
uint8_t McGroupBitMask: 4;
|
|
uint8_t FragIndex: 2;
|
|
uint8_t RFU: 2;
|
|
} Fields;
|
|
} FragSession;
|
|
uint16_t FragNb;
|
|
uint8_t FragSize;
|
|
union
|
|
{
|
|
uint8_t Value;
|
|
struct
|
|
{
|
|
uint8_t BlockAckDelay: 3;
|
|
uint8_t FragAlgo: 3;
|
|
#if ( FRAGMENTATION_VERSION == 1 )
|
|
uint8_t RFU: 2;
|
|
#elif ( FRAGMENTATION_VERSION == 2 )
|
|
uint8_t AckReception: 1;
|
|
uint8_t RFU: 1;
|
|
#endif /* FRAGMENTATION_VERSION */
|
|
} Fields;
|
|
} Control;
|
|
uint8_t Padding;
|
|
uint32_t Descriptor;
|
|
#if ( FRAGMENTATION_VERSION == 2 )
|
|
uint16_t SessionCnt;
|
|
uint32_t Mic;
|
|
#endif /* FRAGMENTATION_VERSION */
|
|
} FragGroupData_t;
|
|
|
|
typedef struct FragSessionData_s
|
|
{
|
|
FragGroupData_t FragGroupData;
|
|
FragDecoderStatus_t FragDecoderStatus;
|
|
int32_t FragDecoderProcessStatus;
|
|
} FragSessionData_t;
|
|
|
|
static FragSessionData_t FragSessionData[FRAGMENTATION_MAX_SESSIONS];
|
|
|
|
static LmhPackage_t LmhpFragmentationPackage =
|
|
{
|
|
.Port = FRAGMENTATION_PORT,
|
|
.Init = LmhpFragmentationInit,
|
|
.IsInitialized = LmhpFragmentationIsInitialized,
|
|
.IsTxPending = LmhpFragmentationIsTxPending,
|
|
.Process = LmhpFragmentationProcess,
|
|
.OnPackageProcessEvent = NULL, /* To be initialized by LmHandler */
|
|
.OnMcpsConfirmProcess = NULL, /* Not used in this package */
|
|
.OnMcpsIndicationProcess = LmhpFragmentationOnMcpsIndication,
|
|
.OnMlmeConfirmProcess = NULL, /* Not used in this package */
|
|
.OnMlmeIndicationProcess = NULL, /* Not used in this package */
|
|
.OnJoinRequest = NULL, /* To be initialized by LmHandler */
|
|
.OnDeviceTimeRequest = NULL, /* To be initialized by LmHandler */
|
|
.OnSysTimeUpdate = NULL, /* To be initialized by LmHandler */
|
|
#if (defined( LORAMAC_VERSION ) && (( LORAMAC_VERSION == 0x01000400 ) || ( LORAMAC_VERSION == 0x01010100 )))
|
|
.OnSystemReset = NULL, /* To be initialized by LmHandler */
|
|
#endif /* LORAMAC_VERSION */
|
|
};
|
|
|
|
/* Delay value. */
|
|
static uint32_t TxDelayTime;
|
|
|
|
/* Fragment Delay Timer struct */
|
|
static TimerEvent_t FragmentProcessTimer;
|
|
|
|
/* Co-efficient used to calculate delay. */
|
|
static uint8_t BlockAckDelay = 0;
|
|
|
|
#if ( FRAGMENTATION_VERSION == 2 )
|
|
/* fragmentation counter session */
|
|
static int32_t SessionCntPrev[FRAGMENTATION_MAX_SESSIONS] = {-1, -1, -1, -1};
|
|
#endif /* FRAGMENTATION_VERSION */
|
|
|
|
/*!
|
|
* \brief Callback function for Fragment delay timer.
|
|
*/
|
|
static void OnFragmentProcessTimer( void *context )
|
|
{
|
|
if( LmhpFragmentationState.DataBufferSize != 0 )
|
|
{
|
|
LmhpFragmentationState.IsTxPending = true;
|
|
}
|
|
if( LmhpFragmentationPackage.OnPackageProcessEvent != NULL )
|
|
{
|
|
LmhpFragmentationPackage.OnPackageProcessEvent();
|
|
}
|
|
}
|
|
|
|
LmhPackage_t *LmhpFragmentationPackageFactory( void )
|
|
{
|
|
return &LmhpFragmentationPackage;
|
|
}
|
|
|
|
static void LmhpFragmentationInit( void *params, uint8_t *dataBuffer, uint8_t dataBufferMaxSize )
|
|
{
|
|
if( ( params != NULL ) && ( dataBuffer != NULL ) )
|
|
{
|
|
LmhpFragmentationParams = ( LmhpFragmentationParams_t * )params;
|
|
LmhpFragmentationState.DataBuffer = dataBuffer;
|
|
LmhpFragmentationState.DataBufferMaxSize = dataBufferMaxSize;
|
|
LmhpFragmentationState.Initialized = true;
|
|
/* Initialize Fragmentation delay time. */
|
|
TxDelayTime = 0;
|
|
/* Initialize Fragmentation delay timer. */
|
|
TimerInit( &FragmentProcessTimer, OnFragmentProcessTimer );
|
|
}
|
|
else
|
|
{
|
|
LmhpFragmentationParams = NULL;
|
|
LmhpFragmentationState.Initialized = false;
|
|
}
|
|
LmhpFragmentationState.IsTxPending = false;
|
|
|
|
/* initialize the global fragmentation session buffer */
|
|
memset1( ( uint8_t * )FragSessionData, 0, sizeof( FragSessionData ) );
|
|
}
|
|
|
|
static bool LmhpFragmentationIsInitialized( void )
|
|
{
|
|
return LmhpFragmentationState.Initialized;
|
|
}
|
|
|
|
static bool LmhpFragmentationIsTxPending( void )
|
|
{
|
|
return LmhpFragmentationState.IsTxPending;
|
|
}
|
|
|
|
static void LmhpFragmentationProcess( void )
|
|
{
|
|
if( LmhpFragmentationState.IsTxPending == true )
|
|
{
|
|
/* Send the reply. */
|
|
LmHandlerAppData_t appData =
|
|
{
|
|
.Buffer = LmhpFragmentationState.DataBuffer,
|
|
.BufferSize = LmhpFragmentationState.DataBufferSize,
|
|
.Port = FRAGMENTATION_PORT,
|
|
};
|
|
|
|
LmHandlerErrorStatus_t lmhStatus = LORAMAC_HANDLER_ERROR;
|
|
lmhStatus = LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG, true );
|
|
#if ( FRAGMENTATION_VERSION == 2 )
|
|
if( ( lmhStatus != LORAMAC_HANDLER_SUCCESS ) || ( LmhpFragmentationState.FragDataBlockAnsRequired == true ) )
|
|
#else
|
|
if( lmhStatus != LORAMAC_HANDLER_SUCCESS )
|
|
#endif /* FRAGMENTATION_VERSION */
|
|
{
|
|
/* try to send the message again */
|
|
TimerSetValue( &FragmentProcessTimer, 1500 );
|
|
TimerStart( &FragmentProcessTimer );
|
|
}
|
|
else
|
|
{
|
|
LmhpFragmentationState.IsTxPending = false;
|
|
LmhpFragmentationState.DataBufferSize = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void LmhpFragmentationOnMcpsIndication( McpsIndication_t *mcpsIndication )
|
|
{
|
|
uint8_t cmdIndex = 0;
|
|
uint8_t dataBufferIndex = 0;
|
|
bool isAnswerDelayed = false;
|
|
|
|
if( mcpsIndication->Port != FRAGMENTATION_PORT )
|
|
{
|
|
return;
|
|
}
|
|
|
|
LmhpFragmentationState.DataBufferSize = 0;
|
|
|
|
while( cmdIndex < mcpsIndication->BufferSize )
|
|
{
|
|
switch( mcpsIndication->Buffer[cmdIndex++] )
|
|
{
|
|
case FRAGMENTATION_PKG_VERSION_REQ:
|
|
{
|
|
if( mcpsIndication->Multicast == 1 )
|
|
{
|
|
/* Multicast channel. Don't process command. */
|
|
break;
|
|
}
|
|
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FRAGMENTATION_PKG_VERSION_ANS;
|
|
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FRAGMENTATION_ID;
|
|
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FRAGMENTATION_VERSION;
|
|
break;
|
|
}
|
|
case FRAGMENTATION_FRAG_STATUS_REQ:
|
|
{
|
|
uint8_t fragIndex = mcpsIndication->Buffer[cmdIndex++];
|
|
uint8_t participants = fragIndex & 0x01;
|
|
|
|
fragIndex = ( fragIndex >> 1 ) & 0x03;
|
|
FragSessionData[fragIndex].FragDecoderStatus = FragDecoderGetStatus( );
|
|
|
|
if( ( participants == 1 ) ||
|
|
( ( participants == 0 ) && ( FragSessionData[fragIndex].FragDecoderStatus.FragNbLost > 0 ) ) )
|
|
{
|
|
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FRAGMENTATION_FRAG_STATUS_ANS;
|
|
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FragSessionData[fragIndex].FragDecoderStatus.FragNbRx & 0xFF;
|
|
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = ( fragIndex << 6 ) |
|
|
( ( FragSessionData[fragIndex].FragDecoderStatus.FragNbRx >> 8 ) & 0x3F );
|
|
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FragSessionData[fragIndex].FragDecoderStatus.FragNbLost;
|
|
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FragSessionData[fragIndex].FragDecoderStatus.MatrixError & 0x01;
|
|
|
|
/* Fetch the co-efficient value required to calculate delay of that respective session. */
|
|
BlockAckDelay = FragSessionData[fragIndex].FragGroupData.Control.Fields.BlockAckDelay;
|
|
isAnswerDelayed = true;
|
|
}
|
|
break;
|
|
}
|
|
case FRAGMENTATION_FRAG_SESSION_SETUP_REQ:
|
|
{
|
|
if( mcpsIndication->Multicast == 1 )
|
|
{
|
|
/* Multicast channel. Don't process command. */
|
|
break;
|
|
}
|
|
FragSessionData_t fragSessionData;
|
|
uint8_t status = 0x00;
|
|
|
|
fragSessionData.FragGroupData.FragSession.Value = mcpsIndication->Buffer[cmdIndex++];
|
|
|
|
fragSessionData.FragGroupData.FragNb = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x00FF;
|
|
fragSessionData.FragGroupData.FragNb |= ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0xFF00;
|
|
|
|
fragSessionData.FragGroupData.FragSize = mcpsIndication->Buffer[cmdIndex++];
|
|
|
|
fragSessionData.FragGroupData.Control.Value = mcpsIndication->Buffer[cmdIndex++];
|
|
|
|
fragSessionData.FragGroupData.Padding = mcpsIndication->Buffer[cmdIndex++];
|
|
|
|
fragSessionData.FragGroupData.Descriptor = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF;
|
|
fragSessionData.FragGroupData.Descriptor += ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00;
|
|
fragSessionData.FragGroupData.Descriptor += ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000;
|
|
fragSessionData.FragGroupData.Descriptor += ( mcpsIndication->Buffer[cmdIndex++] << 24 ) & 0xFF000000;
|
|
|
|
#if ( FRAGMENTATION_VERSION == 2 )
|
|
/* SessionCnt and MIC filed added for V2*/
|
|
fragSessionData.FragGroupData.SessionCnt = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x00FF;
|
|
fragSessionData.FragGroupData.SessionCnt += ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0xFF00;
|
|
|
|
fragSessionData.FragGroupData.Mic = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF;
|
|
fragSessionData.FragGroupData.Mic += ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00;
|
|
fragSessionData.FragGroupData.Mic += ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000;
|
|
fragSessionData.FragGroupData.Mic += ( mcpsIndication->Buffer[cmdIndex++] << 24 ) & 0xFF000000;
|
|
#endif /* FRAGMENTATION_VERSION */
|
|
|
|
if( fragSessionData.FragGroupData.Control.Fields.FragAlgo > 0 )
|
|
{
|
|
status |= 0x01; /* Encoding unsupported */
|
|
}
|
|
|
|
if( ( fragSessionData.FragGroupData.FragNb > FRAG_MAX_NB ) ||
|
|
( fragSessionData.FragGroupData.FragSize > FRAG_MAX_SIZE ) ||
|
|
( fragSessionData.FragGroupData.FragSize < FRAG_MIN_SIZE ) ||
|
|
( ( fragSessionData.FragGroupData.FragNb * fragSessionData.FragGroupData.FragSize ) > FRAG_DECODER_DWL_REGION_SIZE ) )
|
|
{
|
|
status |= 0x02; /* Not enough Memory */
|
|
}
|
|
status |= ( fragSessionData.FragGroupData.FragSession.Fields.FragIndex << 6 ) & 0xC0;
|
|
if( fragSessionData.FragGroupData.FragSession.Fields.FragIndex >= FRAGMENTATION_MAX_SESSIONS )
|
|
{
|
|
status |= 0x04; /* FragSession index not supported */
|
|
}
|
|
|
|
/* Descriptor is not really defined in the specification */
|
|
/* Not clear how to handle this. */
|
|
/* Currently the descriptor is always correct */
|
|
if( fragSessionData.FragGroupData.Descriptor != 0x01020304 )
|
|
{
|
|
/* status |= 0x08; */ /* Wrong Descriptor */
|
|
}
|
|
|
|
#if ( FRAGMENTATION_VERSION == 1 )
|
|
if( ( status & 0x0F ) == 0 )
|
|
{
|
|
#elif ( FRAGMENTATION_VERSION == 2 )
|
|
if( SessionCntPrev[fragSessionData.FragGroupData.FragSession.Fields.FragIndex] >=
|
|
fragSessionData.FragGroupData.SessionCnt )
|
|
{
|
|
status |= 0x10; /* SessionCnt Replay */
|
|
}
|
|
|
|
if( ( status & 0x1F ) == 0 )
|
|
{
|
|
#endif /* FRAGMENTATION_VERSION */
|
|
/* The FragSessionSetup is accepted */
|
|
fragSessionData.FragGroupData.IsActive = true;
|
|
fragSessionData.FragDecoderProcessStatus = FRAG_SESSION_ONGOING;
|
|
FragSessionData[fragSessionData.FragGroupData.FragSession.Fields.FragIndex] = fragSessionData;
|
|
FragDecoderInit( fragSessionData.FragGroupData.FragNb,
|
|
fragSessionData.FragGroupData.FragSize,
|
|
&LmhpFragmentationParams->DecoderCallbacks,
|
|
FRAGMENTATION_VERSION );
|
|
}
|
|
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FRAGMENTATION_FRAG_SESSION_SETUP_ANS;
|
|
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = status;
|
|
isAnswerDelayed = false;
|
|
break;
|
|
}
|
|
case FRAGMENTATION_FRAG_SESSION_DELETE_REQ:
|
|
{
|
|
if( mcpsIndication->Multicast == 1 )
|
|
{
|
|
/* Multicast channel. Don't process command. */
|
|
break;
|
|
}
|
|
uint8_t status = 0x00;
|
|
uint8_t id = mcpsIndication->Buffer[cmdIndex++] & 0x03;
|
|
|
|
status |= id;
|
|
if( ( id >= FRAGMENTATION_MAX_SESSIONS ) || ( FragSessionData[id].FragGroupData.IsActive == false ) )
|
|
{
|
|
status |= 0x04; /* Session does not exist */
|
|
}
|
|
else
|
|
{
|
|
/* Delete session */
|
|
FragSessionData[id].FragGroupData.IsActive = false;
|
|
}
|
|
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FRAGMENTATION_FRAG_SESSION_DELETE_ANS;
|
|
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = status;
|
|
isAnswerDelayed = false;
|
|
break;
|
|
}
|
|
#if ( FRAGMENTATION_VERSION == 2 )
|
|
case FRAGMENTATION_FRAG_DATA_BLOCK_RECEIVED_ANS:
|
|
{
|
|
if( mcpsIndication->Multicast == 1 )
|
|
{
|
|
/* Multicast channel. Don't process command. */
|
|
break;
|
|
}
|
|
uint8_t fragIndex = mcpsIndication->Buffer[cmdIndex++] & 0x03;
|
|
|
|
if( ( FragSessionData[fragIndex].FragGroupData.FragSession.Fields.FragIndex == fragIndex )
|
|
&& ( LmhpFragmentationState.FragDataBlockAnsRequired == true ) )
|
|
{
|
|
TimerStop( &FragmentProcessTimer );
|
|
LmhpFragmentationState.IsTxPending = false;
|
|
LmhpFragmentationState.DataBufferSize = 0;
|
|
LmhpFragmentationState.FragDataBlockAnsRequired = false;
|
|
}
|
|
break;
|
|
}
|
|
#endif /* FRAGMENTATION_VERSION */
|
|
case FRAGMENTATION_DATA_FRAGMENT:
|
|
{
|
|
uint8_t fragIndex = 0;
|
|
uint16_t fragCounter = 0;
|
|
|
|
fragCounter = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x00FF;
|
|
fragCounter |= ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0xFF00;
|
|
|
|
fragIndex = ( fragCounter >> 14 ) & 0x03;
|
|
fragCounter &= 0x3FFF;
|
|
if( FragSessionData[fragIndex].FragGroupData.IsActive == false )
|
|
{
|
|
cmdIndex = mcpsIndication->BufferSize;
|
|
break;
|
|
}
|
|
if( mcpsIndication->Multicast == 1 )
|
|
{
|
|
/* Message received on a multicast address */
|
|
/* Check McGroupBitMask */
|
|
uint8_t groupId = LoRaMacMcChannelGetGroupId( mcpsIndication->DevAddress );
|
|
if( ( groupId == 0xFF ) ||
|
|
( ( FragSessionData[fragIndex].FragGroupData.FragSession.Fields.McGroupBitMask & ( 1 << groupId ) ) == 0 ) )
|
|
{
|
|
/* Ignore message */
|
|
cmdIndex = mcpsIndication->BufferSize;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( FragSessionData[fragIndex].FragDecoderProcessStatus == FRAG_SESSION_ONGOING )
|
|
{
|
|
FragSessionData[fragIndex].FragDecoderProcessStatus = FragDecoderProcess( fragCounter, &mcpsIndication->Buffer[cmdIndex] );
|
|
FragSessionData[fragIndex].FragDecoderStatus = FragDecoderGetStatus( );
|
|
if( LmhpFragmentationParams->OnProgress != NULL )
|
|
{
|
|
LmhpFragmentationParams->OnProgress( FragSessionData[fragIndex].FragDecoderStatus.FragNbRx,
|
|
FragSessionData[fragIndex].FragGroupData.FragNb,
|
|
FragSessionData[fragIndex].FragGroupData.FragSize,
|
|
FragSessionData[fragIndex].FragDecoderStatus.FragNbLost );
|
|
}
|
|
|
|
if( FragSessionData[fragIndex].FragDecoderProcessStatus >= 0 )
|
|
{
|
|
uint32_t UnfragmentedBufferAddr;
|
|
/* Fragmentation successfully done */
|
|
if( LmhpFragmentationParams->OnDone != NULL )
|
|
{
|
|
LmhpFragmentationParams->OnDone( FragSessionData[fragIndex].FragDecoderProcessStatus,
|
|
( FragSessionData[fragIndex].FragGroupData.FragNb * FragSessionData[fragIndex].FragGroupData.FragSize ) -
|
|
FragSessionData[fragIndex].FragGroupData.Padding,
|
|
&UnfragmentedBufferAddr );
|
|
}
|
|
|
|
#if ( FRAGMENTATION_VERSION == 2 )
|
|
/*If AckReception = 0, the end-device SHALL do nothing */
|
|
if( FragSessionData[fragIndex].FragGroupData.Control.Fields.AckReception == 1 )
|
|
{
|
|
uint8_t status = 0x00;
|
|
uint32_t micComputed = 0;
|
|
|
|
status = fragIndex;
|
|
|
|
/* Compute MIC */
|
|
LoRaMacProcessMicForDatablock( ( uint8_t * )UnfragmentedBufferAddr,
|
|
( FragSessionData[fragIndex].FragGroupData.FragNb * FragSessionData[fragIndex].FragGroupData.FragSize ) -
|
|
FragSessionData[fragIndex].FragGroupData.Padding,
|
|
FragSessionData[fragIndex].FragGroupData.SessionCnt,
|
|
fragIndex,
|
|
FragSessionData[fragIndex].FragGroupData.Descriptor,
|
|
&micComputed );
|
|
MW_LOG( TS_OFF, VLEVEL_M, "MIC : %08X\r\n", micComputed );
|
|
|
|
/* check if the MIC computed is equal to the MIC received */
|
|
if( micComputed != FragSessionData[fragIndex].FragGroupData.Mic )
|
|
{
|
|
status |= 0x04;
|
|
}
|
|
|
|
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FRAGMENTATION_FRAG_DATA_BLOCK_RECEIVED_REQ;
|
|
LmhpFragmentationState.DataBuffer[dataBufferIndex++] = status;
|
|
BlockAckDelay = FragSessionData[fragIndex].FragGroupData.Control.Fields.BlockAckDelay;
|
|
isAnswerDelayed = true;
|
|
LmhpFragmentationState.FragDataBlockAnsRequired = true;
|
|
}
|
|
#endif /* FRAGMENTATION_VERSION */
|
|
|
|
FragSessionData[fragIndex].FragDecoderProcessStatus = FRAG_SESSION_NOT_STARTED;
|
|
}
|
|
}
|
|
cmdIndex += FragSessionData[fragIndex].FragGroupData.FragSize;
|
|
#if ( FRAGMENTATION_VERSION == 2 )
|
|
/* Store the previous session counter*/
|
|
SessionCntPrev[fragIndex] = FragSessionData[fragIndex].FragGroupData.SessionCnt;
|
|
#endif /* FRAGMENTATION_VERSION */
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* After processing the commands, if the end-node has to reply back then a flag is checked if the */
|
|
/* reply is to be sent immediately or with a delay. */
|
|
/* In some scenarios it is not desired that multiple end-notes send uplinks at the same time to */
|
|
/* the same server. (Example: Fragment status during a multicast FUOTA) */
|
|
if( dataBufferIndex != 0 )
|
|
{
|
|
/* Prepare Answer that is to be transmitted */
|
|
LmhpFragmentationState.DataBufferSize = dataBufferIndex;
|
|
|
|
if( isAnswerDelayed == true )
|
|
{
|
|
/* Delay value is calculated using BlockAckDelay which is communicated by server during the FragSessionSetupReq
|
|
* Pseudo Random Delay = rand(0:1) * 2^(BlockAckDelay + 4) Seconds.
|
|
* Delay = Pseudo Random Delay * 1000 milli seconds.
|
|
* Eg: BlockAckDelay = 7
|
|
* Pseudo Random Delay = rand(0:1) * 2^11
|
|
* rand(0:1) seconds = rand(0:1000) milliseconds
|
|
* Delay = rand(0:1000) * 2048 => 2048000ms = 34 minutes
|
|
* To prevent too early execution, a minimum delay of 3s is added
|
|
*/
|
|
TxDelayTime = 3000 + ( randr( 0, 1000 ) * ( 1 << ( BlockAckDelay + 4 ) ) );
|
|
}
|
|
else
|
|
{
|
|
TxDelayTime = 3000;
|
|
}
|
|
TimerSetValue( &FragmentProcessTimer, TxDelayTime );
|
|
TimerStart( &FragmentProcessTimer );
|
|
}
|
|
}
|