1 /*
2  / _____)             _              | |
3 ( (____  _____ ____ _| |_ _____  ____| |__
4  \____ \| ___ |    (_   _) ___ |/ ___)  _ \
5  _____) ) ____| | | || |_| ____( (___| | | |
6 (______/|_____)_|_|_| \__)_____)\____)_| |_|
7     (C)2013 Semtech
8  ___ _____ _   ___ _  _____ ___  ___  ___ ___
9 / __|_   _/_\ / __| |/ / __/ _ \| _ \/ __| __|
10 \__ \ | |/ _ \ (__| ' <| _| (_) |   / (__| _|
11 |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
12 embedded.connectivity.solutions===============
13 
14 Description: LoRa MAC commands
15 
16 License: Revised BSD License, see LICENSE.TXT file include in the project
17 
18 Maintainer: Miguel Luis ( Semtech ), Daniel Jaeckle ( STACKFORCE ), Johannes Bruder ( STACKFORCE )
19 */
20 #include <stddef.h>
21 
22 #include "utilities.h"
23 #include "LoRaMacCommands.h"
24 #include "LoRaMacConfirmQueue.h"
25 
26 #ifndef NUM_OF_MAC_COMMANDS
27 /*!
28  * Number of MAC Command slots
29  */
30 #define NUM_OF_MAC_COMMANDS 32
31 #endif
32 
33 /*!
34  * Size of the CID field of MAC commands
35  */
36 #define CID_FIELD_SIZE 1
37 
38 /*!
39  *  Mac Commands list structure
40  */
41 typedef struct sMacCommandsList
42 {
43     /*
44      * First element of MAC command list.
45      */
46     MacCommand_t* First;
47     /*
48      * Last element of MAC command list.
49      */
50     MacCommand_t* Last;
51 } MacCommandsList_t;
52 
53 /*!
54  * LoRaMac Commands Context structure
55  */
56 typedef struct sLoRaMacCommandsCtx
57 {
58     /*
59      * List of MAC command elements
60      */
61     MacCommandsList_t MacCommandList;
62     /*
63      * Buffer to store MAC command elements
64      */
65     MacCommand_t MacCommandSlots[NUM_OF_MAC_COMMANDS];
66     /*
67      * Size of all MAC commands serialized as buffer
68      */
69     size_t SerializedCmdsSize;
70 } LoRaMacCommandsCtx_t;
71 
72 /*!
73  * Non-volatile module context.
74  */
75 static LoRaMacCommandsCtx_t CommandsCtx;
76 
77 /* Memory management functions */
78 
79 /*!
80  * \brief Determines if a MAC command slot is free
81  *
82  * \param[IN]     slot           - Slot to check
83  * \retval                       - Status of the operation
84  */
IsSlotFree(const MacCommand_t * slot)85 static bool IsSlotFree( const MacCommand_t* slot )
86 {
87     uint8_t* mem = ( uint8_t* )slot;
88 
89     for( uint16_t size = 0; size < sizeof( MacCommand_t ); size++ )
90     {
91         if( mem[size] != 0x00 )
92         {
93             return false;
94         }
95     }
96     return true;
97 }
98 
99 /*!
100  * \brief Allocates a new MAC command memory slot
101  *
102  * \retval                       - Pointer to slot
103  */
MallocNewMacCommandSlot(void)104 static MacCommand_t* MallocNewMacCommandSlot( void )
105 {
106     uint8_t itr = 0;
107 
108     while( IsSlotFree( ( const MacCommand_t* )&CommandsCtx.MacCommandSlots[itr] ) == false )
109     {
110         itr++;
111         if( itr == NUM_OF_MAC_COMMANDS )
112         {
113             return NULL;
114         }
115     }
116 
117     return &CommandsCtx.MacCommandSlots[itr];
118 }
119 
120 /*!
121  * \brief Free memory slot
122  *
123  * \param[IN]     slot           - Slot to free
124  *
125  * \retval                       - Status of the operation
126  */
FreeMacCommandSlot(MacCommand_t * slot)127 static bool FreeMacCommandSlot( MacCommand_t* slot )
128 {
129     if( slot == NULL )
130     {
131         return false;
132     }
133 
134     memset1( ( uint8_t* )slot, 0x00, sizeof( MacCommand_t ) );
135 
136     return true;
137 }
138 
139 /* Linked list functions */
140 
141 /*!
142  * \brief Initialize list
143  *
144  * \param[IN]     list           - List that shall be initialized
145  * \retval                       - Status of the operation
146  */
LinkedListInit(MacCommandsList_t * list)147 static bool LinkedListInit( MacCommandsList_t* list )
148 {
149     if( list == NULL )
150     {
151         return false;
152     }
153 
154     list->First = NULL;
155     list->Last = NULL;
156 
157     return true;
158 }
159 
160 /*!
161  * \brief Add an element to the list
162  *
163  * \param[IN]     list           - List where the element shall be added.
164  * \param[IN]     element        - Element to add
165  * \retval                       - Status of the operation
166  */
LinkedListAdd(MacCommandsList_t * list,MacCommand_t * element)167 static bool LinkedListAdd( MacCommandsList_t* list, MacCommand_t* element )
168 {
169     if( ( list == NULL ) || ( element == NULL ) )
170     {
171         return false;
172     }
173 
174     // Check if this is the first entry to enter the list.
175     if( list->First == NULL )
176     {
177         list->First = element;
178     }
179 
180     // Check if the last entry exists and update its next point.
181     if( list->Last )
182     {
183         list->Last->Next = element;
184     }
185 
186     // Update the next point of this entry.
187     element->Next = NULL;
188 
189     // Update the last entry of the list.
190     list->Last = element;
191 
192     return true;
193 }
194 
195 /*!
196  * \brief Return the previous element in the list.
197  *
198  * \param[IN]     list           - List
199  * \param[IN]     element        - Element where the previous element shall be searched
200  * \retval                       - Status of the operation
201  */
LinkedListGetPrevious(MacCommandsList_t * list,MacCommand_t * element)202 static MacCommand_t* LinkedListGetPrevious( MacCommandsList_t* list, MacCommand_t* element )
203 {
204     if( ( list == NULL ) || ( element == NULL ) )
205     {
206         return NULL;
207     }
208 
209     MacCommand_t* curElement;
210 
211     // Start at the head of the list
212     curElement = list->First;
213 
214     // When current element is the first of the list, there's no previous element so we can return NULL immediately.
215     if( element != curElement )
216     {
217         // Loop through all elements until the end is reached or the next of current is the current element.
218         while( ( curElement != NULL ) && ( curElement->Next != element ) )
219         {
220             curElement = curElement->Next;
221         }
222     }
223     else
224     {
225         curElement = NULL;
226     }
227 
228     return curElement;
229 }
230 
231 /*!
232  * \brief Remove an element from the list
233  *
234  * \param[IN]     list           - List where the element shall be removed from.
235  * \param[IN]     element        - Element to remove
236  * \retval                       - Status of the operation
237  */
LinkedListRemove(MacCommandsList_t * list,MacCommand_t * element)238 static bool LinkedListRemove( MacCommandsList_t* list, MacCommand_t* element )
239 {
240     if( ( list == NULL ) || ( element == NULL ) )
241     {
242         return false;
243     }
244 
245     MacCommand_t* PrevElement = LinkedListGetPrevious( list, element );
246 
247     if( list->First == element )
248     {
249         list->First = element->Next;
250     }
251 
252     if( list->Last == element )
253     {
254         list->Last = PrevElement;
255     }
256 
257     if( PrevElement != NULL )
258     {
259         PrevElement->Next = element->Next;
260     }
261 
262     element->Next = NULL;
263 
264     return true;
265 }
266 
267 /*
268  * \brief Determines if a MAC command is sticky or not
269  *
270  * \param[IN]   cid            - MAC command identifier
271  *
272  * \retval                     - Status of the operation
273  */
IsSticky(uint8_t cid)274 static bool IsSticky( uint8_t cid )
275 {
276     switch( cid )
277     {
278         case MOTE_MAC_RESET_IND:
279         case MOTE_MAC_REKEY_IND:
280         case MOTE_MAC_DEVICE_MODE_IND:
281         case MOTE_MAC_DL_CHANNEL_ANS:
282         case MOTE_MAC_RX_PARAM_SETUP_ANS:
283         case MOTE_MAC_RX_TIMING_SETUP_ANS:
284         case MOTE_MAC_TX_PARAM_SETUP_ANS:
285         case MOTE_MAC_PING_SLOT_CHANNEL_ANS:
286             return true;
287         default:
288             return false;
289     }
290 }
291 
292 /*
293  * \brief Determines if a MAC command requires an explicit confirmation
294  *
295  * \param[IN]   cid            - MAC command identifier
296  *
297  * \retval                     - Status of the operation
298  */
IsConfirmationRequired(uint8_t cid)299 static bool IsConfirmationRequired( uint8_t cid )
300 {
301     switch( cid )
302     {
303         case MOTE_MAC_RESET_IND:
304         case MOTE_MAC_REKEY_IND:
305         case MOTE_MAC_DEVICE_MODE_IND:
306             return true;
307         default:
308             return false;
309     }
310 }
311 
LoRaMacCommandsInit(void)312 LoRaMacCommandStatus_t LoRaMacCommandsInit( void )
313 {
314     // Initialize with default
315     memset1( ( uint8_t* )&CommandsCtx, 0, sizeof( CommandsCtx ) );
316 
317     LinkedListInit( &CommandsCtx.MacCommandList );
318 
319     return LORAMAC_COMMANDS_SUCCESS;
320 }
321 
LoRaMacCommandsAddCmd(uint8_t cid,uint8_t * payload,size_t payloadSize)322 LoRaMacCommandStatus_t LoRaMacCommandsAddCmd( uint8_t cid, uint8_t* payload, size_t payloadSize )
323 {
324     if( payload == NULL )
325     {
326         return LORAMAC_COMMANDS_ERROR_NPE;
327     }
328     MacCommand_t* newCmd;
329 
330     // Allocate a memory slot
331     newCmd = MallocNewMacCommandSlot( );
332 
333     if( newCmd == NULL )
334     {
335         return LORAMAC_COMMANDS_ERROR_MEMORY;
336     }
337 
338     // Add it to the list of Mac commands
339     if( LinkedListAdd( &CommandsCtx.MacCommandList, newCmd ) == false )
340     {
341         return LORAMAC_COMMANDS_ERROR;
342     }
343 
344     // Set Values
345     newCmd->CID = cid;
346     newCmd->PayloadSize = payloadSize;
347     memcpy1( ( uint8_t* )newCmd->Payload, payload, payloadSize );
348     newCmd->IsSticky = IsSticky( cid );
349     newCmd->IsConfirmationRequired = IsConfirmationRequired( cid );
350 
351     CommandsCtx.SerializedCmdsSize += ( CID_FIELD_SIZE + payloadSize );
352 
353     return LORAMAC_COMMANDS_SUCCESS;
354 }
355 
LoRaMacCommandsRemoveCmd(MacCommand_t * macCmd)356 LoRaMacCommandStatus_t LoRaMacCommandsRemoveCmd( MacCommand_t* macCmd )
357 {
358     if( macCmd == NULL )
359     {
360         return LORAMAC_COMMANDS_ERROR_NPE;
361     }
362 
363     // Remove the Mac command element from MacCommandList
364     if( LinkedListRemove( &CommandsCtx.MacCommandList, macCmd ) == false )
365     {
366         return LORAMAC_COMMANDS_ERROR_CMD_NOT_FOUND;
367     }
368 
369     CommandsCtx.SerializedCmdsSize -= ( CID_FIELD_SIZE + macCmd->PayloadSize );
370 
371     // Free the MacCommand Slot
372     if( FreeMacCommandSlot( macCmd ) == false )
373     {
374         return LORAMAC_COMMANDS_ERROR;
375     }
376 
377     return LORAMAC_COMMANDS_SUCCESS;
378 }
379 
LoRaMacCommandsGetCmd(uint8_t cid,MacCommand_t ** macCmd)380 LoRaMacCommandStatus_t LoRaMacCommandsGetCmd( uint8_t cid, MacCommand_t** macCmd )
381 {
382     MacCommand_t* curElement;
383 
384     // Start at the head of the list
385     curElement = CommandsCtx.MacCommandList.First;
386 
387     // Loop through all elements until we find the element with the given CID
388     while( ( curElement != NULL ) && ( curElement->CID != cid ) )
389     {
390         curElement = curElement->Next;
391     }
392 
393     // Update the pointer anyway
394     *macCmd = curElement;
395 
396     // Handle error in case if we reached the end without finding it.
397     if( curElement == NULL )
398     {
399         return LORAMAC_COMMANDS_ERROR_CMD_NOT_FOUND;
400     }
401     return LORAMAC_COMMANDS_SUCCESS;
402 }
403 
LoRaMacCommandsRemoveNoneStickyCmds(void)404 LoRaMacCommandStatus_t LoRaMacCommandsRemoveNoneStickyCmds( void )
405 {
406     MacCommand_t* curElement;
407     MacCommand_t* nexElement;
408 
409     // Start at the head of the list
410     curElement = CommandsCtx.MacCommandList.First;
411 
412     // Loop through all elements
413     while( curElement != NULL )
414     {
415         if( curElement->IsSticky == false )
416         {
417             nexElement = curElement->Next;
418             LoRaMacCommandsRemoveCmd( curElement );
419             curElement = nexElement;
420         }
421         else
422         {
423             curElement = curElement->Next;
424         }
425     }
426 
427     return LORAMAC_COMMANDS_SUCCESS;
428 }
429 
LoRaMacCommandsRemoveStickyAnsCmds(void)430 LoRaMacCommandStatus_t LoRaMacCommandsRemoveStickyAnsCmds( void )
431 {
432     MacCommand_t* curElement;
433     MacCommand_t* nexElement;
434 
435     // Start at the head of the list
436     curElement = CommandsCtx.MacCommandList.First;
437 
438     // Loop through all elements
439     while( curElement != NULL )
440     {
441         nexElement = curElement->Next;
442         if( ( IsSticky( curElement->CID ) == true ) &&
443             ( IsConfirmationRequired( curElement->CID ) == false ) )
444         {
445             LoRaMacCommandsRemoveCmd( curElement );
446         }
447         curElement = nexElement;
448     }
449 
450     return LORAMAC_COMMANDS_SUCCESS;
451 }
452 
LoRaMacCommandsGetSizeSerializedCmds(size_t * size)453 LoRaMacCommandStatus_t LoRaMacCommandsGetSizeSerializedCmds( size_t* size )
454 {
455     if( size == NULL )
456     {
457         return LORAMAC_COMMANDS_ERROR_NPE;
458     }
459     *size = CommandsCtx.SerializedCmdsSize;
460     return LORAMAC_COMMANDS_SUCCESS;
461 }
462 
LoRaMacCommandsSerializeCmds(size_t availableSize,size_t * effectiveSize,uint8_t * buffer)463 LoRaMacCommandStatus_t LoRaMacCommandsSerializeCmds( size_t availableSize, size_t* effectiveSize, uint8_t* buffer )
464 {
465     MacCommand_t* curElement = CommandsCtx.MacCommandList.First;
466     MacCommand_t* nextElement;
467     uint8_t itr = 0;
468 
469     if( ( buffer == NULL ) || ( effectiveSize == NULL ) )
470     {
471         return LORAMAC_COMMANDS_ERROR_NPE;
472     }
473 
474     // Loop through all elements which fits into the buffer
475     while( curElement != NULL )
476     {
477         // If the next MAC command still fits into the buffer, add it.
478         if( ( availableSize - itr ) >= ( CID_FIELD_SIZE + curElement->PayloadSize ) )
479         {
480             buffer[itr++] = curElement->CID;
481             memcpy1( &buffer[itr], curElement->Payload, curElement->PayloadSize );
482             itr += curElement->PayloadSize;
483         }
484         else
485         {
486             break;
487         }
488         curElement = curElement->Next;
489     }
490 
491     // Remove all commands which do not fit into the buffer
492     while( curElement != NULL )
493     {
494         // Store the next element before removing the current one
495         nextElement = curElement->Next;
496         LoRaMacCommandsRemoveCmd( curElement );
497         curElement = nextElement;
498     }
499 
500     // Fetch the effective size of the mac commands
501     LoRaMacCommandsGetSizeSerializedCmds( effectiveSize );
502 
503     return LORAMAC_COMMANDS_SUCCESS;
504 }
505 
LoRaMacCommandsGetCmdSize(uint8_t cid)506 uint8_t LoRaMacCommandsGetCmdSize( uint8_t cid )
507 {
508     uint8_t cidSize = 0;
509 
510     // Decode Frame MAC commands
511     switch( cid )
512     {
513         case SRV_MAC_RESET_CONF:
514         {
515             // cid + Serv_LoRaWAN_version
516             cidSize = 2;
517             break;
518         }
519         case SRV_MAC_LINK_CHECK_ANS:
520         {
521             // cid + Margin + GwCnt
522             cidSize = 3;
523             break;
524         }
525         case SRV_MAC_LINK_ADR_REQ:
526         {
527             // cid + DataRate_TXPower + ChMask (2) + Redundancy
528             cidSize = 5;
529             break;
530         }
531         case SRV_MAC_DUTY_CYCLE_REQ:
532         {
533             // cid + DutyCyclePL
534             cidSize = 2;
535             break;
536         }
537         case SRV_MAC_RX_PARAM_SETUP_REQ:
538         {
539             // cid + DLsettings + Frequency (3)
540             cidSize = 5;
541             break;
542         }
543         case SRV_MAC_DEV_STATUS_REQ:
544         {
545             // cid
546             cidSize = 1;
547             break;
548         }
549         case SRV_MAC_NEW_CHANNEL_REQ:
550         {
551             // cid + ChIndex + Frequency (3) + DrRange
552             cidSize = 6;
553             break;
554         }
555         case SRV_MAC_RX_TIMING_SETUP_REQ:
556         {
557             // cid + Settings
558             cidSize = 2;
559             break;
560         }
561         case SRV_MAC_TX_PARAM_SETUP_REQ:
562         {
563             // cid + EIRP_DwellTime
564             cidSize = 2;
565             break;
566         }
567         case SRV_MAC_DL_CHANNEL_REQ:
568         {
569             // cid + ChIndex + Frequency (3)
570             cidSize = 5;
571             break;
572         }
573         case SRV_MAC_REKEY_CONF:
574         {
575             // cid + Serv_LoRaWAN_version
576             cidSize = 2;
577             break;
578         }
579         case SRV_MAC_ADR_PARAM_SETUP_REQ:
580         {
581             // cid + ADRparam
582             cidSize = 2;
583             break;
584         }
585         case SRV_MAC_FORCE_REJOIN_REQ:
586         {
587             // cid + Payload (2)
588             cidSize = 3;
589             break;
590         }
591         case SRV_MAC_REJOIN_PARAM_REQ:
592         {
593             // cid + Payload (1)
594             cidSize = 2;
595             break;
596         }
597         case SRV_MAC_DEVICE_MODE_CONF:
598         {
599             // cid + Class
600             cidSize = 2;
601             break;
602         }
603         case SRV_MAC_DEVICE_TIME_ANS:
604         {
605             // cid + Seconds (4) + Fractional seconds (1)
606             cidSize = 6;
607             break;
608         }
609         case SRV_MAC_PING_SLOT_INFO_ANS:
610         {
611             // cid
612             cidSize = 1;
613             break;
614         }
615         case SRV_MAC_PING_SLOT_CHANNEL_REQ:
616         {
617             // cid + Frequency (3) + DR
618             cidSize = 5;
619             break;
620         }
621         case SRV_MAC_BEACON_TIMING_ANS:
622         {
623             // cid + TimingDelay (2) + Channel
624             cidSize = 4;
625             break;
626         }
627         case SRV_MAC_BEACON_FREQ_REQ:
628         {
629             // cid + Frequency (3)
630             cidSize = 4;
631             break;
632         }
633         default:
634         {
635             // Unknown command. ABORT MAC commands processing
636             break;
637         }
638     }
639     return cidSize;
640 }
641