4920 lines
160 KiB
C
4920 lines
160 KiB
C
/*!
|
|
* \file LoRaMac.c
|
|
*
|
|
* \brief LoRa MAC layer implementation
|
|
*
|
|
* \copyright Revised BSD License, see section \ref LICENSE.
|
|
*
|
|
* \code
|
|
* ______ _
|
|
* / _____) _ | |
|
|
* ( (____ _____ ____ _| |_ _____ ____| |__
|
|
* \____ \| ___ | (_ _) ___ |/ ___) _ \
|
|
* _____) ) ____| | | || |_| ____( (___| | | |
|
|
* (______/|_____)_|_|_| \__)_____)\____)_| |_|
|
|
* (C)2013-2017 Semtech
|
|
*
|
|
* ___ _____ _ ___ _ _____ ___ ___ ___ ___
|
|
* / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
|
|
* \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
|
|
* |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
|
|
* embedded.connectivity.solutions===============
|
|
*
|
|
* \endcode
|
|
*
|
|
* \author Miguel Luis ( Semtech )
|
|
*
|
|
* \author Gregory Cristian ( Semtech )
|
|
*
|
|
* \author Daniel Jaeckle ( STACKFORCE )
|
|
*
|
|
* \author Johannes Bruder ( STACKFORCE )
|
|
*/
|
|
/**
|
|
******************************************************************************
|
|
*
|
|
* Portions COPYRIGHT 2020 STMicroelectronics
|
|
*
|
|
* @file LoRaMac.c
|
|
* @author MCD Application Team
|
|
* @brief LoRa MAC layer implementation
|
|
******************************************************************************
|
|
*/
|
|
/* Includes ------------------------------------------------------------------*/
|
|
#include "utilities.h"
|
|
#include "Region.h"
|
|
#include "LoRaMacClassB.h"
|
|
#include "LoRaMacCrypto.h"
|
|
#include "secure-element.h"
|
|
#include "LoRaMacTest.h"
|
|
#include "LoRaMacTypes.h"
|
|
#include "LoRaMacConfirmQueue.h"
|
|
#include "LoRaMacHeaderTypes.h"
|
|
#include "LoRaMacMessageTypes.h"
|
|
#include "LoRaMacParser.h"
|
|
#include "LoRaMacCommands.h"
|
|
#include "LoRaMacAdr.h"
|
|
#include "LoRaMacSerializer.h"
|
|
|
|
#include "LoRaMac.h"
|
|
#include "mw_log_conf.h"
|
|
|
|
/* Private macro -------------------------------------------------------------*/
|
|
/* Private define ------------------------------------------------------------*/
|
|
#ifndef LORAMAC_VERSION
|
|
/*!
|
|
* LoRaWAN version definition.
|
|
*/
|
|
#define LORAMAC_VERSION 0x01000300
|
|
#endif
|
|
|
|
/*!
|
|
* Maximum PHY layer payload size
|
|
*/
|
|
#define LORAMAC_PHY_MAXPAYLOAD 255
|
|
|
|
/*!
|
|
* Maximum MAC commands buffer size
|
|
*/
|
|
#define LORA_MAC_COMMAND_MAX_LENGTH 128
|
|
|
|
/*!
|
|
* Maximum length of the fOpts field
|
|
*/
|
|
#define LORA_MAC_COMMAND_MAX_FOPTS_LENGTH 15
|
|
|
|
/*!
|
|
* LoRaMac duty cycle for the back-off procedure during the first hour.
|
|
*/
|
|
#define BACKOFF_DC_1_HOUR 100
|
|
|
|
/*!
|
|
* LoRaMac duty cycle for the back-off procedure during the next 10 hours.
|
|
*/
|
|
#define BACKOFF_DC_10_HOURS 1000
|
|
|
|
/*!
|
|
* LoRaMac duty cycle for the back-off procedure during the next 24 hours.
|
|
*/
|
|
#define BACKOFF_DC_24_HOURS 10000
|
|
|
|
/* Private typedef -----------------------------------------------------------*/
|
|
/*!
|
|
* LoRaMac internal states
|
|
*/
|
|
enum eLoRaMacState
|
|
{
|
|
LORAMAC_IDLE = 0x00000000,
|
|
LORAMAC_STOPPED = 0x00000001,
|
|
LORAMAC_TX_RUNNING = 0x00000002,
|
|
LORAMAC_RX = 0x00000004,
|
|
LORAMAC_ACK_RETRY = 0x00000010,
|
|
LORAMAC_TX_DELAYED = 0x00000020,
|
|
LORAMAC_TX_CONFIG = 0x00000040,
|
|
LORAMAC_RX_ABORT = 0x00000080,
|
|
};
|
|
|
|
/*
|
|
* Request permission state
|
|
*/
|
|
typedef enum eLoRaMacRequestHandling
|
|
{
|
|
LORAMAC_REQUEST_HANDLING_OFF = 0,
|
|
LORAMAC_REQUEST_HANDLING_ON = !LORAMAC_REQUEST_HANDLING_OFF
|
|
}LoRaMacRequestHandling_t;
|
|
|
|
typedef struct sLoRaMacNvmCtx
|
|
{
|
|
/*
|
|
* LoRaMac region.
|
|
*/
|
|
LoRaMacRegion_t Region;
|
|
/*
|
|
* LoRaMac default parameters
|
|
*/
|
|
LoRaMacParams_t MacParamsDefaults;
|
|
/*
|
|
* Network ID ( 3 bytes )
|
|
*/
|
|
uint32_t NetID;
|
|
/*
|
|
* Mote Address
|
|
*/
|
|
uint32_t DevAddr;
|
|
/*!
|
|
* Multicast channel list
|
|
*/
|
|
MulticastCtx_t MulticastChannelList[LORAMAC_MAX_MC_CTX];
|
|
/*
|
|
* Actual device class
|
|
*/
|
|
DeviceClass_t DeviceClass;
|
|
/*
|
|
* Indicates if the node is connected to
|
|
* a private or public network
|
|
*/
|
|
bool PublicNetwork;
|
|
/*
|
|
* LoRaMac ADR control status
|
|
*/
|
|
bool AdrCtrlOn;
|
|
/*
|
|
* Counts the number of missed ADR acknowledgements
|
|
*/
|
|
uint32_t AdrAckCounter;
|
|
|
|
/*
|
|
* LoRaMac parameters
|
|
*/
|
|
LoRaMacParams_t MacParams;
|
|
/*
|
|
* Maximum duty cycle
|
|
* \remark Possibility to shutdown the device.
|
|
*/
|
|
uint8_t MaxDCycle;
|
|
/*
|
|
* Enables/Disables duty cycle management (Test only)
|
|
*/
|
|
bool DutyCycleOn;
|
|
/*
|
|
* Holds the current rx window slot
|
|
*/
|
|
bool RepeaterSupport;
|
|
/*
|
|
* Buffer containing the MAC layer commands
|
|
*/
|
|
uint8_t MacCommandsBuffer[LORA_MAC_COMMAND_MAX_LENGTH];
|
|
/*
|
|
* If the server has sent a FRAME_TYPE_DATA_CONFIRMED_DOWN this variable indicates
|
|
* if the ACK bit must be set for the next transmission
|
|
*/
|
|
bool SrvAckRequested;
|
|
/*
|
|
* Aggregated duty cycle management
|
|
*/
|
|
uint16_t AggregatedDCycle;
|
|
/*
|
|
* Aggregated duty cycle management
|
|
*/
|
|
TimerTime_t LastTxDoneTime;
|
|
TimerTime_t AggregatedTimeOff;
|
|
/*
|
|
* Stores the time at LoRaMac initialization.
|
|
*
|
|
* \remark Used for the BACKOFF_DC computation.
|
|
*/
|
|
SysTime_t InitializationTime;
|
|
/*
|
|
* Current LoRaWAN Version
|
|
*/
|
|
Version_t Version;
|
|
/*
|
|
* End-Device network activation
|
|
*/
|
|
ActivationType_t NetworkActivation;
|
|
/*!
|
|
* Last received Message integrity Code (MIC)
|
|
*/
|
|
uint32_t LastRxMic;
|
|
}LoRaMacNvmCtx_t;
|
|
|
|
typedef struct sLoRaMacCtx
|
|
{
|
|
/*
|
|
* Length of packet in PktBuffer
|
|
*/
|
|
uint16_t PktBufferLen;
|
|
/*
|
|
* Buffer containing the data to be sent or received.
|
|
*/
|
|
uint8_t PktBuffer[LORAMAC_PHY_MAXPAYLOAD];
|
|
/*!
|
|
* Current processed transmit message
|
|
*/
|
|
LoRaMacMessage_t TxMsg;
|
|
/*!
|
|
* Buffer containing the data received by the application.
|
|
*/
|
|
uint8_t AppData[LORAMAC_PHY_MAXPAYLOAD];
|
|
/*
|
|
* Size of buffer containing the application data.
|
|
*/
|
|
uint8_t AppDataSize;
|
|
/*
|
|
* Buffer containing the upper layer data.
|
|
*/
|
|
uint8_t RxPayload[LORAMAC_PHY_MAXPAYLOAD];
|
|
SysTime_t LastTxSysTime;
|
|
/*
|
|
* LoRaMac internal state
|
|
*/
|
|
uint32_t MacState;
|
|
/*
|
|
* LoRaMac upper layer event functions
|
|
*/
|
|
LoRaMacPrimitives_t* MacPrimitives;
|
|
/*
|
|
* LoRaMac upper layer callback functions
|
|
*/
|
|
LoRaMacCallback_t* MacCallbacks;
|
|
/*
|
|
* Radio events function pointer
|
|
*/
|
|
RadioEvents_t RadioEvents;
|
|
/*
|
|
* LoRaMac duty cycle delayed Tx timer
|
|
*/
|
|
TimerEvent_t TxDelayedTimer;
|
|
/*
|
|
* LoRaMac reception windows timers
|
|
*/
|
|
TimerEvent_t RxWindowTimer1;
|
|
TimerEvent_t RxWindowTimer2;
|
|
/*
|
|
* LoRaMac reception windows delay
|
|
* \remark normal frame: RxWindowXDelay = ReceiveDelayX - RADIO_WAKEUP_TIME
|
|
* join frame : RxWindowXDelay = JoinAcceptDelayX - RADIO_WAKEUP_TIME
|
|
*/
|
|
uint32_t RxWindow1Delay;
|
|
uint32_t RxWindow2Delay;
|
|
/*
|
|
* LoRaMac Rx windows configuration
|
|
*/
|
|
RxConfigParams_t RxWindow1Config;
|
|
RxConfigParams_t RxWindow2Config;
|
|
RxConfigParams_t RxWindowCConfig;
|
|
/*
|
|
* Limit of uplinks without any donwlink response before the ADRACKReq bit will be set.
|
|
*/
|
|
uint16_t AdrAckLimit;
|
|
/*
|
|
* Limit of uplinks without any donwlink response after a the first frame with set ADRACKReq bit
|
|
* before the trying to regain the connectivity.
|
|
*/
|
|
uint16_t AdrAckDelay;
|
|
/*
|
|
* Acknowledge timeout timer. Used for packet retransmissions.
|
|
*/
|
|
TimerEvent_t AckTimeoutTimer;
|
|
/*
|
|
* Uplink messages repetitions counter
|
|
*/
|
|
uint8_t ChannelsNbTransCounter;
|
|
/*
|
|
* Number of trials to get a frame acknowledged
|
|
*/
|
|
uint8_t AckTimeoutRetries;
|
|
/*
|
|
* Number of trials to get a frame acknowledged
|
|
*/
|
|
uint8_t AckTimeoutRetriesCounter;
|
|
/*
|
|
* Indicates if the AckTimeout timer has expired or not
|
|
*/
|
|
bool AckTimeoutRetry;
|
|
/*
|
|
* If the node has sent a FRAME_TYPE_DATA_CONFIRMED_UP this variable indicates
|
|
* if the nodes needs to manage the server acknowledgement.
|
|
*/
|
|
bool NodeAckRequested;
|
|
/*
|
|
* Current channel index
|
|
*/
|
|
uint8_t Channel;
|
|
/*
|
|
* Last transmission time on air
|
|
*/
|
|
TimerTime_t TxTimeOnAir;
|
|
/*
|
|
* Structure to hold an MCPS indication data.
|
|
*/
|
|
McpsIndication_t McpsIndication;
|
|
/*
|
|
* Structure to hold MCPS confirm data.
|
|
*/
|
|
McpsConfirm_t McpsConfirm;
|
|
/*
|
|
* Structure to hold MLME confirm data.
|
|
*/
|
|
MlmeConfirm_t MlmeConfirm;
|
|
/*
|
|
* Structure to hold MLME indication data.
|
|
*/
|
|
MlmeIndication_t MlmeIndication;
|
|
/*
|
|
* Holds the current rx window slot
|
|
*/
|
|
LoRaMacRxSlot_t RxSlot;
|
|
/*
|
|
* LoRaMac tx/rx operation state
|
|
*/
|
|
LoRaMacFlags_t MacFlags;
|
|
/*
|
|
* Data structure indicating if a request is allowed or not.
|
|
*/
|
|
LoRaMacRequestHandling_t AllowRequests;
|
|
/*
|
|
* Non-volatile module context structure
|
|
*/
|
|
LoRaMacNvmCtx_t* NvmCtx;
|
|
/*
|
|
* Duty cycle wait time
|
|
*/
|
|
TimerTime_t DutyCycleWaitTime;
|
|
}LoRaMacCtx_t;
|
|
|
|
/*!
|
|
* Defines the LoRaMac radio events status
|
|
*/
|
|
typedef union uLoRaMacRadioEvents
|
|
{
|
|
uint32_t Value;
|
|
struct sEvents
|
|
{
|
|
uint32_t RxTimeout : 1;
|
|
uint32_t RxError : 1;
|
|
uint32_t TxTimeout : 1;
|
|
uint32_t RxDone : 1;
|
|
uint32_t TxDone : 1;
|
|
}Events;
|
|
}LoRaMacRadioEvents_t;
|
|
|
|
/*!
|
|
* Structure used to store the radio Tx event data
|
|
*/
|
|
struct
|
|
{
|
|
TimerTime_t CurTime;
|
|
}TxDoneParams;
|
|
|
|
/*!
|
|
* Structure used to store the radio Rx event data
|
|
*/
|
|
struct
|
|
{
|
|
TimerTime_t LastRxDone;
|
|
uint8_t *Payload;
|
|
uint16_t Size;
|
|
int16_t Rssi;
|
|
int8_t Snr;
|
|
}RxDoneParams;
|
|
|
|
/* Private variables ---------------------------------------------------------*/
|
|
/*
|
|
* Module context.
|
|
*/
|
|
static LoRaMacCtx_t MacCtx;
|
|
|
|
/*
|
|
* Non-volatile module context.
|
|
*/
|
|
static LoRaMacNvmCtx_t NvmMacCtx;
|
|
|
|
/*
|
|
* List of module contexts.
|
|
*/
|
|
static LoRaMacCtxs_t Contexts;
|
|
|
|
/*!
|
|
* LoRaMac radio events status
|
|
*/
|
|
LoRaMacRadioEvents_t LoRaMacRadioEvents = { .Value = 0 };
|
|
|
|
/* Private function prototypes -----------------------------------------------*/
|
|
/*!
|
|
* \brief Function to be executed on Radio Tx Done event
|
|
*/
|
|
static void OnRadioTxDone( void );
|
|
|
|
/*!
|
|
* \brief This function prepares the MAC to abort the execution of function
|
|
* OnRadioRxDone in case of a reception error.
|
|
*/
|
|
static void PrepareRxDoneAbort( void );
|
|
|
|
/*!
|
|
* \brief Function to be executed on Radio Rx Done event
|
|
*/
|
|
static void OnRadioRxDone( uint8_t* payload, uint16_t size, int16_t rssi, int8_t snr );
|
|
|
|
/*!
|
|
* \brief Function executed on Radio Tx Timeout event
|
|
*/
|
|
static void OnRadioTxTimeout( void );
|
|
|
|
/*!
|
|
* \brief Function executed on Radio Rx error event
|
|
*/
|
|
static void OnRadioRxError( void );
|
|
|
|
/*!
|
|
* \brief Function executed on Radio Rx Timeout event
|
|
*/
|
|
static void OnRadioRxTimeout( void );
|
|
|
|
/*!
|
|
* \brief Function executed on duty cycle delayed Tx timer event
|
|
*/
|
|
static void OnTxDelayedTimerEvent( void* context );
|
|
|
|
/*!
|
|
* \brief Function executed on first Rx window timer event
|
|
*/
|
|
static void OnRxWindow1TimerEvent( void* context );
|
|
|
|
/*!
|
|
* \brief Function executed on second Rx window timer event
|
|
*/
|
|
static void OnRxWindow2TimerEvent( void* context );
|
|
|
|
/*!
|
|
* \brief Function executed on AckTimeout timer event
|
|
*/
|
|
static void OnAckTimeoutTimerEvent( void* context );
|
|
|
|
/*!
|
|
* \brief Configures the events to trigger an MLME-Indication with
|
|
* a MLME type of MLME_SCHEDULE_UPLINK.
|
|
*/
|
|
static void SetMlmeScheduleUplinkIndication( void );
|
|
|
|
/*!
|
|
* Computes next 32 bit downlink counter value and determines the frame counter ID.
|
|
*
|
|
* \param[IN] addrID - Address identifier
|
|
* \param[IN] fType - Frame type
|
|
* \param[IN] macMsg - Data message object, holding the current 16 bit transmitted frame counter
|
|
* \param[IN] lrWanVersion - LoRaWAN version
|
|
* \param[IN] maxFCntGap - Maximum allowed frame counter difference (only for 1.0.X necessary)
|
|
* \param[OUT] fCntID - Frame counter identifier
|
|
* \param[OUT] currentDown - Current downlink counter value
|
|
*
|
|
* \retval - Status of the operation
|
|
*/
|
|
static LoRaMacCryptoStatus_t GetFCntDown( AddressIdentifier_t addrID, FType_t fType, LoRaMacMessageData_t* macMsg, Version_t lrWanVersion,
|
|
uint16_t maxFCntGap, FCntIdentifier_t* fCntID, uint32_t* currentDown );
|
|
|
|
/*!
|
|
* \brief Switches the device class
|
|
*
|
|
* \param [IN] deviceClass Device class to switch to
|
|
*/
|
|
static LoRaMacStatus_t SwitchClass( DeviceClass_t deviceClass );
|
|
|
|
/*!
|
|
* \brief Gets the maximum application payload length in the absence of the optional FOpt field.
|
|
*
|
|
* \param [IN] datarate Current datarate
|
|
*
|
|
* \retval Max length
|
|
*/
|
|
static uint8_t GetMaxAppPayloadWithoutFOptsLength( int8_t datarate );
|
|
|
|
/*!
|
|
* \brief Validates if the payload fits into the frame, taking the datarate
|
|
* into account.
|
|
*
|
|
* \details Refer to chapter 4.3.2 of the LoRaWAN specification, v1.0
|
|
*
|
|
* \param lenN Length of the application payload. The length depends on the
|
|
* datarate and is region specific
|
|
*
|
|
* \param datarate Current datarate
|
|
*
|
|
* \param fOptsLen Length of the fOpts field
|
|
*
|
|
* \retval [false: payload does not fit into the frame, true: payload fits into
|
|
* the frame]
|
|
*/
|
|
static bool ValidatePayloadLength( uint8_t lenN, int8_t datarate, uint8_t fOptsLen );
|
|
|
|
/*!
|
|
* \brief Decodes MAC commands in the fOpts field and in the payload
|
|
*
|
|
* \param [IN] payload A pointer to the payload
|
|
* \param [IN] macIndex The index of the payload where the MAC commands start
|
|
* \param [IN] commandsSize The size of the MAC commands
|
|
* \param [IN] snr The SNR value of the frame
|
|
* \param [IN] rxSlot The RX slot where the frame was received
|
|
*/
|
|
static void ProcessMacCommands( uint8_t* payload, uint8_t macIndex, uint8_t commandsSize, int8_t snr, LoRaMacRxSlot_t rxSlot );
|
|
|
|
/*!
|
|
* \brief LoRaMAC layer generic send frame
|
|
*
|
|
* \param [IN] macHdr MAC header field
|
|
* \param [IN] fPort MAC payload port
|
|
* \param [IN] fBuffer MAC data buffer to be sent
|
|
* \param [IN] fBufferSize MAC data buffer size
|
|
* \retval status Status of the operation.
|
|
*/
|
|
static LoRaMacStatus_t Send( LoRaMacHeader_t* macHdr, uint8_t fPort, void* fBuffer, uint16_t fBufferSize, bool allowDelayedTx );
|
|
|
|
/*!
|
|
* \brief LoRaMAC layer send join/rejoin request
|
|
*
|
|
* \param [IN] joinReqType Type of join-request or rejoin
|
|
*
|
|
* \retval status Status of the operation.
|
|
*/
|
|
static LoRaMacStatus_t SendReJoinReq( JoinReqIdentifier_t joinReqType );
|
|
|
|
/*!
|
|
* \brief LoRaMAC layer frame buffer initialization
|
|
*
|
|
* \param [IN] macHdr MAC header field
|
|
* \param [IN] fCtrl MAC frame control field
|
|
* \param [IN] fOpts MAC commands buffer
|
|
* \param [IN] fPort MAC payload port
|
|
* \param [IN] fBuffer MAC data buffer to be sent
|
|
* \param [IN] fBufferSize MAC data buffer size
|
|
* \retval status Status of the operation.
|
|
*/
|
|
static LoRaMacStatus_t PrepareFrame( LoRaMacHeader_t* macHdr, LoRaMacFrameCtrl_t* fCtrl, uint8_t fPort, void* fBuffer, uint16_t fBufferSize );
|
|
|
|
/*
|
|
* \brief Schedules the frame according to the duty cycle
|
|
*
|
|
* \param [IN] allowDelayedTx When set to true, the a frame will be delayed,
|
|
* the duty cycle restriction is active
|
|
* \retval Status of the operation
|
|
*/
|
|
static LoRaMacStatus_t ScheduleTx( bool allowDelayedTx );
|
|
|
|
/*
|
|
* \brief Secures the current processed frame ( TxMsg )
|
|
* \param[IN] txDr Data rate used for the transmission
|
|
* \param[IN] txCh Index of the channel used for the transmission
|
|
* \retval status Status of the operation
|
|
*/
|
|
static LoRaMacStatus_t SecureFrame( uint8_t txDr, uint8_t txCh );
|
|
|
|
/*
|
|
* \brief Calculates the aggregated back off time.
|
|
*/
|
|
static void CalculateBackOff( void );
|
|
|
|
/*
|
|
* \brief Function to remove pending MAC commands
|
|
*
|
|
* \param [IN] rxSlot The RX slot on which the frame was received
|
|
* \param [IN] fCtrl The frame control field of the received frame
|
|
* \param [IN] request The request type
|
|
*/
|
|
static void RemoveMacCommands( LoRaMacRxSlot_t rxSlot, LoRaMacFrameCtrl_t fCtrl, Mcps_t request );
|
|
|
|
/*!
|
|
* \brief LoRaMAC layer prepared frame buffer transmission with channel specification
|
|
*
|
|
* \remark PrepareFrame must be called at least once before calling this
|
|
* function.
|
|
*
|
|
* \param [IN] channel Channel to transmit on
|
|
* \retval status Status of the operation.
|
|
*/
|
|
static LoRaMacStatus_t SendFrameOnChannel( uint8_t channel );
|
|
|
|
/*!
|
|
* \brief Sets the radio in continuous transmission mode
|
|
*
|
|
* \remark Uses the radio parameters set on the previous transmission.
|
|
*
|
|
* \param [IN] timeout Time in seconds while the radio is kept in continuous wave mode
|
|
* \retval status Status of the operation.
|
|
*/
|
|
static LoRaMacStatus_t SetTxContinuousWave( uint16_t timeout );
|
|
|
|
/*!
|
|
* \brief Sets the radio in continuous transmission mode
|
|
*
|
|
* \remark Uses the radio parameters set on the previous transmission.
|
|
*
|
|
* \param [IN] timeout Time in seconds while the radio is kept in continuous wave mode
|
|
* \param [IN] frequency RF frequency to be set.
|
|
* \param [IN] power RF output power to be set.
|
|
* \retval status Status of the operation.
|
|
*/
|
|
static LoRaMacStatus_t SetTxContinuousWave1( uint16_t timeout, uint32_t frequency, uint8_t power );
|
|
|
|
/*!
|
|
* \brief Resets MAC specific parameters to default
|
|
*/
|
|
static void ResetMacParameters( void );
|
|
|
|
/*!
|
|
* \brief Initializes and opens the reception window
|
|
*
|
|
* \param [IN] rxTimer Window timer to be topped.
|
|
* \param [IN] rxConfig Window parameters to be setup
|
|
*/
|
|
static void RxWindowSetup( TimerEvent_t* rxTimer, RxConfigParams_t* rxConfig );
|
|
|
|
/*!
|
|
* \brief Opens up a continuous RX C window. This is used for
|
|
* class c devices.
|
|
*/
|
|
static void OpenContinuousRxCWindow( void );
|
|
|
|
/*!
|
|
* \brief Returns a pointer to the internal contexts structure.
|
|
*
|
|
* \retval void Points to a structure containing all contexts
|
|
*/
|
|
static LoRaMacCtxs_t* GetCtxs( void );
|
|
|
|
/*!
|
|
* \brief Restoring of internal module contexts
|
|
*
|
|
* \details This function allows to restore module contexts by a given pointer.
|
|
*
|
|
*
|
|
* \retval LoRaMacStatus_t Status of the operation. Possible returns are:
|
|
* returns are:
|
|
* \ref LORAMAC_STATUS_OK,
|
|
* \ref LORAMAC_STATUS_PARAMETER_INVALID,
|
|
*/
|
|
static LoRaMacStatus_t RestoreCtxs( LoRaMacCtxs_t* contexts );
|
|
|
|
/*!
|
|
* \brief Determines the frame type
|
|
*
|
|
* \param [IN] macMsg Data message object
|
|
*
|
|
* \param [OUT] fType Frame type
|
|
*
|
|
* \retval LoRaMacStatus_t Status of the operation. Possible returns are:
|
|
* returns are:
|
|
* \ref LORAMAC_STATUS_OK,
|
|
* \ref LORAMAC_STATUS_PARAMETER_INVALID,
|
|
*/
|
|
static LoRaMacStatus_t DetermineFrameType( LoRaMacMessageData_t* macMsg, FType_t* fType );
|
|
|
|
/*!
|
|
* \brief Checks if the retransmission should be stopped in case of a unconfirmed uplink
|
|
*
|
|
* \retval Returns true if it should be stopped.
|
|
*/
|
|
static bool CheckRetransUnconfirmedUplink( void );
|
|
|
|
/*!
|
|
* \brief Checks if the retransmission should be stopped in case of a confirmed uplink
|
|
*
|
|
* \retval Returns true it should be stopped.
|
|
*/
|
|
static bool CheckRetransConfirmedUplink( void );
|
|
|
|
/*!
|
|
* \brief Stops the uplink retransmission
|
|
*
|
|
* \retval Returns true if successful.
|
|
*/
|
|
static bool StopRetransmission( void );
|
|
|
|
/*!
|
|
* \brief Handles the ACK retries algorithm.
|
|
* Increments the re-tries counter up until the specified number of
|
|
* trials or the allowed maximum. Decrease the uplink datarate every 2
|
|
* trials.
|
|
*/
|
|
static void AckTimeoutRetriesProcess( void );
|
|
|
|
/*!
|
|
* \brief Finalizes the ACK retries algorithm.
|
|
* If no ACK is received restores the default channels
|
|
*/
|
|
static void AckTimeoutRetriesFinalize( void );
|
|
|
|
/*!
|
|
* \brief Calls the callback to indicate that a context changed
|
|
*/
|
|
static void CallNvmCtxCallback( LoRaMacNvmCtxModule_t module );
|
|
|
|
/*!
|
|
* \brief MAC NVM Context has been changed
|
|
*/
|
|
static void EventMacNvmCtxChanged( void );
|
|
|
|
/*!
|
|
* \brief Region NVM Context has been changed
|
|
*/
|
|
static void EventRegionNvmCtxChanged( void );
|
|
|
|
/*!
|
|
* \brief Crypto NVM Context has been changed
|
|
*/
|
|
static void EventCryptoNvmCtxChanged( void );
|
|
|
|
/*!
|
|
* \brief Secure Element NVM Context has been changed
|
|
*/
|
|
static void EventSecureElementNvmCtxChanged( void );
|
|
|
|
/*!
|
|
* \brief MAC commands module nvm context has been changed
|
|
*/
|
|
static void EventCommandsNvmCtxChanged( void );
|
|
|
|
/*!
|
|
* \brief Class B module nvm context has been changed
|
|
*/
|
|
static void EventClassBNvmCtxChanged( void );
|
|
|
|
/*!
|
|
* \brief Confirm Queue module nvm context has been changed
|
|
*/
|
|
static void EventConfirmQueueNvmCtxChanged( void );
|
|
|
|
/*!
|
|
* \brief Verifies if a request is pending currently
|
|
*
|
|
*\retval 1: Request pending, 0: request not pending
|
|
*/
|
|
static uint8_t IsRequestPending( void );
|
|
|
|
/*!
|
|
* \brief Enabled the possibility to perform requests
|
|
*
|
|
* \param [IN] requestState Request permission state
|
|
*/
|
|
static void LoRaMacEnableRequests( LoRaMacRequestHandling_t requestState );
|
|
|
|
/*!
|
|
* \brief This function verifies if a RX abort occurred
|
|
*/
|
|
static void LoRaMacCheckForRxAbort( void );
|
|
|
|
/*!
|
|
* \brief This function verifies if a beacon acquisition MLME
|
|
* request was pending
|
|
*
|
|
* \retval 1: Request pending, 0: no request pending
|
|
*/
|
|
static uint8_t LoRaMacCheckForBeaconAcquisition( void );
|
|
|
|
/*!
|
|
* \brief This function handles join request
|
|
*/
|
|
static void LoRaMacHandleMlmeRequest( void );
|
|
|
|
/*!
|
|
* \brief This function handles mcps request
|
|
*/
|
|
static void LoRaMacHandleMcpsRequest( void );
|
|
|
|
/*!
|
|
* \brief This function handles callback events for requests
|
|
*/
|
|
static void LoRaMacHandleRequestEvents( void );
|
|
|
|
/*!
|
|
* \brief This function handles callback events for indications
|
|
*/
|
|
static void LoRaMacHandleIndicationEvents( void );
|
|
|
|
/* Private functions ---------------------------------------------------------*/
|
|
static void OnRadioTxDone( void )
|
|
{
|
|
TxDoneParams.CurTime = TimerGetCurrentTime( );
|
|
MacCtx.LastTxSysTime = SysTimeGet( );
|
|
|
|
LoRaMacRadioEvents.Events.TxDone = 1;
|
|
|
|
if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->MacProcessNotify != NULL ) )
|
|
{
|
|
MacCtx.MacCallbacks->MacProcessNotify( );
|
|
}
|
|
MW_LOG(TS_ON, VLEVEL_M, "MAC txDone\r\n" );
|
|
}
|
|
|
|
static void OnRadioRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr )
|
|
{
|
|
RxDoneParams.LastRxDone = TimerGetCurrentTime( );
|
|
RxDoneParams.Payload = payload;
|
|
RxDoneParams.Size = size;
|
|
RxDoneParams.Rssi = rssi;
|
|
RxDoneParams.Snr = snr;
|
|
|
|
LoRaMacRadioEvents.Events.RxDone = 1;
|
|
|
|
if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->MacProcessNotify != NULL ) )
|
|
{
|
|
MacCtx.MacCallbacks->MacProcessNotify( );
|
|
}
|
|
MW_LOG(TS_ON, VLEVEL_M, "MAC rxDone\r\n" );
|
|
}
|
|
|
|
static void OnRadioTxTimeout( void )
|
|
{
|
|
LoRaMacRadioEvents.Events.TxTimeout = 1;
|
|
|
|
if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->MacProcessNotify != NULL ) )
|
|
{
|
|
MacCtx.MacCallbacks->MacProcessNotify( );
|
|
}
|
|
MW_LOG(TS_ON, VLEVEL_M, "MAC txTimeOut\r\n" );
|
|
}
|
|
|
|
static void OnRadioRxError( void )
|
|
{
|
|
LoRaMacRadioEvents.Events.RxError = 1;
|
|
|
|
if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->MacProcessNotify != NULL ) )
|
|
{
|
|
MacCtx.MacCallbacks->MacProcessNotify( );
|
|
}
|
|
}
|
|
|
|
static void OnRadioRxTimeout( void )
|
|
{
|
|
LoRaMacRadioEvents.Events.RxTimeout = 1;
|
|
|
|
if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->MacProcessNotify != NULL ) )
|
|
{
|
|
MacCtx.MacCallbacks->MacProcessNotify( );
|
|
}
|
|
MW_LOG(TS_ON, VLEVEL_M, "MAC rxTimeOut\r\n" );
|
|
}
|
|
|
|
static void UpdateRxSlotIdleState( void )
|
|
{
|
|
if( MacCtx.NvmCtx->DeviceClass != CLASS_C )
|
|
{
|
|
MacCtx.RxSlot = RX_SLOT_NONE;
|
|
}
|
|
else
|
|
{
|
|
MacCtx.RxSlot = RX_SLOT_WIN_CLASS_C;
|
|
}
|
|
}
|
|
|
|
static void ProcessRadioTxDone( void )
|
|
{
|
|
GetPhyParams_t getPhy;
|
|
PhyParam_t phyParam;
|
|
SetBandTxDoneParams_t txDone;
|
|
|
|
if( MacCtx.NvmCtx->DeviceClass != CLASS_C )
|
|
{
|
|
Radio.Sleep( );
|
|
}
|
|
// Setup timers
|
|
TimerSetValue( &MacCtx.RxWindowTimer1, MacCtx.RxWindow1Delay );
|
|
TimerStart( &MacCtx.RxWindowTimer1 );
|
|
TimerSetValue( &MacCtx.RxWindowTimer2, MacCtx.RxWindow2Delay );
|
|
TimerStart( &MacCtx.RxWindowTimer2 );
|
|
|
|
if( ( MacCtx.NvmCtx->DeviceClass == CLASS_C ) || ( MacCtx.NodeAckRequested == true ) )
|
|
{
|
|
getPhy.Attribute = PHY_ACK_TIMEOUT;
|
|
phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
|
|
TimerSetValue( &MacCtx.AckTimeoutTimer, MacCtx.RxWindow2Delay + phyParam.Value );
|
|
TimerStart( &MacCtx.AckTimeoutTimer );
|
|
}
|
|
|
|
// Update Aggregated last tx done time
|
|
MacCtx.NvmCtx->LastTxDoneTime = TxDoneParams.CurTime;
|
|
|
|
// Update last tx done time for the current channel
|
|
txDone.Channel = MacCtx.Channel;
|
|
txDone.LastTxDoneTime = TxDoneParams.CurTime;
|
|
txDone.ElapsedTimeSinceStartUp = SysTimeSub( SysTimeGetMcuTime( ), MacCtx.NvmCtx->InitializationTime );
|
|
txDone.LastTxAirTime = MacCtx.TxTimeOnAir;
|
|
txDone.Joined = true;
|
|
if( MacCtx.NvmCtx->NetworkActivation == ACTIVATION_TYPE_NONE )
|
|
{
|
|
txDone.Joined = false;
|
|
}
|
|
|
|
RegionSetBandTxDone( MacCtx.NvmCtx->Region, &txDone );
|
|
|
|
if( MacCtx.NodeAckRequested == false )
|
|
{
|
|
MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK;
|
|
}
|
|
}
|
|
|
|
static void PrepareRxDoneAbort( void )
|
|
{
|
|
MacCtx.MacState |= LORAMAC_RX_ABORT;
|
|
|
|
if( MacCtx.NodeAckRequested == true )
|
|
{
|
|
OnAckTimeoutTimerEvent( NULL );
|
|
}
|
|
|
|
MacCtx.MacFlags.Bits.McpsInd = 1;
|
|
MacCtx.MacFlags.Bits.MacDone = 1;
|
|
|
|
UpdateRxSlotIdleState( );
|
|
}
|
|
|
|
static void ProcessRadioRxDone( void )
|
|
{
|
|
LoRaMacHeader_t macHdr;
|
|
ApplyCFListParams_t applyCFList;
|
|
GetPhyParams_t getPhy;
|
|
PhyParam_t phyParam;
|
|
LoRaMacCryptoStatus_t macCryptoStatus = LORAMAC_CRYPTO_ERROR;
|
|
|
|
LoRaMacMessageData_t macMsgData;
|
|
LoRaMacMessageJoinAccept_t macMsgJoinAccept;
|
|
uint8_t *payload = RxDoneParams.Payload;
|
|
uint16_t size = RxDoneParams.Size;
|
|
int16_t rssi = RxDoneParams.Rssi;
|
|
int8_t snr = RxDoneParams.Snr;
|
|
|
|
uint8_t pktHeaderLen = 0;
|
|
|
|
uint32_t downLinkCounter = 0;
|
|
uint32_t address = MacCtx.NvmCtx->DevAddr;
|
|
uint8_t multicast = 0;
|
|
AddressIdentifier_t addrID = UNICAST_DEV_ADDR;
|
|
FCntIdentifier_t fCntID;
|
|
|
|
MacCtx.McpsConfirm.AckReceived = false;
|
|
MacCtx.McpsIndication.Rssi = rssi;
|
|
MacCtx.McpsIndication.Snr = snr;
|
|
MacCtx.McpsIndication.RxSlot = MacCtx.RxSlot;
|
|
MacCtx.McpsIndication.Port = 0;
|
|
MacCtx.McpsIndication.Multicast = 0;
|
|
MacCtx.McpsIndication.FramePending = 0;
|
|
MacCtx.McpsIndication.Buffer = NULL;
|
|
MacCtx.McpsIndication.BufferSize = 0;
|
|
MacCtx.McpsIndication.RxData = false;
|
|
MacCtx.McpsIndication.AckReceived = false;
|
|
MacCtx.McpsIndication.DownLinkCounter = 0;
|
|
MacCtx.McpsIndication.McpsIndication = MCPS_UNCONFIRMED;
|
|
MacCtx.McpsIndication.DevAddress = 0;
|
|
MacCtx.McpsIndication.DeviceTimeAnsReceived = false;
|
|
|
|
Radio.Sleep( );
|
|
TimerStop( &MacCtx.RxWindowTimer2 );
|
|
|
|
// This function must be called even if we are not in class b mode yet.
|
|
if( LoRaMacClassBRxBeacon( payload, size ) == true )
|
|
{
|
|
MacCtx.MlmeIndication.BeaconInfo.Rssi = rssi;
|
|
MacCtx.MlmeIndication.BeaconInfo.Snr = snr;
|
|
return;
|
|
}
|
|
// Check if we expect a ping or a multicast slot.
|
|
if( MacCtx.NvmCtx->DeviceClass == CLASS_B )
|
|
{
|
|
if( LoRaMacClassBIsPingExpected( ) == true )
|
|
{
|
|
LoRaMacClassBSetPingSlotState( PINGSLOT_STATE_CALC_PING_OFFSET );
|
|
LoRaMacClassBPingSlotTimerEvent( NULL );
|
|
MacCtx.McpsIndication.RxSlot = RX_SLOT_WIN_CLASS_B_PING_SLOT;
|
|
}
|
|
else if( LoRaMacClassBIsMulticastExpected( ) == true )
|
|
{
|
|
LoRaMacClassBSetMulticastSlotState( PINGSLOT_STATE_CALC_PING_OFFSET );
|
|
LoRaMacClassBMulticastSlotTimerEvent( NULL );
|
|
MacCtx.McpsIndication.RxSlot = RX_SLOT_WIN_CLASS_B_MULTICAST_SLOT;
|
|
}
|
|
}
|
|
|
|
macHdr.Value = payload[pktHeaderLen++];
|
|
|
|
switch( macHdr.Bits.MType )
|
|
{
|
|
case FRAME_TYPE_JOIN_ACCEPT:
|
|
// Check if the received frame size is valid
|
|
if( size < LORAMAC_JOIN_ACCEPT_FRAME_MIN_SIZE )
|
|
{
|
|
MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
|
|
PrepareRxDoneAbort( );
|
|
return;
|
|
}
|
|
macMsgJoinAccept.Buffer = payload;
|
|
macMsgJoinAccept.BufSize = size;
|
|
|
|
// Abort in case if the device isn't joined yet and no rejoin request is ongoing.
|
|
if( MacCtx.NvmCtx->NetworkActivation != ACTIVATION_TYPE_NONE )
|
|
{
|
|
MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
|
|
PrepareRxDoneAbort( );
|
|
return;
|
|
}
|
|
macCryptoStatus = LoRaMacCryptoHandleJoinAccept( JOIN_REQ, SecureElementGetJoinEui( ), &macMsgJoinAccept );
|
|
|
|
if( LORAMAC_CRYPTO_SUCCESS == macCryptoStatus )
|
|
{
|
|
// Network ID
|
|
MacCtx.NvmCtx->NetID = ( uint32_t ) macMsgJoinAccept.NetID[0];
|
|
MacCtx.NvmCtx->NetID |= ( ( uint32_t ) macMsgJoinAccept.NetID[1] << 8 );
|
|
MacCtx.NvmCtx->NetID |= ( ( uint32_t ) macMsgJoinAccept.NetID[2] << 16 );
|
|
|
|
// Device Address
|
|
MacCtx.NvmCtx->DevAddr = macMsgJoinAccept.DevAddr;
|
|
|
|
// DLSettings
|
|
MacCtx.NvmCtx->MacParams.Rx1DrOffset = macMsgJoinAccept.DLSettings.Bits.RX1DRoffset;
|
|
MacCtx.NvmCtx->MacParams.Rx2Channel.Datarate = macMsgJoinAccept.DLSettings.Bits.RX2DataRate;
|
|
MacCtx.NvmCtx->MacParams.RxCChannel.Datarate = macMsgJoinAccept.DLSettings.Bits.RX2DataRate;
|
|
|
|
// RxDelay
|
|
MacCtx.NvmCtx->MacParams.ReceiveDelay1 = macMsgJoinAccept.RxDelay;
|
|
if( MacCtx.NvmCtx->MacParams.ReceiveDelay1 == 0 )
|
|
{
|
|
MacCtx.NvmCtx->MacParams.ReceiveDelay1 = 1;
|
|
}
|
|
MacCtx.NvmCtx->MacParams.ReceiveDelay1 *= 1000;
|
|
MacCtx.NvmCtx->MacParams.ReceiveDelay2 = MacCtx.NvmCtx->MacParams.ReceiveDelay1 + 1000;
|
|
|
|
MacCtx.NvmCtx->Version.Fields.Minor = 0;
|
|
|
|
// Apply CF list
|
|
applyCFList.Payload = macMsgJoinAccept.CFList;
|
|
// Size of the regular payload is 12. Plus 1 byte MHDR and 4 bytes MIC
|
|
applyCFList.Size = size - 17;
|
|
|
|
RegionApplyCFList( MacCtx.NvmCtx->Region, &applyCFList );
|
|
|
|
MacCtx.NvmCtx->NetworkActivation = ACTIVATION_TYPE_OTAA;
|
|
|
|
// MLME handling
|
|
if( LoRaMacConfirmQueueIsCmdActive( MLME_JOIN ) == true )
|
|
{
|
|
LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_JOIN );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// MLME handling
|
|
if( LoRaMacConfirmQueueIsCmdActive( MLME_JOIN ) == true )
|
|
{
|
|
LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL, MLME_JOIN );
|
|
}
|
|
}
|
|
break;
|
|
case FRAME_TYPE_DATA_CONFIRMED_DOWN:
|
|
MacCtx.McpsIndication.McpsIndication = MCPS_CONFIRMED;
|
|
// Intentional fall through
|
|
case FRAME_TYPE_DATA_UNCONFIRMED_DOWN:
|
|
// Check if the received payload size is valid
|
|
getPhy.UplinkDwellTime = MacCtx.NvmCtx->MacParams.DownlinkDwellTime;
|
|
getPhy.Datarate = MacCtx.McpsIndication.RxDatarate;
|
|
getPhy.Attribute = PHY_MAX_PAYLOAD;
|
|
|
|
// Get the maximum payload length
|
|
if( MacCtx.NvmCtx->RepeaterSupport == true )
|
|
{
|
|
getPhy.Attribute = PHY_MAX_PAYLOAD_REPEATER;
|
|
}
|
|
|
|
phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
|
|
if( ( MAX( 0, ( int16_t )( ( int16_t ) size - ( int16_t ) LORAMAC_FRAME_PAYLOAD_OVERHEAD_SIZE ) ) > ( int16_t )phyParam.Value ) ||
|
|
( size < LORAMAC_FRAME_PAYLOAD_MIN_SIZE ) )
|
|
{
|
|
MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
|
|
PrepareRxDoneAbort( );
|
|
return;
|
|
}
|
|
macMsgData.Buffer = payload;
|
|
macMsgData.BufSize = size;
|
|
macMsgData.FRMPayload = MacCtx.RxPayload;
|
|
macMsgData.FRMPayloadSize = LORAMAC_PHY_MAXPAYLOAD;
|
|
|
|
if( LORAMAC_PARSER_SUCCESS != LoRaMacParserData( &macMsgData ) )
|
|
{
|
|
MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
|
|
PrepareRxDoneAbort( );
|
|
return;
|
|
}
|
|
|
|
// Store device address
|
|
MacCtx.McpsIndication.DevAddress = macMsgData.FHDR.DevAddr;
|
|
|
|
FType_t fType;
|
|
if( LORAMAC_STATUS_OK != DetermineFrameType( &macMsgData, &fType ) )
|
|
{
|
|
MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
|
|
PrepareRxDoneAbort( );
|
|
return;
|
|
}
|
|
|
|
//Check if it is a multicast message
|
|
multicast = 0;
|
|
downLinkCounter = 0;
|
|
for( uint8_t i = 0; i < LORAMAC_MAX_MC_CTX; i++ )
|
|
{
|
|
if( ( MacCtx.NvmCtx->MulticastChannelList[i].ChannelParams.Address == macMsgData.FHDR.DevAddr ) &&
|
|
( MacCtx.NvmCtx->MulticastChannelList[i].ChannelParams.IsEnabled == true ) )
|
|
{
|
|
multicast = 1;
|
|
addrID = MacCtx.NvmCtx->MulticastChannelList[i].ChannelParams.GroupID;
|
|
downLinkCounter = *( MacCtx.NvmCtx->MulticastChannelList[i].DownLinkCounter );
|
|
address = MacCtx.NvmCtx->MulticastChannelList[i].ChannelParams.Address;
|
|
if( MacCtx.NvmCtx->DeviceClass == CLASS_C )
|
|
{
|
|
MacCtx.McpsIndication.RxSlot = RX_SLOT_WIN_CLASS_C_MULTICAST;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Filter messages according to multicast downlink exceptions
|
|
if( ( multicast == 1 ) && ( ( fType != FRAME_TYPE_D ) ||
|
|
( macMsgData.FHDR.FCtrl.Bits.Ack != 0 ) ||
|
|
( macMsgData.FHDR.FCtrl.Bits.AdrAckReq != 0 ) ) )
|
|
{
|
|
MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
|
|
PrepareRxDoneAbort( );
|
|
return;
|
|
}
|
|
|
|
// Get maximum allowed counter difference
|
|
getPhy.Attribute = PHY_MAX_FCNT_GAP;
|
|
phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
|
|
|
|
// Get downlink frame counter value
|
|
macCryptoStatus = GetFCntDown( addrID, fType, &macMsgData, MacCtx.NvmCtx->Version, phyParam.Value, &fCntID, &downLinkCounter );
|
|
if( macCryptoStatus != LORAMAC_CRYPTO_SUCCESS )
|
|
{
|
|
if( macCryptoStatus == LORAMAC_CRYPTO_FAIL_FCNT_DUPLICATED )
|
|
{
|
|
// Catch the case of repeated downlink frame counter
|
|
MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED;
|
|
if( ( MacCtx.NvmCtx->Version.Fields.Minor == 0 ) && ( macHdr.Bits.MType == FRAME_TYPE_DATA_CONFIRMED_DOWN ) && ( MacCtx.NvmCtx->LastRxMic == macMsgData.MIC ) )
|
|
{
|
|
MacCtx.NvmCtx->SrvAckRequested = true;
|
|
}
|
|
}
|
|
else if( macCryptoStatus == LORAMAC_CRYPTO_FAIL_MAX_GAP_FCNT )
|
|
{
|
|
// Lost too many frames
|
|
MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS;
|
|
}
|
|
else
|
|
{
|
|
// Other errors
|
|
MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
|
|
}
|
|
MacCtx.McpsIndication.DownLinkCounter = downLinkCounter;
|
|
PrepareRxDoneAbort( );
|
|
return;
|
|
}
|
|
|
|
macCryptoStatus = LoRaMacCryptoUnsecureMessage( addrID, address, fCntID, downLinkCounter, &macMsgData );
|
|
if( macCryptoStatus != LORAMAC_CRYPTO_SUCCESS )
|
|
{
|
|
if( macCryptoStatus == LORAMAC_CRYPTO_FAIL_ADDRESS )
|
|
{
|
|
// We are not the destination of this frame.
|
|
MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL;
|
|
}
|
|
else
|
|
{
|
|
// MIC calculation fail
|
|
MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_MIC_FAIL;
|
|
}
|
|
PrepareRxDoneAbort( );
|
|
return;
|
|
}
|
|
|
|
// Frame is valid
|
|
MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_OK;
|
|
MacCtx.McpsIndication.Multicast = multicast;
|
|
MacCtx.McpsIndication.FramePending = macMsgData.FHDR.FCtrl.Bits.FPending;
|
|
MacCtx.McpsIndication.Buffer = NULL;
|
|
MacCtx.McpsIndication.BufferSize = 0;
|
|
MacCtx.McpsIndication.DownLinkCounter = downLinkCounter;
|
|
MacCtx.McpsIndication.AckReceived = macMsgData.FHDR.FCtrl.Bits.Ack;
|
|
|
|
MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK;
|
|
MacCtx.McpsConfirm.AckReceived = macMsgData.FHDR.FCtrl.Bits.Ack;
|
|
|
|
// Reset ADR ACK Counter only, when RX1 or RX2 slot
|
|
if( ( MacCtx.McpsIndication.RxSlot == RX_SLOT_WIN_1 ) ||
|
|
( MacCtx.McpsIndication.RxSlot == RX_SLOT_WIN_2 ) )
|
|
{
|
|
MacCtx.NvmCtx->AdrAckCounter = 0;
|
|
}
|
|
|
|
// MCPS Indication and ack requested handling
|
|
if( multicast == 1 )
|
|
{
|
|
MacCtx.McpsIndication.McpsIndication = MCPS_MULTICAST;
|
|
}
|
|
else
|
|
{
|
|
if( macHdr.Bits.MType == FRAME_TYPE_DATA_CONFIRMED_DOWN )
|
|
{
|
|
MacCtx.NvmCtx->SrvAckRequested = true;
|
|
if( MacCtx.NvmCtx->Version.Fields.Minor == 0 )
|
|
{
|
|
MacCtx.NvmCtx->LastRxMic = macMsgData.MIC;
|
|
}
|
|
MacCtx.McpsIndication.McpsIndication = MCPS_CONFIRMED;
|
|
}
|
|
else
|
|
{
|
|
MacCtx.NvmCtx->SrvAckRequested = false;
|
|
MacCtx.McpsIndication.McpsIndication = MCPS_UNCONFIRMED;
|
|
}
|
|
}
|
|
|
|
RemoveMacCommands( MacCtx.McpsIndication.RxSlot, macMsgData.FHDR.FCtrl, MacCtx.McpsConfirm.McpsRequest );
|
|
|
|
switch( fType )
|
|
{
|
|
case FRAME_TYPE_A:
|
|
{ /* +----------+------+-------+--------------+
|
|
* | FOptsLen | Fopt | FPort | FRMPayload |
|
|
* +----------+------+-------+--------------+
|
|
* | > 0 | X | > 0 | X |
|
|
* +----------+------+-------+--------------+
|
|
*/
|
|
|
|
// Decode MAC commands in FOpts field
|
|
ProcessMacCommands( macMsgData.FHDR.FOpts, 0, macMsgData.FHDR.FCtrl.Bits.FOptsLen, snr, MacCtx.McpsIndication.RxSlot );
|
|
MacCtx.McpsIndication.Port = macMsgData.FPort;
|
|
MacCtx.McpsIndication.Buffer = macMsgData.FRMPayload;
|
|
MacCtx.McpsIndication.BufferSize = macMsgData.FRMPayloadSize;
|
|
MacCtx.McpsIndication.RxData = true;
|
|
break;
|
|
}
|
|
case FRAME_TYPE_B:
|
|
{ /* +----------+------+-------+--------------+
|
|
* | FOptsLen | Fopt | FPort | FRMPayload |
|
|
* +----------+------+-------+--------------+
|
|
* | > 0 | X | - | - |
|
|
* +----------+------+-------+--------------+
|
|
*/
|
|
|
|
// Decode MAC commands in FOpts field
|
|
ProcessMacCommands( macMsgData.FHDR.FOpts, 0, macMsgData.FHDR.FCtrl.Bits.FOptsLen, snr, MacCtx.McpsIndication.RxSlot );
|
|
MacCtx.McpsIndication.Port = macMsgData.FPort;
|
|
break;
|
|
}
|
|
case FRAME_TYPE_C:
|
|
{ /* +----------+------+-------+--------------+
|
|
* | FOptsLen | Fopt | FPort | FRMPayload |
|
|
* +----------+------+-------+--------------+
|
|
* | = 0 | - | = 0 | MAC commands |
|
|
* +----------+------+-------+--------------+
|
|
*/
|
|
|
|
// Decode MAC commands in FRMPayload
|
|
ProcessMacCommands( macMsgData.FRMPayload, 0, macMsgData.FRMPayloadSize, snr, MacCtx.McpsIndication.RxSlot );
|
|
MacCtx.McpsIndication.Port = macMsgData.FPort;
|
|
break;
|
|
}
|
|
case FRAME_TYPE_D:
|
|
{ /* +----------+------+-------+--------------+
|
|
* | FOptsLen | Fopt | FPort | FRMPayload |
|
|
* +----------+------+-------+--------------+
|
|
* | = 0 | - | > 0 | X |
|
|
* +----------+------+-------+--------------+
|
|
*/
|
|
|
|
// No MAC commands just application payload
|
|
MacCtx.McpsIndication.Port = macMsgData.FPort;
|
|
MacCtx.McpsIndication.Buffer = macMsgData.FRMPayload;
|
|
MacCtx.McpsIndication.BufferSize = macMsgData.FRMPayloadSize;
|
|
MacCtx.McpsIndication.RxData = true;
|
|
break;
|
|
}
|
|
default:
|
|
MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
|
|
PrepareRxDoneAbort( );
|
|
break;
|
|
}
|
|
|
|
// Provide always an indication, skip the callback to the user application,
|
|
// in case of a confirmed downlink retransmission.
|
|
MacCtx.MacFlags.Bits.McpsInd = 1;
|
|
|
|
break;
|
|
case FRAME_TYPE_PROPRIETARY:
|
|
memcpy1( MacCtx.RxPayload, &payload[pktHeaderLen], size - pktHeaderLen );
|
|
|
|
MacCtx.McpsIndication.McpsIndication = MCPS_PROPRIETARY;
|
|
MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_OK;
|
|
MacCtx.McpsIndication.Buffer = MacCtx.RxPayload;
|
|
MacCtx.McpsIndication.BufferSize = size - pktHeaderLen;
|
|
|
|
MacCtx.MacFlags.Bits.McpsInd = 1;
|
|
break;
|
|
default:
|
|
MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
|
|
PrepareRxDoneAbort( );
|
|
break;
|
|
}
|
|
|
|
// Verify if we need to disable the AckTimeoutTimer
|
|
if( MacCtx.NodeAckRequested == true )
|
|
{
|
|
if( MacCtx.McpsConfirm.AckReceived == true )
|
|
{
|
|
OnAckTimeoutTimerEvent( NULL );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( MacCtx.NvmCtx->DeviceClass == CLASS_C )
|
|
{
|
|
OnAckTimeoutTimerEvent( NULL );
|
|
}
|
|
}
|
|
MacCtx.MacFlags.Bits.MacDone = 1;
|
|
|
|
UpdateRxSlotIdleState( );
|
|
}
|
|
|
|
static void ProcessRadioTxTimeout( void )
|
|
{
|
|
if( MacCtx.NvmCtx->DeviceClass != CLASS_C )
|
|
{
|
|
Radio.Sleep( );
|
|
}
|
|
UpdateRxSlotIdleState( );
|
|
|
|
MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT;
|
|
LoRaMacConfirmQueueSetStatusCmn( LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT );
|
|
if( MacCtx.NodeAckRequested == true )
|
|
{
|
|
MacCtx.AckTimeoutRetry = true;
|
|
}
|
|
MacCtx.MacFlags.Bits.MacDone = 1;
|
|
}
|
|
|
|
static void HandleRadioRxErrorTimeout( LoRaMacEventInfoStatus_t rx1EventInfoStatus, LoRaMacEventInfoStatus_t rx2EventInfoStatus )
|
|
{
|
|
bool classBRx = false;
|
|
|
|
if( MacCtx.NvmCtx->DeviceClass != CLASS_C )
|
|
{
|
|
Radio.Sleep( );
|
|
}
|
|
|
|
if( LoRaMacClassBIsBeaconExpected( ) == true )
|
|
{
|
|
LoRaMacClassBSetBeaconState( BEACON_STATE_TIMEOUT );
|
|
LoRaMacClassBBeaconTimerEvent( NULL );
|
|
classBRx = true;
|
|
}
|
|
if( MacCtx.NvmCtx->DeviceClass == CLASS_B )
|
|
{
|
|
if( LoRaMacClassBIsPingExpected( ) == true )
|
|
{
|
|
LoRaMacClassBSetPingSlotState( PINGSLOT_STATE_CALC_PING_OFFSET );
|
|
LoRaMacClassBPingSlotTimerEvent( NULL );
|
|
classBRx = true;
|
|
}
|
|
if( LoRaMacClassBIsMulticastExpected( ) == true )
|
|
{
|
|
LoRaMacClassBSetMulticastSlotState( PINGSLOT_STATE_CALC_PING_OFFSET );
|
|
LoRaMacClassBMulticastSlotTimerEvent( NULL );
|
|
classBRx = true;
|
|
}
|
|
}
|
|
|
|
if( classBRx == false )
|
|
{
|
|
if( MacCtx.RxSlot == RX_SLOT_WIN_1 )
|
|
{
|
|
if( MacCtx.NodeAckRequested == true )
|
|
{
|
|
MacCtx.McpsConfirm.Status = rx1EventInfoStatus;
|
|
}
|
|
LoRaMacConfirmQueueSetStatusCmn( rx1EventInfoStatus );
|
|
|
|
if( TimerGetElapsedTime( MacCtx.NvmCtx->LastTxDoneTime ) >= MacCtx.RxWindow2Delay )
|
|
{
|
|
TimerStop( &MacCtx.RxWindowTimer2 );
|
|
MacCtx.MacFlags.Bits.MacDone = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( MacCtx.NodeAckRequested == true )
|
|
{
|
|
MacCtx.McpsConfirm.Status = rx2EventInfoStatus;
|
|
}
|
|
LoRaMacConfirmQueueSetStatusCmn( rx2EventInfoStatus );
|
|
|
|
if( MacCtx.NvmCtx->DeviceClass != CLASS_C )
|
|
{
|
|
MacCtx.MacFlags.Bits.MacDone = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
UpdateRxSlotIdleState( );
|
|
}
|
|
|
|
static void ProcessRadioRxError( void )
|
|
{
|
|
HandleRadioRxErrorTimeout( LORAMAC_EVENT_INFO_STATUS_RX1_ERROR, LORAMAC_EVENT_INFO_STATUS_RX2_ERROR );
|
|
}
|
|
|
|
static void ProcessRadioRxTimeout( void )
|
|
{
|
|
HandleRadioRxErrorTimeout( LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT, LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT );
|
|
}
|
|
|
|
static void LoRaMacHandleIrqEvents( void )
|
|
{
|
|
LoRaMacRadioEvents_t events;
|
|
|
|
CRITICAL_SECTION_BEGIN( );
|
|
events = LoRaMacRadioEvents;
|
|
LoRaMacRadioEvents.Value = 0;
|
|
CRITICAL_SECTION_END( );
|
|
|
|
if( events.Value != 0 )
|
|
{
|
|
if( events.Events.TxDone == 1 )
|
|
{
|
|
ProcessRadioTxDone( );
|
|
}
|
|
if( events.Events.RxDone == 1 )
|
|
{
|
|
ProcessRadioRxDone( );
|
|
}
|
|
if( events.Events.TxTimeout == 1 )
|
|
{
|
|
ProcessRadioTxTimeout( );
|
|
}
|
|
if( events.Events.RxError == 1 )
|
|
{
|
|
ProcessRadioRxError( );
|
|
}
|
|
if( events.Events.RxTimeout == 1 )
|
|
{
|
|
ProcessRadioRxTimeout( );
|
|
}
|
|
}
|
|
}
|
|
|
|
static void LoRaMacEnableRequests( LoRaMacRequestHandling_t requestState )
|
|
{
|
|
MacCtx.AllowRequests = requestState;
|
|
}
|
|
|
|
static void LoRaMacHandleRequestEvents( void )
|
|
{
|
|
// Handle events
|
|
LoRaMacFlags_t reqEvents = MacCtx.MacFlags;
|
|
|
|
if( MacCtx.MacState == LORAMAC_IDLE )
|
|
{
|
|
// Update event bits
|
|
if( MacCtx.MacFlags.Bits.McpsReq == 1 )
|
|
{
|
|
MacCtx.MacFlags.Bits.McpsReq = 0;
|
|
}
|
|
|
|
if( MacCtx.MacFlags.Bits.MlmeReq == 1 )
|
|
{
|
|
MacCtx.MacFlags.Bits.MlmeReq = 0;
|
|
}
|
|
|
|
// Allow requests again
|
|
LoRaMacEnableRequests( LORAMAC_REQUEST_HANDLING_ON );
|
|
|
|
// Handle callbacks
|
|
if( reqEvents.Bits.McpsReq == 1 )
|
|
{
|
|
MacCtx.MacPrimitives->MacMcpsConfirm( &MacCtx.McpsConfirm );
|
|
}
|
|
|
|
if( reqEvents.Bits.MlmeReq == 1 )
|
|
{
|
|
LoRaMacConfirmQueueHandleCb( &MacCtx.MlmeConfirm );
|
|
if( LoRaMacConfirmQueueGetCnt( ) > 0 )
|
|
{
|
|
MacCtx.MacFlags.Bits.MlmeReq = 1;
|
|
}
|
|
}
|
|
|
|
// Start beaconing again
|
|
LoRaMacClassBResumeBeaconing( );
|
|
|
|
// Procedure done. Reset variables.
|
|
MacCtx.MacFlags.Bits.MacDone = 0;
|
|
}
|
|
}
|
|
|
|
static void LoRaMacHandleScheduleUplinkEvent( void )
|
|
{
|
|
// Handle events
|
|
if( MacCtx.MacState == LORAMAC_IDLE )
|
|
{
|
|
// Verify if sticky MAC commands are pending or not
|
|
bool isStickyMacCommandPending = false;
|
|
LoRaMacCommandsStickyCmdsPending( &isStickyMacCommandPending );
|
|
if( isStickyMacCommandPending == true )
|
|
{// Setup MLME indication
|
|
SetMlmeScheduleUplinkIndication( );
|
|
}
|
|
}
|
|
}
|
|
|
|
static void LoRaMacHandleIndicationEvents( void )
|
|
{
|
|
// Handle MLME indication
|
|
if( MacCtx.MacFlags.Bits.MlmeInd == 1 )
|
|
{
|
|
MacCtx.MacFlags.Bits.MlmeInd = 0;
|
|
MacCtx.MacPrimitives->MacMlmeIndication( &MacCtx.MlmeIndication );
|
|
}
|
|
|
|
if( MacCtx.MacFlags.Bits.MlmeSchedUplinkInd == 1 )
|
|
{
|
|
MlmeIndication_t schduleUplinkIndication;
|
|
schduleUplinkIndication.MlmeIndication = MLME_SCHEDULE_UPLINK;
|
|
schduleUplinkIndication.Status = LORAMAC_EVENT_INFO_STATUS_OK;
|
|
|
|
MacCtx.MacPrimitives->MacMlmeIndication( &schduleUplinkIndication );
|
|
MacCtx.MacFlags.Bits.MlmeSchedUplinkInd = 0;
|
|
}
|
|
|
|
// Handle MCPS indication
|
|
if( MacCtx.MacFlags.Bits.McpsInd == 1 )
|
|
{
|
|
MacCtx.MacFlags.Bits.McpsInd = 0;
|
|
MacCtx.MacPrimitives->MacMcpsIndication( &MacCtx.McpsIndication );
|
|
}
|
|
}
|
|
|
|
static void LoRaMacHandleMcpsRequest( void )
|
|
{
|
|
// Handle MCPS uplinks
|
|
if( MacCtx.MacFlags.Bits.McpsReq == 1 )
|
|
{
|
|
bool stopRetransmission = false;
|
|
bool waitForRetransmission = false;
|
|
|
|
if( ( MacCtx.McpsConfirm.McpsRequest == MCPS_UNCONFIRMED ) ||
|
|
( MacCtx.McpsConfirm.McpsRequest == MCPS_PROPRIETARY ) )
|
|
{
|
|
stopRetransmission = CheckRetransUnconfirmedUplink( );
|
|
}
|
|
else if( MacCtx.McpsConfirm.McpsRequest == MCPS_CONFIRMED )
|
|
{
|
|
if( MacCtx.AckTimeoutRetry == true )
|
|
{
|
|
stopRetransmission = CheckRetransConfirmedUplink( );
|
|
|
|
if( MacCtx.NvmCtx->Version.Fields.Minor == 0 )
|
|
{
|
|
if( stopRetransmission == false )
|
|
{
|
|
AckTimeoutRetriesProcess( );
|
|
}
|
|
else
|
|
{
|
|
AckTimeoutRetriesFinalize( );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
waitForRetransmission = true;
|
|
}
|
|
}
|
|
|
|
if( stopRetransmission == true )
|
|
{// Stop retransmission
|
|
TimerStop( &MacCtx.TxDelayedTimer );
|
|
MacCtx.MacState &= ~LORAMAC_TX_DELAYED;
|
|
StopRetransmission( );
|
|
}
|
|
else if( waitForRetransmission == false )
|
|
{// Arrange further retransmission
|
|
MacCtx.MacFlags.Bits.MacDone = 0;
|
|
// Reset the state of the AckTimeout
|
|
MacCtx.AckTimeoutRetry = false;
|
|
// Sends the same frame again
|
|
OnTxDelayedTimerEvent( NULL );
|
|
}
|
|
}
|
|
}
|
|
|
|
static void LoRaMacHandleMlmeRequest( void )
|
|
{
|
|
// Handle join request
|
|
if( MacCtx.MacFlags.Bits.MlmeReq == 1 )
|
|
{
|
|
if( LoRaMacConfirmQueueIsCmdActive( MLME_JOIN ) == true )
|
|
{
|
|
if( LoRaMacConfirmQueueGetStatus( MLME_JOIN ) == LORAMAC_EVENT_INFO_STATUS_OK )
|
|
{// Node joined successfully
|
|
MacCtx.ChannelsNbTransCounter = 0;
|
|
}
|
|
MacCtx.MacState &= ~LORAMAC_TX_RUNNING;
|
|
}
|
|
else if( ( LoRaMacConfirmQueueIsCmdActive( MLME_TXCW ) == true ) ||
|
|
( LoRaMacConfirmQueueIsCmdActive( MLME_TXCW_1 ) == true ) )
|
|
{
|
|
MacCtx.MacState &= ~LORAMAC_TX_RUNNING;
|
|
}
|
|
}
|
|
}
|
|
|
|
static uint8_t LoRaMacCheckForBeaconAcquisition( void )
|
|
{
|
|
if( ( LoRaMacConfirmQueueIsCmdActive( MLME_BEACON_ACQUISITION ) == true ) &&
|
|
( MacCtx.MacFlags.Bits.McpsReq == 0 ) )
|
|
{
|
|
if( MacCtx.MacFlags.Bits.MlmeReq == 1 )
|
|
{
|
|
MacCtx.MacState &= ~LORAMAC_TX_RUNNING;
|
|
return 0x01;
|
|
}
|
|
}
|
|
return 0x00;
|
|
}
|
|
|
|
static void LoRaMacCheckForRxAbort( void )
|
|
{
|
|
// A error occurs during receiving
|
|
if( ( MacCtx.MacState & LORAMAC_RX_ABORT ) == LORAMAC_RX_ABORT )
|
|
{
|
|
MacCtx.MacState &= ~LORAMAC_RX_ABORT;
|
|
MacCtx.MacState &= ~LORAMAC_TX_RUNNING;
|
|
}
|
|
}
|
|
|
|
static void OnTxDelayedTimerEvent( void* context )
|
|
{
|
|
TimerStop( &MacCtx.TxDelayedTimer );
|
|
MacCtx.MacState &= ~LORAMAC_TX_DELAYED;
|
|
|
|
// Schedule frame, allow delayed frame transmissions
|
|
switch( ScheduleTx( true ) )
|
|
{
|
|
case LORAMAC_STATUS_OK:
|
|
case LORAMAC_STATUS_DUTYCYCLE_RESTRICTED:
|
|
{
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
// Stop retransmission attempt
|
|
MacCtx.McpsConfirm.Datarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate;
|
|
MacCtx.McpsConfirm.NbRetries = MacCtx.AckTimeoutRetriesCounter;
|
|
MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR;
|
|
LoRaMacConfirmQueueSetStatusCmn( LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR );
|
|
StopRetransmission( );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void OnRxWindow1TimerEvent( void* context )
|
|
{
|
|
MacCtx.RxWindow1Config.Channel = MacCtx.Channel;
|
|
MacCtx.RxWindow1Config.DrOffset = MacCtx.NvmCtx->MacParams.Rx1DrOffset;
|
|
MacCtx.RxWindow1Config.DownlinkDwellTime = MacCtx.NvmCtx->MacParams.DownlinkDwellTime;
|
|
MacCtx.RxWindow1Config.RepeaterSupport = MacCtx.NvmCtx->RepeaterSupport;
|
|
MacCtx.RxWindow1Config.RxContinuous = false;
|
|
MacCtx.RxWindow1Config.RxSlot = RX_SLOT_WIN_1;
|
|
|
|
RxWindowSetup( &MacCtx.RxWindowTimer1, &MacCtx.RxWindow1Config );
|
|
}
|
|
|
|
static void OnRxWindow2TimerEvent( void* context )
|
|
{
|
|
// Check if we are processing Rx1 window.
|
|
// If yes, we don't setup the Rx2 window.
|
|
if( MacCtx.RxSlot == RX_SLOT_WIN_1 )
|
|
{
|
|
return;
|
|
}
|
|
MacCtx.RxWindow2Config.Channel = MacCtx.Channel;
|
|
MacCtx.RxWindow2Config.Frequency = MacCtx.NvmCtx->MacParams.Rx2Channel.Frequency;
|
|
MacCtx.RxWindow2Config.DownlinkDwellTime = MacCtx.NvmCtx->MacParams.DownlinkDwellTime;
|
|
MacCtx.RxWindow2Config.RepeaterSupport = MacCtx.NvmCtx->RepeaterSupport;
|
|
MacCtx.RxWindow2Config.RxContinuous = false;
|
|
MacCtx.RxWindow2Config.RxSlot = RX_SLOT_WIN_2;
|
|
|
|
RxWindowSetup( &MacCtx.RxWindowTimer2, &MacCtx.RxWindow2Config );
|
|
}
|
|
|
|
static void OnAckTimeoutTimerEvent( void* context )
|
|
{
|
|
TimerStop( &MacCtx.AckTimeoutTimer );
|
|
|
|
if( MacCtx.NodeAckRequested == true )
|
|
{
|
|
MacCtx.AckTimeoutRetry = true;
|
|
}
|
|
if( MacCtx.NvmCtx->DeviceClass == CLASS_C )
|
|
{
|
|
MacCtx.MacFlags.Bits.MacDone = 1;
|
|
}
|
|
if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->MacProcessNotify != NULL ) )
|
|
{
|
|
MacCtx.MacCallbacks->MacProcessNotify( );
|
|
}
|
|
}
|
|
|
|
static LoRaMacCryptoStatus_t GetFCntDown( AddressIdentifier_t addrID, FType_t fType, LoRaMacMessageData_t* macMsg, Version_t lrWanVersion,
|
|
uint16_t maxFCntGap, FCntIdentifier_t* fCntID, uint32_t* currentDown )
|
|
{
|
|
if( ( macMsg == NULL ) || ( fCntID == NULL ) ||
|
|
( currentDown == NULL ) )
|
|
{
|
|
return LORAMAC_CRYPTO_ERROR_NPE;
|
|
}
|
|
|
|
// Determine the frame counter identifier and choose counter from FCntList
|
|
switch( addrID )
|
|
{
|
|
case UNICAST_DEV_ADDR:
|
|
if( lrWanVersion.Fields.Minor == 1 )
|
|
{
|
|
if( ( fType == FRAME_TYPE_A ) || ( fType == FRAME_TYPE_D ) )
|
|
{
|
|
*fCntID = A_FCNT_DOWN;
|
|
}
|
|
else
|
|
{
|
|
*fCntID = N_FCNT_DOWN;
|
|
}
|
|
}
|
|
else
|
|
{ // For LoRaWAN 1.0.X
|
|
*fCntID = FCNT_DOWN;
|
|
}
|
|
break;
|
|
case MULTICAST_0_ADDR:
|
|
*fCntID = MC_FCNT_DOWN_0;
|
|
break;
|
|
#if ( LORAMAC_MAX_MC_CTX > 1 )
|
|
case MULTICAST_1_ADDR:
|
|
*fCntID = MC_FCNT_DOWN_1;
|
|
break;
|
|
case MULTICAST_2_ADDR:
|
|
*fCntID = MC_FCNT_DOWN_2;
|
|
break;
|
|
case MULTICAST_3_ADDR:
|
|
*fCntID = MC_FCNT_DOWN_3;
|
|
break;
|
|
#endif /* LORAMAC_MAX_MC_CTX > 1 */
|
|
default:
|
|
return LORAMAC_CRYPTO_FAIL_FCNT_ID;
|
|
}
|
|
|
|
return LoRaMacCryptoGetFCntDown( *fCntID, maxFCntGap, macMsg->FHDR.FCnt, currentDown );
|
|
}
|
|
|
|
static LoRaMacStatus_t SwitchClass( DeviceClass_t deviceClass )
|
|
{
|
|
LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
|
|
switch( MacCtx.NvmCtx->DeviceClass )
|
|
{
|
|
case CLASS_A:
|
|
{
|
|
if( deviceClass == CLASS_A )
|
|
{
|
|
// Revert back RxC parameters
|
|
MacCtx.NvmCtx->MacParams.RxCChannel = MacCtx.NvmCtx->MacParams.Rx2Channel;
|
|
}
|
|
if( deviceClass == CLASS_B )
|
|
{
|
|
status = LoRaMacClassBSwitchClass( deviceClass );
|
|
if( status == LORAMAC_STATUS_OK )
|
|
{
|
|
MacCtx.NvmCtx->DeviceClass = deviceClass;
|
|
}
|
|
}
|
|
|
|
if( deviceClass == CLASS_C )
|
|
{
|
|
MacCtx.NvmCtx->DeviceClass = deviceClass;
|
|
|
|
MacCtx.RxWindowCConfig = MacCtx.RxWindow2Config;
|
|
MacCtx.RxWindowCConfig.RxSlot = RX_SLOT_WIN_CLASS_C;
|
|
|
|
for( int8_t i = 0; i < LORAMAC_MAX_MC_CTX; i++ )
|
|
{
|
|
if( MacCtx.NvmCtx->MulticastChannelList[i].ChannelParams.IsEnabled == true )
|
|
{
|
|
MacCtx.NvmCtx->MacParams.RxCChannel.Frequency = MacCtx.NvmCtx->MulticastChannelList[i].ChannelParams.RxParams.ClassC.Frequency;
|
|
MacCtx.NvmCtx->MacParams.RxCChannel.Datarate = MacCtx.NvmCtx->MulticastChannelList[i].ChannelParams.RxParams.ClassC.Datarate;
|
|
|
|
MacCtx.RxWindowCConfig.Channel = MacCtx.Channel;
|
|
MacCtx.RxWindowCConfig.Frequency = MacCtx.NvmCtx->MacParams.RxCChannel.Frequency;
|
|
MacCtx.RxWindowCConfig.DownlinkDwellTime = MacCtx.NvmCtx->MacParams.DownlinkDwellTime;
|
|
MacCtx.RxWindowCConfig.RepeaterSupport = MacCtx.NvmCtx->RepeaterSupport;
|
|
MacCtx.RxWindowCConfig.RxSlot = RX_SLOT_WIN_CLASS_C_MULTICAST;
|
|
MacCtx.RxWindowCConfig.RxContinuous = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Set the NodeAckRequested indicator to default
|
|
MacCtx.NodeAckRequested = false;
|
|
// Set the radio into sleep mode in case we are still in RX mode
|
|
Radio.Sleep( );
|
|
|
|
OpenContinuousRxCWindow( );
|
|
|
|
status = LORAMAC_STATUS_OK;
|
|
}
|
|
break;
|
|
}
|
|
case CLASS_B:
|
|
{
|
|
status = LoRaMacClassBSwitchClass( deviceClass );
|
|
if( status == LORAMAC_STATUS_OK )
|
|
{
|
|
MacCtx.NvmCtx->DeviceClass = deviceClass;
|
|
}
|
|
break;
|
|
}
|
|
case CLASS_C:
|
|
{
|
|
if( deviceClass == CLASS_A )
|
|
{
|
|
MacCtx.NvmCtx->DeviceClass = deviceClass;
|
|
|
|
// Set the radio into sleep to setup a defined state
|
|
Radio.Sleep( );
|
|
|
|
status = LORAMAC_STATUS_OK;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
static uint8_t GetMaxAppPayloadWithoutFOptsLength( int8_t datarate )
|
|
{
|
|
GetPhyParams_t getPhy;
|
|
PhyParam_t phyParam;
|
|
|
|
// Setup PHY request
|
|
getPhy.UplinkDwellTime = MacCtx.NvmCtx->MacParams.UplinkDwellTime;
|
|
getPhy.Datarate = datarate;
|
|
getPhy.Attribute = PHY_MAX_PAYLOAD;
|
|
|
|
// Get the maximum payload length
|
|
if( MacCtx.NvmCtx->RepeaterSupport == true )
|
|
{
|
|
getPhy.Attribute = PHY_MAX_PAYLOAD_REPEATER;
|
|
}
|
|
|
|
phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
|
|
|
|
return phyParam.Value;
|
|
}
|
|
|
|
static bool ValidatePayloadLength( uint8_t lenN, int8_t datarate, uint8_t fOptsLen )
|
|
{
|
|
uint16_t maxN = 0;
|
|
uint16_t payloadSize = 0;
|
|
|
|
maxN = GetMaxAppPayloadWithoutFOptsLength( datarate );
|
|
|
|
// Calculate the resulting payload size
|
|
payloadSize = ( lenN + fOptsLen );
|
|
|
|
// Validation of the application payload size
|
|
if( ( payloadSize <= maxN ) && ( payloadSize <= LORAMAC_PHY_MAXPAYLOAD ) )
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void SetMlmeScheduleUplinkIndication( void )
|
|
{
|
|
MacCtx.MacFlags.Bits.MlmeSchedUplinkInd = 1;
|
|
}
|
|
|
|
static void ProcessMacCommands( uint8_t *payload, uint8_t macIndex, uint8_t commandsSize, int8_t snr, LoRaMacRxSlot_t rxSlot )
|
|
{
|
|
uint8_t status = 0;
|
|
bool adrBlockFound = false;
|
|
uint8_t macCmdPayload[2] = { 0x00, 0x00 };
|
|
|
|
while( macIndex < commandsSize )
|
|
{
|
|
// Make sure to parse only complete MAC commands
|
|
if( ( LoRaMacCommandsGetCmdSize( payload[macIndex] ) + macIndex ) > commandsSize )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Decode Frame MAC commands
|
|
switch( payload[macIndex++] )
|
|
{
|
|
case SRV_MAC_LINK_CHECK_ANS:
|
|
{
|
|
if( LoRaMacConfirmQueueIsCmdActive( MLME_LINK_CHECK ) == true )
|
|
{
|
|
LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_LINK_CHECK );
|
|
MacCtx.MlmeConfirm.DemodMargin = payload[macIndex++];
|
|
MacCtx.MlmeConfirm.NbGateways = payload[macIndex++];
|
|
}
|
|
break;
|
|
}
|
|
case SRV_MAC_LINK_ADR_REQ:
|
|
{
|
|
LinkAdrReqParams_t linkAdrReq;
|
|
int8_t linkAdrDatarate = DR_0;
|
|
int8_t linkAdrTxPower = TX_POWER_0;
|
|
uint8_t linkAdrNbRep = 0;
|
|
uint8_t linkAdrNbBytesParsed = 0;
|
|
|
|
if( adrBlockFound == false )
|
|
{
|
|
adrBlockFound = true;
|
|
|
|
// Fill parameter structure
|
|
linkAdrReq.Payload = &payload[macIndex - 1];
|
|
linkAdrReq.PayloadSize = commandsSize - ( macIndex - 1 );
|
|
linkAdrReq.AdrEnabled = MacCtx.NvmCtx->AdrCtrlOn;
|
|
linkAdrReq.UplinkDwellTime = MacCtx.NvmCtx->MacParams.UplinkDwellTime;
|
|
linkAdrReq.CurrentDatarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate;
|
|
linkAdrReq.CurrentTxPower = MacCtx.NvmCtx->MacParams.ChannelsTxPower;
|
|
linkAdrReq.CurrentNbRep = MacCtx.NvmCtx->MacParams.ChannelsNbTrans;
|
|
linkAdrReq.Version = MacCtx.NvmCtx->Version;
|
|
|
|
// Process the ADR requests
|
|
status = RegionLinkAdrReq( MacCtx.NvmCtx->Region, &linkAdrReq, &linkAdrDatarate,
|
|
&linkAdrTxPower, &linkAdrNbRep, &linkAdrNbBytesParsed );
|
|
|
|
if( ( status & 0x07 ) == 0x07 )
|
|
{
|
|
MacCtx.NvmCtx->MacParams.ChannelsDatarate = linkAdrDatarate;
|
|
MacCtx.NvmCtx->MacParams.ChannelsTxPower = linkAdrTxPower;
|
|
MacCtx.NvmCtx->MacParams.ChannelsNbTrans = linkAdrNbRep;
|
|
}
|
|
|
|
// Add the answers to the buffer
|
|
for( uint8_t i = 0; i < ( linkAdrNbBytesParsed / 5 ); i++ )
|
|
{
|
|
LoRaMacCommandsAddCmd( MOTE_MAC_LINK_ADR_ANS, &status, 1 );
|
|
}
|
|
// Update MAC index
|
|
macIndex += linkAdrNbBytesParsed - 1;
|
|
}
|
|
break;
|
|
}
|
|
case SRV_MAC_DUTY_CYCLE_REQ:
|
|
{
|
|
MacCtx.NvmCtx->MaxDCycle = payload[macIndex++] & 0x0F;
|
|
MacCtx.NvmCtx->AggregatedDCycle = 1 << MacCtx.NvmCtx->MaxDCycle;
|
|
LoRaMacCommandsAddCmd( MOTE_MAC_DUTY_CYCLE_ANS, macCmdPayload, 0 );
|
|
break;
|
|
}
|
|
case SRV_MAC_RX_PARAM_SETUP_REQ:
|
|
{
|
|
RxParamSetupReqParams_t rxParamSetupReq;
|
|
status = 0x07;
|
|
|
|
rxParamSetupReq.DrOffset = ( payload[macIndex] >> 4 ) & 0x07;
|
|
rxParamSetupReq.Datarate = payload[macIndex] & 0x0F;
|
|
macIndex++;
|
|
|
|
rxParamSetupReq.Frequency = ( uint32_t ) payload[macIndex++];
|
|
rxParamSetupReq.Frequency |= ( uint32_t ) payload[macIndex++] << 8;
|
|
rxParamSetupReq.Frequency |= ( uint32_t ) payload[macIndex++] << 16;
|
|
rxParamSetupReq.Frequency *= 100;
|
|
|
|
// Perform request on region
|
|
status = RegionRxParamSetupReq( MacCtx.NvmCtx->Region, &rxParamSetupReq );
|
|
|
|
if( ( status & 0x07 ) == 0x07 )
|
|
{
|
|
MacCtx.NvmCtx->MacParams.Rx2Channel.Datarate = rxParamSetupReq.Datarate;
|
|
MacCtx.NvmCtx->MacParams.RxCChannel.Datarate = rxParamSetupReq.Datarate;
|
|
MacCtx.NvmCtx->MacParams.Rx2Channel.Frequency = rxParamSetupReq.Frequency;
|
|
MacCtx.NvmCtx->MacParams.RxCChannel.Frequency = rxParamSetupReq.Frequency;
|
|
MacCtx.NvmCtx->MacParams.Rx1DrOffset = rxParamSetupReq.DrOffset;
|
|
}
|
|
macCmdPayload[0] = status;
|
|
LoRaMacCommandsAddCmd( MOTE_MAC_RX_PARAM_SETUP_ANS, macCmdPayload, 1 );
|
|
// Setup indication to inform the application
|
|
SetMlmeScheduleUplinkIndication( );
|
|
break;
|
|
}
|
|
case SRV_MAC_DEV_STATUS_REQ:
|
|
{
|
|
uint8_t batteryLevel = BAT_LEVEL_NO_MEASURE;
|
|
if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->GetBatteryLevel != NULL ) )
|
|
{
|
|
batteryLevel = MacCtx.MacCallbacks->GetBatteryLevel( );
|
|
}
|
|
macCmdPayload[0] = batteryLevel;
|
|
macCmdPayload[1] = ( uint8_t )( snr & 0x3F );
|
|
LoRaMacCommandsAddCmd( MOTE_MAC_DEV_STATUS_ANS, macCmdPayload, 2 );
|
|
break;
|
|
}
|
|
case SRV_MAC_NEW_CHANNEL_REQ:
|
|
{
|
|
NewChannelReqParams_t newChannelReq;
|
|
ChannelParams_t chParam;
|
|
status = 0x03;
|
|
|
|
newChannelReq.ChannelId = payload[macIndex++];
|
|
newChannelReq.NewChannel = &chParam;
|
|
|
|
chParam.Frequency = ( uint32_t ) payload[macIndex++];
|
|
chParam.Frequency |= ( uint32_t ) payload[macIndex++] << 8;
|
|
chParam.Frequency |= ( uint32_t ) payload[macIndex++] << 16;
|
|
chParam.Frequency *= 100;
|
|
chParam.Rx1Frequency = 0;
|
|
chParam.DrRange.Value = payload[macIndex++];
|
|
|
|
status = RegionNewChannelReq( MacCtx.NvmCtx->Region, &newChannelReq );
|
|
|
|
macCmdPayload[0] = status;
|
|
LoRaMacCommandsAddCmd( MOTE_MAC_NEW_CHANNEL_ANS, macCmdPayload, 1 );
|
|
break;
|
|
}
|
|
case SRV_MAC_RX_TIMING_SETUP_REQ:
|
|
{
|
|
uint8_t delay = payload[macIndex++] & 0x0F;
|
|
|
|
if( delay == 0 )
|
|
{
|
|
delay++;
|
|
}
|
|
MacCtx.NvmCtx->MacParams.ReceiveDelay1 = delay * 1000;
|
|
MacCtx.NvmCtx->MacParams.ReceiveDelay2 = MacCtx.NvmCtx->MacParams.ReceiveDelay1 + 1000;
|
|
LoRaMacCommandsAddCmd( MOTE_MAC_RX_TIMING_SETUP_ANS, macCmdPayload, 0 );
|
|
// Setup indication to inform the application
|
|
SetMlmeScheduleUplinkIndication( );
|
|
break;
|
|
}
|
|
case SRV_MAC_TX_PARAM_SETUP_REQ:
|
|
{
|
|
TxParamSetupReqParams_t txParamSetupReq;
|
|
GetPhyParams_t getPhy;
|
|
PhyParam_t phyParam;
|
|
uint8_t eirpDwellTime = payload[macIndex++];
|
|
|
|
txParamSetupReq.UplinkDwellTime = 0;
|
|
txParamSetupReq.DownlinkDwellTime = 0;
|
|
|
|
if( ( eirpDwellTime & 0x20 ) == 0x20 )
|
|
{
|
|
txParamSetupReq.DownlinkDwellTime = 1;
|
|
}
|
|
if( ( eirpDwellTime & 0x10 ) == 0x10 )
|
|
{
|
|
txParamSetupReq.UplinkDwellTime = 1;
|
|
}
|
|
txParamSetupReq.MaxEirp = eirpDwellTime & 0x0F;
|
|
|
|
// Check the status for correctness
|
|
if( RegionTxParamSetupReq( MacCtx.NvmCtx->Region, &txParamSetupReq ) != -1 )
|
|
{
|
|
// Accept command
|
|
MacCtx.NvmCtx->MacParams.UplinkDwellTime = txParamSetupReq.UplinkDwellTime;
|
|
MacCtx.NvmCtx->MacParams.DownlinkDwellTime = txParamSetupReq.DownlinkDwellTime;
|
|
MacCtx.NvmCtx->MacParams.MaxEirp = LoRaMacMaxEirpTable[txParamSetupReq.MaxEirp];
|
|
// Update the datarate in case of the new configuration limits it
|
|
getPhy.Attribute = PHY_MIN_TX_DR;
|
|
getPhy.UplinkDwellTime = MacCtx.NvmCtx->MacParams.UplinkDwellTime;
|
|
phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
|
|
MacCtx.NvmCtx->MacParams.ChannelsDatarate = MAX( MacCtx.NvmCtx->MacParams.ChannelsDatarate, ( int8_t )phyParam.Value );
|
|
|
|
// Add command response
|
|
LoRaMacCommandsAddCmd( MOTE_MAC_TX_PARAM_SETUP_ANS, macCmdPayload, 0 );
|
|
}
|
|
break;
|
|
}
|
|
case SRV_MAC_DL_CHANNEL_REQ:
|
|
{
|
|
DlChannelReqParams_t dlChannelReq;
|
|
status = 0x03;
|
|
|
|
dlChannelReq.ChannelId = payload[macIndex++];
|
|
dlChannelReq.Rx1Frequency = ( uint32_t ) payload[macIndex++];
|
|
dlChannelReq.Rx1Frequency |= ( uint32_t ) payload[macIndex++] << 8;
|
|
dlChannelReq.Rx1Frequency |= ( uint32_t ) payload[macIndex++] << 16;
|
|
dlChannelReq.Rx1Frequency *= 100;
|
|
|
|
status = RegionDlChannelReq( MacCtx.NvmCtx->Region, &dlChannelReq );
|
|
macCmdPayload[0] = status;
|
|
LoRaMacCommandsAddCmd( MOTE_MAC_DL_CHANNEL_ANS, macCmdPayload, 1 );
|
|
// Setup indication to inform the application
|
|
SetMlmeScheduleUplinkIndication( );
|
|
break;
|
|
}
|
|
case SRV_MAC_DEVICE_TIME_ANS:
|
|
{
|
|
if( LoRaMacConfirmQueueIsCmdActive( MLME_DEVICE_TIME ) == true )
|
|
{
|
|
LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_DEVICE_TIME );
|
|
SysTime_t gpsEpochTime = { 0 };
|
|
SysTime_t sysTime = { 0 };
|
|
SysTime_t sysTimeCurrent = { 0 };
|
|
|
|
gpsEpochTime.Seconds = ( uint32_t )payload[macIndex++];
|
|
gpsEpochTime.Seconds |= ( uint32_t )payload[macIndex++] << 8;
|
|
gpsEpochTime.Seconds |= ( uint32_t )payload[macIndex++] << 16;
|
|
gpsEpochTime.Seconds |= ( uint32_t )payload[macIndex++] << 24;
|
|
gpsEpochTime.SubSeconds = payload[macIndex++];
|
|
|
|
// Convert the fractional second received in ms
|
|
// round( pow( 0.5, 8.0 ) * 1000 ) = 3.90625
|
|
gpsEpochTime.SubSeconds = ( int16_t )( ( ( int32_t )gpsEpochTime.SubSeconds * 1000 ) >> 8 );
|
|
|
|
// Copy received GPS Epoch time into system time
|
|
sysTime = gpsEpochTime;
|
|
// Add Unix to Gps epcoh offset. The system time is based on Unix time.
|
|
sysTime.Seconds += UNIX_GPS_EPOCH_OFFSET;
|
|
|
|
// Compensate time difference between Tx Done time and now
|
|
sysTimeCurrent = SysTimeGet( );
|
|
sysTime = SysTimeAdd( sysTimeCurrent, SysTimeSub( sysTime, MacCtx.LastTxSysTime ) );
|
|
|
|
// Apply the new system time.
|
|
SysTimeSet( sysTime );
|
|
LoRaMacClassBDeviceTimeAns( );
|
|
MacCtx.McpsIndication.DeviceTimeAnsReceived = true;
|
|
}
|
|
break;
|
|
}
|
|
case SRV_MAC_PING_SLOT_INFO_ANS:
|
|
{
|
|
if( LoRaMacConfirmQueueIsCmdActive( MLME_PING_SLOT_INFO ) == true )
|
|
{
|
|
LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_PING_SLOT_INFO );
|
|
// According to the specification, it is not allowed to process this answer in
|
|
// a ping or multicast slot
|
|
if( ( MacCtx.RxSlot != RX_SLOT_WIN_CLASS_B_PING_SLOT ) && ( MacCtx.RxSlot != RX_SLOT_WIN_CLASS_B_MULTICAST_SLOT ) )
|
|
{
|
|
LoRaMacClassBPingSlotInfoAns( );
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case SRV_MAC_PING_SLOT_CHANNEL_REQ:
|
|
{
|
|
uint8_t status = 0x03;
|
|
uint32_t frequency = 0;
|
|
uint8_t datarate;
|
|
|
|
frequency = ( uint32_t )payload[macIndex++];
|
|
frequency |= ( uint32_t )payload[macIndex++] << 8;
|
|
frequency |= ( uint32_t )payload[macIndex++] << 16;
|
|
frequency *= 100;
|
|
datarate = payload[macIndex++] & 0x0F;
|
|
|
|
status = LoRaMacClassBPingSlotChannelReq( datarate, frequency );
|
|
macCmdPayload[0] = status;
|
|
LoRaMacCommandsAddCmd( MOTE_MAC_PING_SLOT_FREQ_ANS, macCmdPayload, 1 );
|
|
break;
|
|
}
|
|
case SRV_MAC_BEACON_TIMING_ANS:
|
|
{
|
|
if( LoRaMacConfirmQueueIsCmdActive( MLME_BEACON_TIMING ) == true )
|
|
{
|
|
LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_BEACON_TIMING );
|
|
uint16_t beaconTimingDelay = 0;
|
|
uint8_t beaconTimingChannel = 0;
|
|
|
|
beaconTimingDelay = ( uint16_t )payload[macIndex++];
|
|
beaconTimingDelay |= ( uint16_t )payload[macIndex++] << 8;
|
|
beaconTimingChannel = payload[macIndex++];
|
|
|
|
LoRaMacClassBBeaconTimingAns( beaconTimingDelay, beaconTimingChannel, RxDoneParams.LastRxDone );
|
|
}
|
|
break;
|
|
}
|
|
case SRV_MAC_BEACON_FREQ_REQ:
|
|
{
|
|
uint32_t frequency = 0;
|
|
|
|
frequency = ( uint32_t )payload[macIndex++];
|
|
frequency |= ( uint32_t )payload[macIndex++] << 8;
|
|
frequency |= ( uint32_t )payload[macIndex++] << 16;
|
|
frequency *= 100;
|
|
|
|
if( LoRaMacClassBBeaconFreqReq( frequency ) == true )
|
|
{
|
|
macCmdPayload[0] = 1;
|
|
}
|
|
else
|
|
{
|
|
macCmdPayload[0] = 0;
|
|
}
|
|
LoRaMacCommandsAddCmd( MOTE_MAC_BEACON_FREQ_ANS, macCmdPayload, 1 );
|
|
}
|
|
break;
|
|
default:
|
|
// Unknown command. ABORT MAC commands processing
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static LoRaMacStatus_t Send( LoRaMacHeader_t* macHdr, uint8_t fPort, void* fBuffer, uint16_t fBufferSize, bool allowDelayedTx )
|
|
{
|
|
LoRaMacFrameCtrl_t fCtrl;
|
|
LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
int8_t datarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate;
|
|
int8_t txPower = MacCtx.NvmCtx->MacParams.ChannelsTxPower;
|
|
uint32_t adrAckCounter = MacCtx.NvmCtx->AdrAckCounter;
|
|
CalcNextAdrParams_t adrNext;
|
|
|
|
// Check if we are joined
|
|
if( MacCtx.NvmCtx->NetworkActivation == ACTIVATION_TYPE_NONE )
|
|
{
|
|
return LORAMAC_STATUS_NO_NETWORK_JOINED;
|
|
}
|
|
if( MacCtx.NvmCtx->MaxDCycle == 0 )
|
|
{
|
|
MacCtx.NvmCtx->AggregatedTimeOff = 0;
|
|
}
|
|
|
|
fCtrl.Value = 0;
|
|
fCtrl.Bits.FOptsLen = 0;
|
|
fCtrl.Bits.Adr = MacCtx.NvmCtx->AdrCtrlOn;
|
|
|
|
// Check class b
|
|
if( MacCtx.NvmCtx->DeviceClass == CLASS_B )
|
|
{
|
|
fCtrl.Bits.FPending = 1;
|
|
}
|
|
else
|
|
{
|
|
fCtrl.Bits.FPending = 0;
|
|
}
|
|
|
|
// Check server ack
|
|
if( MacCtx.NvmCtx->SrvAckRequested == true )
|
|
{
|
|
fCtrl.Bits.Ack = 1;
|
|
}
|
|
|
|
// ADR next request
|
|
adrNext.Version = MacCtx.NvmCtx->Version;
|
|
adrNext.UpdateChanMask = true;
|
|
adrNext.AdrEnabled = fCtrl.Bits.Adr;
|
|
adrNext.AdrAckCounter = MacCtx.NvmCtx->AdrAckCounter;
|
|
adrNext.AdrAckLimit = MacCtx.AdrAckLimit;
|
|
adrNext.AdrAckDelay = MacCtx.AdrAckDelay;
|
|
adrNext.Datarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate;
|
|
adrNext.TxPower = MacCtx.NvmCtx->MacParams.ChannelsTxPower;
|
|
adrNext.UplinkDwellTime = MacCtx.NvmCtx->MacParams.UplinkDwellTime;
|
|
adrNext.Region = MacCtx.NvmCtx->Region;
|
|
|
|
fCtrl.Bits.AdrAckReq = LoRaMacAdrCalcNext( &adrNext, &MacCtx.NvmCtx->MacParams.ChannelsDatarate,
|
|
&MacCtx.NvmCtx->MacParams.ChannelsTxPower, &adrAckCounter );
|
|
|
|
// Prepare the frame
|
|
status = PrepareFrame( macHdr, &fCtrl, fPort, fBuffer, fBufferSize );
|
|
|
|
// Validate status
|
|
if( ( status == LORAMAC_STATUS_OK ) || ( status == LORAMAC_STATUS_SKIPPED_APP_DATA ) )
|
|
{
|
|
// Schedule frame, do not allow delayed transmissions
|
|
status = ScheduleTx( allowDelayedTx );
|
|
}
|
|
|
|
// Post processing
|
|
if( status != LORAMAC_STATUS_OK )
|
|
{
|
|
// Bad case - restore
|
|
// Store local variables
|
|
MacCtx.NvmCtx->MacParams.ChannelsDatarate = datarate;
|
|
MacCtx.NvmCtx->MacParams.ChannelsTxPower = txPower;
|
|
}
|
|
else
|
|
{
|
|
// Good case
|
|
MacCtx.NvmCtx->SrvAckRequested = false;
|
|
MacCtx.NvmCtx->AdrAckCounter = adrAckCounter;
|
|
// Remove all none sticky MAC commands
|
|
if( LoRaMacCommandsRemoveNoneStickyCmds( ) != LORAMAC_COMMANDS_SUCCESS )
|
|
{
|
|
return LORAMAC_STATUS_MAC_COMMAD_ERROR;
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
static LoRaMacStatus_t SendReJoinReq( JoinReqIdentifier_t joinReqType )
|
|
{
|
|
LoRaMacStatus_t status = LORAMAC_STATUS_OK;
|
|
LoRaMacHeader_t macHdr;
|
|
macHdr.Value = 0;
|
|
bool allowDelayedTx = true;
|
|
|
|
// Setup join/rejoin message
|
|
switch( joinReqType )
|
|
{
|
|
case JOIN_REQ:
|
|
{
|
|
SwitchClass( CLASS_A );
|
|
|
|
MacCtx.TxMsg.Type = LORAMAC_MSG_TYPE_JOIN_REQUEST;
|
|
MacCtx.TxMsg.Message.JoinReq.Buffer = MacCtx.PktBuffer;
|
|
MacCtx.TxMsg.Message.JoinReq.BufSize = LORAMAC_PHY_MAXPAYLOAD;
|
|
|
|
macHdr.Bits.MType = FRAME_TYPE_JOIN_REQ;
|
|
MacCtx.TxMsg.Message.JoinReq.MHDR.Value = macHdr.Value;
|
|
|
|
memcpy1( MacCtx.TxMsg.Message.JoinReq.JoinEUI, SecureElementGetJoinEui( ), LORAMAC_JOIN_EUI_FIELD_SIZE );
|
|
memcpy1( MacCtx.TxMsg.Message.JoinReq.DevEUI, SecureElementGetDevEui( ), LORAMAC_DEV_EUI_FIELD_SIZE );
|
|
|
|
allowDelayedTx = false;
|
|
|
|
break;
|
|
}
|
|
default:
|
|
status = LORAMAC_STATUS_SERVICE_UNKNOWN;
|
|
break;
|
|
}
|
|
|
|
// Schedule frame
|
|
status = ScheduleTx( allowDelayedTx );
|
|
return status;
|
|
}
|
|
|
|
static LoRaMacStatus_t CheckForClassBCollision( void )
|
|
{
|
|
if( LoRaMacClassBIsBeaconExpected( ) == true )
|
|
{
|
|
return LORAMAC_STATUS_BUSY_BEACON_RESERVED_TIME;
|
|
}
|
|
|
|
if( MacCtx.NvmCtx->DeviceClass == CLASS_B )
|
|
{
|
|
if( LoRaMacClassBIsPingExpected( ) == true )
|
|
{
|
|
return LORAMAC_STATUS_BUSY_PING_SLOT_WINDOW_TIME;
|
|
}
|
|
else if( LoRaMacClassBIsMulticastExpected( ) == true )
|
|
{
|
|
return LORAMAC_STATUS_BUSY_PING_SLOT_WINDOW_TIME;
|
|
}
|
|
}
|
|
return LORAMAC_STATUS_OK;
|
|
}
|
|
|
|
static void ComputeRxWindowParameters( void )
|
|
{
|
|
// Compute Rx1 windows parameters
|
|
RegionComputeRxWindowParameters( MacCtx.NvmCtx->Region,
|
|
RegionApplyDrOffset( MacCtx.NvmCtx->Region,
|
|
MacCtx.NvmCtx->MacParams.DownlinkDwellTime,
|
|
MacCtx.NvmCtx->MacParams.ChannelsDatarate,
|
|
MacCtx.NvmCtx->MacParams.Rx1DrOffset ),
|
|
MacCtx.NvmCtx->MacParams.MinRxSymbols,
|
|
MacCtx.NvmCtx->MacParams.SystemMaxRxError,
|
|
&MacCtx.RxWindow1Config );
|
|
// Compute Rx2 windows parameters
|
|
RegionComputeRxWindowParameters( MacCtx.NvmCtx->Region,
|
|
MacCtx.NvmCtx->MacParams.Rx2Channel.Datarate,
|
|
MacCtx.NvmCtx->MacParams.MinRxSymbols,
|
|
MacCtx.NvmCtx->MacParams.SystemMaxRxError,
|
|
&MacCtx.RxWindow2Config );
|
|
|
|
// Default setup, in case the device joined
|
|
MacCtx.RxWindow1Delay = MacCtx.NvmCtx->MacParams.ReceiveDelay1 + MacCtx.RxWindow1Config.WindowOffset;
|
|
MacCtx.RxWindow2Delay = MacCtx.NvmCtx->MacParams.ReceiveDelay2 + MacCtx.RxWindow2Config.WindowOffset;
|
|
|
|
if( MacCtx.NvmCtx->NetworkActivation == ACTIVATION_TYPE_NONE )
|
|
{
|
|
MacCtx.RxWindow1Delay = MacCtx.NvmCtx->MacParams.JoinAcceptDelay1 + MacCtx.RxWindow1Config.WindowOffset;
|
|
MacCtx.RxWindow2Delay = MacCtx.NvmCtx->MacParams.JoinAcceptDelay2 + MacCtx.RxWindow2Config.WindowOffset;
|
|
}
|
|
}
|
|
|
|
static LoRaMacStatus_t VerifyTxFrame( void )
|
|
{
|
|
size_t macCmdsSize = 0;
|
|
|
|
if( MacCtx.NvmCtx->NetworkActivation != ACTIVATION_TYPE_NONE )
|
|
{
|
|
if( LoRaMacCommandsGetSizeSerializedCmds( &macCmdsSize ) != LORAMAC_COMMANDS_SUCCESS )
|
|
{
|
|
return LORAMAC_STATUS_MAC_COMMAD_ERROR;
|
|
}
|
|
|
|
if( ValidatePayloadLength( MacCtx.AppDataSize, MacCtx.NvmCtx->MacParams.ChannelsDatarate, macCmdsSize ) == false )
|
|
{
|
|
return LORAMAC_STATUS_LENGTH_ERROR;
|
|
}
|
|
}
|
|
return LORAMAC_STATUS_OK;
|
|
}
|
|
|
|
static LoRaMacStatus_t SerializeTxFrame( void )
|
|
{
|
|
LoRaMacSerializerStatus_t serializeStatus;
|
|
|
|
switch( MacCtx.TxMsg.Type )
|
|
{
|
|
case LORAMAC_MSG_TYPE_JOIN_REQUEST:
|
|
serializeStatus = LoRaMacSerializerJoinRequest( &MacCtx.TxMsg.Message.JoinReq );
|
|
if( LORAMAC_SERIALIZER_SUCCESS != serializeStatus )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
MacCtx.PktBufferLen = MacCtx.TxMsg.Message.JoinReq.BufSize;
|
|
break;
|
|
case LORAMAC_MSG_TYPE_DATA:
|
|
serializeStatus = LoRaMacSerializerData( &MacCtx.TxMsg.Message.Data );
|
|
if( LORAMAC_SERIALIZER_SUCCESS != serializeStatus )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
MacCtx.PktBufferLen = MacCtx.TxMsg.Message.Data.BufSize;
|
|
break;
|
|
case LORAMAC_MSG_TYPE_JOIN_ACCEPT:
|
|
case LORAMAC_MSG_TYPE_UNDEF:
|
|
default:
|
|
return LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
return LORAMAC_STATUS_OK;
|
|
}
|
|
|
|
static LoRaMacStatus_t ScheduleTx( bool allowDelayedTx )
|
|
{
|
|
LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
NextChanParams_t nextChan;
|
|
|
|
// Check class b collisions
|
|
status = CheckForClassBCollision( );
|
|
if( status != LORAMAC_STATUS_OK )
|
|
{
|
|
return status;
|
|
}
|
|
|
|
// Update back-off
|
|
CalculateBackOff( );
|
|
|
|
// Serialize frame
|
|
status = SerializeTxFrame( );
|
|
if( status != LORAMAC_STATUS_OK )
|
|
{
|
|
return status;
|
|
}
|
|
|
|
nextChan.AggrTimeOff = MacCtx.NvmCtx->AggregatedTimeOff;
|
|
nextChan.Datarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate;
|
|
nextChan.DutyCycleEnabled = MacCtx.NvmCtx->DutyCycleOn;
|
|
nextChan.ElapsedTimeSinceStartUp = SysTimeSub( SysTimeGetMcuTime( ), MacCtx.NvmCtx->InitializationTime );
|
|
nextChan.LastAggrTx = MacCtx.NvmCtx->LastTxDoneTime;
|
|
nextChan.LastTxIsJoinRequest = false;
|
|
nextChan.Joined = true;
|
|
nextChan.PktLen = MacCtx.PktBufferLen;
|
|
|
|
// Setup the parameters based on the join status
|
|
if( MacCtx.NvmCtx->NetworkActivation == ACTIVATION_TYPE_NONE )
|
|
{
|
|
nextChan.LastTxIsJoinRequest = true;
|
|
nextChan.Joined = false;
|
|
}
|
|
|
|
// Select channel
|
|
status = RegionNextChannel( MacCtx.NvmCtx->Region, &nextChan, &MacCtx.Channel, &MacCtx.DutyCycleWaitTime, &MacCtx.NvmCtx->AggregatedTimeOff );
|
|
|
|
if( status != LORAMAC_STATUS_OK )
|
|
{
|
|
if( ( status == LORAMAC_STATUS_DUTYCYCLE_RESTRICTED ) &&
|
|
( allowDelayedTx == true ) )
|
|
{
|
|
// Allow delayed transmissions. We have to allow it in case
|
|
// the MAC must retransmit a frame with the frame repetitions
|
|
if( MacCtx.DutyCycleWaitTime != 0 )
|
|
{// Send later - prepare timer
|
|
MacCtx.MacState |= LORAMAC_TX_DELAYED;
|
|
TimerSetValue( &MacCtx.TxDelayedTimer, MacCtx.DutyCycleWaitTime );
|
|
TimerStart( &MacCtx.TxDelayedTimer );
|
|
}
|
|
return LORAMAC_STATUS_OK;
|
|
}
|
|
else
|
|
{// State where the MAC cannot send a frame
|
|
return status;
|
|
}
|
|
}
|
|
|
|
// Compute window parameters, offsets, rx symbols, system errors etc.
|
|
ComputeRxWindowParameters( );
|
|
|
|
// Verify TX frame
|
|
status = VerifyTxFrame( );
|
|
if( status != LORAMAC_STATUS_OK )
|
|
{
|
|
return status;
|
|
}
|
|
|
|
// Try to send now
|
|
return SendFrameOnChannel( MacCtx.Channel );
|
|
}
|
|
|
|
static LoRaMacStatus_t SecureFrame( uint8_t txDr, uint8_t txCh )
|
|
{
|
|
LoRaMacCryptoStatus_t macCryptoStatus = LORAMAC_CRYPTO_ERROR;
|
|
uint32_t fCntUp = 0;
|
|
|
|
switch( MacCtx.TxMsg.Type )
|
|
{
|
|
case LORAMAC_MSG_TYPE_JOIN_REQUEST:
|
|
macCryptoStatus = LoRaMacCryptoPrepareJoinRequest( &MacCtx.TxMsg.Message.JoinReq );
|
|
if( LORAMAC_CRYPTO_SUCCESS != macCryptoStatus )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
MacCtx.PktBufferLen = MacCtx.TxMsg.Message.JoinReq.BufSize;
|
|
break;
|
|
case LORAMAC_MSG_TYPE_DATA:
|
|
|
|
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoGetFCntUp( &fCntUp ) )
|
|
{
|
|
return LORAMAC_STATUS_FCNT_HANDLER_ERROR;
|
|
}
|
|
|
|
if( ( MacCtx.ChannelsNbTransCounter >= 1 ) || ( MacCtx.AckTimeoutRetriesCounter > 1 ) )
|
|
{
|
|
fCntUp -= 1;
|
|
}
|
|
|
|
macCryptoStatus = LoRaMacCryptoSecureMessage( fCntUp, txDr, txCh, &MacCtx.TxMsg.Message.Data );
|
|
if( LORAMAC_CRYPTO_SUCCESS != macCryptoStatus )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
MacCtx.PktBufferLen = MacCtx.TxMsg.Message.Data.BufSize;
|
|
break;
|
|
case LORAMAC_MSG_TYPE_JOIN_ACCEPT:
|
|
case LORAMAC_MSG_TYPE_UNDEF:
|
|
default:
|
|
return LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
return LORAMAC_STATUS_OK;
|
|
}
|
|
|
|
static void CalculateBackOff( void )
|
|
{
|
|
// Make sure that the calculation of the backoff time for the aggregated time off will only be done in
|
|
// case the value is zero. It will be set to zero in the function RegionNextChannel.
|
|
if( MacCtx.NvmCtx->AggregatedTimeOff == 0 )
|
|
{
|
|
// Update aggregated time-off. This must be an assignment and no incremental
|
|
// update as we do only calculate the time-off based on the last transmission
|
|
MacCtx.NvmCtx->AggregatedTimeOff = ( MacCtx.TxTimeOnAir * MacCtx.NvmCtx->AggregatedDCycle - MacCtx.TxTimeOnAir );
|
|
}
|
|
}
|
|
|
|
static void RemoveMacCommands( LoRaMacRxSlot_t rxSlot, LoRaMacFrameCtrl_t fCtrl, Mcps_t request )
|
|
{
|
|
if( rxSlot == RX_SLOT_WIN_1 || rxSlot == RX_SLOT_WIN_2 )
|
|
{
|
|
// Remove all sticky MAC commands answers since we can assume
|
|
// that they have been received by the server.
|
|
if( request == MCPS_CONFIRMED )
|
|
{
|
|
if( fCtrl.Bits.Ack == 1 )
|
|
{ // For confirmed uplinks only if we have received an ACK.
|
|
LoRaMacCommandsRemoveStickyAnsCmds( );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LoRaMacCommandsRemoveStickyAnsCmds( );
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ResetMacParameters( void )
|
|
{
|
|
MacCtx.NvmCtx->NetworkActivation = ACTIVATION_TYPE_NONE;
|
|
|
|
// ADR counter
|
|
MacCtx.NvmCtx->AdrAckCounter = 0;
|
|
|
|
MacCtx.ChannelsNbTransCounter = 0;
|
|
MacCtx.AckTimeoutRetries = 1;
|
|
MacCtx.AckTimeoutRetriesCounter = 1;
|
|
MacCtx.AckTimeoutRetry = false;
|
|
|
|
MacCtx.NvmCtx->MaxDCycle = 0;
|
|
MacCtx.NvmCtx->AggregatedDCycle = 1;
|
|
|
|
MacCtx.NvmCtx->MacParams.ChannelsTxPower = MacCtx.NvmCtx->MacParamsDefaults.ChannelsTxPower;
|
|
MacCtx.NvmCtx->MacParams.ChannelsDatarate = MacCtx.NvmCtx->MacParamsDefaults.ChannelsDatarate;
|
|
MacCtx.NvmCtx->MacParams.Rx1DrOffset = MacCtx.NvmCtx->MacParamsDefaults.Rx1DrOffset;
|
|
MacCtx.NvmCtx->MacParams.Rx2Channel = MacCtx.NvmCtx->MacParamsDefaults.Rx2Channel;
|
|
MacCtx.NvmCtx->MacParams.RxCChannel = MacCtx.NvmCtx->MacParamsDefaults.RxCChannel;
|
|
MacCtx.NvmCtx->MacParams.UplinkDwellTime = MacCtx.NvmCtx->MacParamsDefaults.UplinkDwellTime;
|
|
MacCtx.NvmCtx->MacParams.DownlinkDwellTime = MacCtx.NvmCtx->MacParamsDefaults.DownlinkDwellTime;
|
|
MacCtx.NvmCtx->MacParams.MaxEirp = MacCtx.NvmCtx->MacParamsDefaults.MaxEirp;
|
|
MacCtx.NvmCtx->MacParams.AntennaGain = MacCtx.NvmCtx->MacParamsDefaults.AntennaGain;
|
|
|
|
MacCtx.NodeAckRequested = false;
|
|
MacCtx.NvmCtx->SrvAckRequested = false;
|
|
|
|
// Reset to application defaults
|
|
InitDefaultsParams_t params;
|
|
params.Type = INIT_TYPE_RESET_TO_DEFAULT_CHANNELS;
|
|
params.NvmCtx = NULL;
|
|
RegionInitDefaults( MacCtx.NvmCtx->Region, ¶ms );
|
|
|
|
// Initialize channel index.
|
|
MacCtx.Channel = 0;
|
|
|
|
// Initialize Rx2 config parameters.
|
|
MacCtx.RxWindow2Config.Channel = MacCtx.Channel;
|
|
MacCtx.RxWindow2Config.Frequency = MacCtx.NvmCtx->MacParams.Rx2Channel.Frequency;
|
|
MacCtx.RxWindow2Config.DownlinkDwellTime = MacCtx.NvmCtx->MacParams.DownlinkDwellTime;
|
|
MacCtx.RxWindow2Config.RepeaterSupport = MacCtx.NvmCtx->RepeaterSupport;
|
|
MacCtx.RxWindow2Config.RxContinuous = false;
|
|
MacCtx.RxWindow2Config.RxSlot = RX_SLOT_WIN_2;
|
|
|
|
// Initialize RxC config parameters.
|
|
MacCtx.RxWindowCConfig = MacCtx.RxWindow2Config;
|
|
MacCtx.RxWindowCConfig.RxContinuous = true;
|
|
MacCtx.RxWindowCConfig.RxSlot = RX_SLOT_WIN_CLASS_C;
|
|
|
|
}
|
|
|
|
/*!
|
|
* \brief Initializes and opens the reception window
|
|
*
|
|
* \param [IN] rxTimer Window timer to be topped.
|
|
* \param [IN] rxConfig Window parameters to be setup
|
|
*/
|
|
static void RxWindowSetup( TimerEvent_t* rxTimer, RxConfigParams_t* rxConfig )
|
|
{
|
|
TimerStop( rxTimer );
|
|
|
|
// Ensure the radio is Idle
|
|
Radio.Standby( );
|
|
|
|
if( RegionRxConfig( MacCtx.NvmCtx->Region, rxConfig, ( int8_t* )&MacCtx.McpsIndication.RxDatarate ) == true )
|
|
{
|
|
Radio.Rx( MacCtx.NvmCtx->MacParams.MaxRxWindow );
|
|
MacCtx.RxSlot = rxConfig->RxSlot;
|
|
}
|
|
}
|
|
|
|
static void OpenContinuousRxCWindow( void )
|
|
{
|
|
// Compute RxC windows parameters
|
|
RegionComputeRxWindowParameters( MacCtx.NvmCtx->Region,
|
|
MacCtx.NvmCtx->MacParams.RxCChannel.Datarate,
|
|
MacCtx.NvmCtx->MacParams.MinRxSymbols,
|
|
MacCtx.NvmCtx->MacParams.SystemMaxRxError,
|
|
&MacCtx.RxWindowCConfig );
|
|
|
|
MacCtx.RxWindowCConfig.RxSlot = RX_SLOT_WIN_CLASS_C;
|
|
// Setup continuous listening
|
|
MacCtx.RxWindowCConfig.RxContinuous = true;
|
|
|
|
// At this point the Radio should be idle.
|
|
// Thus, there is no need to set the radio in standby mode.
|
|
if( RegionRxConfig( MacCtx.NvmCtx->Region, &MacCtx.RxWindowCConfig, ( int8_t* )&MacCtx.McpsIndication.RxDatarate ) == true )
|
|
{
|
|
Radio.Rx( 0 ); // Continuous mode
|
|
MacCtx.RxSlot = MacCtx.RxWindowCConfig.RxSlot;
|
|
}
|
|
}
|
|
|
|
static LoRaMacStatus_t PrepareFrame( LoRaMacHeader_t* macHdr, LoRaMacFrameCtrl_t* fCtrl, uint8_t fPort, void* fBuffer, uint16_t fBufferSize )
|
|
{
|
|
MacCtx.PktBufferLen = 0;
|
|
MacCtx.NodeAckRequested = false;
|
|
uint32_t fCntUp = 0;
|
|
size_t macCmdsSize = 0;
|
|
uint8_t availableSize = 0;
|
|
|
|
if( fBuffer == NULL )
|
|
{
|
|
fBufferSize = 0;
|
|
}
|
|
|
|
memcpy1( MacCtx.AppData, ( uint8_t* ) fBuffer, fBufferSize );
|
|
MacCtx.AppDataSize = fBufferSize;
|
|
MacCtx.PktBuffer[0] = macHdr->Value;
|
|
|
|
switch( macHdr->Bits.MType )
|
|
{
|
|
case FRAME_TYPE_DATA_CONFIRMED_UP:
|
|
MacCtx.NodeAckRequested = true;
|
|
// Intentional fall through
|
|
case FRAME_TYPE_DATA_UNCONFIRMED_UP:
|
|
MacCtx.TxMsg.Type = LORAMAC_MSG_TYPE_DATA;
|
|
MacCtx.TxMsg.Message.Data.Buffer = MacCtx.PktBuffer;
|
|
MacCtx.TxMsg.Message.Data.BufSize = LORAMAC_PHY_MAXPAYLOAD;
|
|
MacCtx.TxMsg.Message.Data.MHDR.Value = macHdr->Value;
|
|
MacCtx.TxMsg.Message.Data.FPort = fPort;
|
|
MacCtx.TxMsg.Message.Data.FHDR.DevAddr = MacCtx.NvmCtx->DevAddr;
|
|
MacCtx.TxMsg.Message.Data.FHDR.FCtrl.Value = fCtrl->Value;
|
|
MacCtx.TxMsg.Message.Data.FRMPayloadSize = MacCtx.AppDataSize;
|
|
MacCtx.TxMsg.Message.Data.FRMPayload = MacCtx.AppData;
|
|
|
|
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoGetFCntUp( &fCntUp ) )
|
|
{
|
|
return LORAMAC_STATUS_FCNT_HANDLER_ERROR;
|
|
}
|
|
MacCtx.TxMsg.Message.Data.FHDR.FCnt = ( uint16_t )fCntUp;
|
|
|
|
// Reset confirm parameters
|
|
MacCtx.McpsConfirm.NbRetries = 0;
|
|
MacCtx.McpsConfirm.AckReceived = false;
|
|
MacCtx.McpsConfirm.UpLinkCounter = fCntUp;
|
|
|
|
// Handle the MAC commands if there are any available
|
|
if( LoRaMacCommandsGetSizeSerializedCmds( &macCmdsSize ) != LORAMAC_COMMANDS_SUCCESS )
|
|
{
|
|
return LORAMAC_STATUS_MAC_COMMAD_ERROR;
|
|
}
|
|
|
|
if( macCmdsSize > 0 )
|
|
{
|
|
availableSize = GetMaxAppPayloadWithoutFOptsLength( MacCtx.NvmCtx->MacParams.ChannelsDatarate );
|
|
|
|
// There is application payload available and the MAC commands fit into FOpts field.
|
|
if( ( MacCtx.AppDataSize > 0 ) && ( macCmdsSize <= LORA_MAC_COMMAND_MAX_FOPTS_LENGTH ) )
|
|
{
|
|
if( LoRaMacCommandsSerializeCmds( LORA_MAC_COMMAND_MAX_FOPTS_LENGTH, &macCmdsSize, MacCtx.TxMsg.Message.Data.FHDR.FOpts ) != LORAMAC_COMMANDS_SUCCESS )
|
|
{
|
|
return LORAMAC_STATUS_MAC_COMMAD_ERROR;
|
|
}
|
|
fCtrl->Bits.FOptsLen = macCmdsSize;
|
|
// Update FCtrl field with new value of FOptionsLength
|
|
MacCtx.TxMsg.Message.Data.FHDR.FCtrl.Value = fCtrl->Value;
|
|
}
|
|
// There is application payload available but the MAC commands does NOT fit into FOpts field.
|
|
else if( ( MacCtx.AppDataSize > 0 ) && ( macCmdsSize > LORA_MAC_COMMAND_MAX_FOPTS_LENGTH ) )
|
|
{
|
|
|
|
if( LoRaMacCommandsSerializeCmds( availableSize, &macCmdsSize, MacCtx.NvmCtx->MacCommandsBuffer ) != LORAMAC_COMMANDS_SUCCESS )
|
|
{
|
|
return LORAMAC_STATUS_MAC_COMMAD_ERROR;
|
|
}
|
|
return LORAMAC_STATUS_SKIPPED_APP_DATA;
|
|
}
|
|
// No application payload available therefore add all mac commands to the FRMPayload.
|
|
else
|
|
{
|
|
if( LoRaMacCommandsSerializeCmds( availableSize, &macCmdsSize, MacCtx.NvmCtx->MacCommandsBuffer ) != LORAMAC_COMMANDS_SUCCESS )
|
|
{
|
|
return LORAMAC_STATUS_MAC_COMMAD_ERROR;
|
|
}
|
|
// Force FPort to be zero
|
|
MacCtx.TxMsg.Message.Data.FPort = 0;
|
|
|
|
MacCtx.TxMsg.Message.Data.FRMPayload = MacCtx.NvmCtx->MacCommandsBuffer;
|
|
MacCtx.TxMsg.Message.Data.FRMPayloadSize = macCmdsSize;
|
|
}
|
|
}
|
|
|
|
break;
|
|
case FRAME_TYPE_PROPRIETARY:
|
|
if( ( fBuffer != NULL ) && ( MacCtx.AppDataSize > 0 ) )
|
|
{
|
|
memcpy1( MacCtx.PktBuffer + LORAMAC_MHDR_FIELD_SIZE, ( uint8_t* ) fBuffer, MacCtx.AppDataSize );
|
|
MacCtx.PktBufferLen = LORAMAC_MHDR_FIELD_SIZE + MacCtx.AppDataSize;
|
|
}
|
|
break;
|
|
default:
|
|
return LORAMAC_STATUS_SERVICE_UNKNOWN;
|
|
}
|
|
|
|
return LORAMAC_STATUS_OK;
|
|
}
|
|
|
|
static LoRaMacStatus_t SendFrameOnChannel( uint8_t channel )
|
|
{
|
|
LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
TxConfigParams_t txConfig;
|
|
int8_t txPower = 0;
|
|
|
|
txConfig.Channel = channel;
|
|
txConfig.Datarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate;
|
|
txConfig.TxPower = MacCtx.NvmCtx->MacParams.ChannelsTxPower;
|
|
txConfig.MaxEirp = MacCtx.NvmCtx->MacParams.MaxEirp;
|
|
txConfig.AntennaGain = MacCtx.NvmCtx->MacParams.AntennaGain;
|
|
txConfig.PktLen = MacCtx.PktBufferLen;
|
|
|
|
RegionTxConfig( MacCtx.NvmCtx->Region, &txConfig, &txPower, &MacCtx.TxTimeOnAir );
|
|
|
|
MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
|
|
MacCtx.McpsConfirm.Datarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate;
|
|
MacCtx.McpsConfirm.TxPower = txPower;
|
|
MacCtx.McpsConfirm.Channel = channel;
|
|
|
|
// Store the time on air
|
|
MacCtx.McpsConfirm.TxTimeOnAir = MacCtx.TxTimeOnAir;
|
|
MacCtx.MlmeConfirm.TxTimeOnAir = MacCtx.TxTimeOnAir;
|
|
|
|
if( LoRaMacClassBIsBeaconModeActive( ) == true )
|
|
{
|
|
// Currently, the Time-On-Air can only be computed when the radio is configured with
|
|
// the TX configuration
|
|
TimerTime_t collisionTime = LoRaMacClassBIsUplinkCollision( MacCtx.TxTimeOnAir );
|
|
|
|
if( collisionTime > 0 )
|
|
{
|
|
return LORAMAC_STATUS_BUSY_UPLINK_COLLISION;
|
|
}
|
|
}
|
|
|
|
if( MacCtx.NvmCtx->DeviceClass == CLASS_B )
|
|
{
|
|
// Stop slots for class b
|
|
LoRaMacClassBStopRxSlots( );
|
|
}
|
|
|
|
LoRaMacClassBHaltBeaconing( );
|
|
|
|
// Secure frame
|
|
status = SecureFrame( MacCtx.NvmCtx->MacParams.ChannelsDatarate, MacCtx.Channel );
|
|
if( status != LORAMAC_STATUS_OK )
|
|
{
|
|
return status;
|
|
}
|
|
|
|
MacCtx.MacState |= LORAMAC_TX_RUNNING;
|
|
if( MacCtx.NodeAckRequested == false )
|
|
{
|
|
MacCtx.ChannelsNbTransCounter++;
|
|
}
|
|
|
|
// Send now
|
|
Radio.Send( MacCtx.PktBuffer, MacCtx.PktBufferLen );
|
|
|
|
return LORAMAC_STATUS_OK;
|
|
}
|
|
|
|
static LoRaMacStatus_t SetTxContinuousWave( uint16_t timeout )
|
|
{
|
|
ContinuousWaveParams_t continuousWave;
|
|
|
|
continuousWave.Channel = MacCtx.Channel;
|
|
continuousWave.Datarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate;
|
|
continuousWave.TxPower = MacCtx.NvmCtx->MacParams.ChannelsTxPower;
|
|
continuousWave.MaxEirp = MacCtx.NvmCtx->MacParams.MaxEirp;
|
|
continuousWave.AntennaGain = MacCtx.NvmCtx->MacParams.AntennaGain;
|
|
continuousWave.Timeout = timeout;
|
|
|
|
RegionSetContinuousWave( MacCtx.NvmCtx->Region, &continuousWave );
|
|
|
|
MacCtx.MacState |= LORAMAC_TX_RUNNING;
|
|
|
|
return LORAMAC_STATUS_OK;
|
|
}
|
|
|
|
static LoRaMacStatus_t SetTxContinuousWave1( uint16_t timeout, uint32_t frequency, uint8_t power )
|
|
{
|
|
Radio.SetTxContinuousWave( frequency, power, timeout );
|
|
|
|
MacCtx.MacState |= LORAMAC_TX_RUNNING;
|
|
|
|
return LORAMAC_STATUS_OK;
|
|
}
|
|
|
|
static LoRaMacCtxs_t* GetCtxs( void )
|
|
{
|
|
Contexts.MacNvmCtx = &NvmMacCtx;
|
|
Contexts.MacNvmCtxSize = sizeof( NvmMacCtx );
|
|
Contexts.CryptoNvmCtx = LoRaMacCryptoGetNvmCtx( &Contexts.CryptoNvmCtxSize );
|
|
GetNvmCtxParams_t params ={ 0 };
|
|
Contexts.RegionNvmCtx = RegionGetNvmCtx( MacCtx.NvmCtx->Region, ¶ms );
|
|
Contexts.RegionNvmCtxSize = params.nvmCtxSize;
|
|
Contexts.SecureElementNvmCtx = SecureElementGetNvmCtx( &Contexts.SecureElementNvmCtxSize );
|
|
Contexts.CommandsNvmCtx = LoRaMacCommandsGetNvmCtx( &Contexts.CommandsNvmCtxSize );
|
|
Contexts.ClassBNvmCtx = LoRaMacClassBGetNvmCtx( &Contexts.ClassBNvmCtxSize );
|
|
Contexts.ConfirmQueueNvmCtx = LoRaMacConfirmQueueGetNvmCtx( &Contexts.ConfirmQueueNvmCtxSize );
|
|
return &Contexts;
|
|
}
|
|
|
|
static LoRaMacStatus_t RestoreCtxs( LoRaMacCtxs_t* contexts )
|
|
{
|
|
if( contexts == NULL )
|
|
{
|
|
return LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
if( MacCtx.MacState != LORAMAC_STOPPED )
|
|
{
|
|
return LORAMAC_STATUS_BUSY;
|
|
}
|
|
|
|
if( contexts->MacNvmCtx != NULL )
|
|
{
|
|
memcpy1( ( uint8_t* ) &NvmMacCtx, ( uint8_t* ) contexts->MacNvmCtx, contexts->MacNvmCtxSize );
|
|
}
|
|
|
|
InitDefaultsParams_t params;
|
|
params.Type = INIT_TYPE_RESTORE_CTX;
|
|
params.NvmCtx = contexts->RegionNvmCtx;
|
|
RegionInitDefaults( MacCtx.NvmCtx->Region, ¶ms );
|
|
|
|
// Initialize RxC config parameters.
|
|
MacCtx.RxWindowCConfig.Channel = MacCtx.Channel;
|
|
MacCtx.RxWindowCConfig.Frequency = MacCtx.NvmCtx->MacParams.RxCChannel.Frequency;
|
|
MacCtx.RxWindowCConfig.DownlinkDwellTime = MacCtx.NvmCtx->MacParams.DownlinkDwellTime;
|
|
MacCtx.RxWindowCConfig.RepeaterSupport = MacCtx.NvmCtx->RepeaterSupport;
|
|
MacCtx.RxWindowCConfig.RxContinuous = true;
|
|
MacCtx.RxWindowCConfig.RxSlot = RX_SLOT_WIN_CLASS_C;
|
|
|
|
if( SecureElementRestoreNvmCtx( contexts->SecureElementNvmCtx ) != SECURE_ELEMENT_SUCCESS )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
|
|
if( LoRaMacCryptoRestoreNvmCtx( contexts->CryptoNvmCtx ) != LORAMAC_CRYPTO_SUCCESS )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
|
|
if( LoRaMacCommandsRestoreNvmCtx( contexts->CommandsNvmCtx ) != LORAMAC_COMMANDS_SUCCESS )
|
|
{
|
|
return LORAMAC_STATUS_MAC_COMMAD_ERROR;
|
|
}
|
|
|
|
if( LoRaMacClassBRestoreNvmCtx( contexts->ClassBNvmCtx ) != true )
|
|
{
|
|
return LORAMAC_STATUS_CLASS_B_ERROR;
|
|
}
|
|
|
|
if( LoRaMacConfirmQueueRestoreNvmCtx( contexts->ConfirmQueueNvmCtx ) != true )
|
|
{
|
|
return LORAMAC_STATUS_CONFIRM_QUEUE_ERROR;
|
|
}
|
|
|
|
return LORAMAC_STATUS_OK;
|
|
}
|
|
|
|
static LoRaMacStatus_t DetermineFrameType( LoRaMacMessageData_t* macMsg, FType_t* fType )
|
|
{
|
|
if( ( macMsg == NULL ) || ( fType == NULL ) )
|
|
{
|
|
return LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
|
|
/* The LoRaWAN specification allows several possible configurations how data up/down frames are built up.
|
|
* In sake of clearness the following naming is applied. Please keep in mind that this is
|
|
* implementation specific since there is no definition in the LoRaWAN specification included.
|
|
*
|
|
* X -> Field is available
|
|
* - -> Field is not available
|
|
*
|
|
* +-------+ +----------+------+-------+--------------+
|
|
* | FType | | FOptsLen | Fopt | FPort | FRMPayload |
|
|
* +-------+ +----------+------+-------+--------------+
|
|
* | A | | > 0 | X | > 0 | X |
|
|
* +-------+ +----------+------+-------+--------------+
|
|
* | B | | >= 0 | X/- | - | - |
|
|
* +-------+ +----------+------+-------+--------------+
|
|
* | C | | = 0 | - | = 0 | MAC commands |
|
|
* +-------+ +----------+------+-------+--------------+
|
|
* | D | | = 0 | - | > 0 | X |
|
|
* +-------+ +----------+------+-------+--------------+
|
|
*/
|
|
|
|
if( ( macMsg->FHDR.FCtrl.Bits.FOptsLen > 0 ) && ( macMsg->FPort > 0 ) )
|
|
{
|
|
*fType = FRAME_TYPE_A;
|
|
}
|
|
else if( macMsg->FRMPayloadSize == 0 )
|
|
{
|
|
*fType = FRAME_TYPE_B;
|
|
}
|
|
else if( ( macMsg->FHDR.FCtrl.Bits.FOptsLen == 0 ) && ( macMsg->FPort == 0 ) )
|
|
{
|
|
*fType = FRAME_TYPE_C;
|
|
}
|
|
else if( ( macMsg->FHDR.FCtrl.Bits.FOptsLen == 0 ) && ( macMsg->FPort > 0 ) )
|
|
{
|
|
*fType = FRAME_TYPE_D;
|
|
}
|
|
else
|
|
{
|
|
// Should never happen.
|
|
return LORAMAC_STATUS_ERROR;
|
|
}
|
|
|
|
return LORAMAC_STATUS_OK;
|
|
}
|
|
|
|
static bool CheckRetransUnconfirmedUplink( void )
|
|
{
|
|
// Unconfirmed uplink, when all retransmissions are done.
|
|
if( MacCtx.ChannelsNbTransCounter >=
|
|
MacCtx.NvmCtx->MacParams.ChannelsNbTrans )
|
|
{
|
|
return true;
|
|
}
|
|
else if( MacCtx.MacFlags.Bits.McpsInd == 1 )
|
|
{
|
|
// For Class A stop in each case
|
|
if( MacCtx.NvmCtx->DeviceClass == CLASS_A )
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{// For Class B & C stop only if the frame was received in RX1 window
|
|
if( MacCtx.McpsIndication.RxSlot == RX_SLOT_WIN_1 )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool CheckRetransConfirmedUplink( void )
|
|
{
|
|
// Confirmed uplink, when all retransmissions ( tries to get a ack ) are done.
|
|
if( MacCtx.AckTimeoutRetriesCounter >=
|
|
MacCtx.AckTimeoutRetries )
|
|
{
|
|
return true;
|
|
}
|
|
else if( MacCtx.MacFlags.Bits.McpsInd == 1 )
|
|
{
|
|
if( MacCtx.McpsConfirm.AckReceived == true )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool StopRetransmission( void )
|
|
{
|
|
if( ( MacCtx.MacFlags.Bits.McpsInd == 0 ) ||
|
|
( ( MacCtx.McpsIndication.RxSlot != RX_SLOT_WIN_1 ) &&
|
|
( MacCtx.McpsIndication.RxSlot != RX_SLOT_WIN_2 ) ) )
|
|
{ // Maximum repetitions without downlink. Increase ADR Ack counter.
|
|
// Only process the case when the MAC did not receive a downlink.
|
|
if( MacCtx.NvmCtx->AdrCtrlOn == true )
|
|
{
|
|
MacCtx.NvmCtx->AdrAckCounter++;
|
|
}
|
|
}
|
|
|
|
MacCtx.ChannelsNbTransCounter = 0;
|
|
MacCtx.NodeAckRequested = false;
|
|
MacCtx.AckTimeoutRetry = false;
|
|
MacCtx.MacState &= ~LORAMAC_TX_RUNNING;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void AckTimeoutRetriesProcess( void )
|
|
{
|
|
if( MacCtx.AckTimeoutRetriesCounter < MacCtx.AckTimeoutRetries )
|
|
{
|
|
MacCtx.AckTimeoutRetriesCounter++;
|
|
if( ( MacCtx.AckTimeoutRetriesCounter % 2 ) == 1 )
|
|
{
|
|
GetPhyParams_t getPhy;
|
|
PhyParam_t phyParam;
|
|
|
|
getPhy.Attribute = PHY_NEXT_LOWER_TX_DR;
|
|
getPhy.UplinkDwellTime = MacCtx.NvmCtx->MacParams.UplinkDwellTime;
|
|
getPhy.Datarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate;
|
|
phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
|
|
MacCtx.NvmCtx->MacParams.ChannelsDatarate = phyParam.Value;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void AckTimeoutRetriesFinalize( void )
|
|
{
|
|
if( MacCtx.McpsConfirm.AckReceived == false )
|
|
{
|
|
InitDefaultsParams_t params;
|
|
params.Type = INIT_TYPE_ACTIVATE_DEFAULT_CHANNELS;
|
|
params.NvmCtx = Contexts.RegionNvmCtx;
|
|
RegionInitDefaults( MacCtx.NvmCtx->Region, ¶ms );
|
|
|
|
MacCtx.NodeAckRequested = false;
|
|
MacCtx.McpsConfirm.AckReceived = false;
|
|
}
|
|
MacCtx.McpsConfirm.NbRetries = MacCtx.AckTimeoutRetriesCounter;
|
|
}
|
|
|
|
static void CallNvmCtxCallback( LoRaMacNvmCtxModule_t module )
|
|
{
|
|
if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->NvmContextChange != NULL ) )
|
|
{
|
|
MacCtx.MacCallbacks->NvmContextChange( module );
|
|
}
|
|
}
|
|
|
|
static void EventMacNvmCtxChanged( void )
|
|
{
|
|
CallNvmCtxCallback( LORAMAC_NVMCTXMODULE_MAC );
|
|
}
|
|
|
|
static void EventRegionNvmCtxChanged( void )
|
|
{
|
|
CallNvmCtxCallback( LORAMAC_NVMCTXMODULE_REGION );
|
|
}
|
|
|
|
static void EventCryptoNvmCtxChanged( void )
|
|
{
|
|
CallNvmCtxCallback( LORAMAC_NVMCTXMODULE_CRYPTO );
|
|
}
|
|
|
|
static void EventSecureElementNvmCtxChanged( void )
|
|
{
|
|
CallNvmCtxCallback( LORAMAC_NVMCTXMODULE_SECURE_ELEMENT );
|
|
}
|
|
|
|
static void EventCommandsNvmCtxChanged( void )
|
|
{
|
|
CallNvmCtxCallback( LORAMAC_NVMCTXMODULE_COMMANDS );
|
|
}
|
|
|
|
static void EventClassBNvmCtxChanged( void )
|
|
{
|
|
CallNvmCtxCallback( LORAMAC_NVMCTXMODULE_CLASS_B );
|
|
}
|
|
|
|
static void EventConfirmQueueNvmCtxChanged( void )
|
|
{
|
|
CallNvmCtxCallback( LORAMAC_NVMCTXMODULE_CONFIRM_QUEUE );
|
|
}
|
|
|
|
static uint8_t IsRequestPending( void )
|
|
{
|
|
if( ( MacCtx.MacFlags.Bits.MlmeReq == 1 ) ||
|
|
( MacCtx.MacFlags.Bits.McpsReq == 1 ) )
|
|
{
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Exported functions ---------------------------------------------------------*/
|
|
bool LoRaMacIsBusy( void )
|
|
{
|
|
if( ( MacCtx.MacState == LORAMAC_IDLE ) &&
|
|
( MacCtx.AllowRequests == LORAMAC_REQUEST_HANDLING_ON ) )
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void LoRaMacProcess( void )
|
|
{
|
|
uint8_t noTx = 0x00;
|
|
|
|
LoRaMacHandleIrqEvents( );
|
|
LoRaMacClassBProcess( );
|
|
|
|
// MAC proceeded a state and is ready to check
|
|
if( MacCtx.MacFlags.Bits.MacDone == 1 )
|
|
{
|
|
LoRaMacEnableRequests( LORAMAC_REQUEST_HANDLING_OFF );
|
|
LoRaMacCheckForRxAbort( );
|
|
|
|
// An error occurs during transmitting
|
|
if( IsRequestPending( ) > 0 )
|
|
{
|
|
noTx |= LoRaMacCheckForBeaconAcquisition( );
|
|
}
|
|
|
|
if( noTx == 0x00 )
|
|
{
|
|
LoRaMacHandleMlmeRequest( );
|
|
LoRaMacHandleMcpsRequest( );
|
|
}
|
|
LoRaMacHandleRequestEvents( );
|
|
LoRaMacHandleScheduleUplinkEvent( );
|
|
LoRaMacEnableRequests( LORAMAC_REQUEST_HANDLING_ON );
|
|
}
|
|
LoRaMacHandleIndicationEvents( );
|
|
if( MacCtx.RxSlot == RX_SLOT_WIN_CLASS_C )
|
|
{
|
|
OpenContinuousRxCWindow( );
|
|
}
|
|
}
|
|
|
|
LoRaMacStatus_t LoRaMacInitialization( LoRaMacPrimitives_t* primitives, LoRaMacCallback_t* callbacks, LoRaMacRegion_t region )
|
|
{
|
|
GetPhyParams_t getPhy;
|
|
PhyParam_t phyParam;
|
|
LoRaMacClassBCallback_t classBCallbacks;
|
|
LoRaMacClassBParams_t classBParams;
|
|
|
|
if( ( primitives == NULL ) ||
|
|
( callbacks == NULL ) )
|
|
{
|
|
return LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
|
|
if( ( primitives->MacMcpsConfirm == NULL ) ||
|
|
( primitives->MacMcpsIndication == NULL ) ||
|
|
( primitives->MacMlmeConfirm == NULL ) ||
|
|
( primitives->MacMlmeIndication == NULL ) )
|
|
{
|
|
return LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
// Verify if the region is supported
|
|
if( RegionIsActive( region ) == false )
|
|
{
|
|
return LORAMAC_STATUS_REGION_NOT_SUPPORTED;
|
|
}
|
|
|
|
// Confirm queue reset
|
|
LoRaMacConfirmQueueInit( primitives, EventConfirmQueueNvmCtxChanged );
|
|
|
|
// Initialize the module context with zeros
|
|
memset1( ( uint8_t* ) &NvmMacCtx, 0x00, sizeof( LoRaMacNvmCtx_t ) );
|
|
memset1( ( uint8_t* ) &MacCtx, 0x00, sizeof( LoRaMacCtx_t ) );
|
|
MacCtx.NvmCtx = &NvmMacCtx;
|
|
|
|
// Set non zero variables to its default value
|
|
MacCtx.AckTimeoutRetriesCounter = 1;
|
|
MacCtx.AckTimeoutRetries = 1;
|
|
MacCtx.NvmCtx->Region = region;
|
|
MacCtx.NvmCtx->DeviceClass = CLASS_A;
|
|
MacCtx.NvmCtx->RepeaterSupport = false;
|
|
|
|
// Setup version
|
|
MacCtx.NvmCtx->Version.Value = LORAMAC_VERSION;
|
|
|
|
// Reset to defaults
|
|
getPhy.Attribute = PHY_DUTY_CYCLE;
|
|
phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
|
|
MacCtx.NvmCtx->DutyCycleOn = ( bool ) phyParam.Value;
|
|
|
|
getPhy.Attribute = PHY_DEF_TX_POWER;
|
|
phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
|
|
MacCtx.NvmCtx->MacParamsDefaults.ChannelsTxPower = phyParam.Value;
|
|
|
|
getPhy.Attribute = PHY_DEF_TX_DR;
|
|
phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
|
|
MacCtx.NvmCtx->MacParamsDefaults.ChannelsDatarate = phyParam.Value;
|
|
|
|
getPhy.Attribute = PHY_MAX_RX_WINDOW;
|
|
phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
|
|
MacCtx.NvmCtx->MacParamsDefaults.MaxRxWindow = phyParam.Value;
|
|
|
|
getPhy.Attribute = PHY_RECEIVE_DELAY1;
|
|
phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
|
|
MacCtx.NvmCtx->MacParamsDefaults.ReceiveDelay1 = phyParam.Value;
|
|
|
|
getPhy.Attribute = PHY_RECEIVE_DELAY2;
|
|
phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
|
|
MacCtx.NvmCtx->MacParamsDefaults.ReceiveDelay2 = phyParam.Value;
|
|
|
|
getPhy.Attribute = PHY_JOIN_ACCEPT_DELAY1;
|
|
phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
|
|
MacCtx.NvmCtx->MacParamsDefaults.JoinAcceptDelay1 = phyParam.Value;
|
|
|
|
getPhy.Attribute = PHY_JOIN_ACCEPT_DELAY2;
|
|
phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
|
|
MacCtx.NvmCtx->MacParamsDefaults.JoinAcceptDelay2 = phyParam.Value;
|
|
|
|
getPhy.Attribute = PHY_DEF_DR1_OFFSET;
|
|
phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
|
|
MacCtx.NvmCtx->MacParamsDefaults.Rx1DrOffset = phyParam.Value;
|
|
|
|
getPhy.Attribute = PHY_DEF_RX2_FREQUENCY;
|
|
phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
|
|
MacCtx.NvmCtx->MacParamsDefaults.Rx2Channel.Frequency = phyParam.Value;
|
|
MacCtx.NvmCtx->MacParamsDefaults.RxCChannel.Frequency = phyParam.Value;
|
|
|
|
getPhy.Attribute = PHY_DEF_RX2_DR;
|
|
phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
|
|
MacCtx.NvmCtx->MacParamsDefaults.Rx2Channel.Datarate = phyParam.Value;
|
|
MacCtx.NvmCtx->MacParamsDefaults.RxCChannel.Datarate = phyParam.Value;
|
|
|
|
getPhy.Attribute = PHY_DEF_UPLINK_DWELL_TIME;
|
|
phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
|
|
MacCtx.NvmCtx->MacParamsDefaults.UplinkDwellTime = phyParam.Value;
|
|
|
|
getPhy.Attribute = PHY_DEF_DOWNLINK_DWELL_TIME;
|
|
phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
|
|
MacCtx.NvmCtx->MacParamsDefaults.DownlinkDwellTime = phyParam.Value;
|
|
|
|
getPhy.Attribute = PHY_DEF_MAX_EIRP;
|
|
phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
|
|
MacCtx.NvmCtx->MacParamsDefaults.MaxEirp = phyParam.fValue;
|
|
|
|
getPhy.Attribute = PHY_DEF_ANTENNA_GAIN;
|
|
phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
|
|
MacCtx.NvmCtx->MacParamsDefaults.AntennaGain = phyParam.fValue;
|
|
|
|
getPhy.Attribute = PHY_DEF_ADR_ACK_LIMIT;
|
|
phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
|
|
MacCtx.AdrAckLimit = phyParam.Value;
|
|
|
|
getPhy.Attribute = PHY_DEF_ADR_ACK_DELAY;
|
|
phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
|
|
MacCtx.AdrAckDelay = phyParam.Value;
|
|
|
|
// Init parameters which are not set in function ResetMacParameters
|
|
MacCtx.NvmCtx->MacParamsDefaults.ChannelsNbTrans = 1;
|
|
MacCtx.NvmCtx->MacParamsDefaults.SystemMaxRxError = 10;
|
|
MacCtx.NvmCtx->MacParamsDefaults.MinRxSymbols = 6;
|
|
|
|
MacCtx.NvmCtx->MacParams.SystemMaxRxError = MacCtx.NvmCtx->MacParamsDefaults.SystemMaxRxError;
|
|
MacCtx.NvmCtx->MacParams.MinRxSymbols = MacCtx.NvmCtx->MacParamsDefaults.MinRxSymbols;
|
|
MacCtx.NvmCtx->MacParams.MaxRxWindow = MacCtx.NvmCtx->MacParamsDefaults.MaxRxWindow;
|
|
MacCtx.NvmCtx->MacParams.ReceiveDelay1 = MacCtx.NvmCtx->MacParamsDefaults.ReceiveDelay1;
|
|
MacCtx.NvmCtx->MacParams.ReceiveDelay2 = MacCtx.NvmCtx->MacParamsDefaults.ReceiveDelay2;
|
|
MacCtx.NvmCtx->MacParams.JoinAcceptDelay1 = MacCtx.NvmCtx->MacParamsDefaults.JoinAcceptDelay1;
|
|
MacCtx.NvmCtx->MacParams.JoinAcceptDelay2 = MacCtx.NvmCtx->MacParamsDefaults.JoinAcceptDelay2;
|
|
MacCtx.NvmCtx->MacParams.ChannelsNbTrans = MacCtx.NvmCtx->MacParamsDefaults.ChannelsNbTrans;
|
|
|
|
InitDefaultsParams_t params;
|
|
params.Type = INIT_TYPE_DEFAULTS;
|
|
params.NvmCtx = NULL;
|
|
RegionInitDefaults( MacCtx.NvmCtx->Region, ¶ms );
|
|
|
|
ResetMacParameters( );
|
|
|
|
MacCtx.NvmCtx->PublicNetwork = true;
|
|
|
|
MacCtx.MacPrimitives = primitives;
|
|
MacCtx.MacCallbacks = callbacks;
|
|
MacCtx.MacFlags.Value = 0;
|
|
MacCtx.MacState = LORAMAC_STOPPED;
|
|
|
|
// Reset duty cycle times
|
|
MacCtx.NvmCtx->LastTxDoneTime = 0;
|
|
MacCtx.NvmCtx->AggregatedTimeOff = 0;
|
|
|
|
// Initialize timers
|
|
TimerInit( &MacCtx.TxDelayedTimer, OnTxDelayedTimerEvent );
|
|
TimerInit( &MacCtx.RxWindowTimer1, OnRxWindow1TimerEvent );
|
|
TimerInit( &MacCtx.RxWindowTimer2, OnRxWindow2TimerEvent );
|
|
TimerInit( &MacCtx.AckTimeoutTimer, OnAckTimeoutTimerEvent );
|
|
|
|
// Store the current initialization time
|
|
MacCtx.NvmCtx->InitializationTime = SysTimeGetMcuTime( );
|
|
|
|
// Initialize Radio driver
|
|
MacCtx.RadioEvents.TxDone = OnRadioTxDone;
|
|
MacCtx.RadioEvents.RxDone = OnRadioRxDone;
|
|
MacCtx.RadioEvents.RxError = OnRadioRxError;
|
|
MacCtx.RadioEvents.TxTimeout = OnRadioTxTimeout;
|
|
MacCtx.RadioEvents.RxTimeout = OnRadioRxTimeout;
|
|
Radio.Init( &MacCtx.RadioEvents );
|
|
|
|
// Initialize the Secure Element driver
|
|
if( SecureElementInit( EventSecureElementNvmCtxChanged ) != SECURE_ELEMENT_SUCCESS )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
|
|
// Initialize Crypto module
|
|
if( LoRaMacCryptoInit( EventCryptoNvmCtxChanged ) != LORAMAC_CRYPTO_SUCCESS )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
|
|
// Initialize MAC commands module
|
|
if( LoRaMacCommandsInit( EventCommandsNvmCtxChanged ) != LORAMAC_COMMANDS_SUCCESS )
|
|
{
|
|
return LORAMAC_STATUS_MAC_COMMAD_ERROR;
|
|
}
|
|
|
|
// Set multicast downlink counter reference
|
|
if( LoRaMacCryptoSetMulticastReference( MacCtx.NvmCtx->MulticastChannelList ) != LORAMAC_CRYPTO_SUCCESS )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
|
|
// Random seed initialization
|
|
srand1( Radio.Random( ) );
|
|
|
|
Radio.SetPublicNetwork( MacCtx.NvmCtx->PublicNetwork );
|
|
Radio.Sleep( );
|
|
|
|
// Initialize class b
|
|
// Apply callback
|
|
classBCallbacks.GetTemperatureLevel = NULL;
|
|
classBCallbacks.MacProcessNotify = NULL;
|
|
if( callbacks != NULL )
|
|
{
|
|
classBCallbacks.GetTemperatureLevel = callbacks->GetTemperatureLevel;
|
|
classBCallbacks.MacProcessNotify = callbacks->MacProcessNotify;
|
|
}
|
|
|
|
// Must all be static. Don't use local references.
|
|
classBParams.MlmeIndication = &MacCtx.MlmeIndication;
|
|
classBParams.McpsIndication = &MacCtx.McpsIndication;
|
|
classBParams.MlmeConfirm = &MacCtx.MlmeConfirm;
|
|
classBParams.LoRaMacFlags = &MacCtx.MacFlags;
|
|
classBParams.LoRaMacDevAddr = &MacCtx.NvmCtx->DevAddr;
|
|
classBParams.LoRaMacRegion = &MacCtx.NvmCtx->Region;
|
|
classBParams.LoRaMacParams = &MacCtx.NvmCtx->MacParams;
|
|
classBParams.MulticastChannels = &MacCtx.NvmCtx->MulticastChannelList[0];
|
|
|
|
LoRaMacClassBInit( &classBParams, &classBCallbacks, &EventClassBNvmCtxChanged );
|
|
|
|
LoRaMacEnableRequests( LORAMAC_REQUEST_HANDLING_ON );
|
|
|
|
return LORAMAC_STATUS_OK;
|
|
}
|
|
|
|
LoRaMacStatus_t LoRaMacStart( void )
|
|
{
|
|
MacCtx.MacState = LORAMAC_IDLE;
|
|
return LORAMAC_STATUS_OK;
|
|
}
|
|
|
|
LoRaMacStatus_t LoRaMacStop( void )
|
|
{
|
|
if( LoRaMacIsBusy( ) == false )
|
|
{
|
|
MacCtx.MacState = LORAMAC_STOPPED;
|
|
return LORAMAC_STATUS_OK;
|
|
}
|
|
else if( MacCtx.MacState == LORAMAC_STOPPED )
|
|
{
|
|
return LORAMAC_STATUS_OK;
|
|
}
|
|
return LORAMAC_STATUS_BUSY;
|
|
}
|
|
|
|
LoRaMacStatus_t LoRaMacQueryTxPossible( uint8_t size, LoRaMacTxInfo_t* txInfo )
|
|
{
|
|
CalcNextAdrParams_t adrNext;
|
|
uint32_t adrAckCounter = MacCtx.NvmCtx->AdrAckCounter;
|
|
int8_t datarate = MacCtx.NvmCtx->MacParamsDefaults.ChannelsDatarate;
|
|
int8_t txPower = MacCtx.NvmCtx->MacParamsDefaults.ChannelsTxPower;
|
|
size_t macCmdsSize = 0;
|
|
|
|
if( txInfo == NULL )
|
|
{
|
|
return LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
|
|
// Setup ADR request
|
|
adrNext.Version = MacCtx.NvmCtx->Version;
|
|
adrNext.UpdateChanMask = false;
|
|
adrNext.AdrEnabled = MacCtx.NvmCtx->AdrCtrlOn;
|
|
adrNext.AdrAckCounter = MacCtx.NvmCtx->AdrAckCounter;
|
|
adrNext.AdrAckLimit = MacCtx.AdrAckLimit;
|
|
adrNext.AdrAckDelay = MacCtx.AdrAckDelay;
|
|
adrNext.Datarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate;
|
|
adrNext.TxPower = MacCtx.NvmCtx->MacParams.ChannelsTxPower;
|
|
adrNext.UplinkDwellTime = MacCtx.NvmCtx->MacParams.UplinkDwellTime;
|
|
adrNext.Region = MacCtx.NvmCtx->Region;
|
|
|
|
// We call the function for information purposes only. We don't want to
|
|
// apply the datarate, the tx power and the ADR ack counter.
|
|
LoRaMacAdrCalcNext( &adrNext, &datarate, &txPower, &adrAckCounter );
|
|
|
|
txInfo->CurrentPossiblePayloadSize = GetMaxAppPayloadWithoutFOptsLength( datarate );
|
|
|
|
if( LoRaMacCommandsGetSizeSerializedCmds( &macCmdsSize ) != LORAMAC_COMMANDS_SUCCESS )
|
|
{
|
|
return LORAMAC_STATUS_MAC_COMMAD_ERROR;
|
|
}
|
|
|
|
// Verify if the MAC commands fit into the FOpts and into the maximum payload.
|
|
if( ( LORA_MAC_COMMAND_MAX_FOPTS_LENGTH >= macCmdsSize ) && ( txInfo->CurrentPossiblePayloadSize >= macCmdsSize ) )
|
|
{
|
|
txInfo->MaxPossibleApplicationDataSize = txInfo->CurrentPossiblePayloadSize - macCmdsSize;
|
|
|
|
// Verify if the application data together with MAC command fit into the maximum payload.
|
|
if( txInfo->CurrentPossiblePayloadSize >= ( macCmdsSize + size ) )
|
|
{
|
|
return LORAMAC_STATUS_OK;
|
|
}
|
|
else
|
|
{
|
|
return LORAMAC_STATUS_LENGTH_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
txInfo->MaxPossibleApplicationDataSize = 0;
|
|
return LORAMAC_STATUS_LENGTH_ERROR;
|
|
}
|
|
}
|
|
|
|
LoRaMacStatus_t LoRaMacMibGetRequestConfirm( MibRequestConfirm_t* mibGet )
|
|
{
|
|
LoRaMacStatus_t status = LORAMAC_STATUS_OK;
|
|
GetPhyParams_t getPhy;
|
|
PhyParam_t phyParam;
|
|
|
|
if( mibGet == NULL )
|
|
{
|
|
return LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
|
|
switch( mibGet->Type )
|
|
{
|
|
case MIB_DEVICE_CLASS:
|
|
{
|
|
mibGet->Param.Class = MacCtx.NvmCtx->DeviceClass;
|
|
break;
|
|
}
|
|
case MIB_NETWORK_ACTIVATION:
|
|
{
|
|
mibGet->Param.NetworkActivation = MacCtx.NvmCtx->NetworkActivation;
|
|
break;
|
|
}
|
|
case MIB_DEV_EUI:
|
|
{
|
|
mibGet->Param.DevEui = SecureElementGetDevEui( );
|
|
break;
|
|
}
|
|
case MIB_JOIN_EUI:
|
|
{
|
|
mibGet->Param.JoinEui = SecureElementGetJoinEui( );
|
|
break;
|
|
}
|
|
case MIB_ADR:
|
|
{
|
|
mibGet->Param.AdrEnable = MacCtx.NvmCtx->AdrCtrlOn;
|
|
break;
|
|
}
|
|
case MIB_NET_ID:
|
|
{
|
|
mibGet->Param.NetID = MacCtx.NvmCtx->NetID;
|
|
break;
|
|
}
|
|
case MIB_DEV_ADDR:
|
|
{
|
|
mibGet->Param.DevAddr = MacCtx.NvmCtx->DevAddr;
|
|
break;
|
|
}
|
|
case MIB_PUBLIC_NETWORK:
|
|
{
|
|
mibGet->Param.EnablePublicNetwork = MacCtx.NvmCtx->PublicNetwork;
|
|
break;
|
|
}
|
|
case MIB_REPEATER_SUPPORT:
|
|
{
|
|
mibGet->Param.EnableRepeaterSupport = MacCtx.NvmCtx->RepeaterSupport;
|
|
break;
|
|
}
|
|
case MIB_CHANNELS:
|
|
{
|
|
getPhy.Attribute = PHY_CHANNELS;
|
|
phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
|
|
|
|
mibGet->Param.ChannelList = phyParam.Channels;
|
|
break;
|
|
}
|
|
case MIB_RX2_CHANNEL:
|
|
{
|
|
mibGet->Param.Rx2Channel = MacCtx.NvmCtx->MacParams.Rx2Channel;
|
|
break;
|
|
}
|
|
case MIB_RX2_DEFAULT_CHANNEL:
|
|
{
|
|
mibGet->Param.Rx2Channel = MacCtx.NvmCtx->MacParamsDefaults.Rx2Channel;
|
|
break;
|
|
}
|
|
case MIB_RXC_CHANNEL:
|
|
{
|
|
mibGet->Param.RxCChannel = MacCtx.NvmCtx->MacParams.RxCChannel;
|
|
break;
|
|
}
|
|
case MIB_RXC_DEFAULT_CHANNEL:
|
|
{
|
|
mibGet->Param.RxCChannel = MacCtx.NvmCtx->MacParamsDefaults.RxCChannel;
|
|
break;
|
|
}
|
|
case MIB_CHANNELS_DEFAULT_MASK:
|
|
{
|
|
getPhy.Attribute = PHY_CHANNELS_DEFAULT_MASK;
|
|
phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
|
|
|
|
mibGet->Param.ChannelsDefaultMask = phyParam.ChannelsMask;
|
|
break;
|
|
}
|
|
case MIB_CHANNELS_MASK:
|
|
{
|
|
getPhy.Attribute = PHY_CHANNELS_MASK;
|
|
phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
|
|
|
|
mibGet->Param.ChannelsMask = phyParam.ChannelsMask;
|
|
break;
|
|
}
|
|
case MIB_CHANNELS_NB_TRANS:
|
|
{
|
|
mibGet->Param.ChannelsNbTrans = MacCtx.NvmCtx->MacParams.ChannelsNbTrans;
|
|
break;
|
|
}
|
|
case MIB_MAX_RX_WINDOW_DURATION:
|
|
{
|
|
mibGet->Param.MaxRxWindow = MacCtx.NvmCtx->MacParams.MaxRxWindow;
|
|
break;
|
|
}
|
|
case MIB_RECEIVE_DELAY_1:
|
|
{
|
|
mibGet->Param.ReceiveDelay1 = MacCtx.NvmCtx->MacParams.ReceiveDelay1;
|
|
break;
|
|
}
|
|
case MIB_RECEIVE_DELAY_2:
|
|
{
|
|
mibGet->Param.ReceiveDelay2 = MacCtx.NvmCtx->MacParams.ReceiveDelay2;
|
|
break;
|
|
}
|
|
case MIB_JOIN_ACCEPT_DELAY_1:
|
|
{
|
|
mibGet->Param.JoinAcceptDelay1 = MacCtx.NvmCtx->MacParams.JoinAcceptDelay1;
|
|
break;
|
|
}
|
|
case MIB_JOIN_ACCEPT_DELAY_2:
|
|
{
|
|
mibGet->Param.JoinAcceptDelay2 = MacCtx.NvmCtx->MacParams.JoinAcceptDelay2;
|
|
break;
|
|
}
|
|
case MIB_CHANNELS_DEFAULT_DATARATE:
|
|
{
|
|
mibGet->Param.ChannelsDefaultDatarate = MacCtx.NvmCtx->MacParamsDefaults.ChannelsDatarate;
|
|
break;
|
|
}
|
|
case MIB_CHANNELS_DATARATE:
|
|
{
|
|
mibGet->Param.ChannelsDatarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate;
|
|
break;
|
|
}
|
|
case MIB_CHANNELS_DEFAULT_TX_POWER:
|
|
{
|
|
mibGet->Param.ChannelsDefaultTxPower = MacCtx.NvmCtx->MacParamsDefaults.ChannelsTxPower;
|
|
break;
|
|
}
|
|
case MIB_CHANNELS_TX_POWER:
|
|
{
|
|
mibGet->Param.ChannelsTxPower = MacCtx.NvmCtx->MacParams.ChannelsTxPower;
|
|
break;
|
|
}
|
|
case MIB_SYSTEM_MAX_RX_ERROR:
|
|
{
|
|
mibGet->Param.SystemMaxRxError = MacCtx.NvmCtx->MacParams.SystemMaxRxError;
|
|
break;
|
|
}
|
|
case MIB_MIN_RX_SYMBOLS:
|
|
{
|
|
mibGet->Param.MinRxSymbols = MacCtx.NvmCtx->MacParams.MinRxSymbols;
|
|
break;
|
|
}
|
|
case MIB_ANTENNA_GAIN:
|
|
{
|
|
mibGet->Param.AntennaGain = MacCtx.NvmCtx->MacParams.AntennaGain;
|
|
break;
|
|
}
|
|
case MIB_NVM_CTXS:
|
|
{
|
|
mibGet->Param.Contexts = GetCtxs( );
|
|
break;
|
|
}
|
|
case MIB_DEFAULT_ANTENNA_GAIN:
|
|
{
|
|
mibGet->Param.DefaultAntennaGain = MacCtx.NvmCtx->MacParamsDefaults.AntennaGain;
|
|
break;
|
|
}
|
|
case MIB_LORAWAN_VERSION:
|
|
{
|
|
mibGet->Param.LrWanVersion.LoRaWan = MacCtx.NvmCtx->Version;
|
|
mibGet->Param.LrWanVersion.LoRaWanRegion = RegionGetVersion( );
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
status = LoRaMacClassBMibGetRequestConfirm( mibGet );
|
|
break;
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
LoRaMacStatus_t LoRaMacMibSetRequestConfirm( MibRequestConfirm_t* mibSet )
|
|
{
|
|
LoRaMacStatus_t status = LORAMAC_STATUS_OK;
|
|
ChanMaskSetParams_t chanMaskSet;
|
|
VerifyParams_t verify;
|
|
|
|
if( mibSet == NULL )
|
|
{
|
|
return LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING )
|
|
{
|
|
return LORAMAC_STATUS_BUSY;
|
|
}
|
|
|
|
switch( mibSet->Type )
|
|
{
|
|
case MIB_DEVICE_CLASS:
|
|
{
|
|
status = SwitchClass( mibSet->Param.Class );
|
|
break;
|
|
}
|
|
case MIB_NETWORK_ACTIVATION:
|
|
{
|
|
if( mibSet->Param.NetworkActivation != ACTIVATION_TYPE_OTAA )
|
|
{
|
|
MacCtx.NvmCtx->NetworkActivation = mibSet->Param.NetworkActivation;
|
|
}
|
|
else
|
|
{ // Do not allow to set ACTIVATION_TYPE_OTAA since the MAC will set it automatically after a successful join process.
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
case MIB_DEV_EUI:
|
|
{
|
|
if( SecureElementSetDevEui( mibSet->Param.DevEui ) != SECURE_ELEMENT_SUCCESS )
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
case MIB_JOIN_EUI:
|
|
{
|
|
if( SecureElementSetJoinEui( mibSet->Param.JoinEui ) != SECURE_ELEMENT_SUCCESS )
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
case MIB_ADR:
|
|
{
|
|
MacCtx.NvmCtx->AdrCtrlOn = mibSet->Param.AdrEnable;
|
|
break;
|
|
}
|
|
case MIB_NET_ID:
|
|
{
|
|
MacCtx.NvmCtx->NetID = mibSet->Param.NetID;
|
|
break;
|
|
}
|
|
case MIB_DEV_ADDR:
|
|
{
|
|
MacCtx.NvmCtx->DevAddr = mibSet->Param.DevAddr;
|
|
break;
|
|
}
|
|
case MIB_APP_KEY:
|
|
{
|
|
if( mibSet->Param.AppKey != NULL )
|
|
{
|
|
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( APP_KEY, mibSet->Param.AppKey ) )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
case MIB_NWK_KEY:
|
|
{
|
|
if( mibSet->Param.NwkKey != NULL )
|
|
{
|
|
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( NWK_KEY, mibSet->Param.NwkKey ) )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
#if ( USE_LRWAN_1_1_X_CRYPTO == 1 )
|
|
case MIB_J_S_INT_KEY:
|
|
{
|
|
if( mibSet->Param.JSIntKey != NULL )
|
|
{
|
|
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( J_S_INT_KEY, mibSet->Param.JSIntKey ) )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
case MIB_J_S_ENC_KEY:
|
|
{
|
|
if( mibSet->Param.JSEncKey != NULL )
|
|
{
|
|
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( J_S_ENC_KEY, mibSet->Param.JSEncKey ) )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
case MIB_F_NWK_S_INT_KEY:
|
|
{
|
|
if( mibSet->Param.FNwkSIntKey != NULL )
|
|
{
|
|
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( F_NWK_S_INT_KEY, mibSet->Param.FNwkSIntKey ) )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
case MIB_S_NWK_S_INT_KEY:
|
|
{
|
|
if( mibSet->Param.SNwkSIntKey != NULL )
|
|
{
|
|
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( S_NWK_S_INT_KEY, mibSet->Param.SNwkSIntKey ) )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
case MIB_NWK_S_ENC_KEY:
|
|
{
|
|
if( mibSet->Param.NwkSEncKey != NULL )
|
|
{
|
|
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( NWK_S_ENC_KEY, mibSet->Param.NwkSEncKey ) )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
#else /* USE_LRWAN_1_1_X_CRYPTO == 0 */
|
|
case MIB_NWK_S_KEY:
|
|
{
|
|
if( mibSet->Param.NwkSKey != NULL )
|
|
{
|
|
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( NWK_S_KEY, mibSet->Param.NwkSKey ) )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
#endif /* USE_LRWAN_1_1_X_CRYPTO */
|
|
case MIB_APP_S_KEY:
|
|
{
|
|
if( mibSet->Param.AppSKey != NULL )
|
|
{
|
|
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( APP_S_KEY, mibSet->Param.AppSKey ) )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
case MIB_MC_KE_KEY:
|
|
{
|
|
if( mibSet->Param.McKEKey != NULL )
|
|
{
|
|
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_KE_KEY, mibSet->Param.McKEKey ) )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
case MIB_MC_KEY_0:
|
|
{
|
|
if( mibSet->Param.McKey0 != NULL )
|
|
{
|
|
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_KEY_0, mibSet->Param.McKey0 ) )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
case MIB_MC_APP_S_KEY_0:
|
|
{
|
|
if( mibSet->Param.McAppSKey0 != NULL )
|
|
{
|
|
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_APP_S_KEY_0, mibSet->Param.McAppSKey0 ) )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
case MIB_MC_NWK_S_KEY_0:
|
|
{
|
|
if( mibSet->Param.McNwkSKey0 != NULL )
|
|
{
|
|
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_NWK_S_KEY_0, mibSet->Param.McNwkSKey0 ) )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
#if ( LORAMAC_MAX_MC_CTX > 1 )
|
|
case MIB_MC_KEY_1:
|
|
{
|
|
if( mibSet->Param.McKey1 != NULL )
|
|
{
|
|
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_KEY_1, mibSet->Param.McKey1 ) )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
case MIB_MC_APP_S_KEY_1:
|
|
{
|
|
if( mibSet->Param.McAppSKey1 != NULL )
|
|
{
|
|
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_APP_S_KEY_1, mibSet->Param.McAppSKey1 ) )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
case MIB_MC_NWK_S_KEY_1:
|
|
{
|
|
if( mibSet->Param.McNwkSKey1 != NULL )
|
|
{
|
|
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_NWK_S_KEY_1, mibSet->Param.McNwkSKey1 ) )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
case MIB_MC_KEY_2:
|
|
{
|
|
if( mibSet->Param.McKey2 != NULL )
|
|
{
|
|
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_KEY_2, mibSet->Param.McKey2 ) )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
case MIB_MC_APP_S_KEY_2:
|
|
{
|
|
if( mibSet->Param.McAppSKey2 != NULL )
|
|
{
|
|
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_APP_S_KEY_2, mibSet->Param.McAppSKey2 ) )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
case MIB_MC_NWK_S_KEY_2:
|
|
{
|
|
if( mibSet->Param.McNwkSKey2 != NULL )
|
|
{
|
|
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_NWK_S_KEY_2, mibSet->Param.McNwkSKey2 ) )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
case MIB_MC_KEY_3:
|
|
{
|
|
if( mibSet->Param.McKey3 != NULL )
|
|
{
|
|
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_KEY_3, mibSet->Param.McKey3 ) )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
case MIB_MC_APP_S_KEY_3:
|
|
{
|
|
if( mibSet->Param.McAppSKey3 != NULL )
|
|
{
|
|
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_APP_S_KEY_3, mibSet->Param.McAppSKey3 ) )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
case MIB_MC_NWK_S_KEY_3:
|
|
{
|
|
if( mibSet->Param.McNwkSKey3 != NULL )
|
|
{
|
|
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_NWK_S_KEY_3, mibSet->Param.McNwkSKey3 ) )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
#endif /* LORAMAC_MAX_MC_CTX > 1 */
|
|
case MIB_PUBLIC_NETWORK:
|
|
{
|
|
MacCtx.NvmCtx->PublicNetwork = mibSet->Param.EnablePublicNetwork;
|
|
Radio.SetPublicNetwork( MacCtx.NvmCtx->PublicNetwork );
|
|
/* ST_WORAROUND_BEGIN: Required to avoid keeping the radio active after init */
|
|
Radio.Sleep( );
|
|
/* ST_WORKAROUND_END */
|
|
break;
|
|
}
|
|
case MIB_REPEATER_SUPPORT:
|
|
{
|
|
MacCtx.NvmCtx->RepeaterSupport = mibSet->Param.EnableRepeaterSupport;
|
|
break;
|
|
}
|
|
case MIB_RX2_CHANNEL:
|
|
{
|
|
verify.DatarateParams.Datarate = mibSet->Param.Rx2Channel.Datarate;
|
|
verify.DatarateParams.DownlinkDwellTime = MacCtx.NvmCtx->MacParams.DownlinkDwellTime;
|
|
/* ST_WORAROUND_BEGIN: Check also the Rx Frequency parameter */
|
|
if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_RX_DR ) != true )
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
else
|
|
{
|
|
verify.Frequency = mibSet->Param.Rx2Channel.Frequency;
|
|
if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_FREQUENCY ) != true )
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
else
|
|
{
|
|
MacCtx.NvmCtx->MacParams.Rx2Channel = mibSet->Param.Rx2Channel;
|
|
}
|
|
}
|
|
/* ST_WORKAROUND_END */
|
|
break;
|
|
}
|
|
case MIB_RX2_DEFAULT_CHANNEL:
|
|
{
|
|
verify.DatarateParams.Datarate = mibSet->Param.Rx2Channel.Datarate;
|
|
verify.DatarateParams.DownlinkDwellTime = MacCtx.NvmCtx->MacParams.DownlinkDwellTime;
|
|
|
|
if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_RX_DR ) == true )
|
|
{
|
|
MacCtx.NvmCtx->MacParamsDefaults.Rx2Channel = mibSet->Param.Rx2DefaultChannel;
|
|
}
|
|
else
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
case MIB_RXC_CHANNEL:
|
|
{
|
|
verify.DatarateParams.Datarate = mibSet->Param.RxCChannel.Datarate;
|
|
verify.DatarateParams.DownlinkDwellTime = MacCtx.NvmCtx->MacParams.DownlinkDwellTime;
|
|
|
|
if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_RX_DR ) == true )
|
|
{
|
|
MacCtx.NvmCtx->MacParams.RxCChannel = mibSet->Param.RxCChannel;
|
|
|
|
if( ( MacCtx.NvmCtx->DeviceClass == CLASS_C ) && ( MacCtx.NvmCtx->NetworkActivation != ACTIVATION_TYPE_NONE ) )
|
|
{
|
|
// We can only compute the RX window parameters directly, if we are already
|
|
// in class c mode and joined. We cannot setup an RX window in case of any other
|
|
// class type.
|
|
// Set the radio into sleep mode in case we are still in RX mode
|
|
Radio.Sleep( );
|
|
|
|
OpenContinuousRxCWindow( );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
case MIB_RXC_DEFAULT_CHANNEL:
|
|
{
|
|
verify.DatarateParams.Datarate = mibSet->Param.RxCChannel.Datarate;
|
|
verify.DatarateParams.DownlinkDwellTime = MacCtx.NvmCtx->MacParams.DownlinkDwellTime;
|
|
|
|
if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_RX_DR ) == true )
|
|
{
|
|
MacCtx.NvmCtx->MacParamsDefaults.RxCChannel = mibSet->Param.RxCDefaultChannel;
|
|
}
|
|
else
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
case MIB_CHANNELS_DEFAULT_MASK:
|
|
{
|
|
chanMaskSet.ChannelsMaskIn = mibSet->Param.ChannelsDefaultMask;
|
|
chanMaskSet.ChannelsMaskType = CHANNELS_DEFAULT_MASK;
|
|
|
|
if( RegionChanMaskSet( MacCtx.NvmCtx->Region, &chanMaskSet ) == false )
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
case MIB_CHANNELS_MASK:
|
|
{
|
|
chanMaskSet.ChannelsMaskIn = mibSet->Param.ChannelsMask;
|
|
chanMaskSet.ChannelsMaskType = CHANNELS_MASK;
|
|
|
|
if( RegionChanMaskSet( MacCtx.NvmCtx->Region, &chanMaskSet ) == false )
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
case MIB_CHANNELS_NB_TRANS:
|
|
{
|
|
if( ( mibSet->Param.ChannelsNbTrans >= 1 ) &&
|
|
( mibSet->Param.ChannelsNbTrans <= 15 ) )
|
|
{
|
|
MacCtx.NvmCtx->MacParams.ChannelsNbTrans = mibSet->Param.ChannelsNbTrans;
|
|
}
|
|
else
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
case MIB_MAX_RX_WINDOW_DURATION:
|
|
{
|
|
MacCtx.NvmCtx->MacParams.MaxRxWindow = mibSet->Param.MaxRxWindow;
|
|
break;
|
|
}
|
|
case MIB_RECEIVE_DELAY_1:
|
|
{
|
|
MacCtx.NvmCtx->MacParams.ReceiveDelay1 = mibSet->Param.ReceiveDelay1;
|
|
break;
|
|
}
|
|
case MIB_RECEIVE_DELAY_2:
|
|
{
|
|
MacCtx.NvmCtx->MacParams.ReceiveDelay2 = mibSet->Param.ReceiveDelay2;
|
|
break;
|
|
}
|
|
case MIB_JOIN_ACCEPT_DELAY_1:
|
|
{
|
|
MacCtx.NvmCtx->MacParams.JoinAcceptDelay1 = mibSet->Param.JoinAcceptDelay1;
|
|
break;
|
|
}
|
|
case MIB_JOIN_ACCEPT_DELAY_2:
|
|
{
|
|
MacCtx.NvmCtx->MacParams.JoinAcceptDelay2 = mibSet->Param.JoinAcceptDelay2;
|
|
break;
|
|
}
|
|
case MIB_CHANNELS_DEFAULT_DATARATE:
|
|
{
|
|
verify.DatarateParams.Datarate = mibSet->Param.ChannelsDefaultDatarate;
|
|
|
|
if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_DEF_TX_DR ) == true )
|
|
{
|
|
MacCtx.NvmCtx->MacParamsDefaults.ChannelsDatarate = verify.DatarateParams.Datarate;
|
|
}
|
|
else
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
case MIB_CHANNELS_DATARATE:
|
|
{
|
|
verify.DatarateParams.Datarate = mibSet->Param.ChannelsDatarate;
|
|
verify.DatarateParams.UplinkDwellTime = MacCtx.NvmCtx->MacParams.UplinkDwellTime;
|
|
|
|
if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_TX_DR ) == true )
|
|
{
|
|
MacCtx.NvmCtx->MacParams.ChannelsDatarate = verify.DatarateParams.Datarate;
|
|
}
|
|
else
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
case MIB_CHANNELS_DEFAULT_TX_POWER:
|
|
{
|
|
verify.TxPower = mibSet->Param.ChannelsDefaultTxPower;
|
|
|
|
if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_DEF_TX_POWER ) == true )
|
|
{
|
|
MacCtx.NvmCtx->MacParamsDefaults.ChannelsTxPower = verify.TxPower;
|
|
}
|
|
else
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
case MIB_CHANNELS_TX_POWER:
|
|
{
|
|
verify.TxPower = mibSet->Param.ChannelsTxPower;
|
|
|
|
if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_TX_POWER ) == true )
|
|
{
|
|
MacCtx.NvmCtx->MacParams.ChannelsTxPower = verify.TxPower;
|
|
}
|
|
else
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
case MIB_SYSTEM_MAX_RX_ERROR:
|
|
{
|
|
MacCtx.NvmCtx->MacParams.SystemMaxRxError = MacCtx.NvmCtx->MacParamsDefaults.SystemMaxRxError = mibSet->Param.SystemMaxRxError;
|
|
break;
|
|
}
|
|
case MIB_MIN_RX_SYMBOLS:
|
|
{
|
|
MacCtx.NvmCtx->MacParams.MinRxSymbols = MacCtx.NvmCtx->MacParamsDefaults.MinRxSymbols = mibSet->Param.MinRxSymbols;
|
|
break;
|
|
}
|
|
case MIB_ANTENNA_GAIN:
|
|
{
|
|
MacCtx.NvmCtx->MacParams.AntennaGain = mibSet->Param.AntennaGain;
|
|
break;
|
|
}
|
|
case MIB_DEFAULT_ANTENNA_GAIN:
|
|
{
|
|
MacCtx.NvmCtx->MacParamsDefaults.AntennaGain = mibSet->Param.DefaultAntennaGain;
|
|
break;
|
|
}
|
|
case MIB_NVM_CTXS:
|
|
{
|
|
if( mibSet->Param.Contexts != 0 )
|
|
{
|
|
status = RestoreCtxs( mibSet->Param.Contexts );
|
|
}
|
|
else
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
case MIB_ABP_LORAWAN_VERSION:
|
|
{
|
|
if( mibSet->Param.AbpLrWanVersion.Fields.Minor <= 1 )
|
|
{
|
|
MacCtx.NvmCtx->Version = mibSet->Param.AbpLrWanVersion;
|
|
|
|
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetLrWanVersion( mibSet->Param.AbpLrWanVersion ) )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
status = LoRaMacMibClassBSetRequestConfirm( mibSet );
|
|
break;
|
|
}
|
|
}
|
|
EventRegionNvmCtxChanged( );
|
|
EventMacNvmCtxChanged( );
|
|
return status;
|
|
}
|
|
|
|
LoRaMacStatus_t LoRaMacChannelAdd( uint8_t id, ChannelParams_t params )
|
|
{
|
|
ChannelAddParams_t channelAdd;
|
|
|
|
// Validate if the MAC is in a correct state
|
|
if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING )
|
|
{
|
|
if( ( MacCtx.MacState & LORAMAC_TX_CONFIG ) != LORAMAC_TX_CONFIG )
|
|
{
|
|
return LORAMAC_STATUS_BUSY;
|
|
}
|
|
}
|
|
|
|
channelAdd.NewChannel = ¶ms;
|
|
channelAdd.ChannelId = id;
|
|
|
|
EventRegionNvmCtxChanged( );
|
|
return RegionChannelAdd( MacCtx.NvmCtx->Region, &channelAdd );
|
|
}
|
|
|
|
LoRaMacStatus_t LoRaMacChannelRemove( uint8_t id )
|
|
{
|
|
ChannelRemoveParams_t channelRemove;
|
|
|
|
if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING )
|
|
{
|
|
if( ( MacCtx.MacState & LORAMAC_TX_CONFIG ) != LORAMAC_TX_CONFIG )
|
|
{
|
|
return LORAMAC_STATUS_BUSY;
|
|
}
|
|
}
|
|
|
|
channelRemove.ChannelId = id;
|
|
|
|
if( RegionChannelsRemove( MacCtx.NvmCtx->Region, &channelRemove ) == false )
|
|
{
|
|
return LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
|
|
EventRegionNvmCtxChanged( );
|
|
return LORAMAC_STATUS_OK;
|
|
}
|
|
|
|
LoRaMacStatus_t LoRaMacMcChannelSetup( McChannelParams_t *channel )
|
|
{
|
|
if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING )
|
|
{
|
|
return LORAMAC_STATUS_BUSY;
|
|
}
|
|
|
|
if( channel->GroupID >= LORAMAC_MAX_MC_CTX )
|
|
{
|
|
return LORAMAC_STATUS_MC_GROUP_UNDEFINED;
|
|
}
|
|
|
|
MacCtx.NvmCtx->MulticastChannelList[channel->GroupID].ChannelParams = *channel;
|
|
|
|
if( channel->IsRemotelySetup == true )
|
|
{
|
|
#if ( LORAMAC_MAX_MC_CTX == 1 )
|
|
const KeyIdentifier_t mcKeys[LORAMAC_MAX_MC_CTX] = { MC_KEY_0 };
|
|
#else /* LORAMAC_MAX_MC_CTX > 1 */
|
|
const KeyIdentifier_t mcKeys[LORAMAC_MAX_MC_CTX] = { MC_KEY_0, MC_KEY_1, MC_KEY_2, MC_KEY_3 };
|
|
#endif /* LORAMAC_MAX_MC_CTX */
|
|
if( LoRaMacCryptoSetKey( mcKeys[channel->GroupID], channel->McKeys.McKeyE ) != LORAMAC_CRYPTO_SUCCESS )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
|
|
if( LoRaMacCryptoDeriveMcSessionKeyPair( channel->GroupID, channel->Address ) != LORAMAC_CRYPTO_SUCCESS )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if ( LORAMAC_MAX_MC_CTX == 1 )
|
|
const KeyIdentifier_t mcAppSKeys[LORAMAC_MAX_MC_CTX] = { MC_APP_S_KEY_0 };
|
|
const KeyIdentifier_t mcNwkSKeys[LORAMAC_MAX_MC_CTX] = { MC_NWK_S_KEY_0 };
|
|
#else /* LORAMAC_MAX_MC_CTX > 1 */
|
|
const KeyIdentifier_t mcAppSKeys[LORAMAC_MAX_MC_CTX] = { MC_APP_S_KEY_0, MC_APP_S_KEY_1, MC_APP_S_KEY_2, MC_APP_S_KEY_3 };
|
|
const KeyIdentifier_t mcNwkSKeys[LORAMAC_MAX_MC_CTX] = { MC_NWK_S_KEY_0, MC_NWK_S_KEY_1, MC_NWK_S_KEY_2, MC_NWK_S_KEY_3 };
|
|
#endif /* LORAMAC_MAX_MC_CTX */
|
|
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( mcAppSKeys[channel->GroupID], channel->McKeys.Session.McAppSKey ) )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( mcNwkSKeys[channel->GroupID], channel->McKeys.Session.McNwkSKey ) )
|
|
{
|
|
return LORAMAC_STATUS_CRYPTO_ERROR;
|
|
}
|
|
}
|
|
|
|
if( channel->Class == CLASS_B )
|
|
{
|
|
// Calculate class b parameters
|
|
LoRaMacClassBSetMulticastPeriodicity( &MacCtx.NvmCtx->MulticastChannelList[channel->GroupID] );
|
|
}
|
|
|
|
// Reset multicast channel downlink counter to initial value.
|
|
*MacCtx.NvmCtx->MulticastChannelList[channel->GroupID].DownLinkCounter = FCNT_DOWN_INITAL_VALUE;
|
|
|
|
EventMacNvmCtxChanged( );
|
|
EventRegionNvmCtxChanged( );
|
|
return LORAMAC_STATUS_OK;
|
|
}
|
|
|
|
LoRaMacStatus_t LoRaMacMcChannelDelete( AddressIdentifier_t groupID )
|
|
{
|
|
if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING )
|
|
{
|
|
return LORAMAC_STATUS_BUSY;
|
|
}
|
|
|
|
if( ( groupID >= LORAMAC_MAX_MC_CTX ) ||
|
|
( MacCtx.NvmCtx->MulticastChannelList[groupID].ChannelParams.IsEnabled == false ) )
|
|
{
|
|
return LORAMAC_STATUS_MC_GROUP_UNDEFINED;
|
|
}
|
|
|
|
McChannelParams_t channel;
|
|
|
|
// Set all channel fields with 0
|
|
memset1( ( uint8_t* )&channel, 0, sizeof( McChannelParams_t ) );
|
|
|
|
MacCtx.NvmCtx->MulticastChannelList[groupID].ChannelParams = channel;
|
|
|
|
EventMacNvmCtxChanged( );
|
|
EventRegionNvmCtxChanged( );
|
|
return LORAMAC_STATUS_OK;
|
|
}
|
|
|
|
uint8_t LoRaMacMcChannelGetGroupId( uint32_t mcAddress )
|
|
{
|
|
for( uint8_t i = 0; i < LORAMAC_MAX_MC_CTX; i++ )
|
|
{
|
|
if( mcAddress == MacCtx.NvmCtx->MulticastChannelList[i].ChannelParams.Address )
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
return 0xFF;
|
|
}
|
|
|
|
LoRaMacStatus_t LoRaMacMcChannelSetupRxParams( AddressIdentifier_t groupID, McRxParams_t *rxParams, uint8_t *status )
|
|
{
|
|
*status = 0x1C + ( groupID & 0x03 );
|
|
|
|
if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING )
|
|
{
|
|
return LORAMAC_STATUS_BUSY;
|
|
}
|
|
|
|
DeviceClass_t devClass = MacCtx.NvmCtx->MulticastChannelList[groupID].ChannelParams.Class;
|
|
if( ( devClass == CLASS_A ) || ( devClass > CLASS_C ) )
|
|
{
|
|
return LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
|
|
if( ( groupID >= LORAMAC_MAX_MC_CTX ) ||
|
|
( MacCtx.NvmCtx->MulticastChannelList[groupID].ChannelParams.IsEnabled == false ) )
|
|
{
|
|
return LORAMAC_STATUS_MC_GROUP_UNDEFINED;
|
|
}
|
|
/* ST_WORAROUND_BEGIN: only clean one bit */
|
|
*status &= 0xEF; // groupID OK
|
|
/* ST_WORAROUND_END */
|
|
|
|
VerifyParams_t verify;
|
|
// Check datarate
|
|
if( devClass == CLASS_B )
|
|
{
|
|
verify.DatarateParams.Datarate = rxParams->ClassB.Datarate;
|
|
}
|
|
else
|
|
{
|
|
verify.DatarateParams.Datarate = rxParams->ClassC.Datarate;
|
|
}
|
|
verify.DatarateParams.DownlinkDwellTime = MacCtx.NvmCtx->MacParams.DownlinkDwellTime;
|
|
|
|
if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_RX_DR ) == true )
|
|
{
|
|
*status &= 0xFB; // datarate OK
|
|
}
|
|
|
|
// Check frequency
|
|
if( devClass == CLASS_B )
|
|
{
|
|
verify.Frequency = rxParams->ClassB.Frequency;
|
|
}
|
|
else
|
|
{
|
|
verify.Frequency = rxParams->ClassC.Frequency;
|
|
}
|
|
if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_FREQUENCY ) == true )
|
|
{
|
|
*status &= 0xF7; // frequency OK
|
|
}
|
|
|
|
if( *status == ( groupID & 0x03 ) )
|
|
{
|
|
// Apply parameters
|
|
MacCtx.NvmCtx->MulticastChannelList[groupID].ChannelParams.RxParams = *rxParams;
|
|
}
|
|
|
|
EventMacNvmCtxChanged( );
|
|
EventRegionNvmCtxChanged( );
|
|
return LORAMAC_STATUS_OK;
|
|
}
|
|
|
|
LoRaMacStatus_t LoRaMacMlmeRequest( MlmeReq_t* mlmeRequest )
|
|
{
|
|
LoRaMacStatus_t status = LORAMAC_STATUS_SERVICE_UNKNOWN;
|
|
MlmeConfirmQueue_t queueElement;
|
|
uint8_t macCmdPayload[2] = { 0x00, 0x00 };
|
|
|
|
if( mlmeRequest == NULL )
|
|
{
|
|
return LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
if( LoRaMacIsBusy( ) == true )
|
|
{
|
|
return LORAMAC_STATUS_BUSY;
|
|
}
|
|
if( LoRaMacConfirmQueueIsFull( ) == true )
|
|
{
|
|
return LORAMAC_STATUS_BUSY;
|
|
}
|
|
|
|
if( LoRaMacConfirmQueueGetCnt( ) == 0 )
|
|
{
|
|
memset1( ( uint8_t* ) &MacCtx.MlmeConfirm, 0, sizeof( MacCtx.MlmeConfirm ) );
|
|
}
|
|
MacCtx.MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
|
|
|
|
MacCtx.MacFlags.Bits.MlmeReq = 1;
|
|
queueElement.Request = mlmeRequest->Type;
|
|
queueElement.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
|
|
queueElement.RestrictCommonReadyToHandle = false;
|
|
|
|
switch( mlmeRequest->Type )
|
|
{
|
|
case MLME_JOIN:
|
|
{
|
|
if( ( MacCtx.MacState & LORAMAC_TX_DELAYED ) == LORAMAC_TX_DELAYED )
|
|
{
|
|
return LORAMAC_STATUS_BUSY;
|
|
}
|
|
|
|
ResetMacParameters( );
|
|
|
|
MacCtx.NvmCtx->MacParams.ChannelsDatarate = RegionAlternateDr( MacCtx.NvmCtx->Region, mlmeRequest->Req.Join.Datarate, ALTERNATE_DR );
|
|
|
|
queueElement.Status = LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL;
|
|
|
|
status = SendReJoinReq( JOIN_REQ );
|
|
|
|
if( status != LORAMAC_STATUS_OK )
|
|
{
|
|
// Revert back the previous datarate ( mainly used for US915 like regions )
|
|
MacCtx.NvmCtx->MacParams.ChannelsDatarate = RegionAlternateDr( MacCtx.NvmCtx->Region, mlmeRequest->Req.Join.Datarate, ALTERNATE_DR_RESTORE );
|
|
}
|
|
break;
|
|
}
|
|
case MLME_LINK_CHECK:
|
|
{
|
|
// LoRaMac will send this command piggy-pack
|
|
status = LORAMAC_STATUS_OK;
|
|
if( LoRaMacCommandsAddCmd( MOTE_MAC_LINK_CHECK_REQ, macCmdPayload, 0 ) != LORAMAC_COMMANDS_SUCCESS )
|
|
{
|
|
status = LORAMAC_STATUS_MAC_COMMAD_ERROR;
|
|
}
|
|
break;
|
|
}
|
|
case MLME_TXCW:
|
|
{
|
|
status = SetTxContinuousWave( mlmeRequest->Req.TxCw.Timeout );
|
|
break;
|
|
}
|
|
case MLME_TXCW_1:
|
|
{
|
|
|
|
status = SetTxContinuousWave1( mlmeRequest->Req.TxCw.Timeout, mlmeRequest->Req.TxCw.Frequency, mlmeRequest->Req.TxCw.Power );
|
|
break;
|
|
}
|
|
case MLME_DEVICE_TIME:
|
|
{
|
|
// LoRaMac will send this command piggy-pack
|
|
status = LORAMAC_STATUS_OK;
|
|
if( LoRaMacCommandsAddCmd( MOTE_MAC_DEVICE_TIME_REQ, macCmdPayload, 0 ) != LORAMAC_COMMANDS_SUCCESS )
|
|
{
|
|
status = LORAMAC_STATUS_MAC_COMMAD_ERROR;
|
|
}
|
|
break;
|
|
}
|
|
case MLME_PING_SLOT_INFO:
|
|
{
|
|
if( MacCtx.NvmCtx->DeviceClass == CLASS_A )
|
|
{
|
|
uint8_t value = mlmeRequest->Req.PingSlotInfo.PingSlot.Value;
|
|
|
|
// LoRaMac will send this command piggy-pack
|
|
LoRaMacClassBSetPingSlotInfo( mlmeRequest->Req.PingSlotInfo.PingSlot.Fields.Periodicity );
|
|
macCmdPayload[0] = value;
|
|
status = LORAMAC_STATUS_OK;
|
|
if( LoRaMacCommandsAddCmd( MOTE_MAC_PING_SLOT_INFO_REQ, macCmdPayload, 1 ) != LORAMAC_COMMANDS_SUCCESS )
|
|
{
|
|
status = LORAMAC_STATUS_MAC_COMMAD_ERROR;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case MLME_BEACON_TIMING:
|
|
{
|
|
// LoRaMac will send this command piggy-pack
|
|
status = LORAMAC_STATUS_OK;
|
|
if( LoRaMacCommandsAddCmd( MOTE_MAC_BEACON_TIMING_REQ, macCmdPayload, 0 ) != LORAMAC_COMMANDS_SUCCESS )
|
|
{
|
|
status = LORAMAC_STATUS_MAC_COMMAD_ERROR;
|
|
}
|
|
break;
|
|
}
|
|
case MLME_BEACON_ACQUISITION:
|
|
{
|
|
// Apply the request
|
|
queueElement.RestrictCommonReadyToHandle = true;
|
|
|
|
if( LoRaMacClassBIsAcquisitionInProgress( ) == false )
|
|
{
|
|
// Start class B algorithm
|
|
LoRaMacClassBSetBeaconState( BEACON_STATE_ACQUISITION );
|
|
LoRaMacClassBBeaconTimerEvent( NULL );
|
|
|
|
status = LORAMAC_STATUS_OK;
|
|
}
|
|
else
|
|
{
|
|
status = LORAMAC_STATUS_BUSY;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Fill return structure
|
|
mlmeRequest->ReqReturn.DutyCycleWaitTime = MacCtx.DutyCycleWaitTime;
|
|
|
|
if( status != LORAMAC_STATUS_OK )
|
|
{
|
|
if( LoRaMacConfirmQueueGetCnt( ) == 0 )
|
|
{
|
|
MacCtx.NodeAckRequested = false;
|
|
MacCtx.MacFlags.Bits.MlmeReq = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LoRaMacConfirmQueueAdd( &queueElement );
|
|
EventMacNvmCtxChanged( );
|
|
}
|
|
return status;
|
|
}
|
|
|
|
LoRaMacStatus_t LoRaMacMcpsRequest( McpsReq_t* mcpsRequest, bool allowDelayedTx )
|
|
{
|
|
GetPhyParams_t getPhy;
|
|
PhyParam_t phyParam;
|
|
LoRaMacStatus_t status = LORAMAC_STATUS_SERVICE_UNKNOWN;
|
|
LoRaMacHeader_t macHdr;
|
|
VerifyParams_t verify;
|
|
uint8_t fPort = 0;
|
|
void* fBuffer;
|
|
uint16_t fBufferSize;
|
|
int8_t datarate = DR_0;
|
|
bool readyToSend = false;
|
|
|
|
if( mcpsRequest == NULL )
|
|
{
|
|
return LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
if( LoRaMacIsBusy( ) == true )
|
|
{
|
|
return LORAMAC_STATUS_BUSY;
|
|
}
|
|
|
|
macHdr.Value = 0;
|
|
memset1( ( uint8_t* ) &MacCtx.McpsConfirm, 0, sizeof( MacCtx.McpsConfirm ) );
|
|
MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
|
|
|
|
// AckTimeoutRetriesCounter must be reset every time a new request (unconfirmed or confirmed) is performed.
|
|
MacCtx.AckTimeoutRetriesCounter = 1;
|
|
|
|
switch( mcpsRequest->Type )
|
|
{
|
|
case MCPS_UNCONFIRMED:
|
|
{
|
|
readyToSend = true;
|
|
MacCtx.AckTimeoutRetries = 1;
|
|
|
|
macHdr.Bits.MType = FRAME_TYPE_DATA_UNCONFIRMED_UP;
|
|
fPort = mcpsRequest->Req.Unconfirmed.fPort;
|
|
fBuffer = mcpsRequest->Req.Unconfirmed.fBuffer;
|
|
fBufferSize = mcpsRequest->Req.Unconfirmed.fBufferSize;
|
|
datarate = mcpsRequest->Req.Unconfirmed.Datarate;
|
|
break;
|
|
}
|
|
case MCPS_CONFIRMED:
|
|
{
|
|
readyToSend = true;
|
|
MacCtx.AckTimeoutRetries = MIN( mcpsRequest->Req.Confirmed.NbTrials, MAX_ACK_RETRIES );
|
|
|
|
macHdr.Bits.MType = FRAME_TYPE_DATA_CONFIRMED_UP;
|
|
fPort = mcpsRequest->Req.Confirmed.fPort;
|
|
fBuffer = mcpsRequest->Req.Confirmed.fBuffer;
|
|
fBufferSize = mcpsRequest->Req.Confirmed.fBufferSize;
|
|
datarate = mcpsRequest->Req.Confirmed.Datarate;
|
|
break;
|
|
}
|
|
case MCPS_PROPRIETARY:
|
|
{
|
|
readyToSend = true;
|
|
MacCtx.AckTimeoutRetries = 1;
|
|
|
|
macHdr.Bits.MType = FRAME_TYPE_PROPRIETARY;
|
|
fBuffer = mcpsRequest->Req.Proprietary.fBuffer;
|
|
fBufferSize = mcpsRequest->Req.Proprietary.fBufferSize;
|
|
datarate = mcpsRequest->Req.Proprietary.Datarate;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Get the minimum possible datarate
|
|
getPhy.Attribute = PHY_MIN_TX_DR;
|
|
getPhy.UplinkDwellTime = MacCtx.NvmCtx->MacParams.UplinkDwellTime;
|
|
phyParam = RegionGetPhyParam( MacCtx.NvmCtx->Region, &getPhy );
|
|
// Apply the minimum possible datarate.
|
|
// Some regions have limitations for the minimum datarate.
|
|
datarate = MAX( datarate, ( int8_t )phyParam.Value );
|
|
|
|
if( readyToSend == true )
|
|
{
|
|
if( MacCtx.NvmCtx->AdrCtrlOn == false )
|
|
{
|
|
verify.DatarateParams.Datarate = datarate;
|
|
verify.DatarateParams.UplinkDwellTime = MacCtx.NvmCtx->MacParams.UplinkDwellTime;
|
|
|
|
if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_TX_DR ) == true )
|
|
{
|
|
MacCtx.NvmCtx->MacParams.ChannelsDatarate = verify.DatarateParams.Datarate;
|
|
}
|
|
else
|
|
{
|
|
return LORAMAC_STATUS_PARAMETER_INVALID;
|
|
}
|
|
}
|
|
|
|
status = Send( &macHdr, fPort, fBuffer, fBufferSize, allowDelayedTx );
|
|
if( status == LORAMAC_STATUS_OK )
|
|
{
|
|
MacCtx.McpsConfirm.McpsRequest = mcpsRequest->Type;
|
|
MacCtx.MacFlags.Bits.McpsReq = 1;
|
|
EventMacNvmCtxChanged( );
|
|
}
|
|
else
|
|
{
|
|
MacCtx.NodeAckRequested = false;
|
|
}
|
|
}
|
|
|
|
// Fill return structure
|
|
mcpsRequest->ReqReturn.DutyCycleWaitTime = MacCtx.DutyCycleWaitTime;
|
|
|
|
return status;
|
|
}
|
|
|
|
void LoRaMacTestSetDutyCycleOn( bool enable )
|
|
{
|
|
VerifyParams_t verify;
|
|
|
|
verify.DutyCycle = enable;
|
|
|
|
if( RegionVerify( MacCtx.NvmCtx->Region, &verify, PHY_DUTY_CYCLE ) == true )
|
|
{
|
|
MacCtx.NvmCtx->DutyCycleOn = enable;
|
|
}
|
|
}
|
|
|
|
LoRaMacStatus_t LoRaMacDeInitialization( void )
|
|
{
|
|
// Check the current state of the LoRaMac
|
|
if ( LoRaMacStop( ) == LORAMAC_STATUS_OK )
|
|
{
|
|
// Stop Timers
|
|
TimerStop( &MacCtx.TxDelayedTimer );
|
|
TimerStop( &MacCtx.RxWindowTimer1 );
|
|
TimerStop( &MacCtx.RxWindowTimer2 );
|
|
TimerStop( &MacCtx.AckTimeoutTimer );
|
|
|
|
// Take care about class B
|
|
LoRaMacClassBHaltBeaconing( );
|
|
|
|
// Reset Mac parameters
|
|
ResetMacParameters( );
|
|
|
|
// Switch off Radio
|
|
Radio.Sleep( );
|
|
|
|
// Return success
|
|
return LORAMAC_STATUS_OK;
|
|
}
|
|
else
|
|
{
|
|
return LORAMAC_STATUS_BUSY;
|
|
}
|
|
}
|