1 /*!
2  * \file      LmhpCompliance.c
3  *
4  * \brief     Implements the LoRa-Alliance certification protocol handling
5  *
6  * \copyright Revised BSD License, see section \ref LICENSE.
7  *
8  * \code
9  *                ______                              _
10  *               / _____)             _              | |
11  *              ( (____  _____ ____ _| |_ _____  ____| |__
12  *               \____ \| ___ |    (_   _) ___ |/ ___)  _ \
13  *               _____) ) ____| | | || |_| ____( (___| | | |
14  *              (______/|_____)_|_|_| \__)_____)\____)_| |_|
15  *              (C)2013-2018 Semtech
16  *
17  * \endcode
18  *
19  * \author    Miguel Luis ( Semtech )
20  */
21 #include <stdlib.h>
22 #include <stdint.h>
23 #include <stdbool.h>
24 #include "board.h"
25 #include "NvmDataMgmt.h"
26 #include "LoRaMacTest.h"
27 #include "LmHandler.h"
28 #include "LmhpCompliance.h"
29 
30 /*!
31  * LoRaWAN compliance certification protocol port number.
32  *
33  * LoRaWAN Specification V1.x.x, chapter 4.3.2
34  */
35 #define COMPLIANCE_PORT 224
36 
37 #define COMPLIANCE_ID 6
38 #define COMPLIANCE_VERSION 1
39 
40 typedef struct ClassBStatus_s
41 {
42     bool         IsBeaconRxOn;
43     uint8_t      PingSlotPeriodicity;
44     uint16_t     BeaconCnt;
45     BeaconInfo_t Info;
46 } ClassBStatus_t;
47 
48 /*!
49  * LoRaWAN compliance tests support data
50  */
51 typedef struct ComplianceTestState_s
52 {
53     bool                Initialized;
54     bool                IsTxPending;
55     TimerTime_t         TxPendingTimestamp;
56     LmHandlerMsgTypes_t IsTxConfirmed;
57     uint8_t             DataBufferMaxSize;
58     uint8_t             DataBufferSize;
59     uint8_t*            DataBuffer;
60     uint16_t            RxAppCnt;
61     bool                IsBeaconRxStatusIndOn;
62     ClassBStatus_t      ClassBStatus;
63     bool                IsResetCmdPending;
64     bool                IsClassReqCmdPending;
65     DeviceClass_t       NewClass;
66 } ComplianceTestState_t;
67 
68 typedef enum ComplianceMoteCmd_e
69 {
70     COMPLIANCE_PKG_VERSION_ANS      = 0x00,
71     COMPLIANCE_ECHO_PAYLOAD_ANS     = 0x08,
72     COMPLIANCE_RX_APP_CNT_ANS       = 0x09,
73     COMPLIANCE_BEACON_RX_STATUS_IND = 0x41,
74     COMPLIANCE_BEACON_CNT_ANS       = 0x42,
75     COMPLIANCE_DUT_VERSION_ANS      = 0x7F,
76 } ComplianceMoteCmd_t;
77 
78 typedef enum ComplianceSrvCmd_e
79 {
80     COMPLIANCE_PKG_VERSION_REQ              = 0x00,
81     COMPLIANCE_DUT_RESET_REQ                = 0x01,
82     COMPLIANCE_DUT_JOIN_REQ                 = 0x02,
83     COMPLIANCE_SWITCH_CLASS_REQ             = 0x03,
84     COMPLIANCE_ADR_BIT_CHANGE_REQ           = 0x04,
85     COMPLIANCE_REGIONAL_DUTY_CYCLE_CTRL_REQ = 0x05,
86     COMPLIANCE_TX_PERIODICITY_CHANGE_REQ    = 0x06,
87     COMPLIANCE_TX_FRAMES_CTRL_REQ           = 0x07,
88     COMPLIANCE_ECHO_PAYLOAD_REQ             = 0x08,
89     COMPLIANCE_RX_APP_CNT_REQ               = 0x09,
90     COMPLIANCE_RX_APP_CNT_RESET_REQ         = 0x0A,
91     COMPLIANCE_LINK_CHECK_REQ               = 0x20,
92     COMPLIANCE_DEVICE_TIME_REQ              = 0x21,
93     COMPLIANCE_PING_SLOT_INFO_REQ           = 0x22,
94     COMPLIANCE_BEACON_RX_STATUS_IND_CTRL    = 0x40,
95     COMPLIANCE_BEACON_CNT_REQ               = 0x42,
96     COMPLIANCE_BEACON_CNT_RESET_REQ         = 0x43,
97     COMPLIANCE_TX_CW_REQ                    = 0x7D,
98     COMPLIANCE_DUT_FPORT_224_DISABLE_REQ    = 0x7E,
99     COMPLIANCE_DUT_VERSION_REQ              = 0x7F,
100 } ComplianceSrvCmd_t;
101 
102 /*!
103  * Holds the compliance test current context
104  */
105 static ComplianceTestState_t ComplianceTestState = {
106     .Initialized           = false,
107     .IsTxPending           = false,
108     .TxPendingTimestamp    = 0,
109     .IsTxConfirmed         = LORAMAC_HANDLER_UNCONFIRMED_MSG,
110     .DataBufferMaxSize     = 0,
111     .DataBufferSize        = 0,
112     .DataBuffer            = NULL,
113     .RxAppCnt              = 0,
114     .ClassBStatus          = { 0 },
115     .IsBeaconRxStatusIndOn = false,
116     .IsResetCmdPending     = false,
117     .IsClassReqCmdPending  = false,
118     .NewClass              = CLASS_A,
119 };
120 
121 /*!
122  * LoRaWAN compliance tests protocol handler parameters
123  */
124 static LmhpComplianceParams_t* ComplianceParams;
125 
126 /*!
127  * Reset Beacon status structure
128  */
ClassBStatusReset(void)129 static inline void ClassBStatusReset( void )
130 {
131     memset1( ( uint8_t* ) &ComplianceTestState.ClassBStatus, 0, sizeof( ClassBStatus_t ) / sizeof( uint8_t ) );
132 }
133 
134 /*!
135  * Initializes the compliance tests with provided parameters
136  *
137  * \param [IN] params Structure containing the initial compliance
138  *                    tests parameters.
139  * \param [IN] dataBuffer        Pointer to main application buffer
140  * \param [IN] dataBufferMaxSize Application buffer maximum size
141  */
142 static void LmhpComplianceInit( void* params, uint8_t* dataBuffer, uint8_t dataBufferMaxSize );
143 
144 /*!
145  * Returns the current compliance certification protocol initialization status.
146  *
147  * \retval status Compliance certification protocol initialization status
148  *                [true: Initialized, false: Not initialized]
149  */
150 static bool LmhpComplianceIsInitialized( void );
151 
152 /*!
153  * Returns if a package transmission is pending or not.
154  *
155  * \retval status Package transmission status
156  *                [true: pending, false: Not pending]
157  */
158 static bool LmhpComplianceIsTxPending( void );
159 
160 /*!
161  * Processes the LoRaMac Compliance events.
162  */
163 static void LmhpComplianceProcess( void );
164 
165 /*!
166  * Processes the MCPS Indication
167  *
168  * \param [IN] mcpsIndication     MCPS indication primitive data
169  */
170 static void LmhpComplianceOnMcpsIndication( McpsIndication_t* mcpsIndication );
171 
172 /*!
173  * Processes the MLME Confirm
174  *
175  * \param [IN] mlmeConfirm MLME confirmation primitive data
176  */
177 static void LmhpComplianceOnMlmeConfirm( MlmeConfirm_t *mlmeConfirm );
178 
179 /*!
180  * Processes the MLME Indication
181  *
182  * \param [IN] mlmeIndication     MLME indication primitive data
183  */
184 static void LmhpComplianceOnMlmeIndication( MlmeIndication_t* mlmeIndication );
185 
186 /*!
187  * Helper function to send the BeaconRxStatusInd message
188  *
189  * \param [IN] isBeaconRxStatusIndOn Indicates if the beacon status info is sent or not
190  */
191 static void SendBeaconRxStatusInd( bool isBeaconRxStatusIndOn );
192 
193 LmhPackage_t CompliancePackage = {
194     .Port                    = COMPLIANCE_PORT,
195     .Init                    = LmhpComplianceInit,
196     .IsInitialized           = LmhpComplianceIsInitialized,
197     .IsTxPending             = LmhpComplianceIsTxPending,
198     .Process                 = LmhpComplianceProcess,
199     .OnMcpsConfirmProcess    = NULL,  // Not used in this package
200     .OnMcpsIndicationProcess = LmhpComplianceOnMcpsIndication,
201     .OnMlmeConfirmProcess    = LmhpComplianceOnMlmeConfirm,
202     .OnMlmeIndicationProcess = LmhpComplianceOnMlmeIndication,
203     .OnMacMcpsRequest        = NULL,  // To be initialized by LmHandler
204     .OnMacMlmeRequest        = NULL,  // To be initialized by LmHandler
205     .OnJoinRequest           = NULL,  // To be initialized by LmHandler
206     .OnDeviceTimeRequest     = NULL,  // To be initialized by LmHandler
207     .OnSysTimeUpdate         = NULL,  // To be initialized by LmHandler
208 };
209 
LmphCompliancePackageFactory(void)210 LmhPackage_t* LmphCompliancePackageFactory( void )
211 {
212     return &CompliancePackage;
213 }
214 
LmhpComplianceInit(void * params,uint8_t * dataBuffer,uint8_t dataBufferMaxSize)215 static void LmhpComplianceInit( void* params, uint8_t* dataBuffer, uint8_t dataBufferMaxSize )
216 {
217     if( ( params != NULL ) && ( dataBuffer != NULL ) )
218     {
219         ComplianceParams                      = ( LmhpComplianceParams_t* ) params;
220         ComplianceTestState.DataBuffer        = dataBuffer;
221         ComplianceTestState.DataBufferMaxSize = dataBufferMaxSize;
222         ComplianceTestState.Initialized       = true;
223     }
224     else
225     {
226         ComplianceParams                = NULL;
227         ComplianceTestState.Initialized = false;
228     }
229     ComplianceTestState.RxAppCnt = 0;
230     ClassBStatusReset( );
231     ComplianceTestState.IsTxPending = false;
232     ComplianceTestState.IsBeaconRxStatusIndOn = false;
233     ComplianceTestState.IsResetCmdPending = false;
234     ComplianceTestState.IsClassReqCmdPending = false;
235 }
236 
LmhpComplianceIsInitialized(void)237 static bool LmhpComplianceIsInitialized( void )
238 {
239     return ComplianceTestState.Initialized;
240 }
241 
LmhpComplianceIsTxPending(void)242 static bool LmhpComplianceIsTxPending( void )
243 {
244     return ComplianceTestState.IsTxPending;
245 }
246 
LmhpComplianceProcess(void)247 static void LmhpComplianceProcess( void )
248 {
249     if( ComplianceTestState.IsTxPending == true )
250     {
251         TimerTime_t now = TimerGetCurrentTime( );
252         if( now > ( ComplianceTestState.TxPendingTimestamp + LmHandlerGetDutyCycleWaitTime( ) ) )
253         {
254             if( ComplianceTestState.DataBufferSize != 0 )
255             {
256                 // Answer commands
257                 LmHandlerAppData_t appData = {
258                     .Buffer     = ComplianceTestState.DataBuffer,
259                     .BufferSize = ComplianceTestState.DataBufferSize,
260                     .Port       = COMPLIANCE_PORT,
261                 };
262 
263                 if( LmHandlerSend( &appData, ComplianceTestState.IsTxConfirmed ) != LORAMAC_HANDLER_SUCCESS )
264                 {
265                     // try to send the message again
266                     ComplianceTestState.IsTxPending = true;
267                 }
268                 else
269                 {
270                     ComplianceTestState.IsTxPending = false;
271                 }
272                 ComplianceTestState.TxPendingTimestamp = now;
273             }
274         }
275     }
276     else
277     { // If no Tx is pending process other commands
278         if( ComplianceTestState.IsClassReqCmdPending == true )
279         {
280             ComplianceTestState.IsClassReqCmdPending = false;
281             LmHandlerRequestClass( ComplianceTestState.NewClass );
282         }
283     }
284 
285     if( ComplianceTestState.IsResetCmdPending == true )
286     {
287         ComplianceTestState.IsResetCmdPending = false;
288 
289         // Call platform MCU reset API
290         BoardResetMcu( );
291     }
292 }
293 
LmhpComplianceOnMcpsIndication(McpsIndication_t * mcpsIndication)294 static void LmhpComplianceOnMcpsIndication( McpsIndication_t* mcpsIndication )
295 {
296     uint8_t cmdIndex        = 0;
297     MibRequestConfirm_t mibReq;
298 
299     if( ComplianceTestState.Initialized == false )
300     {
301         return;
302     }
303 
304     // Increment the compliance certification protocol downlink counter
305     // Not counting downlinks on FPort 0
306     if( ( mcpsIndication->Port > 0 ) || ( mcpsIndication->AckReceived == true ) )
307     {
308         ComplianceTestState.RxAppCnt++;
309     }
310 
311     if( mcpsIndication->RxData == false )
312     {
313         return;
314     }
315 
316     if( mcpsIndication->Port != COMPLIANCE_PORT )
317     {
318         return;
319     }
320 
321     ComplianceTestState.DataBufferSize = 0;
322 
323     switch( mcpsIndication->Buffer[cmdIndex++] )
324     {
325     case COMPLIANCE_PKG_VERSION_REQ:
326     {
327         ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = COMPLIANCE_PKG_VERSION_ANS;
328         ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = COMPLIANCE_ID;
329         ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = COMPLIANCE_VERSION;
330         break;
331     }
332     case COMPLIANCE_DUT_RESET_REQ:
333     {
334         ComplianceTestState.IsResetCmdPending = true;
335         break;
336     }
337     case COMPLIANCE_DUT_JOIN_REQ:
338     {
339         CompliancePackage.OnJoinRequest( true );
340         break;
341     }
342     case COMPLIANCE_SWITCH_CLASS_REQ:
343     {
344         // CLASS_A = 0, CLASS_B = 1, CLASS_C = 2
345         ComplianceTestState.NewClass = ( DeviceClass_t ) mcpsIndication->Buffer[cmdIndex++];
346         ComplianceTestState.IsClassReqCmdPending = true;
347         break;
348     }
349     case COMPLIANCE_ADR_BIT_CHANGE_REQ:
350     {
351         MibRequestConfirm_t mibReq;
352         mibReq.Type            = MIB_ADR;
353         mibReq.Param.AdrEnable = mcpsIndication->Buffer[cmdIndex++];
354 
355         LoRaMacMibSetRequestConfirm( &mibReq );
356         break;
357     }
358     case COMPLIANCE_REGIONAL_DUTY_CYCLE_CTRL_REQ:
359     {
360         LoRaMacTestSetDutyCycleOn( mcpsIndication->Buffer[cmdIndex++] );
361         break;
362     }
363     case COMPLIANCE_TX_PERIODICITY_CHANGE_REQ:
364     {
365         // Periodicity in milli-seconds
366         uint32_t periodicity[] = { 0, 5000, 10000, 20000, 30000, 40000, 50000, 60000, 120000, 240000, 480000 };
367         uint8_t  index         = mcpsIndication->Buffer[cmdIndex++];
368 
369         if( index < ( sizeof( periodicity ) / sizeof( uint32_t ) ) )
370         {
371             if( ComplianceParams->OnTxPeriodicityChanged != NULL )
372             {
373                 ComplianceParams->OnTxPeriodicityChanged( periodicity[index] );
374             }
375         }
376         break;
377     }
378     case COMPLIANCE_TX_FRAMES_CTRL_REQ:
379     {
380         uint8_t frameType = mcpsIndication->Buffer[cmdIndex++];
381 
382         if( ( frameType == 1 ) || ( frameType == 2 ) )
383         {
384             ComplianceTestState.IsTxConfirmed = ( frameType != 1 ) ? LORAMAC_HANDLER_CONFIRMED_MSG : LORAMAC_HANDLER_UNCONFIRMED_MSG;
385 
386             ComplianceParams->OnTxFrameCtrlChanged( ComplianceTestState.IsTxConfirmed );
387         }
388         break;
389     }
390     case COMPLIANCE_ECHO_PAYLOAD_REQ:
391     {
392         ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = COMPLIANCE_ECHO_PAYLOAD_ANS;
393 
394         for( uint8_t i = 1; i < MIN( mcpsIndication->BufferSize, ComplianceTestState.DataBufferMaxSize );
395              i++ )
396         {
397             ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = mcpsIndication->Buffer[cmdIndex++] + 1;
398         }
399         break;
400     }
401     case COMPLIANCE_RX_APP_CNT_REQ:
402     {
403         ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = COMPLIANCE_RX_APP_CNT_ANS;
404         ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceTestState.RxAppCnt;
405         ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceTestState.RxAppCnt >> 8;
406         break;
407     }
408     case COMPLIANCE_RX_APP_CNT_RESET_REQ:
409     {
410         ComplianceTestState.RxAppCnt = 0;
411         break;
412     }
413     case COMPLIANCE_LINK_CHECK_REQ:
414     {
415         MlmeReq_t mlmeReq;
416         mlmeReq.Type = MLME_LINK_CHECK;
417 
418         LoRaMacStatus_t status = LoRaMacMlmeRequest( &mlmeReq );
419         if( CompliancePackage.OnMacMlmeRequest != NULL )
420         {
421             CompliancePackage.OnMacMlmeRequest( status, &mlmeReq, mlmeReq.ReqReturn.DutyCycleWaitTime );
422         }
423         break;
424     }
425     case COMPLIANCE_DEVICE_TIME_REQ:
426     {
427         CompliancePackage.OnDeviceTimeRequest( );
428         break;
429     }
430     case COMPLIANCE_PING_SLOT_INFO_REQ:
431     {
432         ComplianceTestState.ClassBStatus.PingSlotPeriodicity = mcpsIndication->Buffer[cmdIndex++];
433         ComplianceParams->OnPingSlotPeriodicityChanged( ComplianceTestState.ClassBStatus.PingSlotPeriodicity );
434         break;
435     }
436     case COMPLIANCE_BEACON_RX_STATUS_IND_CTRL:
437     {
438         ComplianceTestState.IsBeaconRxStatusIndOn = ( bool ) mcpsIndication->Buffer[cmdIndex++];
439         break;
440     }
441     case COMPLIANCE_BEACON_CNT_REQ:
442     {
443         ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = COMPLIANCE_BEACON_CNT_ANS;
444         ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceTestState.ClassBStatus.BeaconCnt;
445         ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceTestState.ClassBStatus.BeaconCnt >> 8;
446         break;
447     }
448     case COMPLIANCE_BEACON_CNT_RESET_REQ:
449     {
450         ComplianceTestState.ClassBStatus.BeaconCnt = 0;
451         break;
452     }
453     case COMPLIANCE_TX_CW_REQ:
454     {
455         MlmeReq_t mlmeReq;
456         if( mcpsIndication->BufferSize == 7 )
457         {
458             mlmeReq.Type = MLME_TXCW;
459             mlmeReq.Req.TxCw.Timeout =
460                 ( uint16_t )( mcpsIndication->Buffer[cmdIndex] | ( mcpsIndication->Buffer[cmdIndex + 1] << 8 ) );
461             cmdIndex += 2;
462             mlmeReq.Req.TxCw.Frequency =
463                 ( uint32_t )( mcpsIndication->Buffer[cmdIndex] | ( mcpsIndication->Buffer[cmdIndex + 1] << 8 ) |
464                                ( mcpsIndication->Buffer[cmdIndex + 2] << 16 ) ) *
465                 100;
466             cmdIndex += 3;
467             mlmeReq.Req.TxCw.Power = mcpsIndication->Buffer[cmdIndex++];
468 
469             CompliancePackage.OnMacMlmeRequest( LoRaMacMlmeRequest( &mlmeReq ), &mlmeReq,
470                                                 mlmeReq.ReqReturn.DutyCycleWaitTime );
471         }
472         break;
473     }
474     case COMPLIANCE_DUT_FPORT_224_DISABLE_REQ:
475     {
476         mibReq.Type = MIB_IS_CERT_FPORT_ON;
477         mibReq.Param.IsCertPortOn = false;
478         LoRaMacMibSetRequestConfirm( &mibReq );
479 
480         ComplianceTestState.IsResetCmdPending = true;
481         break;
482     }
483     case COMPLIANCE_DUT_VERSION_REQ:
484     {
485         Version_t           lrwanVersion;
486         Version_t           lrwanRpVersion;
487         MibRequestConfirm_t mibReq;
488 
489         mibReq.Type = MIB_LORAWAN_VERSION;
490 
491         LoRaMacMibGetRequestConfirm( &mibReq );
492         lrwanVersion   = mibReq.Param.LrWanVersion.LoRaWan;
493         lrwanRpVersion = mibReq.Param.LrWanVersion.LoRaWanRegion;
494 
495         ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = COMPLIANCE_DUT_VERSION_ANS;
496         ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceParams->FwVersion.Fields.Major;
497         ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceParams->FwVersion.Fields.Minor;
498         ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceParams->FwVersion.Fields.Patch;
499         ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceParams->FwVersion.Fields.Revision;
500         ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = lrwanVersion.Fields.Major;
501         ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = lrwanVersion.Fields.Minor;
502         ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = lrwanVersion.Fields.Patch;
503         ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = lrwanVersion.Fields.Revision;
504         ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = lrwanRpVersion.Fields.Major;
505         ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = lrwanRpVersion.Fields.Minor;
506         ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = lrwanRpVersion.Fields.Patch;
507         ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = lrwanRpVersion.Fields.Revision;
508         break;
509     }
510     default:
511     {
512         break;
513     }
514     }
515 
516     if( ComplianceTestState.DataBufferSize != 0 )
517     {
518         ComplianceTestState.IsTxPending = true;
519     }
520     else
521     {
522         // Abort any pending Tx as a new command has been processed
523         ComplianceTestState.IsTxPending = false;
524     }
525 }
526 
LmhpComplianceOnMlmeConfirm(MlmeConfirm_t * mlmeConfirm)527 static void LmhpComplianceOnMlmeConfirm( MlmeConfirm_t *mlmeConfirm )
528 {
529     switch( mlmeConfirm->MlmeRequest )
530     {
531     case MLME_BEACON_ACQUISITION:
532     {
533         if( mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK )
534         {
535             ClassBStatusReset( );
536             ComplianceTestState.ClassBStatus.IsBeaconRxOn = true;
537         }
538         else
539         {
540             ComplianceTestState.ClassBStatus.IsBeaconRxOn = false;
541         }
542         break;
543     }
544     default:
545         break;
546     }
547 }
548 
LmhpComplianceOnMlmeIndication(MlmeIndication_t * mlmeIndication)549 static void LmhpComplianceOnMlmeIndication( MlmeIndication_t* mlmeIndication )
550 {
551     if( ComplianceTestState.Initialized == false )
552     {
553         return;
554     }
555 
556     switch( mlmeIndication->MlmeIndication )
557     {
558     case MLME_BEACON_LOST:
559     {
560         ClassBStatusReset( );
561         SendBeaconRxStatusInd( ComplianceTestState.IsBeaconRxStatusIndOn );
562         break;
563     }
564     case MLME_BEACON:
565     {
566         if( mlmeIndication->Status == LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED )
567         {
568             // As we received a beacon ensure that IsBeaconRxOn is set to true
569             if( ComplianceTestState.ClassBStatus.IsBeaconRxOn == false )
570             {
571                 ComplianceTestState.ClassBStatus.IsBeaconRxOn = true;
572             }
573             ComplianceTestState.ClassBStatus.BeaconCnt++;
574         }
575         ComplianceTestState.ClassBStatus.Info = mlmeIndication->BeaconInfo;
576         SendBeaconRxStatusInd( ComplianceTestState.IsBeaconRxStatusIndOn );
577         break;
578     }
579     default:
580         break;
581     }
582 }
583 
SendBeaconRxStatusInd(bool isBeaconRxStatusIndOn)584 static void SendBeaconRxStatusInd( bool isBeaconRxStatusIndOn )
585 {
586     if( isBeaconRxStatusIndOn == false )
587     {
588         return;
589     }
590     uint32_t frequency = ComplianceTestState.ClassBStatus.Info.Frequency / 100;
591 
592     ComplianceTestState.DataBufferSize = 0;
593     ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = COMPLIANCE_BEACON_RX_STATUS_IND;
594     ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( ComplianceTestState.ClassBStatus.IsBeaconRxOn == true ) ? 1 : 0;
595     ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.BeaconCnt );
596     ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.BeaconCnt >> 8 );
597     ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( frequency );
598     ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( frequency >> 8 );
599     ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( frequency >> 16 );
600     ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceTestState.ClassBStatus.Info.Datarate;
601     ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.Rssi );
602     ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.Rssi >> 8 );
603     ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.Snr );
604     ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.Param );
605     ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.Time.Seconds );
606     ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.Time.Seconds >> 8 );
607     ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.Time.Seconds >> 16 );
608     ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.Time.Seconds >> 24 );
609     ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.GwSpecific.InfoDesc );
610     ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.GwSpecific.Info[0] );
611     ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.GwSpecific.Info[1] );
612     ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.GwSpecific.Info[2] );
613     ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.GwSpecific.Info[3] );
614     ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.GwSpecific.Info[4] );
615     ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.GwSpecific.Info[5] );
616 
617     ComplianceTestState.IsTxPending = true;
618 }
619