1 /*!
2  * \file      LoRaMac.c
3  *
4  * \brief     LoRa MAC layer implementation
5  *
6  * \copyright Revised BSD License, see section \ref LICENSE.
7  *
8  * \code
9  *                ______                              _
10  *               / _____)             _              | |
11  *              ( (____  _____ ____ _| |_ _____  ____| |__
12  *               \____ \| ___ |    (_   _) ___ |/ ___)  _ \
13  *               _____) ) ____| | | || |_| ____( (___| | | |
14  *              (______/|_____)_|_|_| \__)_____)\____)_| |_|
15  *              (C)2013-2017 Semtech
16  *
17  *               ___ _____ _   ___ _  _____ ___  ___  ___ ___
18  *              / __|_   _/_\ / __| |/ / __/ _ \| _ \/ __| __|
19  *              \__ \ | |/ _ \ (__| ' <| _| (_) |   / (__| _|
20  *              |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
21  *              embedded.connectivity.solutions===============
22  *
23  * \endcode
24  *
25  * \author    Miguel Luis ( Semtech )
26  *
27  * \author    Gregory Cristian ( Semtech )
28  *
29  * \author    Daniel Jaeckle ( STACKFORCE )
30  *
31  * \author    Johannes Bruder ( STACKFORCE )
32  */
33 #include "utilities.h"
34 #include "region/Region.h"
35 #include "LoRaMacClassB.h"
36 #include "LoRaMacCrypto.h"
37 #include "secure-element.h"
38 #include "LoRaMacTest.h"
39 #include "LoRaMacTypes.h"
40 #include "LoRaMacConfirmQueue.h"
41 #include "LoRaMacHeaderTypes.h"
42 #include "LoRaMacMessageTypes.h"
43 #include "LoRaMacParser.h"
44 #include "LoRaMacCommands.h"
45 #include "LoRaMacAdr.h"
46 #include "LoRaMacSerializer.h"
47 #include "radio.h"
48 
49 #include "LoRaMac.h"
50 
51 #ifndef LORAMAC_VERSION
52 /*!
53  * LoRaWAN version definition.
54  */
55 #define LORAMAC_VERSION                             0x01010100
56 #endif
57 
58 /*!
59  * LoRaWAN fallback version definition.
60  */
61 #define LORAMAC_FALLBACK_VERSION                    0x01000400
62 
63 /*!
64  * Maximum PHY layer payload size
65  */
66 #define LORAMAC_PHY_MAXPAYLOAD                      255
67 
68 /*!
69  * Maximum length of the fOpts field
70  */
71 #define LORA_MAC_COMMAND_MAX_FOPTS_LENGTH           15
72 
73 /*!
74  * LoRaMac duty cycle for the back-off procedure during the first hour.
75  */
76 #define BACKOFF_DC_1_HOUR                           100
77 
78 /*!
79  * LoRaMac duty cycle for the back-off procedure during the next 10 hours.
80  */
81 #define BACKOFF_DC_10_HOURS                         1000
82 
83 /*!
84  * LoRaMac duty cycle for the back-off procedure during the next 24 hours.
85  */
86 #define BACKOFF_DC_24_HOURS                         10000
87 
88 /*!
89  * Maximum value for the ADR ack counter
90  */
91 #define ADR_ACK_COUNTER_MAX                         0xFFFFFFFF
92 
93 /*!
94  * LoRaMac internal states
95  */
96 enum eLoRaMacState
97 {
98     LORAMAC_IDLE          = 0x00000000,
99     LORAMAC_STOPPED       = 0x00000001,
100     LORAMAC_TX_RUNNING    = 0x00000002,
101     LORAMAC_RX            = 0x00000004,
102     LORAMAC_ACK_RETRY     = 0x00000010,
103     LORAMAC_TX_DELAYED    = 0x00000020,
104     LORAMAC_TX_CONFIG     = 0x00000040,
105     LORAMAC_RX_ABORT      = 0x00000080,
106 };
107 
108 /*
109  * Request permission state
110  */
111 typedef enum eLoRaMacRequestHandling
112 {
113     LORAMAC_REQUEST_HANDLING_OFF = 0,
114     LORAMAC_REQUEST_HANDLING_ON = !LORAMAC_REQUEST_HANDLING_OFF
115 }LoRaMacRequestHandling_t;
116 
117 typedef struct sLoRaMacCtx
118 {
119     /*
120     * Length of packet in PktBuffer
121     */
122     uint16_t PktBufferLen;
123     /*
124     * Buffer containing the data to be sent or received.
125     */
126     uint8_t PktBuffer[LORAMAC_PHY_MAXPAYLOAD];
127     /*!
128     * Current processed transmit message
129     */
130     LoRaMacMessage_t TxMsg;
131     /*!
132     * Buffer containing the data received by the application.
133     */
134     uint8_t AppData[LORAMAC_PHY_MAXPAYLOAD];
135     /*
136     * Size of buffer containing the application data.
137     */
138     uint8_t AppDataSize;
139     /*
140     * Buffer containing the upper layer data.
141     */
142     uint8_t RxPayload[LORAMAC_PHY_MAXPAYLOAD];
143     SysTime_t LastTxSysTime;
144     /*
145     * LoRaMac internal state
146     */
147     uint32_t MacState;
148     /*
149     * LoRaMac upper layer event functions
150     */
151     LoRaMacPrimitives_t* MacPrimitives;
152     /*
153     * LoRaMac upper layer callback functions
154     */
155     LoRaMacCallback_t* MacCallbacks;
156     /*
157     * Radio events function pointer
158     */
159     RadioEvents_t RadioEvents;
160     /*
161     * LoRaMac duty cycle delayed Tx timer
162     */
163     TimerEvent_t TxDelayedTimer;
164     /*
165     * LoRaMac reception windows timers
166     */
167     TimerEvent_t RxWindowTimer1;
168     TimerEvent_t RxWindowTimer2;
169     /*
170     * LoRaMac reception windows delay
171     * \remark normal frame: RxWindowXDelay = ReceiveDelayX - RADIO_WAKEUP_TIME
172     *         join frame  : RxWindowXDelay = JoinAcceptDelayX - RADIO_WAKEUP_TIME
173     */
174     uint32_t RxWindow1Delay;
175     uint32_t RxWindow2Delay;
176     /*
177     * LoRaMac Rx windows configuration
178     */
179     RxConfigParams_t RxWindow1Config;
180     RxConfigParams_t RxWindow2Config;
181     RxConfigParams_t RxWindowCConfig;
182     /*
183      * Limit of uplinks without any donwlink response before the ADRACKReq bit will be set.
184      */
185     uint16_t AdrAckLimit;
186     /*
187      * Limit of uplinks without any donwlink response after a the first frame with set ADRACKReq bit
188      * before the trying to regain the connectivity.
189      */
190     uint16_t AdrAckDelay;
191     /*
192     * Acknowledge timeout timer. Used for packet retransmissions.
193     */
194     TimerEvent_t RetransmitTimeoutTimer;
195     /*
196      * Uplink messages repetitions counter
197      */
198     uint8_t ChannelsNbTransCounter;
199     /*
200      * Indicates if the AckTimeout timer has expired or not
201      */
202     bool RetransmitTimeoutRetry;
203     /*
204      * If the node has sent a FRAME_TYPE_DATA_CONFIRMED_UP this variable indicates
205      * if the nodes needs to manage the server acknowledgement.
206      */
207     bool NodeAckRequested;
208     /*
209      * Current channel index
210      */
211     uint8_t Channel;
212     /*
213     * Last transmission time on air
214     */
215     TimerTime_t TxTimeOnAir;
216     /*
217     * Structure to hold an MCPS indication data.
218     */
219     McpsIndication_t McpsIndication;
220     /*
221     * Structure to hold MCPS confirm data.
222     */
223     McpsConfirm_t McpsConfirm;
224     /*
225     * Structure to hold MLME confirm data.
226     */
227     MlmeConfirm_t MlmeConfirm;
228     /*
229     * Structure to hold MLME indication data.
230     */
231     MlmeIndication_t MlmeIndication;
232     /*
233     * Holds the current rx window slot
234     */
235     LoRaMacRxSlot_t RxSlot;
236     /*
237     * LoRaMac tx/rx operation state
238     */
239     LoRaMacFlags_t MacFlags;
240     /*
241     * Data structure indicating if a request is allowed or not.
242     */
243     LoRaMacRequestHandling_t AllowRequests;
244     /*
245     * Cycle timer for Type 0 Rejoin requests
246     */
247     TimerEvent_t Rejoin0CycleTimer;
248     /*
249     * Cycle timer for Type 1 Rejoin requests
250     */
251     TimerEvent_t Rejoin1CycleTimer;
252     /*
253     * Cycle timer for Type 2 Rejoin requests
254     */
255     TimerEvent_t Rejoin2CycleTimer;
256     /*
257     * Cycle timer for Rejoin requests trigged by ForceRejoinReq MAC command
258     */
259     TimerEvent_t ForceRejoinReqCycleTimer;
260     /*
261     * Time of Type 0 Rejoin requests cycles
262     */
263     TimerTime_t Rejoin0CycleTime;
264     /*
265     * Time of Type 1 Rejoin requests cycles
266     */
267     TimerTime_t Rejoin1CycleTime;
268     /*
269     * Time of Type 2 Rejoin requests cycles
270     */
271     TimerTime_t Rejoin2CycleTime;
272     /*
273     * Duty cycle wait time
274     */
275     TimerTime_t DutyCycleWaitTime;
276     /*
277      * Start time of the response timeout
278      */
279     TimerTime_t ResponseTimeoutStartTime;
280     /*
281      * Buffer containing the MAC layer commands
282      */
283     uint8_t MacCommandsBuffer[LORA_MAC_COMMAND_MAX_LENGTH];
284 }LoRaMacCtx_t;
285 
286 /*
287  * Module context.
288  */
289 static LoRaMacCtx_t MacCtx;
290 
291 static LoRaMacNvmData_t Nvm;
292 
293 static Band_t RegionBands[REGION_NVM_MAX_NB_BANDS];
294 
295 /*!
296  * Defines the LoRaMac radio events status
297  */
298 typedef union uLoRaMacRadioEvents
299 {
300     uint32_t Value;
301     struct sEvents
302     {
303         uint32_t RxProcessPending : 1;
304         uint32_t RxTimeout        : 1;
305         uint32_t RxError          : 1;
306         uint32_t TxTimeout        : 1;
307         uint32_t RxDone           : 1;
308         uint32_t TxDone           : 1;
309     }Events;
310 }LoRaMacRadioEvents_t;
311 
312 /*!
313  * LoRaMac radio events status
314  */
315 LoRaMacRadioEvents_t LoRaMacRadioEvents = { .Value = 0 };
316 
317 /*!
318  * \brief Function to be executed on Radio Tx Done event
319  */
320 static void OnRadioTxDone( void );
321 
322 /*!
323  * \brief This function prepares the MAC to abort the execution of function
324  *        OnRadioRxDone in case of a reception error.
325  */
326 static void PrepareRxDoneAbort( void );
327 
328 /*!
329  * \brief Function to be executed on Radio Rx Done event
330  */
331 static void OnRadioRxDone( uint8_t* payload, uint16_t size, int16_t rssi, int8_t snr );
332 
333 /*!
334  * \brief Function executed on Radio Tx Timeout event
335  */
336 static void OnRadioTxTimeout( void );
337 
338 /*!
339  * \brief Function executed on Radio Rx error event
340  */
341 static void OnRadioRxError( void );
342 
343 /*!
344  * \brief Function executed on Radio Rx Timeout event
345  */
346 static void OnRadioRxTimeout( void );
347 
348 /*!
349  * \brief Function executed on duty cycle delayed Tx  timer event
350  */
351 static void OnTxDelayedTimerEvent( void* context );
352 
353 /*!
354  * \brief Function executed on first Rx window timer event
355  */
356 static void OnRxWindow1TimerEvent( void* context );
357 
358 /*!
359  * \brief Function executed on second Rx window timer event
360  */
361 static void OnRxWindow2TimerEvent( void* context );
362 
363 /*!
364  * \brief Function executed on Rejoin Type 0 cycle timer event
365  */
366 static void OnRejoin0CycleTimerEvent( void* context );
367 
368 /*!
369  * \brief Function executed on Rejoin Type 0 cycle timer event
370  */
371 static void OnRejoin1CycleTimerEvent( void* context );
372 
373 /*!
374  * \brief Function executed on Rejoin Type 0 cycle timer event
375  */
376 static void OnRejoin2CycleTimerEvent( void* context );
377 
378 /*!
379  * \brief Function executed on Rejoin Type 0 or 2 cycle timer event
380  *        which was requested by a ForceRejoinReq MAC command.
381  */
382 static void OnForceRejoinReqCycleTimerEvent( void* context );
383 
384 /*!
385  * \brief Function executed on AckTimeout timer event
386  */
387 static void OnRetransmitTimeoutTimerEvent( void* context );
388 
389 /*!
390  * Computes next 32 bit downlink counter value and determines the frame counter ID.
391  *
392  * \param[IN]     addrID                - Address identifier
393  * \param[IN]     fType                 - Frame type
394  * \param[IN]     macMsg                - Data message object, holding the current 16 bit transmitted frame counter
395  * \param[IN]     lrWanVersion          - LoRaWAN version
396  * \param[OUT]    fCntID                - Frame counter identifier
397  * \param[OUT]    currentDown           - Current downlink counter value
398  *
399  * \retval                              - Status of the operation
400  */
401 static LoRaMacCryptoStatus_t GetFCntDown( AddressIdentifier_t addrID, FType_t fType, LoRaMacMessageData_t* macMsg, Version_t lrWanVersion,
402                                           FCntIdentifier_t* fCntID, uint32_t* currentDown );
403 
404 /*!
405  * \brief Switches the device class
406  *
407  * \param [IN] deviceClass Device class to switch to
408  */
409 static LoRaMacStatus_t SwitchClass( DeviceClass_t deviceClass );
410 
411 /*!
412  * \brief Gets the maximum application payload length in the absence of the optional FOpt field.
413  *
414  * \param [IN] datarate        Current datarate
415  *
416  * \retval                    Max length
417  */
418 static uint8_t GetMaxAppPayloadWithoutFOptsLength( int8_t datarate );
419 
420 /*!
421  * \brief Validates if the payload fits into the frame, taking the datarate
422  *        into account.
423  *
424  * \details Refer to chapter 4.3.2 of the LoRaWAN specification, v1.0
425  *
426  * \param lenN Length of the application payload. The length depends on the
427  *             datarate and is region specific
428  *
429  * \param datarate Current datarate
430  *
431  * \param fOptsLen Length of the fOpts field
432  *
433  * \retval [false: payload does not fit into the frame, true: payload fits into
434  *          the frame]
435  */
436 static bool ValidatePayloadLength( uint8_t lenN, int8_t datarate, uint8_t fOptsLen );
437 
438 /*!
439  * \brief Decodes MAC commands in the fOpts field and in the payload
440  *
441  * \param [IN] payload      A pointer to the payload
442  * \param [IN] macIndex     The index of the payload where the MAC commands start
443  * \param [IN] commandsSize The size of the MAC commands
444  * \param [IN] snr          The SNR value  of the frame
445  * \param [IN] rxSlot       The RX slot where the frame was received
446  */
447 static void ProcessMacCommands( uint8_t* payload, uint8_t macIndex, uint8_t commandsSize, int8_t snr, LoRaMacRxSlot_t rxSlot );
448 
449 /*!
450  * \brief LoRaMAC layer generic send frame
451  *
452  * \param [IN] macHdr      MAC header field
453  * \param [IN] fPort       MAC payload port
454  * \param [IN] fBuffer     MAC data buffer to be sent
455  * \param [IN] fBufferSize MAC data buffer size
456  * \retval status          Status of the operation.
457  */
458 LoRaMacStatus_t Send( LoRaMacHeader_t* macHdr, uint8_t fPort, void* fBuffer, uint16_t fBufferSize );
459 
460 /*!
461  * \brief LoRaMAC layer send join/rejoin request
462  *
463  * \param [IN] joinReqType Type of join-request or rejoin
464  *
465  * \retval status          Status of the operation.
466  */
467 LoRaMacStatus_t SendReJoinReq( JoinReqIdentifier_t joinReqType );
468 
469 /*!
470  * \brief LoRaMAC layer frame buffer initialization
471  *
472  * \param [IN] macHdr      MAC header field
473  * \param [IN] fCtrl       MAC frame control field
474  * \param [IN] fOpts       MAC commands buffer
475  * \param [IN] fPort       MAC payload port
476  * \param [IN] fBuffer     MAC data buffer to be sent
477  * \param [IN] fBufferSize MAC data buffer size
478  * \retval status          Status of the operation.
479  */
480 LoRaMacStatus_t PrepareFrame( LoRaMacHeader_t* macHdr, LoRaMacFrameCtrl_t* fCtrl, uint8_t fPort, void* fBuffer, uint16_t fBufferSize );
481 
482 /*
483  * \brief Schedules the frame according to the duty cycle
484  *
485  * \param [IN] allowDelayedTx When set to true, the a frame will be delayed,
486  *                            the duty cycle restriction is active
487  * \retval Status of the operation
488  */
489 static LoRaMacStatus_t ScheduleTx( bool allowDelayedTx );
490 
491 /*
492  * \brief Secures the current processed frame ( TxMsg )
493  * \param[IN]     txDr      Data rate used for the transmission
494  * \param[IN]     txCh      Index of the channel used for the transmission
495  * \retval status           Status of the operation
496  */
497 static LoRaMacStatus_t SecureFrame( uint8_t txDr, uint8_t txCh );
498 
499 /*
500  * \brief Calculates the aggregated back off time.
501  */
502 static void CalculateBackOff( void );
503 
504 /*
505  * \brief Function to remove pending MAC commands
506  *
507  * \param [IN] rxSlot     The RX slot on which the frame was received
508  * \param [IN] fCtrl      The frame control field of the received frame
509  * \param [IN] request    The request type
510  */
511 static void RemoveMacCommands( LoRaMacRxSlot_t rxSlot, LoRaMacFrameCtrl_t fCtrl, Mcps_t request );
512 
513 /*!
514  * \brief LoRaMAC layer prepared frame buffer transmission with channel specification
515  *
516  * \remark PrepareFrame must be called at least once before calling this
517  *         function.
518  *
519  * \param [IN] channel     Channel to transmit on
520  * \retval status          Status of the operation.
521  */
522 LoRaMacStatus_t SendFrameOnChannel( uint8_t channel );
523 
524 /*!
525  * \brief Sets the radio in continuous transmission mode
526  *
527  * \remark Uses the radio parameters set on the previous transmission.
528  *
529  * \param [IN] timeout     Time in seconds while the radio is kept in continuous wave mode
530  * \param [IN] frequency   RF frequency to be set.
531  * \param [IN] power       RF output power to be set.
532  * \retval status          Status of the operation.
533  */
534 LoRaMacStatus_t SetTxContinuousWave( uint16_t timeout, uint32_t frequency, uint8_t power );
535 
536 /*!
537  * \brief Converts a second based Rejoin Cycle base in the from timer module required format.
538  *
539  * \param [IN]  rejoinCycleTime     The time in second
540  * \param [out] timeInMiliSec       The time in second
541  * \retval status          Status of the operation.
542  */
543 static bool ConvertRejoinCycleTime( uint32_t rejoinCycleTime, uint32_t* timeInMiliSec );
544 
545 /*!
546  * \brief Resets MAC specific parameters to default
547  */
548 static void ResetMacParameters( void );
549 
550 /*!
551  * \brief Checks if it's required to send a Rejoin (Type 0) request.
552  *
553  * \retval [false: Rejoin not required, true: Rejoin required]
554  */
555 static bool IsReJoin0Required( void );
556 
557 /*!
558  * \brief Initializes and opens the reception window
559  *
560  * \param [IN] rxTimer  Window timer to be topped.
561  * \param [IN] rxConfig Window parameters to be setup
562  */
563 static void RxWindowSetup( TimerEvent_t* rxTimer, RxConfigParams_t* rxConfig );
564 
565 /*!
566  * \brief Opens up a continuous RX C window. This is used for
567  *        class c devices.
568  */
569 static void OpenContinuousRxCWindow( void );
570 
571 /*!
572  * \brief   Returns a pointer to the internal contexts structure.
573  *
574  * \retval  void Points to a structure containing all contexts
575  */
576 static LoRaMacNvmData_t* GetNvmData( void );
577 
578 /*!
579  * \brief   Restoring of internal module contexts
580  *
581  * \details This function allows to restore module contexts by a given pointer.
582  *
583  *
584  * \retval  LoRaMacStatus_t Status of the operation. Possible returns are:
585  *          returns are:
586  *          \ref LORAMAC_STATUS_OK,
587  *          \ref LORAMAC_STATUS_PARAMETER_INVALID,
588  */
589 static LoRaMacStatus_t RestoreNvmData( LoRaMacNvmData_t* contexts );
590 
591 /*!
592  * \brief   Determines the frame type
593  *
594  * \param [IN] macMsg Data message object
595  *
596  * \param [OUT] fType Frame type
597  *
598  * \retval  LoRaMacStatus_t Status of the operation. Possible returns are:
599  *          returns are:
600  *          \ref LORAMAC_STATUS_OK,
601  *          \ref LORAMAC_STATUS_PARAMETER_INVALID,
602  */
603 LoRaMacStatus_t DetermineFrameType( LoRaMacMessageData_t* macMsg, FType_t* fType );
604 
605 /*!
606  * \brief Verifies, if the retransmission counter has reached the limit
607  *
608  * \param [IN] counter Current retransmission counter
609  * \param [IN] limit Retransmission counter limit
610  *
611  * \retval Returns true if the number of retransmissions have reached the limit.
612  */
613 static bool CheckRetrans( uint8_t counter, uint8_t limit );
614 
615 /*!
616  * \brief Checks if the retransmission should be stopped in case of a unconfirmed uplink
617  *
618  * \retval Returns true if it should be stopped.
619  */
620 static bool CheckRetransUnconfirmedUplink( void );
621 
622 /*!
623  * \brief Checks if the retransmission should be stopped in case of a confirmed uplink
624  *
625  * \retval Returns true it should be stopped.
626  */
627 static bool CheckRetransConfirmedUplink( void );
628 
629 /*!
630  * \brief Increases the ADR ack counter. Takes the maximum
631  *        value into account.
632  *
633  * \param [IN] counter Current counter value.
634  *
635  * \retval Returns the next counter value.
636  */
637 static uint32_t IncreaseAdrAckCounter( uint32_t counter );
638 
639 /*!
640  * \brief Stops the uplink retransmission
641  *
642  * \retval Returns true if successful.
643  */
644 static bool StopRetransmission( void );
645 
646 /*!
647  * \brief Calls the callback to indicate that a context changed
648  */
649 static void CallNvmDataChangeCallback( uint16_t notifyFlags );
650 
651 /*!
652  * \brief Verifies if a request is pending currently
653  *
654  * \retval 1: Request pending, 0: request not pending
655  */
656 static uint8_t IsRequestPending( void );
657 
658 /*!
659  * \brief Enabled the possibility to perform requests
660  *
661  * \param [IN] requestState Request permission state
662  */
663 static void LoRaMacEnableRequests( LoRaMacRequestHandling_t requestState );
664 
665 /*!
666  * \brief This function verifies if a RX abort occurred
667  */
668 static void LoRaMacCheckForRxAbort( void );
669 
670 /*!
671  * \brief This function verifies if a beacon acquisition MLME
672  *        request was pending
673  *
674  * \retval 1: Request pending, 0: no request pending
675  */
676 static uint8_t LoRaMacCheckForBeaconAcquisition( void );
677 
678 /*!
679  * \brief Returns true, if the device must apply the minium datarate
680  *
681  * \param [IN] adr ADR status bit
682  *
683  * \param [IN] activation Activation type of the device
684  *
685  * \param [IN] datarateChanged Set to true, if the datarate was changed
686  *                             with the LinkAdrReq.
687  */
688 static bool CheckForMinimumAbpDatarate( bool adr, ActivationType_t activation, bool datarateChanged );
689 
690 /*!
691  * \brief This function handles join request
692  */
693 static void LoRaMacHandleMlmeRequest( void );
694 
695 /*!
696  * \brief This function handles mcps request
697  */
698 static void LoRaMacHandleMcpsRequest( void );
699 
700 /*!
701  * \brief This function handles callback events for requests
702  */
703 static void LoRaMacHandleRequestEvents( void );
704 
705 /*!
706  * \brief This function handles callback events for indications
707  */
708 static void LoRaMacHandleIndicationEvents( void );
709 
710 /*!
711  * \brief This function handles callback events for NVM updates
712  *
713  * \param [IN] nvmData Data structure containing NVM data.
714  */
715 static void LoRaMacHandleNvm( LoRaMacNvmData_t* nvmData );
716 
717 /*!
718  * \brief This function verifies if the response timeout has been elapsed. If
719  *        this is the case, the status of Nvm.MacGroup1.SrvAckRequested will be
720  *        reset.
721  *
722  * \param [IN] timeoutInMs Timeout [ms] to be compared.
723  *
724  * \param [IN] startTimeInMs Start time [ms] used as a base. If set to 0,
725  *                           no comparison will be done.
726  *
727  * \retval true: Response timeout has been elapsed, false: Response timeout
728  *         has not been elapsed or startTimeInMs is 0.
729  */
730 static bool LoRaMacHandleResponseTimeout( TimerTime_t timeoutInMs, TimerTime_t startTimeInMs );
731 
732 /*!
733  * Structure used to store the radio Tx event data
734  */
735 struct
736 {
737     TimerTime_t CurTime;
738 }TxDoneParams;
739 
740 /*!
741  * Structure used to store the radio Rx event data
742  */
743 struct
744 {
745     TimerTime_t LastRxDone;
746     uint8_t *Payload;
747     uint16_t Size;
748     int16_t Rssi;
749     int8_t Snr;
750 }RxDoneParams;
751 
OnRadioTxDone(void)752 static void OnRadioTxDone( void )
753 {
754     TxDoneParams.CurTime = TimerGetCurrentTime( );
755     MacCtx.LastTxSysTime = SysTimeGet( );
756 
757     LoRaMacRadioEvents.Events.TxDone = 1;
758 
759     if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->MacProcessNotify != NULL ) )
760     {
761         MacCtx.MacCallbacks->MacProcessNotify( );
762     }
763 }
764 
OnRadioRxDone(uint8_t * payload,uint16_t size,int16_t rssi,int8_t snr)765 static void OnRadioRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr )
766 {
767     RxDoneParams.LastRxDone = TimerGetCurrentTime( );
768     RxDoneParams.Payload = payload;
769     RxDoneParams.Size = size;
770     RxDoneParams.Rssi = rssi;
771     RxDoneParams.Snr = snr;
772 
773     LoRaMacRadioEvents.Events.RxDone = 1;
774     LoRaMacRadioEvents.Events.RxProcessPending = 1;
775 
776     if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->MacProcessNotify != NULL ) )
777     {
778         MacCtx.MacCallbacks->MacProcessNotify( );
779     }
780 }
781 
OnRadioTxTimeout(void)782 static void OnRadioTxTimeout( void )
783 {
784     LoRaMacRadioEvents.Events.TxTimeout = 1;
785 
786     if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->MacProcessNotify != NULL ) )
787     {
788         MacCtx.MacCallbacks->MacProcessNotify( );
789     }
790 }
791 
OnRadioRxError(void)792 static void OnRadioRxError( void )
793 {
794     LoRaMacRadioEvents.Events.RxError = 1;
795 
796     if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->MacProcessNotify != NULL ) )
797     {
798         MacCtx.MacCallbacks->MacProcessNotify( );
799     }
800 }
801 
OnRadioRxTimeout(void)802 static void OnRadioRxTimeout( void )
803 {
804     LoRaMacRadioEvents.Events.RxTimeout = 1;
805 
806     if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->MacProcessNotify != NULL ) )
807     {
808         MacCtx.MacCallbacks->MacProcessNotify( );
809     }
810 }
811 
UpdateRxSlotIdleState(void)812 static void UpdateRxSlotIdleState( void )
813 {
814     if( Nvm.MacGroup2.DeviceClass != CLASS_C )
815     {
816         MacCtx.RxSlot = RX_SLOT_NONE;
817     }
818     else
819     {
820         MacCtx.RxSlot = RX_SLOT_WIN_CLASS_C;
821     }
822 }
823 
ProcessRadioTxDone(void)824 static void ProcessRadioTxDone( void )
825 {
826     GetPhyParams_t getPhy;
827     PhyParam_t phyParam;
828     SetBandTxDoneParams_t txDone;
829 
830     if( Nvm.MacGroup2.DeviceClass != CLASS_C )
831     {
832         Radio.Sleep( );
833     }
834     // Setup timers
835     TimerSetValue( &MacCtx.RxWindowTimer1, MacCtx.RxWindow1Delay );
836     TimerStart( &MacCtx.RxWindowTimer1 );
837     TimerSetValue( &MacCtx.RxWindowTimer2, MacCtx.RxWindow2Delay );
838     TimerStart( &MacCtx.RxWindowTimer2 );
839 
840     if( MacCtx.NodeAckRequested == true )
841     {
842         getPhy.Attribute = PHY_RETRANSMIT_TIMEOUT;
843         phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
844         TimerSetValue( &MacCtx.RetransmitTimeoutTimer, MacCtx.RxWindow2Delay + phyParam.Value );
845         TimerStart( &MacCtx.RetransmitTimeoutTimer );
846     }
847     else
848     {
849         // Transmission successful, setup status directly
850         MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK;
851     }
852 
853     // Update Aggregated last tx done time
854     Nvm.MacGroup1.LastTxDoneTime = TxDoneParams.CurTime;
855 
856     // Update last tx done time for the current channel
857     txDone.Channel = MacCtx.Channel;
858     txDone.LastTxDoneTime = TxDoneParams.CurTime;
859     txDone.ElapsedTimeSinceStartUp = SysTimeSub( SysTimeGetMcuTime( ), Nvm.MacGroup2.InitializationTime );
860     txDone.LastTxAirTime = MacCtx.TxTimeOnAir;
861     txDone.Joined  = true;
862     if( Nvm.MacGroup2.NetworkActivation == ACTIVATION_TYPE_NONE )
863     {
864         txDone.Joined  = false;
865     }
866 
867     RegionSetBandTxDone( Nvm.MacGroup2.Region, &txDone );
868 }
869 
PrepareRxDoneAbort(void)870 static void PrepareRxDoneAbort( void )
871 {
872     MacCtx.MacState |= LORAMAC_RX_ABORT;
873 
874     if( MacCtx.NodeAckRequested == true )
875     {
876         OnRetransmitTimeoutTimerEvent( NULL );
877     }
878 
879     MacCtx.MacFlags.Bits.McpsInd = 1;
880     MacCtx.MacFlags.Bits.MacDone = 1;
881 
882     UpdateRxSlotIdleState( );
883 }
884 
ProcessRadioRxDone(void)885 static void ProcessRadioRxDone( void )
886 {
887     LoRaMacHeader_t macHdr;
888     ApplyCFListParams_t applyCFList;
889     GetPhyParams_t getPhy;
890     PhyParam_t phyParam;
891     LoRaMacCryptoStatus_t macCryptoStatus = LORAMAC_CRYPTO_ERROR;
892 
893     LoRaMacMessageData_t macMsgData;
894     LoRaMacMessageJoinAccept_t macMsgJoinAccept;
895     uint8_t *payload = RxDoneParams.Payload;
896     uint16_t size = RxDoneParams.Size;
897     int16_t rssi = RxDoneParams.Rssi;
898     int8_t snr = RxDoneParams.Snr;
899 
900     uint8_t pktHeaderLen = 0;
901 
902     uint32_t downLinkCounter = 0;
903     uint32_t address = Nvm.MacGroup2.DevAddr;
904     uint8_t multicast = 0;
905     AddressIdentifier_t addrID = UNICAST_DEV_ADDR;
906     FCntIdentifier_t fCntID;
907     uint8_t macCmdPayload[2] = { 0 };
908 
909     LoRaMacRadioEvents.Events.RxProcessPending = 0;
910 
911     MacCtx.McpsConfirm.AckReceived = false;
912     MacCtx.McpsIndication.Rssi = rssi;
913     MacCtx.McpsIndication.Snr = snr;
914     MacCtx.McpsIndication.RxSlot = MacCtx.RxSlot;
915     MacCtx.McpsIndication.Port = 0;
916     MacCtx.McpsIndication.Multicast = 0;
917     MacCtx.McpsIndication.IsUplinkTxPending = 0;
918     MacCtx.McpsIndication.Buffer = NULL;
919     MacCtx.McpsIndication.BufferSize = 0;
920     MacCtx.McpsIndication.RxData = false;
921     MacCtx.McpsIndication.AckReceived = false;
922     MacCtx.McpsIndication.DownLinkCounter = 0;
923     MacCtx.McpsIndication.McpsIndication = MCPS_UNCONFIRMED;
924     MacCtx.McpsIndication.DevAddress = 0;
925     MacCtx.McpsIndication.DeviceTimeAnsReceived = false;
926     MacCtx.McpsIndication.ResponseTimeout = 0;
927 
928     Radio.Sleep( );
929 
930     if( MacCtx.McpsIndication.RxSlot == RX_SLOT_WIN_1 )
931     {
932         TimerStop( &MacCtx.RxWindowTimer2 );
933     }
934 
935     // This function must be called even if we are not in class b mode yet.
936     if( LoRaMacClassBRxBeacon( payload, size ) == true )
937     {
938         MacCtx.MlmeIndication.BeaconInfo.Rssi = rssi;
939         MacCtx.MlmeIndication.BeaconInfo.Snr = snr;
940         return;
941     }
942     // Check if we expect a ping or a multicast slot.
943     if( Nvm.MacGroup2.DeviceClass == CLASS_B )
944     {
945         if( LoRaMacClassBIsPingExpected( ) == true )
946         {
947             LoRaMacClassBSetPingSlotState( PINGSLOT_STATE_CALC_PING_OFFSET );
948             LoRaMacClassBPingSlotTimerEvent( NULL );
949             MacCtx.McpsIndication.RxSlot = RX_SLOT_WIN_CLASS_B_PING_SLOT;
950         }
951         else if( LoRaMacClassBIsMulticastExpected( ) == true )
952         {
953             LoRaMacClassBSetMulticastSlotState( PINGSLOT_STATE_CALC_PING_OFFSET );
954             LoRaMacClassBMulticastSlotTimerEvent( NULL );
955             MacCtx.McpsIndication.RxSlot = RX_SLOT_WIN_CLASS_B_MULTICAST_SLOT;
956         }
957     }
958 
959     macHdr.Value = payload[pktHeaderLen++];
960 
961     // Accept frames of LoRaWAN Major Version 1 only
962     if( macHdr.Bits.Major != 0 )
963     {
964         MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
965         PrepareRxDoneAbort( );
966         return;
967     }
968 
969     switch( macHdr.Bits.MType )
970     {
971         case FRAME_TYPE_JOIN_ACCEPT:
972         {
973             // Check if the received frame size is valid
974             if( size < LORAMAC_JOIN_ACCEPT_FRAME_MIN_SIZE )
975             {
976                 MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
977                 PrepareRxDoneAbort( );
978                 return;
979             }
980             macMsgJoinAccept.Buffer = payload;
981             macMsgJoinAccept.BufSize = size;
982 
983             // Abort in case if the device isn't joined yet and no rejoin request is ongoing.
984             if( ( Nvm.MacGroup2.NetworkActivation != ACTIVATION_TYPE_NONE ) && ( Nvm.MacGroup2.IsRejoinAcceptPending == false ) )
985             {
986                 MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
987                 PrepareRxDoneAbort( );
988                 return;
989             }
990             macCryptoStatus = LoRaMacCryptoHandleJoinAccept( JOIN_REQ, SecureElementGetJoinEui( ), &macMsgJoinAccept );
991 
992             VerifyParams_t verifyRxDr;
993             bool rxDrValid = false;
994             verifyRxDr.DatarateParams.Datarate = macMsgJoinAccept.DLSettings.Bits.RX2DataRate;
995             verifyRxDr.DatarateParams.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime;
996             rxDrValid = RegionVerify( Nvm.MacGroup2.Region, &verifyRxDr, PHY_RX_DR );
997 
998             if( ( LORAMAC_CRYPTO_SUCCESS == macCryptoStatus ) && ( rxDrValid == true ) )
999             {
1000                 // Network ID
1001                 Nvm.MacGroup2.NetID = ( uint32_t ) macMsgJoinAccept.NetID[0];
1002                 Nvm.MacGroup2.NetID |= ( ( uint32_t ) macMsgJoinAccept.NetID[1] << 8 );
1003                 Nvm.MacGroup2.NetID |= ( ( uint32_t ) macMsgJoinAccept.NetID[2] << 16 );
1004 
1005                 // Device Address
1006                 Nvm.MacGroup2.DevAddr = macMsgJoinAccept.DevAddr;
1007 
1008                 // DLSettings
1009                 Nvm.MacGroup2.MacParams.Rx1DrOffset = macMsgJoinAccept.DLSettings.Bits.RX1DRoffset;
1010                 Nvm.MacGroup2.MacParams.Rx2Channel.Datarate = macMsgJoinAccept.DLSettings.Bits.RX2DataRate;
1011                 Nvm.MacGroup2.MacParams.RxCChannel.Datarate = macMsgJoinAccept.DLSettings.Bits.RX2DataRate;
1012 
1013                 // RxDelay
1014                 Nvm.MacGroup2.MacParams.ReceiveDelay1 = macMsgJoinAccept.RxDelay;
1015                 if( Nvm.MacGroup2.MacParams.ReceiveDelay1 == 0 )
1016                 {
1017                     Nvm.MacGroup2.MacParams.ReceiveDelay1 = 1;
1018                 }
1019                 Nvm.MacGroup2.MacParams.ReceiveDelay1 *= 1000;
1020                 Nvm.MacGroup2.MacParams.ReceiveDelay2 = Nvm.MacGroup2.MacParams.ReceiveDelay1 + 1000;
1021 
1022                 // Reset NbTrans to default value
1023                 Nvm.MacGroup2.MacParams.ChannelsNbTrans = 1;
1024 
1025                 // Is Networkserver's LoRaWAN Version before 1.1.0 ?
1026                 if( macMsgJoinAccept.DLSettings.Bits.OptNeg == 0 )
1027                 {
1028                     Nvm.MacGroup2.Version.Value = LORAMAC_FALLBACK_VERSION;
1029                 }
1030                 else
1031                 {
1032                     Nvm.MacGroup2.Version.Value = LORAMAC_VERSION;
1033                 }
1034 
1035                 // Apply CF list
1036                 applyCFList.Payload = macMsgJoinAccept.CFList;
1037                 // Size of the regular payload is 12. Plus 1 byte MHDR and 4 bytes MIC
1038                 applyCFList.Size = size - 17;
1039                 // Apply the last tx channel
1040                 applyCFList.JoinChannel = MacCtx.Channel;
1041 
1042                 RegionApplyCFList( Nvm.MacGroup2.Region, &applyCFList );
1043 
1044                 Nvm.MacGroup2.NetworkActivation = ACTIVATION_TYPE_OTAA;
1045 
1046                 // Add a RekeyInd MAC command to confirm the security key update.
1047                 if( Nvm.MacGroup2.Version.Fields.Minor >= 1 )
1048                 {
1049                     Nvm.MacGroup1.RekeyIndUplinksCounter = 0;
1050                     macCmdPayload[0] = Nvm.MacGroup2.Version.Fields.Minor;
1051                     LoRaMacCommandsAddCmd( MOTE_MAC_REKEY_IND, macCmdPayload, 1 );
1052                 }
1053 
1054                 // MLME handling
1055                 if( LoRaMacConfirmQueueIsCmdActive( MLME_JOIN ) == true )
1056                 {
1057                     LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_JOIN );
1058                 }
1059 
1060                 // Rejoin handling
1061                 if( Nvm.MacGroup2.IsRejoinAcceptPending == true )
1062                 {
1063                     Nvm.MacGroup2.IsRejoinAcceptPending = false;
1064 
1065                     // Stop in any case the ForceRejoinReqCycleTimer
1066                     TimerStop( &MacCtx.ForceRejoinReqCycleTimer );
1067                 }
1068             }
1069             else
1070             {
1071                 // MLME handling
1072                 if( LoRaMacConfirmQueueIsCmdActive( MLME_JOIN ) == true )
1073                 {
1074                     LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL, MLME_JOIN );
1075                 }
1076             }
1077             break;
1078         }
1079         case FRAME_TYPE_DATA_CONFIRMED_DOWN:
1080             MacCtx.McpsIndication.McpsIndication = MCPS_CONFIRMED;
1081             // Intentional fall through
1082         case FRAME_TYPE_DATA_UNCONFIRMED_DOWN:
1083             // Check if the received payload size is valid
1084             getPhy.UplinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime;
1085             getPhy.Datarate = MacCtx.McpsIndication.RxDatarate;
1086             getPhy.Attribute = PHY_MAX_PAYLOAD;
1087             phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
1088             if( ( MAX( 0, ( int16_t )( ( int16_t ) size - ( int16_t ) LORAMAC_FRAME_PAYLOAD_OVERHEAD_SIZE ) ) > ( int16_t )phyParam.Value ) ||
1089                 ( size < LORAMAC_FRAME_PAYLOAD_MIN_SIZE ) )
1090             {
1091                 MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
1092                 PrepareRxDoneAbort( );
1093                 return;
1094             }
1095             macMsgData.Buffer = payload;
1096             macMsgData.BufSize = size;
1097             macMsgData.FRMPayload = MacCtx.RxPayload;
1098             macMsgData.FRMPayloadSize = LORAMAC_PHY_MAXPAYLOAD;
1099 
1100             if( LORAMAC_PARSER_SUCCESS != LoRaMacParserData( &macMsgData ) )
1101             {
1102                 MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
1103                 PrepareRxDoneAbort( );
1104                 return;
1105             }
1106 
1107             // Handle Class B
1108             // Check if we expect a ping or a multicast slot.
1109             if( Nvm.MacGroup2.DeviceClass == CLASS_B )
1110             {
1111                 if( LoRaMacClassBIsPingExpected( ) == true )
1112                 {
1113                     LoRaMacClassBSetPingSlotState( PINGSLOT_STATE_CALC_PING_OFFSET );
1114                     LoRaMacClassBPingSlotTimerEvent( NULL );
1115                     MacCtx.McpsIndication.RxSlot = RX_SLOT_WIN_CLASS_B_PING_SLOT;
1116                     LoRaMacClassBSetFPendingBit( macMsgData.FHDR.DevAddr, ( uint8_t ) macMsgData.FHDR.FCtrl.Bits.FPending );
1117                 }
1118                 else if( LoRaMacClassBIsMulticastExpected( ) == true )
1119                 {
1120                     LoRaMacClassBSetMulticastSlotState( PINGSLOT_STATE_CALC_PING_OFFSET );
1121                     LoRaMacClassBMulticastSlotTimerEvent( NULL );
1122                     MacCtx.McpsIndication.RxSlot = RX_SLOT_WIN_CLASS_B_MULTICAST_SLOT;
1123                     LoRaMacClassBSetFPendingBit( macMsgData.FHDR.DevAddr, ( uint8_t ) macMsgData.FHDR.FCtrl.Bits.FPending );
1124                 }
1125             }
1126 
1127             // Store device address
1128             MacCtx.McpsIndication.DevAddress = macMsgData.FHDR.DevAddr;
1129 
1130             FType_t fType;
1131             if( LORAMAC_STATUS_OK != DetermineFrameType( &macMsgData, &fType ) )
1132             {
1133                 MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
1134                 PrepareRxDoneAbort( );
1135                 return;
1136             }
1137 
1138             //Check if it is a multicast message
1139             multicast = 0;
1140             downLinkCounter = 0;
1141             for( uint8_t i = 0; i < LORAMAC_MAX_MC_CTX; i++ )
1142             {
1143                 if( ( Nvm.MacGroup2.MulticastChannelList[i].ChannelParams.Address == macMsgData.FHDR.DevAddr ) &&
1144                     ( Nvm.MacGroup2.MulticastChannelList[i].ChannelParams.IsEnabled == true ) )
1145                 {
1146                     multicast = 1;
1147                     addrID = Nvm.MacGroup2.MulticastChannelList[i].ChannelParams.GroupID;
1148                     downLinkCounter = *( Nvm.MacGroup2.MulticastChannelList[i].DownLinkCounter );
1149                     address = Nvm.MacGroup2.MulticastChannelList[i].ChannelParams.Address;
1150                     if( Nvm.MacGroup2.DeviceClass == CLASS_C )
1151                     {
1152                         MacCtx.McpsIndication.RxSlot = RX_SLOT_WIN_CLASS_C_MULTICAST;
1153                     }
1154                     break;
1155                 }
1156             }
1157 
1158             // Filter messages according to multicast downlink exceptions
1159             if( ( multicast == 1 ) && ( ( fType != FRAME_TYPE_D ) ||
1160                                         ( macMsgData.FHDR.FCtrl.Bits.Ack != 0 ) ||
1161                                         ( macMsgData.FHDR.FCtrl.Bits.AdrAckReq != 0 ) ) )
1162             {
1163                 MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
1164                 PrepareRxDoneAbort( );
1165                 return;
1166             }
1167 
1168             // Get downlink frame counter value
1169             macCryptoStatus = GetFCntDown( addrID, fType, &macMsgData, Nvm.MacGroup2.Version, &fCntID, &downLinkCounter );
1170             if( macCryptoStatus != LORAMAC_CRYPTO_SUCCESS )
1171             {
1172                 if( macCryptoStatus == LORAMAC_CRYPTO_FAIL_FCNT_DUPLICATED )
1173                 {
1174                     // Catch the case of repeated downlink frame counter
1175                     MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED;
1176                 }
1177                 else
1178                 {
1179                     // Other errors
1180                     MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
1181                 }
1182                 MacCtx.McpsIndication.DownLinkCounter = downLinkCounter;
1183                 PrepareRxDoneAbort( );
1184                 return;
1185             }
1186 
1187             macCryptoStatus = LoRaMacCryptoUnsecureMessage( addrID, address, fCntID, downLinkCounter, &macMsgData );
1188             if( macCryptoStatus != LORAMAC_CRYPTO_SUCCESS )
1189             {
1190                 if( macCryptoStatus == LORAMAC_CRYPTO_FAIL_ADDRESS )
1191                 {
1192                     // We are not the destination of this frame.
1193                     MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL;
1194                 }
1195                 else
1196                 {
1197                     // MIC calculation fail
1198                     MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_MIC_FAIL;
1199                 }
1200                 PrepareRxDoneAbort( );
1201                 return;
1202             }
1203 
1204             MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_OK;
1205             MacCtx.McpsIndication.Multicast = multicast;
1206             MacCtx.McpsIndication.Buffer = NULL;
1207             MacCtx.McpsIndication.BufferSize = 0;
1208             MacCtx.McpsIndication.DownLinkCounter = downLinkCounter;
1209             MacCtx.McpsIndication.AckReceived = macMsgData.FHDR.FCtrl.Bits.Ack;
1210 
1211             MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK;
1212             MacCtx.McpsConfirm.AckReceived = macMsgData.FHDR.FCtrl.Bits.Ack;
1213 
1214             // Reset ADR ACK Counter only, when RX1 or RX2 slot
1215             if( ( MacCtx.McpsIndication.RxSlot == RX_SLOT_WIN_1 ) ||
1216                 ( MacCtx.McpsIndication.RxSlot == RX_SLOT_WIN_2 ) )
1217             {
1218                 Nvm.MacGroup1.AdrAckCounter = 0;
1219                 Nvm.MacGroup2.DownlinkReceived = true;
1220             }
1221 
1222             // MCPS Indication and ack requested handling
1223             if( multicast == 1 )
1224             {
1225                 MacCtx.McpsIndication.McpsIndication = MCPS_MULTICAST;
1226             }
1227             else
1228             {
1229                 if( macHdr.Bits.MType == FRAME_TYPE_DATA_CONFIRMED_DOWN )
1230                 {
1231                     Nvm.MacGroup1.SrvAckRequested = true;
1232                     if( Nvm.MacGroup2.Version.Fields.Minor == 0 )
1233                     {
1234                         Nvm.MacGroup1.LastRxMic = macMsgData.MIC;
1235                     }
1236                     MacCtx.McpsIndication.McpsIndication = MCPS_CONFIRMED;
1237 
1238                     // Handle response timeout for class c and class b downlinks
1239                     if( ( MacCtx.McpsIndication.RxSlot != RX_SLOT_WIN_1 ) &&
1240                         ( MacCtx.McpsIndication.RxSlot != RX_SLOT_WIN_2 ) )
1241                     {
1242                         // Calculate timeout
1243                         MacCtx.McpsIndication.ResponseTimeout = REGION_COMMON_CLASS_B_C_RESP_TIMEOUT;
1244                         MacCtx.ResponseTimeoutStartTime = RxDoneParams.LastRxDone;
1245                     }
1246                 }
1247                 else
1248                 {
1249                     Nvm.MacGroup1.SrvAckRequested = false;
1250                     MacCtx.McpsIndication.McpsIndication = MCPS_UNCONFIRMED;
1251                 }
1252             }
1253 
1254             // Set the pending status
1255             if( ( ( ( Nvm.MacGroup1.SrvAckRequested == true ) || ( macMsgData.FHDR.FCtrl.Bits.FPending > 0 ) ) && ( Nvm.MacGroup2.DeviceClass == CLASS_A ) ) ||
1256                 ( MacCtx.McpsIndication.ResponseTimeout > 0 ) )
1257             {
1258                 MacCtx.McpsIndication.IsUplinkTxPending = 1;
1259             }
1260 
1261             RemoveMacCommands( MacCtx.McpsIndication.RxSlot, macMsgData.FHDR.FCtrl, MacCtx.McpsConfirm.McpsRequest );
1262 
1263             switch( fType )
1264             {
1265                 case FRAME_TYPE_A:
1266                 {  /* +----------+------+-------+--------------+
1267                     * | FOptsLen | Fopt | FPort |  FRMPayload  |
1268                     * +----------+------+-------+--------------+
1269                     * |    > 0   |   X  |  > 0  |       X      |
1270                     * +----------+------+-------+--------------+
1271                     */
1272 
1273                     // Decode MAC commands in FOpts field
1274                     ProcessMacCommands( macMsgData.FHDR.FOpts, 0, macMsgData.FHDR.FCtrl.Bits.FOptsLen, snr, MacCtx.McpsIndication.RxSlot );
1275                     MacCtx.McpsIndication.Port = macMsgData.FPort;
1276                     MacCtx.McpsIndication.Buffer = macMsgData.FRMPayload;
1277                     MacCtx.McpsIndication.BufferSize = macMsgData.FRMPayloadSize;
1278                     MacCtx.McpsIndication.RxData = true;
1279                     break;
1280                 }
1281                 case FRAME_TYPE_B:
1282                 {  /* +----------+------+-------+--------------+
1283                     * | FOptsLen | Fopt | FPort |  FRMPayload  |
1284                     * +----------+------+-------+--------------+
1285                     * |    > 0   |   X  |   -   |       -      |
1286                     * +----------+------+-------+--------------+
1287                     */
1288 
1289                     // Decode MAC commands in FOpts field
1290                     ProcessMacCommands( macMsgData.FHDR.FOpts, 0, macMsgData.FHDR.FCtrl.Bits.FOptsLen, snr, MacCtx.McpsIndication.RxSlot );
1291                     MacCtx.McpsIndication.Port = macMsgData.FPort;
1292                     break;
1293                 }
1294                 case FRAME_TYPE_C:
1295                 {  /* +----------+------+-------+--------------+
1296                     * | FOptsLen | Fopt | FPort |  FRMPayload  |
1297                     * +----------+------+-------+--------------+
1298                     * |    = 0   |   -  |  = 0  | MAC commands |
1299                     * +----------+------+-------+--------------+
1300                     */
1301 
1302                     // Decode MAC commands in FRMPayload
1303                     ProcessMacCommands( macMsgData.FRMPayload, 0, macMsgData.FRMPayloadSize, snr, MacCtx.McpsIndication.RxSlot );
1304                     MacCtx.McpsIndication.Port = macMsgData.FPort;
1305                     break;
1306                 }
1307                 case FRAME_TYPE_D:
1308                 {  /* +----------+------+-------+--------------+
1309                     * | FOptsLen | Fopt | FPort |  FRMPayload  |
1310                     * +----------+------+-------+--------------+
1311                     * |    = 0   |   -  |  > 0  |       X      |
1312                     * +----------+------+-------+--------------+
1313                     */
1314 
1315                     // No MAC commands just application payload
1316                     MacCtx.McpsIndication.Port = macMsgData.FPort;
1317                     MacCtx.McpsIndication.Buffer = macMsgData.FRMPayload;
1318                     MacCtx.McpsIndication.BufferSize = macMsgData.FRMPayloadSize;
1319                     MacCtx.McpsIndication.RxData = true;
1320                     break;
1321                 }
1322                 default:
1323                     MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
1324                     PrepareRxDoneAbort( );
1325                     break;
1326             }
1327 
1328             // Rejoin handling
1329             if( Nvm.MacGroup2.IsRejoinAcceptPending == true )
1330             {
1331                 Nvm.MacGroup2.IsRejoinAcceptPending = false;
1332 
1333                 // Stop in any case the ForceRejoinReqCycleTimer
1334                 TimerStop( &MacCtx.ForceRejoinReqCycleTimer );
1335 
1336                 // If the rejoin was trigged by MLME, set confirmation status
1337                 if( MacCtx.MacFlags.Bits.MlmeReq == 1 )
1338                 {
1339                     MacCtx.MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK;
1340                 }
1341             }
1342 
1343             if( ( macMsgData.FPort == LORAMAC_CERT_FPORT ) && (Nvm.MacGroup2.IsCertPortOn == false ) )
1344             { // Do not notify the upper layer of data reception on FPort LORAMAC_CERT_FPORT if the port
1345               // handling is disabled.
1346                 MacCtx.McpsIndication.Port = macMsgData.FPort;
1347                 MacCtx.McpsIndication.Buffer = NULL;
1348                 MacCtx.McpsIndication.BufferSize = 0;
1349                 MacCtx.McpsIndication.RxData = false;
1350             }
1351 
1352             // Provide always an indication, skip the callback to the user application,
1353             // in case of a confirmed downlink retransmission.
1354             MacCtx.MacFlags.Bits.McpsInd = 1;
1355 
1356             break;
1357         case FRAME_TYPE_PROPRIETARY:
1358             memcpy1( MacCtx.RxPayload, &payload[pktHeaderLen], size - pktHeaderLen );
1359 
1360             MacCtx.McpsIndication.McpsIndication = MCPS_PROPRIETARY;
1361             MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_OK;
1362             MacCtx.McpsIndication.Buffer = MacCtx.RxPayload;
1363             MacCtx.McpsIndication.BufferSize = size - pktHeaderLen;
1364 
1365             MacCtx.MacFlags.Bits.McpsInd = 1;
1366             break;
1367         default:
1368             MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
1369             PrepareRxDoneAbort( );
1370             break;
1371     }
1372 
1373     // Verify if we need to disable the RetransmitTimeoutTimer
1374     // Only aplies if downlink is received on Rx1 or Rx2 windows.
1375     if( ( MacCtx.McpsIndication.RxSlot == RX_SLOT_WIN_1 ) ||
1376         ( MacCtx.McpsIndication.RxSlot == RX_SLOT_WIN_2 ) )
1377     {
1378         if( MacCtx.NodeAckRequested == true )
1379         {
1380             if( MacCtx.McpsConfirm.AckReceived == true )
1381             {
1382                 OnRetransmitTimeoutTimerEvent( NULL );
1383             }
1384         }
1385     }
1386 
1387     if( MacCtx.McpsIndication.RxSlot != RX_SLOT_WIN_CLASS_C )
1388     {
1389         MacCtx.MacFlags.Bits.MacDone = 1;
1390     }
1391 
1392     UpdateRxSlotIdleState( );
1393 }
1394 
ProcessRadioTxTimeout(void)1395 static void ProcessRadioTxTimeout( void )
1396 {
1397     if( Nvm.MacGroup2.DeviceClass != CLASS_C )
1398     {
1399         Radio.Sleep( );
1400     }
1401     UpdateRxSlotIdleState( );
1402 
1403     MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT;
1404     LoRaMacConfirmQueueSetStatusCmn( LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT );
1405     if( MacCtx.NodeAckRequested == true )
1406     {
1407         MacCtx.RetransmitTimeoutRetry = true;
1408     }
1409     MacCtx.MacFlags.Bits.MacDone = 1;
1410 }
1411 
HandleRadioRxErrorTimeout(LoRaMacEventInfoStatus_t rx1EventInfoStatus,LoRaMacEventInfoStatus_t rx2EventInfoStatus)1412 static void HandleRadioRxErrorTimeout( LoRaMacEventInfoStatus_t rx1EventInfoStatus, LoRaMacEventInfoStatus_t rx2EventInfoStatus )
1413 {
1414     bool classBRx = false;
1415 
1416     if( Nvm.MacGroup2.DeviceClass != CLASS_C )
1417     {
1418         Radio.Sleep( );
1419     }
1420 
1421     if( LoRaMacClassBIsBeaconExpected( ) == true )
1422     {
1423         LoRaMacClassBSetBeaconState( BEACON_STATE_TIMEOUT );
1424         LoRaMacClassBBeaconTimerEvent( NULL );
1425         classBRx = true;
1426     }
1427     if( Nvm.MacGroup2.DeviceClass == CLASS_B )
1428     {
1429         if( LoRaMacClassBIsPingExpected( ) == true )
1430         {
1431             LoRaMacClassBSetPingSlotState( PINGSLOT_STATE_CALC_PING_OFFSET );
1432             LoRaMacClassBPingSlotTimerEvent( NULL );
1433             classBRx = true;
1434         }
1435         if( LoRaMacClassBIsMulticastExpected( ) == true )
1436         {
1437             LoRaMacClassBSetMulticastSlotState( PINGSLOT_STATE_CALC_PING_OFFSET );
1438             LoRaMacClassBMulticastSlotTimerEvent( NULL );
1439             classBRx = true;
1440         }
1441     }
1442 
1443     if( classBRx == false )
1444     {
1445         if( MacCtx.RxSlot == RX_SLOT_WIN_1 )
1446         {
1447             if( MacCtx.NodeAckRequested == true )
1448             {
1449                 MacCtx.McpsConfirm.Status = rx1EventInfoStatus;
1450             }
1451             LoRaMacConfirmQueueSetStatusCmn( rx1EventInfoStatus );
1452 
1453             if( TimerGetElapsedTime( Nvm.MacGroup1.LastTxDoneTime ) >= MacCtx.RxWindow2Delay )
1454             {
1455                 TimerStop( &MacCtx.RxWindowTimer2 );
1456                 MacCtx.MacFlags.Bits.MacDone = 1;
1457             }
1458         }
1459         else
1460         {
1461             if( MacCtx.NodeAckRequested == true )
1462             {
1463                 MacCtx.McpsConfirm.Status = rx2EventInfoStatus;
1464             }
1465             LoRaMacConfirmQueueSetStatusCmn( rx2EventInfoStatus );
1466             MacCtx.MacFlags.Bits.MacDone = 1;
1467         }
1468     }
1469 
1470     UpdateRxSlotIdleState( );
1471 }
1472 
ProcessRadioRxError(void)1473 static void ProcessRadioRxError( void )
1474 {
1475     HandleRadioRxErrorTimeout( LORAMAC_EVENT_INFO_STATUS_RX1_ERROR, LORAMAC_EVENT_INFO_STATUS_RX2_ERROR );
1476 }
1477 
ProcessRadioRxTimeout(void)1478 static void ProcessRadioRxTimeout( void )
1479 {
1480     HandleRadioRxErrorTimeout( LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT, LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT );
1481 }
1482 
LoRaMacHandleIrqEvents(void)1483 static void LoRaMacHandleIrqEvents( void )
1484 {
1485     LoRaMacRadioEvents_t events;
1486 
1487     CRITICAL_SECTION_BEGIN( );
1488     events = LoRaMacRadioEvents;
1489     LoRaMacRadioEvents.Value = 0;
1490     CRITICAL_SECTION_END( );
1491 
1492     if( events.Value != 0 )
1493     {
1494         if( events.Events.TxDone == 1 )
1495         {
1496             ProcessRadioTxDone( );
1497         }
1498         if( events.Events.RxDone == 1 )
1499         {
1500             ProcessRadioRxDone( );
1501         }
1502         if( events.Events.TxTimeout == 1 )
1503         {
1504             ProcessRadioTxTimeout( );
1505         }
1506         if( events.Events.RxError == 1 )
1507         {
1508             ProcessRadioRxError( );
1509         }
1510         if( events.Events.RxTimeout == 1 )
1511         {
1512             ProcessRadioRxTimeout( );
1513         }
1514     }
1515 }
1516 
LoRaMacIsBusy(void)1517 bool LoRaMacIsBusy( void )
1518 {
1519     if( LoRaMacRadioEvents.Events.RxProcessPending == 1 )
1520     {
1521         return true;
1522     }
1523 
1524     if( ( MacCtx.MacState == LORAMAC_IDLE ) &&
1525         ( MacCtx.AllowRequests == LORAMAC_REQUEST_HANDLING_ON ) )
1526     {
1527         return false;
1528     }
1529     return true;
1530 }
1531 
1532 
LoRaMacEnableRequests(LoRaMacRequestHandling_t requestState)1533 static void LoRaMacEnableRequests( LoRaMacRequestHandling_t requestState )
1534 {
1535     MacCtx.AllowRequests = requestState;
1536 }
1537 
LoRaMacHandleRequestEvents(void)1538 static void LoRaMacHandleRequestEvents( void )
1539 {
1540     // Handle events
1541     LoRaMacFlags_t reqEvents = MacCtx.MacFlags;
1542 
1543     if( MacCtx.MacState == LORAMAC_IDLE )
1544     {
1545         // Update event bits
1546         if( MacCtx.MacFlags.Bits.McpsReq == 1 )
1547         {
1548             MacCtx.MacFlags.Bits.McpsReq = 0;
1549         }
1550 
1551         if( MacCtx.MacFlags.Bits.MlmeReq == 1 )
1552         {
1553             MacCtx.MacFlags.Bits.MlmeReq = 0;
1554         }
1555 
1556         // Allow requests again
1557         LoRaMacEnableRequests( LORAMAC_REQUEST_HANDLING_ON );
1558 
1559         // Handle callbacks
1560         if( reqEvents.Bits.McpsReq == 1 )
1561         {
1562             MacCtx.MacPrimitives->MacMcpsConfirm( &MacCtx.McpsConfirm );
1563         }
1564 
1565         if( reqEvents.Bits.MlmeReq == 1 )
1566         {
1567             LoRaMacConfirmQueueHandleCb( &MacCtx.MlmeConfirm );
1568             if( LoRaMacConfirmQueueGetCnt( ) > 0 )
1569             {
1570                 MacCtx.MacFlags.Bits.MlmeReq = 1;
1571             }
1572         }
1573 
1574         // Start beaconing again
1575         LoRaMacClassBResumeBeaconing( );
1576 
1577         // Procedure done. Reset variables.
1578         MacCtx.MacFlags.Bits.MacDone = 0;
1579     }
1580 }
1581 
LoRaMacHandleIndicationEvents(void)1582 static void LoRaMacHandleIndicationEvents( void )
1583 {
1584     // Handle MLME indication
1585     if( MacCtx.MacFlags.Bits.MlmeInd == 1 )
1586     {
1587         MacCtx.MacFlags.Bits.MlmeInd = 0;
1588         MacCtx.MacPrimitives->MacMlmeIndication( &MacCtx.MlmeIndication );
1589     }
1590 
1591     // Handle MCPS indication
1592     if( MacCtx.MacFlags.Bits.McpsInd == 1 )
1593     {
1594         MacCtx.MacFlags.Bits.McpsInd = 0;
1595         MacCtx.MacPrimitives->MacMcpsIndication( &MacCtx.McpsIndication );
1596     }
1597 }
1598 
LoRaMacHandleMcpsRequest(void)1599 static void LoRaMacHandleMcpsRequest( void )
1600 {
1601     // Handle MCPS uplinks
1602     if( MacCtx.MacFlags.Bits.McpsReq == 1 )
1603     {
1604         bool stopRetransmission = false;
1605         bool waitForRetransmission = false;
1606 
1607         if( ( MacCtx.McpsConfirm.McpsRequest == MCPS_UNCONFIRMED ) ||
1608             ( MacCtx.McpsConfirm.McpsRequest == MCPS_PROPRIETARY ) )
1609         {
1610             stopRetransmission = CheckRetransUnconfirmedUplink( );
1611         }
1612         else if( MacCtx.McpsConfirm.McpsRequest == MCPS_CONFIRMED )
1613         {
1614             if( MacCtx.RetransmitTimeoutRetry == true )
1615             {
1616                 stopRetransmission = CheckRetransConfirmedUplink( );
1617             }
1618             else
1619             {
1620                 waitForRetransmission = true;
1621             }
1622         }
1623 
1624         if( stopRetransmission == true )
1625         {// Stop retransmission
1626             TimerStop( &MacCtx.TxDelayedTimer );
1627             MacCtx.MacState &= ~LORAMAC_TX_DELAYED;
1628             StopRetransmission( );
1629 
1630             if( IsReJoin0Required( ) == true )
1631             {
1632                 SendReJoinReq( REJOIN_REQ_0 );
1633             }
1634         }
1635         else if( waitForRetransmission == false )
1636         {// Arrange further retransmission
1637             MacCtx.MacFlags.Bits.MacDone = 0;
1638             // Reset the state of the AckTimeout
1639             MacCtx.RetransmitTimeoutRetry = false;
1640             // Sends the same frame again
1641             OnTxDelayedTimerEvent( NULL );
1642         }
1643     }
1644 }
1645 
LoRaMacHandleMlmeRequest(void)1646 static void LoRaMacHandleMlmeRequest( void )
1647 {
1648     // Handle join request
1649     if( MacCtx.MacFlags.Bits.MlmeReq == 1 )
1650     {
1651         if( LoRaMacConfirmQueueIsCmdActive( MLME_JOIN ) == true )
1652         {
1653             if( LoRaMacConfirmQueueGetStatus( MLME_JOIN ) == LORAMAC_EVENT_INFO_STATUS_OK )
1654             {// Node joined successfully
1655                 MacCtx.ChannelsNbTransCounter = 0;
1656             }
1657             MacCtx.MacState &= ~LORAMAC_TX_RUNNING;
1658         }
1659         else if( LoRaMacConfirmQueueIsCmdActive( MLME_TXCW ) == true )
1660         {
1661             MacCtx.MacState &= ~LORAMAC_TX_RUNNING;
1662         }
1663     }
1664 }
1665 
LoRaMacCheckForBeaconAcquisition(void)1666 static uint8_t LoRaMacCheckForBeaconAcquisition( void )
1667 {
1668     if( ( LoRaMacConfirmQueueIsCmdActive( MLME_BEACON_ACQUISITION ) == true ) &&
1669         ( MacCtx.MacFlags.Bits.McpsReq == 0 ) )
1670     {
1671         if( MacCtx.MacFlags.Bits.MlmeReq == 1 )
1672         {
1673             MacCtx.MacState &= ~LORAMAC_TX_RUNNING;
1674             return 0x01;
1675         }
1676     }
1677     return 0x00;
1678 }
1679 
CheckForMinimumAbpDatarate(bool adr,ActivationType_t activation,bool datarateChanged)1680 static bool CheckForMinimumAbpDatarate( bool adr, ActivationType_t activation, bool datarateChanged )
1681 {
1682     if( ( adr == true ) &&
1683         ( activation == ACTIVATION_TYPE_ABP ) &&
1684         ( datarateChanged == false ) )
1685     {
1686         return true;
1687     }
1688     return false;
1689 }
1690 
LoRaMacCheckForRxAbort(void)1691 static void LoRaMacCheckForRxAbort( void )
1692 {
1693     // A error occurs during receiving
1694     if( ( MacCtx.MacState & LORAMAC_RX_ABORT ) == LORAMAC_RX_ABORT )
1695     {
1696         MacCtx.MacState &= ~LORAMAC_RX_ABORT;
1697         MacCtx.MacState &= ~LORAMAC_TX_RUNNING;
1698     }
1699 }
1700 
LoRaMacHandleNvm(LoRaMacNvmData_t * nvmData)1701 static void LoRaMacHandleNvm( LoRaMacNvmData_t* nvmData )
1702 {
1703     uint32_t crc = 0;
1704     uint16_t notifyFlags = LORAMAC_NVM_NOTIFY_FLAG_NONE;
1705 
1706     if( MacCtx.MacState != LORAMAC_IDLE )
1707     {
1708         return;
1709     }
1710 
1711     // Crypto
1712     crc = Crc32( ( uint8_t* ) &nvmData->Crypto, sizeof( nvmData->Crypto ) -
1713                                                 sizeof( nvmData->Crypto.Crc32 ) );
1714     if( crc != nvmData->Crypto.Crc32 )
1715     {
1716         nvmData->Crypto.Crc32 = crc;
1717         notifyFlags |= LORAMAC_NVM_NOTIFY_FLAG_CRYPTO;
1718     }
1719 
1720     // MacGroup1
1721     crc = Crc32( ( uint8_t* ) &nvmData->MacGroup1, sizeof( nvmData->MacGroup1 ) -
1722                                                    sizeof( nvmData->MacGroup1.Crc32 ) );
1723     if( crc != nvmData->MacGroup1.Crc32 )
1724     {
1725         nvmData->MacGroup1.Crc32 = crc;
1726         notifyFlags |= LORAMAC_NVM_NOTIFY_FLAG_MAC_GROUP1;
1727     }
1728 
1729     // MacGroup2
1730     crc = Crc32( ( uint8_t* ) &nvmData->MacGroup2, sizeof( nvmData->MacGroup2 ) -
1731                                                    sizeof( nvmData->MacGroup2.Crc32 ) );
1732     if( crc != nvmData->MacGroup2.Crc32 )
1733     {
1734         nvmData->MacGroup2.Crc32 = crc;
1735         notifyFlags |= LORAMAC_NVM_NOTIFY_FLAG_MAC_GROUP2;
1736     }
1737 
1738     // Secure Element
1739     crc = Crc32( ( uint8_t* ) &nvmData->SecureElement, sizeof( nvmData->SecureElement ) -
1740                                                        sizeof( nvmData->SecureElement.Crc32 ) );
1741     if( crc != nvmData->SecureElement.Crc32 )
1742     {
1743         nvmData->SecureElement.Crc32 = crc;
1744         notifyFlags |= LORAMAC_NVM_NOTIFY_FLAG_SECURE_ELEMENT;
1745     }
1746 
1747     // Region
1748     crc = Crc32( ( uint8_t* ) &nvmData->RegionGroup1, sizeof( nvmData->RegionGroup1 ) -
1749                                                 sizeof( nvmData->RegionGroup1.Crc32 ) );
1750     if( crc != nvmData->RegionGroup1.Crc32 )
1751     {
1752         nvmData->RegionGroup1.Crc32 = crc;
1753         notifyFlags |= LORAMAC_NVM_NOTIFY_FLAG_REGION_GROUP1;
1754     }
1755 
1756     crc = Crc32( ( uint8_t* ) &nvmData->RegionGroup2, sizeof( nvmData->RegionGroup2 ) -
1757                                                 sizeof( nvmData->RegionGroup2.Crc32 ) );
1758     if( crc != nvmData->RegionGroup2.Crc32 )
1759     {
1760         nvmData->RegionGroup2.Crc32 = crc;
1761         notifyFlags |= LORAMAC_NVM_NOTIFY_FLAG_REGION_GROUP2;
1762     }
1763 
1764     // ClassB
1765     crc = Crc32( ( uint8_t* ) &nvmData->ClassB, sizeof( nvmData->ClassB ) -
1766                                                 sizeof( nvmData->ClassB.Crc32 ) );
1767     if( crc != nvmData->ClassB.Crc32 )
1768     {
1769         nvmData->ClassB.Crc32 = crc;
1770         notifyFlags |= LORAMAC_NVM_NOTIFY_FLAG_CLASS_B;
1771     }
1772 
1773     CallNvmDataChangeCallback( notifyFlags );
1774 }
1775 
LoRaMacHandleResponseTimeout(TimerTime_t timeoutInMs,TimerTime_t startTimeInMs)1776 static bool LoRaMacHandleResponseTimeout( TimerTime_t timeoutInMs, TimerTime_t startTimeInMs )
1777 {
1778     if( startTimeInMs != 0 )
1779     {
1780         TimerTime_t elapsedTime = TimerGetElapsedTime( startTimeInMs );
1781         if( elapsedTime > timeoutInMs )
1782         {
1783             Nvm.MacGroup1.SrvAckRequested = false;
1784             return true;
1785         }
1786     }
1787     return false;
1788 }
1789 
LoRaMacProcess(void)1790 void LoRaMacProcess( void )
1791 {
1792     uint8_t noTx = false;
1793 
1794     LoRaMacHandleIrqEvents( );
1795     LoRaMacClassBProcess( );
1796 
1797     // MAC proceeded a state and is ready to check
1798     if( MacCtx.MacFlags.Bits.MacDone == 1 )
1799     {
1800         LoRaMacEnableRequests( LORAMAC_REQUEST_HANDLING_OFF );
1801         LoRaMacCheckForRxAbort( );
1802 
1803         // An error occurs during transmitting
1804         if( IsRequestPending( ) > 0 )
1805         {
1806             noTx |= LoRaMacCheckForBeaconAcquisition( );
1807         }
1808 
1809         if( noTx == 0x00 )
1810         {
1811             LoRaMacHandleMlmeRequest( );
1812             LoRaMacHandleMcpsRequest( );
1813         }
1814         LoRaMacHandleRequestEvents( );
1815         LoRaMacEnableRequests( LORAMAC_REQUEST_HANDLING_ON );
1816         MacCtx.MacFlags.Bits.NvmHandle = 1;
1817     }
1818     LoRaMacHandleIndicationEvents( );
1819     if( MacCtx.RxSlot == RX_SLOT_WIN_CLASS_C )
1820     {
1821         OpenContinuousRxCWindow( );
1822     }
1823     if( MacCtx.MacFlags.Bits.NvmHandle == 1 )
1824     {
1825         MacCtx.MacFlags.Bits.NvmHandle = 0;
1826         LoRaMacHandleNvm( &Nvm );
1827     }
1828 }
1829 
OnTxDelayedTimerEvent(void * context)1830 static void OnTxDelayedTimerEvent( void* context )
1831 {
1832     TimerStop( &MacCtx.TxDelayedTimer );
1833     MacCtx.MacState &= ~LORAMAC_TX_DELAYED;
1834 
1835     if( LoRaMacHandleResponseTimeout( REGION_COMMON_CLASS_B_C_RESP_TIMEOUT,
1836                                       MacCtx.ResponseTimeoutStartTime ) == true )
1837     {
1838         // Skip retransmission
1839         return;
1840     }
1841 
1842     // Schedule frame, allow delayed frame transmissions
1843     switch( ScheduleTx( true ) )
1844     {
1845         case LORAMAC_STATUS_OK:
1846         case LORAMAC_STATUS_DUTYCYCLE_RESTRICTED:
1847         {
1848             break;
1849         }
1850         default:
1851         {
1852             // Stop retransmission attempt
1853             MacCtx.McpsConfirm.Datarate = Nvm.MacGroup1.ChannelsDatarate;
1854             MacCtx.McpsConfirm.NbTrans = MacCtx.ChannelsNbTransCounter;
1855             MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR;
1856             LoRaMacConfirmQueueSetStatusCmn( LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR );
1857             StopRetransmission( );
1858             break;
1859         }
1860     }
1861 }
1862 
OnRxWindow1TimerEvent(void * context)1863 static void OnRxWindow1TimerEvent( void* context )
1864 {
1865     MacCtx.RxWindow1Config.Channel = MacCtx.Channel;
1866     MacCtx.RxWindow1Config.DrOffset = Nvm.MacGroup2.MacParams.Rx1DrOffset;
1867     MacCtx.RxWindow1Config.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime;
1868     MacCtx.RxWindow1Config.RxContinuous = false;
1869     MacCtx.RxWindow1Config.RxSlot = RX_SLOT_WIN_1;
1870     MacCtx.RxWindow1Config.NetworkActivation = Nvm.MacGroup2.NetworkActivation;
1871 
1872     RxWindowSetup( &MacCtx.RxWindowTimer1, &MacCtx.RxWindow1Config );
1873 }
1874 
OnRxWindow2TimerEvent(void * context)1875 static void OnRxWindow2TimerEvent( void* context )
1876 {
1877     // Check if we are processing Rx1 window.
1878     // If yes, we don't setup the Rx2 window.
1879     if( MacCtx.RxSlot == RX_SLOT_WIN_1 )
1880     {
1881         return;
1882     }
1883     MacCtx.RxWindow2Config.Channel = MacCtx.Channel;
1884     MacCtx.RxWindow2Config.Frequency = Nvm.MacGroup2.MacParams.Rx2Channel.Frequency;
1885     MacCtx.RxWindow2Config.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime;
1886     MacCtx.RxWindow2Config.RxContinuous = false;
1887     MacCtx.RxWindow2Config.RxSlot = RX_SLOT_WIN_2;
1888     MacCtx.RxWindow2Config.NetworkActivation = Nvm.MacGroup2.NetworkActivation;
1889 
1890     RxWindowSetup( &MacCtx.RxWindowTimer2, &MacCtx.RxWindow2Config );
1891 }
1892 
OnRetransmitTimeoutTimerEvent(void * context)1893 static void OnRetransmitTimeoutTimerEvent( void* context )
1894 {
1895     TimerStop( &MacCtx.RetransmitTimeoutTimer );
1896 
1897     if( MacCtx.NodeAckRequested == true )
1898     {
1899         MacCtx.RetransmitTimeoutRetry = true;
1900     }
1901     if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->MacProcessNotify != NULL ) )
1902     {
1903         MacCtx.MacCallbacks->MacProcessNotify( );
1904     }
1905 }
1906 
GetFCntDown(AddressIdentifier_t addrID,FType_t fType,LoRaMacMessageData_t * macMsg,Version_t lrWanVersion,FCntIdentifier_t * fCntID,uint32_t * currentDown)1907 static LoRaMacCryptoStatus_t GetFCntDown( AddressIdentifier_t addrID, FType_t fType, LoRaMacMessageData_t* macMsg, Version_t lrWanVersion,
1908                                           FCntIdentifier_t* fCntID, uint32_t* currentDown )
1909 {
1910     if( ( macMsg == NULL ) || ( fCntID == NULL ) ||
1911         ( currentDown == NULL ) )
1912     {
1913         return LORAMAC_CRYPTO_ERROR_NPE;
1914     }
1915 
1916     // Determine the frame counter identifier and choose counter from FCntList
1917     switch( addrID )
1918     {
1919         case UNICAST_DEV_ADDR:
1920             if( lrWanVersion.Fields.Minor == 1 )
1921             {
1922                 if( ( fType == FRAME_TYPE_A ) || ( fType == FRAME_TYPE_D ) )
1923                 {
1924                     *fCntID = A_FCNT_DOWN;
1925                 }
1926                 else
1927                 {
1928                     *fCntID = N_FCNT_DOWN;
1929                 }
1930             }
1931             else
1932             { // For LoRaWAN 1.0.X
1933                 *fCntID = FCNT_DOWN;
1934             }
1935             break;
1936         case MULTICAST_0_ADDR:
1937             *fCntID = MC_FCNT_DOWN_0;
1938             break;
1939         case MULTICAST_1_ADDR:
1940             *fCntID = MC_FCNT_DOWN_1;
1941             break;
1942         case MULTICAST_2_ADDR:
1943             *fCntID = MC_FCNT_DOWN_2;
1944             break;
1945         case MULTICAST_3_ADDR:
1946             *fCntID = MC_FCNT_DOWN_3;
1947             break;
1948         default:
1949             return LORAMAC_CRYPTO_FAIL_FCNT_ID;
1950     }
1951 
1952     return LoRaMacCryptoGetFCntDown( *fCntID, macMsg->FHDR.FCnt, currentDown );
1953 }
1954 
SwitchClass(DeviceClass_t deviceClass)1955 static LoRaMacStatus_t SwitchClass( DeviceClass_t deviceClass )
1956 {
1957     LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID;
1958 
1959     switch( Nvm.MacGroup2.DeviceClass )
1960     {
1961         case CLASS_A:
1962         {
1963             if( deviceClass == CLASS_A )
1964             {
1965                 // Revert back RxC parameters
1966                 Nvm.MacGroup2.MacParams.RxCChannel = Nvm.MacGroup2.MacParams.Rx2Channel;
1967             }
1968             if( deviceClass == CLASS_B )
1969             {
1970                 status = LoRaMacClassBSwitchClass( deviceClass );
1971                 if( status == LORAMAC_STATUS_OK )
1972                 {
1973                     Nvm.MacGroup2.DeviceClass = deviceClass;
1974                 }
1975             }
1976 
1977             if( deviceClass == CLASS_C )
1978             {
1979                 Nvm.MacGroup2.DeviceClass = deviceClass;
1980 
1981                 MacCtx.RxWindowCConfig = MacCtx.RxWindow2Config;
1982                 MacCtx.RxWindowCConfig.RxSlot = RX_SLOT_WIN_CLASS_C;
1983 
1984                 for( int8_t i = 0; i < LORAMAC_MAX_MC_CTX; i++ )
1985                 {
1986                     if( ( Nvm.MacGroup2.MulticastChannelList[i].ChannelParams.IsEnabled == true ) &&
1987                         ( Nvm.MacGroup2.MulticastChannelList[i].ChannelParams.RxParams.Class == CLASS_C ) )
1988                     {
1989                         Nvm.MacGroup2.MacParams.RxCChannel.Frequency = Nvm.MacGroup2.MulticastChannelList[i].ChannelParams.RxParams.Params.ClassC.Frequency;
1990                         Nvm.MacGroup2.MacParams.RxCChannel.Datarate = Nvm.MacGroup2.MulticastChannelList[i].ChannelParams.RxParams.Params.ClassC.Datarate;
1991 
1992                         MacCtx.RxWindowCConfig.Channel = MacCtx.Channel;
1993                         MacCtx.RxWindowCConfig.Frequency = Nvm.MacGroup2.MacParams.RxCChannel.Frequency;
1994                         MacCtx.RxWindowCConfig.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime;
1995                         MacCtx.RxWindowCConfig.RxSlot = RX_SLOT_WIN_CLASS_C_MULTICAST;
1996                         MacCtx.RxWindowCConfig.RxContinuous = true;
1997                         break;
1998                     }
1999                 }
2000 
2001                 // Set the NodeAckRequested indicator to default
2002                 MacCtx.NodeAckRequested = false;
2003                 // Set the radio into sleep mode in case we are still in RX mode
2004                 Radio.Sleep( );
2005 
2006                 OpenContinuousRxCWindow( );
2007 
2008                 // Add a DeviceModeInd MAC Command to indicate the network a device mode change.
2009                 if( Nvm.MacGroup2.Version.Fields.Minor >= 1 )
2010                 {
2011                     LoRaMacCommandsAddCmd( MOTE_MAC_DEVICE_MODE_IND, ( uint8_t* )&Nvm.MacGroup2.DeviceClass, 1 );
2012                 }
2013 
2014                 status = LORAMAC_STATUS_OK;
2015             }
2016             break;
2017         }
2018         case CLASS_B:
2019         {
2020             status = LoRaMacClassBSwitchClass( deviceClass );
2021             if( status == LORAMAC_STATUS_OK )
2022             {
2023                 Nvm.MacGroup2.DeviceClass = deviceClass;
2024             }
2025             break;
2026         }
2027         case CLASS_C:
2028         {
2029             if( deviceClass == CLASS_A )
2030             {
2031                 Nvm.MacGroup2.DeviceClass = deviceClass;
2032 
2033                 // Set the radio into sleep to setup a defined state
2034                 Radio.Sleep( );
2035 
2036                 status = LORAMAC_STATUS_OK;
2037 
2038                 // Add a DeviceModeInd MAC Command to indicate the network a device mode change.
2039                 if( Nvm.MacGroup2.Version.Fields.Minor >= 1 )
2040                 {
2041                     LoRaMacCommandsAddCmd( MOTE_MAC_DEVICE_MODE_IND, ( uint8_t* )&Nvm.MacGroup2.DeviceClass, 1 );
2042                 }
2043             }
2044             break;
2045         }
2046     }
2047 
2048     return status;
2049 }
2050 
GetMaxAppPayloadWithoutFOptsLength(int8_t datarate)2051 static uint8_t GetMaxAppPayloadWithoutFOptsLength( int8_t datarate )
2052 {
2053     GetPhyParams_t getPhy;
2054     PhyParam_t phyParam;
2055 
2056     // Setup PHY request
2057     getPhy.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime;
2058     getPhy.Datarate = datarate;
2059     getPhy.Attribute = PHY_MAX_PAYLOAD;
2060     phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
2061 
2062     return phyParam.Value;
2063 }
2064 
ValidatePayloadLength(uint8_t lenN,int8_t datarate,uint8_t fOptsLen)2065 static bool ValidatePayloadLength( uint8_t lenN, int8_t datarate, uint8_t fOptsLen )
2066 {
2067     uint16_t maxN = 0;
2068     uint16_t payloadSize = 0;
2069 
2070     maxN = GetMaxAppPayloadWithoutFOptsLength( datarate );
2071 
2072     // Calculate the resulting payload size
2073     payloadSize = ( lenN + fOptsLen );
2074 
2075     // Validation of the application payload size
2076     if( ( payloadSize <= maxN ) && ( payloadSize <= LORAMAC_PHY_MAXPAYLOAD ) )
2077     {
2078         return true;
2079     }
2080     return false;
2081 }
2082 
ProcessMacCommands(uint8_t * payload,uint8_t macIndex,uint8_t commandsSize,int8_t snr,LoRaMacRxSlot_t rxSlot)2083 static void ProcessMacCommands( uint8_t *payload, uint8_t macIndex, uint8_t commandsSize, int8_t snr, LoRaMacRxSlot_t rxSlot )
2084 {
2085     uint8_t status = 0;
2086     bool adrBlockFound = false;
2087     uint8_t macCmdPayload[2] = { 0x00, 0x00 };
2088     MacCommand_t* macCmd;
2089 
2090     if( ( rxSlot != RX_SLOT_WIN_1 ) && ( rxSlot != RX_SLOT_WIN_2 ) )
2091     {
2092         // Do only parse MAC commands for Class A RX windows
2093         return;
2094     }
2095 
2096     while( macIndex < commandsSize )
2097     {
2098         // Make sure to parse only complete MAC commands
2099         if( ( LoRaMacCommandsGetCmdSize( payload[macIndex] ) + macIndex ) > commandsSize )
2100         {
2101             return;
2102         }
2103 
2104         // Decode Frame MAC commands
2105         switch( payload[macIndex++] )
2106         {
2107             case SRV_MAC_RESET_CONF:
2108             {
2109                 uint8_t serverMinorVersion = payload[macIndex++];
2110 
2111                 // Compare own LoRaWAN Version with server's
2112                 if( Nvm.MacGroup2.Version.Fields.Minor >= serverMinorVersion )
2113                 {
2114                     // If they equal remove the sticky ResetInd MAC-Command.
2115                     if( LoRaMacCommandsGetCmd( MOTE_MAC_RESET_IND, &macCmd) == LORAMAC_COMMANDS_SUCCESS )
2116                     {
2117                         LoRaMacCommandsRemoveCmd( macCmd );
2118                     }
2119                 }
2120                 break;
2121             }
2122             case SRV_MAC_LINK_CHECK_ANS:
2123             {
2124                 if( LoRaMacConfirmQueueIsCmdActive( MLME_LINK_CHECK ) == true )
2125                 {
2126                     LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_LINK_CHECK );
2127                     MacCtx.MlmeConfirm.DemodMargin = payload[macIndex++];
2128                     MacCtx.MlmeConfirm.NbGateways = payload[macIndex++];
2129                 }
2130                 break;
2131             }
2132             case SRV_MAC_LINK_ADR_REQ:
2133             {
2134                 LinkAdrReqParams_t linkAdrReq;
2135                 int8_t linkAdrDatarate = DR_0;
2136                 int8_t linkAdrTxPower = TX_POWER_0;
2137                 uint8_t linkAdrNbRep = 0;
2138                 uint8_t linkAdrNbBytesParsed = 0;
2139 
2140                 // The end node is allowed to process one block of LinkAdrRequests.
2141                 // It must ignore subsequent blocks
2142                 if( adrBlockFound == false )
2143                 {
2144                     adrBlockFound = true;
2145 
2146                     do
2147                     {
2148                         // Fill parameter structure
2149                         linkAdrReq.Payload = &payload[macIndex - 1];
2150                         linkAdrReq.AdrEnabled = Nvm.MacGroup2.AdrCtrlOn;
2151                         linkAdrReq.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime;
2152                         linkAdrReq.CurrentDatarate = Nvm.MacGroup1.ChannelsDatarate;
2153                         linkAdrReq.CurrentTxPower = Nvm.MacGroup1.ChannelsTxPower;
2154                         linkAdrReq.CurrentNbRep = Nvm.MacGroup2.MacParams.ChannelsNbTrans;
2155                         linkAdrReq.Version = Nvm.MacGroup2.Version;
2156 
2157                         // There is a fundamental difference in reporting the status
2158                         // of the LinkAdrRequests when ADR is on or off. When ADR is on, every
2159                         // LinkAdrAns contains the same value. This does not hold when ADR is off,
2160                         // where every LinkAdrAns requires an individual status.
2161                         if( Nvm.MacGroup2.AdrCtrlOn == true )
2162                         {
2163                             // When ADR is on, the function RegionLinkAdrReq will take care
2164                             // about the parsing and interpretation of the LinkAdrRequest block and
2165                             // it provides one status which shall be applied to every LinkAdrAns
2166                             linkAdrReq.PayloadSize = commandsSize - ( macIndex - 1 );
2167                         }
2168                         else
2169                         {
2170                             // When ADR is off, this function will loop over the individual LinkAdrRequests
2171                             // and will call RegionLinkAdrReq for each individually, as every request
2172                             // requires an individual answer.
2173                             // When ADR is off, the function RegionLinkAdrReq ignores the new values for
2174                             // ChannelsDatarate, ChannelsTxPower and ChannelsNbTrans.
2175                             linkAdrReq.PayloadSize = 5;
2176                         }
2177 
2178                         // Process the ADR requests
2179                         status = RegionLinkAdrReq( Nvm.MacGroup2.Region, &linkAdrReq, &linkAdrDatarate,
2180                                                 &linkAdrTxPower, &linkAdrNbRep, &linkAdrNbBytesParsed );
2181 
2182                         if( ( status & 0x07 ) == 0x07 )
2183                         {
2184                             // Set the status that the datarate has been increased
2185                             if( linkAdrDatarate > Nvm.MacGroup1.ChannelsDatarate )
2186                             {
2187                                 Nvm.MacGroup2.ChannelsDatarateChangedLinkAdrReq = true;
2188                             }
2189                             Nvm.MacGroup1.ChannelsDatarate = linkAdrDatarate;
2190                             Nvm.MacGroup1.ChannelsTxPower = linkAdrTxPower;
2191                             Nvm.MacGroup2.MacParams.ChannelsNbTrans = linkAdrNbRep;
2192                         }
2193 
2194                         // Add the answers to the buffer
2195                         for( uint8_t i = 0; i < ( linkAdrNbBytesParsed / 5 ); i++ )
2196                         {
2197                             LoRaMacCommandsAddCmd( MOTE_MAC_LINK_ADR_ANS, &status, 1 );
2198                         }
2199                         // Update MAC index
2200                         macIndex += linkAdrNbBytesParsed - 1;
2201 
2202                         // Check to prevent invalid access
2203                         if( macIndex >= commandsSize )
2204                             break;
2205 
2206                     } while( payload[macIndex++] == SRV_MAC_LINK_ADR_REQ );
2207 
2208                     if( macIndex < commandsSize )
2209                     {
2210                         // Decrease the index such that it points to the next MAC command
2211                         macIndex--;
2212                     }
2213                 }
2214                 else
2215                 {
2216                     // Increase the index by the MAC command size (without command)
2217                     macIndex += 4;
2218                 }
2219                 break;
2220             }
2221             case SRV_MAC_DUTY_CYCLE_REQ:
2222             {
2223                 Nvm.MacGroup2.MaxDCycle = payload[macIndex++] & 0x0F;
2224                 Nvm.MacGroup2.AggregatedDCycle = 1 << Nvm.MacGroup2.MaxDCycle;
2225                 LoRaMacCommandsAddCmd( MOTE_MAC_DUTY_CYCLE_ANS, macCmdPayload, 0 );
2226                 break;
2227             }
2228             case SRV_MAC_RX_PARAM_SETUP_REQ:
2229             {
2230                 RxParamSetupReqParams_t rxParamSetupReq;
2231                 status = 0x07;
2232 
2233                 rxParamSetupReq.DrOffset = ( payload[macIndex] >> 4 ) & 0x07;
2234                 rxParamSetupReq.Datarate = payload[macIndex] & 0x0F;
2235                 macIndex++;
2236 
2237                 rxParamSetupReq.Frequency = ( uint32_t ) payload[macIndex++];
2238                 rxParamSetupReq.Frequency |= ( uint32_t ) payload[macIndex++] << 8;
2239                 rxParamSetupReq.Frequency |= ( uint32_t ) payload[macIndex++] << 16;
2240                 rxParamSetupReq.Frequency *= 100;
2241 
2242                 // Perform request on region
2243                 status = RegionRxParamSetupReq( Nvm.MacGroup2.Region, &rxParamSetupReq );
2244 
2245                 if( ( status & 0x07 ) == 0x07 )
2246                 {
2247                     Nvm.MacGroup2.MacParams.Rx2Channel.Datarate = rxParamSetupReq.Datarate;
2248                     Nvm.MacGroup2.MacParams.RxCChannel.Datarate = rxParamSetupReq.Datarate;
2249                     Nvm.MacGroup2.MacParams.Rx2Channel.Frequency = rxParamSetupReq.Frequency;
2250                     Nvm.MacGroup2.MacParams.RxCChannel.Frequency = rxParamSetupReq.Frequency;
2251                     Nvm.MacGroup2.MacParams.Rx1DrOffset = rxParamSetupReq.DrOffset;
2252                 }
2253                 macCmdPayload[0] = status;
2254                 LoRaMacCommandsAddCmd( MOTE_MAC_RX_PARAM_SETUP_ANS, macCmdPayload, 1 );
2255                 break;
2256             }
2257             case SRV_MAC_DEV_STATUS_REQ:
2258             {
2259                 uint8_t batteryLevel = BAT_LEVEL_NO_MEASURE;
2260                 if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->GetBatteryLevel != NULL ) )
2261                 {
2262                     batteryLevel = MacCtx.MacCallbacks->GetBatteryLevel( );
2263                 }
2264                 macCmdPayload[0] = batteryLevel;
2265                 macCmdPayload[1] = ( uint8_t )( snr & 0x3F );
2266                 LoRaMacCommandsAddCmd( MOTE_MAC_DEV_STATUS_ANS, macCmdPayload, 2 );
2267                 break;
2268             }
2269             case SRV_MAC_NEW_CHANNEL_REQ:
2270             {
2271                 NewChannelReqParams_t newChannelReq;
2272                 ChannelParams_t chParam;
2273                 status = 0x03;
2274 
2275                 newChannelReq.ChannelId = payload[macIndex++];
2276                 newChannelReq.NewChannel = &chParam;
2277 
2278                 chParam.Frequency = ( uint32_t ) payload[macIndex++];
2279                 chParam.Frequency |= ( uint32_t ) payload[macIndex++] << 8;
2280                 chParam.Frequency |= ( uint32_t ) payload[macIndex++] << 16;
2281                 chParam.Frequency *= 100;
2282                 chParam.Rx1Frequency = 0;
2283                 chParam.DrRange.Value = payload[macIndex++];
2284 
2285                 status = ( uint8_t )RegionNewChannelReq( Nvm.MacGroup2.Region, &newChannelReq );
2286 
2287                 if( ( int8_t )status >= 0 )
2288                 {
2289                     macCmdPayload[0] = status;
2290                     LoRaMacCommandsAddCmd( MOTE_MAC_NEW_CHANNEL_ANS, macCmdPayload, 1 );
2291                 }
2292                 break;
2293             }
2294             case SRV_MAC_RX_TIMING_SETUP_REQ:
2295             {
2296                 uint8_t delay = payload[macIndex++] & 0x0F;
2297 
2298                 if( delay == 0 )
2299                 {
2300                     delay++;
2301                 }
2302                 Nvm.MacGroup2.MacParams.ReceiveDelay1 = delay * 1000;
2303                 Nvm.MacGroup2.MacParams.ReceiveDelay2 = Nvm.MacGroup2.MacParams.ReceiveDelay1 + 1000;
2304                 LoRaMacCommandsAddCmd( MOTE_MAC_RX_TIMING_SETUP_ANS, macCmdPayload, 0 );
2305                 break;
2306             }
2307             case SRV_MAC_TX_PARAM_SETUP_REQ:
2308             {
2309                 TxParamSetupReqParams_t txParamSetupReq;
2310                 GetPhyParams_t getPhy;
2311                 PhyParam_t phyParam;
2312                 uint8_t eirpDwellTime = payload[macIndex++];
2313 
2314                 txParamSetupReq.UplinkDwellTime = 0;
2315                 txParamSetupReq.DownlinkDwellTime = 0;
2316 
2317                 if( ( eirpDwellTime & 0x20 ) == 0x20 )
2318                 {
2319                     txParamSetupReq.DownlinkDwellTime = 1;
2320                 }
2321                 if( ( eirpDwellTime & 0x10 ) == 0x10 )
2322                 {
2323                     txParamSetupReq.UplinkDwellTime = 1;
2324                 }
2325                 txParamSetupReq.MaxEirp = eirpDwellTime & 0x0F;
2326 
2327                 // Check the status for correctness
2328                 if( RegionTxParamSetupReq( Nvm.MacGroup2.Region, &txParamSetupReq ) != -1 )
2329                 {
2330                     // Accept command
2331                     Nvm.MacGroup2.MacParams.UplinkDwellTime = txParamSetupReq.UplinkDwellTime;
2332                     Nvm.MacGroup2.MacParams.DownlinkDwellTime = txParamSetupReq.DownlinkDwellTime;
2333                     Nvm.MacGroup2.MacParams.MaxEirp = LoRaMacMaxEirpTable[txParamSetupReq.MaxEirp];
2334                     // Update the datarate in case of the new configuration limits it
2335                     getPhy.Attribute = PHY_MIN_TX_DR;
2336                     getPhy.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime;
2337                     phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
2338                     Nvm.MacGroup1.ChannelsDatarate = MAX( Nvm.MacGroup1.ChannelsDatarate, ( int8_t )phyParam.Value );
2339 
2340                     // Add command response
2341                     LoRaMacCommandsAddCmd( MOTE_MAC_TX_PARAM_SETUP_ANS, macCmdPayload, 0 );
2342                 }
2343                 break;
2344             }
2345             case SRV_MAC_REKEY_CONF:
2346             {
2347                 uint8_t serverMinorVersion = payload[macIndex++];
2348 
2349                 // Compare own LoRaWAN Version with server's
2350                 if( Nvm.MacGroup2.Version.Fields.Minor >= serverMinorVersion )
2351                 {
2352                     // If they equal remove the sticky RekeyInd MAC-Command.
2353                     if( LoRaMacCommandsGetCmd( MOTE_MAC_REKEY_IND, &macCmd) == LORAMAC_COMMANDS_SUCCESS )
2354                     {
2355                         LoRaMacCommandsRemoveCmd( macCmd );
2356                     }
2357                 }
2358                 break;
2359             }
2360             case SRV_MAC_DL_CHANNEL_REQ:
2361             {
2362                 DlChannelReqParams_t dlChannelReq;
2363                 status = 0x03;
2364 
2365                 dlChannelReq.ChannelId = payload[macIndex++];
2366                 dlChannelReq.Rx1Frequency = ( uint32_t ) payload[macIndex++];
2367                 dlChannelReq.Rx1Frequency |= ( uint32_t ) payload[macIndex++] << 8;
2368                 dlChannelReq.Rx1Frequency |= ( uint32_t ) payload[macIndex++] << 16;
2369                 dlChannelReq.Rx1Frequency *= 100;
2370 
2371                 status = ( uint8_t )RegionDlChannelReq( Nvm.MacGroup2.Region, &dlChannelReq );
2372 
2373                 if( ( int8_t )status >= 0 )
2374                 {
2375                     macCmdPayload[0] = status;
2376                     LoRaMacCommandsAddCmd( MOTE_MAC_DL_CHANNEL_ANS, macCmdPayload, 1 );
2377                 }
2378                 break;
2379             }
2380             case SRV_MAC_ADR_PARAM_SETUP_REQ:
2381             {
2382                 /* ADRParamSetupReq Payload:  ADRparam
2383                  * +----------------+---------------+
2384                  * | 7:4 Limit_exp  | 3:0 Delay_exp |
2385                  * +----------------+---------------+
2386                  */
2387 
2388                 uint8_t delayExp = 0x0F & payload[macIndex];
2389                 uint8_t limitExp = 0x0F & ( payload[macIndex] >> 4 );
2390                 macIndex++;
2391 
2392                 // ADR_ACK_ DELAY = 2^Delay_exp
2393                 MacCtx.AdrAckDelay = 0x01 << delayExp;
2394 
2395                 // ADR_ACK_LIMIT = 2^Limit_exp
2396                 MacCtx.AdrAckLimit = 0x01 << limitExp;
2397 
2398                 LoRaMacCommandsAddCmd( MOTE_MAC_ADR_PARAM_SETUP_ANS, macCmdPayload, 0 );
2399                 break;
2400             }
2401             case SRV_MAC_FORCE_REJOIN_REQ:
2402             {
2403                 /* ForceRejoinReq Payload:
2404                  * +--------------+------------------+-------+----------------+--------+
2405                  * | 13:11 Period | 10:8 Max_Retries | 7 RFU | 6:4 RejoinType | 3:0 DR |
2406                  * +--------------+------------------+-------+----------------+--------+
2407                  */
2408 
2409                 // Parse payload
2410                 uint8_t period = ( 0x38 & payload[macIndex] ) >> 3;
2411                 Nvm.MacGroup2.ForceRejoinMaxRetries = 0x07 & payload[macIndex];
2412                 macIndex++;
2413                 Nvm.MacGroup2.ForceRejoinType = ( 0x70 & payload[macIndex] ) >> 4;
2414                 Nvm.MacGroup1.ChannelsDatarate = 0x0F & payload[macIndex];
2415                 macIndex ++;
2416 
2417                 // Calc delay between retransmissions: 32 seconds x 2^Period + Rand32
2418                 uint32_t rejoinCycleInSec = 32 * ( 0x01 << period ) + randr( 0, 32 );
2419 
2420                 uint32_t forceRejoinReqCycleTime = 0;
2421                 ConvertRejoinCycleTime( rejoinCycleInSec, &forceRejoinReqCycleTime );
2422                 TimerSetValue( &MacCtx.ForceRejoinReqCycleTimer, forceRejoinReqCycleTime );
2423 
2424                 if( ( Nvm.MacGroup2.ForceRejoinType == 0 ) || ( Nvm.MacGroup2.ForceRejoinType == 1 ) )
2425                 {
2426                     SendReJoinReq( REJOIN_REQ_0 );
2427                 }
2428                 else
2429                 {
2430                     SendReJoinReq( REJOIN_REQ_2 );
2431                 }
2432                 TimerStart( &MacCtx.ForceRejoinReqCycleTimer );
2433                 break;
2434             }
2435             case SRV_MAC_REJOIN_PARAM_REQ:
2436             {
2437                 /* RejoinParamSetupReq Payload:
2438                  * +----------------+---------------+
2439                  * | 7:4 MaxTimeN   | 3:0 MaxCountN |
2440                  * +----------------+---------------+
2441                  */
2442 
2443                 uint8_t timeOK = 0;
2444                 uint8_t maxCountN = 0x0F & payload[macIndex];
2445                 uint8_t maxTimeN = 0x0F & ( payload[macIndex] >> 4 );
2446                 macIndex ++;
2447 
2448                 // Calc delay between retransmissions: 2^(maxTimeN+10)
2449                 Nvm.MacGroup2.Rejoin0CycleInSec = 0x01 << ( maxTimeN + 10 );
2450 
2451                 // Calc number if uplinks without rejoin request: 2^(maxCountN+4)
2452                 Nvm.MacGroup2.Rejoin0UplinksLimit = 0x01 << ( maxCountN + 4 );
2453 
2454                 if( ConvertRejoinCycleTime( Nvm.MacGroup2.Rejoin0CycleInSec, &MacCtx.Rejoin0CycleTime ) == true )
2455                 {
2456                     timeOK = 0x01;
2457                     TimerStop( &MacCtx.Rejoin0CycleTimer );
2458                     TimerSetValue( &MacCtx.Rejoin0CycleTimer, MacCtx.Rejoin0CycleTime );
2459                     TimerStart( &MacCtx.Rejoin0CycleTimer );
2460                 }
2461                 macCmdPayload[0] = timeOK;
2462                 LoRaMacCommandsAddCmd( MOTE_MAC_REJOIN_PARAM_ANS, macCmdPayload, 1 );
2463                 break;
2464              }
2465             case SRV_MAC_DEVICE_MODE_CONF:
2466             {
2467                 // 1 byte payload which we do not handle.
2468                 macIndex++;
2469                 if( LoRaMacCommandsGetCmd( MOTE_MAC_DEVICE_MODE_IND, &macCmd) == LORAMAC_COMMANDS_SUCCESS )
2470                 {
2471                     LoRaMacCommandsRemoveCmd( macCmd );
2472                 }
2473                 break;
2474             }
2475             case SRV_MAC_DEVICE_TIME_ANS:
2476             {
2477                 // The mote time can be updated only when the time is received in classA
2478                 // receive windows only.
2479                 if( LoRaMacConfirmQueueIsCmdActive( MLME_DEVICE_TIME ) == true )
2480                 {
2481                     LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_DEVICE_TIME );
2482 
2483                     SysTime_t gpsEpochTime = { 0 };
2484                     SysTime_t sysTime = { 0 };
2485                     SysTime_t sysTimeCurrent = { 0 };
2486 
2487                     gpsEpochTime.Seconds = ( uint32_t )payload[macIndex++];
2488                     gpsEpochTime.Seconds |= ( uint32_t )payload[macIndex++] << 8;
2489                     gpsEpochTime.Seconds |= ( uint32_t )payload[macIndex++] << 16;
2490                     gpsEpochTime.Seconds |= ( uint32_t )payload[macIndex++] << 24;
2491                     gpsEpochTime.SubSeconds = payload[macIndex++];
2492 
2493                     // Convert the fractional second received in ms
2494                     // round( pow( 0.5, 8.0 ) * 1000 ) = 3.90625
2495                     gpsEpochTime.SubSeconds = ( int16_t )( ( ( int32_t )gpsEpochTime.SubSeconds * 1000 ) >> 8 );
2496 
2497                     // Copy received GPS Epoch time into system time
2498                     sysTime = gpsEpochTime;
2499                     // Add Unix to Gps epoch offset. The system time is based on Unix time.
2500                     sysTime.Seconds += UNIX_GPS_EPOCH_OFFSET;
2501 
2502                     // Compensate time difference between Tx Done time and now
2503                     sysTimeCurrent = SysTimeGet( );
2504                     sysTime = SysTimeAdd( sysTimeCurrent, SysTimeSub( sysTime, MacCtx.LastTxSysTime ) );
2505 
2506                     // Apply the new system time.
2507                     SysTimeSet( sysTime );
2508                     LoRaMacClassBDeviceTimeAns( );
2509                     MacCtx.McpsIndication.DeviceTimeAnsReceived = true;
2510                 }
2511                 else
2512                 {
2513                     // Incase of other receive windows the Device Time Answer is not received.
2514                     MacCtx.McpsIndication.DeviceTimeAnsReceived = false;
2515                 }
2516                 break;
2517             }
2518             case SRV_MAC_PING_SLOT_INFO_ANS:
2519             {
2520                 if( LoRaMacConfirmQueueIsCmdActive( MLME_PING_SLOT_INFO ) == true )
2521                 {
2522                     LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_PING_SLOT_INFO );
2523                     // According to the specification, it is not allowed to process this answer in
2524                     // a ping or multicast slot
2525                     if( ( MacCtx.RxSlot != RX_SLOT_WIN_CLASS_B_PING_SLOT ) && ( MacCtx.RxSlot != RX_SLOT_WIN_CLASS_B_MULTICAST_SLOT ) )
2526                     {
2527                         LoRaMacClassBPingSlotInfoAns( );
2528                     }
2529                 }
2530                 break;
2531             }
2532             case SRV_MAC_PING_SLOT_CHANNEL_REQ:
2533             {
2534                 uint8_t status = 0x03;
2535                 uint32_t frequency = 0;
2536                 uint8_t datarate;
2537 
2538                 frequency = ( uint32_t )payload[macIndex++];
2539                 frequency |= ( uint32_t )payload[macIndex++] << 8;
2540                 frequency |= ( uint32_t )payload[macIndex++] << 16;
2541                 frequency *= 100;
2542                 datarate = payload[macIndex++] & 0x0F;
2543 
2544                 status = LoRaMacClassBPingSlotChannelReq( datarate, frequency );
2545                 macCmdPayload[0] = status;
2546                 LoRaMacCommandsAddCmd( MOTE_MAC_PING_SLOT_CHANNEL_ANS, macCmdPayload, 1 );
2547                 break;
2548             }
2549             case SRV_MAC_BEACON_TIMING_ANS:
2550             {
2551                 if( LoRaMacConfirmQueueIsCmdActive( MLME_BEACON_TIMING ) == true )
2552                 {
2553                     LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_BEACON_TIMING );
2554                     uint16_t beaconTimingDelay = 0;
2555                     uint8_t beaconTimingChannel = 0;
2556 
2557                     beaconTimingDelay = ( uint16_t )payload[macIndex++];
2558                     beaconTimingDelay |= ( uint16_t )payload[macIndex++] << 8;
2559                     beaconTimingChannel = payload[macIndex++];
2560 
2561                     LoRaMacClassBBeaconTimingAns( beaconTimingDelay, beaconTimingChannel, RxDoneParams.LastRxDone );
2562                 }
2563                 break;
2564             }
2565             case SRV_MAC_BEACON_FREQ_REQ:
2566                 {
2567                     uint32_t frequency = 0;
2568 
2569                     frequency = ( uint32_t )payload[macIndex++];
2570                     frequency |= ( uint32_t )payload[macIndex++] << 8;
2571                     frequency |= ( uint32_t )payload[macIndex++] << 16;
2572                     frequency *= 100;
2573 
2574                     if( LoRaMacClassBBeaconFreqReq( frequency ) == true )
2575                     {
2576                         macCmdPayload[0] = 1;
2577                     }
2578                     else
2579                     {
2580                         macCmdPayload[0] = 0;
2581                     }
2582                     LoRaMacCommandsAddCmd( MOTE_MAC_BEACON_FREQ_ANS, macCmdPayload, 1 );
2583                 }
2584                 break;
2585             default:
2586                 // Unknown command. ABORT MAC commands processing
2587                 return;
2588         }
2589     }
2590 }
2591 
Send(LoRaMacHeader_t * macHdr,uint8_t fPort,void * fBuffer,uint16_t fBufferSize)2592 LoRaMacStatus_t Send( LoRaMacHeader_t* macHdr, uint8_t fPort, void* fBuffer, uint16_t fBufferSize )
2593 {
2594     LoRaMacFrameCtrl_t fCtrl;
2595     LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID;
2596     int8_t datarate = Nvm.MacGroup1.ChannelsDatarate;
2597     int8_t txPower = Nvm.MacGroup1.ChannelsTxPower;
2598     uint32_t adrAckCounter = Nvm.MacGroup1.AdrAckCounter;
2599     CalcNextAdrParams_t adrNext;
2600 
2601     // Check if we are joined
2602     if( Nvm.MacGroup2.NetworkActivation == ACTIVATION_TYPE_NONE )
2603     {
2604         return LORAMAC_STATUS_NO_NETWORK_JOINED;
2605     }
2606     if( Nvm.MacGroup2.MaxDCycle == 0 )
2607     {
2608         Nvm.MacGroup1.AggregatedTimeOff = 0;
2609     }
2610 
2611     fCtrl.Value = 0;
2612     fCtrl.Bits.FOptsLen      = 0;
2613     fCtrl.Bits.Adr           = Nvm.MacGroup2.AdrCtrlOn;
2614 
2615     // Check class b
2616     if( Nvm.MacGroup2.DeviceClass == CLASS_B )
2617     {
2618         fCtrl.Bits.FPending      = 1;
2619     }
2620     else
2621     {
2622         fCtrl.Bits.FPending      = 0;
2623     }
2624 
2625     // Check server ack
2626     if( Nvm.MacGroup1.SrvAckRequested == true )
2627     {
2628         fCtrl.Bits.Ack = 1;
2629     }
2630 
2631     // ADR next request
2632     adrNext.UpdateChanMask = true;
2633     adrNext.AdrEnabled = fCtrl.Bits.Adr;
2634     adrNext.AdrAckCounter = Nvm.MacGroup1.AdrAckCounter;
2635     adrNext.AdrAckLimit = MacCtx.AdrAckLimit;
2636     adrNext.AdrAckDelay = MacCtx.AdrAckDelay;
2637     adrNext.Datarate = Nvm.MacGroup1.ChannelsDatarate;
2638     adrNext.TxPower = Nvm.MacGroup1.ChannelsTxPower;
2639     adrNext.NbTrans = Nvm.MacGroup2.MacParams.ChannelsNbTrans;
2640     adrNext.UplinkDwellTime =  Nvm.MacGroup2.MacParams.UplinkDwellTime;
2641     adrNext.Region = Nvm.MacGroup2.Region;
2642 
2643     fCtrl.Bits.AdrAckReq = LoRaMacAdrCalcNext( &adrNext, &Nvm.MacGroup1.ChannelsDatarate,
2644                                                &Nvm.MacGroup1.ChannelsTxPower,
2645                                                &Nvm.MacGroup2.MacParams.ChannelsNbTrans, &adrAckCounter );
2646 
2647     // Prepare the frame
2648     status = PrepareFrame( macHdr, &fCtrl, fPort, fBuffer, fBufferSize );
2649 
2650     // Validate status
2651     if( ( status == LORAMAC_STATUS_OK ) || ( status == LORAMAC_STATUS_SKIPPED_APP_DATA ) )
2652     {
2653         // Schedule frame, do not allow delayed transmissions
2654         status = ScheduleTx( false );
2655     }
2656 
2657     // Post processing
2658     if( status != LORAMAC_STATUS_OK )
2659     {
2660         // Bad case - restore
2661         // Store local variables
2662         Nvm.MacGroup1.ChannelsDatarate = datarate;
2663         Nvm.MacGroup1.ChannelsTxPower = txPower;
2664     }
2665     else
2666     {
2667         // Good case
2668         Nvm.MacGroup1.SrvAckRequested = false;
2669         Nvm.MacGroup1.AdrAckCounter = adrAckCounter;
2670         // Remove all none sticky MAC commands
2671         if( LoRaMacCommandsRemoveNoneStickyCmds( ) != LORAMAC_COMMANDS_SUCCESS )
2672         {
2673             return LORAMAC_STATUS_MAC_COMMAD_ERROR;
2674         }
2675     }
2676     return status;
2677 }
2678 
SendReJoinReq(JoinReqIdentifier_t joinReqType)2679 LoRaMacStatus_t SendReJoinReq( JoinReqIdentifier_t joinReqType )
2680 {
2681     LoRaMacStatus_t status = LORAMAC_STATUS_OK;
2682     LoRaMacHeader_t macHdr;
2683     macHdr.Value = 0;
2684     bool allowDelayedTx = true;
2685 
2686     // Setup join/rejoin message
2687     switch( joinReqType )
2688     {
2689         case REJOIN_REQ_1:
2690         {
2691             Nvm.MacGroup2.IsRejoinAcceptPending = true;
2692 
2693             MacCtx.TxMsg.Type = LORAMAC_MSG_TYPE_RE_JOIN_1;
2694             MacCtx.TxMsg.Message.ReJoin1.Buffer = MacCtx.PktBuffer;
2695             MacCtx.TxMsg.Message.ReJoin1.BufSize = LORAMAC_PHY_MAXPAYLOAD;
2696 
2697             macHdr.Bits.MType = FRAME_TYPE_REJOIN;
2698             MacCtx.TxMsg.Message.ReJoin1.MHDR.Value = macHdr.Value;
2699 
2700             MacCtx.TxMsg.Message.ReJoin1.ReJoinType = 1;
2701 
2702             memcpy1( MacCtx.TxMsg.Message.ReJoin1.JoinEUI, SecureElementGetJoinEui( ), LORAMAC_JOIN_EUI_FIELD_SIZE );
2703             memcpy1( MacCtx.TxMsg.Message.ReJoin1.DevEUI, SecureElementGetDevEui( ), LORAMAC_DEV_EUI_FIELD_SIZE );
2704 
2705             if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoGetRJcount( RJ_COUNT_1, &MacCtx.TxMsg.Message.ReJoin1.RJcount1 ) )
2706             {
2707                 return LORAMAC_STATUS_CRYPTO_ERROR;
2708             }
2709 
2710             break;
2711         }
2712         case REJOIN_REQ_0:
2713         case REJOIN_REQ_2:
2714         {
2715             if( joinReqType == REJOIN_REQ_0 )
2716             {
2717                 MacCtx.TxMsg.Message.ReJoin0or2.ReJoinType = 0;
2718             }
2719             else
2720             {
2721                 MacCtx.TxMsg.Message.ReJoin0or2.ReJoinType = 2;
2722             }
2723 
2724             Nvm.MacGroup2.IsRejoinAcceptPending = true;
2725 
2726             MacCtx.TxMsg.Type = LORAMAC_MSG_TYPE_RE_JOIN_0_2;
2727             MacCtx.TxMsg.Message.ReJoin0or2.Buffer = MacCtx.PktBuffer;
2728             MacCtx.TxMsg.Message.ReJoin0or2.BufSize = LORAMAC_PHY_MAXPAYLOAD;
2729 
2730             macHdr.Bits.MType = FRAME_TYPE_REJOIN;
2731             MacCtx.TxMsg.Message.ReJoin0or2.MHDR.Value = macHdr.Value;
2732 
2733             MacCtx.TxMsg.Message.ReJoin0or2.NetID[0] = Nvm.MacGroup2.NetID & 0xFF;
2734             MacCtx.TxMsg.Message.ReJoin0or2.NetID[1] = ( Nvm.MacGroup2.NetID >> 8 ) & 0xFF;
2735             MacCtx.TxMsg.Message.ReJoin0or2.NetID[2] = ( Nvm.MacGroup2.NetID >> 16 ) & 0xFF;
2736 
2737             memcpy1( MacCtx.TxMsg.Message.ReJoin0or2.DevEUI, SecureElementGetDevEui( ), LORAMAC_DEV_EUI_FIELD_SIZE );
2738 
2739             if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoGetRJcount( RJ_COUNT_0, &MacCtx.TxMsg.Message.ReJoin0or2.RJcount0 ) )
2740             {
2741                 return LORAMAC_STATUS_CRYPTO_ERROR;
2742             }
2743 
2744             break;
2745         }
2746         case JOIN_REQ:
2747         {
2748             SwitchClass( CLASS_A );
2749 
2750             MacCtx.TxMsg.Type = LORAMAC_MSG_TYPE_JOIN_REQUEST;
2751             MacCtx.TxMsg.Message.JoinReq.Buffer = MacCtx.PktBuffer;
2752             MacCtx.TxMsg.Message.JoinReq.BufSize = LORAMAC_PHY_MAXPAYLOAD;
2753 
2754             macHdr.Bits.MType = FRAME_TYPE_JOIN_REQ;
2755             MacCtx.TxMsg.Message.JoinReq.MHDR.Value = macHdr.Value;
2756 
2757             memcpy1( MacCtx.TxMsg.Message.JoinReq.JoinEUI, SecureElementGetJoinEui( ), LORAMAC_JOIN_EUI_FIELD_SIZE );
2758             memcpy1( MacCtx.TxMsg.Message.JoinReq.DevEUI, SecureElementGetDevEui( ), LORAMAC_DEV_EUI_FIELD_SIZE );
2759 
2760             allowDelayedTx = false;
2761 
2762             break;
2763         }
2764         default:
2765             status = LORAMAC_STATUS_SERVICE_UNKNOWN;
2766             break;
2767     }
2768 
2769     // Schedule frame
2770     status = ScheduleTx( allowDelayedTx );
2771     return status;
2772 }
2773 
CheckForClassBCollision(void)2774 static LoRaMacStatus_t CheckForClassBCollision( void )
2775 {
2776     if( LoRaMacClassBIsBeaconExpected( ) == true )
2777     {
2778         return LORAMAC_STATUS_BUSY_BEACON_RESERVED_TIME;
2779     }
2780 
2781     if( Nvm.MacGroup2.DeviceClass == CLASS_B )
2782     {
2783         if( LoRaMacClassBIsPingExpected( ) == true )
2784         {
2785             return LORAMAC_STATUS_BUSY_PING_SLOT_WINDOW_TIME;
2786         }
2787         else if( LoRaMacClassBIsMulticastExpected( ) == true )
2788         {
2789             return LORAMAC_STATUS_BUSY_PING_SLOT_WINDOW_TIME;
2790         }
2791     }
2792     return LORAMAC_STATUS_OK;
2793 }
2794 
ComputeRxWindowParameters(void)2795 static void ComputeRxWindowParameters( void )
2796 {
2797     // Compute Rx1 windows parameters
2798     RegionComputeRxWindowParameters( Nvm.MacGroup2.Region,
2799                                      RegionApplyDrOffset( Nvm.MacGroup2.Region,
2800                                                           Nvm.MacGroup2.MacParams.DownlinkDwellTime,
2801                                                           Nvm.MacGroup1.ChannelsDatarate,
2802                                                           Nvm.MacGroup2.MacParams.Rx1DrOffset ),
2803                                      Nvm.MacGroup2.MacParams.MinRxSymbols,
2804                                      Nvm.MacGroup2.MacParams.SystemMaxRxError,
2805                                      &MacCtx.RxWindow1Config );
2806     // Compute Rx2 windows parameters
2807     RegionComputeRxWindowParameters( Nvm.MacGroup2.Region,
2808                                      Nvm.MacGroup2.MacParams.Rx2Channel.Datarate,
2809                                      Nvm.MacGroup2.MacParams.MinRxSymbols,
2810                                      Nvm.MacGroup2.MacParams.SystemMaxRxError,
2811                                      &MacCtx.RxWindow2Config );
2812 
2813     // Default setup, in case the device joined
2814     MacCtx.RxWindow1Delay = Nvm.MacGroup2.MacParams.ReceiveDelay1 + MacCtx.RxWindow1Config.WindowOffset;
2815     MacCtx.RxWindow2Delay = Nvm.MacGroup2.MacParams.ReceiveDelay2 + MacCtx.RxWindow2Config.WindowOffset;
2816 
2817     if( Nvm.MacGroup2.NetworkActivation == ACTIVATION_TYPE_NONE )
2818     {
2819         MacCtx.RxWindow1Delay = Nvm.MacGroup2.MacParams.JoinAcceptDelay1 + MacCtx.RxWindow1Config.WindowOffset;
2820         MacCtx.RxWindow2Delay = Nvm.MacGroup2.MacParams.JoinAcceptDelay2 + MacCtx.RxWindow2Config.WindowOffset;
2821     }
2822 }
2823 
VerifyTxFrame(void)2824 static LoRaMacStatus_t VerifyTxFrame( void )
2825 {
2826     size_t macCmdsSize = 0;
2827 
2828     if( Nvm.MacGroup2.NetworkActivation != ACTIVATION_TYPE_NONE )
2829     {
2830         if( LoRaMacCommandsGetSizeSerializedCmds( &macCmdsSize ) != LORAMAC_COMMANDS_SUCCESS )
2831         {
2832             return LORAMAC_STATUS_MAC_COMMAD_ERROR;
2833         }
2834 
2835         if( ValidatePayloadLength( MacCtx.AppDataSize, Nvm.MacGroup1.ChannelsDatarate, macCmdsSize ) == false )
2836         {
2837             return LORAMAC_STATUS_LENGTH_ERROR;
2838         }
2839     }
2840     return LORAMAC_STATUS_OK;
2841 }
2842 
SerializeTxFrame(void)2843 static LoRaMacStatus_t SerializeTxFrame( void )
2844 {
2845     LoRaMacSerializerStatus_t serializeStatus;
2846 
2847     switch( MacCtx.TxMsg.Type )
2848     {
2849         case LORAMAC_MSG_TYPE_JOIN_REQUEST:
2850             serializeStatus = LoRaMacSerializerJoinRequest( &MacCtx.TxMsg.Message.JoinReq );
2851             if( LORAMAC_SERIALIZER_SUCCESS != serializeStatus )
2852             {
2853                 return LORAMAC_STATUS_CRYPTO_ERROR;
2854             }
2855             MacCtx.PktBufferLen = MacCtx.TxMsg.Message.JoinReq.BufSize;
2856             break;
2857         case LORAMAC_MSG_TYPE_DATA:
2858             serializeStatus = LoRaMacSerializerData( &MacCtx.TxMsg.Message.Data );
2859             if( LORAMAC_SERIALIZER_SUCCESS != serializeStatus )
2860             {
2861                 return LORAMAC_STATUS_CRYPTO_ERROR;
2862             }
2863             MacCtx.PktBufferLen = MacCtx.TxMsg.Message.Data.BufSize;
2864             break;
2865         case LORAMAC_MSG_TYPE_JOIN_ACCEPT:
2866         case LORAMAC_MSG_TYPE_UNDEF:
2867         default:
2868             return LORAMAC_STATUS_PARAMETER_INVALID;
2869     }
2870     return LORAMAC_STATUS_OK;
2871 }
2872 
ScheduleTx(bool allowDelayedTx)2873 static LoRaMacStatus_t ScheduleTx( bool allowDelayedTx )
2874 {
2875     LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID;
2876     NextChanParams_t nextChan;
2877 
2878     // Check class b collisions
2879     status = CheckForClassBCollision( );
2880     if( status != LORAMAC_STATUS_OK )
2881     {
2882         return status;
2883     }
2884 
2885     // Update back-off
2886     CalculateBackOff( );
2887 
2888     // Serialize frame
2889     status = SerializeTxFrame( );
2890     if( status != LORAMAC_STATUS_OK )
2891     {
2892         return status;
2893     }
2894 
2895     nextChan.AggrTimeOff = Nvm.MacGroup1.AggregatedTimeOff;
2896     nextChan.Datarate = Nvm.MacGroup1.ChannelsDatarate;
2897     nextChan.DutyCycleEnabled = Nvm.MacGroup2.DutyCycleOn;
2898     nextChan.ElapsedTimeSinceStartUp = SysTimeSub( SysTimeGetMcuTime( ), Nvm.MacGroup2.InitializationTime );
2899     nextChan.LastAggrTx = Nvm.MacGroup1.LastTxDoneTime;
2900     nextChan.LastTxIsJoinRequest = false;
2901     nextChan.Joined = true;
2902     nextChan.PktLen = MacCtx.PktBufferLen;
2903 
2904     // Setup the parameters based on the join status
2905     if( Nvm.MacGroup2.NetworkActivation == ACTIVATION_TYPE_NONE )
2906     {
2907         nextChan.LastTxIsJoinRequest = true;
2908         nextChan.Joined = false;
2909     }
2910 
2911     // Select channel
2912     status = RegionNextChannel( Nvm.MacGroup2.Region, &nextChan, &MacCtx.Channel, &MacCtx.DutyCycleWaitTime, &Nvm.MacGroup1.AggregatedTimeOff );
2913 
2914     if( status != LORAMAC_STATUS_OK )
2915     {
2916         if( ( status == LORAMAC_STATUS_DUTYCYCLE_RESTRICTED ) &&
2917             ( allowDelayedTx == true ) )
2918         {
2919             // Allow delayed transmissions. We have to allow it in case
2920             // the MAC must retransmit a frame with the frame repetitions
2921             if( MacCtx.DutyCycleWaitTime != 0 )
2922             {// Send later - prepare timer
2923                 MacCtx.MacState |= LORAMAC_TX_DELAYED;
2924                 TimerSetValue( &MacCtx.TxDelayedTimer, MacCtx.DutyCycleWaitTime );
2925                 TimerStart( &MacCtx.TxDelayedTimer );
2926             }
2927             return LORAMAC_STATUS_OK;
2928         }
2929         else
2930         {// State where the MAC cannot send a frame
2931             return status;
2932         }
2933     }
2934 
2935     // Compute window parameters, offsets, rx symbols, system errors etc.
2936     ComputeRxWindowParameters( );
2937 
2938     // Verify TX frame
2939     status = VerifyTxFrame( );
2940     if( status != LORAMAC_STATUS_OK )
2941     {
2942         return status;
2943     }
2944 
2945     // Try to send now
2946     return SendFrameOnChannel( MacCtx.Channel );
2947 }
2948 
SecureFrame(uint8_t txDr,uint8_t txCh)2949 static LoRaMacStatus_t SecureFrame( uint8_t txDr, uint8_t txCh )
2950 {
2951     LoRaMacCryptoStatus_t macCryptoStatus = LORAMAC_CRYPTO_ERROR;
2952     uint32_t fCntUp = 0;
2953 
2954     switch( MacCtx.TxMsg.Type )
2955     {
2956         case LORAMAC_MSG_TYPE_JOIN_REQUEST:
2957             macCryptoStatus = LoRaMacCryptoPrepareJoinRequest( &MacCtx.TxMsg.Message.JoinReq );
2958             if( LORAMAC_CRYPTO_SUCCESS != macCryptoStatus )
2959             {
2960                 return LORAMAC_STATUS_CRYPTO_ERROR;
2961             }
2962             MacCtx.PktBufferLen = MacCtx.TxMsg.Message.JoinReq.BufSize;
2963             break;
2964         case LORAMAC_MSG_TYPE_RE_JOIN_1:
2965             macCryptoStatus = LoRaMacCryptoPrepareReJoinType1( &MacCtx.TxMsg.Message.ReJoin1 );
2966             if( LORAMAC_CRYPTO_SUCCESS != macCryptoStatus )
2967             {
2968                 return LORAMAC_STATUS_CRYPTO_ERROR;
2969             }
2970             MacCtx.PktBufferLen = MacCtx.TxMsg.Message.ReJoin1.BufSize;
2971             break;
2972         case LORAMAC_MSG_TYPE_RE_JOIN_0_2:
2973             macCryptoStatus = LoRaMacCryptoPrepareReJoinType0or2( &MacCtx.TxMsg.Message.ReJoin0or2 );
2974             if( LORAMAC_CRYPTO_SUCCESS != macCryptoStatus )
2975             {
2976                 return LORAMAC_STATUS_CRYPTO_ERROR;
2977             }
2978             MacCtx.PktBufferLen = MacCtx.TxMsg.Message.ReJoin0or2.BufSize;
2979             break;
2980         case LORAMAC_MSG_TYPE_DATA:
2981 
2982             if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoGetFCntUp( &fCntUp ) )
2983             {
2984                 return LORAMAC_STATUS_FCNT_HANDLER_ERROR;
2985             }
2986 
2987             if( MacCtx.ChannelsNbTransCounter >= 1 )
2988             {
2989                 fCntUp -= 1;
2990             }
2991 
2992             macCryptoStatus = LoRaMacCryptoSecureMessage( fCntUp, txDr, txCh, &MacCtx.TxMsg.Message.Data );
2993             if( LORAMAC_CRYPTO_SUCCESS != macCryptoStatus )
2994             {
2995                 return LORAMAC_STATUS_CRYPTO_ERROR;
2996             }
2997             MacCtx.PktBufferLen = MacCtx.TxMsg.Message.Data.BufSize;
2998             break;
2999         case LORAMAC_MSG_TYPE_JOIN_ACCEPT:
3000         case LORAMAC_MSG_TYPE_UNDEF:
3001         default:
3002             return LORAMAC_STATUS_PARAMETER_INVALID;
3003     }
3004     return LORAMAC_STATUS_OK;
3005 }
3006 
CalculateBackOff(void)3007 static void CalculateBackOff( void )
3008 {
3009     // Make sure that the calculation of the backoff time for the aggregated time off will only be done in
3010     // case the value is zero. It will be set to zero in the function RegionNextChannel.
3011     if( Nvm.MacGroup1.AggregatedTimeOff == 0 )
3012     {
3013         // Update aggregated time-off. This must be an assignment and no incremental
3014         // update as we do only calculate the time-off based on the last transmission
3015         Nvm.MacGroup1.AggregatedTimeOff = ( MacCtx.TxTimeOnAir * Nvm.MacGroup2.AggregatedDCycle - MacCtx.TxTimeOnAir );
3016     }
3017 }
3018 
RemoveMacCommands(LoRaMacRxSlot_t rxSlot,LoRaMacFrameCtrl_t fCtrl,Mcps_t request)3019 static void RemoveMacCommands( LoRaMacRxSlot_t rxSlot, LoRaMacFrameCtrl_t fCtrl, Mcps_t request )
3020 {
3021     if( rxSlot == RX_SLOT_WIN_1 || rxSlot == RX_SLOT_WIN_2  )
3022     {
3023         // Remove all sticky MAC commands answers since we can assume
3024         // that they have been received by the server.
3025         if( request == MCPS_CONFIRMED )
3026         {
3027             if( fCtrl.Bits.Ack == 1 )
3028             {  // For confirmed uplinks only if we have received an ACK.
3029                 LoRaMacCommandsRemoveStickyAnsCmds( );
3030             }
3031         }
3032         else
3033         {
3034             LoRaMacCommandsRemoveStickyAnsCmds( );
3035         }
3036     }
3037 }
3038 
3039 
ResetMacParameters(void)3040 static void ResetMacParameters( void )
3041 {
3042     LoRaMacClassBCallback_t classBCallbacks;
3043     LoRaMacClassBParams_t classBParams;
3044 
3045     Nvm.MacGroup2.NetworkActivation = ACTIVATION_TYPE_NONE;
3046 
3047     // ADR counter
3048     Nvm.MacGroup1.AdrAckCounter = 0;
3049 
3050     MacCtx.ChannelsNbTransCounter = 0;
3051     MacCtx.RetransmitTimeoutRetry = false;
3052     MacCtx.ResponseTimeoutStartTime = 0;
3053 
3054     Nvm.MacGroup2.MaxDCycle = 0;
3055     Nvm.MacGroup2.AggregatedDCycle = 1;
3056 
3057     Nvm.MacGroup1.ChannelsTxPower = Nvm.MacGroup2.ChannelsTxPowerDefault;
3058     Nvm.MacGroup1.ChannelsDatarate = Nvm.MacGroup2.ChannelsDatarateDefault;
3059     Nvm.MacGroup2.MacParams.Rx1DrOffset = Nvm.MacGroup2.MacParamsDefaults.Rx1DrOffset;
3060     Nvm.MacGroup2.MacParams.Rx2Channel = Nvm.MacGroup2.MacParamsDefaults.Rx2Channel;
3061     Nvm.MacGroup2.MacParams.RxCChannel = Nvm.MacGroup2.MacParamsDefaults.RxCChannel;
3062     Nvm.MacGroup2.MacParams.UplinkDwellTime = Nvm.MacGroup2.MacParamsDefaults.UplinkDwellTime;
3063     Nvm.MacGroup2.MacParams.DownlinkDwellTime = Nvm.MacGroup2.MacParamsDefaults.DownlinkDwellTime;
3064     Nvm.MacGroup2.MacParams.MaxEirp = Nvm.MacGroup2.MacParamsDefaults.MaxEirp;
3065     Nvm.MacGroup2.MacParams.AntennaGain = Nvm.MacGroup2.MacParamsDefaults.AntennaGain;
3066 
3067     MacCtx.NodeAckRequested = false;
3068     Nvm.MacGroup1.SrvAckRequested = false;
3069     Nvm.MacGroup2.ChannelsDatarateChangedLinkAdrReq = false;
3070     Nvm.MacGroup2.DownlinkReceived = false;
3071 
3072     // Reset to application defaults
3073     InitDefaultsParams_t params;
3074     params.Type = INIT_TYPE_RESET_TO_DEFAULT_CHANNELS;
3075     params.NvmGroup1 = &Nvm.RegionGroup1;
3076     params.NvmGroup2 = &Nvm.RegionGroup2;
3077     params.Bands = &RegionBands;
3078     RegionInitDefaults( Nvm.MacGroup2.Region, &params );
3079 
3080     // Initialize channel index.
3081     MacCtx.Channel = 0;
3082 
3083     // Initialize Rx2 config parameters.
3084     MacCtx.RxWindow2Config.Channel = MacCtx.Channel;
3085     MacCtx.RxWindow2Config.Frequency = Nvm.MacGroup2.MacParams.Rx2Channel.Frequency;
3086     MacCtx.RxWindow2Config.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime;
3087     MacCtx.RxWindow2Config.RxContinuous = false;
3088     MacCtx.RxWindow2Config.RxSlot = RX_SLOT_WIN_2;
3089     MacCtx.RxWindow2Config.NetworkActivation = Nvm.MacGroup2.NetworkActivation;
3090 
3091     // Initialize RxC config parameters.
3092     MacCtx.RxWindowCConfig = MacCtx.RxWindow2Config;
3093     MacCtx.RxWindowCConfig.RxContinuous = true;
3094     MacCtx.RxWindowCConfig.RxSlot = RX_SLOT_WIN_CLASS_C;
3095 
3096     // Initialize class b
3097     // Apply callback
3098     classBCallbacks.GetTemperatureLevel = NULL;
3099     classBCallbacks.MacProcessNotify = NULL;
3100 
3101     if( MacCtx.MacCallbacks != NULL )
3102     {
3103         classBCallbacks.GetTemperatureLevel = MacCtx.MacCallbacks->GetTemperatureLevel;
3104         classBCallbacks.MacProcessNotify = MacCtx.MacCallbacks->MacProcessNotify;
3105     }
3106 
3107     // Must all be static. Don't use local references.
3108     classBParams.MlmeIndication = &MacCtx.MlmeIndication;
3109     classBParams.McpsIndication = &MacCtx.McpsIndication;
3110     classBParams.MlmeConfirm = &MacCtx.MlmeConfirm;
3111     classBParams.LoRaMacFlags = &MacCtx.MacFlags;
3112     classBParams.LoRaMacDevAddr = &Nvm.MacGroup2.DevAddr;
3113     classBParams.LoRaMacRegion = &Nvm.MacGroup2.Region;
3114     classBParams.LoRaMacParams = &Nvm.MacGroup2.MacParams;
3115     classBParams.MulticastChannels = &Nvm.MacGroup2.MulticastChannelList[0];
3116     classBParams.NetworkActivation = &Nvm.MacGroup2.NetworkActivation;
3117 
3118     LoRaMacClassBInit( &classBParams, &classBCallbacks, &Nvm.ClassB );
3119 }
3120 
IsReJoin0Required()3121 static bool IsReJoin0Required( )
3122 {
3123 
3124     if( ( Nvm.MacGroup2.Rejoin0UplinksLimit == Nvm.MacGroup1.Rejoin0UplinksCounter ) &&
3125         ( Nvm.MacGroup2.Version.Fields.Minor >= 1 ) &&
3126         ( Nvm.MacGroup2.Rejoin0UplinksLimit != 0 ) )
3127     {
3128         Nvm.MacGroup1.Rejoin0UplinksCounter = 0;
3129         return true;
3130     }
3131     return false;
3132 }
3133 
3134 /*!
3135  * \brief Initializes and opens the reception window
3136  *
3137  * \param [IN] rxTimer  Window timer to be topped.
3138  * \param [IN] rxConfig Window parameters to be setup
3139  */
RxWindowSetup(TimerEvent_t * rxTimer,RxConfigParams_t * rxConfig)3140 static void RxWindowSetup( TimerEvent_t* rxTimer, RxConfigParams_t* rxConfig )
3141 {
3142     TimerStop( rxTimer );
3143 
3144     // Ensure the radio is Idle
3145     Radio.Standby( );
3146 
3147     if( RegionRxConfig( Nvm.MacGroup2.Region, rxConfig, ( int8_t* )&MacCtx.McpsIndication.RxDatarate ) == true )
3148     {
3149         Radio.Rx( Nvm.MacGroup2.MacParams.MaxRxWindow );
3150         MacCtx.RxSlot = rxConfig->RxSlot;
3151     }
3152 }
3153 
OpenContinuousRxCWindow(void)3154 static void OpenContinuousRxCWindow( void )
3155 {
3156     // Compute RxC windows parameters
3157     RegionComputeRxWindowParameters( Nvm.MacGroup2.Region,
3158                                      Nvm.MacGroup2.MacParams.RxCChannel.Datarate,
3159                                      Nvm.MacGroup2.MacParams.MinRxSymbols,
3160                                      Nvm.MacGroup2.MacParams.SystemMaxRxError,
3161                                      &MacCtx.RxWindowCConfig );
3162 
3163     MacCtx.RxWindowCConfig.RxSlot = RX_SLOT_WIN_CLASS_C;
3164     MacCtx.RxWindowCConfig.NetworkActivation = Nvm.MacGroup2.NetworkActivation;
3165     // Setup continuous listening
3166     MacCtx.RxWindowCConfig.RxContinuous = true;
3167 
3168     // At this point the Radio should be idle.
3169     // Thus, there is no need to set the radio in standby mode.
3170     if( RegionRxConfig( Nvm.MacGroup2.Region, &MacCtx.RxWindowCConfig, ( int8_t* )&MacCtx.McpsIndication.RxDatarate ) == true )
3171     {
3172         Radio.Rx( 0 ); // Continuous mode
3173         MacCtx.RxSlot = MacCtx.RxWindowCConfig.RxSlot;
3174     }
3175 }
3176 
PrepareFrame(LoRaMacHeader_t * macHdr,LoRaMacFrameCtrl_t * fCtrl,uint8_t fPort,void * fBuffer,uint16_t fBufferSize)3177 LoRaMacStatus_t PrepareFrame( LoRaMacHeader_t* macHdr, LoRaMacFrameCtrl_t* fCtrl, uint8_t fPort, void* fBuffer, uint16_t fBufferSize )
3178 {
3179     MacCtx.PktBufferLen = 0;
3180     MacCtx.NodeAckRequested = false;
3181     uint32_t fCntUp = 0;
3182     size_t macCmdsSize = 0;
3183     uint8_t availableSize = 0;
3184 
3185     if( fBuffer == NULL )
3186     {
3187         fBufferSize = 0;
3188     }
3189 
3190     memcpy1( MacCtx.AppData, ( uint8_t* ) fBuffer, fBufferSize );
3191     MacCtx.AppDataSize = fBufferSize;
3192     MacCtx.PktBuffer[0] = macHdr->Value;
3193 
3194     switch( macHdr->Bits.MType )
3195     {
3196         case FRAME_TYPE_DATA_CONFIRMED_UP:
3197             MacCtx.NodeAckRequested = true;
3198             // Intentional fall through
3199         case FRAME_TYPE_DATA_UNCONFIRMED_UP:
3200             MacCtx.TxMsg.Type = LORAMAC_MSG_TYPE_DATA;
3201             MacCtx.TxMsg.Message.Data.Buffer = MacCtx.PktBuffer;
3202             MacCtx.TxMsg.Message.Data.BufSize = LORAMAC_PHY_MAXPAYLOAD;
3203             MacCtx.TxMsg.Message.Data.MHDR.Value = macHdr->Value;
3204             MacCtx.TxMsg.Message.Data.FPort = fPort;
3205             MacCtx.TxMsg.Message.Data.FHDR.DevAddr = Nvm.MacGroup2.DevAddr;
3206             MacCtx.TxMsg.Message.Data.FHDR.FCtrl.Value = fCtrl->Value;
3207             MacCtx.TxMsg.Message.Data.FRMPayloadSize = MacCtx.AppDataSize;
3208             MacCtx.TxMsg.Message.Data.FRMPayload = MacCtx.AppData;
3209 
3210             if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoGetFCntUp( &fCntUp ) )
3211             {
3212                 return LORAMAC_STATUS_FCNT_HANDLER_ERROR;
3213             }
3214             MacCtx.TxMsg.Message.Data.FHDR.FCnt = ( uint16_t )fCntUp;
3215 
3216             // Reset confirm parameters
3217             MacCtx.McpsConfirm.NbTrans = 0;
3218             MacCtx.McpsConfirm.AckReceived = false;
3219             MacCtx.McpsConfirm.UpLinkCounter = fCntUp;
3220 
3221             // Handle the MAC commands if there are any available
3222             if( LoRaMacCommandsGetSizeSerializedCmds( &macCmdsSize ) != LORAMAC_COMMANDS_SUCCESS )
3223             {
3224                 return LORAMAC_STATUS_MAC_COMMAD_ERROR;
3225             }
3226 
3227             if( macCmdsSize > 0 )
3228             {
3229                 availableSize = GetMaxAppPayloadWithoutFOptsLength( Nvm.MacGroup1.ChannelsDatarate );
3230 
3231                 // There is application payload available and the MAC commands fit into FOpts field.
3232                 if( ( MacCtx.AppDataSize > 0 ) && ( macCmdsSize <= LORA_MAC_COMMAND_MAX_FOPTS_LENGTH ) )
3233                 {
3234                     if( LoRaMacCommandsSerializeCmds( LORA_MAC_COMMAND_MAX_FOPTS_LENGTH, &macCmdsSize, MacCtx.TxMsg.Message.Data.FHDR.FOpts ) != LORAMAC_COMMANDS_SUCCESS )
3235                     {
3236                         return LORAMAC_STATUS_MAC_COMMAD_ERROR;
3237                     }
3238                     fCtrl->Bits.FOptsLen = macCmdsSize;
3239                     // Update FCtrl field with new value of FOptionsLength
3240                     MacCtx.TxMsg.Message.Data.FHDR.FCtrl.Value = fCtrl->Value;
3241                 }
3242                 // There is application payload available but the MAC commands does NOT fit into FOpts field.
3243                 else if( ( MacCtx.AppDataSize > 0 ) && ( macCmdsSize > LORA_MAC_COMMAND_MAX_FOPTS_LENGTH ) )
3244                 {
3245 
3246                     if( LoRaMacCommandsSerializeCmds( availableSize, &macCmdsSize, MacCtx.MacCommandsBuffer ) != LORAMAC_COMMANDS_SUCCESS )
3247                     {
3248                         return LORAMAC_STATUS_MAC_COMMAD_ERROR;
3249                     }
3250                     return LORAMAC_STATUS_SKIPPED_APP_DATA;
3251                 }
3252                 // No application payload available therefore add all mac commands to the FRMPayload.
3253                 else
3254                 {
3255                     if( LoRaMacCommandsSerializeCmds( availableSize, &macCmdsSize, MacCtx.MacCommandsBuffer ) != LORAMAC_COMMANDS_SUCCESS )
3256                     {
3257                         return LORAMAC_STATUS_MAC_COMMAD_ERROR;
3258                     }
3259                     // Force FPort to be zero
3260                     MacCtx.TxMsg.Message.Data.FPort = 0;
3261 
3262                     MacCtx.TxMsg.Message.Data.FRMPayload = MacCtx.MacCommandsBuffer;
3263                     MacCtx.TxMsg.Message.Data.FRMPayloadSize = macCmdsSize;
3264                 }
3265             }
3266 
3267             break;
3268         case FRAME_TYPE_PROPRIETARY:
3269             if( ( fBuffer != NULL ) && ( MacCtx.AppDataSize > 0 ) )
3270             {
3271                 memcpy1( MacCtx.PktBuffer + LORAMAC_MHDR_FIELD_SIZE, ( uint8_t* ) fBuffer, MacCtx.AppDataSize );
3272                 MacCtx.PktBufferLen = LORAMAC_MHDR_FIELD_SIZE + MacCtx.AppDataSize;
3273             }
3274             break;
3275         default:
3276             return LORAMAC_STATUS_SERVICE_UNKNOWN;
3277     }
3278 
3279     return LORAMAC_STATUS_OK;
3280 }
3281 
SendFrameOnChannel(uint8_t channel)3282 LoRaMacStatus_t SendFrameOnChannel( uint8_t channel )
3283 {
3284     LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID;
3285     TxConfigParams_t txConfig;
3286     int8_t txPower = 0;
3287 
3288     txConfig.Channel = channel;
3289     txConfig.Datarate = Nvm.MacGroup1.ChannelsDatarate;
3290     txConfig.TxPower = Nvm.MacGroup1.ChannelsTxPower;
3291     txConfig.MaxEirp = Nvm.MacGroup2.MacParams.MaxEirp;
3292     txConfig.AntennaGain = Nvm.MacGroup2.MacParams.AntennaGain;
3293     txConfig.PktLen = MacCtx.PktBufferLen;
3294 
3295     RegionTxConfig( Nvm.MacGroup2.Region, &txConfig, &txPower, &MacCtx.TxTimeOnAir );
3296 
3297     MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
3298     MacCtx.McpsConfirm.Datarate = Nvm.MacGroup1.ChannelsDatarate;
3299     MacCtx.McpsConfirm.TxPower = txPower;
3300     MacCtx.McpsConfirm.Channel = channel;
3301 
3302     // Store the time on air
3303     MacCtx.McpsConfirm.TxTimeOnAir = MacCtx.TxTimeOnAir;
3304     MacCtx.MlmeConfirm.TxTimeOnAir = MacCtx.TxTimeOnAir;
3305 
3306     if( LoRaMacClassBIsBeaconModeActive( ) == true )
3307     {
3308         // Currently, the Time-On-Air can only be computed when the radio is configured with
3309         // the TX configuration
3310         TimerTime_t collisionTime = LoRaMacClassBIsUplinkCollision( MacCtx.TxTimeOnAir );
3311 
3312         if( collisionTime > 0 )
3313         {
3314             return LORAMAC_STATUS_BUSY_UPLINK_COLLISION;
3315         }
3316     }
3317 
3318     if( Nvm.MacGroup2.DeviceClass == CLASS_B )
3319     {
3320         // Stop slots for class b
3321         LoRaMacClassBStopRxSlots( );
3322     }
3323 
3324     LoRaMacClassBHaltBeaconing( );
3325 
3326     // Secure frame
3327     status = SecureFrame( Nvm.MacGroup1.ChannelsDatarate, MacCtx.Channel );
3328     if( status != LORAMAC_STATUS_OK )
3329     {
3330         return status;
3331     }
3332 
3333     MacCtx.MacState |= LORAMAC_TX_RUNNING;
3334 
3335     MacCtx.ChannelsNbTransCounter++;
3336     MacCtx.McpsConfirm.NbTrans = MacCtx.ChannelsNbTransCounter;
3337     MacCtx.ResponseTimeoutStartTime = 0;
3338 
3339     // Send now
3340     Radio.Send( MacCtx.PktBuffer, MacCtx.PktBufferLen );
3341 
3342     return LORAMAC_STATUS_OK;
3343 }
3344 
SetTxContinuousWave(uint16_t timeout,uint32_t frequency,uint8_t power)3345 LoRaMacStatus_t SetTxContinuousWave( uint16_t timeout, uint32_t frequency, uint8_t power )
3346 {
3347     Radio.SetTxContinuousWave( frequency, power, timeout );
3348 
3349     MacCtx.MacState |= LORAMAC_TX_RUNNING;
3350 
3351     return LORAMAC_STATUS_OK;
3352 }
3353 
GetNvmData(void)3354 LoRaMacNvmData_t* GetNvmData( void )
3355 {
3356     return &Nvm;
3357 }
3358 
RestoreNvmData(LoRaMacNvmData_t * nvm)3359 LoRaMacStatus_t RestoreNvmData( LoRaMacNvmData_t* nvm )
3360 {
3361     uint32_t crc = 0;
3362 
3363     // Status and parameter validation
3364     if( nvm == NULL )
3365     {
3366         return LORAMAC_STATUS_PARAMETER_INVALID;
3367     }
3368     if( MacCtx.MacState != LORAMAC_STOPPED )
3369     {
3370         return LORAMAC_STATUS_BUSY;
3371     }
3372 
3373     // Crypto
3374     crc = Crc32( ( uint8_t* ) &nvm->Crypto, sizeof( nvm->Crypto ) -
3375                                             sizeof( nvm->Crypto.Crc32 ) );
3376     if( crc == nvm->Crypto.Crc32 )
3377     {
3378         memcpy1( ( uint8_t* ) &Nvm.Crypto, ( uint8_t* ) &nvm->Crypto,
3379                  sizeof( Nvm.Crypto ) );
3380     }
3381 
3382     // MacGroup1
3383     crc = Crc32( ( uint8_t* ) &nvm->MacGroup1, sizeof( nvm->MacGroup1 ) -
3384                                                sizeof( nvm->MacGroup1.Crc32 ) );
3385     if( crc == nvm->MacGroup1.Crc32 )
3386     {
3387         memcpy1( ( uint8_t* ) &Nvm.MacGroup1, ( uint8_t* ) &nvm->MacGroup1,
3388                  sizeof( Nvm.MacGroup1 ) );
3389     }
3390 
3391     // MacGroup2
3392     crc = Crc32( ( uint8_t* ) &nvm->MacGroup2, sizeof( nvm->MacGroup2 ) -
3393                                                sizeof( nvm->MacGroup2.Crc32 ) );
3394     if( crc == nvm->MacGroup2.Crc32 )
3395     {
3396         memcpy1( ( uint8_t* ) &Nvm.MacGroup2, ( uint8_t* ) &nvm->MacGroup2,
3397                  sizeof( Nvm.MacGroup2 ) );
3398 
3399         // Initialize RxC config parameters.
3400         MacCtx.RxWindowCConfig.Channel = MacCtx.Channel;
3401         MacCtx.RxWindowCConfig.Frequency = Nvm.MacGroup2.MacParams.RxCChannel.Frequency;
3402         MacCtx.RxWindowCConfig.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime;
3403         MacCtx.RxWindowCConfig.RxContinuous = true;
3404         MacCtx.RxWindowCConfig.RxSlot = RX_SLOT_WIN_CLASS_C;
3405     }
3406 
3407     // Secure Element
3408     crc = Crc32( ( uint8_t* ) &nvm->SecureElement, sizeof( nvm->SecureElement ) -
3409                                                    sizeof( nvm->SecureElement.Crc32 ) );
3410     if( crc == nvm->SecureElement.Crc32 )
3411     {
3412         memcpy1( ( uint8_t* ) &Nvm.SecureElement,( uint8_t* ) &nvm->SecureElement,
3413                  sizeof( Nvm.SecureElement ) );
3414     }
3415 
3416     // Region
3417     crc = Crc32( ( uint8_t* ) &nvm->RegionGroup1, sizeof( nvm->RegionGroup1 ) -
3418                                             sizeof( nvm->RegionGroup1.Crc32 ) );
3419     if( crc == nvm->RegionGroup1.Crc32 )
3420     {
3421         memcpy1( ( uint8_t* ) &Nvm.RegionGroup1,( uint8_t* ) &nvm->RegionGroup1,
3422                  sizeof( Nvm.RegionGroup1 ) );
3423     }
3424 
3425     crc = Crc32( ( uint8_t* ) &nvm->ClassB, sizeof( nvm->ClassB ) -
3426                                             sizeof( nvm->ClassB.Crc32 ) );
3427     if( crc == nvm->ClassB.Crc32 )
3428     {
3429         memcpy1( ( uint8_t* ) &Nvm.ClassB,( uint8_t* ) &nvm->ClassB,
3430                  sizeof( Nvm.ClassB ) );
3431     }
3432 
3433     return LORAMAC_STATUS_OK;
3434 }
3435 
DetermineFrameType(LoRaMacMessageData_t * macMsg,FType_t * fType)3436 LoRaMacStatus_t DetermineFrameType( LoRaMacMessageData_t* macMsg, FType_t* fType )
3437 {
3438     if( ( macMsg == NULL ) || ( fType == NULL ) )
3439     {
3440         return LORAMAC_STATUS_PARAMETER_INVALID;
3441     }
3442 
3443     /* The LoRaWAN specification allows several possible configurations how data up/down frames are built up.
3444      * In sake of clearness the following naming is applied. Please keep in mind that this is
3445      * implementation specific since there is no definition in the LoRaWAN specification included.
3446      *
3447      * X -> Field is available
3448      * - -> Field is not available
3449      *
3450      * +-------+  +----------+------+-------+--------------+
3451      * | FType |  | FOptsLen | Fopt | FPort |  FRMPayload  |
3452      * +-------+  +----------+------+-------+--------------+
3453      * |   A   |  |    > 0   |   X  |  > 0  |       X      |
3454      * +-------+  +----------+------+-------+--------------+
3455      * |   B   |  |   >= 0   |  X/- |   -   |       -      |
3456      * +-------+  +----------+------+-------+--------------+
3457      * |   C   |  |    = 0   |   -  |  = 0  | MAC commands |
3458      * +-------+  +----------+------+-------+--------------+
3459      * |   D   |  |    = 0   |   -  |  > 0  |       X      |
3460      * +-------+  +----------+------+-------+--------------+
3461      */
3462 
3463     if( ( macMsg->FHDR.FCtrl.Bits.FOptsLen > 0 ) && ( macMsg->FPort > 0 ) )
3464     {
3465         *fType = FRAME_TYPE_A;
3466     }
3467     else if( macMsg->FRMPayloadSize == 0 )
3468     {
3469         *fType = FRAME_TYPE_B;
3470     }
3471     else if( ( macMsg->FHDR.FCtrl.Bits.FOptsLen == 0 ) && ( macMsg->FPort == 0 ) )
3472     {
3473         *fType = FRAME_TYPE_C;
3474     }
3475     else if( ( macMsg->FHDR.FCtrl.Bits.FOptsLen == 0 ) && ( macMsg->FPort > 0 ) )
3476     {
3477         *fType = FRAME_TYPE_D;
3478     }
3479     else
3480     {
3481         // Should never happen.
3482         return LORAMAC_STATUS_ERROR;
3483     }
3484 
3485     return LORAMAC_STATUS_OK;
3486 }
3487 
CheckRetrans(uint8_t counter,uint8_t limit)3488 static bool CheckRetrans( uint8_t counter, uint8_t limit )
3489 {
3490     if( counter >= limit )
3491     {
3492         return true;
3493     }
3494     return false;
3495 }
3496 
CheckRetransUnconfirmedUplink(void)3497 static bool CheckRetransUnconfirmedUplink( void )
3498 {
3499     // Verify, if the max number of retransmissions have been reached
3500     if( CheckRetrans( MacCtx.ChannelsNbTransCounter,
3501                       Nvm.MacGroup2.MacParams.ChannelsNbTrans ) == true )
3502     {
3503         return true;
3504     }
3505 
3506     if( MacCtx.MacFlags.Bits.McpsInd == 1 )
3507     {
3508         // Stop the retransmissions, if a valid downlink is received
3509         // a class A RX window. This holds also for class B and C.
3510         if( ( MacCtx.McpsIndication.RxSlot == RX_SLOT_WIN_1 ) ||
3511             ( MacCtx.McpsIndication.RxSlot == RX_SLOT_WIN_2 ) )
3512         {
3513             return true;
3514         }
3515     }
3516     return false;
3517 }
3518 
CheckRetransConfirmedUplink(void)3519 static bool CheckRetransConfirmedUplink( void )
3520 {
3521     // Verify, if the max number of retransmissions have been reached
3522     if( CheckRetrans( MacCtx.ChannelsNbTransCounter,
3523                       Nvm.MacGroup2.MacParams.ChannelsNbTrans ) == true )
3524     {
3525         return true;
3526     }
3527 
3528     if( MacCtx.MacFlags.Bits.McpsInd == 1 )
3529     {
3530         if( MacCtx.McpsConfirm.AckReceived == true )
3531         {
3532             return true;
3533         }
3534     }
3535     return false;
3536 }
3537 
IncreaseAdrAckCounter(uint32_t counter)3538 static uint32_t IncreaseAdrAckCounter( uint32_t counter )
3539 {
3540     if( counter < ADR_ACK_COUNTER_MAX )
3541     {
3542         counter++;
3543     }
3544     return counter;
3545 }
3546 
StopRetransmission(void)3547 static bool StopRetransmission( void )
3548 {
3549     // Increase Rejoin Uplinks counter
3550     if( Nvm.MacGroup2.Rejoin0UplinksLimit != 0 )
3551     {
3552         Nvm.MacGroup1.Rejoin0UplinksCounter++;
3553     }
3554 
3555     if( Nvm.MacGroup2.Version.Fields.Minor >= 1 )
3556     {
3557         MacCommand_t* macCmd;
3558         if( LoRaMacCommandsGetCmd( MOTE_MAC_REKEY_IND, &macCmd ) == LORAMAC_COMMANDS_SUCCESS )
3559         {
3560             // Increase the Rekey Uplinks counter
3561             Nvm.MacGroup1.RekeyIndUplinksCounter++;
3562 
3563             /*
3564              * If the device has not received a RekeyConf within
3565              * the first ADR_ACK_LIMIT uplinks it SHALL revert to the Join state.
3566              */
3567             if( Nvm.MacGroup1.RekeyIndUplinksCounter == MacCtx.AdrAckLimit )
3568             {
3569                 Nvm.MacGroup2.NetworkActivation = ACTIVATION_TYPE_NONE;
3570                 MacCtx.MacFlags.Bits.MlmeInd = 1;
3571                 MacCtx.MlmeIndication.MlmeIndication = MLME_REVERT_JOIN;
3572             }
3573         }
3574     }
3575 
3576     if( ( MacCtx.MacFlags.Bits.McpsInd == 0 ) ||
3577         ( ( MacCtx.McpsIndication.RxSlot != RX_SLOT_WIN_1 ) &&
3578           ( MacCtx.McpsIndication.RxSlot != RX_SLOT_WIN_2 ) ) )
3579     {   // Maximum repetitions without downlink. Increase ADR Ack counter.
3580         // Only process the case when the MAC did not receive a downlink.
3581         if( Nvm.MacGroup2.AdrCtrlOn == true )
3582         {
3583             Nvm.MacGroup1.AdrAckCounter = IncreaseAdrAckCounter( Nvm.MacGroup1.AdrAckCounter );
3584         }
3585     }
3586 
3587     MacCtx.ChannelsNbTransCounter = 0;
3588     MacCtx.NodeAckRequested = false;
3589     MacCtx.RetransmitTimeoutRetry = false;
3590     MacCtx.MacState &= ~LORAMAC_TX_RUNNING;
3591 
3592     return true;
3593 }
3594 
CallNvmDataChangeCallback(uint16_t notifyFlags)3595 static void CallNvmDataChangeCallback( uint16_t notifyFlags )
3596 {
3597     if( ( MacCtx.MacCallbacks != NULL ) &&
3598         ( MacCtx.MacCallbacks->NvmDataChange  != NULL ) )
3599     {
3600         MacCtx.MacCallbacks->NvmDataChange ( notifyFlags );
3601     }
3602 }
IsRequestPending(void)3603 static uint8_t IsRequestPending( void )
3604 {
3605     if( ( MacCtx.MacFlags.Bits.MlmeReq == 1 ) ||
3606         ( MacCtx.MacFlags.Bits.McpsReq == 1 ) )
3607     {
3608         return 1;
3609     }
3610     return 0;
3611 }
3612 
3613 
LoRaMacInitialization(LoRaMacPrimitives_t * primitives,LoRaMacCallback_t * callbacks,LoRaMacRegion_t region)3614 LoRaMacStatus_t LoRaMacInitialization( LoRaMacPrimitives_t* primitives, LoRaMacCallback_t* callbacks, LoRaMacRegion_t region )
3615 {
3616     GetPhyParams_t getPhy;
3617     PhyParam_t phyParam;
3618 
3619     if( ( primitives == NULL ) ||
3620         ( callbacks == NULL ) )
3621     {
3622         return LORAMAC_STATUS_PARAMETER_INVALID;
3623     }
3624 
3625     if( ( primitives->MacMcpsConfirm == NULL ) ||
3626         ( primitives->MacMcpsIndication == NULL ) ||
3627         ( primitives->MacMlmeConfirm == NULL ) ||
3628         ( primitives->MacMlmeIndication == NULL ) )
3629     {
3630         return LORAMAC_STATUS_PARAMETER_INVALID;
3631     }
3632     // Verify if the region is supported
3633     if( RegionIsActive( region ) == false )
3634     {
3635         return LORAMAC_STATUS_REGION_NOT_SUPPORTED;
3636     }
3637 
3638     // Confirm queue reset
3639     LoRaMacConfirmQueueInit( primitives );
3640 
3641     // Initialize the module context with zeros
3642     memset1( ( uint8_t* ) &Nvm, 0x00, sizeof( LoRaMacNvmData_t ) );
3643     memset1( ( uint8_t* ) &MacCtx, 0x00, sizeof( LoRaMacCtx_t ) );
3644 
3645     // Set non zero variables to its default value
3646     Nvm.MacGroup2.Region = region;
3647     Nvm.MacGroup2.DeviceClass = CLASS_A;
3648 
3649     // Setup version
3650     Nvm.MacGroup2.Version.Value = LORAMAC_VERSION;
3651 
3652     InitDefaultsParams_t params;
3653     params.Type = INIT_TYPE_DEFAULTS;
3654     params.NvmGroup1 = &Nvm.RegionGroup1;
3655     params.NvmGroup2 = &Nvm.RegionGroup2;
3656     params.Bands = &RegionBands;
3657     RegionInitDefaults( Nvm.MacGroup2.Region, &params );
3658 
3659     // Reset to defaults
3660     getPhy.Attribute = PHY_DUTY_CYCLE;
3661     phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
3662     Nvm.MacGroup2.DutyCycleOn = ( bool ) phyParam.Value;
3663 
3664     getPhy.Attribute = PHY_DEF_TX_POWER;
3665     phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
3666     Nvm.MacGroup2.ChannelsTxPowerDefault = phyParam.Value;
3667 
3668     getPhy.Attribute = PHY_DEF_TX_DR;
3669     phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
3670     Nvm.MacGroup2.ChannelsDatarateDefault = phyParam.Value;
3671 
3672     getPhy.Attribute = PHY_MAX_RX_WINDOW;
3673     phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
3674     Nvm.MacGroup2.MacParamsDefaults.MaxRxWindow = phyParam.Value;
3675 
3676     getPhy.Attribute = PHY_RECEIVE_DELAY1;
3677     phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
3678     Nvm.MacGroup2.MacParamsDefaults.ReceiveDelay1 = phyParam.Value;
3679 
3680     getPhy.Attribute = PHY_RECEIVE_DELAY2;
3681     phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
3682     Nvm.MacGroup2.MacParamsDefaults.ReceiveDelay2 = phyParam.Value;
3683 
3684     getPhy.Attribute = PHY_JOIN_ACCEPT_DELAY1;
3685     phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
3686     Nvm.MacGroup2.MacParamsDefaults.JoinAcceptDelay1 = phyParam.Value;
3687 
3688     getPhy.Attribute = PHY_JOIN_ACCEPT_DELAY2;
3689     phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
3690     Nvm.MacGroup2.MacParamsDefaults.JoinAcceptDelay2 = phyParam.Value;
3691 
3692     getPhy.Attribute = PHY_DEF_DR1_OFFSET;
3693     phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
3694     Nvm.MacGroup2.MacParamsDefaults.Rx1DrOffset = phyParam.Value;
3695 
3696     getPhy.Attribute = PHY_DEF_RX2_FREQUENCY;
3697     phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
3698     Nvm.MacGroup2.MacParamsDefaults.Rx2Channel.Frequency = phyParam.Value;
3699     Nvm.MacGroup2.MacParamsDefaults.RxCChannel.Frequency = phyParam.Value;
3700 
3701     getPhy.Attribute = PHY_DEF_RX2_DR;
3702     phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
3703     Nvm.MacGroup2.MacParamsDefaults.Rx2Channel.Datarate = phyParam.Value;
3704     Nvm.MacGroup2.MacParamsDefaults.RxCChannel.Datarate = phyParam.Value;
3705 
3706     getPhy.Attribute = PHY_DEF_UPLINK_DWELL_TIME;
3707     phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
3708     Nvm.MacGroup2.MacParamsDefaults.UplinkDwellTime = phyParam.Value;
3709 
3710     getPhy.Attribute = PHY_DEF_DOWNLINK_DWELL_TIME;
3711     phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
3712     Nvm.MacGroup2.MacParamsDefaults.DownlinkDwellTime = phyParam.Value;
3713 
3714     getPhy.Attribute = PHY_DEF_MAX_EIRP;
3715     phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
3716     Nvm.MacGroup2.MacParamsDefaults.MaxEirp = phyParam.fValue;
3717 
3718     getPhy.Attribute = PHY_DEF_ANTENNA_GAIN;
3719     phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
3720     Nvm.MacGroup2.MacParamsDefaults.AntennaGain = phyParam.fValue;
3721 
3722     getPhy.Attribute = PHY_DEF_ADR_ACK_LIMIT;
3723     phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
3724     MacCtx.AdrAckLimit = phyParam.Value;
3725 
3726     getPhy.Attribute = PHY_DEF_ADR_ACK_DELAY;
3727     phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
3728     MacCtx.AdrAckDelay = phyParam.Value;
3729 
3730     // Init parameters which are not set in function ResetMacParameters
3731     Nvm.MacGroup2.MacParamsDefaults.ChannelsNbTrans = 1;
3732     Nvm.MacGroup2.MacParamsDefaults.SystemMaxRxError = 10;
3733     Nvm.MacGroup2.MacParamsDefaults.MinRxSymbols = 6;
3734 
3735     Nvm.MacGroup2.MacParams.SystemMaxRxError = Nvm.MacGroup2.MacParamsDefaults.SystemMaxRxError;
3736     Nvm.MacGroup2.MacParams.MinRxSymbols = Nvm.MacGroup2.MacParamsDefaults.MinRxSymbols;
3737     Nvm.MacGroup2.MacParams.MaxRxWindow = Nvm.MacGroup2.MacParamsDefaults.MaxRxWindow;
3738     Nvm.MacGroup2.MacParams.ReceiveDelay1 = Nvm.MacGroup2.MacParamsDefaults.ReceiveDelay1;
3739     Nvm.MacGroup2.MacParams.ReceiveDelay2 = Nvm.MacGroup2.MacParamsDefaults.ReceiveDelay2;
3740     Nvm.MacGroup2.MacParams.JoinAcceptDelay1 = Nvm.MacGroup2.MacParamsDefaults.JoinAcceptDelay1;
3741     Nvm.MacGroup2.MacParams.JoinAcceptDelay2 = Nvm.MacGroup2.MacParamsDefaults.JoinAcceptDelay2;
3742     Nvm.MacGroup2.MacParams.ChannelsNbTrans = Nvm.MacGroup2.MacParamsDefaults.ChannelsNbTrans;
3743 
3744     // FPort 224 is enabled by default.
3745     Nvm.MacGroup2.IsCertPortOn = true;
3746 
3747     ResetMacParameters( );
3748 
3749     Nvm.MacGroup2.PublicNetwork = true;
3750 
3751     MacCtx.MacPrimitives = primitives;
3752     MacCtx.MacCallbacks = callbacks;
3753     MacCtx.MacFlags.Value = 0;
3754     MacCtx.MacState = LORAMAC_STOPPED;
3755 
3756     // Reset duty cycle times
3757     Nvm.MacGroup1.LastTxDoneTime = 0;
3758     Nvm.MacGroup1.AggregatedTimeOff = 0;
3759 
3760     // Initialize timers
3761     TimerInit( &MacCtx.TxDelayedTimer, OnTxDelayedTimerEvent );
3762     TimerInit( &MacCtx.RxWindowTimer1, OnRxWindow1TimerEvent );
3763     TimerInit( &MacCtx.RxWindowTimer2, OnRxWindow2TimerEvent );
3764     TimerInit( &MacCtx.RetransmitTimeoutTimer, OnRetransmitTimeoutTimerEvent );
3765     TimerInit( &MacCtx.Rejoin0CycleTimer, OnRejoin0CycleTimerEvent );
3766     TimerInit( &MacCtx.Rejoin1CycleTimer, OnRejoin1CycleTimerEvent );
3767     TimerInit( &MacCtx.Rejoin2CycleTimer, OnRejoin2CycleTimerEvent );
3768     TimerInit( &MacCtx.ForceRejoinReqCycleTimer, OnForceRejoinReqCycleTimerEvent );
3769 
3770     // Store the current initialization time
3771     Nvm.MacGroup2.InitializationTime = SysTimeGetMcuTime( );
3772 
3773     // Initialize MAC radio events
3774     LoRaMacRadioEvents.Value = 0;
3775 
3776     // Initialize Radio driver
3777     MacCtx.RadioEvents.TxDone = OnRadioTxDone;
3778     MacCtx.RadioEvents.RxDone = OnRadioRxDone;
3779     MacCtx.RadioEvents.RxError = OnRadioRxError;
3780     MacCtx.RadioEvents.TxTimeout = OnRadioTxTimeout;
3781     MacCtx.RadioEvents.RxTimeout = OnRadioRxTimeout;
3782     Radio.Init( &MacCtx.RadioEvents );
3783 
3784     // Initialize the Secure Element driver
3785     if( SecureElementInit( &Nvm.SecureElement ) != SECURE_ELEMENT_SUCCESS )
3786     {
3787         return LORAMAC_STATUS_CRYPTO_ERROR;
3788     }
3789 
3790     // Initialize Crypto module
3791     if( LoRaMacCryptoInit( &Nvm.Crypto ) != LORAMAC_CRYPTO_SUCCESS )
3792     {
3793         return LORAMAC_STATUS_CRYPTO_ERROR;
3794     }
3795 
3796     // Initialize MAC commands module
3797     if( LoRaMacCommandsInit( ) != LORAMAC_COMMANDS_SUCCESS )
3798     {
3799         return LORAMAC_STATUS_MAC_COMMAD_ERROR;
3800     }
3801 
3802     // Set multicast downlink counter reference
3803     if( LoRaMacCryptoSetMulticastReference( Nvm.MacGroup2.MulticastChannelList ) != LORAMAC_CRYPTO_SUCCESS )
3804     {
3805         return LORAMAC_STATUS_CRYPTO_ERROR;
3806     }
3807 
3808     // Random seed initialization
3809     srand1( Radio.Random( ) );
3810 
3811     Radio.SetPublicNetwork( Nvm.MacGroup2.PublicNetwork );
3812     Radio.Sleep( );
3813 
3814     LoRaMacEnableRequests( LORAMAC_REQUEST_HANDLING_ON );
3815 
3816     return LORAMAC_STATUS_OK;
3817 }
3818 
LoRaMacStart(void)3819 LoRaMacStatus_t LoRaMacStart( void )
3820 {
3821     MacCtx.MacState = LORAMAC_IDLE;
3822     return LORAMAC_STATUS_OK;
3823 }
3824 
LoRaMacStop(void)3825 LoRaMacStatus_t LoRaMacStop( void )
3826 {
3827     if( LoRaMacIsBusy( ) == false )
3828     {
3829         MacCtx.MacState = LORAMAC_STOPPED;
3830         return LORAMAC_STATUS_OK;
3831     }
3832     else if(  MacCtx.MacState == LORAMAC_STOPPED )
3833     {
3834         return LORAMAC_STATUS_OK;
3835     }
3836     return LORAMAC_STATUS_BUSY;
3837 }
3838 
LoRaMacQueryTxPossible(uint8_t size,LoRaMacTxInfo_t * txInfo)3839 LoRaMacStatus_t LoRaMacQueryTxPossible( uint8_t size, LoRaMacTxInfo_t* txInfo )
3840 {
3841     CalcNextAdrParams_t adrNext;
3842     uint32_t adrAckCounter = Nvm.MacGroup1.AdrAckCounter;
3843     int8_t datarate = Nvm.MacGroup2.ChannelsDatarateDefault;
3844     int8_t txPower = Nvm.MacGroup2.ChannelsTxPowerDefault;
3845     uint8_t nbTrans = MacCtx.ChannelsNbTransCounter;
3846     size_t macCmdsSize = 0;
3847 
3848     if( txInfo == NULL )
3849     {
3850         return LORAMAC_STATUS_PARAMETER_INVALID;
3851     }
3852 
3853     // Setup ADR request
3854     adrNext.UpdateChanMask = false;
3855     adrNext.AdrEnabled = Nvm.MacGroup2.AdrCtrlOn;
3856     adrNext.AdrAckCounter = Nvm.MacGroup1.AdrAckCounter;
3857     adrNext.AdrAckLimit = MacCtx.AdrAckLimit;
3858     adrNext.AdrAckDelay = MacCtx.AdrAckDelay;
3859     adrNext.Datarate = Nvm.MacGroup1.ChannelsDatarate;
3860     adrNext.TxPower = Nvm.MacGroup1.ChannelsTxPower;
3861     adrNext.NbTrans = MacCtx.ChannelsNbTransCounter;
3862     adrNext.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime;
3863     adrNext.Region = Nvm.MacGroup2.Region;
3864 
3865     // We call the function for information purposes only. We don't want to
3866     // apply the datarate, the tx power and the ADR ack counter.
3867     LoRaMacAdrCalcNext( &adrNext, &datarate, &txPower, &nbTrans, &adrAckCounter );
3868 
3869     txInfo->CurrentPossiblePayloadSize = GetMaxAppPayloadWithoutFOptsLength( datarate );
3870 
3871     if( LoRaMacCommandsGetSizeSerializedCmds( &macCmdsSize ) != LORAMAC_COMMANDS_SUCCESS )
3872     {
3873         return LORAMAC_STATUS_MAC_COMMAD_ERROR;
3874     }
3875 
3876     // Verify if the MAC commands fit into the FOpts and into the maximum payload.
3877     if( ( LORA_MAC_COMMAND_MAX_FOPTS_LENGTH >= macCmdsSize ) && ( txInfo->CurrentPossiblePayloadSize >= macCmdsSize ) )
3878     {
3879         txInfo->MaxPossibleApplicationDataSize = txInfo->CurrentPossiblePayloadSize - macCmdsSize;
3880 
3881         // Verify if the application data together with MAC command fit into the maximum payload.
3882         if( txInfo->CurrentPossiblePayloadSize >= ( macCmdsSize + size ) )
3883         {
3884             return LORAMAC_STATUS_OK;
3885         }
3886         else
3887         {
3888            return LORAMAC_STATUS_LENGTH_ERROR;
3889         }
3890     }
3891     else
3892     {
3893         txInfo->MaxPossibleApplicationDataSize = 0;
3894         return LORAMAC_STATUS_LENGTH_ERROR;
3895     }
3896 }
3897 
LoRaMacMibGetRequestConfirm(MibRequestConfirm_t * mibGet)3898 LoRaMacStatus_t LoRaMacMibGetRequestConfirm( MibRequestConfirm_t* mibGet )
3899 {
3900     LoRaMacStatus_t status = LORAMAC_STATUS_OK;
3901     GetPhyParams_t getPhy;
3902     PhyParam_t phyParam;
3903 
3904     if( mibGet == NULL )
3905     {
3906         return LORAMAC_STATUS_PARAMETER_INVALID;
3907     }
3908 
3909     switch( mibGet->Type )
3910     {
3911         case MIB_DEVICE_CLASS:
3912         {
3913             mibGet->Param.Class = Nvm.MacGroup2.DeviceClass;
3914             break;
3915         }
3916         case MIB_NETWORK_ACTIVATION:
3917         {
3918             mibGet->Param.NetworkActivation = Nvm.MacGroup2.NetworkActivation;
3919             break;
3920         }
3921         case MIB_DEV_EUI:
3922         {
3923             mibGet->Param.DevEui = SecureElementGetDevEui( );
3924             break;
3925         }
3926         case MIB_JOIN_EUI:
3927         {
3928             mibGet->Param.JoinEui = SecureElementGetJoinEui( );
3929             break;
3930         }
3931         case MIB_SE_PIN:
3932         {
3933             mibGet->Param.SePin = SecureElementGetPin( );
3934             break;
3935         }
3936         case MIB_ADR:
3937         {
3938             mibGet->Param.AdrEnable = Nvm.MacGroup2.AdrCtrlOn;
3939             break;
3940         }
3941         case MIB_NET_ID:
3942         {
3943             mibGet->Param.NetID = Nvm.MacGroup2.NetID;
3944             break;
3945         }
3946         case MIB_DEV_ADDR:
3947         {
3948             mibGet->Param.DevAddr = Nvm.MacGroup2.DevAddr;
3949             break;
3950         }
3951         case MIB_PUBLIC_NETWORK:
3952         {
3953             mibGet->Param.EnablePublicNetwork = Nvm.MacGroup2.PublicNetwork;
3954             break;
3955         }
3956         case MIB_CHANNELS:
3957         {
3958             getPhy.Attribute = PHY_CHANNELS;
3959             phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
3960 
3961             mibGet->Param.ChannelList = phyParam.Channels;
3962             break;
3963         }
3964         case MIB_RX2_CHANNEL:
3965         {
3966             mibGet->Param.Rx2Channel = Nvm.MacGroup2.MacParams.Rx2Channel;
3967             break;
3968         }
3969         case MIB_RX2_DEFAULT_CHANNEL:
3970         {
3971             mibGet->Param.Rx2Channel = Nvm.MacGroup2.MacParamsDefaults.Rx2Channel;
3972             break;
3973         }
3974         case MIB_RXC_CHANNEL:
3975         {
3976             mibGet->Param.RxCChannel = Nvm.MacGroup2.MacParams.RxCChannel;
3977             break;
3978         }
3979         case MIB_RXC_DEFAULT_CHANNEL:
3980         {
3981             mibGet->Param.RxCChannel = Nvm.MacGroup2.MacParamsDefaults.RxCChannel;
3982             break;
3983         }
3984         case MIB_CHANNELS_DEFAULT_MASK:
3985         {
3986             getPhy.Attribute = PHY_CHANNELS_DEFAULT_MASK;
3987             phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
3988 
3989             mibGet->Param.ChannelsDefaultMask = phyParam.ChannelsMask;
3990             break;
3991         }
3992         case MIB_CHANNELS_MASK:
3993         {
3994             getPhy.Attribute = PHY_CHANNELS_MASK;
3995             phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
3996 
3997             mibGet->Param.ChannelsMask = phyParam.ChannelsMask;
3998             break;
3999         }
4000         case MIB_CHANNELS_NB_TRANS:
4001         {
4002             mibGet->Param.ChannelsNbTrans = Nvm.MacGroup2.MacParams.ChannelsNbTrans;
4003             break;
4004         }
4005         case MIB_MAX_RX_WINDOW_DURATION:
4006         {
4007             mibGet->Param.MaxRxWindow = Nvm.MacGroup2.MacParams.MaxRxWindow;
4008             break;
4009         }
4010         case MIB_RECEIVE_DELAY_1:
4011         {
4012             mibGet->Param.ReceiveDelay1 = Nvm.MacGroup2.MacParams.ReceiveDelay1;
4013             break;
4014         }
4015         case MIB_RECEIVE_DELAY_2:
4016         {
4017             mibGet->Param.ReceiveDelay2 = Nvm.MacGroup2.MacParams.ReceiveDelay2;
4018             break;
4019         }
4020         case MIB_JOIN_ACCEPT_DELAY_1:
4021         {
4022             mibGet->Param.JoinAcceptDelay1 = Nvm.MacGroup2.MacParams.JoinAcceptDelay1;
4023             break;
4024         }
4025         case MIB_JOIN_ACCEPT_DELAY_2:
4026         {
4027             mibGet->Param.JoinAcceptDelay2 = Nvm.MacGroup2.MacParams.JoinAcceptDelay2;
4028             break;
4029         }
4030         case MIB_CHANNELS_MIN_TX_DATARATE:
4031         {
4032             getPhy.Attribute = PHY_MIN_TX_DR;
4033             getPhy.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime;
4034             phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
4035 
4036             mibGet->Param.ChannelsMinTxDatarate = phyParam.Value;
4037             break;
4038         }
4039         case MIB_CHANNELS_DEFAULT_DATARATE:
4040         {
4041             mibGet->Param.ChannelsDefaultDatarate = Nvm.MacGroup2.ChannelsDatarateDefault;
4042             break;
4043         }
4044         case MIB_CHANNELS_DATARATE:
4045         {
4046             mibGet->Param.ChannelsDatarate = Nvm.MacGroup1.ChannelsDatarate;
4047             break;
4048         }
4049         case MIB_CHANNELS_DEFAULT_TX_POWER:
4050         {
4051             mibGet->Param.ChannelsDefaultTxPower = Nvm.MacGroup2.ChannelsTxPowerDefault;
4052             break;
4053         }
4054         case MIB_CHANNELS_TX_POWER:
4055         {
4056             mibGet->Param.ChannelsTxPower = Nvm.MacGroup1.ChannelsTxPower;
4057             break;
4058         }
4059         case MIB_SYSTEM_MAX_RX_ERROR:
4060         {
4061             mibGet->Param.SystemMaxRxError = Nvm.MacGroup2.MacParams.SystemMaxRxError;
4062             break;
4063         }
4064         case MIB_MIN_RX_SYMBOLS:
4065         {
4066             mibGet->Param.MinRxSymbols = Nvm.MacGroup2.MacParams.MinRxSymbols;
4067             break;
4068         }
4069         case MIB_ANTENNA_GAIN:
4070         {
4071             mibGet->Param.AntennaGain = Nvm.MacGroup2.MacParams.AntennaGain;
4072             break;
4073         }
4074         case MIB_NVM_CTXS:
4075         {
4076             mibGet->Param.Contexts = GetNvmData( );
4077             break;
4078         }
4079         case MIB_DEFAULT_ANTENNA_GAIN:
4080         {
4081             mibGet->Param.DefaultAntennaGain = Nvm.MacGroup2.MacParamsDefaults.AntennaGain;
4082             break;
4083         }
4084         case MIB_LORAWAN_VERSION:
4085         {
4086             mibGet->Param.LrWanVersion.LoRaWan = Nvm.MacGroup2.Version;
4087             mibGet->Param.LrWanVersion.LoRaWanRegion = RegionGetVersion( );
4088             break;
4089         }
4090         case MIB_IS_CERT_FPORT_ON:
4091         {
4092             mibGet->Param.IsCertPortOn = Nvm.MacGroup2.IsCertPortOn;
4093             break;
4094         }
4095         case MIB_REJOIN_0_CYCLE:
4096         {
4097             mibGet->Param.Rejoin0CycleInSec = Nvm.MacGroup2.Rejoin0CycleInSec;
4098             break;
4099         }
4100         case MIB_REJOIN_1_CYCLE:
4101         {
4102             mibGet->Param.Rejoin1CycleInSec = Nvm.MacGroup2.Rejoin1CycleInSec;
4103             break;
4104         }
4105         case MIB_REJOIN_2_CYCLE:
4106         {
4107             mibGet->Param.Rejoin2CycleInSec = Nvm.MacGroup2.Rejoin2CycleInSec;
4108             break;
4109         }
4110         default:
4111         {
4112             status = LoRaMacClassBMibGetRequestConfirm( mibGet );
4113             break;
4114         }
4115     }
4116     return status;
4117 }
4118 
LoRaMacMibSetRequestConfirm(MibRequestConfirm_t * mibSet)4119 LoRaMacStatus_t LoRaMacMibSetRequestConfirm( MibRequestConfirm_t* mibSet )
4120 {
4121     LoRaMacStatus_t status = LORAMAC_STATUS_OK;
4122     ChanMaskSetParams_t chanMaskSet;
4123     VerifyParams_t verify;
4124 
4125     if( mibSet == NULL )
4126     {
4127         return LORAMAC_STATUS_PARAMETER_INVALID;
4128     }
4129     if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING )
4130     {
4131         return LORAMAC_STATUS_BUSY;
4132     }
4133 
4134     switch( mibSet->Type )
4135     {
4136         case MIB_DEVICE_CLASS:
4137         {
4138             status = SwitchClass( mibSet->Param.Class );
4139             break;
4140         }
4141         case MIB_NETWORK_ACTIVATION:
4142         {
4143             if( mibSet->Param.NetworkActivation != ACTIVATION_TYPE_OTAA  )
4144             {
4145                 Nvm.MacGroup2.NetworkActivation = mibSet->Param.NetworkActivation;
4146             }
4147             else
4148             {   // Do not allow to set ACTIVATION_TYPE_OTAA since the MAC will set it automatically after a successful join process.
4149                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4150             }
4151             break;
4152         }
4153         case MIB_DEV_EUI:
4154         {
4155             if( SecureElementSetDevEui( mibSet->Param.DevEui ) != SECURE_ELEMENT_SUCCESS )
4156             {
4157                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4158             }
4159             break;
4160         }
4161         case MIB_JOIN_EUI:
4162         {
4163             if( SecureElementSetJoinEui( mibSet->Param.JoinEui ) != SECURE_ELEMENT_SUCCESS )
4164             {
4165                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4166             }
4167             break;
4168         }
4169         case MIB_SE_PIN:
4170         {
4171             if( SecureElementSetPin( mibSet->Param.SePin ) != SECURE_ELEMENT_SUCCESS )
4172             {
4173                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4174             }
4175             break;
4176         }
4177         case MIB_ADR:
4178         {
4179             Nvm.MacGroup2.AdrCtrlOn = mibSet->Param.AdrEnable;
4180             break;
4181         }
4182         case MIB_NET_ID:
4183         {
4184             Nvm.MacGroup2.NetID = mibSet->Param.NetID;
4185             break;
4186         }
4187         case MIB_DEV_ADDR:
4188         {
4189             Nvm.MacGroup2.DevAddr = mibSet->Param.DevAddr;
4190             break;
4191         }
4192         case MIB_APP_KEY:
4193         {
4194             if( mibSet->Param.AppKey != NULL )
4195             {
4196                 if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( APP_KEY, mibSet->Param.AppKey ) )
4197                 {
4198                     return LORAMAC_STATUS_CRYPTO_ERROR;
4199                 }
4200             }
4201             else
4202             {
4203                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4204             }
4205             break;
4206         }
4207         case MIB_NWK_KEY:
4208         {
4209             if( mibSet->Param.NwkKey != NULL )
4210             {
4211                 if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( NWK_KEY, mibSet->Param.NwkKey ) )
4212                 {
4213                     return LORAMAC_STATUS_CRYPTO_ERROR;
4214                 }
4215             }
4216             else
4217             {
4218                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4219             }
4220             break;
4221         }
4222         case MIB_J_S_INT_KEY:
4223         {
4224             if( mibSet->Param.JSIntKey != NULL )
4225             {
4226                 if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( J_S_INT_KEY, mibSet->Param.JSIntKey ) )
4227                 {
4228                     return LORAMAC_STATUS_CRYPTO_ERROR;
4229                 }
4230             }
4231             else
4232             {
4233                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4234             }
4235             break;
4236         }
4237         case MIB_J_S_ENC_KEY:
4238         {
4239             if( mibSet->Param.JSEncKey != NULL )
4240             {
4241                 if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( J_S_ENC_KEY, mibSet->Param.JSEncKey ) )
4242                 {
4243                     return LORAMAC_STATUS_CRYPTO_ERROR;
4244                 }
4245             }
4246             else
4247             {
4248                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4249             }
4250             break;
4251         }
4252         case MIB_F_NWK_S_INT_KEY:
4253         {
4254             if( mibSet->Param.FNwkSIntKey != NULL )
4255             {
4256                 if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( F_NWK_S_INT_KEY, mibSet->Param.FNwkSIntKey ) )
4257                 {
4258                     return LORAMAC_STATUS_CRYPTO_ERROR;
4259                 }
4260             }
4261             else
4262             {
4263                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4264             }
4265             break;
4266         }
4267         case MIB_S_NWK_S_INT_KEY:
4268         {
4269             if( mibSet->Param.SNwkSIntKey != NULL )
4270             {
4271                 if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( S_NWK_S_INT_KEY, mibSet->Param.SNwkSIntKey ) )
4272                 {
4273                     return LORAMAC_STATUS_CRYPTO_ERROR;
4274                 }
4275             }
4276             else
4277             {
4278                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4279             }
4280             break;
4281         }
4282         case MIB_NWK_S_ENC_KEY:
4283         {
4284             if( mibSet->Param.NwkSEncKey != NULL )
4285             {
4286                 if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( NWK_S_ENC_KEY, mibSet->Param.NwkSEncKey ) )
4287                 {
4288                     return LORAMAC_STATUS_CRYPTO_ERROR;
4289                 }
4290             }
4291             else
4292             {
4293                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4294             }
4295             break;
4296         }
4297         case MIB_APP_S_KEY:
4298         {
4299             if( mibSet->Param.AppSKey != NULL )
4300             {
4301                 if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( APP_S_KEY, mibSet->Param.AppSKey ) )
4302                 {
4303                     return LORAMAC_STATUS_CRYPTO_ERROR;
4304                 }
4305             }
4306             else
4307             {
4308                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4309             }
4310             break;
4311         }
4312         case MIB_MC_KE_KEY:
4313         {
4314             if( mibSet->Param.McKEKey != NULL )
4315             {
4316                 if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_KE_KEY, mibSet->Param.McKEKey ) )
4317                 {
4318                     return LORAMAC_STATUS_CRYPTO_ERROR;
4319                 }
4320             }
4321             else
4322             {
4323                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4324             }
4325             break;
4326         }
4327         case MIB_MC_KEY_0:
4328         {
4329             if( mibSet->Param.McKey0 != NULL )
4330             {
4331                 if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_KEY_0, mibSet->Param.McKey0 ) )
4332                 {
4333                     return LORAMAC_STATUS_CRYPTO_ERROR;
4334                 }
4335             }
4336             else
4337             {
4338                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4339             }
4340             break;
4341         }
4342         case MIB_MC_APP_S_KEY_0:
4343         {
4344             if( mibSet->Param.McAppSKey0 != NULL )
4345             {
4346                 if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_APP_S_KEY_0, mibSet->Param.McAppSKey0 ) )
4347                 {
4348                     return LORAMAC_STATUS_CRYPTO_ERROR;
4349                 }
4350             }
4351             else
4352             {
4353                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4354             }
4355             break;
4356         }
4357         case MIB_MC_NWK_S_KEY_0:
4358         {
4359             if( mibSet->Param.McNwkSKey0 != NULL )
4360             {
4361                 if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_NWK_S_KEY_0, mibSet->Param.McNwkSKey0 ) )
4362                 {
4363                     return LORAMAC_STATUS_CRYPTO_ERROR;
4364                 }
4365             }
4366             else
4367             {
4368                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4369             }
4370             break;
4371         }
4372         case MIB_MC_KEY_1:
4373         {
4374             if( mibSet->Param.McKey1 != NULL )
4375             {
4376                 if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_KEY_1, mibSet->Param.McKey1 ) )
4377                 {
4378                     return LORAMAC_STATUS_CRYPTO_ERROR;
4379                 }
4380             }
4381             else
4382             {
4383                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4384             }
4385             break;
4386         }
4387         case MIB_MC_APP_S_KEY_1:
4388         {
4389             if( mibSet->Param.McAppSKey1 != NULL )
4390             {
4391                 if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_APP_S_KEY_1, mibSet->Param.McAppSKey1 ) )
4392                 {
4393                     return LORAMAC_STATUS_CRYPTO_ERROR;
4394                 }
4395             }
4396             else
4397             {
4398                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4399             }
4400             break;
4401         }
4402         case MIB_MC_NWK_S_KEY_1:
4403         {
4404             if( mibSet->Param.McNwkSKey1 != NULL )
4405             {
4406                 if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_NWK_S_KEY_1, mibSet->Param.McNwkSKey1 ) )
4407                 {
4408                     return LORAMAC_STATUS_CRYPTO_ERROR;
4409                 }
4410             }
4411             else
4412             {
4413                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4414             }
4415             break;
4416         }
4417         case MIB_MC_KEY_2:
4418         {
4419             if( mibSet->Param.McKey2 != NULL )
4420             {
4421                 if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_KEY_2, mibSet->Param.McKey2 ) )
4422                 {
4423                     return LORAMAC_STATUS_CRYPTO_ERROR;
4424                 }
4425             }
4426             else
4427             {
4428                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4429             }
4430             break;
4431         }
4432         case MIB_MC_APP_S_KEY_2:
4433         {
4434             if( mibSet->Param.McAppSKey2 != NULL )
4435             {
4436                 if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_APP_S_KEY_2, mibSet->Param.McAppSKey2 ) )
4437                 {
4438                     return LORAMAC_STATUS_CRYPTO_ERROR;
4439                 }
4440             }
4441             else
4442             {
4443                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4444             }
4445             break;
4446         }
4447         case MIB_MC_NWK_S_KEY_2:
4448         {
4449             if( mibSet->Param.McNwkSKey2 != NULL )
4450             {
4451                 if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_NWK_S_KEY_2, mibSet->Param.McNwkSKey2 ) )
4452                 {
4453                     return LORAMAC_STATUS_CRYPTO_ERROR;
4454                 }
4455             }
4456             else
4457             {
4458                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4459             }
4460             break;
4461         }
4462         case MIB_MC_KEY_3:
4463         {
4464             if( mibSet->Param.McKey3 != NULL )
4465             {
4466                 if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_KEY_3, mibSet->Param.McKey3 ) )
4467                 {
4468                     return LORAMAC_STATUS_CRYPTO_ERROR;
4469                 }
4470             }
4471             else
4472             {
4473                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4474             }
4475             break;
4476         }
4477         case MIB_MC_APP_S_KEY_3:
4478         {
4479             if( mibSet->Param.McAppSKey3 != NULL )
4480             {
4481                 if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_APP_S_KEY_3, mibSet->Param.McAppSKey3 ) )
4482                 {
4483                     return LORAMAC_STATUS_CRYPTO_ERROR;
4484                 }
4485             }
4486             else
4487             {
4488                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4489             }
4490             break;
4491         }
4492         case MIB_MC_NWK_S_KEY_3:
4493         {
4494             if( mibSet->Param.McNwkSKey3 != NULL )
4495             {
4496                 if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_NWK_S_KEY_3, mibSet->Param.McNwkSKey3 ) )
4497                 {
4498                     return LORAMAC_STATUS_CRYPTO_ERROR;
4499                 }
4500             }
4501             else
4502             {
4503                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4504             }
4505             break;
4506         }
4507         case MIB_PUBLIC_NETWORK:
4508         {
4509             Nvm.MacGroup2.PublicNetwork = mibSet->Param.EnablePublicNetwork;
4510             Radio.SetPublicNetwork( Nvm.MacGroup2.PublicNetwork );
4511             break;
4512         }
4513         case MIB_RX2_CHANNEL:
4514         {
4515             verify.DatarateParams.Datarate = mibSet->Param.Rx2Channel.Datarate;
4516             verify.DatarateParams.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime;
4517 
4518             if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_RX_DR ) == true )
4519             {
4520                 Nvm.MacGroup2.MacParams.Rx2Channel = mibSet->Param.Rx2Channel;
4521             }
4522             else
4523             {
4524                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4525             }
4526             break;
4527         }
4528         case MIB_RX2_DEFAULT_CHANNEL:
4529         {
4530             verify.DatarateParams.Datarate = mibSet->Param.Rx2Channel.Datarate;
4531             verify.DatarateParams.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime;
4532 
4533             if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_RX_DR ) == true )
4534             {
4535                 Nvm.MacGroup2.MacParamsDefaults.Rx2Channel = mibSet->Param.Rx2DefaultChannel;
4536             }
4537             else
4538             {
4539                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4540             }
4541             break;
4542         }
4543         case MIB_RXC_CHANNEL:
4544         {
4545             verify.DatarateParams.Datarate = mibSet->Param.RxCChannel.Datarate;
4546             verify.DatarateParams.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime;
4547 
4548             if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_RX_DR ) == true )
4549             {
4550                 Nvm.MacGroup2.MacParams.RxCChannel = mibSet->Param.RxCChannel;
4551 
4552                 if( ( Nvm.MacGroup2.DeviceClass == CLASS_C ) && ( Nvm.MacGroup2.NetworkActivation != ACTIVATION_TYPE_NONE ) )
4553                 {
4554                     // We can only compute the RX window parameters directly, if we are already
4555                     // in class c mode and joined. We cannot setup an RX window in case of any other
4556                     // class type.
4557                     // Set the radio into sleep mode in case we are still in RX mode
4558                     Radio.Sleep( );
4559 
4560                     OpenContinuousRxCWindow( );
4561                 }
4562             }
4563             else
4564             {
4565                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4566             }
4567             break;
4568         }
4569         case MIB_RXC_DEFAULT_CHANNEL:
4570         {
4571             verify.DatarateParams.Datarate = mibSet->Param.RxCChannel.Datarate;
4572             verify.DatarateParams.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime;
4573 
4574             if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_RX_DR ) == true )
4575             {
4576                 Nvm.MacGroup2.MacParamsDefaults.RxCChannel = mibSet->Param.RxCDefaultChannel;
4577             }
4578             else
4579             {
4580                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4581             }
4582             break;
4583         }
4584         case MIB_CHANNELS_DEFAULT_MASK:
4585         {
4586             chanMaskSet.ChannelsMaskIn = mibSet->Param.ChannelsDefaultMask;
4587             chanMaskSet.ChannelsMaskType = CHANNELS_DEFAULT_MASK;
4588 
4589             if( RegionChanMaskSet( Nvm.MacGroup2.Region, &chanMaskSet ) == false )
4590             {
4591                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4592             }
4593             break;
4594         }
4595         case MIB_CHANNELS_MASK:
4596         {
4597             chanMaskSet.ChannelsMaskIn = mibSet->Param.ChannelsMask;
4598             chanMaskSet.ChannelsMaskType = CHANNELS_MASK;
4599 
4600             if( RegionChanMaskSet( Nvm.MacGroup2.Region, &chanMaskSet ) == false )
4601             {
4602                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4603             }
4604             break;
4605         }
4606         case MIB_CHANNELS_NB_TRANS:
4607         {
4608             if( ( mibSet->Param.ChannelsNbTrans >= 1 ) &&
4609                 ( mibSet->Param.ChannelsNbTrans <= 15 ) )
4610             {
4611                 Nvm.MacGroup2.MacParams.ChannelsNbTrans = mibSet->Param.ChannelsNbTrans;
4612             }
4613             else
4614             {
4615                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4616             }
4617             break;
4618         }
4619         case MIB_MAX_RX_WINDOW_DURATION:
4620         {
4621             Nvm.MacGroup2.MacParams.MaxRxWindow = mibSet->Param.MaxRxWindow;
4622             break;
4623         }
4624         case MIB_RECEIVE_DELAY_1:
4625         {
4626             Nvm.MacGroup2.MacParams.ReceiveDelay1 = mibSet->Param.ReceiveDelay1;
4627             break;
4628         }
4629         case MIB_RECEIVE_DELAY_2:
4630         {
4631             Nvm.MacGroup2.MacParams.ReceiveDelay2 = mibSet->Param.ReceiveDelay2;
4632             break;
4633         }
4634         case MIB_JOIN_ACCEPT_DELAY_1:
4635         {
4636             Nvm.MacGroup2.MacParams.JoinAcceptDelay1 = mibSet->Param.JoinAcceptDelay1;
4637             break;
4638         }
4639         case MIB_JOIN_ACCEPT_DELAY_2:
4640         {
4641             Nvm.MacGroup2.MacParams.JoinAcceptDelay2 = mibSet->Param.JoinAcceptDelay2;
4642             break;
4643         }
4644         case MIB_CHANNELS_DEFAULT_DATARATE:
4645         {
4646             verify.DatarateParams.Datarate = mibSet->Param.ChannelsDefaultDatarate;
4647 
4648             if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_DEF_TX_DR ) == true )
4649             {
4650                 Nvm.MacGroup2.ChannelsDatarateDefault = verify.DatarateParams.Datarate;
4651             }
4652             else
4653             {
4654                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4655             }
4656             break;
4657         }
4658         case MIB_CHANNELS_DATARATE:
4659         {
4660             verify.DatarateParams.Datarate = mibSet->Param.ChannelsDatarate;
4661             verify.DatarateParams.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime;
4662 
4663             if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_TX_DR ) == true )
4664             {
4665                 Nvm.MacGroup1.ChannelsDatarate = verify.DatarateParams.Datarate;
4666             }
4667             else
4668             {
4669                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4670             }
4671             break;
4672         }
4673         case MIB_CHANNELS_DEFAULT_TX_POWER:
4674         {
4675             verify.TxPower = mibSet->Param.ChannelsDefaultTxPower;
4676 
4677             if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_DEF_TX_POWER ) == true )
4678             {
4679                 Nvm.MacGroup2.ChannelsTxPowerDefault = verify.TxPower;
4680             }
4681             else
4682             {
4683                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4684             }
4685             break;
4686         }
4687         case MIB_CHANNELS_TX_POWER:
4688         {
4689             verify.TxPower = mibSet->Param.ChannelsTxPower;
4690 
4691             if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_TX_POWER ) == true )
4692             {
4693                 Nvm.MacGroup1.ChannelsTxPower = verify.TxPower;
4694             }
4695             else
4696             {
4697                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4698             }
4699             break;
4700         }
4701         case MIB_SYSTEM_MAX_RX_ERROR:
4702         {
4703             Nvm.MacGroup2.MacParams.SystemMaxRxError = Nvm.MacGroup2.MacParamsDefaults.SystemMaxRxError = mibSet->Param.SystemMaxRxError;
4704             break;
4705         }
4706         case MIB_MIN_RX_SYMBOLS:
4707         {
4708             Nvm.MacGroup2.MacParams.MinRxSymbols = Nvm.MacGroup2.MacParamsDefaults.MinRxSymbols = mibSet->Param.MinRxSymbols;
4709             break;
4710         }
4711         case MIB_ANTENNA_GAIN:
4712         {
4713             Nvm.MacGroup2.MacParams.AntennaGain = mibSet->Param.AntennaGain;
4714             break;
4715         }
4716         case MIB_DEFAULT_ANTENNA_GAIN:
4717         {
4718             Nvm.MacGroup2.MacParamsDefaults.AntennaGain = mibSet->Param.DefaultAntennaGain;
4719             break;
4720         }
4721         case MIB_NVM_CTXS:
4722         {
4723             if( mibSet->Param.Contexts != 0 )
4724             {
4725                 status = RestoreNvmData( mibSet->Param.Contexts );
4726             }
4727             else
4728             {
4729                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4730             }
4731             break;
4732         }
4733         case MIB_ABP_LORAWAN_VERSION:
4734         {
4735             if( mibSet->Param.AbpLrWanVersion.Fields.Minor <= 1 )
4736             {
4737                 Nvm.MacGroup2.Version = mibSet->Param.AbpLrWanVersion;
4738 
4739                 if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetLrWanVersion( mibSet->Param.AbpLrWanVersion ) )
4740                 {
4741                     return LORAMAC_STATUS_CRYPTO_ERROR;
4742                 }
4743             }
4744             else
4745             {
4746                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4747             }
4748             break;
4749         }
4750         case MIB_IS_CERT_FPORT_ON:
4751         {
4752             Nvm.MacGroup2.IsCertPortOn = mibSet->Param.IsCertPortOn;
4753             break;
4754         }
4755         case MIB_REJOIN_0_CYCLE:
4756         {
4757             if( ConvertRejoinCycleTime( Nvm.MacGroup2.Rejoin0CycleInSec, &MacCtx.Rejoin0CycleTime ) == true )
4758             {
4759                 TimerSetValue( &MacCtx.Rejoin0CycleTimer, MacCtx.Rejoin0CycleTime );
4760                 TimerStart( &MacCtx.Rejoin0CycleTimer );
4761             }
4762             else
4763             {
4764                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4765             }
4766             break;
4767         }
4768         case MIB_REJOIN_1_CYCLE:
4769         {
4770            if( ConvertRejoinCycleTime( Nvm.MacGroup2.Rejoin1CycleInSec, &MacCtx.Rejoin1CycleTime ) == true )
4771             {
4772                 TimerSetValue( &MacCtx.Rejoin1CycleTimer, MacCtx.Rejoin1CycleTime );
4773                 TimerStart( &MacCtx.Rejoin1CycleTimer );
4774             }
4775             else
4776             {
4777                 status = LORAMAC_STATUS_PARAMETER_INVALID;
4778             }
4779             break;
4780         }
4781         default:
4782         {
4783             status = LoRaMacMibClassBSetRequestConfirm( mibSet );
4784             break;
4785         }
4786     }
4787 
4788     if( status == LORAMAC_STATUS_OK )
4789     {
4790         // Handle NVM potential changes
4791         MacCtx.MacFlags.Bits.NvmHandle = 1;
4792     }
4793     return status;
4794 }
4795 
LoRaMacChannelAdd(uint8_t id,ChannelParams_t params)4796 LoRaMacStatus_t LoRaMacChannelAdd( uint8_t id, ChannelParams_t params )
4797 {
4798     ChannelAddParams_t channelAdd;
4799 
4800     // Validate if the MAC is in a correct state
4801     if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING )
4802     {
4803         if( ( MacCtx.MacState & LORAMAC_TX_CONFIG ) != LORAMAC_TX_CONFIG )
4804         {
4805             return LORAMAC_STATUS_BUSY;
4806         }
4807     }
4808 
4809     channelAdd.NewChannel = &params;
4810     channelAdd.ChannelId = id;
4811     return RegionChannelAdd( Nvm.MacGroup2.Region, &channelAdd );
4812 }
4813 
LoRaMacChannelRemove(uint8_t id)4814 LoRaMacStatus_t LoRaMacChannelRemove( uint8_t id )
4815 {
4816     ChannelRemoveParams_t channelRemove;
4817 
4818     if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING )
4819     {
4820         if( ( MacCtx.MacState & LORAMAC_TX_CONFIG ) != LORAMAC_TX_CONFIG )
4821         {
4822             return LORAMAC_STATUS_BUSY;
4823         }
4824     }
4825 
4826     channelRemove.ChannelId = id;
4827 
4828     if( RegionChannelsRemove( Nvm.MacGroup2.Region, &channelRemove ) == false )
4829     {
4830         return LORAMAC_STATUS_PARAMETER_INVALID;
4831     }
4832     return LORAMAC_STATUS_OK;
4833 }
4834 
LoRaMacMcChannelSetup(McChannelParams_t * channel)4835 LoRaMacStatus_t LoRaMacMcChannelSetup( McChannelParams_t *channel )
4836 {
4837     if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING )
4838     {
4839         return LORAMAC_STATUS_BUSY;
4840     }
4841 
4842     if( channel->GroupID >= LORAMAC_MAX_MC_CTX )
4843     {
4844         return LORAMAC_STATUS_MC_GROUP_UNDEFINED;
4845     }
4846 
4847     Nvm.MacGroup2.MulticastChannelList[channel->GroupID].ChannelParams = *channel;
4848 
4849     if( channel->IsRemotelySetup == true )
4850     {
4851         const KeyIdentifier_t mcKeys[LORAMAC_MAX_MC_CTX] = { MC_KEY_0, MC_KEY_1, MC_KEY_2, MC_KEY_3 };
4852         if( LoRaMacCryptoSetKey( mcKeys[channel->GroupID], channel->McKeys.McKeyE ) != LORAMAC_CRYPTO_SUCCESS )
4853         {
4854             return LORAMAC_STATUS_CRYPTO_ERROR;
4855         }
4856 
4857         if( LoRaMacCryptoDeriveMcSessionKeyPair( channel->GroupID, channel->Address ) != LORAMAC_CRYPTO_SUCCESS )
4858         {
4859             return LORAMAC_STATUS_CRYPTO_ERROR;
4860         }
4861     }
4862     else
4863     {
4864         const KeyIdentifier_t mcAppSKeys[LORAMAC_MAX_MC_CTX] = { MC_APP_S_KEY_0, MC_APP_S_KEY_1, MC_APP_S_KEY_2, MC_APP_S_KEY_3 };
4865         const KeyIdentifier_t mcNwkSKeys[LORAMAC_MAX_MC_CTX] = { MC_NWK_S_KEY_0, MC_NWK_S_KEY_1, MC_NWK_S_KEY_2, MC_NWK_S_KEY_3 };
4866         if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( mcAppSKeys[channel->GroupID], channel->McKeys.Session.McAppSKey ) )
4867         {
4868             return LORAMAC_STATUS_CRYPTO_ERROR;
4869         }
4870         if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( mcNwkSKeys[channel->GroupID], channel->McKeys.Session.McNwkSKey ) )
4871         {
4872             return LORAMAC_STATUS_CRYPTO_ERROR;
4873         }
4874     }
4875 
4876     // Reset multicast channel downlink counter to initial value.
4877     *Nvm.MacGroup2.MulticastChannelList[channel->GroupID].DownLinkCounter = FCNT_DOWN_INITAL_VALUE;
4878     return LORAMAC_STATUS_OK;
4879 }
4880 
LoRaMacMcChannelDelete(AddressIdentifier_t groupID)4881 LoRaMacStatus_t LoRaMacMcChannelDelete( AddressIdentifier_t groupID )
4882 {
4883     if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING )
4884     {
4885         return LORAMAC_STATUS_BUSY;
4886     }
4887 
4888     if( ( groupID >= LORAMAC_MAX_MC_CTX ) ||
4889         ( Nvm.MacGroup2.MulticastChannelList[groupID].ChannelParams.IsEnabled == false ) )
4890     {
4891         return LORAMAC_STATUS_MC_GROUP_UNDEFINED;
4892     }
4893 
4894     McChannelParams_t channel;
4895 
4896     // Set all channel fields with 0
4897     memset1( ( uint8_t* )&channel, 0, sizeof( McChannelParams_t ) );
4898 
4899     Nvm.MacGroup2.MulticastChannelList[groupID].ChannelParams = channel;
4900     return LORAMAC_STATUS_OK;
4901 }
4902 
LoRaMacMcChannelGetGroupId(uint32_t mcAddress)4903 uint8_t LoRaMacMcChannelGetGroupId( uint32_t mcAddress )
4904 {
4905     for( uint8_t i = 0; i < LORAMAC_MAX_MC_CTX; i++ )
4906     {
4907         if( mcAddress == Nvm.MacGroup2.MulticastChannelList[i].ChannelParams.Address )
4908         {
4909             return i;
4910         }
4911     }
4912     return 0xFF;
4913 }
4914 
LoRaMacMcChannelSetupRxParams(AddressIdentifier_t groupID,McRxParams_t * rxParams,uint8_t * status)4915 LoRaMacStatus_t LoRaMacMcChannelSetupRxParams( AddressIdentifier_t groupID, McRxParams_t *rxParams, uint8_t *status )
4916 {
4917    *status = 0x1C + ( groupID & 0x03 );
4918 
4919     if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING )
4920     {
4921         return LORAMAC_STATUS_BUSY;
4922     }
4923 
4924     if( ( rxParams->Class == CLASS_A ) || ( rxParams->Class > CLASS_C ) )
4925     {
4926         return LORAMAC_STATUS_PARAMETER_INVALID;
4927     }
4928 
4929     if( ( groupID >= LORAMAC_MAX_MC_CTX ) ||
4930         ( Nvm.MacGroup2.MulticastChannelList[groupID].ChannelParams.IsEnabled == false ) )
4931     {
4932         return LORAMAC_STATUS_MC_GROUP_UNDEFINED;
4933     }
4934     *status &= 0x0F; // groupID OK
4935 
4936     VerifyParams_t verify;
4937     // Check datarate
4938     if( rxParams->Class == CLASS_B )
4939     {
4940         verify.DatarateParams.Datarate = rxParams->Params.ClassB.Datarate;
4941     }
4942     else
4943     {
4944         verify.DatarateParams.Datarate = rxParams->Params.ClassC.Datarate;
4945     }
4946     verify.DatarateParams.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime;
4947 
4948     if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_RX_DR ) == true )
4949     {
4950         *status &= 0xFB; // datarate OK
4951     }
4952 
4953     // Check frequency
4954     if( rxParams->Class == CLASS_B )
4955     {
4956         verify.Frequency = rxParams->Params.ClassB.Frequency;
4957     }
4958     else
4959     {
4960         verify.Frequency = rxParams->Params.ClassC.Frequency;
4961     }
4962     if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_FREQUENCY ) == true )
4963     {
4964         *status &= 0xF7; // frequency OK
4965     }
4966 
4967     if( *status == ( groupID & 0x03 ) )
4968     {
4969         // Apply parameters
4970         Nvm.MacGroup2.MulticastChannelList[groupID].ChannelParams.RxParams = *rxParams;
4971     }
4972     else
4973     {
4974         return LORAMAC_STATUS_PARAMETER_INVALID;
4975     }
4976 
4977     if( rxParams->Class == CLASS_B )
4978     {
4979         // Calculate class b parameters
4980         LoRaMacClassBSetMulticastPeriodicity( &Nvm.MacGroup2.MulticastChannelList[groupID] );
4981     }
4982     return LORAMAC_STATUS_OK;
4983 }
4984 
LoRaMacMlmeRequest(MlmeReq_t * mlmeRequest)4985 LoRaMacStatus_t LoRaMacMlmeRequest( MlmeReq_t* mlmeRequest )
4986 {
4987     LoRaMacStatus_t status = LORAMAC_STATUS_SERVICE_UNKNOWN;
4988     MlmeConfirmQueue_t queueElement;
4989     uint8_t macCmdPayload[2] = { 0x00, 0x00 };
4990 
4991     if( mlmeRequest == NULL )
4992     {
4993         return LORAMAC_STATUS_PARAMETER_INVALID;
4994     }
4995     // Initialize mlmeRequest->ReqReturn.DutyCycleWaitTime to 0 in order to
4996     // return a valid value in case the MAC is busy.
4997     mlmeRequest->ReqReturn.DutyCycleWaitTime = 0;
4998 
4999     if( LoRaMacIsBusy( ) == true )
5000     {
5001         return LORAMAC_STATUS_BUSY;
5002     }
5003     if( LoRaMacConfirmQueueIsFull( ) == true )
5004     {
5005         return LORAMAC_STATUS_BUSY;
5006     }
5007 
5008     if( LoRaMacConfirmQueueGetCnt( ) == 0 )
5009     {
5010         memset1( ( uint8_t* ) &MacCtx.MlmeConfirm, 0, sizeof( MacCtx.MlmeConfirm ) );
5011     }
5012     MacCtx.MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
5013 
5014     MacCtx.MacFlags.Bits.MlmeReq = 1;
5015     queueElement.Request = mlmeRequest->Type;
5016     queueElement.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
5017     queueElement.RestrictCommonReadyToHandle = false;
5018     queueElement.ReadyToHandle = false;
5019 
5020     switch( mlmeRequest->Type )
5021     {
5022         case MLME_JOIN:
5023         {
5024             if( ( MacCtx.MacState & LORAMAC_TX_DELAYED ) == LORAMAC_TX_DELAYED )
5025             {
5026                 return LORAMAC_STATUS_BUSY;
5027             }
5028 
5029             if( mlmeRequest->Req.Join.NetworkActivation == ACTIVATION_TYPE_OTAA )
5030             {
5031                 ResetMacParameters( );
5032 
5033             Nvm.MacGroup1.ChannelsDatarate = RegionAlternateDr( Nvm.MacGroup2.Region, mlmeRequest->Req.Join.Datarate, ALTERNATE_DR );
5034 
5035                 queueElement.Status = LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL;
5036 
5037                 status = SendReJoinReq( JOIN_REQ );
5038 
5039                 if( status != LORAMAC_STATUS_OK )
5040                 {
5041                     // Revert back the previous datarate ( mainly used for US915 like regions )
5042                     Nvm.MacGroup1.ChannelsDatarate = RegionAlternateDr( Nvm.MacGroup2.Region, mlmeRequest->Req.Join.Datarate, ALTERNATE_DR_RESTORE );
5043                 }
5044             }
5045             else if( mlmeRequest->Req.Join.NetworkActivation == ACTIVATION_TYPE_ABP )
5046             {
5047                 // Restore default value for ChannelsDatarateChangedLinkAdrReq
5048                 Nvm.MacGroup2.ChannelsDatarateChangedLinkAdrReq = false;
5049 
5050                 // Activate the default channels
5051                 InitDefaultsParams_t params;
5052                 params.Type = INIT_TYPE_ACTIVATE_DEFAULT_CHANNELS;
5053                 RegionInitDefaults( Nvm.MacGroup2.Region, &params );
5054 
5055                 Nvm.MacGroup2.NetworkActivation = mlmeRequest->Req.Join.NetworkActivation;
5056                 queueElement.Status = LORAMAC_EVENT_INFO_STATUS_OK;
5057                 queueElement.ReadyToHandle = true;
5058                 MacCtx.MacCallbacks->MacProcessNotify( );
5059                 MacCtx.MacFlags.Bits.MacDone = 1;
5060                 status = LORAMAC_STATUS_OK;
5061             }
5062             break;
5063         }
5064         case MLME_REJOIN_0:
5065         {
5066             MacCtx.MacFlags.Bits.MlmeReq = 1;
5067             MacCtx.MlmeConfirm.MlmeRequest = mlmeRequest->Type;
5068 
5069             status = SendReJoinReq( REJOIN_REQ_0 );
5070 
5071             break;
5072         }
5073         case MLME_REJOIN_1:
5074         {
5075             MacCtx.MacFlags.Bits.MlmeReq = 1;
5076             MacCtx.MlmeConfirm.MlmeRequest = mlmeRequest->Type;
5077 
5078             status = SendReJoinReq( REJOIN_REQ_1 );
5079 
5080             break;
5081         }
5082         case MLME_LINK_CHECK:
5083         {
5084             // LoRaMac will send this command piggy-pack
5085             status = LORAMAC_STATUS_OK;
5086             if( LoRaMacCommandsAddCmd( MOTE_MAC_LINK_CHECK_REQ, macCmdPayload, 0 ) != LORAMAC_COMMANDS_SUCCESS )
5087             {
5088                 status = LORAMAC_STATUS_MAC_COMMAD_ERROR;
5089             }
5090             break;
5091         }
5092         case MLME_TXCW:
5093         {
5094             status = SetTxContinuousWave( mlmeRequest->Req.TxCw.Timeout, mlmeRequest->Req.TxCw.Frequency, mlmeRequest->Req.TxCw.Power );
5095             break;
5096         }
5097         case MLME_DEVICE_TIME:
5098         {
5099             // LoRaMac will send this command piggy-pack
5100             status = LORAMAC_STATUS_OK;
5101             if( LoRaMacCommandsAddCmd( MOTE_MAC_DEVICE_TIME_REQ, macCmdPayload, 0 ) != LORAMAC_COMMANDS_SUCCESS )
5102             {
5103                 status = LORAMAC_STATUS_MAC_COMMAD_ERROR;
5104             }
5105             break;
5106         }
5107         case MLME_PING_SLOT_INFO:
5108         {
5109             if( Nvm.MacGroup2.DeviceClass == CLASS_A )
5110             {
5111                 uint8_t value = mlmeRequest->Req.PingSlotInfo.PingSlot.Value;
5112 
5113                 // LoRaMac will send this command piggy-pack
5114                 LoRaMacClassBSetPingSlotInfo( mlmeRequest->Req.PingSlotInfo.PingSlot.Fields.Periodicity );
5115                 macCmdPayload[0] = value;
5116                 status = LORAMAC_STATUS_OK;
5117                 if( LoRaMacCommandsAddCmd( MOTE_MAC_PING_SLOT_INFO_REQ, macCmdPayload, 1 ) != LORAMAC_COMMANDS_SUCCESS )
5118                 {
5119                     status = LORAMAC_STATUS_MAC_COMMAD_ERROR;
5120                 }
5121             }
5122             break;
5123         }
5124         case MLME_BEACON_TIMING:
5125         {
5126             // LoRaMac will send this command piggy-pack
5127             status = LORAMAC_STATUS_OK;
5128             if( LoRaMacCommandsAddCmd( MOTE_MAC_BEACON_TIMING_REQ, macCmdPayload, 0 ) != LORAMAC_COMMANDS_SUCCESS )
5129             {
5130                 status = LORAMAC_STATUS_MAC_COMMAD_ERROR;
5131             }
5132             break;
5133         }
5134         case MLME_BEACON_ACQUISITION:
5135         {
5136             // Apply the request
5137             queueElement.RestrictCommonReadyToHandle = true;
5138 
5139             if( LoRaMacClassBIsAcquisitionInProgress( ) == false )
5140             {
5141                 // Start class B algorithm
5142                 LoRaMacClassBSetBeaconState( BEACON_STATE_ACQUISITION );
5143                 LoRaMacClassBBeaconTimerEvent( NULL );
5144 
5145                 status = LORAMAC_STATUS_OK;
5146             }
5147             else
5148             {
5149                 status = LORAMAC_STATUS_BUSY;
5150             }
5151             break;
5152         }
5153         default:
5154             break;
5155     }
5156 
5157     // Fill return structure
5158     mlmeRequest->ReqReturn.DutyCycleWaitTime = MacCtx.DutyCycleWaitTime;
5159 
5160     if( status != LORAMAC_STATUS_OK )
5161     {
5162         if( LoRaMacConfirmQueueGetCnt( ) == 0 )
5163         {
5164             MacCtx.NodeAckRequested = false;
5165             MacCtx.MacFlags.Bits.MlmeReq = 0;
5166         }
5167     }
5168     else
5169     {
5170         LoRaMacConfirmQueueAdd( &queueElement );
5171     }
5172     return status;
5173 }
5174 
LoRaMacMcpsRequest(McpsReq_t * mcpsRequest)5175 LoRaMacStatus_t LoRaMacMcpsRequest( McpsReq_t* mcpsRequest )
5176 {
5177     GetPhyParams_t getPhy;
5178     PhyParam_t phyParam;
5179     LoRaMacStatus_t status = LORAMAC_STATUS_SERVICE_UNKNOWN;
5180     LoRaMacHeader_t macHdr;
5181     VerifyParams_t verify;
5182     uint8_t fPort = 0;
5183     void* fBuffer;
5184     uint16_t fBufferSize;
5185     int8_t datarate = DR_0;
5186     bool readyToSend = false;
5187 
5188     if( mcpsRequest == NULL )
5189     {
5190         return LORAMAC_STATUS_PARAMETER_INVALID;
5191     }
5192     // Initialize mcpsRequest->ReqReturn.DutyCycleWaitTime to 0 in order to
5193     // return a valid value in case the MAC is busy.
5194     mcpsRequest->ReqReturn.DutyCycleWaitTime = 0;
5195 
5196     if( LoRaMacIsBusy( ) == true )
5197     {
5198         return LORAMAC_STATUS_BUSY;
5199     }
5200 
5201     McpsReq_t request = *mcpsRequest;
5202 
5203     macHdr.Value = 0;
5204     memset1( ( uint8_t* ) &MacCtx.McpsConfirm, 0, sizeof( MacCtx.McpsConfirm ) );
5205     MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
5206 
5207     // Apply confirmed downlinks, if the device has not received a valid
5208     // downlink after a join accept.
5209     if( ( Nvm.MacGroup2.NetworkActivation == ACTIVATION_TYPE_OTAA ) &&
5210         ( Nvm.MacGroup2.DeviceClass == CLASS_C ) &&
5211         ( Nvm.MacGroup2.DownlinkReceived == false ) &&
5212         ( request.Type == MCPS_UNCONFIRMED ) )
5213     {
5214         request.Type = MCPS_CONFIRMED;
5215     }
5216 
5217     switch( request.Type )
5218     {
5219         case MCPS_UNCONFIRMED:
5220         {
5221             readyToSend = true;
5222 
5223             macHdr.Bits.MType = FRAME_TYPE_DATA_UNCONFIRMED_UP;
5224             fPort = request.Req.Unconfirmed.fPort;
5225             fBuffer = request.Req.Unconfirmed.fBuffer;
5226             fBufferSize = request.Req.Unconfirmed.fBufferSize;
5227             datarate = request.Req.Unconfirmed.Datarate;
5228             break;
5229         }
5230         case MCPS_CONFIRMED:
5231         {
5232             readyToSend = true;
5233 
5234             macHdr.Bits.MType = FRAME_TYPE_DATA_CONFIRMED_UP;
5235             fPort = request.Req.Confirmed.fPort;
5236             fBuffer = request.Req.Confirmed.fBuffer;
5237             fBufferSize = request.Req.Confirmed.fBufferSize;
5238             datarate = request.Req.Confirmed.Datarate;
5239             break;
5240         }
5241         case MCPS_PROPRIETARY:
5242         {
5243             readyToSend = true;
5244 
5245             macHdr.Bits.MType = FRAME_TYPE_PROPRIETARY;
5246             fBuffer = request.Req.Proprietary.fBuffer;
5247             fBufferSize = request.Req.Proprietary.fBufferSize;
5248             datarate = request.Req.Proprietary.Datarate;
5249             break;
5250         }
5251         default:
5252             break;
5253     }
5254 
5255     // Make sure that the input datarate is compliant
5256     // to the regional specification.
5257     getPhy.Attribute = PHY_MIN_TX_DR;
5258     getPhy.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime;
5259     phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
5260     // Apply the minimum possible datarate.
5261     // Some regions have limitations for the minimum datarate.
5262     datarate = MAX( datarate, ( int8_t )phyParam.Value );
5263 
5264     // Apply minimum datarate in this special case.
5265     if( CheckForMinimumAbpDatarate( Nvm.MacGroup2.AdrCtrlOn, Nvm.MacGroup2.NetworkActivation,
5266                                     Nvm.MacGroup2.ChannelsDatarateChangedLinkAdrReq ) == true )
5267     {
5268         datarate = ( int8_t )phyParam.Value;
5269     }
5270 
5271     if( readyToSend == true )
5272     {
5273         if( ( Nvm.MacGroup2.AdrCtrlOn == false ) ||
5274             ( CheckForMinimumAbpDatarate( Nvm.MacGroup2.AdrCtrlOn, Nvm.MacGroup2.NetworkActivation,
5275                                           Nvm.MacGroup2.ChannelsDatarateChangedLinkAdrReq ) == true ) )
5276         {
5277             verify.DatarateParams.Datarate = datarate;
5278             verify.DatarateParams.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime;
5279 
5280             if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_TX_DR ) == true )
5281             {
5282                 Nvm.MacGroup1.ChannelsDatarate = verify.DatarateParams.Datarate;
5283             }
5284             else
5285             {
5286                 return LORAMAC_STATUS_PARAMETER_INVALID;
5287             }
5288         }
5289 
5290         // Verification of response timeout for class b and class c
5291         LoRaMacHandleResponseTimeout( REGION_COMMON_CLASS_B_C_RESP_TIMEOUT,
5292                                       MacCtx.ResponseTimeoutStartTime );
5293 
5294         status = Send( &macHdr, fPort, fBuffer, fBufferSize );
5295         if( status == LORAMAC_STATUS_OK )
5296         {
5297             MacCtx.McpsConfirm.McpsRequest = request.Type;
5298             MacCtx.MacFlags.Bits.McpsReq = 1;
5299         }
5300         else
5301         {
5302             MacCtx.NodeAckRequested = false;
5303         }
5304     }
5305 
5306     // Fill return structure
5307     mcpsRequest->ReqReturn.DutyCycleWaitTime = MacCtx.DutyCycleWaitTime;
5308 
5309     return status;
5310 }
5311 
ConvertRejoinCycleTime(uint32_t rejoinCycleTime,uint32_t * timeInMiliSec)5312 static bool ConvertRejoinCycleTime( uint32_t rejoinCycleTime, uint32_t* timeInMiliSec )
5313 {
5314     // Our timer implementation do not allow longer times than 4294967295 ms
5315     if( rejoinCycleTime > 4294967 )
5316     {
5317         *timeInMiliSec = rejoinCycleTime * 1000;
5318         return true;
5319     }
5320     else
5321     {
5322         return false;
5323     }
5324 }
5325 
OnRejoin0CycleTimerEvent(void * context)5326 static void OnRejoin0CycleTimerEvent( void* context )
5327 {
5328     TimerStop( &MacCtx.Rejoin0CycleTimer );
5329     ConvertRejoinCycleTime( Nvm.MacGroup2.Rejoin0CycleInSec, &MacCtx.Rejoin0CycleTime );
5330 
5331     SendReJoinReq( REJOIN_REQ_0 );
5332 
5333     TimerSetValue( &MacCtx.Rejoin0CycleTimer, MacCtx.Rejoin0CycleTime );
5334     TimerStart( &MacCtx.Rejoin0CycleTimer );
5335 }
5336 
OnRejoin1CycleTimerEvent(void * context)5337 static void OnRejoin1CycleTimerEvent( void* context )
5338 {
5339     TimerStop( &MacCtx.Rejoin1CycleTimer );
5340     ConvertRejoinCycleTime( Nvm.MacGroup2.Rejoin1CycleInSec, &MacCtx.Rejoin1CycleTime );
5341 
5342     SendReJoinReq( REJOIN_REQ_1 );
5343 
5344     TimerSetValue( &MacCtx.Rejoin1CycleTimer, MacCtx.Rejoin1CycleTime );
5345     TimerStart( &MacCtx.Rejoin1CycleTimer );
5346 }
5347 
OnRejoin2CycleTimerEvent(void * context)5348 static void OnRejoin2CycleTimerEvent( void* context )
5349 {
5350     TimerStop( &MacCtx.Rejoin2CycleTimer );
5351     ConvertRejoinCycleTime( Nvm.MacGroup2.Rejoin2CycleInSec, &MacCtx.Rejoin2CycleTime );
5352 
5353     SendReJoinReq( REJOIN_REQ_2 );
5354 
5355     TimerSetValue( &MacCtx.Rejoin2CycleTimer, MacCtx.Rejoin2CycleTime );
5356     TimerStart( &MacCtx.Rejoin2CycleTimer );
5357 }
5358 
OnForceRejoinReqCycleTimerEvent(void * context)5359 static void OnForceRejoinReqCycleTimerEvent( void* context )
5360 {
5361     if( Nvm.MacGroup1.ForceRejoinRetriesCounter == Nvm.MacGroup2.ForceRejoinMaxRetries )
5362     {
5363         TimerStop( &MacCtx.ForceRejoinReqCycleTimer );
5364         Nvm.MacGroup1.ForceRejoinRetriesCounter = 0;
5365     }
5366 
5367     Nvm.MacGroup1.ForceRejoinRetriesCounter ++;
5368 
5369     if( ( Nvm.MacGroup2.ForceRejoinType == 0 ) || ( Nvm.MacGroup2.ForceRejoinType == 1 ) )
5370     {
5371         SendReJoinReq( REJOIN_REQ_0 );
5372     }
5373     else
5374     {
5375         SendReJoinReq( REJOIN_REQ_2 );
5376     }
5377 }
5378 
LoRaMacTestSetDutyCycleOn(bool enable)5379 void LoRaMacTestSetDutyCycleOn( bool enable )
5380 {
5381     VerifyParams_t verify;
5382 
5383     verify.DutyCycle = enable;
5384 
5385     if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_DUTY_CYCLE ) == true )
5386     {
5387         Nvm.MacGroup2.DutyCycleOn = enable;
5388     }
5389 }
5390 
LoRaMacDeInitialization(void)5391 LoRaMacStatus_t LoRaMacDeInitialization( void )
5392 {
5393     // Check the current state of the LoRaMac
5394     if ( LoRaMacStop( ) == LORAMAC_STATUS_OK )
5395     {
5396         // Stop Timers
5397         TimerStop( &MacCtx.TxDelayedTimer );
5398         TimerStop( &MacCtx.RxWindowTimer1 );
5399         TimerStop( &MacCtx.RxWindowTimer2 );
5400 
5401         // Take care about class B
5402         LoRaMacClassBHaltBeaconing( );
5403 
5404         // Reset Mac parameters
5405         ResetMacParameters( );
5406 
5407         // Switch off Radio
5408         Radio.Sleep( );
5409 
5410         // Return success
5411         return LORAMAC_STATUS_OK;
5412     }
5413     else
5414     {
5415         return LORAMAC_STATUS_BUSY;
5416     }
5417 }
5418