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