/*! * \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 */ #if (!defined (LORAWAN_DATA_DISTRIB_MGT) || (LORAWAN_DATA_DISTRIB_MGT == 0)) #else /* LORAWAN_DATA_DISTRIB_MGT == 1 */ #include "LmhpPackagesRegistration.h" #endif /* LORAWAN_DATA_DISTRIB_MGT */ /* 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 .NetworkId = LORAWAN_NETWORK_ID, .DevAddr = LORAWAN_DEVICE_ADDRESS, }; #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 )) /*! * 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, .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 )) /*! * 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 ) /*! * 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 */ /*! * 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 ); #if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) static bool LmHandlerPackageIsTxPending( void ); #endif /* LORAMAC_VERSION */ 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); /* 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.NvmDataChange = NvmDataMgmtEvent; LoRaMacCallbacks.MacProcessNotify = LmHandlerCallbacks->OnMacProcess; #if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) LmhpComplianceParams.FwVersion.Value = fwVersion; LmhpComplianceParams.OnTxPeriodicityChanged = LmHandlerCallbacks->OnTxPeriodicityChanged; LmhpComplianceParams.OnTxFrameCtrlChanged = LmHandlerCallbacks->OnTxFrameCtrlChanged; LmhpComplianceParams.OnPingSlotPeriodicityChanged = LmHandlerCallbacks->OnPingSlotPeriodicityChanged; #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 (!defined (LORAWAN_DATA_DISTRIB_MGT) || (LORAWAN_DATA_DISTRIB_MGT == 0)) #else /*LORAWAN_DATA_DISTRIB_MGT == 1*/ if (LmhpPackagesRegistrationInit() != LORAMAC_HANDLER_SUCCESS) { return LORAMAC_HANDLER_ERROR; } #endif /*LORAWAN_DATA_DISTRIB_MGT*/ 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; UTIL_MEM_cpy_8((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 )) 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; } // 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 ); } mibReq.Type = MIB_DEV_ADDR; LoRaMacMibGetRequestConfirm( &mibReq ); CommissioningParams.DevAddr = mibReq.Param.DevAddr; mibReq.Type = MIB_NVM_CTXS; LoRaMacMibGetRequestConfirm( &mibReq ); LmHandlerParams.ActiveRegion = mibReq.Param.Contexts->MacGroup2.Region; LmHandlerParams.DefaultClass = mibReq.Param.Contexts->MacGroup2.DeviceClass; LmHandlerParams.AdrEnable = mibReq.Param.Contexts->MacGroup2.AdrCtrlOn; } else { mibReq.Type = MIB_NET_ID; mibReq.Param.NetID = LORAWAN_NETWORK_ID; LoRaMacMibSetRequestConfirm(&mibReq); #if ( STATIC_DEVICE_ADDRESS != 1 ) CommissioningParams.DevAddr = LmHandlerCallbacks->GetDevAddr(); #endif /* STATIC_DEVICE_ADDRESS != 1 */ mibReq.Type = MIB_DEV_ADDR; mibReq.Param.DevAddr = CommissioningParams.DevAddr; LoRaMacMibSetRequestConfirm(&mibReq); } // Read secure-element DEV_EUI and JOIN_EUI values. mibReq.Type = MIB_DEV_EUI; LoRaMacMibGetRequestConfirm( &mibReq ); memcpy1( CommissioningParams.DevEui, mibReq.Param.DevEui, 8 ); mibReq.Type = MIB_JOIN_EUI; LoRaMacMibGetRequestConfirm( &mibReq ); memcpy1( CommissioningParams.JoinEui, mibReq.Param.JoinEui, 8 ); SecureElementPrintKeys(); MW_LOG(TS_OFF, VLEVEL_M, "###### DevAddr: %02X:%02X:%02X:%02X\r\n", (unsigned)((unsigned char *)(&CommissioningParams.DevAddr))[3], (unsigned)((unsigned char *)(&CommissioningParams.DevAddr))[2], (unsigned)((unsigned char *)(&CommissioningParams.DevAddr))[1], (unsigned)((unsigned char *)(&CommissioningParams.DevAddr))[0]); #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; } #elif (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) if( LmHandlerPackageIsTxPending( ) == true ) { return true; } #endif /* LORAMAC_VERSION */ 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( ); #if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) // Check if a package transmission is pending. // If it is the case exit function earlier if( LmHandlerPackageIsTxPending( ) == true ) { return; } // 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; } void LmHandlerJoin( ActivationType_t mode, bool forceRejoin ) { #if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) #else /* LORAWAN_KMS == 1 */ #if (OVER_THE_AIR_ACTIVATION == 0) if ( mode == ACTIVATION_TYPE_OTAA ) { MW_LOG(TS_OFF, VLEVEL_M, "ERROR: OTAA mode not implemented\r\n"); while (1); } #endif /* OVER_THE_AIR_ACTIVATION */ #if (ACTIVATION_BY_PERSONALIZATION == 0) if ( mode == ACTIVATION_TYPE_ABP ) { MW_LOG(TS_OFF, VLEVEL_M, "ERROR: ABP mode not implemented\r\n"); while (1); } #endif /* ACTIVATION_BY_PERSONALIZATION */ #endif /* LORAWAN_KMS */ MlmeReq_t mlmeReq; mlmeReq.Type = MLME_JOIN; mlmeReq.Req.Join.Datarate = LmHandlerParams.TxDatarate; 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 LoRaMacMlmeRequest( &mlmeReq ); #endif /* LORAMAC_VERSION */ } else { MibRequestConfirm_t mibReq; mlmeReq.Req.Join.NetworkActivation = ACTIVATION_TYPE_ABP; JoinParams.Mode = ACTIVATION_TYPE_ABP; JoinParams.Datarate = LmHandlerParams.TxDatarate; 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 ); /* 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); #if (defined (LORAWAN_KMS) && (LORAWAN_KMS == 1)) #if ( USE_LRWAN_1_1_X_CRYPTO == 1 ) SecureElementSetObjHandler(F_NWK_S_INT_KEY, KMS_F_NWK_S_INT_KEY_OBJECT_HANDLE); SecureElementSetObjHandler(S_NWK_S_INT_KEY, KMS_S_NWK_S_INT_KEY_OBJECT_HANDLE); SecureElementSetObjHandler(NWK_S_ENC_KEY, KMS_NWK_S_ENC_KEY_OBJECT_HANDLE); #else /* USE_LRWAN_1_1_X_CRYPTO == 0 */ SecureElementSetObjHandler(NWK_S_KEY, KMS_NWK_S_KEY_OBJECT_HANDLE); #endif /* USE_LRWAN_1_1_X_CRYPTO */ SecureElementSetObjHandler(APP_S_KEY, KMS_APP_S_KEY_OBJECT_HANDLE); #endif /* LORAWAN_KMS == 1 */ } LoRaMacStart(); mibReq.Type = MIB_NETWORK_ACTIVATION; mibReq.Param.NetworkActivation = ACTIVATION_TYPE_ABP; LoRaMacMibSetRequestConfirm( &mibReq ); #if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) // Notify upper layer LmHandlerCallbacks->OnJoinRequest( &JoinParams ); LmHandlerRequestClass(LmHandlerParams.DefaultClass); #endif } #if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) if ((CtxRestoreDone == false) || (forceRejoin == true)) { // Starts the join procedure LoRaMacMlmeRequest( &mlmeReq ); } DutyCycleWaitTime = mlmeReq.ReqReturn.DutyCycleWaitTime; #endif /* LORAMAC_VERSION */ } 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( 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 )) 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; 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; 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->FramePending == true) && (deviceClass == CLASS_A)) { // 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 )) if ( ( mcpsIndication->FramePending == true ) && ( deviceClass == CLASS_A ) ) { // 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; LmHandlerCallbacks->OnTxData( &TxParams ); LmHandlerPackagesNotify( PACKAGE_MLME_CONFIRM, mlmeConfirm ); switch( mlmeConfirm->MlmeRequest ) { case MLME_JOIN: { MibRequestConfirm_t mibReq; mibReq.Type = MIB_DEV_ADDR; LoRaMacMibGetRequestConfirm( &mibReq ); CommissioningParams.DevAddr = mibReq.Param.DevAddr; LmHandlerGetTxDatarate( &JoinParams.Datarate ); if( mlmeConfirm->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 LmHandlerCallbacks->OnJoinRequest( &JoinParams ); if( mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK ) { SecureElementPrintSessionKeys(); } } 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 ((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_SCHEDULE_UPLINK: { #if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 )) // The MAC signals that we shall provide an uplink as soon as possible // Send an empty message LmHandlerAppData_t appData = { .Buffer = NULL, .BufferSize = 0, .Port = 0 }; if( LmHandlerPackages[PACKAGE_ID_COMPLIANCE]->IsRunning( ) == false ) { LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG, true ); } #elif (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) // The MAC layer signals that we shall provide an uplink as soon as possible IsUplinkTxPending = true; #endif /* LORAMAC_VERSION */ } break; 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; UTIL_MEM_set_8( 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 (!defined (LORAWAN_DATA_DISTRIB_MGT) || (LORAWAN_DATA_DISTRIB_MGT == 0)) #else /*LORAWAN_DATA_DISTRIB_MGT == 1*/ LmhpPackagesRegister( id, &package ); #endif /*LORAWAN_DATA_DISTRIB_MGT*/ 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 )) 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 )) 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; } } } } } #if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000400 )) static bool LmHandlerPackageIsTxPending( void ) { for( int8_t i = 0; i < PKG_MAX_NUMBER; i++ ) { if( LmHandlerPackages[i] != NULL ) { if( LmHandlerPackages[i]->IsTxPending( ) == true ) { return true; } } } return false; } #endif /* LORAMAC_VERSION */ 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( ); } } } /* *============================================================================= * 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; if (LoRaMacMibGetRequestConfirm(&mibReq) != LORAMAC_STATUS_OK) { return LORAMAC_HANDLER_ERROR; } UTIL_MEM_cpy_8(devEUI, mibReq.Param.DevEui, SE_EUI_SIZE); return LORAMAC_HANDLER_SUCCESS; } LmHandlerErrorStatus_t LmHandlerSetDevEUI(uint8_t *devEUI) { #if ( STATIC_DEVICE_EUI != 1 ) 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; } #else /* STATIC_DEVICE_EUI == 1 */ return LORAMAC_HANDLER_ERROR; #endif /* STATIC_DEVICE_EUI */ } LmHandlerErrorStatus_t LmHandlerGetAppEUI(uint8_t *appEUI) { MibRequestConfirm_t mibReq; if (appEUI == NULL) { return LORAMAC_HANDLER_ERROR; } mibReq.Type = MIB_JOIN_EUI; if (LoRaMacMibGetRequestConfirm(&mibReq) != LORAMAC_STATUS_OK) { return LORAMAC_HANDLER_ERROR; } UTIL_MEM_cpy_8(appEUI, mibReq.Param.JoinEui, SE_EUI_SIZE); 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) { #if ( STATIC_DEVICE_ADDRESS != 1 ) 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; } #else /* STATIC_DEVICE_ADDRESS == 1 */ return LORAMAC_HANDLER_ERROR; #endif /* STATIC_DEVICE_ADDRESS */ } 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; 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; } 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 LmHandlerGetNwkKey( uint8_t *nwkKey ) { #if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) Key_t *keyItem; #endif if (nwkKey == NULL) { return LORAMAC_HANDLER_ERROR; } #if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) if (SECURE_ELEMENT_SUCCESS != SecureElementGetKeyByID(NWK_KEY, &keyItem)) { return LORAMAC_HANDLER_ERROR; } UTIL_MEM_cpy_8( nwkKey, keyItem->KeyValue, 16 ); #else if (SECURE_ELEMENT_SUCCESS != SecureElementGetKeyByID(NWK_KEY, nwkKey)) { return LORAMAC_HANDLER_ERROR; } #endif return LORAMAC_HANDLER_SUCCESS; } LmHandlerErrorStatus_t LmHandlerSetNwkKey( uint8_t *nwkKey ) { /* Not yet joined */ if( LmHandlerJoinStatus( ) != LORAMAC_HANDLER_SET ) { MibRequestConfirm_t mibReq; mibReq.Type = MIB_NWK_KEY; mibReq.Param.NwkKey = nwkKey; 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 LmHandlerGetAppKey( uint8_t *appKey ) { #if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) Key_t *keyItem; #endif if (appKey == NULL) { return LORAMAC_HANDLER_ERROR; } #if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) if (SECURE_ELEMENT_SUCCESS != SecureElementGetKeyByID(APP_KEY, &keyItem)) { return LORAMAC_HANDLER_ERROR; } UTIL_MEM_cpy_8( appKey, keyItem->KeyValue, 16 ); #else if (SECURE_ELEMENT_SUCCESS != SecureElementGetKeyByID(APP_KEY, appKey)) { return LORAMAC_HANDLER_ERROR; } #endif return LORAMAC_HANDLER_SUCCESS; } LmHandlerErrorStatus_t LmHandlerSetAppKey( uint8_t *appKey ) { /* Not yet joined */ if( LmHandlerJoinStatus( ) != LORAMAC_HANDLER_SET ) { MibRequestConfirm_t mibReq; mibReq.Type = MIB_APP_KEY; mibReq.Param.AppKey = appKey; 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 LmHandlerGetNwkSKey( uint8_t *nwkSKey ) { #if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) Key_t *keyItem; #endif if (nwkSKey == NULL) { return LORAMAC_HANDLER_ERROR; } #if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) if (SECURE_ELEMENT_SUCCESS != SecureElementGetKeyByID(NWK_S_KEY, &keyItem)) { return LORAMAC_HANDLER_ERROR; } UTIL_MEM_cpy_8( nwkSKey, keyItem->KeyValue, 16 ); #else if (SECURE_ELEMENT_SUCCESS != SecureElementGetKeyByID(NWK_S_KEY, nwkSKey)) { return LORAMAC_HANDLER_ERROR; } #endif return LORAMAC_HANDLER_SUCCESS; } LmHandlerErrorStatus_t LmHandlerSetNwkSKey( uint8_t *nwkSKey ) { /* Not yet joined */ if( LmHandlerJoinStatus( ) != LORAMAC_HANDLER_SET ) { MibRequestConfirm_t mibReq; mibReq.Type = MIB_NWK_S_KEY; mibReq.Param.NwkSKey = nwkSKey; 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 LmHandlerGetAppSKey( uint8_t *appSKey ) { #if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) Key_t *keyItem; #endif if (appSKey == NULL) { return LORAMAC_HANDLER_ERROR; } #if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) if (SECURE_ELEMENT_SUCCESS != SecureElementGetKeyByID(APP_S_KEY, &keyItem)) { return LORAMAC_HANDLER_ERROR; } UTIL_MEM_cpy_8( appSKey, keyItem->KeyValue, 16 ); #else if (SECURE_ELEMENT_SUCCESS != SecureElementGetKeyByID(APP_S_KEY, appSKey)) { return LORAMAC_HANDLER_ERROR; } #endif return LORAMAC_HANDLER_SUCCESS; } LmHandlerErrorStatus_t LmHandlerSetAppSKey( uint8_t *appSKey ) { /* Not yet joined */ if( LmHandlerJoinStatus( ) != LORAMAC_HANDLER_SET ) { MibRequestConfirm_t mibReq; mibReq.Type = MIB_APP_S_KEY; mibReq.Param.AppSKey = appSKey; 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 LmHandlerNvmDataStore( void ) { 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 = 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; }