Fork of Semtech LoRaWAN stack
Fork of LoRaWAN-lib by
Diff: LoRaMac.cpp.orig
- Branch:
- v4.2.0
- Revision:
- 29:c7583fcfad8c
diff -r cda377b2a41f -r c7583fcfad8c LoRaMac.cpp.orig --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LoRaMac.cpp.orig Thu Nov 17 14:12:24 2016 +0000 @@ -0,0 +1,4309 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: LoRa MAC layer implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jäckle ( STACKFORCE ) +*/ +#include "board.h" + +#include "LoRaMacCrypto.h" +#include "LoRaMac.h" +#include "LoRaMacTest.h" + +/*! + * Maximum PHY layer payload size + */ +#define LORAMAC_PHY_MAXPAYLOAD 255 + +/*! + * Maximum MAC commands buffer size + */ +#define LORA_MAC_COMMAND_MAX_LENGTH 15 + +/*! + * FRMPayload overhead to be used when setting the Radio.SetMaxPayloadLength + * in RxWindowSetup function. + * Maximum PHYPayload = MaxPayloadOfDatarate/MaxPayloadOfDatarateRepeater + LORA_MAC_FRMPAYLOAD_OVERHEAD + */ +#define LORA_MAC_FRMPAYLOAD_OVERHEAD 13 // MHDR(1) + FHDR(7) + Port(1) + MIC(4) + +/*! + * Minimum number of available channels in Frequency Hopping Mode + */ +#define FRQ_HOP_CHNLS_MIN 50 + +/*! + * Minimum number of available channels in Hybrid Mode + */ +#define HYBRD_CHNLS_MIN 4 + + +/*! + * Device IEEE EUI + */ +static uint8_t *LoRaMacDevEui; + +/*! + * Application IEEE EUI + */ +static uint8_t *LoRaMacAppEui; + +/*! + * AES encryption/decryption cipher application key + */ +static uint8_t *LoRaMacAppKey; + +/*! + * AES encryption/decryption cipher network session key + */ +static uint8_t LoRaMacNwkSKey[] = +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/*! + * AES encryption/decryption cipher application session key + */ +static uint8_t LoRaMacAppSKey[] = +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/*! + * Device nonce is a random value extracted by issuing a sequence of RSSI + * measurements + */ +static uint16_t LoRaMacDevNonce; + +/*! + * Network ID ( 3 bytes ) + */ +static uint32_t LoRaMacNetID; + +/*! + * Mote Address + */ +static uint32_t LoRaMacDevAddr; + +/*! + * Multicast channels linked list + */ +static MulticastParams_t *MulticastChannels = NULL; + +/*! + * Actual device class + */ +static DeviceClass_t LoRaMacDeviceClass; + +/*! + * Indicates if the node is connected to a private or public network + */ +static bool PublicNetwork; + +/*! + * Indicates if the node supports repeaters + */ +static bool RepeaterSupport; + +/*! + * Buffer containing the data to be sent or received. + */ +static uint8_t LoRaMacBuffer[LORAMAC_PHY_MAXPAYLOAD]; + +/*! + * Length of packet in LoRaMacBuffer + */ +static uint16_t LoRaMacBufferPktLen = 0; + +/*! + * Buffer containing the upper layer data. + */ +static uint8_t LoRaMacPayload[LORAMAC_PHY_MAXPAYLOAD]; +static uint8_t LoRaMacRxPayload[LORAMAC_PHY_MAXPAYLOAD]; + +/*! + * LoRaMAC frame counter. Each time a packet is sent the counter is incremented. + * Only the 16 LSB bits are sent + */ +static uint32_t UpLinkCounter = 1; + +/*! + * LoRaMAC frame counter. Each time a packet is received the counter is incremented. + * Only the 16 LSB bits are received + */ +static uint32_t DownLinkCounter = 0; + +/*! + * IsPacketCounterFixed enables the MIC field tests by fixing the + * UpLinkCounter value + */ +static bool IsUpLinkCounterFixed = false; + +/*! + * Used for test purposes. Disables the opening of the reception windows. + */ +static bool IsRxWindowsEnabled = true; + +/*! + * Indicates if the MAC layer has already joined a network. + */ +static bool IsLoRaMacNetworkJoined = false; + +/*! + * LoRaMac ADR control status + */ +static bool AdrCtrlOn = true; + +/*! + * Counts the number of missed ADR acknowledgements + */ +static uint32_t AdrAckCounter = 0; + +/*! + * If the node has sent a FRAME_TYPE_DATA_CONFIRMED_UP this variable indicates + * if the nodes needs to manage the server acknowledgement. + */ +static bool NodeAckRequested = false; + +/*! + * 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 + */ +static bool SrvAckRequested = false; + +/*! + * Indicates if the MAC layer wants to send MAC commands + */ +static bool MacCommandsInNextTx = false; + +/*! + * Contains the current MacCommandsBuffer index + */ +static uint8_t MacCommandsBufferIndex = 0; + +/*! + * Contains the current MacCommandsBuffer index for MAC commands to repeat + */ +static uint8_t MacCommandsBufferToRepeatIndex = 0; + +/*! + * Buffer containing the MAC layer commands + */ +static uint8_t MacCommandsBuffer[LORA_MAC_COMMAND_MAX_LENGTH]; + +/*! + * Buffer containing the MAC layer commands which must be repeated + */ +static uint8_t MacCommandsBufferToRepeat[LORA_MAC_COMMAND_MAX_LENGTH]; + +#if defined( USE_BAND_433 ) +/*! + * Data rates table definition + */ +const uint8_t Datarates[] = { 12, 11, 10, 9, 8, 7, 7, 50 }; + +/*! + * Maximum payload with respect to the datarate index. Cannot operate with repeater. + */ +const uint8_t MaxPayloadOfDatarate[] = { 59, 59, 59, 123, 250, 250, 250, 250 }; + +/*! + * Maximum payload with respect to the datarate index. Can operate with repeater. + */ +const uint8_t MaxPayloadOfDatarateRepeater[] = { 59, 59, 59, 123, 230, 230, 230, 230 }; + +/*! + * Tx output powers table definition + */ +const int8_t TxPowers[] = { 20, 14, 11, 8, 5, 2 }; + +/*! + * LoRaMac bands + */ +static Band_t Bands[LORA_MAX_NB_BANDS] = +{ + BAND0, +}; + +/*! + * LoRaMAC channels + */ +static ChannelParams_t Channels[LORA_MAX_NB_CHANNELS] = +{ + LC1, + LC2, + LC3, +}; +#elif defined( USE_BAND_780 ) +/*! + * Data rates table definition + */ +const uint8_t Datarates[] = { 12, 11, 10, 9, 8, 7, 7, 50 }; + +/*! + * Maximum payload with respect to the datarate index. Cannot operate with repeater. + */ +const uint8_t MaxPayloadOfDatarate[] = { 59, 59, 59, 123, 250, 250, 250, 250 }; + +/*! + * Maximum payload with respect to the datarate index. Can operate with repeater. + */ +const uint8_t MaxPayloadOfDatarateRepeater[] = { 59, 59, 59, 123, 230, 230, 230, 230 }; + +/*! + * Tx output powers table definition + */ +const int8_t TxPowers[] = { 20, 14, 11, 8, 5, 2 }; + +/*! + * LoRaMac bands + */ +static Band_t Bands[LORA_MAX_NB_BANDS] = +{ + BAND0, +}; + +/*! + * LoRaMAC channels + */ +static ChannelParams_t Channels[LORA_MAX_NB_CHANNELS] = +{ + LC1, + LC2, + LC3, +}; +#elif defined( USE_BAND_868 ) +/*! + * Data rates table definition + */ +const uint8_t Datarates[] = { 12, 11, 10, 9, 8, 7, 7, 50 }; + +/*! + * Maximum payload with respect to the datarate index. Cannot operate with repeater. + */ +const uint8_t MaxPayloadOfDatarate[] = { 51, 51, 51, 115, 242, 242, 242, 242 }; + +/*! + * Maximum payload with respect to the datarate index. Can operate with repeater. + */ +const uint8_t MaxPayloadOfDatarateRepeater[] = { 51, 51, 51, 115, 222, 222, 222, 222 }; + +/*! + * Tx output powers table definition + */ +const int8_t TxPowers[] = { 20, 14, 11, 8, 5, 2 }; + +/*! + * LoRaMac bands + */ +static Band_t Bands[LORA_MAX_NB_BANDS] = +{ + BAND0, + BAND1, + BAND2, + BAND3, + BAND4, +}; + +/*! + * LoRaMAC channels + */ +static ChannelParams_t Channels[LORA_MAX_NB_CHANNELS] = +{ + LC1, + LC2, + LC3, +}; +#elif defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) +/*! + * Data rates table definition + */ +const uint8_t Datarates[] = { 10, 9, 8, 7, 8, 0, 0, 0, 12, 11, 10, 9, 8, 7, 0, 0 }; + +/*! + * Up/Down link data rates offset definition + */ +const uint8_t datarateOffsets[16][4] = +{ + { DR_10, DR_9 , DR_8 , DR_8 }, // DR_0 + { DR_11, DR_10, DR_9 , DR_8 }, // DR_1 + { DR_12, DR_11, DR_10, DR_9 }, // DR_2 + { DR_13, DR_12, DR_11, DR_10 }, // DR_3 + { DR_13, DR_13, DR_12, DR_11 }, // DR_4 + { 0xFF , 0xFF , 0xFF , 0xFF }, + { 0xFF , 0xFF , 0xFF , 0xFF }, + { 0xFF , 0xFF , 0xFF , 0xFF }, + { DR_8 , DR_8 , DR_8 , DR_8 }, + { DR_9 , DR_8 , DR_8 , DR_8 }, + { DR_10, DR_9 , DR_8 , DR_8 }, + { DR_11, DR_10, DR_9 , DR_8 }, + { DR_12, DR_11, DR_10, DR_9 }, + { DR_13, DR_12, DR_11, DR_10 }, + { 0xFF , 0xFF , 0xFF , 0xFF }, + { 0xFF , 0xFF , 0xFF , 0xFF }, +}; + +/*! + * Maximum payload with respect to the datarate index. Cannot operate with repeater. + */ +const uint8_t MaxPayloadOfDatarate[] = { 11, 53, 126, 242, 242, 0, 0, 0, 53, 129, 242, 242, 242, 242, 0, 0 }; + +/*! + * Maximum payload with respect to the datarate index. Can operate with repeater. + */ +const uint8_t MaxPayloadOfDatarateRepeater[] = { 11, 53, 126, 242, 242, 0, 0, 0, 33, 109, 222, 222, 222, 222, 0, 0 }; + +/*! + * Tx output powers table definition + */ +const int8_t TxPowers[] = { 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10 }; + +/*! + * LoRaMac bands + */ +static Band_t Bands[LORA_MAX_NB_BANDS] = +{ + BAND0, +}; + +/*! + * LoRaMAC channels + */ +static ChannelParams_t Channels[LORA_MAX_NB_CHANNELS]; + +/*! + * Contains the channels which remain to be applied. + */ +static uint16_t ChannelsMaskRemaining[6]; + +#if defined( USE_BAND_915 ) +/*! + * Last join request sub-band + */ +static int8_t LastJoinBlock; + +/*! + * Next join sub-band block + */ +static uint8_t NextJoinBlock; + +/*! + * Mask of 125 KHz sub-bands not used for join transmit + */ +static uint8_t JoinBlocksRemaining; + +/*! + * Mask of 500 KHz sub-bands not used for join transmit + */ +static uint8_t Join500KHzRemaining; + +#ifndef JOIN_BLOCK_ORDER +#define JOIN_BLOCK_ORDER { -1, -1, -1, -1, -1, -1, -1, -1 } +#endif + +/*! + * join sub-band block order + */ +static int8_t JoinBlock[8] = JOIN_BLOCK_ORDER; + +#endif + +/*! + * Defines the first channel for RX window 2 for US band + */ +#define LORAMAC_FIRST_RX2_CHANNEL ( (uint32_t) 923.3e6 ) + +/*! + * Defines the last channel for RX window 2 for US band + */ +#define LORAMAC_LAST_RX2_CHANNEL ( (uint32_t) 927.5e6 ) + +/*! + * Defines the step width of the channels for RX window 2 + */ +#define LORAMAC_STEPWIDTH_RX2_CHANNEL ( (uint32_t) 600e3 ) + + +#else + #error "Please define a frequency band in the compiler options." +#endif + +/*! + * LoRaMAC 2nd reception window settings + */ +static Rx2ChannelParams_t Rx2Channel = RX_WND_2_CHANNEL; + +/*! + * Datarate offset between uplink and downlink on first window + */ +static uint8_t Rx1DrOffset = 0; + +/*! + * Mask indicating which channels are enabled + */ +static uint16_t ChannelsMask[6]; + +/*! + * Channels Tx output power + */ +static int8_t ChannelsTxPower = LORAMAC_DEFAULT_TX_POWER; + +/*! + * Channels datarate + */ +static int8_t ChannelsDatarate = LORAMAC_DEFAULT_DATARATE; + +/*! + * Channels default datarate + */ +static int8_t ChannelsDefaultDatarate = LORAMAC_DEFAULT_DATARATE; + +/*! + * Number of uplink messages repetitions [1:15] (unconfirmed messages only) + */ +static uint8_t ChannelsNbRep = 1; + +/*! + * Uplink messages repetitions counter + */ +static uint8_t ChannelsNbRepCounter = 0; + +/*! + * Maximum duty cycle + * \remark Possibility to shutdown the device. + */ +static uint8_t MaxDCycle = 0; + +/*! + * Aggregated duty cycle management + */ +static uint16_t AggregatedDCycle; +static TimerTime_t AggregatedLastTxDoneTime; +static TimerTime_t AggregatedTimeOff; + +/*! + * Enables/Disables duty cycle management (Test only) + */ +static bool DutyCycleOn; + +/*! + * Current channel index + */ +static uint8_t Channel; + +static uint8_t LastTxChannel; + +/*! + * LoRaMac internal states + */ +enum eLoRaMacState +{ + MAC_IDLE = 0x00000000, + MAC_TX_RUNNING = 0x00000001, + MAC_RX = 0x00000002, + MAC_ACK_REQ = 0x00000004, + MAC_ACK_RETRY = 0x00000008, + MAC_TX_DELAYED = 0x00000010, + MAC_TX_CONFIG = 0x00000020, + MAC_RX_ABORT = 0x00000040, +}; + +/*! + * LoRaMac internal state + */ +uint32_t LoRaMacState = MAC_IDLE; + +/*! + * LoRaMac timer used to check the LoRaMacState (runs every second) + */ +static TimerEvent_t MacStateCheckTimer; + +/*! + * LoRaMac upper layer event functions + */ +static LoRaMacPrimitives_t *LoRaMacPrimitives; + +/*! + * LoRaMac upper layer callback functions + */ +static LoRaMacCallback_t *LoRaMacCallbacks; + +/*! + * Radio events function pointer + */ +static RadioEvents_t RadioEvents; + +/*! + * LoRaMac duty cycle delayed Tx timer + */ +static TimerEvent_t TxDelayedTimer; + +/*! + * LoRaMac reception windows timers + */ +static TimerEvent_t RxWindowTimer1; +static TimerEvent_t RxWindowTimer2; + +/*! + * LoRaMac reception windows delay from end of Tx + */ +static uint32_t ReceiveDelay1; +static uint32_t ReceiveDelay2; +static uint32_t JoinAcceptDelay1; +static uint32_t JoinAcceptDelay2; + +/*! + * LoRaMac reception windows delay + * \remark normal frame: RxWindowXDelay = ReceiveDelayX - RADIO_WAKEUP_TIME + * join frame : RxWindowXDelay = JoinAcceptDelayX - RADIO_WAKEUP_TIME + */ +static uint32_t RxWindow1Delay; +static uint32_t RxWindow2Delay; + +/*! + * LoRaMac maximum time a reception window stays open + */ +static uint32_t MaxRxWindow; + +/*! + * Acknowledge timeout timer. Used for packet retransmissions. + */ +static TimerEvent_t AckTimeoutTimer; + +/*! + * Number of trials to get a frame acknowledged + */ +static uint8_t AckTimeoutRetries = 1; + +/*! + * Number of trials to get a frame acknowledged + */ +static uint8_t AckTimeoutRetriesCounter = 1; + +/*! + * Indicates if the AckTimeout timer has expired or not + */ +static bool AckTimeoutRetry = false; + +/*! + * Last transmission time on air + */ +TimerTime_t TxTimeOnAir = 0; + +/*! + * Structure to hold an MCPS indication data. + */ +static McpsIndication_t McpsIndication; + +/*! + * Structure to hold MCPS confirm data. + */ +static McpsConfirm_t McpsConfirm; + +/*! + * Structure to hold MLME confirm data. + */ +static MlmeConfirm_t MlmeConfirm; + +/*! + * Holds the current rx window slot + */ +static uint8_t RxSlot = 0; + +/*! + * LoRaMac tx/rx operation state + */ +LoRaMacFlags_t LoRaMacFlags; + +/*! + * Join Retransmission back-off configuration + */ +LoRaMacRetransmissionDCycle_t JoinReTransmitDCycle[JOIN_NB_RETRANSMISSION_DCYCLES] = +{ + JOIN_RETRANSMISSION_DCYCLE1, + JOIN_RETRANSMISSION_DCYCLE2, + JOIN_RETRANSMISSION_DCYCLE3 +}; + +/*! + * Uptime of the last sent join request + */ +static TimerTime_t LastJoinTxTime; + +/*! + * Aggregated join request time on air + */ +static TimerTime_t JoinAggTimeOnAir; + + +/*! + * \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 Resend Frame timer event. + */ +static void OnMacStateCheckTimerEvent( void ); + +/*! + * \brief Function executed on duty cycle delayed Tx timer event + */ +static void OnTxDelayedTimerEvent( void ); + +/*! + * \brief Function executed on first Rx window timer event + */ +static void OnRxWindow1TimerEvent( void ); + +/*! + * \brief Function executed on second Rx window timer event + */ +static void OnRxWindow2TimerEvent( void ); + +/*! + * \brief Function executed on AckTimeout timer event + */ +static void OnAckTimeoutTimerEvent( void ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [OUT] Time to wait for the next transmission according to the duty + * cycle. + * + * \retval status Function status [1: OK, 0: Unable to find a channel on the + * current datarate] + */ +static bool SetNextChannel( TimerTime_t* time ); + +/*! + * \brief Sets the network to public or private. Updates the sync byte. + * + * \param [IN] enable if true, it enables a public network + */ +static void SetPublicNetwork( bool enable ); + +/*! + * \brief Initializes and opens the reception window + * + * \param [IN] freq window channel frequency + * \param [IN] datarate window channel datarate + * \param [IN] bandwidth window channel bandwidth + * \param [IN] timeout window channel timeout + */ +static void RxWindowSetup( uint32_t freq, int8_t datarate, uint32_t bandwidth, uint16_t timeout, bool rxContinuous ); + +/*! + * \brief Verifies if the RX window 2 frequency is in range + * + * \param [IN] freq window channel frequency + * + * \retval status Function status [1: OK, 0: Frequency not applicable] + */ +static bool Rx2FreqInRange( uint32_t freq ); + + +/*! + * \brief Adds a new MAC command to be sent. + * + * \Remark MAC layer internal function + * + * \param [in] cmd MAC command to be added + * [MOTE_MAC_LINK_CHECK_REQ, + * MOTE_MAC_LINK_ADR_ANS, + * MOTE_MAC_DUTY_CYCLE_ANS, + * MOTE_MAC_RX2_PARAM_SET_ANS, + * MOTE_MAC_DEV_STATUS_ANS + * MOTE_MAC_NEW_CHANNEL_ANS] + * \param [in] p1 1st parameter ( optional depends on the command ) + * \param [in] p2 2nd parameter ( optional depends on the command ) + * + * \retval status Function status [0: OK, 1: Unknown command, 2: Buffer full] + */ +static LoRaMacStatus_t AddMacCommand( uint8_t cmd, uint8_t p1, uint8_t p2 ); + +/*! + * \brief Parses the MAC commands which must be repeated. + * + * \Remark MAC layer internal function + * + * \param [IN] cmdBufIn Buffer which stores the MAC commands to send + * \param [IN] length Length of the input buffer to parse + * \param [OUT] cmdBufOut Buffer which stores the MAC commands which must be + * repeated. + * + * \retval Size of the MAC commands to repeat. + */ +static uint8_t ParseMacCommandsToRepeat( uint8_t* cmdBufIn, uint8_t length, uint8_t* cmdBufOut ); + +/*! + * \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 Counts the number of bits in a mask. + * + * \param [IN] mask A mask from which the function counts the active bits. + * \param [IN] nbBits The number of bits to check. + * + * \retval Number of enabled bits in the mask. + */ +static uint8_t CountBits( uint16_t mask, uint8_t nbBits ); + +#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) +/*! + * \brief Counts the number of enabled 125 kHz channels in the channel mask. + * This function can only be applied to US915 band. + * + * \param [IN] channelsMask Pointer to the first element of the channel mask + * + * \retval Number of enabled channels in the channel mask + */ +static uint8_t CountNbEnabled125kHzChannels( uint16_t *channelsMask ); + +#if defined( USE_BAND_915_HYBRID ) +/*! + * \brief Validates the correctness of the channel mask for US915, hybrid mode. + * + * \param [IN] mask Block definition to set. + * \param [OUT] channelsMask Pointer to the first element of the channel mask + */ +static void ReenableChannels( uint16_t mask, uint16_t* channelMask ); + +/*! + * \brief Validates the correctness of the channel mask for US915, hybrid mode. + * + * \param [IN] channelsMask Pointer to the first element of the channel mask + * + * \retval [true: channel mask correct, false: channel mask not correct] + */ +static bool ValidateChannelMask( uint16_t* channelMask ); +#endif + +#endif + +/*! + * \brief Limits the Tx power according to the number of enabled channels + * + * \retval Returns the maximum valid tx power + */ +static int8_t LimitTxPower( int8_t txPower ); + +/*! + * \brief Verifies, if a value is in a given range. + * + * \param value Value to verify, if it is in range + * + * \param min Minimum possible value + * + * \param max Maximum possible value + * + * \retval Returns the maximum valid tx power + */ +static bool ValueInRange( int8_t value, int8_t min, int8_t max ); + +/*! + * \brief Calculates the next datarate to set, when ADR is on or off + * + * \param [IN] adrEnabled Specify whether ADR is on or off + * + * \param [IN] isTx Set to true if called by transmit, set to false if a dry run + * + * \param [OUT] datarateOut Reports the datarate which will be used next + * + * \param [OUT] txpowerOut Reports the tx power which will be used next + * + * \retval Returns the state of ADR ack request + */ +static bool AdrNextDr( bool adrEnabled, bool isTx, int8_t* datarateOut, int8_t* txpowerOut ); + +/*! + * \brief Disables channel in a specified channel mask + * + * \param [IN] id - Id of the channel + * + * \param [IN] mask - Pointer to the channel mask to edit + * + * \retval [true, if disable was successful, false if not] + */ +static bool DisableChannelInMask( uint8_t id, uint16_t* mask ); + +/*! + * \brief Decodes MAC commands in the fOpts field and in the payload + */ +static void ProcessMacCommands( uint8_t *payload, uint8_t macIndex, uint8_t commandsSize, uint8_t snr ); + +/*! + * \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. + */ +LoRaMacStatus_t Send( LoRaMacHeader_t *macHdr, uint8_t fPort, void *fBuffer, uint16_t fBufferSize ); + +/*! + * \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. + */ +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 + * + * \retval Status of the operation + */ +static LoRaMacStatus_t ScheduleTx( void ); + +/* + * \brief Calculates the back-off time for the band of a channel. + * + * \param [IN] channel The last Tx channel index + */ +static void CalculateBackOff( uint8_t channel ); + +/*! + * \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 parameters + * \retval status Status of the operation. + */ +LoRaMacStatus_t SendFrameOnChannel( ChannelParams_t channel ); + + + +static void OnRadioTxDone( void ) +{ + TimerTime_t curTime = TimerGetCurrentTime( ); + if( LoRaMacDeviceClass != CLASS_C ) + { + Radio.Sleep( ); + } + else + { + OnRxWindow2TimerEvent( ); + } + + // Store last Tx channel + LastTxChannel = Channel; + // Update last tx done time for the current channel + Bands[Channels[LastTxChannel].Band].LastTxDoneTime = curTime; + // Update Aggregated last tx done time + AggregatedLastTxDoneTime = curTime; + + // Update join tx done and time on air + if( ( LoRaMacFlags.Bits.MlmeReq == 1 ) && ( MlmeConfirm.MlmeRequest == MLME_JOIN ) ) + { + JoinAggTimeOnAir += TxTimeOnAir; + LastJoinTxTime = curTime; + } + + if( IsRxWindowsEnabled == true ) + { + TimerSetValue( &RxWindowTimer1, RxWindow1Delay ); + TimerStart( &RxWindowTimer1 ); + if( LoRaMacDeviceClass != CLASS_C ) + { + TimerSetValue( &RxWindowTimer2, RxWindow2Delay ); + TimerStart( &RxWindowTimer2 ); + } + if( ( LoRaMacDeviceClass == CLASS_C ) || ( NodeAckRequested == true ) ) + { + TimerSetValue( &AckTimeoutTimer, RxWindow2Delay + ACK_TIMEOUT + + randr( -ACK_TIMEOUT_RND, ACK_TIMEOUT_RND ) ); + TimerStart( &AckTimeoutTimer ); + } + } + else + { + McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK; + MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT; + + if( LoRaMacFlags.Value == 0 ) + { + LoRaMacFlags.Bits.McpsReq = 1; + } + LoRaMacFlags.Bits.MacDone = 1; + } + + if( NodeAckRequested == false ) + { + McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK; + ChannelsNbRepCounter++; + } +} + +static void PrepareRxDoneAbort( void ) +{ + LoRaMacState |= MAC_RX_ABORT; + + if( NodeAckRequested ) + { + OnAckTimeoutTimerEvent( ); + } + + if( ( RxSlot == 0 ) && ( LoRaMacDeviceClass == CLASS_C ) ) + { + OnRxWindow2TimerEvent( ); + } + + LoRaMacFlags.Bits.McpsInd = 1; + LoRaMacFlags.Bits.MacDone = 1; + + // Trig OnMacCheckTimerEvent call as soon as possible + TimerSetValue( &MacStateCheckTimer, 1000 ); + TimerStart( &MacStateCheckTimer ); +} + +static void OnRadioRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ) +{ + LoRaMacHeader_t macHdr; + LoRaMacFrameCtrl_t fCtrl; + bool skipIndication = false; + + uint8_t pktHeaderLen = 0; + uint32_t address = 0; + uint8_t appPayloadStartIndex = 0; + uint8_t port = 0xFF; + uint8_t frameLen = 0; + uint32_t mic = 0; + uint32_t micRx = 0; + + uint16_t sequenceCounter = 0; + uint16_t sequenceCounterPrev = 0; + uint16_t sequenceCounterDiff = 0; + uint32_t downLinkCounter = 0; + + MulticastParams_t *curMulticastParams = NULL; + uint8_t *nwkSKey = LoRaMacNwkSKey; + uint8_t *appSKey = LoRaMacAppSKey; + + uint8_t multicast = 0; + + bool isMicOk = false; + + McpsConfirm.AckReceived = false; + McpsIndication.Rssi = rssi; + McpsIndication.Snr = snr; + McpsIndication.RxSlot = RxSlot; + McpsIndication.Port = 0; + McpsIndication.Multicast = 0; + McpsIndication.FramePending = 0; + McpsIndication.Buffer = NULL; + McpsIndication.BufferSize = 0; + McpsIndication.RxData = false; + McpsIndication.AckReceived = false; + McpsIndication.DownLinkCounter = 0; + McpsIndication.McpsIndication = MCPS_UNCONFIRMED; + + if( LoRaMacDeviceClass != CLASS_C ) + { + Radio.Sleep( ); + } + TimerStop( &RxWindowTimer2 ); + + macHdr.Value = payload[pktHeaderLen++]; + + switch( macHdr.Bits.MType ) + { + case FRAME_TYPE_JOIN_ACCEPT: + if( IsLoRaMacNetworkJoined == true ) + { + break; + } + LoRaMacJoinDecrypt( payload + 1, size - 1, LoRaMacAppKey, LoRaMacRxPayload + 1 ); + + LoRaMacRxPayload[0] = macHdr.Value; + + LoRaMacJoinComputeMic( LoRaMacRxPayload, size - LORAMAC_MFR_LEN, LoRaMacAppKey, &mic ); + + micRx |= ( uint32_t )LoRaMacRxPayload[size - LORAMAC_MFR_LEN]; + micRx |= ( ( uint32_t )LoRaMacRxPayload[size - LORAMAC_MFR_LEN + 1] << 8 ); + micRx |= ( ( uint32_t )LoRaMacRxPayload[size - LORAMAC_MFR_LEN + 2] << 16 ); + micRx |= ( ( uint32_t )LoRaMacRxPayload[size - LORAMAC_MFR_LEN + 3] << 24 ); + + if( micRx == mic ) + { + LoRaMacJoinComputeSKeys( LoRaMacAppKey, LoRaMacRxPayload + 1, LoRaMacDevNonce, LoRaMacNwkSKey, LoRaMacAppSKey ); + + LoRaMacNetID = ( uint32_t )LoRaMacRxPayload[4]; + LoRaMacNetID |= ( ( uint32_t )LoRaMacRxPayload[5] << 8 ); + LoRaMacNetID |= ( ( uint32_t )LoRaMacRxPayload[6] << 16 ); + + LoRaMacDevAddr = ( uint32_t )LoRaMacRxPayload[7]; + LoRaMacDevAddr |= ( ( uint32_t )LoRaMacRxPayload[8] << 8 ); + LoRaMacDevAddr |= ( ( uint32_t )LoRaMacRxPayload[9] << 16 ); + LoRaMacDevAddr |= ( ( uint32_t )LoRaMacRxPayload[10] << 24 ); + + // DLSettings + Rx1DrOffset = ( LoRaMacRxPayload[11] >> 4 ) & 0x07; + Rx2Channel.Datarate = LoRaMacRxPayload[11] & 0x0F; + // RxDelay + ReceiveDelay1 = ( LoRaMacRxPayload[12] & 0x0F ); + if( ReceiveDelay1 == 0 ) + { + ReceiveDelay1 = 1; + } + ReceiveDelay1 *= 1e6; + ReceiveDelay2 = ReceiveDelay1 + 1e6; + +#if !( defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) ) + //CFList + if( ( size - 1 ) > 16 ) + { + ChannelParams_t param; + param.DrRange.Value = ( DR_5 << 4 ) | DR_0; + + LoRaMacState |= MAC_TX_CONFIG; + for( uint8_t i = 3, j = 0; i < ( 5 + 3 ); i++, j += 3 ) + { + param.Frequency = ( ( uint32_t )LoRaMacRxPayload[13 + j] | ( ( uint32_t )LoRaMacRxPayload[14 + j] << 8 ) | ( ( uint32_t )LoRaMacRxPayload[15 + j] << 16 ) ) * 100; + LoRaMacChannelAdd( i, param ); + } + LoRaMacState &= ~MAC_TX_CONFIG; + } +#endif + MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK; + IsLoRaMacNetworkJoined = true; + + // Do not change the datarate + // ChannelsDatarate = ChannelsDefaultDatarate; + } + else + { + MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL; + } + break; + case FRAME_TYPE_DATA_CONFIRMED_DOWN: + case FRAME_TYPE_DATA_UNCONFIRMED_DOWN: + { + address = payload[pktHeaderLen++]; + address |= ( (uint32_t)payload[pktHeaderLen++] << 8 ); + address |= ( (uint32_t)payload[pktHeaderLen++] << 16 ); + address |= ( (uint32_t)payload[pktHeaderLen++] << 24 ); + + if( address != LoRaMacDevAddr ) + { + curMulticastParams = MulticastChannels; + while( curMulticastParams != NULL ) + { + if( address == curMulticastParams->Address ) + { + multicast = 1; + nwkSKey = curMulticastParams->NwkSKey; + appSKey = curMulticastParams->AppSKey; + downLinkCounter = curMulticastParams->DownLinkCounter; + break; + } + curMulticastParams = curMulticastParams->Next; + } + if( multicast == 0 ) + { + // We are not the destination of this frame. + McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL; + PrepareRxDoneAbort( ); + return; + } + } + else + { + multicast = 0; + nwkSKey = LoRaMacNwkSKey; + appSKey = LoRaMacAppSKey; + downLinkCounter = DownLinkCounter; + } + + fCtrl.Value = payload[pktHeaderLen++]; + + sequenceCounter = ( uint16_t )payload[pktHeaderLen++]; + sequenceCounter |= ( uint16_t )payload[pktHeaderLen++] << 8; + + appPayloadStartIndex = 8 + fCtrl.Bits.FOptsLen; + + micRx |= ( uint32_t )payload[size - LORAMAC_MFR_LEN]; + micRx |= ( ( uint32_t )payload[size - LORAMAC_MFR_LEN + 1] << 8 ); + micRx |= ( ( uint32_t )payload[size - LORAMAC_MFR_LEN + 2] << 16 ); + micRx |= ( ( uint32_t )payload[size - LORAMAC_MFR_LEN + 3] << 24 ); + + sequenceCounterPrev = ( uint16_t )downLinkCounter; + sequenceCounterDiff = ( sequenceCounter - sequenceCounterPrev ); + + if( sequenceCounterDiff < ( 1 << 15 ) ) + { + downLinkCounter += sequenceCounterDiff; + LoRaMacComputeMic( payload, size - LORAMAC_MFR_LEN, nwkSKey, address, DOWN_LINK, downLinkCounter, &mic ); + if( micRx == mic ) + { + isMicOk = true; + } + } + else + { + // check for sequence roll-over + uint32_t downLinkCounterTmp = downLinkCounter + 0x10000 + ( int16_t )sequenceCounterDiff; + LoRaMacComputeMic( payload, size - LORAMAC_MFR_LEN, nwkSKey, address, DOWN_LINK, downLinkCounterTmp, &mic ); + if( micRx == mic ) + { + isMicOk = true; + downLinkCounter = downLinkCounterTmp; + } + } + + // Check for a the maximum allowed counter difference + if( sequenceCounterDiff >= MAX_FCNT_GAP ) + { + McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS; + McpsIndication.DownLinkCounter = downLinkCounter; + PrepareRxDoneAbort( ); + return; + } + + if( isMicOk == true ) + { + McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_OK; + McpsIndication.Multicast = multicast; + McpsIndication.FramePending = fCtrl.Bits.FPending; + McpsIndication.Buffer = NULL; + McpsIndication.BufferSize = 0; + McpsIndication.DownLinkCounter = downLinkCounter; + + McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK; + + AdrAckCounter = 0; + MacCommandsBufferToRepeatIndex = 0; + + // Update 32 bits downlink counter + if( multicast == 1 ) + { + McpsIndication.McpsIndication = MCPS_MULTICAST; + + if( ( curMulticastParams->DownLinkCounter == downLinkCounter ) && + ( curMulticastParams->DownLinkCounter != 0 ) ) + { + McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED; + McpsIndication.DownLinkCounter = downLinkCounter; + PrepareRxDoneAbort( ); + return; + } + curMulticastParams->DownLinkCounter = downLinkCounter; + } + else + { + if( macHdr.Bits.MType == FRAME_TYPE_DATA_CONFIRMED_DOWN ) + { + SrvAckRequested = true; + McpsIndication.McpsIndication = MCPS_CONFIRMED; + + if( ( DownLinkCounter == downLinkCounter ) && + ( DownLinkCounter != 0 ) ) + { + // Duplicated confirmed downlink. Skip indication. + skipIndication = true; + } + } + else + { + SrvAckRequested = false; + McpsIndication.McpsIndication = MCPS_UNCONFIRMED; + + if( ( DownLinkCounter == downLinkCounter ) && + ( DownLinkCounter != 0 ) ) + { + McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED; + McpsIndication.DownLinkCounter = downLinkCounter; + PrepareRxDoneAbort( ); + return; + } + } + DownLinkCounter = downLinkCounter; + } + + + if( ( ( size - 4 ) - appPayloadStartIndex ) > 0 ) + { + port = payload[appPayloadStartIndex++]; + frameLen = ( size - 4 ) - appPayloadStartIndex; + + McpsIndication.Port = port; + + if( port == 0 ) + { + if( fCtrl.Bits.FOptsLen == 0 ) + { + LoRaMacPayloadDecrypt( payload + appPayloadStartIndex, + frameLen, + nwkSKey, + address, + DOWN_LINK, + downLinkCounter, + LoRaMacRxPayload ); + + // Decode frame payload MAC commands + ProcessMacCommands( LoRaMacRxPayload, 0, frameLen, snr ); + } + else + { + skipIndication = true; + } + } + else + { + if( fCtrl.Bits.FOptsLen > 0 ) + { + // Decode Options field MAC commands. Omit the fPort. + ProcessMacCommands( payload, 8, appPayloadStartIndex - 1, snr ); + } + + LoRaMacPayloadDecrypt( payload + appPayloadStartIndex, + frameLen, + appSKey, + address, + DOWN_LINK, + downLinkCounter, + LoRaMacRxPayload ); + + if( skipIndication == false ) + { + McpsIndication.Buffer = LoRaMacRxPayload; + McpsIndication.BufferSize = frameLen; + McpsIndication.RxData = true; + } + } + } + else + { + if( fCtrl.Bits.FOptsLen > 0 ) + { + // Decode Options field MAC commands + ProcessMacCommands( payload, 8, appPayloadStartIndex, snr ); + } + } + + if( skipIndication == false ) + { + // Check if the frame is an acknowledgement + if( fCtrl.Bits.Ack == 1 ) + { + McpsConfirm.AckReceived = true; + McpsIndication.AckReceived = true; + + // Stop the AckTimeout timer as no more retransmissions + // are needed. + TimerStop( &AckTimeoutTimer ); + } + else + { + McpsConfirm.AckReceived = false; + + if( AckTimeoutRetriesCounter > AckTimeoutRetries ) + { + // Stop the AckTimeout timer as no more retransmissions + // are needed. + TimerStop( &AckTimeoutTimer ); + } + } + LoRaMacFlags.Bits.McpsInd = 1; + } + } + else + { + McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_MIC_FAIL; + + PrepareRxDoneAbort( ); + return; + } + } + break; + case FRAME_TYPE_PROPRIETARY: + { + memcpy1( LoRaMacRxPayload, &payload[pktHeaderLen], size ); + + McpsIndication.McpsIndication = MCPS_PROPRIETARY; + McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_OK; + McpsIndication.Buffer = LoRaMacRxPayload; + McpsIndication.BufferSize = size - pktHeaderLen; + + LoRaMacFlags.Bits.McpsInd = 1; + break; + } + default: + McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + PrepareRxDoneAbort( ); + break; + } + + if( ( RxSlot == 0 ) && ( LoRaMacDeviceClass == CLASS_C ) ) + { + OnRxWindow2TimerEvent( ); + } + LoRaMacFlags.Bits.MacDone = 1; + + // Trig OnMacCheckTimerEvent call as soon as possible + TimerSetValue( &MacStateCheckTimer, 1000 ); + TimerStart( &MacStateCheckTimer ); +} + +static void OnRadioTxTimeout( void ) +{ + if( LoRaMacDeviceClass != CLASS_C ) + { + Radio.Sleep( ); + } + else + { + OnRxWindow2TimerEvent( ); + } + + McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT; + MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT; + LoRaMacFlags.Bits.MacDone = 1; +} + +static void OnRadioRxError( void ) +{ + if( LoRaMacDeviceClass != CLASS_C ) + { + Radio.Sleep( ); + } + else + { + OnRxWindow2TimerEvent( ); + } + + if( RxSlot == 1 ) + { + if( NodeAckRequested == true ) + { + McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX2_ERROR; + } + MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX2_ERROR; + LoRaMacFlags.Bits.MacDone = 1; + } +} + +static void OnRadioRxTimeout( void ) +{ + if( LoRaMacDeviceClass != CLASS_C ) + { + Radio.Sleep( ); + } + else + { + OnRxWindow2TimerEvent( ); + } + + if( RxSlot == 1 ) + { + if( NodeAckRequested == true ) + { + McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT; + } + MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT; + LoRaMacFlags.Bits.MacDone = 1; + } +} + +static void OnMacStateCheckTimerEvent( void ) +{ + TimerStop( &MacStateCheckTimer ); + bool txTimeout = false; + + if( LoRaMacFlags.Bits.MacDone == 1 ) + { + if( ( LoRaMacState & MAC_RX_ABORT ) == MAC_RX_ABORT ) + { + LoRaMacState &= ~MAC_RX_ABORT; + LoRaMacState &= ~MAC_TX_RUNNING; + } + + if( ( LoRaMacFlags.Bits.MlmeReq == 1 ) || ( ( LoRaMacFlags.Bits.McpsReq == 1 ) ) ) + { + if( ( McpsConfirm.Status == LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT ) || + ( MlmeConfirm.Status == LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT ) ) + { + // Stop transmit cycle due to tx timeout. + LoRaMacState &= ~MAC_TX_RUNNING; + McpsConfirm.NbRetries = AckTimeoutRetriesCounter; + McpsConfirm.AckReceived = false; + McpsConfirm.TxTimeOnAir = 0; + txTimeout = true; + } + } + + if( ( NodeAckRequested == false ) && ( txTimeout == false ) ) + { + if( LoRaMacFlags.Bits.MlmeReq == 1 ) + { + if( MlmeConfirm.MlmeRequest == MLME_JOIN ) + { + if( MlmeConfirm.Status == LORAMAC_EVENT_INFO_STATUS_OK ) + { + UpLinkCounter = 0; + } + // Join messages aren't repeated automatically + ChannelsNbRepCounter = ChannelsNbRep; + } + } + if( ( LoRaMacFlags.Bits.MlmeReq == 1 ) || ( ( LoRaMacFlags.Bits.McpsReq == 1 ) ) ) + { + if( ( ChannelsNbRepCounter >= ChannelsNbRep ) || ( LoRaMacFlags.Bits.McpsInd == 1 ) ) + { + ChannelsNbRepCounter = 0; + + AdrAckCounter++; + if( IsUpLinkCounterFixed == false ) + { + UpLinkCounter++; + } + + LoRaMacState &= ~MAC_TX_RUNNING; + } + else + { + LoRaMacFlags.Bits.MacDone = 0; + // Sends the same frame again + ScheduleTx( ); + } + } + } + + if( LoRaMacFlags.Bits.McpsInd == 1 ) + { + if( ( McpsConfirm.AckReceived == true ) || ( AckTimeoutRetriesCounter > AckTimeoutRetries ) ) + { + AckTimeoutRetry = false; + NodeAckRequested = false; + if( IsUpLinkCounterFixed == false ) + { + UpLinkCounter++; + } + McpsConfirm.NbRetries = AckTimeoutRetriesCounter; + + LoRaMacState &= ~MAC_TX_RUNNING; + } + } + + if( ( AckTimeoutRetry == true ) && ( ( LoRaMacState & MAC_TX_DELAYED ) == 0 ) ) + { + AckTimeoutRetry = false; + if( ( AckTimeoutRetriesCounter < AckTimeoutRetries ) && ( AckTimeoutRetriesCounter <= MAX_ACK_RETRIES ) ) + { + AckTimeoutRetriesCounter++; + + if( ( AckTimeoutRetriesCounter % 2 ) == 1 ) + { + ChannelsDatarate = MAX( ChannelsDatarate - 1, LORAMAC_TX_MIN_DATARATE ); + // Check if new datarate is valid for the Payload length + if( ValidatePayloadLength( LoRaMacBufferPktLen - 13, ChannelsDatarate, 0 ) == false ) + { + // If invalid payload length, then revert to previous datarate + ChannelsDatarate++; + } + } + LoRaMacFlags.Bits.MacDone = 0; + // Sends the same frame again + ScheduleTx( ); + } + else + { +#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 ) + // Re-enable default channels LC1, LC2, LC3 + ChannelsMask[0] = ChannelsMask[0] | ( LC( 1 ) + LC( 2 ) + LC( 3 ) ); +#elif !defined( USE_BAND_915 ) && !defined( USE_BAND_915_HYBRID ) + #error "Please define a frequency band in the compiler options." +#endif + LoRaMacState &= ~MAC_TX_RUNNING; + + NodeAckRequested = false; + McpsConfirm.AckReceived = false; + McpsConfirm.NbRetries = AckTimeoutRetriesCounter; + if( IsUpLinkCounterFixed == false ) + { + UpLinkCounter++; + } + } + } + } + // Handle reception for Class B and Class C + if( ( LoRaMacState & MAC_RX ) == MAC_RX ) + { + LoRaMacState &= ~MAC_RX; + } + if( LoRaMacState == MAC_IDLE ) + { + if( LoRaMacFlags.Bits.McpsReq == 1 ) + { + LoRaMacPrimitives->MacMcpsConfirm( &McpsConfirm ); + LoRaMacFlags.Bits.McpsReq = 0; + } + + if( LoRaMacFlags.Bits.MlmeReq == 1 ) + { + LoRaMacPrimitives->MacMlmeConfirm( &MlmeConfirm ); + LoRaMacFlags.Bits.MlmeReq = 0; + } + + LoRaMacFlags.Bits.MacDone = 0; + } + else + { + // Operation not finished restart timer + TimerSetValue( &MacStateCheckTimer, MAC_STATE_CHECK_TIMEOUT ); + TimerStart( &MacStateCheckTimer ); + } + + if( LoRaMacFlags.Bits.McpsInd == 1 ) + { + LoRaMacPrimitives->MacMcpsIndication( &McpsIndication ); + LoRaMacFlags.Bits.McpsInd = 0; + } +} + +static void OnTxDelayedTimerEvent( void ) +{ + TimerStop( &TxDelayedTimer ); + LoRaMacState &= ~MAC_TX_DELAYED; + + ScheduleTx( ); +} + +static void OnRxWindow1TimerEvent( void ) +{ + uint16_t symbTimeout = 5; // DR_2, DR_1, DR_0 + int8_t datarate = 0; + uint32_t bandwidth = 0; // LoRa 125 kHz + + TimerStop( &RxWindowTimer1 ); + RxSlot = 0; + + if( LoRaMacDeviceClass == CLASS_C ) + { + Radio.Standby( ); + } + +#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 ) + datarate = ChannelsDatarate - Rx1DrOffset; + if( datarate < 0 ) + { + datarate = DR_0; + } + + // For higher datarates, we increase the number of symbols generating a Rx Timeout + if( ( datarate == DR_3 ) || ( datarate == DR_4 ) ) + { // DR_4, DR_3 + symbTimeout = 8; + } + else if( datarate == DR_5 ) + { + symbTimeout = 10; + } + else if( datarate == DR_6 ) + {// LoRa 250 kHz + bandwidth = 1; + symbTimeout = 14; + } + RxWindowSetup( Channels[Channel].Frequency, datarate, bandwidth, symbTimeout, false ); +#elif ( defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) ) + datarate = datarateOffsets[ChannelsDatarate][Rx1DrOffset]; + if( datarate < 0 ) + { + datarate = DR_0; + } + // For higher datarates, we increase the number of symbols generating a Rx Timeout + switch( datarate ) + { + case DR_0: // SF10 - BW125 + symbTimeout = 5; + break; + + case DR_1: // SF9 - BW125 + case DR_2: // SF8 - BW125 + case DR_8: // SF12 - BW500 + case DR_9: // SF11 - BW500 + case DR_10: // SF10 - BW500 + symbTimeout = 8; + break; + + case DR_3: // SF7 - BW125 + case DR_11: // SF9 - BW500 + symbTimeout = 10; + break; + + case DR_4: // SF8 - BW500 + case DR_12: // SF8 - BW500 + symbTimeout = 14; + break; + + case DR_13: // SF7 - BW500 + symbTimeout = 16; + break; + default: + break; + } + if( datarate >= DR_4 ) + {// LoRa 500 kHz + bandwidth = 2; + } + RxWindowSetup( 923.3e6 + ( Channel % 8 ) * 600e3, datarate, bandwidth, symbTimeout, false ); +#else + #error "Please define a frequency band in the compiler options." +#endif +} + +static void OnRxWindow2TimerEvent( void ) +{ + uint16_t symbTimeout = 5; // DR_2, DR_1, DR_0 + uint32_t bandwidth = 0; // LoRa 125 kHz + + TimerStop( &RxWindowTimer2 ); + RxSlot = 1; + +#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 ) + // For higher datarates, we increase the number of symbols generating a Rx Timeout + if( ( Rx2Channel.Datarate == DR_3 ) || ( Rx2Channel.Datarate == DR_4 ) ) + { // DR_4, DR_3 + symbTimeout = 8; + } + else if( Rx2Channel.Datarate == DR_5 ) + { + symbTimeout = 10; + } + else if( Rx2Channel.Datarate == DR_6 ) + {// LoRa 250 kHz + bandwidth = 1; + symbTimeout = 14; + } +#elif ( defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) ) + // For higher datarates, we increase the number of symbols generating a Rx Timeout + switch( Rx2Channel.Datarate ) + { + case DR_0: // SF10 - BW125 + symbTimeout = 5; + break; + + case DR_1: // SF9 - BW125 + case DR_2: // SF8 - BW125 + case DR_8: // SF12 - BW500 + case DR_9: // SF11 - BW500 + case DR_10: // SF10 - BW500 + symbTimeout = 8; + break; + + case DR_3: // SF7 - BW125 + case DR_11: // SF9 - BW500 + symbTimeout = 10; + break; + + case DR_4: // SF8 - BW500 + case DR_12: // SF8 - BW500 + symbTimeout = 14; + break; + + case DR_13: // SF7 - BW500 + symbTimeout = 16; + break; + default: + break; + } + if( Rx2Channel.Datarate >= DR_4 ) + {// LoRa 500 kHz + bandwidth = 2; + } +#else + #error "Please define a frequency band in the compiler options." +#endif + if( LoRaMacDeviceClass != CLASS_C ) + { + RxWindowSetup( Rx2Channel.Frequency, Rx2Channel.Datarate, bandwidth, symbTimeout, false ); + } + else + { + RxWindowSetup( Rx2Channel.Frequency, Rx2Channel.Datarate, bandwidth, symbTimeout, true ); + } +} + +static void OnAckTimeoutTimerEvent( void ) +{ + TimerStop( &AckTimeoutTimer ); + + if( NodeAckRequested == true ) + { + AckTimeoutRetry = true; + LoRaMacState &= ~MAC_ACK_REQ; + } + if( LoRaMacDeviceClass == CLASS_C ) + { + LoRaMacFlags.Bits.MacDone = 1; + } +} + +#if defined( USE_BAND_915 ) +uint8_t GetNextJoinChannel( uint8_t* enabledChannels, uint8_t nbEnabledChannels ) +{ + int8_t channel = -1; + uint8_t block; + uint8_t i; + + // Use 125KHz channel + if( ChannelsDatarate < DR_4 ) + { + block = JoinBlock[NextJoinBlock]; + NextJoinBlock = (NextJoinBlock + 1) % 8; + + // If next block is greater than max block then randomly select the next block + if( block >= 8 ) + block = randr(0, 7); + + // Start search for next join channel at the selected block + for(i = 0; ( i < 8 ) && ( channel == -1 ); i++) + { + uint8_t curBlock = (block + i) % 8; + uint8_t chMask; + + // Cycle through all blocks before using a previously used block + if( ( ( JoinBlocksRemaining & ( 1 << curBlock ) ) != 0) ) + { + chMask = ( ChannelsMaskRemaining[curBlock/2] >> ( curBlock & 1 ? 8 : 0 ) ) & 0xff; + if( chMask != 0) + { + channel = randr(0, 7); + for(uint8_t i = 0; ( i < 8 ); i++) + { + if( ( chMask & ( 1 << channel ) ) != 0 ) + { + uint16_t chMaskTmp = ( 1 << channel ); + chMaskTmp = chMaskTmp << ( curBlock & 1 ? 8 : 0 ); + channel = channel + ( curBlock * 8 ); + JoinBlocksRemaining &= ~(1 << curBlock); + ChannelsMaskRemaining[curBlock/2] &= ~chMaskTmp; + if( ( JoinBlocksRemaining == 0 ) ) + { + JoinBlocksRemaining = 0xFF; + } + break; + } + else + { + channel = ( channel + 1 ) % 8; + } + } + } + } + } + + // No next channel case should never happen since ScheduleTx has already + // checked for at least one enabled channel and if none re-enabled all channels. + // But if we find ourselves without a channel then randomly select one + if ( i >= 8 ) + { + channel = randr( 0, 63 ); + } + } + // Use 500 KHz channel + else + { + channel = randr( 0, 7 ); + for(i = 0; i < 8; i++) + { + uint8_t curChannel = (channel + i) % 8; + + if( ( ( Join500KHzRemaining & ( 1 << curChannel ) ) != 0 ) ) + { + Join500KHzRemaining &= ~(1 << curChannel); + channel = 64 + curChannel; + if( Join500KHzRemaining == 0 ) + { + Join500KHzRemaining = 0xFF; + } + break; + } + } + } + + for( i = 0; i < nbEnabledChannels; i++ ) + { + if( enabledChannels[i] == channel ) + break; + } + + if( i == nbEnabledChannels ) + { + channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; + } + + LastJoinBlock = channel / 8; + + return channel; +} +#endif + +static bool SetNextChannel( TimerTime_t* time ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t delayTx = 0; + uint8_t enabledChannels[LORA_MAX_NB_CHANNELS]; + TimerTime_t nextTxDelay = ( TimerTime_t )( -1 ); + + memset1( enabledChannels, 0, LORA_MAX_NB_CHANNELS ); + +#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) + if( CountNbEnabled125kHzChannels( ChannelsMaskRemaining ) == 0 ) + { // Restore default channels + memcpy1( ( uint8_t* ) ChannelsMaskRemaining, ( uint8_t* ) ChannelsMask, 8 ); + } + if( ( ChannelsDatarate >= DR_4 ) && ( ( ChannelsMaskRemaining[4] & 0x00FF ) == 0 ) ) + { // Make sure, that the channels are activated + ChannelsMaskRemaining[4] = ChannelsMask[4]; + } +#else + if( CountBits( ChannelsMask[0], 16 ) == 0 ) + { + // Re-enable default channels, if no channel is enabled + ChannelsMask[0] = ChannelsMask[0] | ( LC( 1 ) + LC( 2 ) + LC( 3 ) ); + } +#endif + + // Update Aggregated duty cycle + if( AggregatedTimeOff <= TimerGetElapsedTime( AggregatedLastTxDoneTime ) ) + { + AggregatedTimeOff = 0; + + // Update bands Time OFF + for( uint8_t i = 0; i < LORA_MAX_NB_BANDS; i++ ) + { + if( DutyCycleOn == true ) + { + if( Bands[i].TimeOff <= TimerGetElapsedTime( Bands[i].LastTxDoneTime ) ) + { + Bands[i].TimeOff = 0; + } + if( Bands[i].TimeOff != 0 ) + { + nextTxDelay = MIN( Bands[i].TimeOff - + TimerGetElapsedTime( Bands[i].LastTxDoneTime ), + nextTxDelay ); + } + } + else + { + nextTxDelay = 0; + Bands[i].TimeOff = 0; + } + } + + // Search how many channels are enabled + for( uint8_t i = 0, k = 0; i < LORA_MAX_NB_CHANNELS; i += 16, k++ ) + { + for( uint8_t j = 0; j < 16; j++ ) + { +#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) + if( ( ChannelsMaskRemaining[k] & ( 1 << j ) ) != 0 ) +#else + if( ( ChannelsMask[k] & ( 1 << j ) ) != 0 ) +#endif + { + if( Channels[i + j].Frequency == 0 ) + { // Check if the channel is enabled + continue; + } +#if defined( USE_BAND_868 ) || defined( USE_BAND_433 ) || defined( USE_BAND_780 ) + if( IsLoRaMacNetworkJoined == false ) + { + if( ( JOIN_CHANNELS & ( 1 << j ) ) == 0 ) + { + continue; + } + } +#endif + if( ( ( Channels[i + j].DrRange.Fields.Min <= ChannelsDatarate ) && + ( ChannelsDatarate <= Channels[i + j].DrRange.Fields.Max ) ) == false ) + { // Check if the current channel selection supports the given datarate + continue; + } + if( Bands[Channels[i + j].Band].TimeOff > 0 ) + { // Check if the band is available for transmission + delayTx++; + continue; + } + enabledChannels[nbEnabledChannels++] = i + j; + } + } + } + } + else + { + delayTx++; + nextTxDelay = AggregatedTimeOff - TimerGetElapsedTime( AggregatedLastTxDoneTime ); + } + + if( nbEnabledChannels > 0 ) + { +#if defined( USE_BAND_915 ) + if ( IsLoRaMacNetworkJoined == false ) + { + Channel = GetNextJoinChannel(enabledChannels, nbEnabledChannels); + } + // Send first uplink on channel from same sub-band as the join + else if( ( UpLinkCounter == 1 ) && ( LastJoinBlock != -1 ) ) + { + uint8_t i; + uint8_t blockFirstChannel = LastJoinBlock*8; + + Channel = randr( blockFirstChannel, blockFirstChannel + 7 ); + + // Check channel is enabled + for( i = 0; i < nbEnabledChannels; i++ ) + { + if( Channel == enabledChannels[i] ) + break; + } + + // If channel is not enabled fallback to selecting from the list of + // enabled channels + if (i == nbEnabledChannels) + { + Channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; + } + } + else +#endif + { + Channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; + } + +#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) + if( Channel < ( LORA_MAX_NB_CHANNELS - 8 ) ) + { + DisableChannelInMask( Channel, ChannelsMaskRemaining ); + } +#endif + *time = 0; + return true; + } + else + { + if( delayTx > 0 ) + { + // Delay transmission due to AggregatedTimeOff or to a band time off + *time = nextTxDelay; + return true; + } + // Datarate not supported by any channel + *time = 0; + return false; + } +} + +static void SetPublicNetwork( bool enable ) +{ + PublicNetwork = enable; + Radio.SetModem( MODEM_LORA ); + if( PublicNetwork == true ) + { + // Change LoRa modem SyncWord + Radio.Write( REG_LR_SYNCWORD, LORA_MAC_PUBLIC_SYNCWORD ); + } + else + { + // Change LoRa modem SyncWord + Radio.Write( REG_LR_SYNCWORD, LORA_MAC_PRIVATE_SYNCWORD ); + } +} + +static void RxWindowSetup( uint32_t freq, int8_t datarate, uint32_t bandwidth, uint16_t timeout, bool rxContinuous ) +{ + uint8_t downlinkDatarate = Datarates[datarate]; + RadioModems_t modem; + + if( Radio.GetStatus( ) == RF_IDLE ) + { + Radio.SetChannel( freq ); + + // Store downlink datarate + McpsIndication.RxDatarate = ( uint8_t ) datarate; + +#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 ) + if( datarate == DR_7 ) + { + modem = MODEM_FSK; + Radio.SetRxConfig( modem, 50e3, downlinkDatarate * 1e3, 0, 83.333e3, 5, 0, false, 0, true, 0, 0, false, rxContinuous ); + } + else + { + modem = MODEM_LORA; + Radio.SetRxConfig( modem, bandwidth, downlinkDatarate, 1, 0, 8, timeout, false, 0, false, 0, 0, true, rxContinuous ); + } +#elif defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) + modem = MODEM_LORA; + Radio.SetRxConfig( modem, bandwidth, downlinkDatarate, 1, 0, 8, timeout, false, 0, false, 0, 0, true, rxContinuous ); +#endif + + if( RepeaterSupport == true ) + { + Radio.SetMaxPayloadLength( modem, MaxPayloadOfDatarateRepeater[datarate] + LORA_MAC_FRMPAYLOAD_OVERHEAD ); + } + else + { + Radio.SetMaxPayloadLength( modem, MaxPayloadOfDatarate[datarate] + LORA_MAC_FRMPAYLOAD_OVERHEAD ); + } + + if( rxContinuous == false ) + { + Radio.Rx( MaxRxWindow ); + } + else + { + Radio.Rx( 0 ); // Continuous mode + } + } +} + +static bool Rx2FreqInRange( uint32_t freq ) +{ +#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 ) + if( Radio.CheckRfFrequency( freq ) == true ) +#elif ( defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) ) + if( ( Radio.CheckRfFrequency( freq ) == true ) && + ( freq >= LORAMAC_FIRST_RX2_CHANNEL ) && + ( freq <= LORAMAC_LAST_RX2_CHANNEL ) && + ( ( ( freq - ( uint32_t ) LORAMAC_FIRST_RX2_CHANNEL ) % ( uint32_t ) LORAMAC_STEPWIDTH_RX2_CHANNEL ) == 0 ) ) +#endif + { + return true; + } + return false; +} + + +static bool ValidatePayloadLength( uint8_t lenN, int8_t datarate, uint8_t fOptsLen ) +{ + uint16_t maxN = 0; + uint16_t payloadSize = 0; + + // Get the maximum payload length + if( RepeaterSupport == true ) + { + maxN = MaxPayloadOfDatarateRepeater[datarate]; + } + else + { + maxN = MaxPayloadOfDatarate[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 uint8_t CountBits( uint16_t mask, uint8_t nbBits ) +{ + uint8_t nbActiveBits = 0; + + for( uint8_t j = 0; j < nbBits; j++ ) + { + if( ( mask & ( 1 << j ) ) == ( 1 << j ) ) + { + nbActiveBits++; + } + } + return nbActiveBits; +} + +#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) +static uint8_t CountNbEnabled125kHzChannels( uint16_t *channelsMask ) +{ + uint8_t nb125kHzChannels = 0; + + for( uint8_t i = 0, k = 0; i < LORA_MAX_NB_CHANNELS - 8; i += 16, k++ ) + { + nb125kHzChannels += CountBits( channelsMask[k], 16 ); + } + + return nb125kHzChannels; +} + +#if defined( USE_BAND_915_HYBRID ) +static void ReenableChannels( uint16_t mask, uint16_t* channelMask ) +{ + uint16_t blockMask = mask; + + for( uint8_t i = 0, j = 0; i < 4; i++, j += 2 ) + { + channelMask[i] = 0; + if( ( blockMask & ( 1 << j ) ) != 0 ) + { + channelMask[i] |= 0x00FF; + } + if( ( blockMask & ( 1 << ( j + 1 ) ) ) != 0 ) + { + channelMask[i] |= 0xFF00; + } + } + channelMask[4] = blockMask; + channelMask[5] = 0x0000; +} + +static bool ValidateChannelMask( uint16_t* channelMask ) +{ + bool chanMaskState = false; + uint16_t block1 = 0; + uint16_t block2 = 0; + uint8_t index = 0; + + for( uint8_t i = 0; i < 4; i++ ) + { + block1 = channelMask[i] & 0x00FF; + block2 = channelMask[i] & 0xFF00; + + if( ( CountBits( block1, 16 ) > 5 ) && ( chanMaskState == false ) ) + { + channelMask[i] &= block1; + channelMask[4] = 1 << ( i * 2 ); + chanMaskState = true; + index = i; + } + else if( ( CountBits( block2, 16 ) > 5 ) && ( chanMaskState == false ) ) + { + channelMask[i] &= block2; + channelMask[4] = 1 << ( i * 2 + 1 ); + chanMaskState = true; + index = i; + } + } + + // Do only change the channel mask, if we have found a valid block. + if( chanMaskState == true ) + { + for( uint8_t i = 0; i < 4; i++ ) + { + if( i != index ) + { + channelMask[i] = 0; + } + } + } + return chanMaskState; +} +#endif +#endif + +static int8_t LimitTxPower( int8_t txPower ) +{ + int8_t resultTxPower = txPower; +#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) + if( ( ChannelsDatarate == DR_4 ) || + ( ( ChannelsDatarate >= DR_8 ) && ( ChannelsDatarate <= DR_13 ) ) ) + {// Limit tx power to max 26dBm + resultTxPower = MAX( txPower, TX_POWER_26_DBM ); + } + else + { + if( CountNbEnabled125kHzChannels( ChannelsMask ) < FRQ_HOP_CHNLS_MIN ) + {// Limit tx power to max 21dBm + resultTxPower = MAX( txPower, TX_POWER_20_DBM ); + } + } +#endif + return resultTxPower; +} + +static bool ValueInRange( int8_t value, int8_t min, int8_t max ) +{ + if( ( value >= min ) && ( value <= max ) ) + { + return true; + } + return false; +} + +static bool DisableChannelInMask( uint8_t id, uint16_t* mask ) +{ + uint8_t index = 0; + index = id / 16; + + if( ( index > 4 ) || ( id >= LORA_MAX_NB_CHANNELS ) ) + { + return false; + } + + // Deactivate channel + mask[index] &= ~( 1 << ( id % 16 ) ); + + return true; +} + +static bool AdrNextDr( bool adrEnabled, bool isTx, int8_t* datarateOut, int8_t* txpowerOut ) +{ + bool adrAckReq = false; + int8_t datarate = ChannelsDatarate; + int8_t txpower = ChannelsTxPower; + + if( adrEnabled == true ) + { + /* Request ADR Ack Request (including at lowest available datarate) */ + adrAckReq = AdrAckCounter >= ADR_ACK_LIMIT ? true : false; + + if( AdrAckCounter >= (ADR_ACK_LIMIT + ADR_ACK_DELAY) ) + { + // Step up power + txpower = LORAMAC_DEFAULT_TX_POWER; + + if( ( AdrAckCounter % ADR_ACK_DELAY ) == 0 ) + { +#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 ) + if( datarate > LORAMAC_TX_MIN_DATARATE ) + { + datarate--; + } + + if( datarate == LORAMAC_TX_MIN_DATARATE ) + { + if( isTx == true ) + { + // Re-enable default channels LC1, LC2, LC3 + ChannelsMask[0] = ChannelsMask[0] | ( LC( 1 ) + LC( 2 ) + LC( 3 ) ); + } + } +#elif defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) + if( datarate > LORAMAC_TX_MIN_DATARATE ) + { + if ( datarate == DR_8) + datarate = DR_4; + else + datarate--; + } + else if( isTx == true ) + { +#if defined( USE_BAND_915 ) + // Re-enable default channels + ChannelsMask[0] = 0xFFFF; + ChannelsMask[1] = 0xFFFF; + ChannelsMask[2] = 0xFFFF; + ChannelsMask[3] = 0xFFFF; + ChannelsMask[4] = 0x00FF; + ChannelsMask[5] = 0x0000; +#else // defined( USE_BAND_915_HYBRID ) + // Re-enable default channels + ChannelsMask[0] = 0x00FF; + ChannelsMask[1] = 0x0000; + ChannelsMask[2] = 0x0000; + ChannelsMask[3] = 0x0000; + ChannelsMask[4] = 0x0001; + ChannelsMask[5] = 0x0000; +#endif + } +#else +#error "Please define a frequency band in the compiler options." +#endif + } + } + } + + if(datarateOut != NULL) + *datarateOut = datarate; + + if(txpowerOut != NULL) + *txpowerOut = txpower; + + return adrAckReq; + +} + +static LoRaMacStatus_t AddMacCommand( uint8_t cmd, uint8_t p1, uint8_t p2 ) +{ + LoRaMacStatus_t status = LORAMAC_STATUS_BUSY; + // The maximum buffer length must take MAC commands to re-send into account. + uint8_t bufLen = LORA_MAC_COMMAND_MAX_LENGTH - MacCommandsBufferToRepeatIndex; + + switch( cmd ) + { + case MOTE_MAC_LINK_CHECK_REQ: + if( MacCommandsBufferIndex < bufLen ) + { + MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; + // No payload for this command + status = LORAMAC_STATUS_OK; + } + break; + case MOTE_MAC_LINK_ADR_ANS: + if( MacCommandsBufferIndex < ( bufLen - 1 ) ) + { + MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; + // Margin + MacCommandsBuffer[MacCommandsBufferIndex++] = p1; + status = LORAMAC_STATUS_OK; + } + break; + case MOTE_MAC_DUTY_CYCLE_ANS: + if( MacCommandsBufferIndex < bufLen ) + { + MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; + // No payload for this answer + status = LORAMAC_STATUS_OK; + } + break; + case MOTE_MAC_RX_PARAM_SETUP_ANS: + if( MacCommandsBufferIndex < ( bufLen - 1 ) ) + { + MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; + // Status: Datarate ACK, Channel ACK + MacCommandsBuffer[MacCommandsBufferIndex++] = p1; + status = LORAMAC_STATUS_OK; + } + break; + case MOTE_MAC_DEV_STATUS_ANS: + if( MacCommandsBufferIndex < ( bufLen - 2 ) ) + { + MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; + // 1st byte Battery + // 2nd byte Margin + MacCommandsBuffer[MacCommandsBufferIndex++] = p1; + MacCommandsBuffer[MacCommandsBufferIndex++] = p2; + status = LORAMAC_STATUS_OK; + } + break; + case MOTE_MAC_NEW_CHANNEL_ANS: + if( MacCommandsBufferIndex < ( bufLen - 1 ) ) + { + MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; + // Status: Datarate range OK, Channel frequency OK + MacCommandsBuffer[MacCommandsBufferIndex++] = p1; + status = LORAMAC_STATUS_OK; + } + break; + case MOTE_MAC_RX_TIMING_SETUP_ANS: + if( MacCommandsBufferIndex < bufLen ) + { + MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; + // No payload for this answer + status = LORAMAC_STATUS_OK; + } + break; + default: + return LORAMAC_STATUS_SERVICE_UNKNOWN; + } + if( status == LORAMAC_STATUS_OK ) + { + MacCommandsInNextTx = true; + } + return status; +} + +static uint8_t ParseMacCommandsToRepeat( uint8_t* cmdBufIn, uint8_t length, uint8_t* cmdBufOut ) +{ + uint8_t i = 0; + uint8_t cmdCount = 0; + + if( ( cmdBufIn == NULL ) || ( cmdBufOut == NULL ) ) + { + return 0; + } + + for( i = 0; i < length; i++ ) + { + switch( cmdBufIn[i] ) + { + case MOTE_MAC_RX_PARAM_SETUP_ANS: + { + cmdBufOut[cmdCount++] = cmdBufIn[i++]; + cmdBufOut[cmdCount++] = cmdBufIn[i++]; + cmdBufOut[cmdCount++] = cmdBufIn[i]; + break; + } + case MOTE_MAC_RX_TIMING_SETUP_ANS: + { + cmdBufOut[cmdCount++] = cmdBufIn[i]; + break; + } + default: + break; + } + } + + return cmdCount; +} + +static void ProcessMacCommands( uint8_t *payload, uint8_t macIndex, uint8_t commandsSize, uint8_t snr ) +{ + uint8_t validLinkAdrCmds = 0; + int8_t txPower = 0; + int8_t datarate = 0; + uint8_t nbRep = 0; + uint16_t channelsMask[6] = {0,0,0,0,0,0}; + uint8_t LinkAdrMacCmdBufferIndex = 0; + + while( macIndex < commandsSize ) + { + // Decode Frame MAC commands + switch( payload[macIndex++] ) + { + case SRV_MAC_LINK_CHECK_ANS: + MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK; + MlmeConfirm.DemodMargin = payload[macIndex++]; + MlmeConfirm.NbGateways = payload[macIndex++]; + break; + case SRV_MAC_LINK_ADR_REQ: + { + uint8_t chMaskCntl = 0; + uint8_t status = 0x07; + uint16_t chMask; + + datarate = payload[macIndex++]; + txPower = datarate & 0x0F; + datarate = ( datarate >> 4 ) & 0x0F; + + if( ( AdrCtrlOn == false ) && + ( ( ChannelsDatarate != datarate ) || ( ChannelsTxPower != txPower ) ) ) + { // ADR disabled don't handle ADR requests if server tries to change datarate or txpower + // Answer the server with fail status + // Power ACK = 0 + // Data rate ACK = 0 + // Channel mask = 0 + AddMacCommand( MOTE_MAC_LINK_ADR_ANS, 0, 0 ); + macIndex += 3; // Skip over the remaining bytes of the request + break; + } + else if ( validLinkAdrCmds++ == 0 ) + { + // Initialize local copy of the channels mask array + for(uint8_t i = 0; i < 6; i++ ) + { + channelsMask[i] = ChannelsMask[i]; + } + } + + + chMask = ( uint16_t )payload[macIndex++]; + chMask |= ( uint16_t )payload[macIndex++] << 8; + + nbRep = payload[macIndex++]; + chMaskCntl = ( nbRep >> 4 ) & 0x07; + nbRep &= 0x0F; + if( nbRep == 0 ) + { + nbRep = 1; + } +#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 ) + if( ( chMaskCntl == 0 ) && ( chMask == 0 ) ) + { + status &= 0xFE; // Channel mask KO + } + else if( ( ( chMaskCntl >= 1 ) && ( chMaskCntl <= 5 )) || + ( chMaskCntl >= 7 ) ) + { + // RFU + status &= 0xFE; // Channel mask KO + } + else + { + for( i = 0; i < LORA_MAX_NB_CHANNELS; i++ ) + { + if( chMaskCntl == 6 ) + { + if( Channels[i].Frequency != 0 ) + { + chMask |= 1 << i; + } + } + else + { + if( ( ( chMask & ( 1 << i ) ) != 0 ) && + ( Channels[i].Frequency == 0 ) ) + {// Trying to enable an undefined channel + status &= 0xFE; // Channel mask KO + } + } + } + channelsMask[0] = chMask; + } +#elif defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) + if( chMaskCntl < 4 ) + { + channelsMask[chMaskCntl] = chMask; + } + else if( chMaskCntl == 6 ) + { + // Enable all 125 kHz channels + for( uint8_t i = 0, k = 0; i < LORA_MAX_NB_CHANNELS - 8; i += 16, k++ ) + { + for( uint8_t j = 0; j < 16; j++ ) + { + if( Channels[i + j].Frequency != 0 ) + { + channelsMask[k] |= 1 << j; + } + } + } + // channel mask applied to 500 kHz channels + channelsMask[4] = chMask; + } + else if( chMaskCntl == 7 ) + { + // Disable all 125 kHz channels + channelsMask[0] = 0x0000; + channelsMask[1] = 0x0000; + channelsMask[2] = 0x0000; + channelsMask[3] = 0x0000; + + // channel mask applied to 500 kHz channels + channelsMask[4] = chMask; + } + else + { + // RFU + status &= 0xFE; // Channel mask KO + } +#else + #error "Please define a frequency band in the compiler options." +#endif + if( ValueInRange( datarate, LORAMAC_TX_MIN_DATARATE, LORAMAC_TX_MAX_DATARATE ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // + // Remark MaxTxPower = 0 and MinTxPower = 5 + // + if( ValueInRange( txPower, LORAMAC_MAX_TX_POWER, LORAMAC_MIN_TX_POWER ) == false ) + { + status &= 0xFB; // TxPower KO + } + + + // Save the mac command status location for possible channel mask KO later + LinkAdrMacCmdBufferIndex = MacCommandsBufferIndex; + + AddMacCommand( MOTE_MAC_LINK_ADR_ANS, status, 0 ); + } + break; + case SRV_MAC_DUTY_CYCLE_REQ: + MaxDCycle = payload[macIndex++]; + AggregatedDCycle = 1 << MaxDCycle; + AddMacCommand( MOTE_MAC_DUTY_CYCLE_ANS, 0, 0 ); + break; + case SRV_MAC_RX_PARAM_SETUP_REQ: + { + uint8_t status = 0x07; + int8_t datarate = 0; + int8_t drOffset = 0; + uint32_t freq = 0; + + drOffset = ( payload[macIndex] >> 4 ) & 0x07; + datarate = payload[macIndex] & 0x0F; + macIndex++; + + freq = ( uint32_t )payload[macIndex++]; + freq |= ( uint32_t )payload[macIndex++] << 8; + freq |= ( uint32_t )payload[macIndex++] << 16; + freq *= 100; + + if( Rx2FreqInRange( freq ) == false ) + { + status &= 0xFE; // Channel frequency KO + } + + if( ValueInRange( datarate, LORAMAC_RX_MIN_DATARATE, LORAMAC_RX_MAX_DATARATE ) == false ) + { + status &= 0xFD; // Datarate KO + } +#if ( defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) ) + if( ( ValueInRange( datarate, DR_5, DR_7 ) == true ) || + ( datarate > DR_13 ) ) + { + status &= 0xFD; // Datarate KO + } +#endif + if( ValueInRange( drOffset, LORAMAC_MIN_RX1_DR_OFFSET, LORAMAC_MAX_RX1_DR_OFFSET ) == false ) + { + status &= 0xFB; // Rx1DrOffset range KO + } + + if( ( status & 0x07 ) == 0x07 ) + { + Rx2Channel.Datarate = datarate; + Rx2Channel.Frequency = freq; + Rx1DrOffset = drOffset; + } + AddMacCommand( MOTE_MAC_RX_PARAM_SETUP_ANS, status, 0 ); + } + break; + case SRV_MAC_DEV_STATUS_REQ: + { + uint8_t batteryLevel = BAT_LEVEL_NO_MEASURE; + if( ( LoRaMacCallbacks != NULL ) && ( LoRaMacCallbacks->GetBatteryLevel != NULL ) ) + { + batteryLevel = LoRaMacCallbacks->GetBatteryLevel( ); + } + AddMacCommand( MOTE_MAC_DEV_STATUS_ANS, batteryLevel, snr ); + break; + } + case SRV_MAC_NEW_CHANNEL_REQ: + { + uint8_t status = 0x03; + +#if ( defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) ) + status &= 0xFC; // Channel frequency and datarate KO + macIndex += 5; +#else + int8_t channelIndex = 0; + ChannelParams_t chParam; + + channelIndex = payload[macIndex++]; + chParam.Frequency = ( uint32_t )payload[macIndex++]; + chParam.Frequency |= ( uint32_t )payload[macIndex++] << 8; + chParam.Frequency |= ( uint32_t )payload[macIndex++] << 16; + chParam.Frequency *= 100; + chParam.DrRange.Value = payload[macIndex++]; + + LoRaMacState |= MAC_TX_CONFIG; + if( chParam.Frequency == 0 ) + { + if( channelIndex < 3 ) + { + status &= 0xFC; + } + else + { + if( LoRaMacChannelRemove( channelIndex ) != LORAMAC_STATUS_OK ) + { + status &= 0xFC; + } + } + } + else + { + switch( LoRaMacChannelAdd( channelIndex, chParam ) ) + { + case LORAMAC_STATUS_OK: + { + break; + } + case LORAMAC_STATUS_FREQUENCY_INVALID: + { + status &= 0xFE; + break; + } + case LORAMAC_STATUS_DATARATE_INVALID: + { + status &= 0xFD; + break; + } + case LORAMAC_STATUS_FREQ_AND_DR_INVALID: + { + status &= 0xFC; + break; + } + default: + { + status &= 0xFC; + break; + } + } + } + LoRaMacState &= ~MAC_TX_CONFIG; +#endif + AddMacCommand( MOTE_MAC_NEW_CHANNEL_ANS, status, 0 ); + } + break; + case SRV_MAC_RX_TIMING_SETUP_REQ: + { + uint8_t delay = payload[macIndex++] & 0x0F; + + if( delay == 0 ) + { + delay++; + } + ReceiveDelay1 = delay * 1e6; + ReceiveDelay2 = ReceiveDelay1 + 1e6; + AddMacCommand( MOTE_MAC_RX_TIMING_SETUP_ANS, 0, 0 ); + } + break; + default: + // Unknown command. ABORT MAC commands processing + return; + } + } + + // Process accumulated Link ADR channel mask changes + if( validLinkAdrCmds > 0 ) + { + uint8_t drOkCounter = 0; + uint8_t status = 0x07; + +#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) + for(uint8_t i=0; i< 6; i++) + { + for( uint8_t j = 0; j < 16; j++ ) + { + if( ( channelsMask[i] & ( 1 << j ) ) != 0 ) + { + if ( ( Channels[i * 16 + j].Frequency == 0 ) ) + { + status &= 0xFE; // Channel mask KO + break; + } + else if( ( datarate >= Channels[i * 16 + j].DrRange.Fields.Min ) && + ( datarate <= Channels[i * 16 + j].DrRange.Fields.Max ) ) + { + drOkCounter++; + } + } + } + } + + if ( drOkCounter == 0 ) + { + status &= 0xFD; // Datarate KO + } + + if( ( CountNbEnabled125kHzChannels( channelsMask ) > 0 ) && ( CountNbEnabled125kHzChannels( channelsMask ) < HYBRD_CHNLS_MIN ) ) + { + status &= 0xFE; // Channel mask KO + } + +#if defined( USE_BAND_915_HYBRID ) + if( ValidateChannelMask( channelsMask ) == false ) + { + status &= 0xFE; // Channel mask KO + } +#endif + +#endif + if( ( status & 0x07 ) == 0x07 ) + { + ChannelsDatarate = datarate; + ChannelsDefaultDatarate = datarate; + ChannelsTxPower = txPower; + + ChannelsMask[0] = channelsMask[0]; + ChannelsMask[1] = channelsMask[1]; + ChannelsMask[2] = channelsMask[2]; + ChannelsMask[3] = channelsMask[3]; + ChannelsMask[4] = channelsMask[4]; + ChannelsMask[5] = channelsMask[5]; + + ChannelsNbRep = nbRep; + + // Clear remaining channels mask + memcpy1( ( uint8_t* ) ChannelsMaskRemaining, ( uint8_t* ) ChannelsMask, 10 ); + } + // Channel Mask KO + else + { + // Update the last Link ADR status with channel mask KO + if( (LinkAdrMacCmdBufferIndex+1 ) < LORA_MAC_COMMAND_MAX_LENGTH ) + MacCommandsBuffer[LinkAdrMacCmdBufferIndex+1] &= status; + } + } +} + +LoRaMacStatus_t Send( LoRaMacHeader_t *macHdr, uint8_t fPort, void *fBuffer, uint16_t fBufferSize ) +{ + LoRaMacFrameCtrl_t fCtrl; + LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID; + + fCtrl.Value = 0; + fCtrl.Bits.FOptsLen = 0; + fCtrl.Bits.FPending = 0; + fCtrl.Bits.Ack = false; + fCtrl.Bits.AdrAckReq = false; + fCtrl.Bits.Adr = AdrCtrlOn; + + // Prepare the frame + status = PrepareFrame( macHdr, &fCtrl, fPort, fBuffer, fBufferSize ); + + // Validate status + if( status != LORAMAC_STATUS_OK ) + { + return status; + } + + // Reset confirm parameters + McpsConfirm.NbRetries = 0; + McpsConfirm.AckReceived = false; + McpsConfirm.UpLinkCounter = UpLinkCounter; + + status = ScheduleTx( ); + + return status; +} + +static LoRaMacStatus_t ScheduleTx( ) +{ + TimerTime_t dutyCycleTimeOff = 0; + + // Check if the device is off + if( MaxDCycle == 255 ) + { + return LORAMAC_STATUS_DEVICE_OFF; + } + if( MaxDCycle == 0 ) + { + AggregatedTimeOff = 0; + } + + CalculateBackOff( LastTxChannel ); + + // Select channel + while( SetNextChannel( &dutyCycleTimeOff ) == false ) + { + // Set the default datarate + ChannelsDatarate = ChannelsDefaultDatarate; + +#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 ) + // Re-enable default channels LC1, LC2, LC3 + ChannelsMask[0] = ChannelsMask[0] | ( LC( 1 ) + LC( 2 ) + LC( 3 ) ); +#endif + } + + // Schedule transmission of frame + if( dutyCycleTimeOff == 0 ) + { + // Try to send now + return SendFrameOnChannel( Channels[Channel] ); + } + else + { + // Send later - prepare timer + LoRaMacState |= MAC_TX_DELAYED; + TimerSetValue( &TxDelayedTimer, dutyCycleTimeOff ); + TimerStart( &TxDelayedTimer ); + + return LORAMAC_STATUS_OK; + } +} + +static void CalculateBackOff( uint8_t channel ) +{ + uint16_t dutyCycle = Bands[Channels[channel].Band].DCycle; + + if( IsLoRaMacNetworkJoined == false ) + { +#if defined( USE_BAND_868 ) || defined( USE_BAND_433 ) || defined( USE_BAND_780 ) + dutyCycle = JOIN_DC; +#endif + } + + // Update Band Time OFF + if( DutyCycleOn == true ) + { + Bands[Channels[channel].Band].TimeOff = TxTimeOnAir * dutyCycle - TxTimeOnAir; + } + else + { + Bands[Channels[channel].Band].TimeOff = 0; + } + // Update Aggregated Time OFF + AggregatedTimeOff = AggregatedTimeOff + ( TxTimeOnAir * AggregatedDCycle - TxTimeOnAir ); +} + +LoRaMacStatus_t PrepareFrame( LoRaMacHeader_t *macHdr, LoRaMacFrameCtrl_t *fCtrl, uint8_t fPort, void *fBuffer, uint16_t fBufferSize ) +{ + uint16_t i; + uint8_t pktHeaderLen = 0; + uint32_t mic = 0; + const void* payload = fBuffer; + uint8_t payloadSize = fBufferSize; + uint8_t framePort = fPort; + + LoRaMacBufferPktLen = 0; + + NodeAckRequested = false; + + if( fBuffer == NULL ) + { + fBufferSize = 0; + } + + LoRaMacBuffer[pktHeaderLen++] = macHdr->Value; + + switch( macHdr->Bits.MType ) + { + case FRAME_TYPE_JOIN_REQ: + RxWindow1Delay = JoinAcceptDelay1 - RADIO_WAKEUP_TIME; + RxWindow2Delay = JoinAcceptDelay2 - RADIO_WAKEUP_TIME; + + LoRaMacBufferPktLen = pktHeaderLen; + + memcpyr( LoRaMacBuffer + LoRaMacBufferPktLen, LoRaMacAppEui, 8 ); + LoRaMacBufferPktLen += 8; + memcpyr( LoRaMacBuffer + LoRaMacBufferPktLen, LoRaMacDevEui, 8 ); + LoRaMacBufferPktLen += 8; + + LoRaMacDevNonce = Radio.Random( ); + + LoRaMacBuffer[LoRaMacBufferPktLen++] = LoRaMacDevNonce & 0xFF; + LoRaMacBuffer[LoRaMacBufferPktLen++] = ( LoRaMacDevNonce >> 8 ) & 0xFF; + + LoRaMacJoinComputeMic( LoRaMacBuffer, LoRaMacBufferPktLen & 0xFF, LoRaMacAppKey, &mic ); + + LoRaMacBuffer[LoRaMacBufferPktLen++] = mic & 0xFF; + LoRaMacBuffer[LoRaMacBufferPktLen++] = ( mic >> 8 ) & 0xFF; + LoRaMacBuffer[LoRaMacBufferPktLen++] = ( mic >> 16 ) & 0xFF; + LoRaMacBuffer[LoRaMacBufferPktLen++] = ( mic >> 24 ) & 0xFF; + + break; + case FRAME_TYPE_DATA_CONFIRMED_UP: + NodeAckRequested = true; + //Intentional falltrough + case FRAME_TYPE_DATA_UNCONFIRMED_UP: + if( IsLoRaMacNetworkJoined == false ) + { + return LORAMAC_STATUS_NO_NETWORK_JOINED; // No network has been joined yet + } + + fCtrl->Bits.AdrAckReq = AdrNextDr( fCtrl->Bits.Adr, true, &ChannelsDatarate, &ChannelsTxPower ); + + if( ValidatePayloadLength( fBufferSize, ChannelsDatarate, MacCommandsBufferIndex ) == false ) + { + return LORAMAC_STATUS_LENGTH_ERROR; + } + + RxWindow1Delay = ReceiveDelay1 - RADIO_WAKEUP_TIME; + RxWindow2Delay = ReceiveDelay2 - RADIO_WAKEUP_TIME; + + if( SrvAckRequested == true ) + { + SrvAckRequested = false; + fCtrl->Bits.Ack = 1; + } + + LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr ) & 0xFF; + LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr >> 8 ) & 0xFF; + LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr >> 16 ) & 0xFF; + LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr >> 24 ) & 0xFF; + + LoRaMacBuffer[pktHeaderLen++] = fCtrl->Value; + + LoRaMacBuffer[pktHeaderLen++] = UpLinkCounter & 0xFF; + LoRaMacBuffer[pktHeaderLen++] = ( UpLinkCounter >> 8 ) & 0xFF; + + // Copy the MAC commands which must be re-send into the MAC command buffer + memcpy1( &MacCommandsBuffer[MacCommandsBufferIndex], MacCommandsBufferToRepeat, MacCommandsBufferToRepeatIndex ); + MacCommandsBufferIndex += MacCommandsBufferToRepeatIndex; + + if( ( payload != NULL ) && ( payloadSize > 0 ) ) + { + if( ( MacCommandsBufferIndex <= LORA_MAC_COMMAND_MAX_LENGTH ) && ( MacCommandsInNextTx == true ) ) + { + fCtrl->Bits.FOptsLen += MacCommandsBufferIndex; + + // Update FCtrl field with new value of OptionsLength + LoRaMacBuffer[0x05] = fCtrl->Value; + for( i = 0; i < MacCommandsBufferIndex; i++ ) + { + LoRaMacBuffer[pktHeaderLen++] = MacCommandsBuffer[i]; + } + } + } + else + { + if( ( MacCommandsBufferIndex > 0 ) && ( MacCommandsInNextTx ) ) + { + payloadSize = MacCommandsBufferIndex; + payload = MacCommandsBuffer; + framePort = 0; + } + } + MacCommandsInNextTx = false; + // Store MAC commands which must be re-send in case the device does not receive a downlink anymore + MacCommandsBufferToRepeatIndex = ParseMacCommandsToRepeat( MacCommandsBuffer, MacCommandsBufferIndex, MacCommandsBufferToRepeat ); + if( MacCommandsBufferToRepeatIndex > 0 ) + { + MacCommandsInNextTx = true; + } + MacCommandsBufferIndex = 0; + + if( ( payload != NULL ) && ( payloadSize > 0 ) ) + { + LoRaMacBuffer[pktHeaderLen++] = framePort; + + if( framePort == 0 ) + { + LoRaMacPayloadEncrypt( (uint8_t* ) payload, payloadSize, LoRaMacNwkSKey, LoRaMacDevAddr, UP_LINK, UpLinkCounter, LoRaMacPayload ); + } + else + { + LoRaMacPayloadEncrypt( (uint8_t* ) payload, payloadSize, LoRaMacAppSKey, LoRaMacDevAddr, UP_LINK, UpLinkCounter, LoRaMacPayload ); + } + memcpy1( LoRaMacBuffer + pktHeaderLen, LoRaMacPayload, payloadSize ); + } + LoRaMacBufferPktLen = pktHeaderLen + payloadSize; + + LoRaMacComputeMic( LoRaMacBuffer, LoRaMacBufferPktLen, LoRaMacNwkSKey, LoRaMacDevAddr, UP_LINK, UpLinkCounter, &mic ); + + LoRaMacBuffer[LoRaMacBufferPktLen + 0] = mic & 0xFF; + LoRaMacBuffer[LoRaMacBufferPktLen + 1] = ( mic >> 8 ) & 0xFF; + LoRaMacBuffer[LoRaMacBufferPktLen + 2] = ( mic >> 16 ) & 0xFF; + LoRaMacBuffer[LoRaMacBufferPktLen + 3] = ( mic >> 24 ) & 0xFF; + + LoRaMacBufferPktLen += LORAMAC_MFR_LEN; + + break; + case FRAME_TYPE_PROPRIETARY: + if( ( fBuffer != NULL ) && ( fBufferSize > 0 ) ) + { + memcpy1( LoRaMacBuffer + pktHeaderLen, ( uint8_t* ) fBuffer, fBufferSize ); + LoRaMacBufferPktLen = pktHeaderLen + fBufferSize; + } + break; + default: + return LORAMAC_STATUS_SERVICE_UNKNOWN; + } + + return LORAMAC_STATUS_OK; +} + +LoRaMacStatus_t SendFrameOnChannel( ChannelParams_t channel ) +{ + int8_t datarate = Datarates[ChannelsDatarate]; + int8_t txPowerIndex = 0; + int8_t txPower = 0; + + txPowerIndex = LimitTxPower( ChannelsTxPower ); + txPower = TxPowers[txPowerIndex]; + + MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + McpsConfirm.Datarate = ChannelsDatarate; + McpsConfirm.TxPower = txPowerIndex; + + Radio.SetChannel( channel.Frequency ); + +#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 ) + if( ChannelsDatarate == DR_7 ) + { // High Speed FSK channel + Radio.SetMaxPayloadLength( MODEM_FSK, LoRaMacBufferPktLen ); + Radio.SetTxConfig( MODEM_FSK, txPower, 25e3, 0, datarate * 1e3, 0, 5, false, true, 0, 0, false, 3e6 ); + TxTimeOnAir = Radio.TimeOnAir( MODEM_FSK, LoRaMacBufferPktLen ); + + } + else if( ChannelsDatarate == DR_6 ) + { // High speed LoRa channel + Radio.SetMaxPayloadLength( MODEM_LORA, LoRaMacBufferPktLen ); + Radio.SetTxConfig( MODEM_LORA, txPower, 0, 1, datarate, 1, 8, false, true, 0, 0, false, 3e6 ); + TxTimeOnAir = Radio.TimeOnAir( MODEM_LORA, LoRaMacBufferPktLen ); + } + else + { // Normal LoRa channel + Radio.SetMaxPayloadLength( MODEM_LORA, LoRaMacBufferPktLen ); + Radio.SetTxConfig( MODEM_LORA, txPower, 0, 0, datarate, 1, 8, false, true, 0, 0, false, 3e6 ); + TxTimeOnAir = Radio.TimeOnAir( MODEM_LORA, LoRaMacBufferPktLen ); + } +#elif defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) + Radio.SetMaxPayloadLength( MODEM_LORA, LoRaMacBufferPktLen ); + if( ChannelsDatarate >= DR_4 ) + { // High speed LoRa channel BW500 kHz + Radio.SetTxConfig( MODEM_LORA, txPower, 0, 2, datarate, 1, 8, false, true, 0, 0, false, 3e6 ); + TxTimeOnAir = Radio.TimeOnAir( MODEM_LORA, LoRaMacBufferPktLen ); + } + else + { // Normal LoRa channel + Radio.SetTxConfig( MODEM_LORA, txPower, 0, 0, datarate, 1, 8, false, true, 0, 0, false, 3e6 ); + TxTimeOnAir = Radio.TimeOnAir( MODEM_LORA, LoRaMacBufferPktLen ); + } +#else + #error "Please define a frequency band in the compiler options." +#endif + + // Store the time on air + McpsConfirm.TxTimeOnAir = TxTimeOnAir; + MlmeConfirm.TxTimeOnAir = TxTimeOnAir; + + // Starts the MAC layer status check timer + TimerSetValue( &MacStateCheckTimer, MAC_STATE_CHECK_TIMEOUT ); + TimerStart( &MacStateCheckTimer ); + + // Send now + Radio.Send( LoRaMacBuffer, LoRaMacBufferPktLen ); + + LoRaMacState |= MAC_TX_RUNNING; + + return LORAMAC_STATUS_OK; +} + + +LoRaMacStatus_t LoRaMacInitialization( LoRaMacPrimitives_t *primitives, LoRaMacCallback_t *callbacks ) +{ + if( primitives == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + if( ( primitives->MacMcpsConfirm == NULL ) || + ( primitives->MacMcpsIndication == NULL ) || + ( primitives->MacMlmeConfirm == NULL )) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + LoRaMacPrimitives = primitives; + LoRaMacCallbacks = callbacks; + + LoRaMacFlags.Value = 0; + + LoRaMacDeviceClass = CLASS_A; + + UpLinkCounter = 1; + DownLinkCounter = 0; + AdrAckCounter = 0; + + ChannelsNbRepCounter = 0; + + AckTimeoutRetries = 1; + AckTimeoutRetriesCounter = 1; + AckTimeoutRetry = false; + + MaxDCycle = 0; + AggregatedDCycle = 1; + + MacCommandsBufferIndex = 0; + MacCommandsBufferToRepeatIndex = 0; + + IsRxWindowsEnabled = true; + + RepeaterSupport = false; + IsRxWindowsEnabled = true; + IsLoRaMacNetworkJoined = false; + LoRaMacState = MAC_IDLE; + + NodeAckRequested = false; + SrvAckRequested = false; + MacCommandsInNextTx = false; + +#if defined( USE_BAND_433 ) + ChannelsMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 ); +#elif defined( USE_BAND_780 ) + ChannelsMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 ); +#elif defined( USE_BAND_868 ) + ChannelsMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 ); +#elif defined( USE_BAND_915 ) + ChannelsMask[0] = 0xFFFF; + ChannelsMask[1] = 0xFFFF; + ChannelsMask[2] = 0xFFFF; + ChannelsMask[3] = 0xFFFF; + ChannelsMask[4] = 0x00FF; + ChannelsMask[5] = 0x0000; + + memcpy1( ( uint8_t* ) ChannelsMaskRemaining, ( uint8_t* ) ChannelsMask, sizeof( ChannelsMask ) ); +#elif defined( USE_BAND_915_HYBRID ) + ChannelsMask[0] = 0x00FF; + ChannelsMask[1] = 0x0000; + ChannelsMask[2] = 0x0000; + ChannelsMask[3] = 0x0000; + ChannelsMask[4] = 0x0001; + ChannelsMask[5] = 0x0000; + + memcpy1( ( uint8_t* ) ChannelsMaskRemaining, ( uint8_t* ) ChannelsMask, sizeof( ChannelsMask ) ); +#else + #error "Please define a frequency band in the compiler options." +#endif + +#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) + // 125 kHz channels + for( uint8_t i = 0; i < LORA_MAX_NB_CHANNELS - 8; i++ ) + { + Channels[i].Frequency = 902.3e6 + i * 200e3; + Channels[i].DrRange.Value = ( DR_3 << 4 ) | DR_0; + Channels[i].Band = 0; + } + // 500 kHz channels + for( uint8_t i = LORA_MAX_NB_CHANNELS - 8; i < LORA_MAX_NB_CHANNELS; i++ ) + { + Channels[i].Frequency = 903.0e6 + ( i - ( LORA_MAX_NB_CHANNELS - 8 ) ) * 1.6e6; + Channels[i].DrRange.Value = ( DR_4 << 4 ) | DR_4; + Channels[i].Band = 0; + } +#endif + + ChannelsTxPower = LORAMAC_DEFAULT_TX_POWER; + ChannelsDefaultDatarate = ChannelsDatarate = LORAMAC_DEFAULT_DATARATE; + ChannelsNbRep = 1; + ChannelsNbRepCounter = 0; + + Rx2Channel.Frequency = 923300000; + Rx2Channel.Datarate = DR_8; + Rx1DrOffset = 0; + + + MaxDCycle = 0; + AggregatedDCycle = 1; + AggregatedLastTxDoneTime = 0; + AggregatedTimeOff = 0; + +#if defined( USE_BAND_433 ) + DutyCycleOn = false; +#elif defined( USE_BAND_780 ) + DutyCycleOn = false; +#elif defined( USE_BAND_868 ) + DutyCycleOn = true; +#elif defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) + DutyCycleOn = false; +#else + #error "Please define a frequency band in the compiler options." +#endif + + MaxRxWindow = MAX_RX_WINDOW; + ReceiveDelay1 = RECEIVE_DELAY1; + ReceiveDelay2 = RECEIVE_DELAY2; + JoinAcceptDelay1 = JOIN_ACCEPT_DELAY1; + JoinAcceptDelay2 = JOIN_ACCEPT_DELAY2; + +#if defined( USE_BAND_915 ) + NextJoinBlock = 0; + LastJoinBlock = -1; + JoinBlocksRemaining = 0xff; + Join500KHzRemaining = 0xff; +#endif + + LastJoinTxTime = 0; + JoinAggTimeOnAir = 0; + + TimerInit( &MacStateCheckTimer, OnMacStateCheckTimerEvent ); + TimerSetValue( &MacStateCheckTimer, MAC_STATE_CHECK_TIMEOUT ); + + TimerInit( &TxDelayedTimer, OnTxDelayedTimerEvent ); + TimerInit( &RxWindowTimer1, OnRxWindow1TimerEvent ); + TimerInit( &RxWindowTimer2, OnRxWindow2TimerEvent ); + TimerInit( &AckTimeoutTimer, OnAckTimeoutTimerEvent ); + + // Initialize Radio driver + RadioEvents.TxDone = OnRadioTxDone; + RadioEvents.RxDone = OnRadioRxDone; + RadioEvents.RxError = OnRadioRxError; + RadioEvents.TxTimeout = OnRadioTxTimeout; + RadioEvents.RxTimeout = OnRadioRxTimeout; + Radio.Init( &RadioEvents ); + + // Random seed initialization + srand1( Radio.Random( ) ); + + // Initialize channel index. + Channel = LORA_MAX_NB_CHANNELS; + + PublicNetwork = true; + SetPublicNetwork( PublicNetwork ); + Radio.Sleep( ); + + return LORAMAC_STATUS_OK; +} + +LoRaMacStatus_t LoRaMacQueryTxPossible( uint8_t size, LoRaMacTxInfo_t* txInfo ) +{ + int8_t datarate = ChannelsDefaultDatarate; + uint8_t fOptLen = MacCommandsBufferIndex + MacCommandsBufferToRepeatIndex; + + if( txInfo == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + AdrNextDr( AdrCtrlOn, false, &datarate, NULL ); + + if( RepeaterSupport == true ) + { + txInfo->CurrentPayloadSize = MaxPayloadOfDatarateRepeater[datarate]; + } + else + { + txInfo->CurrentPayloadSize = MaxPayloadOfDatarate[datarate]; + } + + if( txInfo->CurrentPayloadSize >= fOptLen ) + { + txInfo->MaxPossiblePayload = txInfo->CurrentPayloadSize - fOptLen; + } + else + { + return LORAMAC_STATUS_MAC_CMD_LENGTH_ERROR; + } + + if( ValidatePayloadLength( size, datarate, 0 ) == false ) + { + return LORAMAC_STATUS_LENGTH_ERROR; + } + + if( ValidatePayloadLength( size, datarate, fOptLen ) == false ) + { + return LORAMAC_STATUS_MAC_CMD_LENGTH_ERROR; + } + + return LORAMAC_STATUS_OK; +} + +LoRaMacStatus_t LoRaMacMibGetRequestConfirm( MibRequestConfirm_t *mibGet ) +{ + LoRaMacStatus_t status = LORAMAC_STATUS_OK; + + if( mibGet == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + switch( mibGet->Type ) + { + case MIB_DEVICE_CLASS: + { + mibGet->Param.Class = LoRaMacDeviceClass; + break; + } + case MIB_NETWORK_JOINED: + { + mibGet->Param.IsNetworkJoined = IsLoRaMacNetworkJoined; + break; + } + case MIB_ADR: + { + mibGet->Param.AdrEnable = AdrCtrlOn; + break; + } + case MIB_NET_ID: + { + mibGet->Param.NetID = LoRaMacNetID; + break; + } + case MIB_DEV_ADDR: + { + mibGet->Param.DevAddr = LoRaMacDevAddr; + break; + } + case MIB_NWK_SKEY: + { + mibGet->Param.NwkSKey = LoRaMacNwkSKey; + break; + } + case MIB_APP_SKEY: + { + mibGet->Param.AppSKey = LoRaMacAppSKey; + break; + } + case MIB_PUBLIC_NETWORK: + { + mibGet->Param.EnablePublicNetwork = PublicNetwork; + break; + } + case MIB_REPEATER_SUPPORT: + { + mibGet->Param.EnableRepeaterSupport = RepeaterSupport; + break; + } + case MIB_CHANNELS: + { + mibGet->Param.ChannelList = Channels; + break; + } + case MIB_RX2_CHANNEL: + { + mibGet->Param.Rx2Channel = Rx2Channel; + break; + } + case MIB_CHANNELS_MASK: + { + mibGet->Param.ChannelsMask = ChannelsMask; + break; + } + case MIB_CHANNELS_NB_REP: + { + mibGet->Param.ChannelNbRep = ChannelsNbRep; + break; + } + case MIB_MAX_RX_WINDOW_DURATION: + { + mibGet->Param.MaxRxWindow = MaxRxWindow; + break; + } + case MIB_RECEIVE_DELAY_1: + { + mibGet->Param.ReceiveDelay1 = ReceiveDelay1; + break; + } + case MIB_RECEIVE_DELAY_2: + { + mibGet->Param.ReceiveDelay2 = ReceiveDelay2; + break; + } + case MIB_JOIN_ACCEPT_DELAY_1: + { + mibGet->Param.JoinAcceptDelay1 = JoinAcceptDelay1; + break; + } + case MIB_JOIN_ACCEPT_DELAY_2: + { + mibGet->Param.JoinAcceptDelay2 = JoinAcceptDelay2; + break; + } + case MIB_CHANNELS_DEFAULT_DATARATE: + { + mibGet->Param.ChannelsDefaultDatarate = ChannelsDefaultDatarate; + break; + } + case MIB_CHANNELS_DATARATE: + { + mibGet->Param.ChannelsDatarate = ChannelsDatarate; + break; + } + case MIB_CHANNELS_TX_POWER: + { + mibGet->Param.ChannelsTxPower = ChannelsTxPower; + break; + } + case MIB_UPLINK_COUNTER: + { + mibGet->Param.UpLinkCounter = UpLinkCounter; + break; + } + case MIB_DOWNLINK_COUNTER: + { + mibGet->Param.DownLinkCounter = DownLinkCounter; + break; + } + case MIB_MULTICAST_CHANNEL: + { + mibGet->Param.MulticastList = MulticastChannels; + break; + } + default: + status = LORAMAC_STATUS_SERVICE_UNKNOWN; + break; + } + + return status; +} + +LoRaMacStatus_t LoRaMacMibSetRequestConfirm( MibRequestConfirm_t *mibSet ) +{ + LoRaMacStatus_t status = LORAMAC_STATUS_OK; + + if( mibSet == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + if( ( LoRaMacState & MAC_TX_RUNNING ) == MAC_TX_RUNNING ) + { + return LORAMAC_STATUS_BUSY; + } + + switch( mibSet->Type ) + { + case MIB_DEVICE_CLASS: + { + LoRaMacDeviceClass = mibSet->Param.Class; + switch( LoRaMacDeviceClass ) + { + case CLASS_A: + { + // Set the radio into sleep to setup a defined state + Radio.Sleep( ); + break; + } + case CLASS_B: + { + break; + } + case CLASS_C: + { + // Set the NodeAckRequested indicator to default + NodeAckRequested = false; + OnRxWindow2TimerEvent( ); + break; + } + } + break; + } + case MIB_NETWORK_JOINED: + { + IsLoRaMacNetworkJoined = mibSet->Param.IsNetworkJoined; +#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) + if( IsLoRaMacNetworkJoined == false ) + { + NextJoinBlock = 0; + JoinBlocksRemaining = 0xff; + Join500KHzRemaining = 0xff; + } +#endif + break; + } + case MIB_ADR: + { + AdrCtrlOn = mibSet->Param.AdrEnable; + break; + } + case MIB_NET_ID: + { + LoRaMacNetID = mibSet->Param.NetID; + break; + } + case MIB_DEV_ADDR: + { + LoRaMacDevAddr = mibSet->Param.DevAddr; + break; + } + case MIB_NWK_SKEY: + { + if( mibSet->Param.NwkSKey != NULL ) + { + memcpy1( LoRaMacNwkSKey, mibSet->Param.NwkSKey, + sizeof( LoRaMacNwkSKey ) ); + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_APP_SKEY: + { + if( mibSet->Param.AppSKey != NULL ) + { + memcpy1( LoRaMacAppSKey, mibSet->Param.AppSKey, + sizeof( LoRaMacAppSKey ) ); + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_PUBLIC_NETWORK: + { + SetPublicNetwork( mibSet->Param.EnablePublicNetwork ); + break; + } + case MIB_REPEATER_SUPPORT: + { + RepeaterSupport = mibSet->Param.EnableRepeaterSupport; + break; + } + case MIB_RX2_CHANNEL: + { + Rx2Channel = mibSet->Param.Rx2Channel; + break; + } + case MIB_CHANNELS_MASK: + { + if( mibSet->Param.ChannelsMask ) + { +#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) + bool chanMaskState = true; + +#if defined( USE_BAND_915_HYBRID ) + chanMaskState = ValidateChannelMask( mibSet->Param.ChannelsMask ); +#endif + if( chanMaskState == true ) + { + if( ( CountNbEnabled125kHzChannels( mibSet->Param.ChannelsMask ) < HYBRD_CHNLS_MIN ) && + ( CountNbEnabled125kHzChannels( mibSet->Param.ChannelsMask ) > 0 ) ) + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + else + { + memcpy1( ( uint8_t* ) ChannelsMask, + ( uint8_t* ) mibSet->Param.ChannelsMask, sizeof( ChannelsMask ) ); + for ( uint8_t i = 0; i < sizeof( ChannelsMask ) / 2; i++ ) + { + // Disable channels which are no longer available + ChannelsMaskRemaining[i] &= ChannelsMask[i]; + } + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } +#else + memcpy1( ( uint8_t* ) ChannelsMask, + ( uint8_t* ) mibSet->Param.ChannelsMask, 2 ); +#endif + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_CHANNELS_NB_REP: + { + if( ( mibSet->Param.ChannelNbRep >= 1 ) && + ( mibSet->Param.ChannelNbRep <= 15 ) ) + { + ChannelsNbRep = mibSet->Param.ChannelNbRep; + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_MAX_RX_WINDOW_DURATION: + { + MaxRxWindow = mibSet->Param.MaxRxWindow; + break; + } + case MIB_RECEIVE_DELAY_1: + { + ReceiveDelay1 = mibSet->Param.ReceiveDelay1; + break; + } + case MIB_RECEIVE_DELAY_2: + { + ReceiveDelay2 = mibSet->Param.ReceiveDelay2; + break; + } + case MIB_JOIN_ACCEPT_DELAY_1: + { + JoinAcceptDelay1 = mibSet->Param.JoinAcceptDelay1; + break; + } + case MIB_JOIN_ACCEPT_DELAY_2: + { + JoinAcceptDelay2 = mibSet->Param.JoinAcceptDelay2; + break; + } + case MIB_CHANNELS_DEFAULT_DATARATE: + { + if( ValueInRange( mibSet->Param.ChannelsDefaultDatarate, + LORAMAC_TX_MIN_DATARATE, LORAMAC_TX_MAX_DATARATE ) ) + { + ChannelsDefaultDatarate = mibSet->Param.ChannelsDefaultDatarate; + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_CHANNELS_DATARATE: + { + if( ValueInRange( mibSet->Param.ChannelsDatarate, + LORAMAC_TX_MIN_DATARATE, LORAMAC_TX_MAX_DATARATE ) ) + { + ChannelsDatarate = mibSet->Param.ChannelsDatarate; + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_CHANNELS_TX_POWER: + { + if( ValueInRange( mibSet->Param.ChannelsTxPower, + LORAMAC_MAX_TX_POWER, LORAMAC_MIN_TX_POWER ) ) + { +#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) + int8_t txPower = LimitTxPower( mibSet->Param.ChannelsTxPower ); + if( txPower == mibSet->Param.ChannelsTxPower ) + { + ChannelsTxPower = mibSet->Param.ChannelsTxPower; + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } +#else + ChannelsTxPower = mibSet->Param.ChannelsTxPower; +#endif + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_UPLINK_COUNTER: + { + UpLinkCounter = mibSet->Param.UpLinkCounter; + break; + } + case MIB_DOWNLINK_COUNTER: + { + DownLinkCounter = mibSet->Param.DownLinkCounter; + break; + } + default: + status = LORAMAC_STATUS_SERVICE_UNKNOWN; + break; + } + + return status; +} + +LoRaMacStatus_t LoRaMacChannelAdd( uint8_t id, ChannelParams_t params ) +{ +#if ( defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) ) + return LORAMAC_STATUS_PARAMETER_INVALID; +#else + bool datarateInvalid = false; + bool frequencyInvalid = false; + uint8_t band = 0; + + // The id must not exceed LORA_MAX_NB_CHANNELS + if( id >= LORA_MAX_NB_CHANNELS ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + // Validate if the MAC is in a correct state + if( ( LoRaMacState & MAC_TX_RUNNING ) == MAC_TX_RUNNING ) + { + if( ( LoRaMacState & MAC_TX_CONFIG ) != MAC_TX_CONFIG ) + { + return LORAMAC_STATUS_BUSY; + } + } + // Validate the datarate + if( ( params.DrRange.Fields.Min > params.DrRange.Fields.Max ) || + ( ValueInRange( params.DrRange.Fields.Min, LORAMAC_TX_MIN_DATARATE, + LORAMAC_TX_MAX_DATARATE ) == false ) || + ( ValueInRange( params.DrRange.Fields.Max, LORAMAC_TX_MIN_DATARATE, + LORAMAC_TX_MAX_DATARATE ) == false ) ) + { + datarateInvalid = true; + } + +#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 ) + if( id < 3 ) + { + if( params.Frequency != Channels[id].Frequency ) + { + frequencyInvalid = true; + } + + if( params.DrRange.Fields.Min > ChannelsDefaultDatarate ) + { + datarateInvalid = true; + } + if( ValueInRange( params.DrRange.Fields.Max, DR_5, LORAMAC_TX_MAX_DATARATE ) == false ) + { + datarateInvalid = true; + } + } +#endif + + // Validate the frequency + if( ( Radio.CheckRfFrequency( params.Frequency ) == true ) && ( params.Frequency > 0 ) && ( frequencyInvalid == false ) ) + { +#if defined( USE_BAND_868 ) + if( ( params.Frequency >= 865000000 ) && ( params.Frequency <= 868000000 ) ) + { + band = BAND_G1_0; + } + else if( ( params.Frequency > 868000000 ) && ( params.Frequency <= 868600000 ) ) + { + band = BAND_G1_1; + } + else if( ( params.Frequency >= 868700000 ) && ( params.Frequency <= 869200000 ) ) + { + band = BAND_G1_2; + } + else if( ( params.Frequency >= 869400000 ) && ( params.Frequency <= 869650000 ) ) + { + band = BAND_G1_3; + } + else if( ( params.Frequency >= 869700000 ) && ( params.Frequency <= 870000000 ) ) + { + band = BAND_G1_4; + } + else + { + frequencyInvalid = true; + } +#endif + } + else + { + frequencyInvalid = true; + } + + if( ( datarateInvalid == true ) && ( frequencyInvalid == true ) ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + if( datarateInvalid == true ) + { + return LORAMAC_STATUS_DATARATE_INVALID; + } + if( frequencyInvalid == true ) + { + return LORAMAC_STATUS_FREQUENCY_INVALID; + } + + // Every parameter is valid, activate the channel + Channels[id] = params; + Channels[id].Band = band; + ChannelsMask[0] |= ( 1 << id ); + + return LORAMAC_STATUS_OK; +#endif +} + +LoRaMacStatus_t LoRaMacChannelRemove( uint8_t id ) +{ +#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 ) + if( ( LoRaMacState & MAC_TX_RUNNING ) == MAC_TX_RUNNING ) + { + if( ( LoRaMacState & MAC_TX_CONFIG ) != MAC_TX_CONFIG ) + { + return LORAMAC_STATUS_BUSY; + } + } + + if( ( id < 3 ) || ( id >= LORA_MAX_NB_CHANNELS ) ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + else + { + // Remove the channel from the list of channels + Channels[id] = ( ChannelParams_t ){ 0, { 0 }, 0 }; + + // Disable the channel as it doesn't exist anymore + if( DisableChannelInMask( id, ChannelsMask ) == false ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + } + return LORAMAC_STATUS_OK; +#elif ( defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) ) + return LORAMAC_STATUS_PARAMETER_INVALID; +#endif +} + +LoRaMacStatus_t LoRaMacMulticastChannelLink( MulticastParams_t *channelParam ) +{ + if( channelParam == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + if( ( LoRaMacState & MAC_TX_RUNNING ) == MAC_TX_RUNNING ) + { + return LORAMAC_STATUS_BUSY; + } + + // Reset downlink counter + channelParam->DownLinkCounter = 0; + + if( MulticastChannels == NULL ) + { + // New node is the fist element + MulticastChannels = channelParam; + } + else + { + MulticastParams_t *cur = MulticastChannels; + + // Search the last node in the list + while( cur->Next != NULL ) + { + cur = cur->Next; + } + // This function always finds the last node + cur->Next = channelParam; + } + + return LORAMAC_STATUS_OK; +} + +LoRaMacStatus_t LoRaMacMulticastChannelUnlink( MulticastParams_t *channelParam ) +{ + if( channelParam == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + if( ( LoRaMacState & MAC_TX_RUNNING ) == MAC_TX_RUNNING ) + { + return LORAMAC_STATUS_BUSY; + } + + if( MulticastChannels != NULL ) + { + if( MulticastChannels == channelParam ) + { + // First element + MulticastChannels = channelParam->Next; + } + else + { + MulticastParams_t *cur = MulticastChannels; + + // Search the node in the list + while( cur->Next && cur->Next != channelParam ) + { + cur = cur->Next; + } + // If we found the node, remove it + if( cur->Next ) + { + cur->Next = channelParam->Next; + } + } + channelParam->Next = NULL; + } + + return LORAMAC_STATUS_OK; +} + +LoRaMacStatus_t LoRaMacMlmeRequest( MlmeReq_t *mlmeRequest ) +{ + LoRaMacStatus_t status = LORAMAC_STATUS_SERVICE_UNKNOWN; + LoRaMacHeader_t macHdr; + + if( mlmeRequest == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + if( ( LoRaMacState & MAC_TX_RUNNING ) == MAC_TX_RUNNING ) + { + return LORAMAC_STATUS_BUSY; + } + + memset1( ( uint8_t* ) &MlmeConfirm, 0, sizeof( MlmeConfirm ) ); + + MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + + switch( mlmeRequest->Type ) + { + case MLME_JOIN: + { + if( ( LoRaMacState & MAC_TX_DELAYED ) == MAC_TX_DELAYED ) + { + return LORAMAC_STATUS_BUSY; + } + + MlmeConfirm.MlmeRequest = mlmeRequest->Type; + + if( ( mlmeRequest->Req.Join.DevEui == NULL ) || + ( mlmeRequest->Req.Join.AppEui == NULL ) || + ( mlmeRequest->Req.Join.AppKey == NULL ) ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + LoRaMacFlags.Bits.MlmeReq = 1; + + LoRaMacDevEui = mlmeRequest->Req.Join.DevEui; + LoRaMacAppEui = mlmeRequest->Req.Join.AppEui; + LoRaMacAppKey = mlmeRequest->Req.Join.AppKey; + +// Disable join retransmission dutycycle as it does not currently +// handle device reset +#if 0 + if( LoRaMacCalcJoinBackOff( ) != 0 ) + { + return LORAMAC_STATUS_TX_DCYCLE_EXCEEDED; + } +#endif + + macHdr.Value = 0; + macHdr.Bits.MType = FRAME_TYPE_JOIN_REQ; + +#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) + if ( IsLoRaMacNetworkJoined == true ) + { + NextJoinBlock = 0; + JoinBlocksRemaining = 0xff; + Join500KHzRemaining = 0xff; + IsLoRaMacNetworkJoined = false; + } +#endif + +#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) +#if defined( USE_BAND_915 ) + // Re-enable 500 kHz default channels + ChannelsMask[4] = 0x00FF; +#else // defined( USE_BAND_915_HYBRID ) + // Re-enable 500 kHz default channels + ChannelsMask[4] = 0x0001; +#endif + + static uint8_t drSwitch = 0; + + if( ( ++drSwitch & 0x01 ) == 0x01 ) + { + ChannelsDatarate = DR_0; + } + else + { + ChannelsDatarate = DR_4; + } +#endif + + status = Send( &macHdr, 0, NULL, 0 ); + break; + } + case MLME_LINK_CHECK: + { + LoRaMacFlags.Bits.MlmeReq = 1; + // LoRaMac will send this command piggy-pack + MlmeConfirm.MlmeRequest = mlmeRequest->Type; + + status = AddMacCommand( MOTE_MAC_LINK_CHECK_REQ, 0, 0 ); + break; + } + default: + break; + } + + if( status != LORAMAC_STATUS_OK ) + { + NodeAckRequested = false; + LoRaMacFlags.Bits.MlmeReq = 0; + } + + return status; +} + +LoRaMacStatus_t LoRaMacMcpsRequest( McpsReq_t *mcpsRequest ) +{ + LoRaMacStatus_t status = LORAMAC_STATUS_SERVICE_UNKNOWN; + LoRaMacHeader_t macHdr; + uint8_t fPort = 0; + void *fBuffer; + uint16_t fBufferSize; + int8_t datarate; + bool readyToSend = false; + + if( mcpsRequest == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + if( ( ( LoRaMacState & MAC_TX_RUNNING ) == MAC_TX_RUNNING ) || + ( ( LoRaMacState & MAC_TX_DELAYED ) == MAC_TX_DELAYED ) ) + { + return LORAMAC_STATUS_BUSY; + } + + macHdr.Value = 0; + memset1 ( ( uint8_t* ) &McpsConfirm, 0, sizeof( McpsConfirm ) ); + McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + + switch( mcpsRequest->Type ) + { + case MCPS_UNCONFIRMED: + { + readyToSend = true; + 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; + AckTimeoutRetriesCounter = 1; + AckTimeoutRetries = mcpsRequest->Req.Confirmed.NbTrials; + + 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; + 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; + } + + if( readyToSend == true ) + { + if( AdrCtrlOn == false ) + { + if( ValueInRange( datarate, LORAMAC_TX_MIN_DATARATE, LORAMAC_TX_MAX_DATARATE ) == true ) + { + ChannelsDatarate = datarate; + } + else + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + } + + status = Send( &macHdr, fPort, fBuffer, fBufferSize ); + if( status == LORAMAC_STATUS_OK ) + { + McpsConfirm.McpsRequest = mcpsRequest->Type; + LoRaMacFlags.Bits.McpsReq = 1; + } + else + { + NodeAckRequested = false; + } + } + + return status; +} + +TimerTime_t LoRaMacCalcJoinBackOff( ) +{ + TimerTime_t timeOff = 0; + TimerTime_t uptime; + TimerTime_t currDCycleEndTime; + TimerTime_t prevDCycleEndTime; + TimerTime_t period; + TimerTime_t onAirTimeMax; + TimerTime_t lastJoinTime; + TimerTime_t elapsedTime; + uint8_t dcyclePeriod; + + // Check retransmit duty-cycle is enabled + if ( JOIN_NB_RETRANSMISSION_DCYCLES == 0 ) + return 0; + + // Normalize system time values to seconds + uptime = TimerGetCurrentTime( ) / 1e6; + lastJoinTime = LastJoinTxTime / 1e6; + + // Get dutycycle for current time + prevDCycleEndTime = 0; + currDCycleEndTime = 0; + for(dcyclePeriod = 0; dcyclePeriod < JOIN_NB_RETRANSMISSION_DCYCLES; dcyclePeriod++) + { + prevDCycleEndTime = currDCycleEndTime; + currDCycleEndTime += JoinReTransmitDCycle[dcyclePeriod].period; + + bool isCurrentPeriod = uptime < currDCycleEndTime; + + if( isCurrentPeriod || ( dcyclePeriod == ( JOIN_NB_RETRANSMISSION_DCYCLES-1 ) ) ) + { + period = JoinReTransmitDCycle[dcyclePeriod].period; + onAirTimeMax = JoinReTransmitDCycle[dcyclePeriod].onAirTimeMax; + if( isCurrentPeriod == true ) + break; + } + } + + // Clear aggregate on air time if dutycycle period of last join has elapsed + if( lastJoinTime < prevDCycleEndTime ) + { + JoinAggTimeOnAir = 0; + } + else + { + elapsedTime = ( uptime - prevDCycleEndTime ) % period; + + if( dcyclePeriod == JOIN_NB_RETRANSMISSION_DCYCLES ) + { + if( ( ( uptime - prevDCycleEndTime ) / period ) > ( ( lastJoinTime - prevDCycleEndTime ) / period ) ) + { + JoinAggTimeOnAir = 0; + } + } + } + + if ( JoinAggTimeOnAir >= onAirTimeMax ) + { + // time off is remaining time until beginning of next period + timeOff = ( period - elapsedTime ); + } + + + if(timeOff > JOIN_RETRANSMISSION_DC_WAIT_MAX) + timeOff = JOIN_RETRANSMISSION_DC_WAIT_MAX; + + return ( timeOff * 1e6 ); +} + +void LoRaMacTestRxWindowsOn( bool enable ) +{ + IsRxWindowsEnabled = enable; +} + +void LoRaMacTestSetMic( uint16_t txPacketCounter ) +{ + UpLinkCounter = txPacketCounter; + IsUpLinkCounterFixed = true; +} + +void LoRaMacTestSetDutyCycleOn( bool enable ) +{ + DutyCycleOn = enable; +}