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