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