/*! * \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 #include #include #include "utilities.h" #include "timer.h" #include "LoRaMac.h" #include "LoRaMacTest.h" #include "Region.h" #include "LmhPackage.h" #include "LmhpCompliance.h" /*! * LoRaWAN compliance certification protocol port number. * * LoRaWAN Specification V1.0.2, chapter 4.3.2 */ #define COMPLIANCE_PORT 224 /*! * 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 /*! * 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 ); return LmhpCompliancePackage.OnSendRequest( &appData, ( LmHandlerMsgTypes_t )ComplianceTestState.IsTxConfirmed, NULL, 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 seeting 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 seeting 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( ); } LmhpCompliancePackage.OnJoinRequest( ACTIVATION_TYPE_OTAA ); } 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( ); }