1 /*!
2 * \file LmhpRemoteMcastSetup.c
3 *
4 * \brief Implements the LoRa-Alliance remote multicast setup package
5 * Specification: https://lora-alliance.org/sites/default/files/2018-09/remote_multicast_setup_v1.0.0.pdf
6 *
7 * \copyright Revised BSD License, see section \ref LICENSE.
8 *
9 * \code
10 * ______ _
11 * / _____) _ | |
12 * ( (____ _____ ____ _| |_ _____ ____| |__
13 * \____ \| ___ | (_ _) ___ |/ ___) _ \
14 * _____) ) ____| | | || |_| ____( (___| | | |
15 * (______/|_____)_|_|_| \__)_____)\____)_| |_|
16 * (C)2013-2018 Semtech
17 *
18 * \endcode
19 *
20 * \author Miguel Luis ( Semtech )
21 */
22 #include "LmHandler.h"
23 #include "LmhpRemoteMcastSetup.h"
24
25 #define DBG_TRACE 1
26
27 #if DBG_TRACE == 1
28 #include <stdio.h>
29 /*!
30 * Works in the same way as the printf function does.
31 */
32 #define DBG( ... ) \
33 do \
34 { \
35 printf( __VA_ARGS__ ); \
36 }while( 0 )
37
38 #define DBG_SESSION( id, isRxParamsSetup ) \
39 do \
40 { \
41 DBG( "ID : %d\n", McSessionData[id].McGroupData.IdHeader.Fields.McGroupId ); \
42 DBG( "McAddr : %08lX\n", McSessionData[id].McGroupData.McAddr ); \
43 DBG( "McKey : %02X", McSessionData[id].McGroupData.McKeyEncrypted[0] ); \
44 for( int i = 1; i < 16; i++ ) \
45 { \
46 DBG( "-%02X", McSessionData[id].McGroupData.McKeyEncrypted[i] ); \
47 } \
48 DBG( "\n" ); \
49 DBG( "McFCountMin : %lu\n", McSessionData[id].McGroupData.McFCountMin ); \
50 DBG( "McFCountMax : %lu\n", McSessionData[id].McGroupData.McFCountMax ); \
51 if( isRxParamsSetup == true ) \
52 { \
53 DBG( "SessionTime : %lu\n", McSessionData[id].SessionTime ); \
54 DBG( "SessionTimeT: %d\n", McSessionData[id].SessionTimeout ); \
55 if( McSessionData[id].RxParams.Class == CLASS_B ) \
56 { \
57 DBG( "Rx Freq : %lu\n", McSessionData[id].RxParams.Params.ClassB.Frequency ); \
58 DBG( "Rx DR : DR_%d\n", McSessionData[id].RxParams.Params.ClassB.Datarate ); \
59 DBG( "Periodicity : %u\n", McSessionData[id].RxParams.Params.ClassB.Periodicity ); \
60 } \
61 else \
62 { \
63 DBG( "Rx Freq : %lu\n", McSessionData[id].RxParams.Params.ClassC.Frequency ); \
64 DBG( "Rx DR : DR_%d\n", McSessionData[id].RxParams.Params.ClassC.Datarate ); \
65 } \
66 } \
67 } while ( 0 )
68 #else
69 #define DBG( ... )
70 #define DBG_SESSION( id, isRxParamsSetup )
71 #endif
72
73 /*!
74 * LoRaWAN Application Layer Remote multicast setup Specification
75 */
76 #define REMOTE_MCAST_SETUP_PORT 200
77
78 #define REMOTE_MCAST_SETUP_ID 2
79 #define REMOTE_MCAST_SETUP_VERSION 1
80
81 typedef enum LmhpRemoteMcastSetupSessionStates_e
82 {
83 REMOTE_MCAST_SETUP_SESSION_STATE_IDLE,
84 REMOTE_MCAST_SETUP_SESSION_STATE_START,
85 REMOTE_MCAST_SETUP_SESSION_STATE_STOP,
86 }LmhpRemoteMcastSetupSessionStates_t;
87
88 /*!
89 * Package current context
90 */
91 typedef struct LmhpRemoteMcastSetupState_s
92 {
93 bool Initialized;
94 bool IsTxPending;
95 LmhpRemoteMcastSetupSessionStates_t SessionState;
96 uint8_t DataBufferMaxSize;
97 uint8_t *DataBuffer;
98 }LmhpRemoteMcastSetupState_t;
99
100 typedef enum LmhpRemoteMcastSetupMoteCmd_e
101 {
102 REMOTE_MCAST_SETUP_PKG_VERSION_ANS = 0x00,
103 REMOTE_MCAST_SETUP_MC_GROUP_STATUS_ANS = 0x01,
104 REMOTE_MCAST_SETUP_MC_GROUP_SETUP_ANS = 0x02,
105 REMOTE_MCAST_SETUP_MC_GROUP_DELETE_ANS = 0x03,
106 REMOTE_MCAST_SETUP_MC_GROUP_CLASS_C_SESSION_ANS = 0x04,
107 REMOTE_MCAST_SETUP_MC_GROUP_CLASS_B_SESSION_ANS = 0x05,
108 }LmhpRemoteMcastSetupMoteCmd_t;
109
110 typedef enum LmhpRemoteMcastSetupSrvCmd_e
111 {
112 REMOTE_MCAST_SETUP_PKG_VERSION_REQ = 0x00,
113 REMOTE_MCAST_SETUP_MC_GROUP_STATUS_REQ = 0x01,
114 REMOTE_MCAST_SETUP_MC_GROUP_SETUP_REQ = 0x02,
115 REMOTE_MCAST_SETUP_MC_GROUP_DELETE_REQ = 0x03,
116 REMOTE_MCAST_SETUP_MC_GROUP_CLASS_C_SESSION_REQ = 0x04,
117 REMOTE_MCAST_SETUP_MC_GROUP_CLASS_B_SESSION_REQ = 0x05,
118 }LmhpRemoteMcastSetupSrvCmd_t;
119
120 /*!
121 * Initializes the package with provided parameters
122 *
123 * \param [IN] params Pointer to the package parameters
124 * \param [IN] dataBuffer Pointer to main application buffer
125 * \param [IN] dataBufferMaxSize Main application buffer maximum size
126 */
127 static void LmhpRemoteMcastSetupInit( void *params, uint8_t *dataBuffer, uint8_t dataBufferMaxSize );
128
129 /*!
130 * Returns the current package initialization status.
131 *
132 * \retval status Package initialization status
133 * [true: Initialized, false: Not initialized]
134 */
135 static bool LmhpRemoteMcastSetupIsInitialized( void );
136
137 /*!
138 * Returns if a package transmission is pending or not.
139 *
140 * \retval status Package transmission status
141 * [true: pending, false: Not pending]
142 */
143 static bool LmhpRemoteMcastSetupIsTxPending( void );
144
145 /*!
146 * Processes the internal package events.
147 */
148 static void LmhpRemoteMcastSetupProcess( void );
149
150 /*!
151 * Processes the MCPS Indication
152 *
153 * \param [IN] mcpsIndication MCPS indication primitive data
154 */
155 static void LmhpRemoteMcastSetupOnMcpsIndication( McpsIndication_t *mcpsIndication );
156
157 static void OnSessionStartTimer( void *context );
158
159 static void OnSessionStopTimer( void *context );
160
161 static LmhpRemoteMcastSetupState_t LmhpRemoteMcastSetupState =
162 {
163 .Initialized = false,
164 .IsTxPending = false,
165 .SessionState = REMOTE_MCAST_SETUP_SESSION_STATE_IDLE,
166 };
167
168 typedef struct McGroupData_s
169 {
170 union
171 {
172 uint8_t Value;
173 struct
174 {
175 uint8_t McGroupId: 2;
176 uint8_t RFU: 6;
177 }Fields;
178 }IdHeader;
179 uint32_t McAddr;
180 uint8_t McKeyEncrypted[16];
181 uint32_t McFCountMin;
182 uint32_t McFCountMax;
183 }McGroupData_t;
184
185 typedef enum eSessionState
186 {
187 SESSION_STOPED,
188 SESSION_STARTED
189 }SessionState_t;
190
191 typedef struct McSessionData_s
192 {
193 McGroupData_t McGroupData;
194 SessionState_t SessionState;
195 uint32_t SessionTime;
196 uint8_t SessionTimeout;
197 McRxParams_t RxParams;
198 }McSessionData_t;
199
200 McSessionData_t McSessionData[LORAMAC_MAX_MC_CTX];
201
202 /*!
203 * Session start timer
204 */
205 static TimerEvent_t SessionStartTimer;
206
207 /*!
208 * Session start timer
209 */
210 static TimerEvent_t SessionStopTimer;
211
212 static LmhPackage_t LmhpRemoteMcastSetupPackage =
213 {
214 .Port = REMOTE_MCAST_SETUP_PORT,
215 .Init = LmhpRemoteMcastSetupInit,
216 .IsInitialized = LmhpRemoteMcastSetupIsInitialized,
217 .IsTxPending = LmhpRemoteMcastSetupIsTxPending,
218 .Process = LmhpRemoteMcastSetupProcess,
219 .OnMcpsConfirmProcess = NULL, // Not used in this package
220 .OnMcpsIndicationProcess = LmhpRemoteMcastSetupOnMcpsIndication,
221 .OnMlmeConfirmProcess = NULL, // Not used in this package
222 .OnMlmeIndicationProcess = NULL, // Not used in this package
223 .OnMacMcpsRequest = NULL, // To be initialized by LmHandler
224 .OnMacMlmeRequest = NULL, // To be initialized by LmHandler
225 .OnJoinRequest = NULL, // To be initialized by LmHandler
226 .OnDeviceTimeRequest = NULL, // To be initialized by LmHandler
227 .OnSysTimeUpdate = NULL, // To be initialized by LmHandler
228 };
229
LmhpRemoteMcastSetupPackageFactory(void)230 LmhPackage_t *LmhpRemoteMcastSetupPackageFactory( void )
231 {
232 return &LmhpRemoteMcastSetupPackage;
233 }
234
LmhpRemoteMcastSetupInit(void * params,uint8_t * dataBuffer,uint8_t dataBufferMaxSize)235 static void LmhpRemoteMcastSetupInit( void * params, uint8_t *dataBuffer, uint8_t dataBufferMaxSize )
236 {
237 if( dataBuffer != NULL )
238 {
239 LmhpRemoteMcastSetupState.DataBuffer = dataBuffer;
240 LmhpRemoteMcastSetupState.DataBufferMaxSize = dataBufferMaxSize;
241 LmhpRemoteMcastSetupState.Initialized = true;
242 TimerInit( &SessionStartTimer, OnSessionStartTimer );
243 TimerInit( &SessionStopTimer, OnSessionStopTimer );
244 }
245 else
246 {
247 LmhpRemoteMcastSetupState.Initialized = false;
248 }
249 LmhpRemoteMcastSetupState.IsTxPending = false;
250 }
251
LmhpRemoteMcastSetupIsInitialized(void)252 static bool LmhpRemoteMcastSetupIsInitialized( void )
253 {
254 return LmhpRemoteMcastSetupState.Initialized;
255 }
256
LmhpRemoteMcastSetupIsTxPending(void)257 static bool LmhpRemoteMcastSetupIsTxPending( void )
258 {
259 return LmhpRemoteMcastSetupState.IsTxPending;
260 }
261
LmhpRemoteMcastSetupProcess(void)262 static void LmhpRemoteMcastSetupProcess( void )
263 {
264 LmhpRemoteMcastSetupSessionStates_t state;
265
266 CRITICAL_SECTION_BEGIN( );
267 state = LmhpRemoteMcastSetupState.SessionState;
268 LmhpRemoteMcastSetupState.SessionState = REMOTE_MCAST_SETUP_SESSION_STATE_IDLE;
269 CRITICAL_SECTION_END( );
270
271 switch( state )
272 {
273 case REMOTE_MCAST_SETUP_SESSION_STATE_START:
274 // Switch to Class C
275 LmHandlerRequestClass( CLASS_C );
276
277 TimerSetValue( &SessionStopTimer, ( 1 << McSessionData[0].SessionTimeout ) * 1000 );
278 TimerStart( &SessionStopTimer );
279 break;
280 case REMOTE_MCAST_SETUP_SESSION_STATE_STOP:
281 // Switch back to Class A
282 LmHandlerRequestClass( CLASS_A );
283 break;
284 case REMOTE_MCAST_SETUP_SESSION_STATE_IDLE:
285 // Intentional fall through
286 default:
287 // Nothing to do.
288 break;
289 }
290 }
291
LmhpRemoteMcastSetupOnMcpsIndication(McpsIndication_t * mcpsIndication)292 static void LmhpRemoteMcastSetupOnMcpsIndication( McpsIndication_t *mcpsIndication )
293 {
294 uint8_t cmdIndex = 0;
295 uint8_t dataBufferIndex = 0;
296
297 if( mcpsIndication->Port != REMOTE_MCAST_SETUP_PORT )
298 {
299 return;
300 }
301
302 while( cmdIndex < mcpsIndication->BufferSize )
303 {
304 switch( mcpsIndication->Buffer[cmdIndex++] )
305 {
306 case REMOTE_MCAST_SETUP_PKG_VERSION_REQ:
307 {
308 LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = REMOTE_MCAST_SETUP_PKG_VERSION_ANS;
309 LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = REMOTE_MCAST_SETUP_ID;
310 LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = REMOTE_MCAST_SETUP_VERSION;
311 break;
312 }
313 case REMOTE_MCAST_SETUP_MC_GROUP_STATUS_REQ:
314 {
315 // TODO implement command processing and handling
316 break;
317 }
318 case REMOTE_MCAST_SETUP_MC_GROUP_SETUP_REQ:
319 {
320 uint8_t idError = 0x01; // One bit value
321 uint8_t id = mcpsIndication->Buffer[cmdIndex++];
322
323 McSessionData[id].McGroupData.IdHeader.Value = id;
324
325 if( id < LORAMAC_MAX_MC_CTX )
326 {
327 McSessionData[id].McGroupData.McAddr = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF;
328 McSessionData[id].McGroupData.McAddr += ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00;
329 McSessionData[id].McGroupData.McAddr += ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000;
330 McSessionData[id].McGroupData.McAddr += ( mcpsIndication->Buffer[cmdIndex++] << 24 ) & 0xFF000000;
331
332 for( int8_t i = 0; i < 16; i++ )
333 {
334 McSessionData[id].McGroupData.McKeyEncrypted[i] = mcpsIndication->Buffer[cmdIndex++];
335 }
336
337 McSessionData[id].McGroupData.McFCountMin = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF;
338 McSessionData[id].McGroupData.McFCountMin += ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00;
339 McSessionData[id].McGroupData.McFCountMin += ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000;
340 McSessionData[id].McGroupData.McFCountMin += ( mcpsIndication->Buffer[cmdIndex++] << 24 ) & 0xFF000000;
341
342 McSessionData[id].McGroupData.McFCountMax = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF;
343 McSessionData[id].McGroupData.McFCountMax += ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00;
344 McSessionData[id].McGroupData.McFCountMax += ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000;
345 McSessionData[id].McGroupData.McFCountMax += ( mcpsIndication->Buffer[cmdIndex++] << 24 ) & 0xFF000000;
346
347 McChannelParams_t channel =
348 {
349 .IsRemotelySetup = true,
350 .IsEnabled = true,
351 .GroupID = ( AddressIdentifier_t )McSessionData[id].McGroupData.IdHeader.Fields.McGroupId,
352 .Address = McSessionData[id].McGroupData.McAddr,
353 .McKeys.McKeyE = McSessionData[id].McGroupData.McKeyEncrypted,
354 .FCountMin = McSessionData[id].McGroupData.McFCountMin,
355 .FCountMax = McSessionData[id].McGroupData.McFCountMax,
356 .RxParams.Params.ClassC = // Field not used for multicast channel setup. Must be initialized to something
357 {
358 .Frequency = 0,
359 .Datarate = 0
360 }
361 };
362
363 if( LoRaMacMcChannelSetup( &channel ) == LORAMAC_STATUS_OK )
364 {
365 idError = 0x00;
366
367 }
368 }
369 LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = REMOTE_MCAST_SETUP_MC_GROUP_SETUP_ANS;
370 LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = ( idError << 2 ) | McSessionData[id].McGroupData.IdHeader.Fields.McGroupId;
371 DBG_SESSION( id, false );
372 break;
373 }
374 case REMOTE_MCAST_SETUP_MC_GROUP_DELETE_REQ:
375 {
376 uint8_t status = 0x00;
377 uint8_t id = mcpsIndication->Buffer[cmdIndex++] & 0x03;
378
379 status = id;
380
381 LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = REMOTE_MCAST_SETUP_MC_GROUP_DELETE_ANS;
382
383 if( LoRaMacMcChannelDelete( ( AddressIdentifier_t )id ) != LORAMAC_STATUS_OK )
384 {
385 status |= 0x04; // McGroupUndefined bit set
386 }
387 LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = status;
388 break;
389 }
390 case REMOTE_MCAST_SETUP_MC_GROUP_CLASS_C_SESSION_REQ:
391 {
392 bool isTimerSet = false;
393 int32_t timeToSessionStart = 0;
394 uint8_t status = 0x00;
395 uint8_t id = mcpsIndication->Buffer[cmdIndex++] & 0x03;
396
397 if( id < LORAMAC_MAX_MC_CTX )
398 {
399 McSessionData[id].RxParams.Class = CLASS_C;
400
401 McSessionData[id].SessionTime = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF;
402 McSessionData[id].SessionTime += ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00;
403 McSessionData[id].SessionTime += ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000;
404 McSessionData[id].SessionTime += ( mcpsIndication->Buffer[cmdIndex++] << 24 ) & 0xFF000000;
405
406 // Add Unix to Gps epoch offset. The system time is based on Unix time.
407 McSessionData[id].SessionTime += UNIX_GPS_EPOCH_OFFSET;
408
409 McSessionData[id].SessionTimeout = mcpsIndication->Buffer[cmdIndex++] & 0x0F;
410
411 McSessionData[id].RxParams.Params.ClassC.Frequency = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF;
412 McSessionData[id].RxParams.Params.ClassC.Frequency |= ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00;
413 McSessionData[id].RxParams.Params.ClassC.Frequency |= ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000;
414 McSessionData[id].RxParams.Params.ClassC.Frequency *= 100;
415 McSessionData[id].RxParams.Params.ClassC.Datarate = mcpsIndication->Buffer[cmdIndex++];
416
417 if( LoRaMacMcChannelSetupRxParams( ( AddressIdentifier_t )id, &McSessionData[id].RxParams, &status ) == LORAMAC_STATUS_OK )
418 {
419 SysTime_t curTime = { .Seconds = 0, .SubSeconds = 0 };
420 curTime = SysTimeGet( );
421
422 timeToSessionStart = McSessionData[id].SessionTime - curTime.Seconds;
423 if( timeToSessionStart > 0 )
424 {
425 // Start session start timer
426 TimerSetValue( &SessionStartTimer, timeToSessionStart * 1000 );
427 TimerStart( &SessionStartTimer );
428
429 isTimerSet = true;
430
431 DBG( "Time2SessionStart: %ld ms\n", timeToSessionStart * 1000 );
432 }
433 else
434 {
435 // Session start time before current device time
436 status |= 0x10; // McGroupUndefined bit set
437 }
438 }
439 }
440 else
441 {
442 status |= 0x10; // McGroupUndefined bit set
443 }
444
445 LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = REMOTE_MCAST_SETUP_MC_GROUP_CLASS_C_SESSION_ANS;
446 LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = status;
447 if( isTimerSet == true )
448 {
449 LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = ( timeToSessionStart >> 0 ) & 0xFF;
450 LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = ( timeToSessionStart >> 8 ) & 0xFF;
451 LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = ( timeToSessionStart >> 16 ) & 0xFF;
452 }
453 DBG_SESSION( id, true );
454 break;
455 }
456 case REMOTE_MCAST_SETUP_MC_GROUP_CLASS_B_SESSION_REQ:
457 {
458 bool isTimerSet = false;
459 int32_t timeToSessionStart = 0;
460 uint8_t status = 0x00;
461 uint8_t id = mcpsIndication->Buffer[cmdIndex++] & 0x03;
462
463 if( id < LORAMAC_MAX_MC_CTX )
464 {
465 McSessionData[id].RxParams.Class = CLASS_B;
466
467 McSessionData[id].SessionTime = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF;
468 McSessionData[id].SessionTime += ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00;
469 McSessionData[id].SessionTime += ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000;
470 McSessionData[id].SessionTime += ( mcpsIndication->Buffer[cmdIndex++] << 24 ) & 0xFF000000;
471
472 // Add Unix to Gps epoch offset. The system time is based on Unix time.
473 McSessionData[id].SessionTime += UNIX_GPS_EPOCH_OFFSET;
474
475 McSessionData[id].RxParams.Params.ClassB.Periodicity = ( mcpsIndication->Buffer[cmdIndex] >> 4 ) & 0x07;
476 McSessionData[id].SessionTimeout = mcpsIndication->Buffer[cmdIndex++] & 0x0F;
477
478 McSessionData[id].RxParams.Params.ClassB.Frequency = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF;
479 McSessionData[id].RxParams.Params.ClassB.Frequency |= ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00;
480 McSessionData[id].RxParams.Params.ClassB.Frequency |= ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000;
481 McSessionData[id].RxParams.Params.ClassB.Frequency *= 100;
482 McSessionData[id].RxParams.Params.ClassB.Datarate = mcpsIndication->Buffer[cmdIndex++];
483
484 if( LoRaMacMcChannelSetupRxParams( ( AddressIdentifier_t )id, &McSessionData[id].RxParams, &status ) == LORAMAC_STATUS_OK )
485 {
486 SysTime_t curTime = { .Seconds = 0, .SubSeconds = 0 };
487 curTime = SysTimeGet( );
488
489 timeToSessionStart = McSessionData[id].SessionTime - curTime.Seconds;
490 if( timeToSessionStart > 0 )
491 {
492 // Start session start timer
493 TimerSetValue( &SessionStartTimer, timeToSessionStart * 1000 );
494 TimerStart( &SessionStartTimer );
495
496 isTimerSet = true;
497
498 DBG( "Time2SessionStart: %ld ms\n", timeToSessionStart * 1000 );
499 }
500 else
501 {
502 // Session start time before current device time
503 status |= 0x10; // McGroupUndefined bit set
504 }
505 }
506 }
507 else
508 {
509 status |= 0x10; // McGroupUndefined bit set
510 }
511
512 LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = REMOTE_MCAST_SETUP_MC_GROUP_CLASS_B_SESSION_ANS;
513 LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = status;
514 if( isTimerSet == true )
515 {
516 LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = ( timeToSessionStart >> 0 ) & 0xFF;
517 LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = ( timeToSessionStart >> 8 ) & 0xFF;
518 LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = ( timeToSessionStart >> 16 ) & 0xFF;
519 }
520 DBG_SESSION( id, true );
521 break;
522 }
523 default:
524 {
525 break;
526 }
527 }
528 }
529
530 if( dataBufferIndex != 0 )
531 {
532 // Answer commands
533 LmHandlerAppData_t appData =
534 {
535 .Buffer = LmhpRemoteMcastSetupState.DataBuffer,
536 .BufferSize = dataBufferIndex,
537 .Port = REMOTE_MCAST_SETUP_PORT
538 };
539 LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG );
540 }
541 }
542
OnSessionStartTimer(void * context)543 static void OnSessionStartTimer( void *context )
544 {
545 TimerStop( &SessionStartTimer );
546
547 LmhpRemoteMcastSetupState.SessionState = REMOTE_MCAST_SETUP_SESSION_STATE_START;
548 }
549
OnSessionStopTimer(void * context)550 static void OnSessionStopTimer( void *context )
551 {
552 TimerStop( &SessionStopTimer );
553
554 LmhpRemoteMcastSetupState.SessionState = REMOTE_MCAST_SETUP_SESSION_STATE_STOP;
555 }
556