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 #else
38     #define DBG( ... )
39 #endif
40 
41 /*!
42  * LoRaWAN Application Layer Remote multicast setup Specification
43  */
44 #define REMOTE_MCAST_SETUP_PORT                     200
45 
46 #define REMOTE_MCAST_SETUP_ID                       2
47 #define REMOTE_MCAST_SETUP_VERSION                  1
48 
49 typedef enum LmhpRemoteMcastSetupSessionStates_e
50 {
51     REMOTE_MCAST_SETUP_SESSION_STATE_IDLE,
52     REMOTE_MCAST_SETUP_SESSION_STATE_START,
53     REMOTE_MCAST_SETUP_SESSION_STATE_STOP,
54 }LmhpRemoteMcastSetupSessionStates_t;
55 
56 /*!
57  * Package current context
58  */
59 typedef struct LmhpRemoteMcastSetupState_s
60 {
61     bool Initialized;
62     bool IsTxPending;
63     LmhpRemoteMcastSetupSessionStates_t SessionState;
64     uint8_t DataBufferMaxSize;
65     uint8_t *DataBuffer;
66 }LmhpRemoteMcastSetupState_t;
67 
68 typedef enum LmhpRemoteMcastSetupMoteCmd_e
69 {
70     REMOTE_MCAST_SETUP_PKG_VERSION_ANS              = 0x00,
71     REMOTE_MCAST_SETUP_MC_GROUP_STATUS_ANS          = 0x01,
72     REMOTE_MCAST_SETUP_MC_GROUP_SETUP_ANS           = 0x02,
73     REMOTE_MCAST_SETUP_MC_GROUP_DELETE_ANS          = 0x03,
74     REMOTE_MCAST_SETUP_MC_GROUP_CLASS_C_SESSION_ANS = 0x04,
75     REMOTE_MCAST_SETUP_MC_GROUP_CLASS_B_SESSION_ANS = 0x05,
76 }LmhpRemoteMcastSetupMoteCmd_t;
77 
78 typedef enum LmhpRemoteMcastSetupSrvCmd_e
79 {
80     REMOTE_MCAST_SETUP_PKG_VERSION_REQ              = 0x00,
81     REMOTE_MCAST_SETUP_MC_GROUP_STATUS_REQ          = 0x01,
82     REMOTE_MCAST_SETUP_MC_GROUP_SETUP_REQ           = 0x02,
83     REMOTE_MCAST_SETUP_MC_GROUP_DELETE_REQ          = 0x03,
84     REMOTE_MCAST_SETUP_MC_GROUP_CLASS_C_SESSION_REQ = 0x04,
85     REMOTE_MCAST_SETUP_MC_GROUP_CLASS_B_SESSION_REQ = 0x05,
86 }LmhpRemoteMcastSetupSrvCmd_t;
87 
88 /*!
89  * Initializes the package with provided parameters
90  *
91  * \param [IN] params            Pointer to the package parameters
92  * \param [IN] dataBuffer        Pointer to main application buffer
93  * \param [IN] dataBufferMaxSize Main application buffer maximum size
94  */
95 static void LmhpRemoteMcastSetupInit( void *params, uint8_t *dataBuffer, uint8_t dataBufferMaxSize );
96 
97 /*!
98  * Returns the current package initialization status.
99  *
100  * \retval status Package initialization status
101  *                [true: Initialized, false: Not initialized]
102  */
103 static bool LmhpRemoteMcastSetupIsInitialized( void );
104 
105 /*!
106  * Returns if a package transmission is pending or not.
107  *
108  * \retval status Package transmission status
109  *                [true: pending, false: Not pending]
110  */
111 static bool LmhpRemoteMcastSetupIsTxPending( void );
112 
113 /*!
114  * Processes the internal package events.
115  */
116 static void LmhpRemoteMcastSetupProcess( void );
117 
118 /*!
119  * Processes the MCPS Indication
120  *
121  * \param [IN] mcpsIndication     MCPS indication primitive data
122  */
123 static void LmhpRemoteMcastSetupOnMcpsIndication( McpsIndication_t *mcpsIndication );
124 
125 static void OnSessionStartTimer( void *context );
126 
127 static void OnSessionStopTimer( void *context );
128 
129 static LmhpRemoteMcastSetupState_t LmhpRemoteMcastSetupState =
130 {
131     .Initialized = false,
132     .IsTxPending = false,
133     .SessionState = REMOTE_MCAST_SETUP_SESSION_STATE_IDLE,
134 };
135 
136 typedef struct McGroupData_s
137 {
138     union
139     {
140         uint8_t Value;
141         struct
142         {
143             uint8_t McGroupId:   2;
144             uint8_t RFU:         6;
145         }Fields;
146     }IdHeader;
147     uint32_t McAddr;
148     uint8_t McKeyEncrypted[16];
149     uint32_t McFCountMin;
150     uint32_t McFCountMax;
151 }McGroupData_t;
152 
153 typedef enum eSessionState
154 {
155     SESSION_STOPED,
156     SESSION_STARTED
157 }SessionState_t;
158 
159 typedef struct McSessionData_s
160 {
161     McGroupData_t McGroupData;
162     SessionState_t SessionState;
163     uint32_t SessionTime;
164     uint8_t SessionTimeout;
165     McRxParams_t RxParams;
166 }McSessionData_t;
167 
168 McSessionData_t McSessionData[LORAMAC_MAX_MC_CTX];
169 
170 /*!
171  * Session start timer
172  */
173 static TimerEvent_t SessionStartTimer;
174 
175 /*!
176  * Session start timer
177  */
178 static TimerEvent_t SessionStopTimer;
179 
180 static LmhPackage_t LmhpRemoteMcastSetupPackage =
181 {
182     .Port = REMOTE_MCAST_SETUP_PORT,
183     .Init = LmhpRemoteMcastSetupInit,
184     .IsInitialized = LmhpRemoteMcastSetupIsInitialized,
185     .IsTxPending = LmhpRemoteMcastSetupIsTxPending,
186     .Process = LmhpRemoteMcastSetupProcess,
187     .OnMcpsConfirmProcess = NULL,                              // Not used in this package
188     .OnMcpsIndicationProcess = LmhpRemoteMcastSetupOnMcpsIndication,
189     .OnMlmeConfirmProcess = NULL,                              // Not used in this package
190     .OnMlmeIndicationProcess = NULL,                           // Not used in this package
191     .OnMacMcpsRequest = NULL,                                  // To be initialized by LmHandler
192     .OnMacMlmeRequest = NULL,                                  // To be initialized by LmHandler
193     .OnJoinRequest = NULL,                                     // To be initialized by LmHandler
194     .OnDeviceTimeRequest = NULL,                               // To be initialized by LmHandler
195     .OnSysTimeUpdate = NULL,                                   // To be initialized by LmHandler
196 };
197 
LmhpRemoteMcastSetupPackageFactory(void)198 LmhPackage_t *LmhpRemoteMcastSetupPackageFactory( void )
199 {
200     return &LmhpRemoteMcastSetupPackage;
201 }
202 
LmhpRemoteMcastSetupInit(void * params,uint8_t * dataBuffer,uint8_t dataBufferMaxSize)203 static void LmhpRemoteMcastSetupInit( void * params, uint8_t *dataBuffer, uint8_t dataBufferMaxSize )
204 {
205     if( dataBuffer != NULL )
206     {
207         LmhpRemoteMcastSetupState.DataBuffer = dataBuffer;
208         LmhpRemoteMcastSetupState.DataBufferMaxSize = dataBufferMaxSize;
209         LmhpRemoteMcastSetupState.Initialized = true;
210         TimerInit( &SessionStartTimer, OnSessionStartTimer );
211         TimerInit( &SessionStopTimer, OnSessionStopTimer );
212     }
213     else
214     {
215         LmhpRemoteMcastSetupState.Initialized = false;
216     }
217     LmhpRemoteMcastSetupState.IsTxPending = false;
218 }
219 
LmhpRemoteMcastSetupIsInitialized(void)220 static bool LmhpRemoteMcastSetupIsInitialized( void )
221 {
222     return LmhpRemoteMcastSetupState.Initialized;
223 }
224 
LmhpRemoteMcastSetupIsTxPending(void)225 static bool LmhpRemoteMcastSetupIsTxPending( void )
226 {
227     return LmhpRemoteMcastSetupState.IsTxPending;
228 }
229 
LmhpRemoteMcastSetupProcess(void)230 static void LmhpRemoteMcastSetupProcess( void )
231 {
232     LmhpRemoteMcastSetupSessionStates_t state;
233 
234     CRITICAL_SECTION_BEGIN( );
235     state = LmhpRemoteMcastSetupState.SessionState;
236     LmhpRemoteMcastSetupState.SessionState = REMOTE_MCAST_SETUP_SESSION_STATE_IDLE;
237     CRITICAL_SECTION_END( );
238 
239     switch( state )
240     {
241         case REMOTE_MCAST_SETUP_SESSION_STATE_START:
242             // Switch to Class C
243             LmHandlerRequestClass( CLASS_C );
244 
245             TimerSetValue( &SessionStopTimer, ( 1 << McSessionData[0].SessionTimeout ) * 1000 );
246             TimerStart( &SessionStopTimer );
247             break;
248         case REMOTE_MCAST_SETUP_SESSION_STATE_STOP:
249             // Switch back to Class A
250             LmHandlerRequestClass( CLASS_A );
251             break;
252         case REMOTE_MCAST_SETUP_SESSION_STATE_IDLE:
253         // Intentional fall through
254         default:
255             // Nothing to do.
256             break;
257     }
258 }
259 
LmhpRemoteMcastSetupOnMcpsIndication(McpsIndication_t * mcpsIndication)260 static void LmhpRemoteMcastSetupOnMcpsIndication( McpsIndication_t *mcpsIndication )
261 {
262     uint8_t cmdIndex = 0;
263     uint8_t dataBufferIndex = 0;
264 
265     if( mcpsIndication->Port != REMOTE_MCAST_SETUP_PORT )
266     {
267         return;
268     }
269 
270     while( cmdIndex < mcpsIndication->BufferSize )
271     {
272         switch( mcpsIndication->Buffer[cmdIndex++] )
273         {
274             case REMOTE_MCAST_SETUP_PKG_VERSION_REQ:
275             {
276                 LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = REMOTE_MCAST_SETUP_PKG_VERSION_ANS;
277                 LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = REMOTE_MCAST_SETUP_ID;
278                 LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = REMOTE_MCAST_SETUP_VERSION;
279                 break;
280             }
281             case REMOTE_MCAST_SETUP_MC_GROUP_STATUS_REQ:
282             {
283                 // TODO implement command prosessing and handling
284                 break;
285             }
286             case REMOTE_MCAST_SETUP_MC_GROUP_SETUP_REQ:
287             {
288                 uint8_t id = mcpsIndication->Buffer[cmdIndex++];
289                 McSessionData[id].McGroupData.IdHeader.Value = id;
290 
291                 McSessionData[id].McGroupData.McAddr =  ( mcpsIndication->Buffer[cmdIndex++] << 0  ) & 0x000000FF;
292                 McSessionData[id].McGroupData.McAddr += ( mcpsIndication->Buffer[cmdIndex++] << 8  ) & 0x0000FF00;
293                 McSessionData[id].McGroupData.McAddr += ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000;
294                 McSessionData[id].McGroupData.McAddr += ( mcpsIndication->Buffer[cmdIndex++] << 24 ) & 0xFF000000;
295 
296                 for( int8_t i = 0; i < 16; i++ )
297                 {
298                     McSessionData[id].McGroupData.McKeyEncrypted[i] = mcpsIndication->Buffer[cmdIndex++];
299                 }
300 
301                 McSessionData[id].McGroupData.McFCountMin =  ( mcpsIndication->Buffer[cmdIndex++] << 0  ) & 0x000000FF;
302                 McSessionData[id].McGroupData.McFCountMin += ( mcpsIndication->Buffer[cmdIndex++] << 8  ) & 0x0000FF00;
303                 McSessionData[id].McGroupData.McFCountMin += ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000;
304                 McSessionData[id].McGroupData.McFCountMin += ( mcpsIndication->Buffer[cmdIndex++] << 24 ) & 0xFF000000;
305 
306                 McSessionData[id].McGroupData.McFCountMax =  ( mcpsIndication->Buffer[cmdIndex++] << 0  ) & 0x000000FF;
307                 McSessionData[id].McGroupData.McFCountMax += ( mcpsIndication->Buffer[cmdIndex++] << 8  ) & 0x0000FF00;
308                 McSessionData[id].McGroupData.McFCountMax += ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000;
309                 McSessionData[id].McGroupData.McFCountMax += ( mcpsIndication->Buffer[cmdIndex++] << 24 ) & 0xFF000000;
310 
311                 McChannelParams_t channel =
312                 {
313                     .IsRemotelySetup = true,
314                     .Class = CLASS_C, // Field not used for multicast channel setup. Must be initialized to something
315                     .IsEnabled = true,
316                     .GroupID = ( AddressIdentifier_t )McSessionData[id].McGroupData.IdHeader.Fields.McGroupId,
317                     .Address = McSessionData[id].McGroupData.McAddr,
318                     .McKeys.McKeyE = McSessionData[id].McGroupData.McKeyEncrypted,
319                     .FCountMin = McSessionData[id].McGroupData.McFCountMin,
320                     .FCountMax = McSessionData[id].McGroupData.McFCountMax,
321                     .RxParams.ClassC = // Field not used for multicast channel setup. Must be initialized to something
322                     {
323                         .Frequency = 0,
324                         .Datarate = 0
325                     }
326                 };
327                 uint8_t idError = 0x01; // One bit value
328                 if( LoRaMacMcChannelSetup( &channel ) == LORAMAC_STATUS_OK )
329                 {
330                     idError = 0x00;
331                 }
332                 LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = REMOTE_MCAST_SETUP_MC_GROUP_SETUP_ANS;
333                 LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = ( idError << 2 ) | McSessionData[id].McGroupData.IdHeader.Fields.McGroupId;
334                 break;
335             }
336             case REMOTE_MCAST_SETUP_MC_GROUP_DELETE_REQ:
337             {
338                 uint8_t status = 0x00;
339                 uint8_t id = mcpsIndication->Buffer[cmdIndex++] & 0x03;
340 
341                 status = id;
342 
343                 LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = REMOTE_MCAST_SETUP_MC_GROUP_DELETE_ANS;
344 
345                 if( LoRaMacMcChannelDelete( ( AddressIdentifier_t )id ) != LORAMAC_STATUS_OK )
346                 {
347                     status |= 0x04; // McGroupUndefined bit set
348                 }
349                 LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = status;
350                 break;
351             }
352             case REMOTE_MCAST_SETUP_MC_GROUP_CLASS_C_SESSION_REQ:
353             {
354                 uint8_t status = 0x00;
355                 uint8_t id = mcpsIndication->Buffer[cmdIndex++] & 0x03;
356 
357                 McSessionData[id].SessionTime =  ( mcpsIndication->Buffer[cmdIndex++] << 0  ) & 0x000000FF;
358                 McSessionData[id].SessionTime += ( mcpsIndication->Buffer[cmdIndex++] << 8  ) & 0x0000FF00;
359                 McSessionData[id].SessionTime += ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000;
360                 McSessionData[id].SessionTime += ( mcpsIndication->Buffer[cmdIndex++] << 24 ) & 0xFF000000;
361 
362                 // Add Unix to Gps epcoh offset. The system time is based on Unix time.
363                 McSessionData[id].SessionTime += UNIX_GPS_EPOCH_OFFSET;
364 
365                 McSessionData[id].SessionTimeout =  mcpsIndication->Buffer[cmdIndex++] & 0x0F;
366 
367                 McSessionData[id].RxParams.ClassC.Frequency =  ( mcpsIndication->Buffer[cmdIndex++] << 0  ) & 0x000000FF;
368                 McSessionData[id].RxParams.ClassC.Frequency |= ( mcpsIndication->Buffer[cmdIndex++] << 8  ) & 0x0000FF00;
369                 McSessionData[id].RxParams.ClassC.Frequency |= ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000;
370                 McSessionData[id].RxParams.ClassC.Frequency *= 100;
371 
372                 McSessionData[id].RxParams.ClassC.Datarate = mcpsIndication->Buffer[cmdIndex++];
373 
374                 LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = REMOTE_MCAST_SETUP_MC_GROUP_CLASS_C_SESSION_ANS;
375                 if( LoRaMacMcChannelSetupRxParams( ( AddressIdentifier_t )id, &McSessionData[id].RxParams, &status ) == LORAMAC_STATUS_OK )
376                 {
377                     SysTime_t curTime = { .Seconds = 0, .SubSeconds = 0 };
378                     curTime = SysTimeGet( );
379 
380                     int32_t timeToSessionStart = McSessionData[id].SessionTime - curTime.Seconds;
381                     if( timeToSessionStart > 0 )
382                     {
383                         // Start session start timer
384                         TimerSetValue( &SessionStartTimer, timeToSessionStart * 1000 );
385                         TimerStart( &SessionStartTimer );
386 
387                         DBG( "Time2SessionStart: %ld ms\n", timeToSessionStart * 1000 );
388 
389                         LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = status;
390                         LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = ( timeToSessionStart >> 0  ) & 0xFF;
391                         LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = ( timeToSessionStart >> 8  ) & 0xFF;
392                         LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = ( timeToSessionStart >> 16 ) & 0xFF;
393                         break;
394                     }
395                     else
396                     {
397                         // Session start time before current device time
398                         status |= 0x10;
399                     }
400                 }
401                 LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = status;
402                 break;
403             }
404             case REMOTE_MCAST_SETUP_MC_GROUP_CLASS_B_SESSION_REQ:
405             {
406                 // TODO implement command prosessing and handling
407                 break;
408             }
409             default:
410             {
411                 break;
412             }
413         }
414     }
415 
416     if( dataBufferIndex != 0 )
417     {
418         // Answer commands
419         LmHandlerAppData_t appData =
420         {
421             .Buffer = LmhpRemoteMcastSetupState.DataBuffer,
422             .BufferSize = dataBufferIndex,
423             .Port = REMOTE_MCAST_SETUP_PORT
424         };
425         LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG );
426 
427         DBG( "ID          : %d\n", McSessionData[0].McGroupData.IdHeader.Fields.McGroupId );
428         DBG( "McAddr      : %08lX\n", McSessionData[0].McGroupData.McAddr );
429         DBG( "McKey       : %02X", McSessionData[0].McGroupData.McKeyEncrypted[0] );
430         for( int i = 1; i < 16; i++ )
431         {
432             DBG( "-%02X",  McSessionData[0].McGroupData.McKeyEncrypted[i] );
433         }
434         DBG( "\n" );
435         DBG( "McFCountMin : %lu\n",  McSessionData[0].McGroupData.McFCountMin );
436         DBG( "McFCountMax : %lu\n",  McSessionData[0].McGroupData.McFCountMax );
437         DBG( "SessionTime : %lu\n",  McSessionData[0].SessionTime );
438         DBG( "SessionTimeT: %d\n",  McSessionData[0].SessionTimeout );
439         DBG( "Rx Freq     : %lu\n", McSessionData[0].RxParams.ClassC.Frequency );
440         DBG( "Rx DR       : DR_%d\n", McSessionData[0].RxParams.ClassC.Datarate );
441 
442     }
443 }
444 
OnSessionStartTimer(void * context)445 static void OnSessionStartTimer( void *context )
446 {
447     TimerStop( &SessionStartTimer );
448 
449     LmhpRemoteMcastSetupState.SessionState = REMOTE_MCAST_SETUP_SESSION_STATE_START;
450 }
451 
OnSessionStopTimer(void * context)452 static void OnSessionStopTimer( void *context )
453 {
454     TimerStop( &SessionStopTimer );
455 
456     LmhpRemoteMcastSetupState.SessionState = REMOTE_MCAST_SETUP_SESSION_STATE_STOP;
457 }
458