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_DL_CHANNEL_ANS:
279         case MOTE_MAC_RX_PARAM_SETUP_ANS:
280         case MOTE_MAC_RX_TIMING_SETUP_ANS:
281         case MOTE_MAC_TX_PARAM_SETUP_ANS:
282         case MOTE_MAC_PING_SLOT_CHANNEL_ANS:
283             return true;
284         default:
285             return false;
286     }
287 }
288 
LoRaMacCommandsInit(void)289 LoRaMacCommandStatus_t LoRaMacCommandsInit( void )
290 {
291     // Initialize with default
292     memset1( ( uint8_t* )&CommandsCtx, 0, sizeof( CommandsCtx ) );
293 
294     LinkedListInit( &CommandsCtx.MacCommandList );
295 
296     return LORAMAC_COMMANDS_SUCCESS;
297 }
298 
LoRaMacCommandsAddCmd(uint8_t cid,uint8_t * payload,size_t payloadSize)299 LoRaMacCommandStatus_t LoRaMacCommandsAddCmd( uint8_t cid, uint8_t* payload, size_t payloadSize )
300 {
301     if( payload == NULL )
302     {
303         return LORAMAC_COMMANDS_ERROR_NPE;
304     }
305     MacCommand_t* newCmd;
306 
307     // Allocate a memory slot
308     newCmd = MallocNewMacCommandSlot( );
309 
310     if( newCmd == NULL )
311     {
312         return LORAMAC_COMMANDS_ERROR_MEMORY;
313     }
314 
315     // Add it to the list of Mac commands
316     if( LinkedListAdd( &CommandsCtx.MacCommandList, newCmd ) == false )
317     {
318         return LORAMAC_COMMANDS_ERROR;
319     }
320 
321     // Set Values
322     newCmd->CID = cid;
323     newCmd->PayloadSize = payloadSize;
324     memcpy1( ( uint8_t* )newCmd->Payload, payload, payloadSize );
325     newCmd->IsSticky = IsSticky( cid );
326 
327     CommandsCtx.SerializedCmdsSize += ( CID_FIELD_SIZE + payloadSize );
328 
329     return LORAMAC_COMMANDS_SUCCESS;
330 }
331 
LoRaMacCommandsRemoveCmd(MacCommand_t * macCmd)332 LoRaMacCommandStatus_t LoRaMacCommandsRemoveCmd( MacCommand_t* macCmd )
333 {
334     if( macCmd == NULL )
335     {
336         return LORAMAC_COMMANDS_ERROR_NPE;
337     }
338 
339     // Remove the Mac command element from MacCommandList
340     if( LinkedListRemove( &CommandsCtx.MacCommandList, macCmd ) == false )
341     {
342         return LORAMAC_COMMANDS_ERROR_CMD_NOT_FOUND;
343     }
344 
345     CommandsCtx.SerializedCmdsSize -= ( CID_FIELD_SIZE + macCmd->PayloadSize );
346 
347     // Free the MacCommand Slot
348     if( FreeMacCommandSlot( macCmd ) == false )
349     {
350         return LORAMAC_COMMANDS_ERROR;
351     }
352 
353     return LORAMAC_COMMANDS_SUCCESS;
354 }
355 
LoRaMacCommandsGetCmd(uint8_t cid,MacCommand_t ** macCmd)356 LoRaMacCommandStatus_t LoRaMacCommandsGetCmd( uint8_t cid, MacCommand_t** macCmd )
357 {
358     MacCommand_t* curElement;
359 
360     // Start at the head of the list
361     curElement = CommandsCtx.MacCommandList.First;
362 
363     // Loop through all elements until we find the element with the given CID
364     while( ( curElement != NULL ) && ( curElement->CID != cid ) )
365     {
366         curElement = curElement->Next;
367     }
368 
369     // Update the pointer anyway
370     *macCmd = curElement;
371 
372     // Handle error in case if we reached the end without finding it.
373     if( curElement == NULL )
374     {
375         return LORAMAC_COMMANDS_ERROR_CMD_NOT_FOUND;
376     }
377     return LORAMAC_COMMANDS_SUCCESS;
378 }
379 
LoRaMacCommandsRemoveNoneStickyCmds(void)380 LoRaMacCommandStatus_t LoRaMacCommandsRemoveNoneStickyCmds( void )
381 {
382     MacCommand_t* curElement;
383     MacCommand_t* nexElement;
384 
385     // Start at the head of the list
386     curElement = CommandsCtx.MacCommandList.First;
387 
388     // Loop through all elements
389     while( curElement != NULL )
390     {
391         if( curElement->IsSticky == false )
392         {
393             nexElement = curElement->Next;
394             LoRaMacCommandsRemoveCmd( curElement );
395             curElement = nexElement;
396         }
397         else
398         {
399             curElement = curElement->Next;
400         }
401     }
402 
403     return LORAMAC_COMMANDS_SUCCESS;
404 }
405 
LoRaMacCommandsRemoveStickyAnsCmds(void)406 LoRaMacCommandStatus_t LoRaMacCommandsRemoveStickyAnsCmds( void )
407 {
408     MacCommand_t* curElement;
409     MacCommand_t* nexElement;
410 
411     // Start at the head of the list
412     curElement = CommandsCtx.MacCommandList.First;
413 
414     // Loop through all elements
415     while( curElement != NULL )
416     {
417         nexElement = curElement->Next;
418         if( IsSticky( curElement->CID ) == true )
419         {
420             LoRaMacCommandsRemoveCmd( curElement );
421         }
422         curElement = nexElement;
423     }
424 
425     return LORAMAC_COMMANDS_SUCCESS;
426 }
427 
LoRaMacCommandsGetSizeSerializedCmds(size_t * size)428 LoRaMacCommandStatus_t LoRaMacCommandsGetSizeSerializedCmds( size_t* size )
429 {
430     if( size == NULL )
431     {
432         return LORAMAC_COMMANDS_ERROR_NPE;
433     }
434     *size = CommandsCtx.SerializedCmdsSize;
435     return LORAMAC_COMMANDS_SUCCESS;
436 }
437 
LoRaMacCommandsSerializeCmds(size_t availableSize,size_t * effectiveSize,uint8_t * buffer)438 LoRaMacCommandStatus_t LoRaMacCommandsSerializeCmds( size_t availableSize, size_t* effectiveSize, uint8_t* buffer )
439 {
440     MacCommand_t* curElement = CommandsCtx.MacCommandList.First;
441     MacCommand_t* nextElement;
442     uint8_t itr = 0;
443 
444     if( ( buffer == NULL ) || ( effectiveSize == NULL ) )
445     {
446         return LORAMAC_COMMANDS_ERROR_NPE;
447     }
448 
449     // Loop through all elements which fits into the buffer
450     while( curElement != NULL )
451     {
452         // If the next MAC command still fits into the buffer, add it.
453         if( ( availableSize - itr ) >= ( CID_FIELD_SIZE + curElement->PayloadSize ) )
454         {
455             buffer[itr++] = curElement->CID;
456             memcpy1( &buffer[itr], curElement->Payload, curElement->PayloadSize );
457             itr += curElement->PayloadSize;
458         }
459         else
460         {
461             break;
462         }
463         curElement = curElement->Next;
464     }
465 
466     // Remove all commands which do not fit into the buffer
467     while( curElement != NULL )
468     {
469         // Store the next element before removing the current one
470         nextElement = curElement->Next;
471         LoRaMacCommandsRemoveCmd( curElement );
472         curElement = nextElement;
473     }
474 
475     // Fetch the effective size of the mac commands
476     LoRaMacCommandsGetSizeSerializedCmds( effectiveSize );
477 
478     return LORAMAC_COMMANDS_SUCCESS;
479 }
480 
LoRaMacCommandsStickyCmdsPending(bool * cmdsPending)481 LoRaMacCommandStatus_t LoRaMacCommandsStickyCmdsPending( bool* cmdsPending )
482 {
483     if( cmdsPending == NULL )
484     {
485         return LORAMAC_COMMANDS_ERROR_NPE;
486     }
487     MacCommand_t* curElement;
488     curElement = CommandsCtx.MacCommandList.First;
489 
490     *cmdsPending = false;
491 
492     // Loop through all elements
493     while( curElement != NULL )
494     {
495         if( curElement->IsSticky == true )
496         {
497             // Found one sticky MAC command
498             *cmdsPending = true;
499             return LORAMAC_COMMANDS_SUCCESS;
500         }
501         curElement = curElement->Next;
502     }
503 
504     return LORAMAC_COMMANDS_SUCCESS;
505 }
506 
LoRaMacCommandsGetCmdSize(uint8_t cid)507 uint8_t LoRaMacCommandsGetCmdSize( uint8_t cid )
508 {
509     uint8_t cidSize = 0;
510 
511     // Decode Frame MAC commands
512     switch( cid )
513     {
514         case SRV_MAC_LINK_CHECK_ANS:
515         {
516             // cid + Margin + GwCnt
517             cidSize = 3;
518             break;
519         }
520         case SRV_MAC_LINK_ADR_REQ:
521         {
522             // cid + DataRate_TXPower + ChMask (2) + Redundancy
523             cidSize = 5;
524             break;
525         }
526         case SRV_MAC_DUTY_CYCLE_REQ:
527         {
528             // cid + DutyCyclePL
529             cidSize = 2;
530             break;
531         }
532         case SRV_MAC_RX_PARAM_SETUP_REQ:
533         {
534             // cid + DLsettings + Frequency (3)
535             cidSize = 5;
536             break;
537         }
538         case SRV_MAC_DEV_STATUS_REQ:
539         {
540             // cid
541             cidSize = 1;
542             break;
543         }
544         case SRV_MAC_NEW_CHANNEL_REQ:
545         {
546             // cid + ChIndex + Frequency (3) + DrRange
547             cidSize = 6;
548             break;
549         }
550         case SRV_MAC_RX_TIMING_SETUP_REQ:
551         {
552             // cid + Settings
553             cidSize = 2;
554             break;
555         }
556         case SRV_MAC_TX_PARAM_SETUP_REQ:
557         {
558             // cid + EIRP_DwellTime
559             cidSize = 2;
560             break;
561         }
562         case SRV_MAC_DL_CHANNEL_REQ:
563         {
564             // cid + ChIndex + Frequency (3)
565             cidSize = 5;
566             break;
567         }
568         case SRV_MAC_DEVICE_TIME_ANS:
569         {
570             // cid + Seconds (4) + Fractional seconds (1)
571             cidSize = 6;
572             break;
573         }
574         case SRV_MAC_PING_SLOT_INFO_ANS:
575         {
576             // cid
577             cidSize = 1;
578             break;
579         }
580         case SRV_MAC_PING_SLOT_CHANNEL_REQ:
581         {
582             // cid + Frequency (3) + DR
583             cidSize = 5;
584             break;
585         }
586         case SRV_MAC_BEACON_TIMING_ANS:
587         {
588             // cid + TimingDelay (2) + Channel
589             cidSize = 4;
590             break;
591         }
592         case SRV_MAC_BEACON_FREQ_REQ:
593         {
594             // cid + Frequency (3)
595             cidSize = 4;
596             break;
597         }
598         default:
599         {
600             // Unknown command. ABORT MAC commands processing
601             break;
602         }
603     }
604     return cidSize;
605 }
606