1 /*!
2  * \file      LmHandler.c
3  *
4  * \brief     Implements the LoRaMac layer handling.
5  *            Provides the possibility to register applicative packages.
6  *
7  * \remark    Inspired by the examples provided on the en.i-cube_lrwan fork.
8  *            MCD Application Team ( STMicroelectronics International )
9  *
10  * \copyright Revised BSD License, see section \ref LICENSE.
11  *
12  * \code
13  *                ______                              _
14  *               / _____)             _              | |
15  *              ( (____  _____ ____ _| |_ _____  ____| |__
16  *               \____ \| ___ |    (_   _) ___ |/ ___)  _ \
17  *               _____) ) ____| | | || |_| ____( (___| | | |
18  *              (______/|_____)_|_|_| \__)_____)\____)_| |_|
19  *              (C)2013-2018 Semtech
20  *
21  * \endcode
22  *
23  * \author    Miguel Luis ( Semtech )
24  */
25 #include <stdlib.h>
26 #include <stdint.h>
27 #include <stdbool.h>
28 #include "utilities.h"
29 #include "timer.h"
30 #include "Commissioning.h"
31 #include "NvmDataMgmt.h"
32 #include "radio.h"
33 #include "LmHandler.h"
34 #include "LmhPackage.h"
35 #include "LmhpCompliance.h"
36 #include "LmhpClockSync.h"
37 #include "LmhpRemoteMcastSetup.h"
38 #include "LmhpFragmentation.h"
39 
40 #include "LoRaMacTest.h"
41 
42 static CommissioningParams_t CommissioningParams =
43 {
44     .IsOtaaActivation = OVER_THE_AIR_ACTIVATION,
45     .DevEui = { 0 },  // Automatically filed from secure-element
46     .JoinEui = { 0 }, // Automatically filed from secure-element
47     .SePin = { 0 },   // Automatically filed from secure-element
48     .NetworkId = LORAWAN_NETWORK_ID,
49     .DevAddr = LORAWAN_DEVICE_ADDRESS,
50 };
51 
52 static LmhPackage_t *LmHandlerPackages[PKG_MAX_NUMBER];
53 
54 /*!
55  * Upper layer LoRaMac parameters
56  */
57 static LmHandlerParams_t *LmHandlerParams;
58 
59 /*!
60  * Upper layer callbacks
61  */
62 static LmHandlerCallbacks_t *LmHandlerCallbacks;
63 
64 /*!
65  * Used to notify LmHandler of LoRaMac events
66  */
67 static LoRaMacPrimitives_t LoRaMacPrimitives;
68 
69 /*!
70  * LoRaMac callbacks
71  */
72 static LoRaMacCallback_t LoRaMacCallbacks;
73 
74 static LmHandlerJoinParams_t JoinParams =
75 {
76     .CommissioningParams = &CommissioningParams,
77     .Datarate = DR_0,
78     .Status = LORAMAC_HANDLER_ERROR
79 };
80 
81 static LmHandlerTxParams_t TxParams =
82 {
83     .CommissioningParams = &CommissioningParams,
84     .MsgType = LORAMAC_HANDLER_UNCONFIRMED_MSG,
85     .AckReceived = 0,
86     .Datarate = DR_0,
87     .UplinkCounter = 0,
88     .AppData =
89     {
90         .Port = 0,
91         .BufferSize = 0,
92         .Buffer = NULL,
93     },
94     .TxPower = TX_POWER_0,
95     .Channel = 0,
96 };
97 
98 static LmHandlerRxParams_t RxParams =
99 {
100     .CommissioningParams = &CommissioningParams,
101     .Rssi = 0,
102     .Snr = 0,
103     .DownlinkCounter = 0,
104     .RxSlot = -1,
105 };
106 
107 static LoRaMacHandlerBeaconParams_t BeaconParams =
108 {
109     .State = LORAMAC_HANDLER_BEACON_ACQUIRING,
110     .Info =
111     {
112         .Time = { .Seconds = 0, .SubSeconds = 0 },
113         .Frequency = 0,
114         .Datarate = 0,
115         .Rssi = 0,
116         .Snr = 0,
117         .GwSpecific =
118         {
119             .InfoDesc = 0,
120             .Info = { 0 },
121         },
122     },
123 };
124 
125 /*!
126  * Indicates if a switch to Class B operation is pending or not.
127  *
128  * TODO: Create a new structure to store the current handler states/status
129  *       and add the below variable to it.
130  */
131 static bool IsClassBSwitchPending = false;
132 
133 /*!
134  * Stores the time to wait before next transmission
135  *
136  * TODO: Create a new structure to store the current handler states/status
137  *       and add the below variable to it.
138  */
139 static TimerTime_t DutyCycleWaitTime = 0;
140 
141 /*!
142  * Indicates if an uplink is pending upon MAC layer request
143  *
144  * TODO: Create a new structure to store the current handler states/status
145  *       and add the below variable to it.
146  */
147 static bool IsUplinkTxPending = false;
148 
149 /*!
150  * \brief   MCPS-Confirm event function
151  *
152  * \param   [IN] mcpsConfirm - Pointer to the confirm structure,
153  *                             containing confirm attributes.
154  */
155 static void McpsConfirm( McpsConfirm_t *mcpsConfirm );
156 
157 /*!
158  * \brief   MCPS-Indication event function
159  *
160  * \param   [IN] mcpsIndication - Pointer to the indication structure,
161  *               containing indication attributes.
162  */
163 static void McpsIndication( McpsIndication_t *mcpsIndication );
164 
165 /*!
166  * \brief   MLME-Confirm event function
167  *
168  * \param   [IN] MlmeConfirm - Pointer to the confirm structure,
169  *               containing confirm attributes.
170  */
171 static void MlmeConfirm( MlmeConfirm_t *mlmeConfirm );
172 
173 /*!
174  * \brief   MLME-Indication event function
175  *
176  * \param   [IN] mlmeIndication - Pointer to the indication structure,
177  *               containing indication attributes.
178  */
179 static void MlmeIndication( MlmeIndication_t *mlmeIndication );
180 
181 /*!
182  * Calls the OnClassChange callback to indicate the new active device class
183  *
184  * \param [in] deviceClass Active device class
185  */
OnClassChangeNotify(DeviceClass_t deviceClass)186 static void OnClassChangeNotify( DeviceClass_t deviceClass )
187 {
188     if( LmHandlerCallbacks->OnClassChange != NULL )
189     {
190         LmHandlerCallbacks->OnClassChange( deviceClass );
191     }
192 }
193 
194 /*!
195  * Calls the OnBeaconStatusChange callback to indicate current beacon status
196  *
197  * \param [in] params Current beacon parameters
198  */
OnBeaconStatusChangeNotify(LoRaMacHandlerBeaconParams_t * params)199 static void OnBeaconStatusChangeNotify( LoRaMacHandlerBeaconParams_t *params )
200 {
201     if( LmHandlerCallbacks->OnBeaconStatusChange != NULL )
202     {
203         LmHandlerCallbacks->OnBeaconStatusChange( params );
204     }
205 }
206 
207 /*!
208  * Starts the beacon search
209  *
210  * \retval status Returns \ref LORAMAC_HANDLER_SET if joined else \ref LORAMAC_HANDLER_RESET
211  */
212 static LmHandlerErrorStatus_t LmHandlerBeaconReq( void );
213 
214 /*
215  *=============================================================================
216  * PACKAGES HANDLING
217  *=============================================================================
218  */
219 typedef enum PackageNotifyTypes_e
220 {
221     PACKAGE_MCPS_CONFIRM,
222     PACKAGE_MCPS_INDICATION,
223     PACKAGE_MLME_CONFIRM,
224     PACKAGE_MLME_INDICATION,
225 }PackageNotifyTypes_t;
226 
227 /*!
228  * Notifies the package to process the LoRaMac callbacks.
229  *
230  * \param [IN] notifyType MAC notification type [PACKAGE_MCPS_CONFIRM,
231  *                                               PACKAGE_MCPS_INDICATION,
232  *                                               PACKAGE_MLME_CONFIRM,
233  *                                               PACKAGE_MLME_INDICATION]
234  * \param[IN] params      Notification parameters. The params type can be
235  *                        [McpsConfirm_t, McpsIndication_t, MlmeConfirm_t, MlmeIndication_t]
236  */
237 static void LmHandlerPackagesNotify( PackageNotifyTypes_t notifyType, void *params );
238 
239 static bool LmHandlerPackageIsTxPending( void );
240 
241 static void LmHandlerPackagesProcess( void );
242 
LmHandlerInit(LmHandlerCallbacks_t * handlerCallbacks,LmHandlerParams_t * handlerParams)243 LmHandlerErrorStatus_t LmHandlerInit( LmHandlerCallbacks_t *handlerCallbacks,
244                                       LmHandlerParams_t *handlerParams )
245 {
246     //
247     uint16_t nbNvmData = 0;
248     MibRequestConfirm_t mibReq;
249     LmHandlerParams = handlerParams;
250     LmHandlerCallbacks = handlerCallbacks;
251 
252     LoRaMacPrimitives.MacMcpsConfirm = McpsConfirm;
253     LoRaMacPrimitives.MacMcpsIndication = McpsIndication;
254     LoRaMacPrimitives.MacMlmeConfirm = MlmeConfirm;
255     LoRaMacPrimitives.MacMlmeIndication = MlmeIndication;
256     LoRaMacCallbacks.GetBatteryLevel = LmHandlerCallbacks->GetBatteryLevel;
257     LoRaMacCallbacks.GetTemperatureLevel = LmHandlerCallbacks->GetTemperature;
258     LoRaMacCallbacks.NvmDataChange  = NvmDataMgmtEvent;
259     LoRaMacCallbacks.MacProcessNotify = LmHandlerCallbacks->OnMacProcess;
260 
261     IsClassBSwitchPending = false;
262     IsUplinkTxPending = false;
263 
264     if( LoRaMacInitialization( &LoRaMacPrimitives, &LoRaMacCallbacks, LmHandlerParams->Region ) != LORAMAC_STATUS_OK )
265     {
266         return LORAMAC_HANDLER_ERROR;
267     }
268 
269     // Restore data if required
270     nbNvmData = NvmDataMgmtRestore( );
271 
272     // Try to restore from NVM and query the mac if possible.
273     if( ( LmHandlerCallbacks->OnNvmDataChange != NULL ) && ( nbNvmData > 0 ) )
274     {
275         LmHandlerCallbacks->OnNvmDataChange( LORAMAC_HANDLER_NVM_RESTORE, nbNvmData );
276     }
277     else
278     {
279         // Configure the default datarate
280         mibReq.Type = MIB_CHANNELS_DEFAULT_DATARATE;
281         mibReq.Param.ChannelsDefaultDatarate = LmHandlerParams->TxDatarate;
282         LoRaMacMibSetRequestConfirm( &mibReq );
283 
284         mibReq.Type = MIB_CHANNELS_DATARATE;
285         mibReq.Param.ChannelsDatarate = LmHandlerParams->TxDatarate;
286         LoRaMacMibSetRequestConfirm( &mibReq );
287 
288 #if( OVER_THE_AIR_ACTIVATION == 0 )
289         // Tell the MAC layer which network server version are we connecting too.
290         mibReq.Type = MIB_ABP_LORAWAN_VERSION;
291         mibReq.Param.AbpLrWanVersion.Value = ABP_ACTIVATION_LRWAN_VERSION;
292         LoRaMacMibSetRequestConfirm( &mibReq );
293 
294         mibReq.Type = MIB_NET_ID;
295         mibReq.Param.NetID = LORAWAN_NETWORK_ID;
296         LoRaMacMibSetRequestConfirm( &mibReq );
297 
298 #if( STATIC_DEVICE_ADDRESS != 1 )
299         // TODO: Remove STATIC_DEVICE_ADDRESS = 0. Up to the application to decide what to do
300         // Random seed initialization
301         if( LmHandlerCallbacks->GetRandomSeed != NULL )
302         {
303             srand1( LmHandlerCallbacks->GetRandomSeed( ) );
304         }
305         // Choose a random device address
306         CommissioningParams.DevAddr = randr( 0, 0x01FFFFFF );
307 #endif
308 
309         mibReq.Type = MIB_DEV_ADDR;
310         mibReq.Param.DevAddr = CommissioningParams.DevAddr;
311         LoRaMacMibSetRequestConfirm( &mibReq );
312 #endif // #if( OVER_THE_AIR_ACTIVATION == 0 )
313     }
314 
315     // Read secure-element DEV_EUI, JOI_EUI and SE_PIN values.
316     mibReq.Type = MIB_DEV_EUI;
317     LoRaMacMibGetRequestConfirm( &mibReq );
318     memcpy1( CommissioningParams.DevEui, mibReq.Param.DevEui, 8 );
319 
320     mibReq.Type = MIB_JOIN_EUI;
321     LoRaMacMibGetRequestConfirm( &mibReq );
322     memcpy1( CommissioningParams.JoinEui, mibReq.Param.JoinEui, 8 );
323 
324     mibReq.Type = MIB_SE_PIN;
325     LoRaMacMibGetRequestConfirm( &mibReq );
326     memcpy1( CommissioningParams.SePin, mibReq.Param.SePin, 4 );
327 
328     mibReq.Type = MIB_PUBLIC_NETWORK;
329     mibReq.Param.EnablePublicNetwork = LmHandlerParams->PublicNetworkEnable;
330     LoRaMacMibSetRequestConfirm( &mibReq );
331 
332     mibReq.Type = MIB_ADR;
333     mibReq.Param.AdrEnable = LmHandlerParams->AdrEnable;
334     LoRaMacMibSetRequestConfirm( &mibReq );
335 
336     LoRaMacTestSetDutyCycleOn( LmHandlerParams->DutyCycleEnabled );
337 
338     LoRaMacStart( );
339 
340     mibReq.Type = MIB_NETWORK_ACTIVATION;
341     if( LoRaMacMibGetRequestConfirm( &mibReq ) == LORAMAC_STATUS_OK )
342     {
343         if( mibReq.Param.NetworkActivation == ACTIVATION_TYPE_NONE )
344         {
345             if( LmHandlerCallbacks->OnNetworkParametersChange != NULL )
346             {
347                 LmHandlerCallbacks->OnNetworkParametersChange( &CommissioningParams );
348             }
349         }
350     }
351     return LORAMAC_HANDLER_SUCCESS;
352 }
353 
LmHandlerIsBusy(void)354 bool LmHandlerIsBusy( void )
355 {
356     if( LoRaMacIsBusy( ) == true )
357     {
358         return true;
359     }
360     if( LmHandlerJoinStatus( ) != LORAMAC_HANDLER_SET )
361     {
362         // The network isn't yet joined, try again later.
363         LmHandlerJoin( );
364         return true;
365     }
366 
367     if( LmHandlerPackageIsTxPending( ) == true )
368     {
369         return true;
370     }
371 
372     return false;
373 }
374 
LmHandlerProcess(void)375 void LmHandlerProcess( void )
376 {
377     uint16_t size = 0;
378 
379     // Process Radio IRQ
380     if( Radio.IrqProcess != NULL )
381     {
382         Radio.IrqProcess( );
383     }
384 
385     // Processes the LoRaMac events
386     LoRaMacProcess( );
387 
388     // Store to NVM if required
389     size = NvmDataMgmtStore( );
390 
391     if( size > 0 )
392     {
393         if( LmHandlerCallbacks->OnNvmDataChange != NULL )
394         {
395             LmHandlerCallbacks->OnNvmDataChange( LORAMAC_HANDLER_NVM_STORE, size );
396         }
397     }
398 
399     // Call all packages process functions
400     LmHandlerPackagesProcess( );
401 
402     // Check if a package transmission is pending.
403     // If it is the case exit function earlier
404     if( LmHandlerPackageIsTxPending( ) == true )
405     {
406         return;
407     }
408 
409     // If a MAC layer scheduled uplink is still pending try to send it.
410     if( IsUplinkTxPending == true )
411     {
412         // Send an empty message
413         LmHandlerAppData_t appData =
414         {
415             .Buffer = NULL,
416             .BufferSize = 0,
417             .Port = 0,
418         };
419 
420         if( LmHandlerSend( &appData, LmHandlerParams->IsTxConfirmed ) == LORAMAC_HANDLER_SUCCESS )
421         {
422             IsUplinkTxPending = false;
423         }
424     }
425 }
426 
LmHandlerGetDutyCycleWaitTime(void)427 TimerTime_t LmHandlerGetDutyCycleWaitTime( void )
428 {
429     return DutyCycleWaitTime;
430 }
431 
432 /*!
433  * Join a LoRa Network in classA
434  *
435  * \Note if the device is ABP, this is a pass through function
436  *
437  * \param [IN] isOtaa Indicates which activation mode must be used
438  */
LmHandlerJoinRequest(bool isOtaa)439 static void LmHandlerJoinRequest( bool isOtaa )
440 {
441     MlmeReq_t mlmeReq;
442 
443     mlmeReq.Type = MLME_JOIN;
444     mlmeReq.Req.Join.Datarate = LmHandlerParams->TxDatarate;
445 
446     if( isOtaa == true )
447     {
448         mlmeReq.Req.Join.NetworkActivation = ACTIVATION_TYPE_OTAA;
449         // Update commissioning parameters activation type variable.
450         CommissioningParams.IsOtaaActivation = true;
451     }
452     else
453     {
454         mlmeReq.Req.Join.NetworkActivation = ACTIVATION_TYPE_ABP;
455         // Update commissioning parameters activation type variable.
456         CommissioningParams.IsOtaaActivation = false;
457     }
458     // Starts the join procedure
459     LoRaMacStatus_t status = LoRaMacMlmeRequest( &mlmeReq );
460     if( LmHandlerCallbacks->OnMacMlmeRequest != NULL )
461     {
462         LmHandlerCallbacks->OnMacMlmeRequest( status, &mlmeReq, mlmeReq.ReqReturn.DutyCycleWaitTime );
463     }
464     DutyCycleWaitTime = mlmeReq.ReqReturn.DutyCycleWaitTime;
465 }
466 
LmHandlerJoin(void)467 void LmHandlerJoin( void )
468 {
469     LmHandlerJoinRequest( CommissioningParams.IsOtaaActivation );
470 }
471 
LmHandlerJoinStatus(void)472 LmHandlerFlagStatus_t LmHandlerJoinStatus( void )
473 {
474     MibRequestConfirm_t mibReq;
475     LoRaMacStatus_t status;
476 
477     mibReq.Type = MIB_NETWORK_ACTIVATION;
478     status = LoRaMacMibGetRequestConfirm( &mibReq );
479 
480     if( status == LORAMAC_STATUS_OK )
481     {
482         if( mibReq.Param.NetworkActivation == ACTIVATION_TYPE_NONE )
483         {
484             return LORAMAC_HANDLER_RESET;
485         }
486         else
487         {
488             return LORAMAC_HANDLER_SET;
489         }
490     }
491     else
492     {
493         return LORAMAC_HANDLER_RESET;
494     }
495 }
496 
LmHandlerSend(LmHandlerAppData_t * appData,LmHandlerMsgTypes_t isTxConfirmed)497 LmHandlerErrorStatus_t LmHandlerSend( LmHandlerAppData_t *appData, LmHandlerMsgTypes_t isTxConfirmed )
498 {
499     LoRaMacStatus_t status;
500     McpsReq_t mcpsReq;
501     LoRaMacTxInfo_t txInfo;
502 
503     if( LmHandlerJoinStatus( ) != LORAMAC_HANDLER_SET )
504     {
505         // The network isn't joined, try again.
506         LmHandlerJoinRequest( CommissioningParams.IsOtaaActivation );
507         return LORAMAC_HANDLER_ERROR;
508     }
509 
510     TxParams.MsgType = isTxConfirmed;
511     mcpsReq.Type = ( isTxConfirmed == LORAMAC_HANDLER_UNCONFIRMED_MSG ) ? MCPS_UNCONFIRMED : MCPS_CONFIRMED;
512     mcpsReq.Req.Unconfirmed.Datarate = LmHandlerParams->TxDatarate;
513     if( LoRaMacQueryTxPossible( appData->BufferSize, &txInfo ) != LORAMAC_STATUS_OK )
514     {
515         // Send empty frame in order to flush MAC commands
516         mcpsReq.Type = MCPS_UNCONFIRMED;
517         mcpsReq.Req.Unconfirmed.fBuffer = NULL;
518         mcpsReq.Req.Unconfirmed.fBufferSize = 0;
519     }
520     else
521     {
522         mcpsReq.Req.Unconfirmed.fPort = appData->Port;
523         mcpsReq.Req.Unconfirmed.fBufferSize = appData->BufferSize;
524         mcpsReq.Req.Unconfirmed.fBuffer = appData->Buffer;
525     }
526 
527     TxParams.AppData = *appData;
528     TxParams.Datarate = LmHandlerParams->TxDatarate;
529 
530     status = LoRaMacMcpsRequest( &mcpsReq );
531     if( LmHandlerCallbacks->OnMacMcpsRequest != NULL )
532     {
533         LmHandlerCallbacks->OnMacMcpsRequest( status, &mcpsReq, mcpsReq.ReqReturn.DutyCycleWaitTime );
534     }
535     DutyCycleWaitTime = mcpsReq.ReqReturn.DutyCycleWaitTime;
536 
537     if( status == LORAMAC_STATUS_OK )
538     {
539         IsUplinkTxPending = false;
540         return LORAMAC_HANDLER_SUCCESS;
541     }
542     else
543     {
544         return LORAMAC_HANDLER_ERROR;
545     }
546 }
547 
LmHandlerDeviceTimeReq(void)548 LmHandlerErrorStatus_t LmHandlerDeviceTimeReq( void )
549 {
550     LoRaMacStatus_t status;
551     MlmeReq_t mlmeReq;
552 
553     mlmeReq.Type = MLME_DEVICE_TIME;
554 
555     status = LoRaMacMlmeRequest( &mlmeReq );
556     if( LmHandlerCallbacks->OnMacMlmeRequest != NULL )
557     {
558         LmHandlerCallbacks->OnMacMlmeRequest( status, &mlmeReq, mlmeReq.ReqReturn.DutyCycleWaitTime );
559     }
560     DutyCycleWaitTime = mlmeReq.ReqReturn.DutyCycleWaitTime;
561 
562     if( status == LORAMAC_STATUS_OK )
563     {
564         return LORAMAC_HANDLER_SUCCESS;
565     }
566     else
567     {
568         return LORAMAC_HANDLER_ERROR;
569     }
570 }
571 
LmHandlerBeaconReq(void)572 static LmHandlerErrorStatus_t LmHandlerBeaconReq( void )
573 {
574     LoRaMacStatus_t status;
575     MlmeReq_t mlmeReq;
576 
577     mlmeReq.Type = MLME_BEACON_ACQUISITION;
578 
579     status = LoRaMacMlmeRequest( &mlmeReq );
580     if( LmHandlerCallbacks->OnMacMlmeRequest != NULL )
581     {
582         LmHandlerCallbacks->OnMacMlmeRequest( status, &mlmeReq, mlmeReq.ReqReturn.DutyCycleWaitTime );
583     }
584     DutyCycleWaitTime = mlmeReq.ReqReturn.DutyCycleWaitTime;
585 
586     if( status == LORAMAC_STATUS_OK )
587     {
588         return LORAMAC_HANDLER_SUCCESS;
589     }
590     else
591     {
592         return LORAMAC_HANDLER_ERROR;
593     }
594 }
595 
LmHandlerPingSlotReq(uint8_t periodicity)596 LmHandlerErrorStatus_t LmHandlerPingSlotReq( uint8_t periodicity )
597 {
598     LoRaMacStatus_t status;
599     MlmeReq_t mlmeReq;
600 
601     mlmeReq.Type = MLME_PING_SLOT_INFO;
602     mlmeReq.Req.PingSlotInfo.PingSlot.Fields.Periodicity = periodicity;
603     mlmeReq.Req.PingSlotInfo.PingSlot.Fields.RFU = 0;
604 
605     status = LoRaMacMlmeRequest( &mlmeReq );
606     if( LmHandlerCallbacks->OnMacMlmeRequest != NULL )
607     {
608         LmHandlerCallbacks->OnMacMlmeRequest( status, &mlmeReq, mlmeReq.ReqReturn.DutyCycleWaitTime );
609     }
610     DutyCycleWaitTime = mlmeReq.ReqReturn.DutyCycleWaitTime;
611 
612     if( status == LORAMAC_STATUS_OK )
613     {
614         // Send an empty message
615         LmHandlerAppData_t appData =
616         {
617             .Buffer = NULL,
618             .BufferSize = 0,
619             .Port = 0,
620         };
621         return LmHandlerSend( &appData, LmHandlerParams->IsTxConfirmed );
622     }
623     else
624     {
625         return LORAMAC_HANDLER_ERROR;
626     }
627 }
628 
LmHandlerRequestClass(DeviceClass_t newClass)629 LmHandlerErrorStatus_t LmHandlerRequestClass( DeviceClass_t newClass )
630 {
631     MibRequestConfirm_t mibReq;
632     DeviceClass_t currentClass;
633     LmHandlerErrorStatus_t errorStatus = LORAMAC_HANDLER_SUCCESS;
634 
635     mibReq.Type = MIB_DEVICE_CLASS;
636     LoRaMacMibGetRequestConfirm( &mibReq );
637     currentClass = mibReq.Param.Class;
638 
639     // Attempt to switch only if class update
640     if( currentClass != newClass )
641     {
642         switch( newClass )
643         {
644         case CLASS_A:
645             {
646                 if( currentClass != CLASS_A )
647                 {
648                     mibReq.Param.Class = CLASS_A;
649                     if( LoRaMacMibSetRequestConfirm( &mibReq ) == LORAMAC_STATUS_OK )
650                     {
651                         // Switch is instantaneous
652                         OnClassChangeNotify( CLASS_A );
653                     }
654                     else
655                     {
656                         errorStatus = LORAMAC_HANDLER_ERROR;
657                     }
658                 }
659             }
660             break;
661         case CLASS_B:
662             {
663                 if( currentClass != CLASS_A )
664                 {
665                     errorStatus = LORAMAC_HANDLER_ERROR;
666                 }
667                 // Beacon must first be acquired
668                 errorStatus = LmHandlerDeviceTimeReq( );
669                 IsClassBSwitchPending = true;
670             }
671             break;
672         case CLASS_C:
673             {
674                 if( currentClass != CLASS_A )
675                 {
676                     errorStatus = LORAMAC_HANDLER_ERROR;
677                 }
678                 // Switch is instantaneous
679                 mibReq.Param.Class = CLASS_C;
680                 if( LoRaMacMibSetRequestConfirm( &mibReq ) == LORAMAC_STATUS_OK )
681                 {
682                     OnClassChangeNotify( CLASS_C );
683                 }
684                 else
685                 {
686                     errorStatus = LORAMAC_HANDLER_ERROR;
687                 }
688             }
689             break;
690         default:
691             break;
692         }
693     }
694     return errorStatus;
695 }
696 
LmHandlerGetCurrentClass(void)697 DeviceClass_t LmHandlerGetCurrentClass( void )
698 {
699     MibRequestConfirm_t mibReq;
700 
701     mibReq.Type = MIB_DEVICE_CLASS;
702     LoRaMacMibGetRequestConfirm( &mibReq );
703 
704     return mibReq.Param.Class;
705 }
706 
LmHandlerGetCurrentDatarate(void)707 int8_t LmHandlerGetCurrentDatarate( void )
708 {
709     MibRequestConfirm_t mibGet;
710 
711     mibGet.Type = MIB_CHANNELS_DATARATE;
712     LoRaMacMibGetRequestConfirm( &mibGet );
713 
714     return mibGet.Param.ChannelsDatarate;
715 }
716 
LmHandlerGetActiveRegion(void)717 LoRaMacRegion_t LmHandlerGetActiveRegion( void )
718 {
719     return LmHandlerParams->Region;
720 }
721 
LmHandlerSetSystemMaxRxError(uint32_t maxErrorInMs)722 LmHandlerErrorStatus_t LmHandlerSetSystemMaxRxError( uint32_t maxErrorInMs )
723 {
724     MibRequestConfirm_t mibReq;
725 
726     mibReq.Type = MIB_SYSTEM_MAX_RX_ERROR;
727     mibReq.Param.SystemMaxRxError = maxErrorInMs;
728     if( LoRaMacMibSetRequestConfirm( &mibReq ) != LORAMAC_STATUS_OK )
729     {
730         return LORAMAC_HANDLER_ERROR;
731     }
732     return LORAMAC_HANDLER_SUCCESS;
733 }
734 
735 /*
736  *=============================================================================
737  * LORAMAC NOTIFICATIONS HANDLING
738  *=============================================================================
739  */
740 
McpsConfirm(McpsConfirm_t * mcpsConfirm)741 static void McpsConfirm( McpsConfirm_t *mcpsConfirm )
742 {
743     TxParams.IsMcpsConfirm = 1;
744     TxParams.Status = mcpsConfirm->Status;
745     TxParams.Datarate = mcpsConfirm->Datarate;
746     TxParams.UplinkCounter = mcpsConfirm->UpLinkCounter;
747     TxParams.TxPower = mcpsConfirm->TxPower;
748     TxParams.Channel = mcpsConfirm->Channel;
749     TxParams.AckReceived = mcpsConfirm->AckReceived;
750 
751     if( LmHandlerCallbacks->OnTxData != NULL )
752     {
753         LmHandlerCallbacks->OnTxData( &TxParams );
754     }
755 
756     LmHandlerPackagesNotify( PACKAGE_MCPS_CONFIRM, mcpsConfirm );
757 }
758 
McpsIndication(McpsIndication_t * mcpsIndication)759 static void McpsIndication( McpsIndication_t *mcpsIndication )
760 {
761     LmHandlerAppData_t appData;
762 
763     RxParams.IsMcpsIndication = 1;
764     RxParams.Status = mcpsIndication->Status;
765 
766     if( RxParams.Status != LORAMAC_EVENT_INFO_STATUS_OK )
767     {
768         return;
769     }
770 
771     RxParams.Datarate = mcpsIndication->RxDatarate;
772     RxParams.Rssi = mcpsIndication->Rssi;
773     RxParams.Snr = mcpsIndication->Snr;
774     RxParams.DownlinkCounter = mcpsIndication->DownLinkCounter;
775     RxParams.RxSlot = mcpsIndication->RxSlot;
776 
777     appData.Port = mcpsIndication->Port;
778     appData.BufferSize = mcpsIndication->BufferSize;
779     appData.Buffer = mcpsIndication->Buffer;
780 
781     if( LmHandlerCallbacks->OnRxData != NULL )
782     {
783         LmHandlerCallbacks->OnRxData( &appData, &RxParams );
784     }
785 
786     if( mcpsIndication->DeviceTimeAnsReceived == true )
787     {
788         if( LmHandlerCallbacks->OnSysTimeUpdate != NULL )
789         {
790 #if( LMH_SYS_TIME_UPDATE_NEW_API == 1 )
791             // Provide fix values. DeviceTimeAns is accurate
792             LmHandlerCallbacks->OnSysTimeUpdate( true, 0 );
793 #else
794             LmHandlerCallbacks->OnSysTimeUpdate( );
795 #endif
796         }
797     }
798     // Call packages RxProcess function
799     LmHandlerPackagesNotify( PACKAGE_MCPS_INDICATION, mcpsIndication );
800 
801     if( mcpsIndication->IsUplinkTxPending != 0 )
802     {
803         // The server signals that it has pending data to be sent.
804         // We schedule an uplink as soon as possible to flush the server.
805         IsUplinkTxPending = true;
806     }
807 }
808 
MlmeConfirm(MlmeConfirm_t * mlmeConfirm)809 static void MlmeConfirm( MlmeConfirm_t *mlmeConfirm )
810 {
811     TxParams.IsMcpsConfirm = 0;
812     TxParams.Status = mlmeConfirm->Status;
813     if( LmHandlerCallbacks->OnTxData != NULL )
814     {
815         LmHandlerCallbacks->OnTxData( &TxParams );
816     }
817 
818     LmHandlerPackagesNotify( PACKAGE_MLME_CONFIRM, mlmeConfirm );
819 
820     switch( mlmeConfirm->MlmeRequest )
821     {
822     case MLME_JOIN:
823         {
824             MibRequestConfirm_t mibReq;
825             mibReq.Type = MIB_DEV_ADDR;
826             LoRaMacMibGetRequestConfirm( &mibReq );
827             JoinParams.CommissioningParams->DevAddr = mibReq.Param.DevAddr;
828             JoinParams.Datarate = LmHandlerGetCurrentDatarate( );
829 
830             if( mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK )
831             {
832                 // Status is OK, node has joined the network
833                 JoinParams.Status = LORAMAC_HANDLER_SUCCESS;
834             }
835             else
836             {
837                 // Join was not successful. Try to join again
838                 JoinParams.Status = LORAMAC_HANDLER_ERROR;
839             }
840             // Notify upper layer
841             if( LmHandlerCallbacks->OnJoinRequest != NULL )
842             {
843                 LmHandlerCallbacks->OnJoinRequest( &JoinParams );
844             }
845         }
846         break;
847     case MLME_LINK_CHECK:
848         {
849             // Check DemodMargin
850             // Check NbGateways
851         }
852         break;
853     case MLME_DEVICE_TIME:
854         {
855             if( IsClassBSwitchPending == true )
856             {
857                 LmHandlerBeaconReq( );
858             }
859         }
860         break;
861     case MLME_BEACON_ACQUISITION:
862         {
863             if( mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK )
864             {
865                 // Beacon has been acquired
866                 // Request server for ping slot
867                 LmHandlerPingSlotReq( LmHandlerParams->PingSlotPeriodicity );
868             }
869             else
870             {
871                 // Beacon not acquired
872                 // Request Device Time again.
873                 LmHandlerDeviceTimeReq( );
874             }
875         }
876         break;
877     case MLME_PING_SLOT_INFO:
878         {
879             if( mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK )
880             {
881                 MibRequestConfirm_t mibReq;
882 
883                 // Class B is now activated
884                 mibReq.Type = MIB_DEVICE_CLASS;
885                 mibReq.Param.Class = CLASS_B;
886                 LoRaMacMibSetRequestConfirm( &mibReq );
887                 // Notify upper layer
888                 OnClassChangeNotify( CLASS_B );
889                 IsClassBSwitchPending = false;
890             }
891             else
892             {
893                 LmHandlerPingSlotReq( LmHandlerParams->PingSlotPeriodicity );
894             }
895         }
896         break;
897     default:
898         break;
899     }
900 }
901 
MlmeIndication(MlmeIndication_t * mlmeIndication)902 static void MlmeIndication( MlmeIndication_t *mlmeIndication )
903 {
904     RxParams.IsMcpsIndication = 0;
905     RxParams.Status = mlmeIndication->Status;
906     if( RxParams.Status != LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED )
907     {
908         if( LmHandlerCallbacks->OnRxData != NULL )
909         {
910             LmHandlerCallbacks->OnRxData( NULL, &RxParams );
911         }
912     }
913 
914     // Call packages RxProcess function
915     LmHandlerPackagesNotify( PACKAGE_MLME_INDICATION, mlmeIndication );
916 
917     switch( mlmeIndication->MlmeIndication )
918     {
919     case MLME_BEACON_LOST:
920         {
921             MibRequestConfirm_t mibReq;
922             // Switch to class A again
923             mibReq.Type = MIB_DEVICE_CLASS;
924             mibReq.Param.Class = CLASS_A;
925             LoRaMacMibSetRequestConfirm( &mibReq );
926 
927             BeaconParams.State = LORAMAC_HANDLER_BEACON_LOST;
928             BeaconParams.Info.Time.Seconds = 0;
929             BeaconParams.Info.GwSpecific.InfoDesc = 0;
930             memset1( BeaconParams.Info.GwSpecific.Info, 0, 6 );
931 
932             OnClassChangeNotify( CLASS_A );
933             OnBeaconStatusChangeNotify( &BeaconParams );
934 
935             LmHandlerDeviceTimeReq( );
936         }
937         break;
938     case MLME_BEACON:
939     {
940         if( mlmeIndication->Status == LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED )
941         {
942             BeaconParams.State = LORAMAC_HANDLER_BEACON_RX;
943             BeaconParams.Info = mlmeIndication->BeaconInfo;
944 
945             OnBeaconStatusChangeNotify( &BeaconParams );
946         }
947         else
948         {
949             BeaconParams.State = LORAMAC_HANDLER_BEACON_NRX;
950             BeaconParams.Info = mlmeIndication->BeaconInfo;
951 
952             OnBeaconStatusChangeNotify( &BeaconParams );
953         }
954         break;
955     }
956     default:
957         break;
958     }
959 }
960 
961 /*
962  *=============================================================================
963  * PACKAGES HANDLING
964  *=============================================================================
965  */
966 
LmHandlerPackageRegister(uint8_t id,void * params)967 LmHandlerErrorStatus_t LmHandlerPackageRegister( uint8_t id, void *params )
968 {
969     LmhPackage_t *package = NULL;
970     switch( id )
971     {
972         case PACKAGE_ID_COMPLIANCE:
973         {
974             package = LmphCompliancePackageFactory( );
975             break;
976         }
977         case PACKAGE_ID_CLOCK_SYNC:
978         {
979             package = LmphClockSyncPackageFactory( );
980             break;
981         }
982         case PACKAGE_ID_REMOTE_MCAST_SETUP:
983         {
984             package = LmhpRemoteMcastSetupPackageFactory( );
985             break;
986         }
987         case PACKAGE_ID_FRAGMENTATION:
988         {
989             package = LmhpFragmentationPackageFactory( );
990             break;
991         }
992     }
993     if( package != NULL )
994     {
995         LmHandlerPackages[id] = package;
996         LmHandlerPackages[id]->OnMacMcpsRequest = LmHandlerCallbacks->OnMacMcpsRequest;
997         LmHandlerPackages[id]->OnMacMlmeRequest = LmHandlerCallbacks->OnMacMlmeRequest;
998         LmHandlerPackages[id]->OnJoinRequest = LmHandlerJoinRequest;
999         LmHandlerPackages[id]->OnDeviceTimeRequest = LmHandlerDeviceTimeReq;
1000         LmHandlerPackages[id]->OnSysTimeUpdate = LmHandlerCallbacks->OnSysTimeUpdate;
1001         LmHandlerPackages[id]->Init( params, LmHandlerParams->DataBuffer, LmHandlerParams->DataBufferMaxSize );
1002 
1003         return LORAMAC_HANDLER_SUCCESS;
1004     }
1005     else
1006     {
1007         return LORAMAC_HANDLER_ERROR;
1008     }
1009 }
1010 
LmHandlerPackageIsInitialized(uint8_t id)1011 bool LmHandlerPackageIsInitialized( uint8_t id )
1012 {
1013     if( LmHandlerPackages[id]->IsInitialized != NULL )
1014     {
1015         return LmHandlerPackages[id]->IsInitialized( );
1016     }
1017     else
1018     {
1019         return false;
1020     }
1021 }
1022 
LmHandlerPackagesNotify(PackageNotifyTypes_t notifyType,void * params)1023 static void LmHandlerPackagesNotify( PackageNotifyTypes_t notifyType, void *params )
1024 {
1025     for( int8_t i = 0; i < PKG_MAX_NUMBER; i++ )
1026     {
1027         if( LmHandlerPackages[i] != NULL )
1028         {
1029             switch( notifyType )
1030             {
1031                 case PACKAGE_MCPS_CONFIRM:
1032                 {
1033                     if( LmHandlerPackages[i]->OnMcpsConfirmProcess != NULL )
1034                     {
1035                         LmHandlerPackages[i]->OnMcpsConfirmProcess( ( McpsConfirm_t* ) params );
1036                     }
1037                     break;
1038                 }
1039                 case PACKAGE_MCPS_INDICATION:
1040                 {
1041                     if( LmHandlerPackages[i]->OnMcpsIndicationProcess != NULL )
1042                     {
1043                         LmHandlerPackages[i]->OnMcpsIndicationProcess( ( McpsIndication_t* )params );
1044                     }
1045                     break;
1046                 }
1047                 case PACKAGE_MLME_CONFIRM:
1048                 {
1049                     if( LmHandlerPackages[i]->OnMlmeConfirmProcess != NULL )
1050                     {
1051                         LmHandlerPackages[i]->OnMlmeConfirmProcess( ( MlmeConfirm_t* )params );
1052                     }
1053                     break;
1054                 }
1055                 case PACKAGE_MLME_INDICATION:
1056                 {
1057                     if( LmHandlerPackages[i]->OnMlmeIndicationProcess != NULL )
1058                     {
1059                         LmHandlerPackages[i]->OnMlmeIndicationProcess( params );
1060                     }
1061                     break;
1062                 }
1063             }
1064         }
1065     }
1066 }
1067 
LmHandlerPackageIsTxPending(void)1068 static bool LmHandlerPackageIsTxPending( void )
1069 {
1070     for( int8_t i = 0; i < PKG_MAX_NUMBER; i++ )
1071     {
1072         if( LmHandlerPackages[i] != NULL )
1073         {
1074             if( LmHandlerPackages[i]->IsTxPending( ) == true )
1075             {
1076                 return true;
1077             }
1078         }
1079     }
1080     return false;
1081 }
1082 
LmHandlerPackagesProcess(void)1083 static void LmHandlerPackagesProcess( void )
1084 {
1085     for( int8_t i = 0; i < PKG_MAX_NUMBER; i++ )
1086     {
1087         if( ( LmHandlerPackages[i] != NULL ) &&
1088             ( LmHandlerPackages[i]->Process != NULL ) &&
1089             ( LmHandlerPackageIsInitialized( i ) != false ) )
1090         {
1091             LmHandlerPackages[i]->Process( );
1092         }
1093     }
1094 }
1095