/*! * \file LmHandler.c * * \brief Implements the LoRaMac layer handling. * Provides the possibility to register applicative packages. * * \remark Inspired by the examples provided on the en.i-cube_lrwan fork. * MCD Application Team ( STMicroelectronics International ) * * \copyright Revised BSD License, see section \ref LICENSE.txt. * * \code * ______ _ * / _____) _ | | * ( (____ _____ ____ _| |_ _____ ____| |__ * \____ \| ___ | (_ _) ___ |/ ___) _ \ * _____) ) ____| | | || |_| ____( (___| | | | * (______/|_____)_|_|_| \__)_____)\____)_| |_| * (C)2013-2018 Semtech * * \endcode * * \author Miguel Luis ( Semtech ) */ /** ****************************************************************************** * * Portions COPYRIGHT 2020 STMicroelectronics * * @file LmHandler.c * @author MCD Application Team * @brief LoRaMAC Layer handling definition ****************************************************************************** */ /* Includes ------------------------------------------------------------------*/ #include "utilities.h" #include "timer.h" #include "Commissioning.h" #include "NvmDataMgmt.h" #include "radio.h" #include "Region.h" #include "LoRaMac.h" #include "LoRaMacVersion.h" #include "LoRaMacTest.h" #include "LmHandler.h" #include "LmhPackage.h" #include "LmhpCompliance.h" #include "secure-element.h" #include "mw_log_conf.h" /* needed for MW_LOG */ #include "lorawan_version.h" #include "lora_info.h" #if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) #else /* LORAWAN_KMS == 1 */ #include "kms.h" #include "kms_platf_objects_interface.h" #endif /* LORAWAN_KMS */ #include "LmhpPackagesRegistration.h" /* Private typedef -----------------------------------------------------------*/ /*! * MAC notification type */ typedef enum PackageNotifyTypes_e { PACKAGE_MCPS_CONFIRM, PACKAGE_MCPS_INDICATION, PACKAGE_MLME_CONFIRM, PACKAGE_MLME_INDICATION, } PackageNotifyTypes_t; /* Private define ------------------------------------------------------------*/ /*! * Package application data buffer size */ #define LORAWAN_APP_DATA_BUFFER_MAX_SIZE 242 /* Private macro -------------------------------------------------------------*/ /*! * Hex 8 split buffer */ #define HEX8(X) X[0], X[1], X[2], X[3], X[4], X[5], X[6], X[7] /*! * Hex 16 split buffer */ #define HEX16(X) HEX8(X), X[8], X[9], X[10], X[11], X[12], X[13], X[14], X[15] /* Private variables ---------------------------------------------------------*/ static CommissioningParams_t CommissioningParams = { .DevEui = { 0 }, /* Automatically filed from secure-element */ .JoinEui = { 0 }, /* Automatically filed from secure-element */ .DevAddr = 0, /* Automatically filed from secure-element */ .NetworkId = LORAWAN_NETWORK_ID, }; #if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) /*! * LoRaWAN compliance tests handler parameters */ static LmhpComplianceParams_t LmhpComplianceParams = { .AdrEnabled = LORAMAC_HANDLER_ADR_ON, .DutyCycleEnabled = false, .StopPeripherals = NULL, .StartPeripherals = NULL, }; #elif (defined( LORAMAC_VERSION ) && (( LORAMAC_VERSION == 0x01000400 ) || ( LORAMAC_VERSION == 0x01010100 ))) /*! * LoRaWAN compliance tests handler parameters */ static LmhpComplianceParams_t LmhpComplianceParams = { .FwVersion.Value = 0U, /* To be initialized by LmHandler */ .OnTxPeriodicityChanged = NULL, /* To be initialized by LmHandler */ .OnTxFrameCtrlChanged = NULL, /* To be initialized by LmHandler */ .OnPingSlotPeriodicityChanged = NULL, /* To be initialized by LmHandler */ }; #endif /* LORAMAC_VERSION */ static LmhPackage_t *LmHandlerPackages[PKG_MAX_NUMBER]; /*! * Upper layer LoRaMac parameters */ static LmHandlerParams_t LmHandlerParams; /*! * Upper layer callbacks */ static LmHandlerCallbacks_t *LmHandlerCallbacks; /*! * Used to notify LmHandler of LoRaMac events */ static LoRaMacPrimitives_t LoRaMacPrimitives; /*! * LoRaMac callbacks */ static LoRaMacCallback_t LoRaMacCallbacks; static LmHandlerJoinParams_t JoinParams = { .CommissioningParams = &CommissioningParams, .Mode = ACTIVATION_TYPE_NONE, .forceRejoin = false, .Datarate = DR_0, .TxPower = TX_POWER_0, .Status = LORAMAC_HANDLER_ERROR }; static LmHandlerTxParams_t TxParams = { .CommissioningParams = &CommissioningParams, .MsgType = LORAMAC_HANDLER_UNCONFIRMED_MSG, .AckReceived = 0, .Datarate = DR_0, .UplinkCounter = 0, .AppData = { .Port = 0, .BufferSize = 0, .Buffer = NULL, }, .TxPower = TX_POWER_0, .Channel = 0, }; static LmHandlerRxParams_t RxParams = { .CommissioningParams = &CommissioningParams, .Rssi = 0, .Snr = 0, .DownlinkCounter = 0, .RxSlot = -1, .LinkCheck = false, .DemodMargin = 0, .NbGateways = 0, }; #if ( LORAMAC_CLASSB_ENABLED == 1 ) static LmHandlerBeaconParams_t BeaconParams = { .State = LORAMAC_HANDLER_BEACON_ACQUIRING, .Info = { .Time = { .Seconds = 0, .SubSeconds = 0 }, .Frequency = 0, .Datarate = 0, .Rssi = 0, .Snr = 0, .GwSpecific = { .InfoDesc = 0, .Info = { 0 }, }, }, }; #endif /* LORAMAC_CLASSB_ENABLED == 1 */ #if ( LORAMAC_CLASSB_ENABLED == 1 ) /*! * Indicates if a switch to Class B operation is pending or not. */ static bool IsClassBSwitchPending = false; #endif /* LORAMAC_CLASSB_ENABLED == 1 */ /*! * Stores the time to wait before next transmission * */ static TimerTime_t DutyCycleWaitTime = 0; #if (defined( LORAMAC_VERSION ) && (( LORAMAC_VERSION == 0x01000400 ) || ( LORAMAC_VERSION == 0x01010100 ))) /*! * Indicates if an uplink is pending upon MAC layer request * */ static bool IsUplinkTxPending = false; #endif /* LORAMAC_VERSION */ /*! * Package Application buffer */ static uint8_t AppDataBuffer[LORAWAN_APP_DATA_BUFFER_MAX_SIZE]; /*! * Package application data structure */ static LmHandlerAppData_t AppData = { 0, LORAWAN_APP_DATA_BUFFER_MAX_SIZE, AppDataBuffer }; static bool CtxRestoreDone = false; /* Private function prototypes -----------------------------------------------*/ /*! * \brief MCPS-Confirm event function * * \param [in] mcpsConfirm - Pointer to the confirm structure, * containing confirm attributes. */ static void McpsConfirm( McpsConfirm_t *mcpsConfirm ); /*! * \brief MCPS-Indication event function * * \param [in] mcpsIndication - Pointer to the indication structure, * containing indication attributes. * \param [in] rxStatus - Pointer to RX status structure */ static void McpsIndication( McpsIndication_t *mcpsIndication, LoRaMacRxStatus_t *rxStatus ); /*! * \brief MLME-Confirm event function * * \param [in] mlmeConfirm - Pointer to the confirm structure, * containing confirm attributes. */ static void MlmeConfirm( MlmeConfirm_t *mlmeConfirm ); /*! * \brief MLME-Indication event function * * \param [in] mlmeIndication - Pointer to the indication structure, * containing indication attributes. * \param [in] rxStatus - Pointer to RX status structure */ static void MlmeIndication( MlmeIndication_t *mlmeIndication, LoRaMacRxStatus_t *rxStatus ); #if ( LORAMAC_CLASSB_ENABLED == 1 ) /*! * \brief Starts the beacon search * * \retval status Returns \ref LORAMAC_HANDLER_SUCCESS if request has been * processed else \ref LORAMAC_HANDLER_ERROR */ static LmHandlerErrorStatus_t LmHandlerBeaconReq( void ); #endif /* LORAMAC_CLASSB_ENABLED == 1 */ /*! * \brief Notifies the package to process the LoRaMac callbacks. * * \param [in] notifyType MAC notification type [PACKAGE_MCPS_CONFIRM, * PACKAGE_MCPS_INDICATION, * PACKAGE_MLME_CONFIRM, * PACKAGE_MLME_INDICATION] * \param [in] params Notification parameters. The params type can be * [McpsConfirm_t, McpsIndication_t, MlmeConfirm_t, MlmeIndication_t] */ static void LmHandlerPackagesNotify( PackageNotifyTypes_t notifyType, void *params ); /*! * \brief Returns if at least one package transmission is pending or not * * \retval status Package transmission status * [true: pending, false: Not pending] */ static bool LmHandlerPackageIsTxPending( void ); /*! * \brief Processes the internal package events. */ static void LmHandlerPackagesProcess( void ); /*! * \brief Check if the package ID is initialized * * \param [in] id package identifier * * \retval status Returns true if initialized else false */ static bool LmHandlerPackageIsInitialized( uint8_t id ); #if (defined( LORAMAC_VERSION ) && (( LORAMAC_VERSION == 0x01000400 ) || ( LORAMAC_VERSION == 0x01010100 ))) /*! * \brief Will be called to change applicative Tx frame control * * \param [in] isTxConfirmed set the Tx frame in confirmed/unconfirmed control * \note Compliance test protocol callbacks used when TS001-1.0.4 + TS009 1.0.0 are defined */ static void LmHandlerOnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ); /*! * \brief Will be called to change the ping period * * \param [in] pingSlotPeriodicity index of ping period as 2^X value * \note Compliance test protocol callbacks used when TS001-1.0.4 + TS009 1.0.0 are defined */ static void LmHandlerOnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ); #endif /* LORAMAC_VERSION */ /* Exported functions ---------------------------------------------------------*/ LmHandlerErrorStatus_t LmHandlerInit( LmHandlerCallbacks_t *handlerCallbacks, uint32_t fwVersion ) { LmHandlerCallbacks = handlerCallbacks; LoRaMacPrimitives.MacMcpsConfirm = McpsConfirm; LoRaMacPrimitives.MacMcpsIndication = McpsIndication; LoRaMacPrimitives.MacMlmeConfirm = MlmeConfirm; LoRaMacPrimitives.MacMlmeIndication = MlmeIndication; LoRaMacCallbacks.GetBatteryLevel = LmHandlerCallbacks->GetBatteryLevel; LoRaMacCallbacks.GetTemperatureLevel = LmHandlerCallbacks->GetTemperature; LoRaMacCallbacks.GetUniqueId = LmHandlerCallbacks->GetUniqueId; LoRaMacCallbacks.GetDevAddress = LmHandlerCallbacks->GetDevAddr; LoRaMacCallbacks.NvmDataChange = NvmDataMgmtEvent; LoRaMacCallbacks.MacProcessNotify = LmHandlerCallbacks->OnMacProcess; #if (defined( LORAMAC_VERSION ) && (( LORAMAC_VERSION == 0x01000400 ) || ( LORAMAC_VERSION == 0x01010100 ))) LmhpComplianceParams.FwVersion.Value = fwVersion; LmhpComplianceParams.OnTxPeriodicityChanged = LmHandlerCallbacks->OnTxPeriodicityChanged; LmhpComplianceParams.OnTxFrameCtrlChanged = LmHandlerOnTxFrameCtrlChanged; LmhpComplianceParams.OnPingSlotPeriodicityChanged = LmHandlerOnPingSlotPeriodicityChanged; #endif /* LORAMAC_VERSION */ /*The LoRa-Alliance Compliance protocol package should always be initialized and activated.*/ if( LmHandlerPackageRegister( PACKAGE_ID_COMPLIANCE, &LmhpComplianceParams ) != LORAMAC_HANDLER_SUCCESS ) { return LORAMAC_HANDLER_ERROR; } if( LmhpPackagesRegistrationInit( ( Version_t * )&fwVersion ) != LORAMAC_HANDLER_SUCCESS ) { return LORAMAC_HANDLER_ERROR; } return LORAMAC_HANDLER_SUCCESS; } LmHandlerErrorStatus_t LmHandlerDeInit( void ) { if( LoRaMacDeInitialization() == LORAMAC_STATUS_OK ) { LmHandlerCallbacks = NULL; memset1( ( uint8_t * )&LoRaMacPrimitives, 0, sizeof( LoRaMacPrimitives_t ) ); memset1( ( uint8_t * )&LoRaMacCallbacks, 0, sizeof( LoRaMacCallback_t ) ); return LORAMAC_HANDLER_SUCCESS; } else { return LORAMAC_HANDLER_BUSY_ERROR; } } LmHandlerErrorStatus_t LmHandlerConfigure( LmHandlerParams_t *handlerParams ) { MibRequestConfirm_t mibReq; LoraInfo_t *loraInfo; memcpy1( ( void * )&LmHandlerParams, ( const void * )handlerParams, sizeof( LmHandlerParams_t ) ); #if ( LORAMAC_CLASSB_ENABLED == 1 ) IsClassBSwitchPending = false; #endif /* LORAMAC_CLASSB_ENABLED == 1 */ #if (defined( LORAMAC_VERSION ) && (( LORAMAC_VERSION == 0x01000400 ) || ( LORAMAC_VERSION == 0x01010100 ))) IsUplinkTxPending = false; #endif /* LORAMAC_VERSION */ loraInfo = LoraInfo_GetPtr(); if( 0U == ( ( 1 << ( LmHandlerParams.ActiveRegion ) ) & ( loraInfo->Region ) ) ) { MW_LOG( TS_ON, VLEVEL_ALWAYS, "error: Region is not defined in the MW: set lorawan_conf.h accordingly\r\n" ); while( 1 ) {} /* error: Region is not defined in the MW */ } if( LoRaMacInitialization( &LoRaMacPrimitives, &LoRaMacCallbacks, LmHandlerParams.ActiveRegion ) != LORAMAC_STATUS_OK ) { return LORAMAC_HANDLER_ERROR; } #if (defined( CONTEXT_MANAGEMENT_ENABLED ) && ( CONTEXT_MANAGEMENT_ENABLED == 1 )) /* Try the restore context from the Backup RAM structure if data retention is available */ mibReq.Type = MIB_NVM_CTXS; if( LoRaMacMibSetRequestConfirm( &mibReq ) == LORAMAC_STATUS_OK ) { CtxRestoreDone = true; } else { /* Restore context data backup from user callback (stored in FLASH) */ mibReq.Type = MIB_NVM_BKP_CTXS; if( LmHandlerCallbacks->OnRestoreContextRequest != NULL ) { LoRaMacMibGetRequestConfirm( &mibReq ); LmHandlerCallbacks->OnRestoreContextRequest( mibReq.Param.BackupContexts, sizeof( LoRaMacNvmData_t ) ); } /* Restore context data from backup to main nvm structure */ mibReq.Type = MIB_NVM_CTXS; if( LoRaMacMibSetRequestConfirm( &mibReq ) == LORAMAC_STATUS_OK ) { mibReq.Type = MIB_NETWORK_ACTIVATION; LoRaMacMibGetRequestConfirm( &mibReq ); if( mibReq.Param.NetworkActivation != ACTIVATION_TYPE_NONE ) { CtxRestoreDone = true; } } } if( CtxRestoreDone == true ) { if( LmHandlerCallbacks->OnNvmDataChange != NULL ) { LmHandlerCallbacks->OnNvmDataChange( LORAMAC_HANDLER_NVM_RESTORE ); } //BZ #156695 if(( LmHandlerJoinStatus() == LORAMAC_HANDLER_SET) && LoRaMacIsStopped()) { LoRaMacStart(); } mibReq.Type = MIB_NVM_CTXS; LoRaMacMibGetRequestConfirm( &mibReq ); LoRaMacNvmData_t *current_nvm = mibReq.Param.Contexts; LmHandlerParams.ActiveRegion = current_nvm->MacGroup2.Region; LmHandlerParams.DefaultClass = current_nvm->MacGroup2.DeviceClass; LmHandlerParams.AdrEnable = current_nvm->MacGroup2.AdrCtrlOn; } else #endif /* CONTEXT_MANAGEMENT_ENABLED == 1 */ { mibReq.Type = MIB_NET_ID; mibReq.Param.NetID = LORAWAN_NETWORK_ID; LoRaMacMibSetRequestConfirm( &mibReq ); } /* Restore ID struct from NVM or Init from callbacks */ if( SecureElementInitMcuID( LoRaMacCallbacks.GetUniqueId, LoRaMacCallbacks.GetDevAddress ) != SECURE_ELEMENT_SUCCESS ) { return LORAMAC_HANDLER_ERROR; } /* Read secure-element DEV_EUI, JOIN_EUI and DEV_ADDR values. */ mibReq.Type = MIB_DEV_ADDR; LoRaMacMibGetRequestConfirm( &mibReq ); CommissioningParams.DevAddr = mibReq.Param.DevAddr; /* Override DevAddress value after init from callbacks */ LoRaMacMibSetRequestConfirm( &mibReq ); mibReq.Type = MIB_DEV_EUI; mibReq.Param.DevEui = CommissioningParams.DevEui; LoRaMacMibGetRequestConfirm( &mibReq ); mibReq.Type = MIB_JOIN_EUI; mibReq.Param.JoinEui = CommissioningParams.JoinEui; LoRaMacMibGetRequestConfirm( &mibReq ); SecureElementPrintKeys(); #if (defined (LORAWAN_KMS) && (LORAWAN_KMS == 1)) MW_LOG( TS_OFF, VLEVEL_L, "###### KMS ENABLED \r\n" ); #endif /* LORAWAN_KMS == 1 */ mibReq.Type = MIB_PUBLIC_NETWORK; mibReq.Param.EnablePublicNetwork = LORAWAN_PUBLIC_NETWORK; LoRaMacMibSetRequestConfirm( &mibReq ); mibReq.Type = MIB_REPEATER_SUPPORT; mibReq.Param.EnableRepeaterSupport = LORAWAN_REPEATER_SUPPORT; LoRaMacMibSetRequestConfirm( &mibReq ); mibReq.Type = MIB_ADR; mibReq.Param.AdrEnable = LmHandlerParams.AdrEnable; LoRaMacMibSetRequestConfirm( &mibReq ); mibReq.Type = MIB_RXB_C_TIMEOUT; mibReq.Param.RxBCTimeout = LmHandlerParams.RxBCTimeout; LoRaMacMibSetRequestConfirm( &mibReq ); GetPhyParams_t getPhy; PhyParam_t phyParam; getPhy.Attribute = PHY_DUTY_CYCLE; phyParam = RegionGetPhyParam( LmHandlerParams.ActiveRegion, &getPhy ); LmHandlerParams.DutyCycleEnabled = ( bool ) phyParam.Value; /* Set system maximum tolerated rx error in milliseconds */ LmHandlerSetSystemMaxRxError( 20 ); /* override previous value if reconfigure new region */ LoRaMacTestSetDutyCycleOn( LmHandlerParams.DutyCycleEnabled ); return LORAMAC_HANDLER_SUCCESS; } bool LmHandlerIsBusy( void ) { if( LoRaMacIsBusy( ) == true ) { return true; } if( LmHandlerJoinStatus( ) != LORAMAC_HANDLER_SET ) { /* The network isn't yet joined, try again later. */ LmHandlerJoin( JoinParams.Mode, JoinParams.forceRejoin ); return true; } #if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) if( LmHandlerPackages[PACKAGE_ID_COMPLIANCE]->IsRunning( ) == true ) { return true; } #endif /* LORAMAC_VERSION */ if( LmHandlerPackageIsTxPending( ) == true ) { return true; } return false; } void LmHandlerProcess( void ) { /* Call at first the LoRaMAC process before to run all package process features */ /* Processes the LoRaMac events */ LoRaMacProcess( ); /* Call all packages process functions */ LmHandlerPackagesProcess( ); /* Check if a package transmission is pending. */ /* If it is the case exit function earlier */ if( LmHandlerPackageIsTxPending( ) == true ) { return; } #if (defined( LORAMAC_VERSION ) && (( LORAMAC_VERSION == 0x01000400 ) || ( LORAMAC_VERSION == 0x01010100 ))) /* If a MAC layer scheduled uplink is still pending try to send it. */ if( IsUplinkTxPending == true ) { /* Send an empty message */ LmHandlerAppData_t appData = { .Buffer = NULL, .BufferSize = 0, .Port = 0, }; if( LmHandlerSend( &appData, LmHandlerParams.IsTxConfirmed, false ) == LORAMAC_HANDLER_SUCCESS ) { IsUplinkTxPending = false; } } #endif /* LORAMAC_VERSION */ } TimerTime_t LmHandlerGetDutyCycleWaitTime( void ) { return DutyCycleWaitTime; } LmHandlerErrorStatus_t LmHandlerJoin( ActivationType_t mode, bool forceRejoin ) { LmHandlerErrorStatus_t lmhStatus = LORAMAC_HANDLER_ERROR; LoRaMacStatus_t status; MlmeReq_t mlmeReq; mlmeReq.Type = MLME_JOIN; mlmeReq.Req.Join.Datarate = LmHandlerParams.TxDatarate; mlmeReq.Req.Join.TxPower = LmHandlerParams.TxPower; if( mode == ACTIVATION_TYPE_OTAA ) { mlmeReq.Req.Join.NetworkActivation = ACTIVATION_TYPE_OTAA; JoinParams.Mode = ACTIVATION_TYPE_OTAA; JoinParams.forceRejoin = forceRejoin; LoRaMacStart(); #if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) /* Starts the OTAA join procedure */ status = LoRaMacMlmeRequest( &mlmeReq ); if( status == LORAMAC_STATUS_OK ) { lmhStatus = LORAMAC_STATUS_OK; } #endif /* LORAMAC_VERSION */ } else { MibRequestConfirm_t mibReq; mlmeReq.Req.Join.NetworkActivation = ACTIVATION_TYPE_ABP; JoinParams.Mode = ACTIVATION_TYPE_ABP; JoinParams.Datarate = LmHandlerParams.TxDatarate; JoinParams.TxPower = LmHandlerParams.TxPower; JoinParams.Status = LORAMAC_HANDLER_SUCCESS; JoinParams.forceRejoin = forceRejoin; if( CtxRestoreDone == false ) { /* Configure the default datarate */ mibReq.Type = MIB_CHANNELS_DEFAULT_DATARATE; mibReq.Param.ChannelsDefaultDatarate = LmHandlerParams.TxDatarate; LoRaMacMibSetRequestConfirm( &mibReq ); mibReq.Type = MIB_CHANNELS_DATARATE; mibReq.Param.ChannelsDatarate = LmHandlerParams.TxDatarate; LoRaMacMibSetRequestConfirm( &mibReq ); /* Configure the default Tx Power */ mibReq.Type = MIB_CHANNELS_DEFAULT_TX_POWER; mibReq.Param.ChannelsDefaultTxPower = LmHandlerParams.TxPower; LoRaMacMibSetRequestConfirm( &mibReq ); mibReq.Type = MIB_CHANNELS_TX_POWER; mibReq.Param.ChannelsTxPower = LmHandlerParams.TxPower; LoRaMacMibSetRequestConfirm( &mibReq ); /* Tell the MAC layer which network server version are we connecting too. */ mibReq.Type = MIB_ABP_LORAWAN_VERSION; mibReq.Param.AbpLrWanVersion.Value = ABP_ACTIVATION_LRWAN_VERSION; LoRaMacMibSetRequestConfirm( &mibReq ); } LoRaMacStart(); mibReq.Type = MIB_NETWORK_ACTIVATION; mibReq.Param.NetworkActivation = ACTIVATION_TYPE_ABP; LoRaMacMibSetRequestConfirm( &mibReq ); #if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) /* Notify upper layer */ if( LmHandlerCallbacks->OnJoinRequest != NULL ) { LmHandlerCallbacks->OnJoinRequest( &JoinParams ); } lmhStatus = LmHandlerRequestClass( LmHandlerParams.DefaultClass ); #endif /* LORAMAC_VERSION */ } #if (defined( LORAMAC_VERSION ) && (( LORAMAC_VERSION == 0x01000400 ) || ( LORAMAC_VERSION == 0x01010100 ))) if( ( CtxRestoreDone == false ) || ( forceRejoin == true ) ) { /* Starts the join procedure */ status = LoRaMacMlmeRequest( &mlmeReq ); if( status == LORAMAC_STATUS_OK ) { lmhStatus = LORAMAC_STATUS_OK; } } DutyCycleWaitTime = mlmeReq.ReqReturn.DutyCycleWaitTime; #endif /* LORAMAC_VERSION */ return lmhStatus; } LmHandlerFlagStatus_t LmHandlerJoinStatus( void ) { MibRequestConfirm_t mibReq; LoRaMacStatus_t status; mibReq.Type = MIB_NETWORK_ACTIVATION; status = LoRaMacMibGetRequestConfirm( &mibReq ); if( status == LORAMAC_STATUS_OK ) { if( mibReq.Param.NetworkActivation == ACTIVATION_TYPE_NONE ) { return LORAMAC_HANDLER_RESET; } else { return LORAMAC_HANDLER_SET; } } else { return LORAMAC_HANDLER_RESET; } } LmHandlerErrorStatus_t LmHandlerSend( LmHandlerAppData_t *appData, LmHandlerMsgTypes_t isTxConfirmed, bool allowDelayedTx ) { LoRaMacStatus_t status; LmHandlerErrorStatus_t lmhStatus = LORAMAC_HANDLER_ERROR; McpsReq_t mcpsReq; LoRaMacTxInfo_t txInfo; if( LoRaMacIsBusy() == true ) { return LORAMAC_HANDLER_BUSY_ERROR; } if( LoRaMacIsStopped() == true ) { return LORAMAC_HANDLER_NO_NETWORK_JOINED; } if( LmHandlerJoinStatus( ) != LORAMAC_HANDLER_SET ) { /* The network isn't joined, try again. */ LmHandlerJoin( JoinParams.Mode, JoinParams.forceRejoin ); return LORAMAC_HANDLER_NO_NETWORK_JOINED; } #if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) if( ( LmHandlerPackages[PACKAGE_ID_COMPLIANCE]->IsRunning( ) == true ) && ( appData->Port != LmHandlerPackages[PACKAGE_ID_COMPLIANCE]->Port ) && ( appData->Port != 0 ) ) { return LORAMAC_HANDLER_COMPLIANCE_RUNNING; } #endif /* LORAMAC_VERSION */ TxParams.MsgType = isTxConfirmed; mcpsReq.Type = ( isTxConfirmed == LORAMAC_HANDLER_UNCONFIRMED_MSG ) ? MCPS_UNCONFIRMED : MCPS_CONFIRMED; mcpsReq.Req.Unconfirmed.Datarate = LmHandlerParams.TxDatarate; if( LoRaMacQueryTxPossible( appData->BufferSize, &txInfo ) != LORAMAC_STATUS_OK ) { /* Send empty frame in order to flush MAC commands */ mcpsReq.Type = MCPS_UNCONFIRMED; mcpsReq.Req.Unconfirmed.fBuffer = NULL; mcpsReq.Req.Unconfirmed.fBufferSize = 0; lmhStatus = LORAMAC_HANDLER_PAYLOAD_LENGTH_RESTRICTED; } else { mcpsReq.Req.Unconfirmed.fPort = appData->Port; mcpsReq.Req.Unconfirmed.fBufferSize = appData->BufferSize; mcpsReq.Req.Unconfirmed.fBuffer = appData->Buffer; } TxParams.AppData = *appData; TxParams.Datarate = LmHandlerParams.TxDatarate; status = LoRaMacMcpsRequest( &mcpsReq, allowDelayedTx ); DutyCycleWaitTime = mcpsReq.ReqReturn.DutyCycleWaitTime; switch( status ) { case LORAMAC_STATUS_OK: #if (defined( LORAMAC_VERSION ) && (( LORAMAC_VERSION == 0x01000400 ) || ( LORAMAC_VERSION == 0x01010100 ))) IsUplinkTxPending = false; #endif /* LORAMAC_VERSION */ if( lmhStatus != LORAMAC_HANDLER_PAYLOAD_LENGTH_RESTRICTED ) { lmhStatus = LORAMAC_HANDLER_SUCCESS; } break; case LORAMAC_STATUS_BUSY: case LORAMAC_STATUS_BUSY_UPLINK_COLLISION: case LORAMAC_STATUS_BUSY_BEACON_RESERVED_TIME: case LORAMAC_STATUS_BUSY_PING_SLOT_WINDOW_TIME: lmhStatus = LORAMAC_HANDLER_BUSY_ERROR; break; case LORAMAC_STATUS_NO_NETWORK_JOINED: lmhStatus = LORAMAC_HANDLER_NO_NETWORK_JOINED; break; case LORAMAC_STATUS_CRYPTO_ERROR: lmhStatus = LORAMAC_HANDLER_CRYPTO_ERROR; break; case LORAMAC_STATUS_DUTYCYCLE_RESTRICTED: lmhStatus = LORAMAC_HANDLER_DUTYCYCLE_RESTRICTED; break; case LORAMAC_STATUS_SERVICE_UNKNOWN: case LORAMAC_STATUS_PARAMETER_INVALID: case LORAMAC_STATUS_MAC_COMMAD_ERROR: case LORAMAC_STATUS_FCNT_HANDLER_ERROR: case LORAMAC_STATUS_REGION_NOT_SUPPORTED: case LORAMAC_STATUS_NO_FREE_CHANNEL_FOUND: case LORAMAC_STATUS_NO_CHANNEL_FOUND: case LORAMAC_STATUS_LENGTH_ERROR: default: lmhStatus = LORAMAC_HANDLER_ERROR; break; } return lmhStatus; } LmHandlerErrorStatus_t LmHandlerDeviceTimeReq( void ) { LoRaMacStatus_t status; MlmeReq_t mlmeReq; mlmeReq.Type = MLME_DEVICE_TIME; status = LoRaMacMlmeRequest( &mlmeReq ); DutyCycleWaitTime = mlmeReq.ReqReturn.DutyCycleWaitTime; if( status == LORAMAC_STATUS_OK ) { return LORAMAC_HANDLER_SUCCESS; } else { return LORAMAC_HANDLER_ERROR; } } #if ( LORAMAC_CLASSB_ENABLED == 1 ) static LmHandlerErrorStatus_t LmHandlerBeaconReq( void ) { LoRaMacStatus_t status; MlmeReq_t mlmeReq; mlmeReq.Type = MLME_BEACON_ACQUISITION; status = LoRaMacMlmeRequest( &mlmeReq ); DutyCycleWaitTime = mlmeReq.ReqReturn.DutyCycleWaitTime; if( status == LORAMAC_STATUS_OK ) { return LORAMAC_HANDLER_SUCCESS; } else { return LORAMAC_HANDLER_ERROR; } } #endif /* LORAMAC_CLASSB_ENABLED == 1 */ LmHandlerErrorStatus_t LmHandlerPingSlotReq( uint8_t periodicity ) { #if ( LORAMAC_CLASSB_ENABLED == 1 ) LoRaMacStatus_t status; MlmeReq_t mlmeReq; mlmeReq.Type = MLME_PING_SLOT_INFO; mlmeReq.Req.PingSlotInfo.PingSlot.Fields.Periodicity = periodicity; mlmeReq.Req.PingSlotInfo.PingSlot.Fields.RFU = 0; status = LoRaMacMlmeRequest( &mlmeReq ); DutyCycleWaitTime = mlmeReq.ReqReturn.DutyCycleWaitTime; if( status == LORAMAC_STATUS_OK ) { LmHandlerParams.PingSlotPeriodicity = periodicity; /* Send an empty message */ LmHandlerAppData_t appData = { .Buffer = NULL, .BufferSize = 0, .Port = 0, }; return LmHandlerSend( &appData, LmHandlerParams.IsTxConfirmed, false ); } else #endif /* LORAMAC_CLASSB_ENABLED == 1 */ { return LORAMAC_HANDLER_ERROR; } } LmHandlerErrorStatus_t LmHandlerRequestClass( DeviceClass_t newClass ) { MibRequestConfirm_t mibReq; DeviceClass_t currentClass; LmHandlerErrorStatus_t errorStatus = LORAMAC_HANDLER_SUCCESS; if( LoRaMacIsBusy() == true ) { return LORAMAC_HANDLER_BUSY_ERROR; } if( LmHandlerJoinStatus() != LORAMAC_HANDLER_SET ) { return LORAMAC_HANDLER_NO_NETWORK_JOINED; } mibReq.Type = MIB_DEVICE_CLASS; if( LoRaMacMibGetRequestConfirm( &mibReq ) != LORAMAC_STATUS_OK ) { return LORAMAC_HANDLER_ERROR; } currentClass = mibReq.Param.Class; /* Attempt to switch only if class update */ if( currentClass != newClass ) { switch( newClass ) { case CLASS_A: { if( currentClass != CLASS_A ) { mibReq.Param.Class = newClass; if( LoRaMacMibSetRequestConfirm( &mibReq ) == LORAMAC_STATUS_OK ) { /* Switch is instantaneous */ if( LmHandlerCallbacks->OnClassChange != NULL ) { LmHandlerCallbacks->OnClassChange( newClass ); } } else { errorStatus = LORAMAC_HANDLER_ERROR; } } } break; case CLASS_B: { #if ( LORAMAC_CLASSB_ENABLED == 1 ) if( currentClass != CLASS_A ) { errorStatus = LORAMAC_HANDLER_ERROR; } else { /* Beacon must first be acquired */ errorStatus = LmHandlerDeviceTimeReq( ); IsClassBSwitchPending = true; } #else /* LORAMAC_CLASSB_ENABLED == 0 */ errorStatus = LORAMAC_HANDLER_ERROR; #endif /* LORAMAC_CLASSB_ENABLED */ } break; case CLASS_C: { if( currentClass != CLASS_A ) { errorStatus = LORAMAC_HANDLER_ERROR; } else { /* Switch is instantaneous */ mibReq.Param.Class = newClass; if( LoRaMacMibSetRequestConfirm( &mibReq ) == LORAMAC_STATUS_OK ) { if( LmHandlerCallbacks->OnClassChange != NULL ) { LmHandlerCallbacks->OnClassChange( newClass ); } } else { errorStatus = LORAMAC_HANDLER_ERROR; } } } break; default: break; } } return errorStatus; } LmHandlerErrorStatus_t LmHandlerGetCurrentClass( DeviceClass_t *deviceClass ) { MibRequestConfirm_t mibReq; if( deviceClass == NULL ) { return LORAMAC_HANDLER_ERROR; } mibReq.Type = MIB_DEVICE_CLASS; if( LoRaMacMibGetRequestConfirm( &mibReq ) != LORAMAC_STATUS_OK ) { return LORAMAC_HANDLER_ERROR; } *deviceClass = mibReq.Param.Class; return LORAMAC_HANDLER_SUCCESS; } LmHandlerErrorStatus_t LmHandlerGetTxDatarate( int8_t *txDatarate ) { MibRequestConfirm_t mibGet; if( txDatarate == NULL ) { return LORAMAC_HANDLER_ERROR; } mibGet.Type = MIB_CHANNELS_DATARATE; if( LoRaMacMibGetRequestConfirm( &mibGet ) != LORAMAC_STATUS_OK ) { return LORAMAC_HANDLER_ERROR; } *txDatarate = mibGet.Param.ChannelsDatarate; LmHandlerParams.TxDatarate = *txDatarate; return LORAMAC_HANDLER_SUCCESS; } LmHandlerErrorStatus_t LmHandlerGetActiveRegion( LoRaMacRegion_t *region ) { if( region == NULL ) { return LORAMAC_HANDLER_ERROR; } *region = LmHandlerParams.ActiveRegion; return LORAMAC_HANDLER_SUCCESS; } LmHandlerErrorStatus_t LmHandlerSetSystemMaxRxError( uint32_t maxErrorInMs ) { MibRequestConfirm_t mibReq; mibReq.Type = MIB_SYSTEM_MAX_RX_ERROR; mibReq.Param.SystemMaxRxError = maxErrorInMs; if( LoRaMacMibSetRequestConfirm( &mibReq ) != LORAMAC_STATUS_OK ) { return LORAMAC_HANDLER_ERROR; } return LORAMAC_HANDLER_SUCCESS; } /* *============================================================================= * LORAMAC NOTIFICATIONS HANDLING *============================================================================= */ static void McpsConfirm( McpsConfirm_t *mcpsConfirm ) { TxParams.IsMcpsConfirm = 1; TxParams.Status = mcpsConfirm->Status; TxParams.Datarate = mcpsConfirm->Datarate; TxParams.UplinkCounter = mcpsConfirm->UpLinkCounter; TxParams.TxPower = mcpsConfirm->TxPower; TxParams.Channel = mcpsConfirm->Channel; TxParams.AckReceived = mcpsConfirm->AckReceived; if( LmHandlerCallbacks->OnTxData != NULL ) { LmHandlerCallbacks->OnTxData( &TxParams ); } LmHandlerPackagesNotify( PACKAGE_MCPS_CONFIRM, mcpsConfirm ); } static void McpsIndication( McpsIndication_t *mcpsIndication, LoRaMacRxStatus_t *rxStatus ) { LmHandlerAppData_t appData; DeviceClass_t deviceClass = CLASS_A; RxParams.IsMcpsIndication = 1; RxParams.Status = mcpsIndication->Status; if( RxParams.Status != LORAMAC_EVENT_INFO_STATUS_OK ) { return; } RxParams.Datarate = mcpsIndication->RxDatarate; RxParams.Rssi = rxStatus->Rssi; RxParams.Snr = rxStatus->Snr; RxParams.RxSlot = rxStatus->RxSlot; RxParams.DownlinkCounter = mcpsIndication->DownLinkCounter; appData.Port = mcpsIndication->Port; appData.BufferSize = mcpsIndication->BufferSize; appData.Buffer = mcpsIndication->Buffer; if( LmHandlerCallbacks->OnRxData != NULL ) { LmHandlerCallbacks->OnRxData( &appData, &RxParams ); } if( ( LmHandlerCallbacks->OnSysTimeUpdate != NULL ) && ( mcpsIndication->DeviceTimeAnsReceived == true ) ) { LmHandlerCallbacks->OnSysTimeUpdate( ); } /* Call packages RxProcess function */ LmHandlerPackagesNotify( PACKAGE_MCPS_INDICATION, mcpsIndication ); LmHandlerGetCurrentClass( &deviceClass ); #if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) if( mcpsIndication->IsUplinkTxPending != 0 ) { /* The server signals that it has pending data to be sent. */ /* We schedule an uplink as soon as possible to flush the server. */ /* Send an empty message */ LmHandlerAppData_t appData = { .Buffer = NULL, .BufferSize = 0, .Port = 0 }; LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG, true ); } #elif (defined( LORAMAC_VERSION ) && (( LORAMAC_VERSION == 0x01000400 ) || ( LORAMAC_VERSION == 0x01010100 ))) if( mcpsIndication->IsUplinkTxPending != 0 ) { /* The server signals that it has pending data to be sent. */ /* We schedule an uplink as soon as possible to flush the server. */ IsUplinkTxPending = true; } #endif /* LORAMAC_VERSION */ } static void MlmeConfirm( MlmeConfirm_t *mlmeConfirm ) { TxParams.IsMcpsConfirm = 0; TxParams.Status = mlmeConfirm->Status; if( LmHandlerCallbacks->OnTxData != NULL ) { LmHandlerCallbacks->OnTxData( &TxParams ); } LmHandlerPackagesNotify( PACKAGE_MLME_CONFIRM, mlmeConfirm ); switch( mlmeConfirm->MlmeRequest ) { case MLME_JOIN: { MibRequestConfirm_t mibReq; mibReq.Type = MIB_DEV_ADDR; LoRaMacMibGetRequestConfirm( &mibReq ); if( SecureElementSetDevAddr( JoinParams.Mode, mibReq.Param.DevAddr ) == SECURE_ELEMENT_SUCCESS ) { CommissioningParams.DevAddr = mibReq.Param.DevAddr; } LmHandlerGetTxDatarate( &JoinParams.Datarate ); LmHandlerGetTxPower( &JoinParams.TxPower ); if( TxParams.Status == LORAMAC_EVENT_INFO_STATUS_OK ) { /* Status is OK, node has joined the network */ JoinParams.Status = LORAMAC_HANDLER_SUCCESS; LmHandlerRequestClass( LmHandlerParams.DefaultClass ); } else { /* Join was not successful. Try to join again */ JoinParams.Status = LORAMAC_HANDLER_ERROR; } /* Notify upper layer */ if( LmHandlerCallbacks->OnJoinRequest != NULL ) { LmHandlerCallbacks->OnJoinRequest( &JoinParams ); } if( TxParams.Status == LORAMAC_EVENT_INFO_STATUS_OK ) { SecureElementPrintSessionKeys( JoinParams.Mode ); } } break; case MLME_LINK_CHECK: { RxParams.LinkCheck = true; RxParams.DemodMargin = mlmeConfirm->DemodMargin; RxParams.NbGateways = mlmeConfirm->NbGateways; } break; case MLME_DEVICE_TIME: { #if ( LORAMAC_CLASSB_ENABLED == 1 ) if( IsClassBSwitchPending == true ) { LmHandlerBeaconReq( ); } #endif /* LORAMAC_CLASSB_ENABLED == 1 */ } break; case MLME_BEACON_ACQUISITION: { if( mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK ) { /* Beacon has been acquired */ /* Request server for ping slot */ LmHandlerPingSlotReq( LmHandlerParams.PingSlotPeriodicity ); } else { /* Beacon not acquired */ /* Request Device Time again. */ LmHandlerDeviceTimeReq( ); } } break; case MLME_PING_SLOT_INFO: { #if ( LORAMAC_CLASSB_ENABLED == 1 ) if( mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK ) { MibRequestConfirm_t mibReq; /* Class B is now activated */ mibReq.Type = MIB_DEVICE_CLASS; mibReq.Param.Class = CLASS_B; LoRaMacMibSetRequestConfirm( &mibReq ); /* Notify upper layer */ if( LmHandlerCallbacks->OnClassChange != NULL ) { LmHandlerCallbacks->OnClassChange( CLASS_B ); } IsClassBSwitchPending = false; } else { LmHandlerPingSlotReq( LmHandlerParams.PingSlotPeriodicity ); } #endif /* LORAMAC_CLASSB_ENABLED == 1 */ } break; default: break; } } static void MlmeIndication( MlmeIndication_t *mlmeIndication, LoRaMacRxStatus_t *rxStatus ) { RxParams.IsMcpsIndication = 0; RxParams.Status = mlmeIndication->Status; RxParams.Datarate = mlmeIndication->RxDatarate; RxParams.Rssi = rxStatus->Rssi; RxParams.Snr = rxStatus->Snr; RxParams.RxSlot = rxStatus->RxSlot; RxParams.DownlinkCounter = mlmeIndication->DownLinkCounter; if( ( LmHandlerCallbacks->OnRxData != NULL ) && ( mlmeIndication->MlmeIndication != MLME_BEACON ) && ( mlmeIndication->MlmeIndication != MLME_BEACON_LOST ) ) { LmHandlerCallbacks->OnRxData( NULL, &RxParams ); } /* Call packages RxProcess function */ LmHandlerPackagesNotify( PACKAGE_MLME_INDICATION, mlmeIndication ); switch( mlmeIndication->MlmeIndication ) { case MLME_BEACON_LOST: { #if ( LORAMAC_CLASSB_ENABLED == 1 ) MibRequestConfirm_t mibReq; /* Switch to class A again */ mibReq.Type = MIB_DEVICE_CLASS; mibReq.Param.Class = CLASS_A; LoRaMacMibSetRequestConfirm( &mibReq ); BeaconParams.State = LORAMAC_HANDLER_BEACON_LOST; BeaconParams.Info.Time.Seconds = 0; BeaconParams.Info.GwSpecific.InfoDesc = 0; memset1( BeaconParams.Info.GwSpecific.Info, 0, 6 ); if( LmHandlerCallbacks->OnClassChange != NULL ) { LmHandlerCallbacks->OnClassChange( CLASS_A ); } if( LmHandlerCallbacks->OnBeaconStatusChange != NULL ) { LmHandlerCallbacks->OnBeaconStatusChange( &BeaconParams ); } LmHandlerDeviceTimeReq( ); #endif /* LORAMAC_CLASSB_ENABLED == 1 */ } break; case MLME_BEACON: { #if ( LORAMAC_CLASSB_ENABLED == 1 ) if( mlmeIndication->Status == LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED ) { BeaconParams.State = LORAMAC_HANDLER_BEACON_RX; BeaconParams.Info = mlmeIndication->BeaconInfo; if( LmHandlerCallbacks->OnBeaconStatusChange != NULL ) { LmHandlerCallbacks->OnBeaconStatusChange( &BeaconParams ); } } else if( mlmeIndication->Status == LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND ) { BeaconParams.State = LORAMAC_HANDLER_BEACON_NRX; BeaconParams.Info = mlmeIndication->BeaconInfo; if( LmHandlerCallbacks->OnBeaconStatusChange != NULL ) { LmHandlerCallbacks->OnBeaconStatusChange( &BeaconParams ); } } #endif /* LORAMAC_CLASSB_ENABLED == 1 */ } break; default: break; } } /* *============================================================================= * PACKAGES HANDLING *============================================================================= */ LmHandlerErrorStatus_t LmHandlerPackageRegister( uint8_t id, void *params ) { LmhPackage_t *package = NULL; switch( id ) { case PACKAGE_ID_COMPLIANCE: { package = LmhpCompliancePackageFactory( ); break; } default: { if( LORAMAC_HANDLER_SUCCESS != LmhpPackagesRegister( id, &package ) ) { return LORAMAC_HANDLER_ERROR; } break; } } if( package != NULL ) { LmHandlerPackages[id] = package; LmHandlerPackages[id]->OnJoinRequest = LmHandlerJoin; #if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) LmHandlerPackages[id]->OnSendRequest = LmHandlerSend; #elif (defined( LORAMAC_VERSION ) && (( LORAMAC_VERSION == 0x01000400 ) || ( LORAMAC_VERSION == 0x01010100 ))) LmHandlerPackages[id]->OnSysTimeUpdate = LmHandlerCallbacks->OnSysTimeUpdate; LmHandlerPackages[id]->OnSystemReset = LmHandlerCallbacks->OnSystemReset; #endif /* LORAMAC_VERSION */ LmHandlerPackages[id]->OnDeviceTimeRequest = LmHandlerDeviceTimeReq; LmHandlerPackages[id]->OnPackageProcessEvent = LmHandlerCallbacks->OnMacProcess; LmHandlerPackages[id]->Init( params, AppData.Buffer, AppData.BufferSize ); return LORAMAC_HANDLER_SUCCESS; } else { return LORAMAC_HANDLER_ERROR; } } static bool LmHandlerPackageIsInitialized( uint8_t id ) { if( ( id < PKG_MAX_NUMBER ) && ( LmHandlerPackages[id]->IsInitialized != NULL ) ) { return LmHandlerPackages[id]->IsInitialized( ); } else { return false; } } static void LmHandlerPackagesNotify( PackageNotifyTypes_t notifyType, void *params ) { for( int8_t i = 0; i < PKG_MAX_NUMBER; i++ ) { if( LmHandlerPackages[i] != NULL ) { switch( notifyType ) { case PACKAGE_MCPS_CONFIRM: { if( LmHandlerPackages[i]->OnMcpsConfirmProcess != NULL ) { LmHandlerPackages[i]->OnMcpsConfirmProcess( ( McpsConfirm_t * ) params ); } break; } case PACKAGE_MCPS_INDICATION: { #if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) if( ( LmHandlerPackages[i]->OnMcpsIndicationProcess != NULL ) && ( ( LmHandlerPackages[i]->Port == ( ( McpsIndication_t * )params )->Port ) || ( ( i == PACKAGE_ID_COMPLIANCE ) && ( LmHandlerPackages[PACKAGE_ID_COMPLIANCE]->IsRunning() ) ) ) ) #elif (defined( LORAMAC_VERSION ) && (( LORAMAC_VERSION == 0x01000400 ) || ( LORAMAC_VERSION == 0x01010100 ))) if( LmHandlerPackages[i]->OnMcpsIndicationProcess != NULL ) #endif /* LORAMAC_VERSION */ { LmHandlerPackages[i]->OnMcpsIndicationProcess( ( McpsIndication_t * )params ); } break; } case PACKAGE_MLME_CONFIRM: { if( LmHandlerPackages[i]->OnMlmeConfirmProcess != NULL ) { LmHandlerPackages[i]->OnMlmeConfirmProcess( ( MlmeConfirm_t * )params ); } break; } case PACKAGE_MLME_INDICATION: { if( LmHandlerPackages[i]->OnMlmeIndicationProcess != NULL ) { LmHandlerPackages[i]->OnMlmeIndicationProcess( params ); } break; } default: { break; } } } } } static bool LmHandlerPackageIsTxPending( void ) { for( int8_t i = 0; i < PKG_MAX_NUMBER; i++ ) { #if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) if( ( LmHandlerPackages[i] != NULL ) && ( i != PACKAGE_ID_COMPLIANCE ) ) #else if( LmHandlerPackages[i] != NULL ) #endif /* LORAMAC_VERSION */ { if( LmHandlerPackages[i]->IsTxPending( ) == true ) { return true; } } } return false; } static void LmHandlerPackagesProcess( void ) { for( int8_t i = 0; i < PKG_MAX_NUMBER; i++ ) { if( ( LmHandlerPackages[i] != NULL ) && ( LmHandlerPackages[i]->Process != NULL ) && ( LmHandlerPackageIsInitialized( i ) != false ) ) { LmHandlerPackages[i]->Process( ); } } } #if (defined( LORAMAC_VERSION ) && (( LORAMAC_VERSION == 0x01000400 ) || ( LORAMAC_VERSION == 0x01010100 ))) static void LmHandlerOnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ) { LmHandlerParams.IsTxConfirmed = isTxConfirmed; if (LmHandlerCallbacks->OnTxFrameCtrlChanged != NULL) { LmHandlerCallbacks->OnTxFrameCtrlChanged( isTxConfirmed ); } } static void LmHandlerOnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ) { LmHandlerParams.PingSlotPeriodicity = pingSlotPeriodicity; if (LmHandlerCallbacks->OnPingSlotPeriodicityChanged != NULL) { LmHandlerCallbacks->OnPingSlotPeriodicityChanged( pingSlotPeriodicity ); } } #endif /* LORAMAC_VERSION */ /* *============================================================================= * ST ADDITIONAL FUNCTIONS *============================================================================= */ LmHandlerErrorStatus_t LmHandlerGetVersion( LmHandlerVersionType_t lmhType, uint32_t *featureVersion ) { if( featureVersion == NULL ) { return LORAMAC_HANDLER_ERROR; } switch( lmhType ) { case LORAMAC_HANDLER_L2_VERSION: *featureVersion = LORAMAC_VERSION; break; case LORAMAC_HANDLER_REGION_VERSION: *featureVersion = REGION_VERSION; break; default: break; } return LORAMAC_HANDLER_SUCCESS; } LmHandlerErrorStatus_t LmHandlerStop( void ) { if( LoRaMacDeInitialization() == LORAMAC_STATUS_OK ) { return LORAMAC_HANDLER_SUCCESS; } else { return LORAMAC_HANDLER_BUSY_ERROR; } } LmHandlerErrorStatus_t LmHandlerHalt( void ) { if( LoRaMacHalt() == LORAMAC_STATUS_OK ) { return LORAMAC_HANDLER_SUCCESS; } else { return LORAMAC_HANDLER_BUSY_ERROR; } } LmHandlerErrorStatus_t LmHandlerLinkCheckReq( void ) { LoRaMacStatus_t status; MlmeReq_t mlmeReq; mlmeReq.Type = MLME_LINK_CHECK; status = LoRaMacMlmeRequest( &mlmeReq ); if( status == LORAMAC_STATUS_OK ) { return LORAMAC_HANDLER_SUCCESS; } else { return LORAMAC_HANDLER_ERROR; } } LmHandlerErrorStatus_t LmHandlerGetDevEUI( uint8_t *devEUI ) { MibRequestConfirm_t mibReq; if( devEUI == NULL ) { return LORAMAC_HANDLER_ERROR; } mibReq.Type = MIB_DEV_EUI; mibReq.Param.DevEui = devEUI; if( LoRaMacMibGetRequestConfirm( &mibReq ) != LORAMAC_STATUS_OK ) { return LORAMAC_HANDLER_ERROR; } return LORAMAC_HANDLER_SUCCESS; } LmHandlerErrorStatus_t LmHandlerSetDevEUI( uint8_t *devEUI ) { MibRequestConfirm_t mibReq; /* Not yet joined */ if( LmHandlerJoinStatus() != LORAMAC_HANDLER_SET ) { mibReq.Type = MIB_DEV_EUI; mibReq.Param.DevEui = devEUI; if( LoRaMacMibSetRequestConfirm( &mibReq ) != LORAMAC_STATUS_OK ) { return LORAMAC_HANDLER_ERROR; } return LORAMAC_HANDLER_SUCCESS; } else { /* Cannot change Keys in running state */ return LORAMAC_HANDLER_ERROR; } } LmHandlerErrorStatus_t LmHandlerGetAppEUI( uint8_t *appEUI ) { MibRequestConfirm_t mibReq; if( appEUI == NULL ) { return LORAMAC_HANDLER_ERROR; } mibReq.Type = MIB_JOIN_EUI; mibReq.Param.JoinEui = appEUI; if( LoRaMacMibGetRequestConfirm( &mibReq ) != LORAMAC_STATUS_OK ) { return LORAMAC_HANDLER_ERROR; } return LORAMAC_HANDLER_SUCCESS; } LmHandlerErrorStatus_t LmHandlerSetAppEUI( uint8_t *appEUI ) { MibRequestConfirm_t mibReq; /* Not yet joined */ if( LmHandlerJoinStatus() != LORAMAC_HANDLER_SET ) { mibReq.Type = MIB_JOIN_EUI; mibReq.Param.JoinEui = appEUI; if( LoRaMacMibSetRequestConfirm( &mibReq ) != LORAMAC_STATUS_OK ) { return LORAMAC_HANDLER_ERROR; } return LORAMAC_HANDLER_SUCCESS; } else { /* Cannot change Keys in running state */ return LORAMAC_HANDLER_ERROR; } } LmHandlerErrorStatus_t LmHandlerGetNetworkID( uint32_t *networkId ) { MibRequestConfirm_t mibReq; if( networkId == NULL ) { return LORAMAC_HANDLER_ERROR; } mibReq.Type = MIB_NET_ID; if( LoRaMacMibGetRequestConfirm( &mibReq ) != LORAMAC_STATUS_OK ) { return LORAMAC_HANDLER_ERROR; } *networkId = mibReq.Param.NetID; return LORAMAC_HANDLER_SUCCESS; } LmHandlerErrorStatus_t LmHandlerSetNetworkID( uint32_t networkId ) { MibRequestConfirm_t mibReq; /* Not yet joined */ if( LmHandlerJoinStatus() != LORAMAC_HANDLER_SET ) { mibReq.Type = MIB_NET_ID; mibReq.Param.NetID = networkId; if( LoRaMacMibSetRequestConfirm( &mibReq ) != LORAMAC_STATUS_OK ) { return LORAMAC_HANDLER_ERROR; } return LORAMAC_HANDLER_SUCCESS; } else { /* Cannot change NetworkID in running state */ return LORAMAC_HANDLER_ERROR; } } LmHandlerErrorStatus_t LmHandlerGetDevAddr( uint32_t *devAddr ) { MibRequestConfirm_t mibReq; if( devAddr == NULL ) { return LORAMAC_HANDLER_ERROR; } mibReq.Type = MIB_DEV_ADDR; if( LoRaMacMibGetRequestConfirm( &mibReq ) != LORAMAC_STATUS_OK ) { return LORAMAC_HANDLER_ERROR; } *devAddr = mibReq.Param.DevAddr; return LORAMAC_HANDLER_SUCCESS; } LmHandlerErrorStatus_t LmHandlerSetDevAddr( uint32_t devAddr ) { MibRequestConfirm_t mibReq; /* Not yet joined */ if( LmHandlerJoinStatus() != LORAMAC_HANDLER_SET ) { mibReq.Type = MIB_DEV_ADDR; mibReq.Param.DevAddr = devAddr; if( LoRaMacMibSetRequestConfirm( &mibReq ) != LORAMAC_STATUS_OK ) { return LORAMAC_HANDLER_ERROR; } return LORAMAC_HANDLER_SUCCESS; } else { /* Cannot change DevAddr in running state */ return LORAMAC_HANDLER_ERROR; } } LmHandlerErrorStatus_t LmHandlerSetActiveRegion( LoRaMacRegion_t region ) { /* Not yet joined */ if( LmHandlerJoinStatus() != LORAMAC_HANDLER_SET ) { LmHandlerParams.ActiveRegion = region; return LmHandlerConfigure( &LmHandlerParams ); } else { /* Cannot change Region in running state */ return LORAMAC_HANDLER_ERROR; } } LmHandlerErrorStatus_t LmHandlerGetAdrEnable( bool *adrEnable ) { if( adrEnable == NULL ) { return LORAMAC_HANDLER_ERROR; } *adrEnable = LmHandlerParams.AdrEnable; return LORAMAC_HANDLER_SUCCESS; } LmHandlerErrorStatus_t LmHandlerSetAdrEnable( bool adrEnable ) { MibRequestConfirm_t mibReq; mibReq.Type = MIB_ADR; mibReq.Param.AdrEnable = adrEnable; if( LoRaMacMibSetRequestConfirm( &mibReq ) != LORAMAC_STATUS_OK ) { return LORAMAC_HANDLER_ERROR; } LmHandlerParams.AdrEnable = adrEnable; return LORAMAC_HANDLER_SUCCESS; } LmHandlerErrorStatus_t LmHandlerSetTxDatarate( int8_t txDatarate ) { if( LmHandlerParams.AdrEnable == true ) { return LORAMAC_HANDLER_ERROR; } MibRequestConfirm_t mibReq; mibReq.Type = MIB_CHANNELS_DATARATE; mibReq.Param.ChannelsDatarate = txDatarate; if( LoRaMacMibSetRequestConfirm( &mibReq ) != LORAMAC_STATUS_OK ) { return LORAMAC_HANDLER_ERROR; } LmHandlerParams.TxDatarate = txDatarate; return LORAMAC_HANDLER_SUCCESS; } LmHandlerErrorStatus_t LmHandlerGetDutyCycleEnable( bool *dutyCycleEnable ) { if( dutyCycleEnable == NULL ) { return LORAMAC_HANDLER_ERROR; } *dutyCycleEnable = LmHandlerParams.DutyCycleEnabled; return LORAMAC_HANDLER_SUCCESS; } LmHandlerErrorStatus_t LmHandlerSetDutyCycleEnable( bool dutyCycleEnable ) { LmHandlerParams.DutyCycleEnabled = dutyCycleEnable; LoRaMacTestSetDutyCycleOn( dutyCycleEnable ); return LORAMAC_HANDLER_SUCCESS; } LmHandlerErrorStatus_t LmHandlerGetRX2Params( RxChannelParams_t *rxParams ) { if( rxParams == NULL ) { return LORAMAC_HANDLER_ERROR; } MibRequestConfirm_t mibReq; mibReq.Type = MIB_RX2_CHANNEL; if( LoRaMacMibGetRequestConfirm( &mibReq ) != LORAMAC_STATUS_OK ) { return LORAMAC_HANDLER_ERROR; } rxParams->Frequency = mibReq.Param.Rx2Channel.Frequency; rxParams->Datarate = mibReq.Param.Rx2Channel.Datarate; return LORAMAC_HANDLER_SUCCESS; } LmHandlerErrorStatus_t LmHandlerGetTxPower( int8_t *txPower ) { MibRequestConfirm_t mibReq; if( txPower == NULL ) { return LORAMAC_HANDLER_ERROR; } mibReq.Type = MIB_CHANNELS_TX_POWER; if( LoRaMacMibGetRequestConfirm( &mibReq ) != LORAMAC_STATUS_OK ) { return LORAMAC_HANDLER_ERROR; } *txPower = mibReq.Param.ChannelsTxPower; LmHandlerParams.TxPower = *txPower; return LORAMAC_HANDLER_SUCCESS; } LmHandlerErrorStatus_t LmHandlerGetRx1Delay( uint32_t *rxDelay ) { MibRequestConfirm_t mibReq; if( rxDelay == NULL ) { return LORAMAC_HANDLER_ERROR; } mibReq.Type = MIB_RECEIVE_DELAY_1; if( LoRaMacMibGetRequestConfirm( &mibReq ) != LORAMAC_STATUS_OK ) { return LORAMAC_HANDLER_ERROR; } *rxDelay = mibReq.Param.ReceiveDelay1; return LORAMAC_HANDLER_SUCCESS; } LmHandlerErrorStatus_t LmHandlerGetRx2Delay( uint32_t *rxDelay ) { MibRequestConfirm_t mibReq; if( rxDelay == NULL ) { return LORAMAC_HANDLER_ERROR; } mibReq.Type = MIB_RECEIVE_DELAY_2; if( LoRaMacMibGetRequestConfirm( &mibReq ) != LORAMAC_STATUS_OK ) { return LORAMAC_HANDLER_ERROR; } *rxDelay = mibReq.Param.ReceiveDelay2; return LORAMAC_HANDLER_SUCCESS; } LmHandlerErrorStatus_t LmHandlerGetJoinRx1Delay( uint32_t *rxDelay ) { MibRequestConfirm_t mibReq; if( rxDelay == NULL ) { return LORAMAC_HANDLER_ERROR; } mibReq.Type = MIB_JOIN_ACCEPT_DELAY_1; if( LoRaMacMibGetRequestConfirm( &mibReq ) != LORAMAC_STATUS_OK ) { return LORAMAC_HANDLER_ERROR; } *rxDelay = mibReq.Param.JoinAcceptDelay1; return LORAMAC_HANDLER_SUCCESS; } LmHandlerErrorStatus_t LmHandlerGetJoinRx2Delay( uint32_t *rxDelay ) { MibRequestConfirm_t mibReq; if( rxDelay == NULL ) { return LORAMAC_HANDLER_ERROR; } mibReq.Type = MIB_JOIN_ACCEPT_DELAY_2; if( LoRaMacMibGetRequestConfirm( &mibReq ) != LORAMAC_STATUS_OK ) { return LORAMAC_HANDLER_ERROR; } *rxDelay = mibReq.Param.JoinAcceptDelay2; return LORAMAC_HANDLER_SUCCESS; } LmHandlerErrorStatus_t LmHandlerSetTxPower( int8_t txPower ) { MibRequestConfirm_t mibReq; mibReq.Type = MIB_CHANNELS_TX_POWER; mibReq.Param.ChannelsTxPower = txPower; if( LoRaMacMibSetRequestConfirm( &mibReq ) != LORAMAC_STATUS_OK ) { return LORAMAC_HANDLER_ERROR; } LmHandlerParams.TxPower = txPower; return LORAMAC_HANDLER_SUCCESS; } LmHandlerErrorStatus_t LmHandlerSetRX2Params( RxChannelParams_t *rxParams ) { MibRequestConfirm_t mibReq; mibReq.Type = MIB_RX2_CHANNEL; mibReq.Param.Rx2Channel.Frequency = rxParams->Frequency; mibReq.Param.Rx2Channel.Datarate = rxParams->Datarate; if( LoRaMacMibSetRequestConfirm( &mibReq ) != LORAMAC_STATUS_OK ) { return LORAMAC_HANDLER_ERROR; } return LORAMAC_HANDLER_SUCCESS; } LmHandlerErrorStatus_t LmHandlerSetRx1Delay( uint32_t rxDelay ) { MibRequestConfirm_t mibReq; mibReq.Type = MIB_RECEIVE_DELAY_1; mibReq.Param.ReceiveDelay1 = rxDelay; if( LoRaMacMibSetRequestConfirm( &mibReq ) != LORAMAC_STATUS_OK ) { return LORAMAC_HANDLER_ERROR; } return LORAMAC_HANDLER_SUCCESS; } LmHandlerErrorStatus_t LmHandlerSetRx2Delay( uint32_t rxDelay ) { MibRequestConfirm_t mibReq; mibReq.Type = MIB_RECEIVE_DELAY_2; mibReq.Param.ReceiveDelay2 = rxDelay; if( LoRaMacMibSetRequestConfirm( &mibReq ) != LORAMAC_STATUS_OK ) { return LORAMAC_HANDLER_ERROR; } return LORAMAC_HANDLER_SUCCESS; } LmHandlerErrorStatus_t LmHandlerSetJoinRx1Delay( uint32_t rxDelay ) { MibRequestConfirm_t mibReq; mibReq.Type = MIB_JOIN_ACCEPT_DELAY_1; mibReq.Param.JoinAcceptDelay1 = rxDelay; if( LoRaMacMibSetRequestConfirm( &mibReq ) != LORAMAC_STATUS_OK ) { return LORAMAC_HANDLER_ERROR; } return LORAMAC_HANDLER_SUCCESS; } LmHandlerErrorStatus_t LmHandlerSetJoinRx2Delay( uint32_t rxDelay ) { MibRequestConfirm_t mibReq; mibReq.Type = MIB_JOIN_ACCEPT_DELAY_2; mibReq.Param.JoinAcceptDelay2 = rxDelay; if( LoRaMacMibSetRequestConfirm( &mibReq ) != LORAMAC_STATUS_OK ) { return LORAMAC_HANDLER_ERROR; } return LORAMAC_HANDLER_SUCCESS; } LmHandlerErrorStatus_t LmHandlerGetPingPeriodicity( uint8_t *pingPeriodicity ) { #if ( LORAMAC_CLASSB_ENABLED == 1 ) if( pingPeriodicity == NULL ) { return LORAMAC_HANDLER_ERROR; } *pingPeriodicity = LmHandlerParams.PingSlotPeriodicity; return LORAMAC_HANDLER_SUCCESS; #else /* LORAMAC_CLASSB_ENABLED == 0 */ return LORAMAC_HANDLER_ERROR; #endif /* LORAMAC_CLASSB_ENABLED */ } LmHandlerErrorStatus_t LmHandlerSetPingPeriodicity( uint8_t pingPeriodicity ) { #if ( LORAMAC_CLASSB_ENABLED == 1 ) /* Not yet joined */ if( LmHandlerJoinStatus() != LORAMAC_HANDLER_SET ) { LmHandlerParams.PingSlotPeriodicity = pingPeriodicity; return LORAMAC_HANDLER_SUCCESS; } else { /* Cannot change Region in running state */ return LmHandlerPingSlotReq( pingPeriodicity ); } #else /* LORAMAC_CLASSB_ENABLED == 0 */ return LORAMAC_HANDLER_ERROR; #endif /* LORAMAC_CLASSB_ENABLED */ } LmHandlerErrorStatus_t LmHandlerGetBeaconState( BeaconState_t *beaconState ) { #if ( LORAMAC_CLASSB_ENABLED == 1 ) MibRequestConfirm_t mibReq; if( beaconState == NULL ) { return LORAMAC_HANDLER_ERROR; } mibReq.Type = MIB_BEACON_STATE; if( LoRaMacMibGetRequestConfirm( &mibReq ) != LORAMAC_STATUS_OK ) { return LORAMAC_HANDLER_ERROR; } *beaconState = mibReq.Param.BeaconState; return LORAMAC_HANDLER_SUCCESS; #else /* LORAMAC_CLASSB_ENABLED == 0 */ return LORAMAC_HANDLER_ERROR; #endif /* LORAMAC_CLASSB_ENABLED */ } LmHandlerErrorStatus_t LmHandlerGetKey( KeyIdentifier_t keyID, uint8_t *key ) { #if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) Key_t *keyItem; #endif /* LORAWAN_KMS */ if( key == NULL ) { return LORAMAC_HANDLER_ERROR; } #if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) if( SECURE_ELEMENT_SUCCESS != SecureElementGetKeyByID( keyID, &keyItem ) ) { return LORAMAC_HANDLER_ERROR; } memcpy1( key, keyItem->KeyValue, 16 ); #else if( SECURE_ELEMENT_SUCCESS != SecureElementGetKeyByID( keyID, key ) ) { return LORAMAC_HANDLER_ERROR; } #endif /* LORAWAN_KMS */ return LORAMAC_HANDLER_SUCCESS; } LmHandlerErrorStatus_t LmHandlerSetKey( KeyIdentifier_t keyID, uint8_t *key ) { /* Not yet joined */ if( LmHandlerJoinStatus( ) != LORAMAC_HANDLER_SET ) { if( keyID == APP_KEY ) /* Specific usage because APP_KEY is used to derive other keys */ { MibRequestConfirm_t mibReq; mibReq.Type = MIB_APP_KEY; mibReq.Param.AppKey = key; if( LoRaMacMibSetRequestConfirm( &mibReq ) != LORAMAC_STATUS_OK ) { return LORAMAC_HANDLER_ERROR; } return LORAMAC_HANDLER_SUCCESS; } else if( SECURE_ELEMENT_SUCCESS != SecureElementSetKey( keyID, key ) ) { return LORAMAC_HANDLER_ERROR; } } else { /* Cannot change Keys in running state */ return LORAMAC_HANDLER_ERROR; } return LORAMAC_HANDLER_SUCCESS; } LmHandlerErrorStatus_t LmHandlerNvmDataStore( void ) { #if (defined( CONTEXT_MANAGEMENT_ENABLED ) && ( CONTEXT_MANAGEMENT_ENABLED == 1 )) LoRaMacNvmData_t *nvm; uint32_t nvm_size; LmHandlerErrorStatus_t lmhStatus = LORAMAC_HANDLER_SUCCESS; int32_t status = NVM_DATA_OK; lmhStatus = LmHandlerHalt(); if( lmhStatus == LORAMAC_HANDLER_SUCCESS ) { status = NvmDataMgmtStoreBegin(); if( status == NVM_DATA_NO_UPDATED_DATA ) { lmhStatus = LORAMAC_HANDLER_NVM_DATA_UP_TO_DATE; } else if( ( status != NVM_DATA_OK ) || ( LmHandlerCallbacks->OnStoreContextRequest == NULL ) ) { lmhStatus = LORAMAC_HANDLER_ERROR; } else { MibRequestConfirm_t mibReq; mibReq.Type = MIB_NVM_CTXS; LoRaMacMibGetRequestConfirm( &mibReq ); nvm = ( LoRaMacNvmData_t * )mibReq.Param.Contexts; nvm_size = ( ( sizeof( LoRaMacNvmData_t ) + 7 ) & ~0x07 ); LmHandlerCallbacks->OnStoreContextRequest( nvm, nvm_size ); } if( NvmDataMgmtStoreEnd() != NVM_DATA_OK ) { lmhStatus = LORAMAC_HANDLER_ERROR; } } if( ( lmhStatus == LORAMAC_HANDLER_SUCCESS ) && ( LmHandlerCallbacks->OnNvmDataChange != NULL ) ) { LmHandlerCallbacks->OnNvmDataChange( LORAMAC_HANDLER_NVM_STORE ); } return lmhStatus; #else return LORAMAC_HANDLER_ERROR; #endif /* CONTEXT_MANAGEMENT_ENABLED */ }