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