STM32CubeWL/Middlewares/Third_Party/LoRaWAN/LmHandler/Packages/LmhpCompliance.c

1177 lines
42 KiB
C

/*!
* \file LmhpCompliance.c
*
* \brief Implements the LoRa-Alliance certification protocol handling
*
* \copyright Revised BSD License, see section \ref LICENSE.
*
* \code
* ______ _
* / _____) _ | |
* ( (____ _____ ____ _| |_ _____ ____| |__
* \____ \| ___ | (_ _) ___ |/ ___) _ \
* _____) ) ____| | | || |_| ____( (___| | | |
* (______/|_____)_|_|_| \__)_____)\____)_| |_|
* (C)2013-2018 Semtech
*
* \endcode
*
* \author Miguel Luis ( Semtech )
*/
/**
******************************************************************************
*
* Portions COPYRIGHT 2020 STMicroelectronics
*
* @file LmhpCompliance.c
* @author MCD Application Team
* @brief Certification Protocol Handling definition
******************************************************************************
*/
#include "platform.h"
#include "NvmDataMgmt.h"
#include "LoRaMac.h"
#include "LoRaMacTest.h"
#include "LmHandler.h"
#include "LmhpCompliance.h"
/*!
* LoRaWAN compliance certification protocol port number.
*
* LoRaWAN Specification V1.x.x, chapter 4.3.2
*/
#define COMPLIANCE_PORT 224
#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 ))
/*!
* Defines the compliance mode data transmission duty cycle.
* An uplink will be transmitted ever \ref COMPLIANCE_TX_DUTYCYCLE [ms].
*/
#define COMPLIANCE_TX_DUTYCYCLE 5000
/*!
* LoRaWAN compliance tests support data
*/
typedef struct ComplianceTestState_s
{
bool Initialized;
bool IsRunning;
uint8_t State;
bool IsTxConfirmed;
uint8_t Port;
uint8_t DataBufferMaxSize;
uint8_t DataBufferSize;
uint8_t *DataBuffer;
uint16_t DownLinkCounter;
bool LinkCheck;
uint8_t DemodMargin;
uint8_t NbGateways;
} ComplianceTestState_t;
#ifndef MIN
#define MIN( a, b ) ( ( ( a ) < ( b ) ) ? ( a ) : ( b ) )
#endif /* MIN */
/*!
* Timer to handle the application data transmission duty cycle
*/
static TimerEvent_t ComplianceTxNextPacketTimer;
/*!
* Holds the compliance test current context
*/
static ComplianceTestState_t ComplianceTestState =
{
.Initialized = false,
.IsRunning = false,
.State = 0,
.IsTxConfirmed = false,
.Port = 0,
.DataBufferMaxSize = 0,
.DataBufferSize = 0,
.DataBuffer = NULL,
.DownLinkCounter = 0,
.LinkCheck = false,
.DemodMargin = 0,
.NbGateways = 0
};
/*!
* LoRaWAN compliance tests protocol handler parameters
*/
static LmhpComplianceParams_t *LmhpComplianceParams;
/*!
* Initializes the compliance tests with provided parameters
*
* \param [IN] params Structure containing the initial compliance
* tests parameters.
* \param [IN] dataBuffer Pointer to main application buffer
* \param [IN] dataBufferMaxSize Application buffer maximum size
*/
static void LmhpComplianceInit( void *params, uint8_t *dataBuffer, uint8_t dataBufferMaxSize );
/*!
* Returns the current compliance certification protocol initialization status.
*
* \retval status Compliance certification protocol initialization status
* [true: Initialized, false: Not initialized]
*/
static bool LmhpComplianceIsInitialized( void );
/*!
* Returns the current compliance certification protocol handling status.
*
* \retval status Compliance certification protocol handling status
* [true: Running, false: Not running]
*/
static bool LmhpComplianceIsRunning( void );
/*!
* Processes the LoRaMac Compliance events.
*/
static void LmhpComplianceProcess( void );
/*!
* Processes the MCPS Confirm
*
* \param [in] mcpsConfirm MCPS confirmation primitive data
*/
static void LmhpComplianceOnMcpsConfirm( McpsConfirm_t *mcpsConfirm );
/*!
* Processes the MCPS Indication
*
* \param [IN] mcpsIndication MCPS indication primitive data
*/
static void LmhpComplianceOnMcpsIndication( McpsIndication_t *mcpsIndication );
/*!
* Processes the MLME Confirm
*
* \param [IN] mlmeConfirm MLME confirmation primitive data
*/
static void LmhpComplianceOnMlmeConfirm( MlmeConfirm_t *mlmeConfirm );
/*!
* Function executed on TxNextPacket Timeout event
*/
static void OnComplianceTxNextPacketTimerEvent( void *context );
/*!
* Processes the data to transmit on port \ref COMPLIANCE_PORT
* Handles the compliance certification protocol data transmission
*
* \retval status Returns \ref LORAMAC_HANDLER_SUCCESS if request has been
* processed else \ref LORAMAC_HANDLER_ERROR
*/
static LmHandlerErrorStatus_t LmhpComplianceTxProcess( void );
static LmhPackage_t LmhpCompliancePackage =
{
.Port = COMPLIANCE_PORT,
.Init = LmhpComplianceInit,
.IsInitialized = LmhpComplianceIsInitialized,
.IsRunning = LmhpComplianceIsRunning,
.Process = LmhpComplianceProcess,
.OnMcpsConfirmProcess = LmhpComplianceOnMcpsConfirm,
.OnMcpsIndicationProcess = LmhpComplianceOnMcpsIndication,
.OnMlmeConfirmProcess = LmhpComplianceOnMlmeConfirm,
.OnJoinRequest = NULL, /* To be initialized by LmHandler */
.OnSendRequest = NULL, /* To be initialized by LmHandler */
.OnDeviceTimeRequest = NULL, /* To be initialized by LmHandler */
.OnSysTimeUpdate = NULL, /* To be initialized by LmHandler */
.OnPackageProcessEvent = NULL, /* To be initialized by LmHandler */
};
LmhPackage_t *LmhpCompliancePackageFactory( void )
{
return &LmhpCompliancePackage;
}
static void LmhpComplianceInit( void *params, uint8_t *dataBuffer, uint8_t dataBufferMaxSize )
{
if( ( params != NULL ) && ( dataBuffer != NULL ) )
{
LmhpComplianceParams = ( LmhpComplianceParams_t * )params;
ComplianceTestState.DataBuffer = dataBuffer;
ComplianceTestState.DataBufferMaxSize = dataBufferMaxSize;
ComplianceTestState.Initialized = true;
}
else
{
LmhpComplianceParams = NULL;
ComplianceTestState.Initialized = false;
}
}
static bool LmhpComplianceIsInitialized( void )
{
return ComplianceTestState.Initialized;
}
static bool LmhpComplianceIsRunning( void )
{
if( ComplianceTestState.Initialized == false )
{
return false;
}
return ComplianceTestState.IsRunning;
}
static void LmhpComplianceOnMcpsConfirm( McpsConfirm_t *mcpsConfirm )
{
if( ComplianceTestState.Initialized == false )
{
return;
}
if( ( ComplianceTestState.IsRunning == true ) &&
( mcpsConfirm->McpsRequest == MCPS_CONFIRMED ) &&
( mcpsConfirm->AckReceived != 0 ) )
{
/* Increment the compliance certification protocol downlink counter */
ComplianceTestState.DownLinkCounter++;
}
}
static void LmhpComplianceOnMlmeConfirm( MlmeConfirm_t *mlmeConfirm )
{
if( ComplianceTestState.Initialized == false )
{
return;
}
if( ComplianceTestState.IsRunning == false )
{
return;
}
if( mlmeConfirm->MlmeRequest == MLME_LINK_CHECK )
{
ComplianceTestState.LinkCheck = true;
ComplianceTestState.DemodMargin = mlmeConfirm->DemodMargin;
ComplianceTestState.NbGateways = mlmeConfirm->NbGateways;
}
}
static LmHandlerErrorStatus_t LmhpComplianceTxProcess( void )
{
if( ComplianceTestState.Initialized == false )
{
return LORAMAC_HANDLER_ERROR;
}
if( ComplianceTestState.IsRunning == false )
{
return LORAMAC_HANDLER_SUCCESS;
}
if( ComplianceTestState.LinkCheck == true )
{
ComplianceTestState.LinkCheck = false;
ComplianceTestState.DataBufferSize = 3;
ComplianceTestState.DataBuffer[0] = 5;
ComplianceTestState.DataBuffer[1] = ComplianceTestState.DemodMargin;
ComplianceTestState.DataBuffer[2] = ComplianceTestState.NbGateways;
ComplianceTestState.State = 1;
}
else
{
switch( ComplianceTestState.State )
{
case 4:
ComplianceTestState.State = 1;
break;
case 1:
ComplianceTestState.DataBufferSize = 2;
ComplianceTestState.DataBuffer[0] = ComplianceTestState.DownLinkCounter >> 8;
ComplianceTestState.DataBuffer[1] = ComplianceTestState.DownLinkCounter;
break;
}
}
LmHandlerAppData_t appData =
{
.Buffer = ComplianceTestState.DataBuffer,
.BufferSize = ComplianceTestState.DataBufferSize,
.Port = COMPLIANCE_PORT
};
/* Schedule next transmission */
TimerStart( &ComplianceTxNextPacketTimer );
if( LmhpCompliancePackage.OnSendRequest == NULL)
{
return LORAMAC_HANDLER_ERROR;
}
return LmhpCompliancePackage.OnSendRequest( &appData, ( LmHandlerMsgTypes_t )ComplianceTestState.IsTxConfirmed, true );
}
static void LmhpComplianceOnMcpsIndication( McpsIndication_t *mcpsIndication )
{
if( ComplianceTestState.Initialized == false )
{
return;
}
if( mcpsIndication->RxData == false )
{
return;
}
if( ( ComplianceTestState.IsRunning == true ) &&
( mcpsIndication->AckReceived == 0 ) )
{
/* Increment the compliance certification protocol downlink counter */
ComplianceTestState.DownLinkCounter++;
}
if( mcpsIndication->Port != COMPLIANCE_PORT )
{
return;
}
if( ComplianceTestState.IsRunning == false )
{
/* Check compliance test enable command (i) */
if( ( mcpsIndication->BufferSize == 4 ) &&
( mcpsIndication->Buffer[0] == 0x01 ) &&
( mcpsIndication->Buffer[1] == 0x01 ) &&
( mcpsIndication->Buffer[2] == 0x01 ) &&
( mcpsIndication->Buffer[3] == 0x01 ) )
{
MibRequestConfirm_t mibReq;
/* Initialize compliance test mode context */
ComplianceTestState.IsTxConfirmed = false;
ComplianceTestState.Port = 224;
ComplianceTestState.DataBufferSize = 2;
ComplianceTestState.DownLinkCounter = 0;
ComplianceTestState.LinkCheck = false;
ComplianceTestState.DemodMargin = 0;
ComplianceTestState.NbGateways = 0;
ComplianceTestState.IsRunning = true;
ComplianceTestState.State = 1;
/* Enable ADR while in compliance test mode */
mibReq.Type = MIB_ADR;
mibReq.Param.AdrEnable = true;
LoRaMacMibSetRequestConfirm( &mibReq );
/* Disable duty cycle enforcement while in compliance test mode */
LoRaMacTestSetDutyCycleOn( false );
/* Stop peripherals */
if( LmhpComplianceParams->StopPeripherals != NULL )
{
LmhpComplianceParams->StopPeripherals( );
}
/* Initialize compliance protocol transmission timer */
TimerInit( &ComplianceTxNextPacketTimer, OnComplianceTxNextPacketTimerEvent );
TimerSetValue( &ComplianceTxNextPacketTimer, COMPLIANCE_TX_DUTYCYCLE );
/* Confirm compliance test protocol activation */
LmhpComplianceTxProcess( );
}
}
else
{
/* Parse compliance test protocol */
ComplianceTestState.State = mcpsIndication->Buffer[0];
switch( ComplianceTestState.State )
{
case 0: /* Check compliance test disable command (ii) */
{
MibRequestConfirm_t mibReq;
TimerStop( &ComplianceTxNextPacketTimer );
/* Disable compliance test mode and reset the downlink counter. */
ComplianceTestState.DownLinkCounter = 0;
ComplianceTestState.IsRunning = false;
/* Restore previous ADR setting */
mibReq.Type = MIB_ADR;
mibReq.Param.AdrEnable = LmhpComplianceParams->AdrEnabled;
LoRaMacMibSetRequestConfirm( &mibReq );
/* Enable duty cycle enforcement */
LoRaMacTestSetDutyCycleOn( LmhpComplianceParams->DutyCycleEnabled );
/* Restart peripherals */
if( LmhpComplianceParams->StartPeripherals != NULL )
{
LmhpComplianceParams->StartPeripherals( );
}
}
break;
case 1: /* (iii, iv) */
ComplianceTestState.DataBufferSize = 2;
break;
case 2: /* Enable confirmed messages (v) */
ComplianceTestState.IsTxConfirmed = true;
ComplianceTestState.State = 1;
break;
case 3: /* Disable confirmed messages (vi) */
ComplianceTestState.IsTxConfirmed = false;
ComplianceTestState.State = 1;
break;
case 4: /* (vii) */
ComplianceTestState.DataBufferSize = mcpsIndication->BufferSize;
ComplianceTestState.DataBuffer[0] = 4;
for( uint8_t i = 1; i < MIN( ComplianceTestState.DataBufferSize, ComplianceTestState.DataBufferMaxSize ); i++ )
{
ComplianceTestState.DataBuffer[i] = mcpsIndication->Buffer[i] + 1;
}
break;
case 5: /* (viii) */
{
MlmeReq_t mlmeReq;
mlmeReq.Type = MLME_LINK_CHECK;
LoRaMacMlmeRequest( &mlmeReq );
}
break;
case 6: /* (ix) */
{
MibRequestConfirm_t mibReq;
TimerStop( &ComplianceTxNextPacketTimer );
/* Disable TestMode and revert back to normal operation */
/* Disable compliance test mode and reset the downlink counter. */
ComplianceTestState.DownLinkCounter = 0;
ComplianceTestState.IsRunning = false;
/* Restore previous ADR setting */
mibReq.Type = MIB_ADR;
mibReq.Param.AdrEnable = LmhpComplianceParams->AdrEnabled;
LoRaMacMibSetRequestConfirm( &mibReq );
/* Enable duty cycle enforcement */
LoRaMacTestSetDutyCycleOn( LmhpComplianceParams->DutyCycleEnabled );
/* Restart peripherals */
if( LmhpComplianceParams->StartPeripherals != NULL )
{
LmhpComplianceParams->StartPeripherals( );
}
if( LmhpCompliancePackage.OnJoinRequest != NULL )
{
LmhpCompliancePackage.OnJoinRequest( ACTIVATION_TYPE_OTAA, true );
}
}
break;
case 7: /* (x) */
{
MlmeReq_t mlmeReq;
if( mcpsIndication->BufferSize == 3 )
{
mlmeReq.Type = MLME_TXCW;
mlmeReq.Req.TxCw.Timeout = ( uint16_t )( ( mcpsIndication->Buffer[1] << 8 ) | mcpsIndication->Buffer[2] );
}
else if( mcpsIndication->BufferSize == 7 )
{
mlmeReq.Type = MLME_TXCW_1;
mlmeReq.Req.TxCw.Timeout = ( uint16_t )( ( mcpsIndication->Buffer[1] << 8 ) | mcpsIndication->Buffer[2] );
mlmeReq.Req.TxCw.Frequency = ( uint32_t )( ( mcpsIndication->Buffer[3] << 16 ) | ( mcpsIndication->Buffer[4] << 8 ) | mcpsIndication->Buffer[5] ) * 100;
mlmeReq.Req.TxCw.Power = mcpsIndication->Buffer[6];
}
LoRaMacMlmeRequest( &mlmeReq );
ComplianceTestState.State = 1;
}
break;
case 8: /* Send DeviceTimeReq */
{
MlmeReq_t mlmeReq;
mlmeReq.Type = MLME_DEVICE_TIME;
LoRaMacMlmeRequest( &mlmeReq );
}
break;
case 9: /* Switch end device Class */
{
MibRequestConfirm_t mibReq;
mibReq.Type = MIB_DEVICE_CLASS;
/* CLASS_A = 0, CLASS_B = 1, CLASS_C = 2 */
mibReq.Param.Class = ( DeviceClass_t )mcpsIndication->Buffer[1];;
LoRaMacMibSetRequestConfirm( &mibReq );
}
break;
case 10: /* Send PingSlotInfoReq */
{
MlmeReq_t mlmeReq;
mlmeReq.Type = MLME_PING_SLOT_INFO;
mlmeReq.Req.PingSlotInfo.PingSlot.Value = mcpsIndication->Buffer[1];
LoRaMacMlmeRequest( &mlmeReq );
}
break;
default:
break;
}
}
}
static void LmhpComplianceProcess( void )
{
/* Nothing to process */
}
static void OnComplianceTxNextPacketTimerEvent( void *context )
{
LmhpComplianceTxProcess( );
}
#elif (defined( LORAMAC_VERSION ) && (( LORAMAC_VERSION == 0x01000400 ) || ( LORAMAC_VERSION == 0x01010100 )))
#define COMPLIANCE_ID 6
#define COMPLIANCE_VERSION 1
typedef struct ClassBStatus_s
{
bool IsBeaconRxOn;
uint8_t PingSlotPeriodicity;
uint16_t BeaconCnt;
BeaconInfo_t Info;
} ClassBStatus_t;
/*!
* LoRaWAN compliance tests support data
*/
typedef struct ComplianceTestState_s
{
bool Initialized;
bool IsTxPending;
TimerTime_t TxPendingTimestamp;
LmHandlerMsgTypes_t IsTxConfirmed;
uint8_t DataBufferMaxSize;
uint8_t DataBufferSize;
uint8_t *DataBuffer;
uint16_t RxAppCnt;
bool IsBeaconRxStatusIndOn;
ClassBStatus_t ClassBStatus;
bool IsResetCmdPending;
bool IsClassReqCmdPending;
DeviceClass_t NewClass;
} ComplianceTestState_t;
typedef enum ComplianceMoteCmd_e
{
COMPLIANCE_PKG_VERSION_ANS = 0x00,
COMPLIANCE_ECHO_PAYLOAD_ANS = 0x08,
COMPLIANCE_RX_APP_CNT_ANS = 0x09,
#if 0
COMPLIANCE_BEACON_RX_STATUS_IND = 0x41,
COMPLIANCE_BEACON_CNT_ANS = 0x42,
#endif /* CLASS_B not available */
COMPLIANCE_DUT_VERSION_ANS = 0x7F,
} ComplianceMoteCmd_t;
typedef enum ComplianceSrvCmd_e
{
COMPLIANCE_PKG_VERSION_REQ = 0x00,
COMPLIANCE_DUT_RESET_REQ = 0x01,
COMPLIANCE_DUT_JOIN_REQ = 0x02,
COMPLIANCE_SWITCH_CLASS_REQ = 0x03,
COMPLIANCE_ADR_BIT_CHANGE_REQ = 0x04,
COMPLIANCE_REGIONAL_DUTY_CYCLE_CTRL_REQ = 0x05,
COMPLIANCE_TX_PERIODICITY_CHANGE_REQ = 0x06,
COMPLIANCE_TX_FRAMES_CTRL_REQ = 0x07,
COMPLIANCE_ECHO_PAYLOAD_REQ = 0x08,
COMPLIANCE_RX_APP_CNT_REQ = 0x09,
COMPLIANCE_RX_APP_CNT_RESET_REQ = 0x0A,
COMPLIANCE_LINK_CHECK_REQ = 0x20,
COMPLIANCE_DEVICE_TIME_REQ = 0x21,
COMPLIANCE_PING_SLOT_INFO_REQ = 0x22,
#if 0
COMPLIANCE_BEACON_RX_STATUS_IND_CTRL = 0x40,
COMPLIANCE_BEACON_CNT_REQ = 0x42,
COMPLIANCE_BEACON_CNT_RESET_REQ = 0x43,
#endif /* CLASS_B not available */
COMPLIANCE_TX_CW_REQ = 0x7D,
COMPLIANCE_DUT_FPORT_224_DISABLE_REQ = 0x7E,
COMPLIANCE_DUT_VERSION_REQ = 0x7F,
} ComplianceSrvCmd_t;
/*!
* Holds the compliance test current context
*/
static ComplianceTestState_t ComplianceTestState =
{
.Initialized = false,
.IsTxPending = false,
.TxPendingTimestamp = 0,
.IsTxConfirmed = LORAMAC_HANDLER_UNCONFIRMED_MSG,
.DataBufferMaxSize = 0,
.DataBufferSize = 0,
.DataBuffer = NULL,
.RxAppCnt = 0,
.ClassBStatus = { 0 },
.IsBeaconRxStatusIndOn = false,
.IsResetCmdPending = false,
.IsClassReqCmdPending = false,
.NewClass = CLASS_A,
};
/*!
* LoRaWAN compliance tests protocol handler parameters
*/
static LmhpComplianceParams_t *ComplianceParams;
/*!
* Reset Beacon status structure
*/
static inline void ClassBStatusReset( void )
{
memset1( ( uint8_t * ) &ComplianceTestState.ClassBStatus, 0, sizeof( ClassBStatus_t ) / sizeof( uint8_t ) );
}
/*!
* Initializes the compliance tests with provided parameters
*
* \param [in] params Structure containing the initial compliance
* tests parameters.
* \param [in] dataBuffer Pointer to main application buffer
* \param [in] dataBufferMaxSize Application buffer maximum size
*/
static void LmhpComplianceInit( void *params, uint8_t *dataBuffer, uint8_t dataBufferMaxSize );
/*!
* Returns the current compliance certification protocol initialization status.
*
* \retval status Compliance certification protocol initialization status
* [true: Initialized, false: Not initialized]
*/
static bool LmhpComplianceIsInitialized( void );
/*!
* Returns if a package transmission is pending or not.
*
* \retval status Package transmission status
* [true: pending, false: Not pending]
*/
static bool LmhpComplianceIsTxPending( void );
/*!
* Processes the LoRaMac Compliance events.
*/
static void LmhpComplianceProcess( void );
/*!
* Processes the MCPS Indication
*
* \param [in] mcpsIndication MCPS indication primitive data
*/
static void LmhpComplianceOnMcpsIndication( McpsIndication_t *mcpsIndication );
/*!
* Processes the MLME Confirm
*
* \param [in] mlmeConfirm MLME confirmation primitive data
*/
static void LmhpComplianceOnMlmeConfirm( MlmeConfirm_t *mlmeConfirm );
/*!
* Processes the MLME Indication
*
* \param [in] mlmeIndication MLME indication primitive data
*/
static void LmhpComplianceOnMlmeIndication( MlmeIndication_t *mlmeIndication );
#if 0
*Helper function to send the BeaconRxStatusInd message
*
* \param [IN] isBeaconRxStatusIndOn Indicates if the beacon status info is sent or not
* /
static void SendBeaconRxStatusInd( bool isBeaconRxStatusIndOn );
#endif /* CLASS_B not available */
static void OnProcessTimer( void *context );
/*!
* Process timer
*/
static TimerEvent_t ProcessTimer;
LmhPackage_t CompliancePackage =
{
.Port = COMPLIANCE_PORT,
.Init = LmhpComplianceInit,
.IsInitialized = LmhpComplianceIsInitialized,
.IsTxPending = LmhpComplianceIsTxPending,
.Process = LmhpComplianceProcess,
.OnPackageProcessEvent = NULL, /* To be initialized by LmHandler */
.OnMcpsConfirmProcess = NULL, /* Not used in this package */
.OnMcpsIndicationProcess = LmhpComplianceOnMcpsIndication,
.OnMlmeConfirmProcess = LmhpComplianceOnMlmeConfirm,
.OnMlmeIndicationProcess = LmhpComplianceOnMlmeIndication,
.OnJoinRequest = NULL, /* To be initialized by LmHandler */
.OnDeviceTimeRequest = NULL, /* To be initialized by LmHandler */
.OnSysTimeUpdate = NULL, /* To be initialized by LmHandler */
.OnSystemReset = NULL, /* To be initialized by LmHandler */
};
LmhPackage_t *LmhpCompliancePackageFactory( void )
{
return &CompliancePackage;
}
static void LmhpComplianceInit( void *params, uint8_t *dataBuffer, uint8_t dataBufferMaxSize )
{
if( ( params != NULL ) && ( dataBuffer != NULL ) )
{
ComplianceParams = ( LmhpComplianceParams_t * ) params;
ComplianceTestState.DataBuffer = dataBuffer;
ComplianceTestState.DataBufferMaxSize = dataBufferMaxSize;
ComplianceTestState.Initialized = true;
TimerInit( &ProcessTimer, OnProcessTimer );
}
else
{
ComplianceParams = NULL;
ComplianceTestState.Initialized = false;
}
ComplianceTestState.RxAppCnt = 0;
ClassBStatusReset( );
ComplianceTestState.IsTxPending = false;
ComplianceTestState.IsBeaconRxStatusIndOn = false;
ComplianceTestState.IsResetCmdPending = false;
ComplianceTestState.IsClassReqCmdPending = false;
}
static bool LmhpComplianceIsInitialized( void )
{
return ComplianceTestState.Initialized;
}
static bool LmhpComplianceIsTxPending( void )
{
return ComplianceTestState.IsTxPending;
}
static void LmhpComplianceProcess( void )
{
if( ComplianceTestState.IsTxPending == true )
{
TimerTime_t now = TimerGetCurrentTime( );
if( now > ( ComplianceTestState.TxPendingTimestamp + LmHandlerGetDutyCycleWaitTime( ) ) )
{
if( ComplianceTestState.DataBufferSize != 0 )
{
/* Answer commands */
LmHandlerAppData_t appData =
{
.Buffer = ComplianceTestState.DataBuffer,
.BufferSize = ComplianceTestState.DataBufferSize,
.Port = COMPLIANCE_PORT,
};
LmHandlerErrorStatus_t lmhStatus = LORAMAC_HANDLER_ERROR;
lmhStatus = LmHandlerSend( &appData, ComplianceTestState.IsTxConfirmed, true );
if( ( lmhStatus == LORAMAC_HANDLER_SUCCESS ) || ( lmhStatus == LORAMAC_HANDLER_PAYLOAD_LENGTH_RESTRICTED ) )
{
ComplianceTestState.IsTxPending = false;
ComplianceTestState.DataBufferSize = 0;
}
else
{
/* try to send the message again */
TimerSetValue( &ProcessTimer, 1500 );
TimerStart( &ProcessTimer );
}
ComplianceTestState.TxPendingTimestamp = now;
}
}
}
else
{
/* If no Tx is pending process other commands */
if( ComplianceTestState.IsClassReqCmdPending == true )
{
ComplianceTestState.IsClassReqCmdPending = false;
LmHandlerRequestClass( ComplianceTestState.NewClass );
}
}
if( ComplianceTestState.IsResetCmdPending == true )
{
ComplianceTestState.IsResetCmdPending = false;
/* Call platform MCU reset API */
if( CompliancePackage.OnSystemReset != NULL )
{
CompliancePackage.OnSystemReset( );
}
}
}
static void LmhpComplianceOnMcpsIndication( McpsIndication_t *mcpsIndication )
{
uint8_t cmdIndex = 0;
MibRequestConfirm_t mibReq;
if( ComplianceTestState.Initialized == false )
{
return;
}
/* Increment the compliance certification protocol downlink counter */
/* Not counting downlinks on FPort 0 */
if( ( mcpsIndication->Port > 0 ) || ( mcpsIndication->AckReceived == true ) )
{
ComplianceTestState.RxAppCnt++;
}
if( mcpsIndication->RxData == false )
{
return;
}
if( mcpsIndication->Port != COMPLIANCE_PORT )
{
return;
}
ComplianceTestState.DataBufferSize = 0;
switch( mcpsIndication->Buffer[cmdIndex++] )
{
case COMPLIANCE_PKG_VERSION_REQ:
{
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = COMPLIANCE_PKG_VERSION_ANS;
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = COMPLIANCE_ID;
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = COMPLIANCE_VERSION;
break;
}
case COMPLIANCE_DUT_RESET_REQ:
{
ComplianceTestState.IsResetCmdPending = true;
break;
}
case COMPLIANCE_DUT_JOIN_REQ:
{
if( CompliancePackage.OnJoinRequest != NULL )
{
CompliancePackage.OnJoinRequest( ACTIVATION_TYPE_OTAA, true );
}
break;
}
case COMPLIANCE_SWITCH_CLASS_REQ:
{
/* CLASS_A = 0, CLASS_B = 1, CLASS_C = 2 */
ComplianceTestState.NewClass = ( DeviceClass_t ) mcpsIndication->Buffer[cmdIndex++];
ComplianceTestState.IsClassReqCmdPending = true;
break;
}
case COMPLIANCE_ADR_BIT_CHANGE_REQ:
{
MibRequestConfirm_t mibReq;
mibReq.Type = MIB_ADR;
mibReq.Param.AdrEnable = mcpsIndication->Buffer[cmdIndex++];
LoRaMacMibSetRequestConfirm( &mibReq );
break;
}
case COMPLIANCE_REGIONAL_DUTY_CYCLE_CTRL_REQ:
{
LoRaMacTestSetDutyCycleOn( mcpsIndication->Buffer[cmdIndex++] );
break;
}
case COMPLIANCE_TX_PERIODICITY_CHANGE_REQ:
{
/* Periodicity in milli-seconds */
uint32_t periodicity[] = { 0, 5000, 10000, 20000, 30000, 40000, 50000, 60000, 120000, 240000, 480000 };
uint8_t index = mcpsIndication->Buffer[cmdIndex++];
if( index < ( sizeof( periodicity ) / sizeof( uint32_t ) ) )
{
if( ComplianceParams->OnTxPeriodicityChanged != NULL )
{
ComplianceParams->OnTxPeriodicityChanged( periodicity[index] );
}
}
break;
}
case COMPLIANCE_TX_FRAMES_CTRL_REQ:
{
uint8_t frameType = mcpsIndication->Buffer[cmdIndex++];
if( ( frameType == 1 ) || ( frameType == 2 ) )
{
ComplianceTestState.IsTxConfirmed = ( frameType != 1 ) ? LORAMAC_HANDLER_CONFIRMED_MSG : LORAMAC_HANDLER_UNCONFIRMED_MSG;
if( ComplianceParams->OnTxFrameCtrlChanged != NULL )
{
ComplianceParams->OnTxFrameCtrlChanged( ComplianceTestState.IsTxConfirmed );
}
}
break;
}
case COMPLIANCE_ECHO_PAYLOAD_REQ:
{
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = COMPLIANCE_ECHO_PAYLOAD_ANS;
for( uint8_t i = 1; i < MIN( mcpsIndication->BufferSize, ComplianceTestState.DataBufferMaxSize );
i++ )
{
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = mcpsIndication->Buffer[cmdIndex++] + 1;
}
break;
}
case COMPLIANCE_RX_APP_CNT_REQ:
{
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = COMPLIANCE_RX_APP_CNT_ANS;
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceTestState.RxAppCnt;
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceTestState.RxAppCnt >> 8;
break;
}
case COMPLIANCE_RX_APP_CNT_RESET_REQ:
{
ComplianceTestState.RxAppCnt = 0;
break;
}
case COMPLIANCE_LINK_CHECK_REQ:
{
MlmeReq_t mlmeReq;
mlmeReq.Type = MLME_LINK_CHECK;
LoRaMacMlmeRequest( &mlmeReq );
break;
}
case COMPLIANCE_DEVICE_TIME_REQ:
{
CompliancePackage.OnDeviceTimeRequest( );
break;
}
case COMPLIANCE_PING_SLOT_INFO_REQ:
{
ComplianceTestState.ClassBStatus.PingSlotPeriodicity = mcpsIndication->Buffer[cmdIndex++];
if( ComplianceParams->OnPingSlotPeriodicityChanged != NULL )
{
ComplianceParams->OnPingSlotPeriodicityChanged( ComplianceTestState.ClassBStatus.PingSlotPeriodicity );
}
break;
}
#if 0
case COMPLIANCE_BEACON_RX_STATUS_IND_CTRL:
{
ComplianceTestState.IsBeaconRxStatusIndOn = ( bool ) mcpsIndication->Buffer[cmdIndex++];
break;
}
case COMPLIANCE_BEACON_CNT_REQ:
{
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = COMPLIANCE_BEACON_CNT_ANS;
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceTestState.ClassBStatus.BeaconCnt;
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceTestState.ClassBStatus.BeaconCnt >> 8;
break;
}
case COMPLIANCE_BEACON_CNT_RESET_REQ:
{
ComplianceTestState.ClassBStatus.BeaconCnt = 0;
break;
}
#endif /* CLASS_B not available */
case COMPLIANCE_TX_CW_REQ:
{
MlmeReq_t mlmeReq;
if( mcpsIndication->BufferSize == 7 )
{
mlmeReq.Type = MLME_TXCW;
mlmeReq.Req.TxCw.Timeout =
( uint16_t )( mcpsIndication->Buffer[cmdIndex] | ( mcpsIndication->Buffer[cmdIndex + 1] << 8 ) );
cmdIndex += 2;
mlmeReq.Req.TxCw.Frequency =
( uint32_t )( mcpsIndication->Buffer[cmdIndex] | ( mcpsIndication->Buffer[cmdIndex + 1] << 8 ) |
( mcpsIndication->Buffer[cmdIndex + 2] << 16 ) ) *
100;
cmdIndex += 3;
mlmeReq.Req.TxCw.Power = mcpsIndication->Buffer[cmdIndex++];
LoRaMacMlmeRequest( &mlmeReq );
}
break;
}
case COMPLIANCE_DUT_FPORT_224_DISABLE_REQ:
{
mibReq.Type = MIB_IS_CERT_FPORT_ON;
mibReq.Param.IsCertPortOn = false;
LoRaMacMibSetRequestConfirm( &mibReq );
ComplianceTestState.IsResetCmdPending = true;
break;
}
case COMPLIANCE_DUT_VERSION_REQ:
{
Version_t lrwanVersion;
Version_t lrwanRpVersion;
MibRequestConfirm_t mibReq;
mibReq.Type = MIB_LORAWAN_VERSION;
LoRaMacMibGetRequestConfirm( &mibReq );
lrwanVersion = mibReq.Param.LrWanVersion.LoRaWan;
lrwanRpVersion = mibReq.Param.LrWanVersion.LoRaWanRegion;
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = COMPLIANCE_DUT_VERSION_ANS;
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceParams->FwVersion.Fields.Major;
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceParams->FwVersion.Fields.Minor;
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceParams->FwVersion.Fields.Patch;
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceParams->FwVersion.Fields.Revision;
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = lrwanVersion.Fields.Major;
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = lrwanVersion.Fields.Minor;
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = lrwanVersion.Fields.Patch;
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = lrwanVersion.Fields.Revision;
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = lrwanRpVersion.Fields.Major;
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = lrwanRpVersion.Fields.Minor;
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = lrwanRpVersion.Fields.Patch;
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = lrwanRpVersion.Fields.Revision;
break;
}
default:
{
break;
}
}
if( ComplianceTestState.DataBufferSize != 0 )
{
if( ProcessTimer.IsRunning == 0U)
{
TimerSetValue( &ProcessTimer, 1000 );
TimerStart( &ProcessTimer );
}
}
else
{
/* Abort any pending Tx as a new command has been processed */
TimerStop( &ProcessTimer );
ComplianceTestState.IsTxPending = false;
}
}
static void LmhpComplianceOnMlmeConfirm( MlmeConfirm_t *mlmeConfirm )
{
switch( mlmeConfirm->MlmeRequest )
{
#if 0
case MLME_BEACON_ACQUISITION:
{
if( mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK )
{
ClassBStatusReset( );
ComplianceTestState.ClassBStatus.IsBeaconRxOn = true;
}
else
{
ComplianceTestState.ClassBStatus.IsBeaconRxOn = false;
}
break;
}
#endif /* CLASS_B not available */
default:
break;
}
}
static void LmhpComplianceOnMlmeIndication( MlmeIndication_t *mlmeIndication )
{
if( ComplianceTestState.Initialized == false )
{
return;
}
switch( mlmeIndication->MlmeIndication )
{
#if 0
case MLME_BEACON_LOST:
{
ClassBStatusReset( );
SendBeaconRxStatusInd( ComplianceTestState.IsBeaconRxStatusIndOn );
break;
}
case MLME_BEACON:
{
if( mlmeIndication->Status == LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED )
{
/* As we received a beacon ensure that IsBeaconRxOn is set to true */
if( ComplianceTestState.ClassBStatus.IsBeaconRxOn == false )
{
ComplianceTestState.ClassBStatus.IsBeaconRxOn = true;
}
ComplianceTestState.ClassBStatus.BeaconCnt++;
}
ComplianceTestState.ClassBStatus.Info = mlmeIndication->BeaconInfo;
SendBeaconRxStatusInd( ComplianceTestState.IsBeaconRxStatusIndOn );
break;
}
#endif /* CLASS_B not available */
default:
break;
}
}
#if 0
static void SendBeaconRxStatusInd( bool isBeaconRxStatusIndOn )
{
if( isBeaconRxStatusIndOn == false )
{
return;
}
uint32_t frequency = ComplianceTestState.ClassBStatus.Info.Frequency / 100;
ComplianceTestState.DataBufferSize = 0;
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = COMPLIANCE_BEACON_RX_STATUS_IND;
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( ComplianceTestState.ClassBStatus.IsBeaconRxOn == true ) ? 1 : 0;
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.BeaconCnt );
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.BeaconCnt >> 8 );
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( frequency );
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( frequency >> 8 );
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( frequency >> 16 );
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceTestState.ClassBStatus.Info.Datarate;
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.Rssi );
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.Rssi >> 8 );
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.Snr );
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.Param );
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.Time.Seconds );
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.Time.Seconds >> 8 );
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.Time.Seconds >> 16 );
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.Time.Seconds >> 24 );
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.GwSpecific.InfoDesc );
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.GwSpecific.Info[0] );
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.GwSpecific.Info[1] );
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.GwSpecific.Info[2] );
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.GwSpecific.Info[3] );
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.GwSpecific.Info[4] );
ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.GwSpecific.Info[5] );
if( ProcessTimer.IsRunning == 0U)
{
TimerSetValue( &ProcessTimer, 1000 );
TimerStart( &ProcessTimer );
}
}
#endif /* CLASS_B not available */
static void OnProcessTimer( void *context )
{
if( ComplianceTestState.DataBufferSize != 0 )
{
ComplianceTestState.IsTxPending = true;
}
if( CompliancePackage.OnPackageProcessEvent != NULL )
{
CompliancePackage.OnPackageProcessEvent();
}
}
#endif /* LORAMAC_VERSION */