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