/*! * \file LmhpRemoteMcastSetup.c * * \brief Implements the LoRa-Alliance remote multicast setup package * Specification: https://lora-alliance.org/sites/default/files/2018-09/remote_multicast_setup_v1.0.0.pdf * * \copyright Revised BSD License, see section \ref LICENSE. * * \code * ______ _ * / _____) _ | | * ( (____ _____ ____ _| |_ _____ ____| |__ * \____ \| ___ | (_ _) ___ |/ ___) _ \ * _____) ) ____| | | || |_| ____( (___| | | | * (______/|_____)_|_|_| \__)_____)\____)_| |_| * (C)2013-2018 Semtech * * \endcode * * \author Miguel Luis ( Semtech ) */ /** ****************************************************************************** * * Portions COPYRIGHT 2020 STMicroelectronics * * @file LmhpRemoteMcastSetup.c * @author MCD Application Team * @brief Remote Multicast Package definition ****************************************************************************** */ /* Includes ------------------------------------------------------------------*/ #include "LmHandler.h" #include "LmhpRemoteMcastSetup.h" #include "mw_log_conf.h" /* needed for MW_LOG */ /*! * LoRaWAN Application Layer Remote multicast setup Specification */ #define REMOTE_MCAST_SETUP_PORT 200 #define REMOTE_MCAST_SETUP_ID 2 #define REMOTE_MCAST_SETUP_VERSION 1 typedef enum LmhpRemoteMcastSetupSessionStates_e { REMOTE_MCAST_SETUP_SESSION_STATE_IDLE, REMOTE_MCAST_SETUP_SESSION_STATE_START, REMOTE_MCAST_SETUP_SESSION_STATE_STOP, }LmhpRemoteMcastSetupSessionStates_t; /*! * Package current context */ typedef struct LmhpRemoteMcastSetupState_s { bool Initialized; bool IsRunning; LmhpRemoteMcastSetupSessionStates_t SessionState; uint8_t DataBufferMaxSize; uint8_t *DataBuffer; }LmhpRemoteMcastSetupState_t; typedef enum LmhpRemoteMcastSetupMoteCmd_e { REMOTE_MCAST_SETUP_PKG_VERSION_ANS = 0x00, REMOTE_MCAST_SETUP_MC_GROUP_STATUS_ANS = 0x01, REMOTE_MCAST_SETUP_MC_GROUP_SETUP_ANS = 0x02, REMOTE_MCAST_SETUP_MC_GROUP_DELETE_ANS = 0x03, REMOTE_MCAST_SETUP_MC_GROUP_CLASS_C_SESSION_ANS = 0x04, REMOTE_MCAST_SETUP_MC_GROUP_CLASS_B_SESSION_ANS = 0x05, }LmhpRemoteMcastSetupMoteCmd_t; typedef enum LmhpRemoteMcastSetupSrvCmd_e { REMOTE_MCAST_SETUP_PKG_VERSION_REQ = 0x00, REMOTE_MCAST_SETUP_MC_GROUP_STATUS_REQ = 0x01, REMOTE_MCAST_SETUP_MC_GROUP_SETUP_REQ = 0x02, REMOTE_MCAST_SETUP_MC_GROUP_DELETE_REQ = 0x03, REMOTE_MCAST_SETUP_MC_GROUP_CLASS_C_SESSION_REQ = 0x04, REMOTE_MCAST_SETUP_MC_GROUP_CLASS_B_SESSION_REQ = 0x05, }LmhpRemoteMcastSetupSrvCmd_t; /*! * Initializes the package with provided parameters * * \param [IN] params Pointer to the package parameters * \param [IN] dataBuffer Pointer to main application buffer * \param [IN] dataBufferMaxSize Main application buffer maximum size */ static void LmhpRemoteMcastSetupInit( void *params, uint8_t *dataBuffer, uint8_t dataBufferMaxSize ); /*! * Returns the current package initialization status. * * \retval status Package initialization status * [true: Initialized, false: Not initialized] */ static bool LmhpRemoteMcastSetupIsInitialized( void ); /*! * Returns the package operation status. * * \retval status Package operation status * [true: Running, false: Not running] */ static bool LmhpRemoteMcastSetupIsRunning( void ); /*! * Processes the internal package events. */ static void LmhpRemoteMcastSetupProcess( void ); /*! * Processes the MCPS Indication * * \param [IN] mcpsIndication MCPS indication primitive data */ static void LmhpRemoteMcastSetupOnMcpsIndication( McpsIndication_t *mcpsIndication ); static void OnSessionStartTimer( void *context ); static void OnSessionStopTimer( void *context ); static LmhpRemoteMcastSetupState_t LmhpRemoteMcastSetupState = { .Initialized = false, .IsRunning = false, .SessionState = REMOTE_MCAST_SETUP_SESSION_STATE_IDLE, }; typedef struct McGroupData_s { uint8_t McGroupEnabled; union { uint8_t Value; struct { uint8_t McGroupId: 2; uint8_t RFU: 6; }Fields; }IdHeader; uint32_t McAddr; uint8_t McKeyEncrypted[16]; uint32_t McFCountMin; uint32_t McFCountMax; }McGroupData_t; typedef enum eSessionState { SESSION_STOPPED, SESSION_STARTED }SessionState_t; typedef struct McSessionData_s { McGroupData_t McGroupData; SessionState_t SessionState; uint32_t SessionTime; uint8_t SessionTimeout; McRxParams_t RxParams; }McSessionData_t; static McSessionData_t McSessionData[LORAMAC_MAX_MC_CTX]; /*! * Session start timer */ static TimerEvent_t SessionStartTimer; /*! * Session start timer */ static TimerEvent_t SessionStopTimer; static LmhPackage_t LmhpRemoteMcastSetupPackage = { .Port = REMOTE_MCAST_SETUP_PORT, .Init = LmhpRemoteMcastSetupInit, .IsInitialized = LmhpRemoteMcastSetupIsInitialized, .IsRunning = LmhpRemoteMcastSetupIsRunning, .Process = LmhpRemoteMcastSetupProcess, .OnMcpsConfirmProcess = NULL, // Not used in this package .OnMcpsIndicationProcess = LmhpRemoteMcastSetupOnMcpsIndication, .OnMlmeConfirmProcess = NULL, // Not used in this package .OnJoinRequest = NULL, // To be initialized by LmHandler .OnSendRequest = NULL, // To be initialized by LmHandler .OnDeviceTimeRequest = NULL, // To be initialized by LmHandler .OnSysTimeUpdate = NULL, // To be initialized by LmHandler .OnPackageProcessEvent = NULL, // To be initialized by LmHandler }; LmhPackage_t *LmhpRemoteMcastSetupPackageFactory( void ) { return &LmhpRemoteMcastSetupPackage; } static void LmhpRemoteMcastSetupInit( void * params, uint8_t *dataBuffer, uint8_t dataBufferMaxSize ) { if( dataBuffer != NULL ) { LmhpRemoteMcastSetupState.DataBuffer = dataBuffer; LmhpRemoteMcastSetupState.DataBufferMaxSize = dataBufferMaxSize; LmhpRemoteMcastSetupState.Initialized = true; LmhpRemoteMcastSetupState.IsRunning = true; TimerInit( &SessionStartTimer, OnSessionStartTimer ); TimerInit( &SessionStopTimer, OnSessionStopTimer ); } else { LmhpRemoteMcastSetupState.IsRunning = false; LmhpRemoteMcastSetupState.Initialized = false; } for (uint8_t id = 0; id < LORAMAC_MAX_MC_CTX; id++) { McSessionData[id].McGroupData.McGroupEnabled = false; } } static bool LmhpRemoteMcastSetupIsInitialized( void ) { return LmhpRemoteMcastSetupState.Initialized; } static bool LmhpRemoteMcastSetupIsRunning( void ) { if( LmhpRemoteMcastSetupState.Initialized == false ) { return false; } return LmhpRemoteMcastSetupState.IsRunning; } static void LmhpRemoteMcastSetupProcess( void ) { LmhpRemoteMcastSetupSessionStates_t state; CRITICAL_SECTION_BEGIN( ); state = LmhpRemoteMcastSetupState.SessionState; CRITICAL_SECTION_END( ); switch( state ) { case REMOTE_MCAST_SETUP_SESSION_STATE_START: // Switch to Class C if ( LmHandlerRequestClass( CLASS_C ) == LORAMAC_HANDLER_SUCCESS ) { LmhpRemoteMcastSetupState.SessionState = REMOTE_MCAST_SETUP_SESSION_STATE_IDLE; TimerSetValue( &SessionStopTimer, ( 1 << McSessionData[0].SessionTimeout ) * 1000 ); TimerStart(&SessionStopTimer); } else { TimerSetValue( &SessionStartTimer, 1000 ); TimerStart(&SessionStartTimer); } break; case REMOTE_MCAST_SETUP_SESSION_STATE_STOP: // Switch back to Class A if ( LmHandlerRequestClass( CLASS_A ) == LORAMAC_HANDLER_SUCCESS ) { LmhpRemoteMcastSetupState.SessionState = REMOTE_MCAST_SETUP_SESSION_STATE_IDLE; } else { TimerSetValue( &SessionStopTimer, 1000 ); TimerStart(&SessionStopTimer); } break; case REMOTE_MCAST_SETUP_SESSION_STATE_IDLE: break; default: break; } } static void LmhpRemoteMcastSetupOnMcpsIndication( McpsIndication_t *mcpsIndication ) { uint8_t cmdIndex = 0; uint8_t dataBufferIndex = 0; uint8_t id = 0xFF; if( mcpsIndication->Port != REMOTE_MCAST_SETUP_PORT ) { return; } while( cmdIndex < mcpsIndication->BufferSize ) { switch( mcpsIndication->Buffer[cmdIndex++] ) { case REMOTE_MCAST_SETUP_PKG_VERSION_REQ: { LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = REMOTE_MCAST_SETUP_PKG_VERSION_ANS; LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = REMOTE_MCAST_SETUP_ID; LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = REMOTE_MCAST_SETUP_VERSION; break; } case REMOTE_MCAST_SETUP_MC_GROUP_STATUS_REQ: { uint8_t nbAvailableGroups = 0; uint8_t reqGroupMask = mcpsIndication->Buffer[cmdIndex++] & 0x0F; uint8_t AnsGroupMask = 0x00; LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = REMOTE_MCAST_SETUP_MC_GROUP_STATUS_ANS; /* move index to the next first optional list */ dataBufferIndex++; for (id = 0; id < LORAMAC_MAX_MC_CTX; id++) { if( McSessionData[id].McGroupData.McGroupEnabled ) { nbAvailableGroups++; /* If multicast group defined in the input bit mask */ if( ( reqGroupMask & ( 1 << id ) ) != 0 ) { AnsGroupMask |= (1 << id); LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = id; LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = (McSessionData[id].McGroupData.McAddr >> 0) & 0xFF; LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = (McSessionData[id].McGroupData.McAddr >> 8) & 0xFF; LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = (McSessionData[id].McGroupData.McAddr >> 16) & 0xFF; LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = (McSessionData[id].McGroupData.McAddr >> 24) & 0xFF; } } } /* set the status bit */ LmhpRemoteMcastSetupState.DataBuffer[1] = (nbAvailableGroups & 0x07) << 4 | (AnsGroupMask & 0x0F); break; } case REMOTE_MCAST_SETUP_MC_GROUP_SETUP_REQ: { id = mcpsIndication->Buffer[cmdIndex++] & 0x03; McSessionData[id].McGroupData.IdHeader.Value = id; McSessionData[id].McGroupData.McAddr = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF; McSessionData[id].McGroupData.McAddr += ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00; McSessionData[id].McGroupData.McAddr += ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000; McSessionData[id].McGroupData.McAddr += ( mcpsIndication->Buffer[cmdIndex++] << 24 ) & 0xFF000000; for( int8_t i = 0; i < 16; i++ ) { McSessionData[id].McGroupData.McKeyEncrypted[i] = mcpsIndication->Buffer[cmdIndex++]; } McSessionData[id].McGroupData.McFCountMin = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF; McSessionData[id].McGroupData.McFCountMin += ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00; McSessionData[id].McGroupData.McFCountMin += ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000; McSessionData[id].McGroupData.McFCountMin += ( mcpsIndication->Buffer[cmdIndex++] << 24 ) & 0xFF000000; McSessionData[id].McGroupData.McFCountMax = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF; McSessionData[id].McGroupData.McFCountMax += ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00; McSessionData[id].McGroupData.McFCountMax += ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000; McSessionData[id].McGroupData.McFCountMax += ( mcpsIndication->Buffer[cmdIndex++] << 24 ) & 0xFF000000; McChannelParams_t channel = { .IsRemotelySetup = true, .Class = CLASS_C, // Field not used for multicast channel setup. Must be initialized to something .IsEnabled = true, .GroupID = ( AddressIdentifier_t )McSessionData[id].McGroupData.IdHeader.Fields.McGroupId, .Address = McSessionData[id].McGroupData.McAddr, .McKeys.McKeyE = McSessionData[id].McGroupData.McKeyEncrypted, .FCountMin = McSessionData[id].McGroupData.McFCountMin, .FCountMax = McSessionData[id].McGroupData.McFCountMax, .RxParams.ClassC = // Field not used for multicast channel setup. Must be initialized to something { .Frequency = 0, .Datarate = 0 } }; uint8_t idError = 0x01; // One bit value if( LoRaMacMcChannelSetup( &channel ) == LORAMAC_STATUS_OK ) { idError = 0x00; McSessionData[id].McGroupData.McGroupEnabled = true; } LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = REMOTE_MCAST_SETUP_MC_GROUP_SETUP_ANS; LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = ( idError << 2 ) | McSessionData[id].McGroupData.IdHeader.Fields.McGroupId; break; } case REMOTE_MCAST_SETUP_MC_GROUP_DELETE_REQ: { uint8_t status = 0x00; id = mcpsIndication->Buffer[cmdIndex++] & 0x03; status = id; McSessionData[id].McGroupData.IdHeader.Value = 0; McSessionData[id].McGroupData.McAddr = 0; UTIL_MEM_set_8( McSessionData[id].McGroupData.McKeyEncrypted, 0x00, 16 ); McSessionData[id].McGroupData.McFCountMin = 0; McSessionData[id].McGroupData.McFCountMax = 0; LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = REMOTE_MCAST_SETUP_MC_GROUP_DELETE_ANS; if( LoRaMacMcChannelDelete( ( AddressIdentifier_t )id ) != LORAMAC_STATUS_OK ) { status |= 0x04; // McGroupUndefined bit set } else { McSessionData[id].McGroupData.McGroupEnabled = false; } LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = status; break; } case REMOTE_MCAST_SETUP_MC_GROUP_CLASS_C_SESSION_REQ: { uint8_t status = 0x00; int32_t timeToSessionStart = 0; id = mcpsIndication->Buffer[cmdIndex++] & 0x03; McSessionData[id].SessionTime = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF; McSessionData[id].SessionTime += ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00; McSessionData[id].SessionTime += ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000; McSessionData[id].SessionTime += ( mcpsIndication->Buffer[cmdIndex++] << 24 ) & 0xFF000000; // Add Unix to Gps epoch offset. The system time is based on Unix time. McSessionData[id].SessionTime += UNIX_GPS_EPOCH_OFFSET; McSessionData[id].SessionTimeout = mcpsIndication->Buffer[cmdIndex++] & 0x0F; McSessionData[id].RxParams.ClassC.Frequency = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF; McSessionData[id].RxParams.ClassC.Frequency |= ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00; McSessionData[id].RxParams.ClassC.Frequency |= ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000; McSessionData[id].RxParams.ClassC.Frequency *= 100; McSessionData[id].RxParams.ClassC.Datarate = mcpsIndication->Buffer[cmdIndex++]; LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = REMOTE_MCAST_SETUP_MC_GROUP_CLASS_C_SESSION_ANS; if( LoRaMacMcChannelSetupRxParams( ( AddressIdentifier_t )id, &McSessionData[id].RxParams, &status ) == LORAMAC_STATUS_OK ) { SysTime_t curTime = { .Seconds = 0, .SubSeconds = 0 }; curTime = SysTimeGet( ); timeToSessionStart = McSessionData[id].SessionTime - curTime.Seconds; if( timeToSessionStart > 0 ) { // Start session start timer TimerSetValue( &SessionStartTimer, timeToSessionStart * 1000 ); TimerStart( &SessionStartTimer ); MW_LOG(TS_OFF, VLEVEL_M, "Time2SessionStart: %d ms\r\n", timeToSessionStart * 1000); } else { // Session start time before current device time status |= 0x10; } } LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = status; if( status == 0x00 ) { LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = (timeToSessionStart >> 0) & 0xFF; LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = (timeToSessionStart >> 8) & 0xFF; LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = (timeToSessionStart >> 16) & 0xFF; } break; } case REMOTE_MCAST_SETUP_MC_GROUP_CLASS_B_SESSION_REQ: { // implement command prosessing and handling break; } default: { break; } } } if( dataBufferIndex != 0 ) { // Answer commands LmHandlerAppData_t appData = { .Buffer = LmhpRemoteMcastSetupState.DataBuffer, .BufferSize = dataBufferIndex, .Port = REMOTE_MCAST_SETUP_PORT }; bool current_dutycycle; LmHandlerGetDutyCycleEnable( ¤t_dutycycle ); /* force Duty Cycle OFF to this Send */ LmHandlerSetDutyCycleEnable( false ); LmhpRemoteMcastSetupPackage.OnSendRequest( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG, NULL, true ); /* restore initial Duty Cycle */ LmHandlerSetDutyCycleEnable( current_dutycycle ); if (id != 0xFF && id < LORAMAC_MAX_MC_CTX) { MW_LOG(TS_OFF, VLEVEL_M, "ID : %d\r\n", McSessionData[id].McGroupData.IdHeader.Fields.McGroupId); MW_LOG(TS_OFF, VLEVEL_M, "McAddr : %08X\r\n", McSessionData[id].McGroupData.McAddr); MW_LOG(TS_OFF, VLEVEL_M, "McKey : %02X", McSessionData[id].McGroupData.McKeyEncrypted[0]); for ( int i = 1; i < 16; i++ ) { MW_LOG(TS_OFF, VLEVEL_M, "-%02X", McSessionData[id].McGroupData.McKeyEncrypted[i]); } MW_LOG(TS_OFF, VLEVEL_M, "\r\n"); MW_LOG(TS_OFF, VLEVEL_M, "McFCountMin : %u\r\n", McSessionData[id].McGroupData.McFCountMin); MW_LOG(TS_OFF, VLEVEL_M, "McFCountMax : %u\r\n", McSessionData[id].McGroupData.McFCountMax); MW_LOG(TS_OFF, VLEVEL_M, "SessionTime : %u\r\n", McSessionData[id].SessionTime); MW_LOG(TS_OFF, VLEVEL_M, "SessionTimeT: %d s\r\n", (1 << McSessionData[id].SessionTimeout)); MW_LOG(TS_OFF, VLEVEL_M, "Rx Freq : %u\r\n", McSessionData[id].RxParams.ClassC.Frequency); MW_LOG(TS_OFF, VLEVEL_M, "Rx DR : DR_%d\r\n", McSessionData[id].RxParams.ClassC.Datarate); } } } static void OnSessionStartTimer( void *context ) { TimerStop( &SessionStartTimer ); LmhpRemoteMcastSetupState.SessionState = REMOTE_MCAST_SETUP_SESSION_STATE_START; LmhpRemoteMcastSetupPackage.OnPackageProcessEvent(); } static void OnSessionStopTimer( void *context ) { TimerStop( &SessionStopTimer ); LmhpRemoteMcastSetupState.SessionState = REMOTE_MCAST_SETUP_SESSION_STATE_STOP; LmhpRemoteMcastSetupPackage.OnPackageProcessEvent(); }