for publishing
Fork of LoRaWAN-lib_publishing_testing_UART_bug by
Revision 2:14a5d6ad92d5, committed 2016-01-05
- Comitter:
- mluis
- Date:
- Tue Jan 05 16:41:54 2016 +0000
- Parent:
- 1:91e4e6c60d1e
- Child:
- 3:b9d87593a8ae
- Commit message:
- Synchronized with https://github.com/Lora-net/LoRaMac-node git revision a2226468d470eceb251338e1acfb24cfd121effa
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LoRaMac-api-v3.cpp Tue Jan 05 16:41:54 2016 +0000 @@ -0,0 +1,626 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (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 "LoRaMac-api-v3.h" +#include "LoRaMacTest.h" + +/*! + * Extern function declarations. + */ +extern LoRaMacStatus_t Send( LoRaMacHeader_t *macHdr, uint8_t fPort, + void *fBuffer, uint16_t fBufferSize ); +extern LoRaMacStatus_t PrepareFrame( LoRaMacHeader_t *macHdr, LoRaMacFrameCtrl_t *fCtrl, + uint8_t fPort, void *fBuffer, uint16_t fBufferSize ); +extern LoRaMacStatus_t SendFrameOnChannel( ChannelParams_t channel ); +extern uint32_t LoRaMacState; +extern LoRaMacFlags_t LoRaMacFlags; + +/*! + * Static variables + */ +static LoRaMacEventFlags_t LoRaMacEventFlags; +static LoRaMacEventInfo_t LoRaMacEventInfo; +static LoRaMacPrimitives_t LoRaMacPrimitives; +static LoRaMacCallback_t LoRaMacCallback; +static LoRaMacCallbacks_t LoRaMacCallbacks; + +/*! + * \brief MCPS-Confirm event function + * + * \param [IN] mcpsConfirm - Pointer to the confirm structure, + * containing confirm attributes. + */ +static void McpsConfirm( McpsConfirm_t *mcpsConfirm ) +{ + LoRaMacEventInfo.Status = mcpsConfirm->Status; + LoRaMacEventFlags.Bits.Tx = 1; + + LoRaMacEventInfo.TxDatarate = mcpsConfirm->Datarate; + LoRaMacEventInfo.TxNbRetries = mcpsConfirm->NbRetries; + LoRaMacEventInfo.TxAckReceived = mcpsConfirm->AckReceived; + + if( ( LoRaMacFlags.Bits.McpsInd != 1 ) && ( LoRaMacFlags.Bits.MlmeReq != 1 ) ) + { + LoRaMacCallbacks.MacEvent( &LoRaMacEventFlags, &LoRaMacEventInfo ); + LoRaMacEventFlags.Value = 0; + } +} + +/*! + * \brief MCPS-Indication event function + * + * \param [IN] mcpsIndication - Pointer to the indication structure, + * containing indication attributes. + */ +static void McpsIndication( McpsIndication_t *mcpsIndication ) +{ + LoRaMacEventInfo.Status = mcpsIndication->Status; + LoRaMacEventFlags.Bits.Rx = 1; + LoRaMacEventFlags.Bits.RxSlot = mcpsIndication->RxSlot; + LoRaMacEventFlags.Bits.Multicast = mcpsIndication->Multicast; + if( mcpsIndication->RxData == true ) + { + LoRaMacEventFlags.Bits.RxData = 1; + } + + LoRaMacEventInfo.RxPort = mcpsIndication->Port; + LoRaMacEventInfo.RxBuffer = mcpsIndication->Buffer; + LoRaMacEventInfo.RxBufferSize = mcpsIndication->BufferSize; + LoRaMacEventInfo.RxRssi = mcpsIndication->Rssi; + LoRaMacEventInfo.RxSnr = mcpsIndication->Snr; + + LoRaMacCallbacks.MacEvent( &LoRaMacEventFlags, &LoRaMacEventInfo ); + LoRaMacEventFlags.Value = 0; +} + +/*! + * \brief MLME-Confirm event function + * + * \param [IN] mlmeConfirm - Pointer to the confirm structure, + * containing confirm attributes. + */ +static void MlmeConfirm( MlmeConfirm_t *mlmeConfirm ) +{ + if( mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK ) + { + switch( mlmeConfirm->MlmeRequest ) + { + case MLME_JOIN: + { + // Status is OK, node has joined the network + LoRaMacEventInfo.Status = mlmeConfirm->Status; + LoRaMacEventFlags.Bits.Tx = 1; + LoRaMacEventFlags.Bits.Rx = 1; + LoRaMacEventFlags.Bits.JoinAccept = 1; + break; + } + case MLME_LINK_CHECK: + { + LoRaMacEventFlags.Bits.Tx = 1; + LoRaMacEventFlags.Bits.Rx = 1; + LoRaMacEventFlags.Bits.LinkCheck = 1; + + LoRaMacEventInfo.DemodMargin = mlmeConfirm->DemodMargin; + LoRaMacEventInfo.NbGateways = mlmeConfirm->NbGateways; + break; + } + default: + break; + } + } + + if( LoRaMacFlags.Bits.McpsInd != 1 ) + { + LoRaMacCallbacks.MacEvent( &LoRaMacEventFlags, &LoRaMacEventInfo ); + LoRaMacEventFlags.Value = 0; + } +} + +void LoRaMacInit( LoRaMacCallbacks_t *callbacks ) +{ + LoRaMacPrimitives.MacMcpsConfirm = McpsConfirm; + LoRaMacPrimitives.MacMcpsIndication = McpsIndication; + LoRaMacPrimitives.MacMlmeConfirm = MlmeConfirm; + + LoRaMacCallbacks.MacEvent = callbacks->MacEvent; + LoRaMacCallbacks.GetBatteryLevel = callbacks->GetBatteryLevel; + LoRaMacCallback.GetBatteryLevel = callbacks->GetBatteryLevel; + + LoRaMacInitialization( &LoRaMacPrimitives, &LoRaMacCallback ); +} + +void LoRaMacSetAdrOn( bool enable ) +{ + MibRequestConfirm_t mibSet; + + mibSet.Type = MIB_ADR; + mibSet.Param.AdrEnable = enable; + + LoRaMacMibSetRequestConfirm( &mibSet ); +} + +void LoRaMacInitNwkIds( uint32_t netID, uint32_t devAddr, uint8_t *nwkSKey, uint8_t *appSKey ) +{ + MibRequestConfirm_t mibSet; + + mibSet.Type = MIB_NET_ID; + mibSet.Param.NetID = netID; + + LoRaMacMibSetRequestConfirm( &mibSet ); + + mibSet.Type = MIB_DEV_ADDR; + mibSet.Param.DevAddr = devAddr; + + LoRaMacMibSetRequestConfirm( &mibSet ); + + mibSet.Type = MIB_NWK_SKEY; + mibSet.Param.NwkSKey = nwkSKey; + + LoRaMacMibSetRequestConfirm( &mibSet ); + + mibSet.Type = MIB_APP_SKEY; + mibSet.Param.AppSKey = appSKey; + + LoRaMacMibSetRequestConfirm( &mibSet ); + + mibSet.Type = MIB_NETWORK_JOINED; + mibSet.Param.IsNetworkJoined = true; + + LoRaMacMibSetRequestConfirm( &mibSet ); +} + +void LoRaMacMulticastChannelAdd( MulticastParams_t *channelParam ) +{ + LoRaMacMulticastChannelLink( channelParam ); +} + +void LoRaMacMulticastChannelRemove( MulticastParams_t *channelParam ) +{ + LoRaMacMulticastChannelUnlink( channelParam ); +} + +uint8_t LoRaMacJoinReq( uint8_t *devEui, uint8_t *appEui, uint8_t *appKey ) +{ + MlmeReq_t mlmeRequest; + uint8_t status; + + mlmeRequest.Type = MLME_JOIN; + mlmeRequest.Req.Join.AppEui = appEui; + mlmeRequest.Req.Join.AppKey = appKey; + mlmeRequest.Req.Join.DevEui = devEui; + + switch( LoRaMacMlmeRequest( &mlmeRequest ) ) + { + case LORAMAC_STATUS_OK: + { + status = 0; + break; + } + case LORAMAC_STATUS_BUSY: + { + status = 1; + break; + } + case LORAMAC_STATUS_NO_NETWORK_JOINED: + { + status = 2; + break; + } + case LORAMAC_STATUS_LENGTH_ERROR: + case LORAMAC_STATUS_MAC_CMD_LENGTH_ERROR: + { + status = 3; + break; + } + case LORAMAC_STATUS_SERVICE_UNKNOWN: + { + status = 4; + break; + } + case LORAMAC_STATUS_DEVICE_OFF: + { + status = 6; + break; + } + default: + { + status = 1; + break; + } + } + + return status; +} + +uint8_t LoRaMacLinkCheckReq( void ) +{ + MlmeReq_t mlmeRequest; + uint8_t status; + + mlmeRequest.Type = MLME_LINK_CHECK; + + switch( LoRaMacMlmeRequest( &mlmeRequest ) ) + { + case LORAMAC_STATUS_OK: + { + status = 0; + break; + } + case LORAMAC_STATUS_SERVICE_UNKNOWN: + { + status = 1; + break; + } + default: + { + status = 1; + break; + } + } + + return status; +} + +uint8_t LoRaMacSendFrame( uint8_t fPort, void *fBuffer, uint16_t fBufferSize ) +{ + MibRequestConfirm_t mibGet; + McpsReq_t mcpsRequest; + uint8_t retStatus; + + memset1( ( uint8_t* )&LoRaMacEventInfo, 0, sizeof( LoRaMacEventInfo ) ); + + mibGet.Type = MIB_CHANNELS_DATARATE; + LoRaMacMibGetRequestConfirm( &mibGet ); + + mcpsRequest.Type = MCPS_UNCONFIRMED; + mcpsRequest.Req.Unconfirmed.fBuffer = fBuffer; + mcpsRequest.Req.Unconfirmed.fBufferSize = fBufferSize; + mcpsRequest.Req.Unconfirmed.fPort = fPort; + mcpsRequest.Req.Unconfirmed.Datarate = mibGet.Param.ChannelsDatarate; + + switch( LoRaMacMcpsRequest( &mcpsRequest ) ) + { + case LORAMAC_STATUS_OK: + retStatus = 0U; + break; + case LORAMAC_STATUS_BUSY: + retStatus = 1U; + break; + case LORAMAC_STATUS_NO_NETWORK_JOINED: + retStatus = 2U; + break; + case LORAMAC_STATUS_LENGTH_ERROR: + case LORAMAC_STATUS_MAC_CMD_LENGTH_ERROR: + retStatus = 3U; + break; + case LORAMAC_STATUS_SERVICE_UNKNOWN: + retStatus = 4U; + break; + case LORAMAC_STATUS_DEVICE_OFF: + retStatus = 6U; + break; + default: + retStatus = 1U; + break; + } + + return retStatus; +} + +uint8_t LoRaMacSendConfirmedFrame( uint8_t fPort, void *fBuffer, uint16_t fBufferSize, uint8_t nbRetries ) +{ + MibRequestConfirm_t mibGet; + McpsReq_t mcpsRequest; + uint8_t retStatus; + + memset1( ( uint8_t* )&LoRaMacEventInfo, 0, sizeof( LoRaMacEventInfo ) ); + + mibGet.Type = MIB_CHANNELS_DATARATE; + LoRaMacMibGetRequestConfirm( &mibGet ); + + mcpsRequest.Type = MCPS_CONFIRMED; + mcpsRequest.Req.Confirmed.fBuffer = fBuffer; + mcpsRequest.Req.Confirmed.fBufferSize = fBufferSize; + mcpsRequest.Req.Confirmed.fPort = fPort; + mcpsRequest.Req.Confirmed.nbRetries = nbRetries; + mcpsRequest.Req.Confirmed.Datarate = mibGet.Param.ChannelsDatarate; + + switch( LoRaMacMcpsRequest( &mcpsRequest ) ) + { + case LORAMAC_STATUS_OK: + retStatus = 0U; + break; + case LORAMAC_STATUS_BUSY: + retStatus = 1U; + break; + case LORAMAC_STATUS_NO_NETWORK_JOINED: + retStatus = 2U; + break; + case LORAMAC_STATUS_LENGTH_ERROR: + case LORAMAC_STATUS_MAC_CMD_LENGTH_ERROR: + retStatus = 3U; + break; + case LORAMAC_STATUS_SERVICE_UNKNOWN: + retStatus = 4U; + break; + case LORAMAC_STATUS_DEVICE_OFF: + retStatus = 6U; + break; + default: + retStatus = 1U; + break; + } + + return retStatus; +} + +uint8_t LoRaMacSend( LoRaMacHeader_t *macHdr, uint8_t *fOpts, uint8_t fPort, void *fBuffer, uint16_t fBufferSize ) +{ + uint8_t retStatus; + + memset1( ( uint8_t* ) &LoRaMacEventInfo, 0, sizeof( LoRaMacEventInfo ) ); + + switch( Send( macHdr, fPort, fBuffer, fBufferSize ) ) + { + case LORAMAC_STATUS_OK: + retStatus = 0U; + break; + case LORAMAC_STATUS_BUSY: + retStatus = 1U; + break; + case LORAMAC_STATUS_NO_NETWORK_JOINED: + retStatus = 2U; + break; + case LORAMAC_STATUS_LENGTH_ERROR: + case LORAMAC_STATUS_MAC_CMD_LENGTH_ERROR: + retStatus = 3U; + break; + case LORAMAC_STATUS_SERVICE_UNKNOWN: + retStatus = 4U; + break; + case LORAMAC_STATUS_DEVICE_OFF: + retStatus = 6U; + break; + default: + retStatus = 1U; + break; + } + + return retStatus; +} + +uint8_t LoRaMacPrepareFrame( ChannelParams_t channel,LoRaMacHeader_t *macHdr, LoRaMacFrameCtrl_t *fCtrl, uint8_t *fOpts, uint8_t fPort, void *fBuffer, uint16_t fBufferSize ) +{ + uint8_t retStatus; + + switch( PrepareFrame( macHdr, fCtrl, fPort, fBuffer, fBufferSize ) ) + { + case LORAMAC_STATUS_OK: + retStatus = 0U; + break; + case LORAMAC_STATUS_BUSY: + retStatus = 1U; + break; + case LORAMAC_STATUS_NO_NETWORK_JOINED: + retStatus = 2U; + break; + case LORAMAC_STATUS_LENGTH_ERROR: + case LORAMAC_STATUS_MAC_CMD_LENGTH_ERROR: + retStatus = 3U; + break; + case LORAMAC_STATUS_SERVICE_UNKNOWN: + retStatus = 4U; + break; + default: + retStatus = 1U; + break; + } + + return retStatus; +} + +uint8_t LoRaMacSendFrameOnChannel( ChannelParams_t channel ) +{ + memset1( ( uint8_t* ) &LoRaMacEventInfo, 0, sizeof( LoRaMacEventInfo ) ); + + SendFrameOnChannel( channel ); + + /* SendFrameOnChannel has always status "OK" */ + return 0; +} + +uint8_t LoRaMacSendOnChannel( ChannelParams_t channel, LoRaMacHeader_t *macHdr, LoRaMacFrameCtrl_t *fCtrl, uint8_t *fOpts, uint8_t fPort, void *fBuffer, uint16_t fBufferSize ) +{ + uint8_t status = 0; + + if( ( LoRaMacState & 0x00000001 ) == 0x00000001 ) + { + return 1; // MAC is busy transmitting a previous frame + } + + status = LoRaMacPrepareFrame( channel, macHdr, fCtrl, fOpts, fPort, fBuffer, fBufferSize ); + if( status != 0 ) + { + return status; + } + + LoRaMacEventInfo.TxNbRetries = 0; + LoRaMacEventInfo.TxAckReceived = false; + + return LoRaMacSendFrameOnChannel( channel ); +} + +void LoRaMacSetDeviceClass( DeviceClass_t deviceClass ) +{ + MibRequestConfirm_t mibSet; + + mibSet.Type = MIB_DEVICE_CLASS; + mibSet.Param.Class = deviceClass; + + LoRaMacMibSetRequestConfirm( &mibSet ); +} + +void LoRaMacSetPublicNetwork( bool enable ) +{ + MibRequestConfirm_t mibSet; + + mibSet.Type = MIB_PUBLIC_NETWORK; + mibSet.Param.EnablePublicNetwork = enable; + + LoRaMacMibSetRequestConfirm( &mibSet ); +} + +void LoRaMacSetDutyCycleOn( bool enable ) +{ + LoRaMacTestSetDutyCycleOn( enable ); +} + +void LoRaMacSetChannel( uint8_t id, ChannelParams_t params ) +{ + LoRaMacChannelAdd( id, params ); +} + +void LoRaMacSetRx2Channel( Rx2ChannelParams_t param ) +{ + MibRequestConfirm_t mibSet; + + mibSet.Type = MIB_RX2_CHANNEL; + mibSet.Param.Rx2Channel = param; + + LoRaMacMibSetRequestConfirm( &mibSet ); +} + +void LoRaMacSetChannelsMask( uint16_t *mask ) +{ + MibRequestConfirm_t mibSet; + + mibSet.Type = MIB_CHANNELS_MASK; + mibSet.Param.ChannelsMask = mask; + + LoRaMacMibSetRequestConfirm( &mibSet ); +} + +void LoRaMacSetChannelsNbRep( uint8_t nbRep ) +{ + MibRequestConfirm_t mibSet; + + mibSet.Type = MIB_CHANNELS_NB_REP; + mibSet.Param.ChannelNbRep = nbRep; + + LoRaMacMibSetRequestConfirm( &mibSet ); +} + +void LoRaMacSetMaxRxWindow( uint32_t delay ) +{ + MibRequestConfirm_t mibSet; + + mibSet.Type = MIB_MAX_RX_WINDOW_DURATION; + mibSet.Param.MaxRxWindow = delay; + + LoRaMacMibSetRequestConfirm( &mibSet ); +} + +void LoRaMacSetReceiveDelay1( uint32_t delay ) +{ + MibRequestConfirm_t mibSet; + + mibSet.Type = MIB_RECEIVE_DELAY_1; + mibSet.Param.ReceiveDelay1 = delay; + + LoRaMacMibSetRequestConfirm( &mibSet ); +} + +void LoRaMacSetReceiveDelay2( uint32_t delay ) +{ + MibRequestConfirm_t mibSet; + + mibSet.Type = MIB_RECEIVE_DELAY_2; + mibSet.Param.ReceiveDelay2 = delay; + + LoRaMacMibSetRequestConfirm( &mibSet ); +} + +void LoRaMacSetJoinAcceptDelay1( uint32_t delay ) +{ + MibRequestConfirm_t mibSet; + + mibSet.Type = MIB_JOIN_ACCEPT_DELAY_1; + mibSet.Param.JoinAcceptDelay1 = delay; + + LoRaMacMibSetRequestConfirm( &mibSet ); +} + +void LoRaMacSetJoinAcceptDelay2( uint32_t delay ) +{ + MibRequestConfirm_t mibSet; + + mibSet.Type = MIB_JOIN_ACCEPT_DELAY_2; + mibSet.Param.JoinAcceptDelay2 = delay; + + LoRaMacMibSetRequestConfirm( &mibSet ); +} + +void LoRaMacSetChannelsDatarate( int8_t datarate ) +{ + MibRequestConfirm_t mibSet; + + mibSet.Type = MIB_CHANNELS_DATARATE; + mibSet.Param.ChannelsDatarate = datarate; + + LoRaMacMibSetRequestConfirm( &mibSet ); +} + +void LoRaMacSetChannelsTxPower( int8_t txPower ) +{ + MibRequestConfirm_t mibSet; + + mibSet.Type = MIB_CHANNELS_TX_POWER; + mibSet.Param.ChannelsTxPower = txPower; + + LoRaMacMibSetRequestConfirm( &mibSet ); +} + +uint32_t LoRaMacGetUpLinkCounter( void ) +{ + MibRequestConfirm_t mibGet; + + mibGet.Type = MIB_UPLINK_COUNTER; + + LoRaMacMibGetRequestConfirm( &mibGet ); + + return mibGet.Param.UpLinkCounter; +} + +uint32_t LoRaMacGetDownLinkCounter( void ) +{ + MibRequestConfirm_t mibGet; + + mibGet.Type = MIB_DOWNLINK_COUNTER; + + LoRaMacMibGetRequestConfirm( &mibGet ); + + return mibGet.Param.DownLinkCounter; +} + +void LoRaMacSetMicTest( uint16_t txPacketCounter ) +{ + LoRaMacTestSetMic( txPacketCounter ); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LoRaMac-api-v3.h Tue Jan 05 16:41:54 2016 +0000 @@ -0,0 +1,463 @@ +/*! + * \file LoRaMac-api-v3.h + * + * \brief LoRa MAC wrapper layer implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jäckle ( STACKFORCE ) + */ +#ifndef __LORAMAC_API_V3_H__ +#define __LORAMAC_API_V3_H__ + +// Includes board dependent definitions such as channels frequencies +#include "LoRaMac.h" +#include "LoRaMac-board.h" + +/*! + * Beacon interval in us + */ +#define BEACON_INTERVAL 128000000 + +/*! + * Class A&B receive delay in us + */ +#define RECEIVE_DELAY1 1000000 +#define RECEIVE_DELAY2 2000000 + +/*! + * Join accept receive delay in us + */ +#define JOIN_ACCEPT_DELAY1 5000000 +#define JOIN_ACCEPT_DELAY2 6000000 + +/*! + * Class A&B maximum receive window delay in us + */ +#define MAX_RX_WINDOW 3000000 + +/*! + * Maximum allowed gap for the FCNT field + */ +#define MAX_FCNT_GAP 16384 + +/*! + * ADR acknowledgement counter limit + */ +#define ADR_ACK_LIMIT 64 + +/*! + * Number of ADR acknowledgement requests before returning to default datarate + */ +#define ADR_ACK_DELAY 32 + +/*! + * Number of seconds after the start of the second reception window without + * receiving an acknowledge. + * AckTimeout = ACK_TIMEOUT + Random( -ACK_TIMEOUT_RND, ACK_TIMEOUT_RND ) + */ +#define ACK_TIMEOUT 2000000 + +/*! + * Random number of seconds after the start of the second reception window without + * receiving an acknowledge + * AckTimeout = ACK_TIMEOUT + Random( -ACK_TIMEOUT_RND, ACK_TIMEOUT_RND ) + */ +#define ACK_TIMEOUT_RND 1000000 + +/*! + * Check the Mac layer state every MAC_STATE_CHECK_TIMEOUT + */ +#define MAC_STATE_CHECK_TIMEOUT 1000000 + +/*! + * Maximum number of times the MAC layer tries to get an acknowledge. + */ +#define MAX_ACK_RETRIES 8 + +/*! + * RSSI free threshold + */ +#define RSSI_FREE_TH ( int8_t )( -90 ) // [dBm] + +/*! + * Frame direction definition + */ +#define UP_LINK 0 +#define DOWN_LINK 1 + +/*! + * Sets the length of the LoRaMAC footer field. + * Mainly indicates the MIC field length + */ +#define LORAMAC_MFR_LEN 4 + +/*! + * Syncword for Private LoRa networks + */ +#define LORA_MAC_PRIVATE_SYNCWORD 0x12 + +/*! + * Syncword for Public LoRa networks + */ +#define LORA_MAC_PUBLIC_SYNCWORD 0x34 + +/*! + * LoRaMAC event flags + */ +typedef union +{ + uint8_t Value; + struct + { + uint8_t Tx : 1; + uint8_t Rx : 1; + uint8_t RxData : 1; + uint8_t Multicast : 1; + uint8_t RxSlot : 2; + uint8_t LinkCheck : 1; + uint8_t JoinAccept : 1; + }Bits; +}LoRaMacEventFlags_t; + +/*! + * LoRaMAC event information + */ +typedef struct +{ + LoRaMacEventInfoStatus_t Status; + bool TxAckReceived; + uint8_t TxNbRetries; + uint8_t TxDatarate; + uint8_t RxPort; + uint8_t *RxBuffer; + uint8_t RxBufferSize; + int16_t RxRssi; + uint8_t RxSnr; + uint16_t Energy; + uint8_t DemodMargin; + uint8_t NbGateways; +}LoRaMacEventInfo_t; + +/*! + * LoRaMAC events structure + * Used to notify upper layers of MAC events + */ +typedef struct sLoRaMacCallbacks +{ + /*! + * MAC layer event callback prototype. + * + * \param [IN] flags Bit field indicating the MAC events occurred + * \param [IN] info Details about MAC events occurred + */ + void ( *MacEvent )( LoRaMacEventFlags_t *flags, LoRaMacEventInfo_t *info ); + /*! + * Function callback to get the current battery level + * + * \retval batteryLevel Current battery level + */ + uint8_t ( *GetBatteryLevel )( void ); +}LoRaMacCallbacks_t; + +/*! + * LoRaMAC layer initialization + * + * \param [IN] callbacks Pointer to a structure defining the LoRaMAC + * callback functions. + */ +void LoRaMacInit( LoRaMacCallbacks_t *callbacks ); + +/*! + * Enables/Disables the ADR (Adaptive Data Rate) + * + * \param [IN] enable [true: ADR ON, false: ADR OFF] + */ +void LoRaMacSetAdrOn( bool enable ); + +/*! + * Initializes the network IDs. Device address, + * network session AES128 key and application session AES128 key. + * + * \remark To be only used when Over-the-Air activation isn't used. + * + * \param [IN] netID 24 bits network identifier + * ( provided by network operator ) + * \param [IN] devAddr 32 bits device address on the network + * (must be unique to the network) + * \param [IN] nwkSKey Pointer to the network session AES128 key array + * ( 16 bytes ) + * \param [IN] appSKey Pointer to the application session AES128 key array + * ( 16 bytes ) + */ +void LoRaMacInitNwkIds( uint32_t netID, uint32_t devAddr, uint8_t *nwkSKey, uint8_t *appSKey ); + +/* + * Wrapper function which calls \ref LoRaMacMulticastChannelLink. + */ +void LoRaMacMulticastChannelAdd( MulticastParams_t *channelParam ); + +/* + * Wrapper function which calls \ref LoRaMacMulticastChannelUnlink. + */ +void LoRaMacMulticastChannelRemove( MulticastParams_t *channelParam ); + +/*! + * Initiates the Over-the-Air activation + * + * \param [IN] devEui Pointer to the device EUI array ( 8 bytes ) + * \param [IN] appEui Pointer to the application EUI array ( 8 bytes ) + * \param [IN] appKey Pointer to the application AES128 key array ( 16 bytes ) + * + * \retval status [0: OK, 1: Tx error, 2: Already joined a network] + */ +uint8_t LoRaMacJoinReq( uint8_t *devEui, uint8_t *appEui, uint8_t *appKey ); + +/*! + * Sends a LinkCheckReq MAC command on the next uplink frame + * + * \retval status Function status [0: OK, 1: Busy] + */ +uint8_t LoRaMacLinkCheckReq( void ); + +/*! + * LoRaMAC layer send frame + * + * \param [IN] fPort MAC payload port (must be > 0) + * \param [IN] fBuffer MAC data buffer to be sent + * \param [IN] fBufferSize MAC data buffer size + * + * \retval status [0: OK, 1: Busy, 2: No network joined, + * 3: Length or port error, 4: Unknown MAC command + * 5: Unable to find a free channel + * 6: Device switched off] + */ +uint8_t LoRaMacSendFrame( uint8_t fPort, void *fBuffer, uint16_t fBufferSize ); + +/*! + * LoRaMAC layer send frame + * + * \param [IN] fPort MAC payload port (must be > 0) + * \param [IN] fBuffer MAC data buffer to be sent + * \param [IN] fBufferSize MAC data buffer size + * \param [IN] fBufferSize MAC data buffer size + * \param [IN] nbRetries Number of retries to receive the acknowledgement + * + * \retval status [0: OK, 1: Busy, 2: No network joined, + * 3: Length or port error, 4: Unknown MAC command + * 5: Unable to find a free channel + * 6: Device switched off] + */ +uint8_t LoRaMacSendConfirmedFrame( uint8_t fPort, void *fBuffer, uint16_t fBufferSize, uint8_t nbRetries ); + +/*! + * ============================================================================ + * = LoRaMac test functions = + * ============================================================================ + */ + +/*! + * LoRaMAC layer generic send frame + * + * \param [IN] macHdr MAC header 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 [0: OK, 1: Busy, 2: No network joined, + * 3: Length or port error, 4: Unknown MAC command + * 5: Unable to find a free channel + * 6: Device switched off] + */ +uint8_t LoRaMacSend( LoRaMacHeader_t *macHdr, uint8_t *fOpts, uint8_t fPort, void *fBuffer, uint16_t fBufferSize ); + +/*! + * LoRaMAC layer frame buffer initialization. + * + * \param [IN] channel Channel parameters + * \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 [0: OK, 1: N/A, 2: No network joined, + * 3: Length or port error, 4: Unknown MAC command] + */ +uint8_t LoRaMacPrepareFrame( ChannelParams_t channel,LoRaMacHeader_t *macHdr, LoRaMacFrameCtrl_t *fCtrl, uint8_t *fOpts, uint8_t fPort, void *fBuffer, uint16_t fBufferSize ); + +/*! + * LoRaMAC layer prepared frame buffer transmission with channel specification + * + * \remark LoRaMacPrepareFrame must be called at least once before calling this + * function. + * + * \param [IN] channel Channel parameters + * \retval status [0: OK, 1: Busy] + */ +uint8_t LoRaMacSendFrameOnChannel( ChannelParams_t channel ); + +/*! + * LoRaMAC layer generic send frame with channel specification + * + * \param [IN] channel Channel parameters + * \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 [0: OK, 1: Busy, 2: No network joined, + * 3: Length or port error, 4: Unknown MAC command] + */ +uint8_t LoRaMacSendOnChannel( ChannelParams_t channel, LoRaMacHeader_t *macHdr, LoRaMacFrameCtrl_t *fCtrl, uint8_t *fOpts, uint8_t fPort, void *fBuffer, uint16_t fBufferSize ); + +/*! + * ============================================================================ + * = LoRaMac setup functions = + * ============================================================================ + */ + +/* + * Wrapper function which calls \ref LoRaMacMibSetRequestConfirm to + * set the LoRaWan device class. + */ +void LoRaMacSetDeviceClass( DeviceClass_t deviceClass ); + +/* + * Wrapper function which calls \ref LoRaMacMibSetRequestConfirm to + * set the network type to public or private. + */ +void LoRaMacSetPublicNetwork( bool enable ); + +/* + * Wrapper function which calls \ref LoRaMacChannelAdd. + */ +void LoRaMacSetChannel( uint8_t id, ChannelParams_t params ); + +/* + * Wrapper function which calls \ref LoRaMacMibSetRequestConfirm to + * set the receive window 2 channel. + */ +void LoRaMacSetRx2Channel( Rx2ChannelParams_t param ); + +/*! + * Sets channels tx output power + * + * \param [IN] txPower [TX_POWER_20_DBM, TX_POWER_14_DBM, + TX_POWER_11_DBM, TX_POWER_08_DBM, + TX_POWER_05_DBM, TX_POWER_02_DBM] + */ +void LoRaMacSetChannelsTxPower( int8_t txPower ); + +/*! + * Sets channels datarate + * + * \param [IN] datarate eu868 - [DR_0, DR_1, DR_2, DR_3, DR_4, DR_5, DR_6, DR_7] + * us915 - [DR_0, DR_1, DR_2, DR_3, DR_4] + */ +void LoRaMacSetChannelsDatarate( int8_t datarate ); + +/* + * Wrapper function which calls \ref LoRaMacMibSetRequestConfirm to + * set the channels mask. + */ +void LoRaMacSetChannelsMask( uint16_t *mask ); + +/* + * Wrapper function which calls \ref LoRaMacMibSetRequestConfirm to + * set the number of repetitions on a channel. + */ +void LoRaMacSetChannelsNbRep( uint8_t nbRep ); + +/* + * Wrapper function which calls \ref LoRaMacMibSetRequestConfirm to + * set the maximum receive window duration in [us]. + */ +void LoRaMacSetMaxRxWindow( uint32_t delay ); + +/* + * Wrapper function which calls \ref LoRaMacMibSetRequestConfirm to + * set the receive delay 1 in [us]. + */ +void LoRaMacSetReceiveDelay1( uint32_t delay ); + +/* + * Wrapper function which calls \ref LoRaMacMibSetRequestConfirm to + * set the receive delay 2 in [us]. + */ +void LoRaMacSetReceiveDelay2( uint32_t delay ); + +/* + * Wrapper function which calls \ref LoRaMacMibSetRequestConfirm to + * set the join accept delay 1 in [us]. + */ +void LoRaMacSetJoinAcceptDelay1( uint32_t delay ); + +/* + * Wrapper function which calls \ref LoRaMacMibSetRequestConfirm to + * set the join accept delay 2 in [us]. + */ +void LoRaMacSetJoinAcceptDelay2( uint32_t delay ); + +/* + * Wrapper function which calls \ref LoRaMacMibGetRequestConfirm to + * get the up-link counter. + */ +uint32_t LoRaMacGetUpLinkCounter( void ); + +/* + * Wrapper function which calls \ref LoRaMacMibGetRequestConfirm to + * get the down-link counter. + */ +uint32_t LoRaMacGetDownLinkCounter( void ); + +/* + * ============================================================================ + * = LoRaMac test functions = + * ============================================================================ + */ + +/*! + * Disables/Enables the duty cycle enforcement (EU868) + * + * \param [IN] enable - Enabled or disables the duty cycle + */ +void LoRaMacTestSetDutyCycleOn( bool enable ); + +/*! + * Disables/Enables the reception windows opening + * + * \param [IN] enable [true: enable, false: disable] + */ +void LoRaMacTestRxWindowsOn( bool enable ); + +/*! + * Enables the MIC field test + * + * \param [IN] upLinkCounter Fixed Tx packet counter value + */ +void LoRaMacTestSetMic( uint16_t upLinkCounter ); + +#endif /* __LORAMAC_API_V3_H__ */
--- a/LoRaMac-board.h Mon Nov 23 10:09:43 2015 +0000 +++ b/LoRaMac-board.h Tue Jan 05 16:41:54 2016 +0000 @@ -428,7 +428,7 @@ * Channels[i].DrRange.Value = ( DR_3 << 4 ) | DR_0; * Channels[i].Band = 0; * } - * // 500 kHz channels + * // 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;
--- a/LoRaMac.cpp Mon Nov 23 10:09:43 2015 +0000 +++ b/LoRaMac.cpp Tue Jan 05 16:41:54 2016 +0000 @@ -5,17 +5,23 @@ _____) ) ____| | | || |_| ____( (___| | | | (______/|_____)_|_|_| \__)_____)\____)_| |_| (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 and Gregory Cristian +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 @@ -77,7 +83,7 @@ static uint32_t LoRaMacDevAddr; /*! - * Mutlicast channels linked list + * Multicast channels linked list */ static MulticastParams_t *MulticastChannels = NULL; @@ -310,7 +316,7 @@ /*! * Up/Down link data rates offset definition */ -const int8_t datarateOffsets[16][4] = +const int8_t datarateOffsets[16][4] = { { DR_10, DR_9 , DR_8 , DR_8 }, // DR_0 { DR_11, DR_10, DR_9 , DR_8 }, // DR_1 @@ -358,6 +364,11 @@ */ static ChannelParams_t Channels[LORA_MAX_NB_CHANNELS]; +/*! + * Contains the channels which remain to be applied. + */ +static uint16_t ChannelsMaskRemaining[6]; + #else #error "Please define a frequency band in the compiler options." #endif @@ -409,7 +420,7 @@ static uint8_t MaxDCycle = 0; /*! - * Agregated duty cycle management + * Aggregated duty cycle management */ static uint16_t AggregatedDCycle; static TimerTime_t AggregatedLastTxDoneTime; @@ -428,14 +439,14 @@ /*! * LoRaMac internal states */ -enum LoRaMacState_e +enum eLoRaMacState { MAC_IDLE = 0x00000000, MAC_TX_RUNNING = 0x00000001, MAC_RX = 0x00000002, MAC_ACK_REQ = 0x00000004, MAC_ACK_RETRY = 0x00000008, - MAC_CHANNEL_CHECK = 0x00000010, + MAC_TX_DELAYED = 0x00000010, }; /*! @@ -451,22 +462,17 @@ /*! * LoRaMac upper layer event functions */ -static LoRaMacCallbacks_t *LoRaMacCallbacks; - -/*! - * LoRaMac notification event flags - */ -LoRaMacEventFlags_t LoRaMacEventFlags; +static LoRaMacPrimitives_t *LoRaMacPrimitives; /*! - * LoRaMac notification event info + * LoRaMac upper layer callback functions */ -LoRaMacEventInfo_t LoRaMacEventInfo; +static LoRaMacCallback_t *LoRaMacCallbacks; /*! - * LoRaMac channel check timer + * Radio events function pointer */ -static TimerEvent_t ChannelCheckTimer; +static RadioEvents_t RadioEvents; /*! * LoRaMac duty cycle delayed Tx timer @@ -526,64 +532,122 @@ TimerTime_t TxTimeOnAir = 0; /*! - * Function to be executed on Radio Tx Done event + * 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; + +/*! + * \brief Function to be executed on Radio Tx Done event */ static void OnRadioTxDone( void ); /*! - * Function to be executed on Radio Rx Done event + * \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 ); /*! - * Function executed on Radio Tx Timeout event + * \brief Function executed on Radio Tx Timeout event */ static void OnRadioTxTimeout( void ); /*! - * Function executed on Radio Rx error event + * \brief Function executed on Radio Rx error event */ static void OnRadioRxError( void ); /*! - * Function executed on Radio Rx Timeout event + * \brief Function executed on Radio Rx Timeout event */ static void OnRadioRxTimeout( void ); /*! - * Function executed on Resend Frame timer event. + * \brief Function executed on Resend Frame timer event. */ static void OnMacStateCheckTimerEvent( void ); /*! - * Function executed on duty cycle delayed Tx timer event + * \brief Function executed on duty cycle delayed Tx timer event */ static void OnTxDelayedTimerEvent( void ); /*! - * Function executed on channel check timer event - */ -static void OnChannelCheckTimerEvent( void ); - -/*! - * Function executed on first Rx window timer event + * \brief Function executed on first Rx window timer event */ static void OnRxWindow1TimerEvent( void ); /*! - * Function executed on second Rx window timer event + * \brief Function executed on second Rx window timer event */ static void OnRxWindow2TimerEvent( void ); /*! - * Function executed on AckTimeout timer event + * \brief Function executed on AckTimeout timer event */ static void OnAckTimeoutTimerEvent( void ); /*! - * Radio events function pointer + * \brief Searches and set the next random available channel + * + * \retval status Function status [0: OK, 1: Unable to find a free channel] + */ +static TimerTime_t SetNextChannel( void ); + +/*! + * \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 RadioEvents_t RadioEvents; +static void RxWindowSetup( uint32_t freq, int8_t datarate, uint32_t bandwidth, uint16_t timeout, bool rxContinuous ); + +/*! + * \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 Validates if the payload fits into the frame, taking the datarate @@ -596,10 +660,12 @@ * * \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 ); +static bool ValidatePayloadLength( uint8_t lenN, int8_t datarate, uint8_t fOptsLen ); #if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) /*! @@ -621,132 +687,1203 @@ static int8_t LimitTxPower( int8_t txPower ); /*! - * Searches and set the next random available channel + * \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] updateChannelMask Set to true, if the channel masks shall be updated + * + * \param [OUT] datarateOut Reports the datarate which will be used next + * + * \retval Returns the state of ADR ack request + */ +static bool AdrNextDr( bool adrEnabled, bool updateChannelMask, int8_t* datarateOut ); + +/*! + * \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 status Function status [0: OK, 1: Unable to find a free channel] + * \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 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. */ -static uint8_t LoRaMacSetNextChannel( void ) +LoRaMacStatus_t SendFrameOnChannel( ChannelParams_t channel ); + + + +static void OnRadioTxDone( void ) +{ + TimerTime_t curTime = TimerGetCurrentTime( ); + if( LoRaMacDeviceClass != CLASS_C ) + { + Radio.Sleep( ); + } + else + { + OnRxWindow2TimerEvent( ); + } + + // Update Band Time OFF + Bands[Channels[Channel].Band].LastTxDoneTime = curTime; + if( DutyCycleOn == true ) + { + Bands[Channels[Channel].Band].TimeOff = TxTimeOnAir * Bands[Channels[Channel].Band].DCycle - TxTimeOnAir; + } + else + { + Bands[Channels[Channel].Band].TimeOff = 0; + } + // Update Aggregated Time OFF + AggregatedLastTxDoneTime = curTime; + AggregatedTimeOff = AggregatedTimeOff + ( TxTimeOnAir * AggregatedDCycle - TxTimeOnAir ); + + 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 OnRadioRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ) { - uint8_t i = 0; - uint8_t j = 0; - uint8_t k = 0; + LoRaMacHeader_t macHdr; + LoRaMacFrameCtrl_t fCtrl; + + 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; +#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) + /* + * WARNING: To be removed once Semtech server implementation + * is corrected. + */ + if( Rx2Channel.Datarate == DR_3 ) + { + Rx2Channel.Datarate = DR_8; + } +#endif + // 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; + + 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 ); + } + } +#endif + MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK; + IsLoRaMacNetworkJoined = true; + ChannelsDatarate = ChannelsDefaultDatarate; + } + else + { + MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL; + } + break; + case FRAME_TYPE_DATA_CONFIRMED_DOWN: + case FRAME_TYPE_DATA_UNCONFIRMED_DOWN: + { + LoRaMacFlags.Bits.McpsInd = 1; + + 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. + LoRaMacFlags.Bits.MacDone = 1; + McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL; + LoRaMacState &= ~MAC_TX_RUNNING; + if( NodeAckRequested ) + { + OnAckTimeoutTimerEvent( ); + } + return; + } + } + else + { + multicast = 0; + nwkSKey = LoRaMacNwkSKey; + appSKey = LoRaMacAppSKey; + downLinkCounter = DownLinkCounter; + } + + if( LoRaMacDeviceClass != CLASS_A ) + { + LoRaMacState |= MAC_RX; + // Starts the MAC layer status check timer + TimerStart( &MacStateCheckTimer ); + } + 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; + } + } + + 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; + + // Update 32 bits downlink counter + if( multicast == 1 ) + { + McpsIndication.McpsIndication = MCPS_MULTICAST; + + if( ( curMulticastParams->DownLinkCounter == downLinkCounter ) && + ( curMulticastParams->DownLinkCounter != 0 ) ) + { + LoRaMacFlags.Bits.MacDone = 1; + McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED; + McpsIndication.DownLinkCounter = downLinkCounter; + LoRaMacState &= ~MAC_TX_RUNNING; + if( NodeAckRequested == true ) + { + OnAckTimeoutTimerEvent( ); + } + return; + } + curMulticastParams->DownLinkCounter = downLinkCounter; + } + else + { + if( macHdr.Bits.MType == FRAME_TYPE_DATA_CONFIRMED_DOWN ) + { + McpsIndication.McpsIndication = MCPS_CONFIRMED; + } + else + { + McpsIndication.McpsIndication = MCPS_UNCONFIRMED; + } + if( ( DownLinkCounter == downLinkCounter ) && + ( DownLinkCounter != 0 ) ) + { + LoRaMacFlags.Bits.MacDone = 1; + McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED; + McpsIndication.DownLinkCounter = downLinkCounter; + LoRaMacState &= ~MAC_TX_RUNNING; + if( NodeAckRequested == true ) + { + OnAckTimeoutTimerEvent( ); + } + return; + } + DownLinkCounter = downLinkCounter; + } + + if( macHdr.Bits.MType == FRAME_TYPE_DATA_CONFIRMED_DOWN ) + { + SrvAckRequested = true; + } + else + { + SrvAckRequested = 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 ); + } + } + + if( fCtrl.Bits.FOptsLen > 0 ) + { + // Decode Options field MAC commands + ProcessMacCommands( payload, 8, appPayloadStartIndex, snr ); + } + if( ( ( size - 4 ) - appPayloadStartIndex ) > 0 ) + { + port = payload[appPayloadStartIndex++]; + frameLen = ( size - 4 ) - appPayloadStartIndex; + + McpsIndication.Port = port; + + if( port == 0 ) + { + LoRaMacPayloadDecrypt( payload + appPayloadStartIndex, + frameLen, + nwkSKey, + address, + DOWN_LINK, + downLinkCounter, + LoRaMacRxPayload ); + + // Decode frame payload MAC commands + ProcessMacCommands( LoRaMacRxPayload, 0, frameLen, snr ); + } + else + { + LoRaMacPayloadDecrypt( payload + appPayloadStartIndex, + frameLen, + appSKey, + address, + DOWN_LINK, + downLinkCounter, + LoRaMacRxPayload ); + + McpsIndication.Buffer = LoRaMacRxPayload; + McpsIndication.BufferSize = frameLen; + McpsIndication.RxData = true; + } + } + } + else + { + McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_MIC_FAIL; + LoRaMacState &= ~MAC_TX_RUNNING; + if( NodeAckRequested == true ) + { + OnAckTimeoutTimerEvent( ); + } + } + } + break; + case FRAME_TYPE_PROPRIETARY: + { + LoRaMacFlags.Bits.McpsInd = 1; + + memcpy1( LoRaMacRxPayload, &payload[pktHeaderLen], size ); + + McpsIndication.McpsIndication = MCPS_PROPRIETARY; + McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_OK; + McpsIndication.Buffer = LoRaMacRxPayload; + McpsIndication.BufferSize = size - pktHeaderLen; + break; + } + default: + LoRaMacFlags.Bits.McpsInd = 1; + McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + LoRaMacState &= ~MAC_TX_RUNNING; + break; + } + + if( ( RxSlot == 0 ) && ( LoRaMacDeviceClass == CLASS_C ) ) + { + OnRxWindow2TimerEvent( ); + } + + LoRaMacFlags.Bits.MacDone = 1; +} + +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( ( 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; + 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_MIN_DATARATE ); + } + 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 ) + // Re-enable default channels + ChannelsMask[0] = 0xFFFF; + ChannelsMask[1] = 0xFFFF; + ChannelsMask[2] = 0xFFFF; + ChannelsMask[3] = 0xFFFF; + ChannelsMask[4] = 0x00FF; + ChannelsMask[5] = 0x0000; +#elif 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; +#else + #error "Please define a frequency band in the compiler options." +#endif + LoRaMacState &= ~MAC_TX_RUNNING; + + 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 + 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 ) + { // DR_6, DR_5, DR_4, DR_3 + symbTimeout = 8; + } + if( datarate == DR_6 ) + {// LoRa 250 kHz + bandwidth = 1; + } + 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 + if( datarate > DR_0 ) + { // DR_1, DR_2, DR_3, DR_4, DR_8, DR_9, DR_10, DR_11, DR_12, DR_13 + symbTimeout = 8; + } + 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 ) + { // DR_6, DR_5, DR_4, DR_3 + symbTimeout = 8; + } + if( Rx2Channel.Datarate == DR_6 ) + {// LoRa 250 kHz + bandwidth = 1; + } +#elif ( defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) ) + // For higher datarates, we increase the number of symbols generating a Rx Timeout + if( Rx2Channel.Datarate > DR_0 ) + { // DR_1, DR_2, DR_3, DR_4, DR_8, DR_9, DR_10, DR_11, DR_12, DR_13 + symbTimeout = 8; + } + 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; + } +} + +static TimerTime_t SetNextChannel( void ) +{ uint8_t nbEnabledChannels = 0; uint8_t enabledChannels[LORA_MAX_NB_CHANNELS]; TimerTime_t curTime = TimerGetCurrentTime( ); + 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 ) + + ( ChannelsMaskRemaining[4] & 0x00FF ) ) == 0 ) + { + memcpy1( ( uint8_t* ) ChannelsMaskRemaining, ( uint8_t* ) ChannelsMask, + sizeof( ChannelsMask ) ); + } +#endif + // Update Aggregated duty cycle if( AggregatedTimeOff < ( curTime - AggregatedLastTxDoneTime ) ) { AggregatedTimeOff = 0; - } - - // Update bands Time OFF - TimerTime_t minTime = ( TimerTime_t )( -1 ); - for( i = 0; i < LORA_MAX_NB_BANDS; i++ ) - { - if( DutyCycleOn == true ) + + // Update bands Time OFF + for( uint8_t i = 0; i < LORA_MAX_NB_BANDS; i++ ) { - if( Bands[i].TimeOff < ( curTime - Bands[i].LastTxDoneTime ) ) + if( DutyCycleOn == true ) { + if( Bands[i].TimeOff < ( curTime - Bands[i].LastTxDoneTime ) ) + { + Bands[i].TimeOff = 0; + } + if( Bands[i].TimeOff != 0 ) + { + nextTxDelay = MIN( Bands[i].TimeOff - + ( curTime - Bands[i].LastTxDoneTime ), + nextTxDelay ); + } + } + else + { + nextTxDelay = 0; Bands[i].TimeOff = 0; } - if( 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++ ) { - minTime = MIN( Bands[i].TimeOff, minTime ); +#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 +#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) + DisableChannelInMask( i + j, ChannelsMaskRemaining ); +#endif + continue; + } + 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 +#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) + DisableChannelInMask( i + j, ChannelsMaskRemaining ); +#endif + continue; + } + if( Bands[Channels[i + j].Band].TimeOff > 0 ) + { // Check if the band is available for transmission + continue; + } + enabledChannels[nbEnabledChannels++] = i + j; + } + } + } + } + else + { + nextTxDelay = AggregatedTimeOff - ( curTime - AggregatedLastTxDoneTime ); + } + + if( nbEnabledChannels > 0 ) + { + Channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; +#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) + DisableChannelInMask( Channel, ChannelsMaskRemaining ); +#endif + return 0; + } + else + { + // Protect the function of returning the initialization value of nextTxDelay + return ( nextTxDelay == ( TimerTime_t )( -1 ) ) ? 0 : nextTxDelay; + } +} + +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] ); + } + else + { + Radio.SetMaxPayloadLength( modem, MaxPayloadOfDatarate[datarate] ); + } + + if( rxContinuous == false ) + { + Radio.Rx( MaxRxWindow ); + } + else + { + Radio.Rx( 0 ); // Continuous mode + } + } +} + +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; +} + +#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++ ) + { + for( uint8_t j = 0; j < 16; j++ ) + {// Verify if the channel is active + if( ( channelsMask[k] & ( 1 << j ) ) == ( 1 << j ) ) + { + nb125kHzChannels++; + } + } + } + + return nb125kHzChannels; +} +#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 ) < 50 ) + {// 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 updateChannelMask, int8_t* datarateOut ) +{ + bool adrAckReq = false; + int8_t datarate = ChannelsDatarate; + + if( adrEnabled == true ) + { + if( datarate == LORAMAC_MIN_DATARATE ) + { + AdrAckCounter = 0; + adrAckReq = false; + + if( updateChannelMask == true ) + { +#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 ) +#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 } } else { - minTime = 0; - Bands[i].TimeOff = 0; - } - } - - // Search how many channels are enabled - for( i = 0, k = 0; i < LORA_MAX_NB_CHANNELS; i += 16, k++ ) - { - for( j = 0; j < 16; j++ ) - { - if( ( ChannelsMask[k] & ( 1 << j ) ) != 0 ) + if( AdrAckCounter > ADR_ACK_LIMIT ) + { + adrAckReq = true; + } + else + { + adrAckReq = false; + } + if( AdrAckCounter > ( ADR_ACK_LIMIT + ADR_ACK_DELAY ) ) { - if( Channels[i + j].Frequency == 0 ) - { // Check if the channel is enabled - continue; - } - 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; + AdrAckCounter = 0; +#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 ) + if( datarate > LORAMAC_MIN_DATARATE ) + { + datarate--; } - if( Bands[Channels[i + j].Band].TimeOff > 0 ) - { // Check if the band is available for transmission - continue; +#elif defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) + if( ( datarate > LORAMAC_MIN_DATARATE ) && ( datarate == DR_8 ) ) + { + datarate = DR_4; } - if( AggregatedTimeOff > 0 ) - { // Check if there is time available for transmission - continue; + else if( datarate > LORAMAC_MIN_DATARATE ) + { + datarate--; } - enabledChannels[nbEnabledChannels++] = i + j; +#else +#error "Please define a frequency band in the compiler options." +#endif } } } - if( nbEnabledChannels > 0 ) - { - Channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; - LoRaMacState &= ~MAC_CHANNEL_CHECK; - TimerStop( &ChannelCheckTimer ); - return 0; - } - // No free channel found. - // Check again - if( ( LoRaMacState & MAC_CHANNEL_CHECK ) == 0 ) - { - TimerSetValue( &ChannelCheckTimer, minTime ); - TimerStart( &ChannelCheckTimer ); - LoRaMacState |= MAC_CHANNEL_CHECK; - } - return 1; + + *datarateOut = datarate; + + return adrAckReq; } -/* - * TODO: Add documentation - */ -void OnChannelCheckTimerEvent( void ) +static LoRaMacStatus_t AddMacCommand( uint8_t cmd, uint8_t p1, uint8_t p2 ) { - TimerStop( &ChannelCheckTimer ); - - LoRaMacState &= ~MAC_CHANNEL_CHECK; - if( LoRaMacSetNextChannel( ) == 0 ) - { - if( ( LoRaMacState & MAC_TX_RUNNING ) == MAC_TX_RUNNING ) - { - LoRaMacSendFrameOnChannel( Channels[Channel] ); - } - } -} - -/*! - * 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: Busy] - */ -static uint8_t AddMacCommand( uint8_t cmd, uint8_t p1, uint8_t p2 ) -{ - uint8_t status = 2; // Busy + LoRaMacStatus_t status = LORAMAC_STATUS_BUSY; switch( cmd ) { @@ -755,7 +1892,7 @@ { MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; // No payload for this command - status = 0; // OK + status = LORAMAC_STATUS_OK; } break; case MOTE_MAC_LINK_ADR_ANS: @@ -764,7 +1901,7 @@ MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; // Margin MacCommandsBuffer[MacCommandsBufferIndex++] = p1; - status = 0; // OK + status = LORAMAC_STATUS_OK; } break; case MOTE_MAC_DUTY_CYCLE_ANS: @@ -772,7 +1909,7 @@ { MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; // No payload for this answer - status = 0; // OK + status = LORAMAC_STATUS_OK; } break; case MOTE_MAC_RX_PARAM_SETUP_ANS: @@ -781,7 +1918,7 @@ MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; // Status: Datarate ACK, Channel ACK MacCommandsBuffer[MacCommandsBufferIndex++] = p1; - status = 0; // OK + status = LORAMAC_STATUS_OK; } break; case MOTE_MAC_DEV_STATUS_ANS: @@ -792,7 +1929,7 @@ // 2nd byte Margin MacCommandsBuffer[MacCommandsBufferIndex++] = p1; MacCommandsBuffer[MacCommandsBufferIndex++] = p2; - status = 0; // OK + status = LORAMAC_STATUS_OK; } break; case MOTE_MAC_NEW_CHANNEL_ANS: @@ -801,7 +1938,7 @@ MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; // Status: Datarate range OK, Channel frequency OK MacCommandsBuffer[MacCommandsBufferIndex++] = p1; - status = 0; // OK + status = LORAMAC_STATUS_OK; } break; case MOTE_MAC_RX_TIMING_SETUP_ANS: @@ -809,642 +1946,20 @@ { MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; // No payload for this answer - status = 0; // OK + status = LORAMAC_STATUS_OK; } break; default: - return 1; // Unknown command + return LORAMAC_STATUS_SERVICE_UNKNOWN; } - if( status == 0 ) + if( status == LORAMAC_STATUS_OK ) { MacCommandsInNextTx = true; } return status; } -// TODO: Add Documentation -static void LoRaMacNotify( LoRaMacEventFlags_t *flags, LoRaMacEventInfo_t *info ) -{ - if( ( LoRaMacCallbacks != NULL ) && ( LoRaMacCallbacks->MacEvent != NULL ) ) - { - LoRaMacCallbacks->MacEvent( flags, info ); - } - flags->Value = 0; -} - -void LoRaMacInit( LoRaMacCallbacks_t *callbacks ) -{ - LoRaMacCallbacks = callbacks; - - LoRaMacEventFlags.Value = 0; - - LoRaMacEventInfo.TxAckReceived = false; - LoRaMacEventInfo.TxNbRetries = 0; - LoRaMacEventInfo.TxDatarate = 7; - LoRaMacEventInfo.RxPort = 1; - LoRaMacEventInfo.RxBuffer = NULL; - LoRaMacEventInfo.RxBufferSize = 0; - LoRaMacEventInfo.RxRssi = 0; - LoRaMacEventInfo.RxSnr = 0; - LoRaMacEventInfo.Energy = 0; - LoRaMacEventInfo.DemodMargin = 0; - LoRaMacEventInfo.NbGateways = 0; - LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_OK; - - LoRaMacDeviceClass = CLASS_A; - - UpLinkCounter = 1; - DownLinkCounter = 0; - - IsLoRaMacNetworkJoined = false; - LoRaMacState = MAC_IDLE; - -#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; -#elif defined( USE_BAND_915_HYBRID ) - ChannelsMask[0] = 0x00FF; - ChannelsMask[1] = 0x0000; - ChannelsMask[2] = 0x0000; - ChannelsMask[3] = 0x0000; - ChannelsMask[4] = 0x0001; - ChannelsMask[5] = 0x0000; -#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; - - 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; - - TimerInit( &MacStateCheckTimer, OnMacStateCheckTimerEvent ); - TimerSetValue( &MacStateCheckTimer, MAC_STATE_CHECK_TIMEOUT ); - - TimerInit( &ChannelCheckTimer, OnChannelCheckTimerEvent ); - 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; - LoRaMacSetPublicNetwork( PublicNetwork ); - Radio.Sleep( ); -} - -void LoRaMacSetAdrOn( bool enable ) -{ - AdrCtrlOn = enable; -} - -void LoRaMacInitNwkIds( uint32_t netID, uint32_t devAddr, uint8_t *nwkSKey, uint8_t *appSKey ) -{ - LoRaMacNetID = netID; - LoRaMacDevAddr = devAddr; - LoRaMacMemCpy( nwkSKey, LoRaMacNwkSKey, 16 ); - LoRaMacMemCpy( appSKey, LoRaMacAppSKey, 16 ); - - IsLoRaMacNetworkJoined = true; -} - -void LoRaMacMulticastChannelAdd( MulticastParams_t *channelParam ) -{ - // Reset downlink counter - channelParam->DownLinkCounter = 0; - - if( MulticastChannels == NULL ) - { - MulticastChannels = channelParam; - } - else - { - MulticastParams_t *cur = MulticastChannels; - while( cur->Next != NULL ) - { - cur = cur->Next; - } - cur->Next = channelParam; - } -} - -void LoRaMacMulticastChannelRemove( MulticastParams_t *channelParam ) -{ - MulticastParams_t *cur = NULL; - - // Remove the front element - if( MulticastChannels == channelParam ) - { - if( MulticastChannels != NULL ) - { - cur = MulticastChannels; - MulticastChannels = MulticastChannels->Next; - cur->Next = NULL; - // Last node in the list - if( cur == MulticastChannels ) - { - MulticastChannels = NULL; - } - } - return; - } - - // Remove last element - if( channelParam->Next == NULL ) - { - if( MulticastChannels != NULL ) - { - cur = MulticastChannels; - MulticastParams_t *last = NULL; - while( cur->Next != NULL ) - { - last = cur; - cur = cur->Next; - } - if( last != NULL ) - { - last->Next = NULL; - } - // Last node in the list - if( cur == last ) - { - MulticastChannels = NULL; - } - } - return; - } - - // Remove a middle element - cur = MulticastChannels; - while( cur != NULL ) - { - if( cur->Next == channelParam ) - { - break; - } - cur = cur->Next; - } - if( cur != NULL ) - { - MulticastParams_t *tmp = cur ->Next; - cur->Next = tmp->Next; - tmp->Next = NULL; - } -} - -uint8_t LoRaMacJoinReq( uint8_t *devEui, uint8_t *appEui, uint8_t *appKey ) -{ - LoRaMacHeader_t macHdr; - - LoRaMacDevEui = devEui; - LoRaMacAppEui = appEui; - LoRaMacAppKey = appKey; - - macHdr.Value = 0; - macHdr.Bits.MType = FRAME_TYPE_JOIN_REQ; - - IsLoRaMacNetworkJoined = false; - -#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) - static uint8_t drSwitch = 0; - - if( ( ++drSwitch & 0x01 ) == 0x01 ) - { - ChannelsDatarate = DR_0; - } - else - { - ChannelsDatarate = DR_4; - } -#endif - return LoRaMacSend( &macHdr, NULL, 0, NULL, 0 ); -} - -uint8_t LoRaMacLinkCheckReq( void ) -{ - return AddMacCommand( MOTE_MAC_LINK_CHECK_REQ, 0, 0 ); -} - -uint8_t LoRaMacSendFrame( uint8_t fPort, void *fBuffer, uint16_t fBufferSize ) -{ - LoRaMacHeader_t macHdr; - - macHdr.Value = 0; - - macHdr.Bits.MType = FRAME_TYPE_DATA_UNCONFIRMED_UP; - return LoRaMacSend( &macHdr, NULL, fPort, fBuffer, fBufferSize ); -} - -uint8_t LoRaMacSendConfirmedFrame( uint8_t fPort, void *fBuffer, uint16_t fBufferSize, uint8_t retries ) -{ - LoRaMacHeader_t macHdr; - - if( AdrCtrlOn == false ) - { - ChannelsDatarate = ChannelsDefaultDatarate; - } - AckTimeoutRetries = retries; - AckTimeoutRetriesCounter = 1; - - macHdr.Value = 0; - - macHdr.Bits.MType = FRAME_TYPE_DATA_CONFIRMED_UP; - return LoRaMacSend( &macHdr, NULL, fPort, fBuffer, fBufferSize ); -} - -uint8_t LoRaMacSend( LoRaMacHeader_t *macHdr, uint8_t *fOpts, uint8_t fPort, void *fBuffer, uint16_t fBufferSize ) -{ - LoRaMacFrameCtrl_t fCtrl; - - fCtrl.Value = 0; - - fCtrl.Bits.FOptsLen = 0; - fCtrl.Bits.FPending = 0; - fCtrl.Bits.Ack = false; - fCtrl.Bits.AdrAckReq = false; - fCtrl.Bits.Adr = AdrCtrlOn; - - if( LoRaMacSetNextChannel( ) == 0 ) - { - return LoRaMacSendOnChannel( Channels[Channel], macHdr, &fCtrl, fOpts, fPort, fBuffer, fBufferSize ); - } - return 5; -} - -uint8_t LoRaMacPrepareFrame( ChannelParams_t channel, LoRaMacHeader_t *macHdr, LoRaMacFrameCtrl_t *fCtrl, uint8_t *fOpts, uint8_t fPort, void *fBuffer, uint16_t fBufferSize ) -{ - uint16_t i; - uint8_t pktHeaderLen = 0; - uint32_t mic = 0; - - LoRaMacBufferPktLen = 0; - - NodeAckRequested = false; - - if( fBuffer == NULL ) - { - fBufferSize = 0; - } - else - { - if( ValidatePayloadLength( fBufferSize, ChannelsDatarate ) == false ) - { - return 3; - } - } - - 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; - - LoRaMacMemCpy( LoRaMacAppEui, LoRaMacBuffer + LoRaMacBufferPktLen, 8 ); - LoRaMacBufferPktLen += 8; - LoRaMacMemCpy( LoRaMacDevEui, LoRaMacBuffer + LoRaMacBufferPktLen, 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 2; // No network has been joined yet - } - - RxWindow1Delay = ReceiveDelay1 - RADIO_WAKEUP_TIME; - RxWindow2Delay = ReceiveDelay2 - RADIO_WAKEUP_TIME; - - if( fOpts == NULL ) - { - fCtrl->Bits.FOptsLen = 0; - } - - if( SrvAckRequested == true ) - { - SrvAckRequested = false; - fCtrl->Bits.Ack = 1; - } - - if( fCtrl->Bits.Adr == true ) - { - if( ChannelsDatarate == LORAMAC_MIN_DATARATE ) - { - AdrAckCounter = 0; - fCtrl->Bits.AdrAckReq = false; - } - else - { - if( AdrAckCounter > ADR_ACK_LIMIT ) - { - fCtrl->Bits.AdrAckReq = true; - } - else - { - fCtrl->Bits.AdrAckReq = false; - } - if( AdrAckCounter > ( ADR_ACK_LIMIT + ADR_ACK_DELAY ) ) - { - AdrAckCounter = 0; -#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 ) - if( ChannelsDatarate > LORAMAC_MIN_DATARATE ) - { - ChannelsDatarate--; - } - else - { - // 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( ( ChannelsDatarate > LORAMAC_MIN_DATARATE ) && ( ChannelsDatarate == DR_8 ) ) - { - ChannelsDatarate = DR_4; - } - if( ChannelsDatarate > LORAMAC_MIN_DATARATE ) - { - ChannelsDatarate--; - } - else - { -#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 - } - } - } - - 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; - - if( fOpts != NULL ) - { - for( i = 0; i < fCtrl->Bits.FOptsLen; i++ ) - { - LoRaMacBuffer[pktHeaderLen++] = fOpts[i]; - } - } - if( ( MacCommandsBufferIndex + fCtrl->Bits.FOptsLen ) <= 15 ) - { - if( 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]; - } - } - } - MacCommandsInNextTx = false; - MacCommandsBufferIndex = 0; - - if( ( pktHeaderLen + fBufferSize ) > LORAMAC_PHY_MAXPAYLOAD ) - { - return 3; - } - - if( fBuffer != NULL ) - { - LoRaMacBuffer[pktHeaderLen++] = fPort; - - if( fPort == 0 ) - { - LoRaMacPayloadEncrypt( ( uint8_t* )fBuffer, fBufferSize, LoRaMacNwkSKey, LoRaMacDevAddr, UP_LINK, UpLinkCounter, LoRaMacPayload ); - } - else - { - LoRaMacPayloadEncrypt( ( uint8_t* )fBuffer, fBufferSize, LoRaMacAppSKey, LoRaMacDevAddr, UP_LINK, UpLinkCounter, LoRaMacPayload ); - } - LoRaMacMemCpy( LoRaMacPayload, LoRaMacBuffer + pktHeaderLen, fBufferSize ); - } - LoRaMacBufferPktLen = pktHeaderLen + fBufferSize; - - LoRaMacComputeMic( LoRaMacBuffer, LoRaMacBufferPktLen, LoRaMacNwkSKey, LoRaMacDevAddr, UP_LINK, UpLinkCounter, &mic ); - - if( ( LoRaMacBufferPktLen + LORAMAC_MFR_LEN ) > LORAMAC_PHY_MAXPAYLOAD ) - { - return 3; - } - 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; - default: - return 4; - } - - return 0; -} - -uint8_t LoRaMacSendFrameOnChannel( ChannelParams_t channel ) -{ - LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; - LoRaMacEventInfo.TxDatarate = ChannelsDatarate; - - ChannelsTxPower = LimitTxPower( ChannelsTxPower ); - - Radio.SetChannel( channel.Frequency ); - Radio.SetMaxPayloadLength( MODEM_LORA, LoRaMacBufferPktLen ); - -#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 ) - if( ChannelsDatarate == DR_7 ) - { // High Speed FSK channel - Radio.SetTxConfig( MODEM_FSK, TxPowers[ChannelsTxPower], 25e3, 0, Datarates[ChannelsDatarate] * 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.SetTxConfig( MODEM_LORA, TxPowers[ChannelsTxPower], 0, 1, Datarates[ChannelsDatarate], 1, 8, false, true, 0, 0, false, 3e6 ); - TxTimeOnAir = Radio.TimeOnAir( MODEM_LORA, LoRaMacBufferPktLen ); - } - else - { // Normal LoRa channel - Radio.SetTxConfig( MODEM_LORA, TxPowers[ChannelsTxPower], 0, 0, Datarates[ChannelsDatarate], 1, 8, false, true, 0, 0, false, 3e6 ); - TxTimeOnAir = Radio.TimeOnAir( MODEM_LORA, LoRaMacBufferPktLen ); - } -#elif defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) - if( ChannelsDatarate >= DR_4 ) - { // High speed LoRa channel BW500 kHz - Radio.SetTxConfig( MODEM_LORA, TxPowers[ChannelsTxPower], 0, 2, Datarates[ChannelsDatarate], 1, 8, false, true, 0, 0, false, 3e6 ); - TxTimeOnAir = Radio.TimeOnAir( MODEM_LORA, LoRaMacBufferPktLen ); - } - else - { // Normal LoRa channel - Radio.SetTxConfig( MODEM_LORA, TxPowers[ChannelsTxPower], 0, 0, Datarates[ChannelsDatarate], 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 - - if( MaxDCycle == 255 ) - { - return 6; - } - if( MaxDCycle == 0 ) - { - AggregatedTimeOff = 0; - } - - LoRaMacState |= MAC_TX_RUNNING; - // Starts the MAC layer status check timer - TimerStart( &MacStateCheckTimer ); - - if( MAX( Bands[channel.Band].TimeOff, AggregatedTimeOff ) > ( TimerGetCurrentTime( ) ) ) - { - // Schedule transmission - TimerSetValue( &TxDelayedTimer, MAX( Bands[channel.Band].TimeOff, AggregatedTimeOff ) ); - TimerStart( &TxDelayedTimer ); - } - else - { - // Send now - Radio.Send( LoRaMacBuffer, LoRaMacBufferPktLen ); - } - return 0; -} - - -void OnTxDelayedTimerEvent( void ) -{ - TimerStop( &TxDelayedTimer ); - Radio.Send( LoRaMacBuffer, LoRaMacBufferPktLen ); -} - -uint8_t LoRaMacSendOnChannel( ChannelParams_t channel, LoRaMacHeader_t *macHdr, LoRaMacFrameCtrl_t *fCtrl, uint8_t *fOpts, uint8_t fPort, void *fBuffer, uint16_t fBufferSize ) -{ - uint8_t status = 0; - - if( ( LoRaMacState & MAC_TX_RUNNING ) == MAC_TX_RUNNING ) - { - return 1; // MAC is busy transmitting a previous frame - } - - status = LoRaMacPrepareFrame( channel, macHdr, fCtrl, fOpts, fPort, fBuffer, fBufferSize ); - if( status != 0 ) - { - return status; - } - - LoRaMacEventInfo.TxNbRetries = 0; - LoRaMacEventInfo.TxAckReceived = false; - - return LoRaMacSendFrameOnChannel( channel ); -} - -static void LoRaMacProcessMacCommands( uint8_t *payload, uint8_t macIndex, uint8_t commandsSize ) +static void ProcessMacCommands( uint8_t *payload, uint8_t macIndex, uint8_t commandsSize, uint8_t snr ) { while( macIndex < commandsSize ) { @@ -1452,12 +1967,13 @@ switch( payload[macIndex++] ) { case SRV_MAC_LINK_CHECK_ANS: - LoRaMacEventFlags.Bits.LinkCheck = 1; - LoRaMacEventInfo.DemodMargin = payload[macIndex++]; - LoRaMacEventInfo.NbGateways = payload[macIndex++]; + MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK; + MlmeConfirm.DemodMargin = payload[macIndex++]; + MlmeConfirm.NbGateways = payload[macIndex++]; break; case SRV_MAC_LINK_ADR_REQ: { + uint8_t i; uint8_t status = 0x07; uint16_t chMask; int8_t txPower = 0; @@ -1465,9 +1981,9 @@ uint8_t nbRep = 0; uint8_t chMaskCntl = 0; uint16_t channelsMask[6] = { 0, 0, 0, 0, 0, 0 }; - + // Initialize local copy of the channels mask array - for( uint8_t i = 0; i < 6; i++ ) + for( i = 0; i < 6; i++ ) { channelsMask[i] = ChannelsMask[i]; } @@ -1475,7 +1991,7 @@ txPower = datarate & 0x0F; datarate = ( datarate >> 4 ) & 0x0F; - if( ( AdrCtrlOn == false ) && + 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 @@ -1501,14 +2017,15 @@ { status &= 0xFE; // Channel mask KO } - else if( ( chMaskCntl >= 1 ) && ( chMaskCntl <= 5 ) ) + else if( ( ( chMaskCntl >= 1 ) && ( chMaskCntl <= 5 )) || + ( chMaskCntl >= 7 ) ) { // RFU status &= 0xFE; // Channel mask KO } else { - for( uint8_t i = 0; i < LORA_MAX_NB_CHANNELS; i++ ) + for( i = 0; i < LORA_MAX_NB_CHANNELS; i++ ) { if( chMaskCntl == 6 ) { @@ -1567,7 +2084,7 @@ } } channelsMask[chMaskCntl] = chMask; - + if( CountNbEnabled125kHzChannels( channelsMask ) < 6 ) { status &= 0xFE; // Channel mask KO @@ -1576,8 +2093,7 @@ #else #error "Please define a frequency band in the compiler options." #endif - if( ( ( datarate < LORAMAC_MIN_DATARATE ) || - ( datarate > LORAMAC_MAX_DATARATE ) ) == true ) + if( ValueInRange( datarate, LORAMAC_MIN_DATARATE, LORAMAC_MAX_DATARATE ) == false ) { status &= 0xFD; // Datarate KO } @@ -1585,8 +2101,7 @@ // // Remark MaxTxPower = 0 and MinTxPower = 5 // - if( ( ( LORAMAC_MAX_TX_POWER <= txPower ) && - ( txPower <= LORAMAC_MIN_TX_POWER ) ) == false ) + if( ValueInRange( txPower, LORAMAC_MAX_TX_POWER, LORAMAC_MIN_TX_POWER ) == false ) { status &= 0xFB; // TxPower KO } @@ -1625,32 +2140,37 @@ 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++]; freq |= ( uint32_t )payload[macIndex++] << 8; freq |= ( uint32_t )payload[macIndex++] << 16; freq *= 100; - + if( Radio.CheckRfFrequency( freq ) == false ) { status &= 0xFE; // Channel frequency KO } - - if( ( ( datarate < LORAMAC_MIN_DATARATE ) || - ( datarate > LORAMAC_MAX_DATARATE ) ) == true ) + + if( ValueInRange( datarate, LORAMAC_MIN_DATARATE, LORAMAC_MAX_DATARATE ) == false ) { status &= 0xFD; // Datarate KO } - - if( ( ( drOffset < LORAMAC_MIN_RX1_DR_OFFSET ) || - ( drOffset > LORAMAC_MAX_RX1_DR_OFFSET ) ) == true ) +#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; @@ -1667,51 +2187,48 @@ { batteryLevel = LoRaMacCallbacks->GetBatteryLevel( ); } - AddMacCommand( MOTE_MAC_DEV_STATUS_ANS, batteryLevel, LoRaMacEventInfo.RxSnr ); + AddMacCommand( MOTE_MAC_DEV_STATUS_ANS, batteryLevel, snr ); + break; } - break; case SRV_MAC_NEW_CHANNEL_REQ: { uint8_t status = 0x03; +#if ( defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) ) + status &= 0xFE; // Channel frequency KO +#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++]; - - if( ( channelIndex < 3 ) || ( channelIndex > LORA_MAX_NB_CHANNELS ) ) + + if( ValueInRange( channelIndex, 3, LORA_MAX_NB_CHANNELS - 1 ) == false ) + { status &= 0xFE; // Channel frequency KO } - + if( Radio.CheckRfFrequency( chParam.Frequency ) == false ) { status &= 0xFE; // Channel frequency KO } - if( ( chParam.DrRange.Fields.Min > chParam.DrRange.Fields.Max ) || - ( ( ( LORAMAC_MIN_DATARATE <= chParam.DrRange.Fields.Min ) && - ( chParam.DrRange.Fields.Min <= LORAMAC_MAX_DATARATE ) ) == false ) || - ( ( ( LORAMAC_MIN_DATARATE <= chParam.DrRange.Fields.Max ) && - ( chParam.DrRange.Fields.Max <= LORAMAC_MAX_DATARATE ) ) == false ) ) + if( LoRaMacChannelAdd( channelIndex, chParam ) != LORAMAC_STATUS_OK ) { status &= 0xFD; // Datarate range KO } - if( ( status & 0x03 ) == 0x03 ) - { - LoRaMacSetChannel( channelIndex, chParam ); - } +#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++; @@ -1728,891 +2245,836 @@ } } -/*! - * Function to be executed on Tx Done event - */ -static void OnRadioTxDone( void ) +LoRaMacStatus_t Send( LoRaMacHeader_t *macHdr, uint8_t fPort, void *fBuffer, uint16_t fBufferSize ) { - TimerTime_t curTime = TimerGetCurrentTime( ); - if( LoRaMacDeviceClass != CLASS_C ) - { - Radio.Sleep( ); - } - else - { - OnRxWindow2TimerEvent( ); - } - - // Update Band Time OFF - Bands[Channels[Channel].Band].LastTxDoneTime = curTime; - if( DutyCycleOn == true ) - { - Bands[Channels[Channel].Band].TimeOff = TxTimeOnAir * Bands[Channels[Channel].Band].DCycle - TxTimeOnAir; - } - else - { - Bands[Channels[Channel].Band].TimeOff = 0; - } - // Update Agregated Time OFF - AggregatedLastTxDoneTime = curTime; - AggregatedTimeOff = AggregatedTimeOff + ( TxTimeOnAir * AggregatedDCycle - TxTimeOnAir ); - - if( IsRxWindowsEnabled == true ) + 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 ) { - 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 ); - } + return status; } - else - { - LoRaMacEventFlags.Bits.Tx = 1; - LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_OK; - } - - if( NodeAckRequested == false ) - { - LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_OK; - ChannelsNbRepCounter++; - } + + // Reset confirm parameters + McpsConfirm.NbRetries = 0; + McpsConfirm.AckReceived = false; + McpsConfirm.UpLinkCounter = UpLinkCounter; + + status = ScheduleTx( ); + + return status; } -/*! - * Function to be executed on Rx Done event - */ -static void OnRadioRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ) +static LoRaMacStatus_t ScheduleTx( ) { - LoRaMacHeader_t macHdr; - LoRaMacFrameCtrl_t fCtrl; - - 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; - - bool isMicOk = false; - - if( LoRaMacDeviceClass != CLASS_C ) + TimerTime_t dutyCycleTimeOff = 0; + + // Check if the device is off + if( MaxDCycle == 255 ) + { + return LORAMAC_STATUS_DEVICE_OFF; + } + if( MaxDCycle == 0 ) { - Radio.Sleep( ); + AggregatedTimeOff = 0; + } + + // Select channel + dutyCycleTimeOff = SetNextChannel( ); + + // Schedule transmission of frame + if( dutyCycleTimeOff == 0 ) + { + // Try to send now + return SendFrameOnChannel( Channels[Channel] ); } else { - if( LoRaMacEventFlags.Bits.RxSlot == 0 ) - { - OnRxWindow2TimerEvent( ); - } + // Send later - prepare timer + LoRaMacState |= MAC_TX_DELAYED; + TimerSetValue( &TxDelayedTimer, dutyCycleTimeOff ); + TimerStart( &TxDelayedTimer ); + + return LORAMAC_STATUS_OK; } - TimerStop( &RxWindowTimer2 ); - - macHdr.Value = payload[pktHeaderLen++]; - - switch( macHdr.Bits.MType ) +} + +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 ) { - 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 ) + 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 ) { - LoRaMacEventFlags.Bits.Rx = 1; - LoRaMacEventInfo.RxSnr = snr; - LoRaMacEventInfo.RxRssi = rssi; - - 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; -#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) - /* - * WARNING: To be removed once Semtech server implementation - * is corrected. - */ - if( Rx2Channel.Datarate == DR_3 ) + return LORAMAC_STATUS_NO_NETWORK_JOINED; // No network has been joined yet + } + + 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; + } + + fCtrl->Bits.AdrAckReq = AdrNextDr( fCtrl->Bits.Adr, true, &ChannelsDatarate ); + + 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; + + if( ( payload != NULL ) && ( payloadSize > 0 ) ) + { + if( ( MacCommandsBufferIndex <= LORA_MAC_COMMAND_MAX_LENGTH ) && ( MacCommandsInNextTx == true ) ) { - Rx2Channel.Datarate = DR_8; - } -#endif - // 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; - - for( uint8_t i = 3, j = 0; i < ( 5 + 3 ); i++, j += 3 ) + fCtrl->Bits.FOptsLen += MacCommandsBufferIndex; + + // Update FCtrl field with new value of OptionsLength + LoRaMacBuffer[0x05] = fCtrl->Value; + for( i = 0; i < MacCommandsBufferIndex; i++ ) { - param.Frequency = ( ( uint32_t )LoRaMacRxPayload[13 + j] | ( ( uint32_t )LoRaMacRxPayload[14 + j] << 8 ) | ( ( uint32_t )LoRaMacRxPayload[15 + j] << 16 ) ) * 100; - LoRaMacSetChannel( i, param ); + LoRaMacBuffer[pktHeaderLen++] = MacCommandsBuffer[i]; } } -#endif - LoRaMacEventFlags.Bits.JoinAccept = 1; - IsLoRaMacNetworkJoined = true; - ChannelsDatarate = ChannelsDefaultDatarate; - LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_OK; } else { - LoRaMacEventInfo.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 ) + if( ( MacCommandsBufferIndex > 0 ) && ( MacCommandsInNextTx ) ) { - curMulticastParams = MulticastChannels; - while( curMulticastParams != NULL ) - { - if( address == curMulticastParams->Address ) - { - LoRaMacEventFlags.Bits.Multicast = 1; - nwkSKey = curMulticastParams->NwkSKey; - appSKey = curMulticastParams->AppSKey; - downLinkCounter = curMulticastParams->DownLinkCounter; - break; - } - curMulticastParams = curMulticastParams->Next; - } - if( LoRaMacEventFlags.Bits.Multicast == 0 ) - { - // We are not the destination of this frame. - LoRaMacEventFlags.Bits.Tx = 1; - LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL; - LoRaMacState &= ~MAC_TX_RUNNING; - return; - } + payloadSize = MacCommandsBufferIndex; + payload = MacCommandsBuffer; + framePort = 0; } - else - { - LoRaMacEventFlags.Bits.Multicast = 0; - nwkSKey = LoRaMacNwkSKey; - appSKey = LoRaMacAppSKey; - downLinkCounter = DownLinkCounter; - } - - if( LoRaMacDeviceClass != CLASS_A ) + } + MacCommandsInNextTx = false; + MacCommandsBufferIndex = 0; + + if( ( payload != NULL ) && ( payloadSize > 0 ) ) + { + LoRaMacBuffer[pktHeaderLen++] = framePort; + + if( framePort == 0 ) { - LoRaMacState |= MAC_RX; - // Starts the MAC layer status check timer - TimerStart( &MacStateCheckTimer ); - } - 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; - } + LoRaMacPayloadEncrypt( (uint8_t* ) payload, payloadSize, LoRaMacNwkSKey, LoRaMacDevAddr, UP_LINK, UpLinkCounter, LoRaMacPayload ); } else { - // check for downlink counter 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; - } + LoRaMacPayloadEncrypt( (uint8_t* ) payload, payloadSize, LoRaMacAppSKey, LoRaMacDevAddr, UP_LINK, UpLinkCounter, LoRaMacPayload ); } - - if( isMicOk == true ) - { - LoRaMacEventFlags.Bits.Rx = 1; - LoRaMacEventInfo.RxSnr = snr; - LoRaMacEventInfo.RxRssi = rssi; - LoRaMacEventInfo.RxBufferSize = 0; - AdrAckCounter = 0; - - // Update 32 bits downlink counter - if( LoRaMacEventFlags.Bits.Multicast == 1 ) - { - curMulticastParams->DownLinkCounter = downLinkCounter; - } - else - { - DownLinkCounter = downLinkCounter; - } - - if( macHdr.Bits.MType == FRAME_TYPE_DATA_CONFIRMED_DOWN ) - { - SrvAckRequested = true; - } - else - { - SrvAckRequested = false; - } - // Check if the frame is an acknowledgement - if( fCtrl.Bits.Ack == 1 ) - { - LoRaMacEventInfo.TxAckReceived = true; - - // Stop the AckTimeout timer as no more retransmissions - // are needed. - TimerStop( &AckTimeoutTimer ); - } - else - { - LoRaMacEventInfo.TxAckReceived = false; - if( AckTimeoutRetriesCounter > AckTimeoutRetries ) - { - // Stop the AckTimeout timer as no more retransmissions - // are needed. - TimerStop( &AckTimeoutTimer ); - } - } - - if( fCtrl.Bits.FOptsLen > 0 ) - { - // Decode Options field MAC commands - LoRaMacProcessMacCommands( payload, 8, appPayloadStartIndex ); - } - - if( ( ( size - 4 ) - appPayloadStartIndex ) > 0 ) - { - port = payload[appPayloadStartIndex++]; - frameLen = ( size - 4 ) - appPayloadStartIndex; - - if( port == 0 ) - { - LoRaMacPayloadDecrypt( payload + appPayloadStartIndex, - frameLen, - nwkSKey, - address, - DOWN_LINK, - downLinkCounter, - LoRaMacRxPayload ); - - // Decode frame payload MAC commands - LoRaMacProcessMacCommands( LoRaMacRxPayload, 0, frameLen ); - } - else - { - LoRaMacPayloadDecrypt( payload + appPayloadStartIndex, - frameLen, - appSKey, - address, - DOWN_LINK, - downLinkCounter, - LoRaMacRxPayload ); - - LoRaMacEventFlags.Bits.RxData = 1; - LoRaMacEventInfo.RxPort = port; - LoRaMacEventInfo.RxBuffer = LoRaMacRxPayload; - LoRaMacEventInfo.RxBufferSize = frameLen; - } - } - - LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_OK; - } - else - { - LoRaMacEventInfo.TxAckReceived = false; - - LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_MIC_FAIL; - LoRaMacState &= ~MAC_TX_RUNNING; - if( NodeAckRequested == true ) - { - OnAckTimeoutTimerEvent( ); - } - } + 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; - case FRAME_TYPE_PROPRIETARY: - //Intentional falltrough default: - LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; - LoRaMacState &= ~MAC_TX_RUNNING; - break; + return LORAMAC_STATUS_SERVICE_UNKNOWN; } - LoRaMacEventFlags.Bits.Tx = 1; + + return LORAMAC_STATUS_OK; } -/*! - * Function executed on Radio Tx Timeout event - */ -static void OnRadioTxTimeout( void ) +LoRaMacStatus_t SendFrameOnChannel( ChannelParams_t channel ) { - if( LoRaMacDeviceClass != CLASS_C ) - { - Radio.Sleep( ); - } - else - { - OnRxWindow2TimerEvent( ); + int8_t datarate = Datarates[ChannelsDatarate]; + int8_t txPower = 0; + + ChannelsTxPower = LimitTxPower( ChannelsTxPower ); + txPower = TxPowers[ChannelsTxPower]; + + MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + McpsConfirm.Datarate = ChannelsDatarate; + McpsConfirm.TxPower = ChannelsTxPower; + + 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 ); + } - - LoRaMacEventFlags.Bits.Tx = 1; - LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT; -} - -/*! - * Function executed on Radio Rx Timeout event - */ -static void OnRadioRxTimeout( void ) -{ - if( LoRaMacDeviceClass != CLASS_C ) - { - Radio.Sleep( ); + 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 - { - OnRxWindow2TimerEvent( ); - } - if( LoRaMacEventFlags.Bits.RxSlot == 1 ) - { - LoRaMacEventFlags.Bits.Tx = 1; - LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT; + { // 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 ); } -} - -/*! - * Function executed on Radio Rx Error event - */ -static void OnRadioRxError( void ) -{ - if( LoRaMacDeviceClass != CLASS_C ) - { - Radio.Sleep( ); +#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 - { - OnRxWindow2TimerEvent( ); - } - if( LoRaMacEventFlags.Bits.RxSlot == 1 ) - { - LoRaMacEventFlags.Bits.Tx = 1; - LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_RX2_ERROR; - } -} - -/*! - * 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 - */ -void LoRaMacRxWindowSetup( 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 ); -#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 ) - if( datarate == DR_7 ) - { - modem = MODEM_FSK; - Radio.SetRxConfig( MODEM_FSK, 50e3, downlinkDatarate * 1e3, 0, 83.333e3, 5, 0, false, 0, true, 0, 0, false, rxContinuous ); - } - else - { - modem = MODEM_LORA; - Radio.SetRxConfig( MODEM_LORA, 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_LORA, bandwidth, downlinkDatarate, 1, 0, 8, timeout, false, 0, false, 0, 0, true, rxContinuous ); -#endif - if( RepeaterSupport == true ) - { - Radio.SetMaxPayloadLength( modem, MaxPayloadOfDatarateRepeater[datarate] ); - } - else - { - Radio.SetMaxPayloadLength( modem, MaxPayloadOfDatarate[datarate] ); - } - - if( rxContinuous == false ) - { - Radio.Rx( MaxRxWindow ); - } - else - { - Radio.Rx( 0 ); // Continuous mode - } - } -} - -/*! - * Function executed on first Rx window timer event - */ -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 ); - LoRaMacEventFlags.Bits.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 ) - { // DR_6, DR_5, DR_4, DR_3 - symbTimeout = 8; - } - if( datarate == DR_6 ) - {// LoRa 250 kHz - bandwidth = 1; - } - LoRaMacRxWindowSetup( 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 - if( datarate > DR_0 ) - { // DR_1, DR_2, DR_3, DR_4, DR_8, DR_9, DR_10, DR_11, DR_12, DR_13 - symbTimeout = 8; - } - if( datarate >= DR_4 ) - {// LoRa 500 kHz - bandwidth = 2; - } - LoRaMacRxWindowSetup( 923.3e6 + ( Channel % 8 ) * 600e3, datarate, bandwidth, symbTimeout, false ); -#else - #error "Please define a frequency band in the compiler options." -#endif -} - -/*! - * Function executed on second Rx window timer event - */ -static void OnRxWindow2TimerEvent( void ) -{ - uint16_t symbTimeout = 5; // DR_2, DR_1, DR_0 - uint32_t bandwidth = 0; // LoRa 125 kHz - - TimerStop( &RxWindowTimer2 ); - LoRaMacEventFlags.Bits.RxSlot = 1; - - if( NodeAckRequested == true ) - { - TimerSetValue( &AckTimeoutTimer, ACK_TIMEOUT + randr( -ACK_TIMEOUT_RND, ACK_TIMEOUT_RND ) ); - TimerStart( &AckTimeoutTimer ); - } - -#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 ) - { // DR_6, DR_5, DR_4, DR_3 - symbTimeout = 8; - } - if( Rx2Channel.Datarate == DR_6 ) - {// LoRa 250 kHz - bandwidth = 1; - } -#elif ( defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) ) - // For higher datarates, we increase the number of symbols generating a Rx Timeout - if( Rx2Channel.Datarate > DR_0 ) - { // DR_1, DR_2, DR_3, DR_4, DR_8, DR_9, DR_10, DR_11, DR_12, DR_13 - symbTimeout = 8; - } - if( Rx2Channel.Datarate >= DR_4 ) - {// LoRa 500 kHz - bandwidth = 2; + { // 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 - if( LoRaMacDeviceClass != CLASS_C ) + + // Store the time on air + McpsConfirm.TxTimeOnAir = TxTimeOnAir; + MlmeConfirm.TxTimeOnAir = TxTimeOnAir; + + // Starts the MAC layer status check timer + 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; + + RepeaterSupport = false; + IsRxWindowsEnabled = true; + IsLoRaMacNetworkJoined = false; + LoRaMacState = MAC_IDLE; + +#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++ ) { - LoRaMacRxWindowSetup( Rx2Channel.Frequency, Rx2Channel.Datarate, bandwidth, symbTimeout, false ); + 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; + + 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; + + 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; + + if( txInfo == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + AdrNextDr( AdrCtrlOn, false, &datarate ); + + if( RepeaterSupport == true ) + { + txInfo->CurrentPayloadSize = MaxPayloadOfDatarateRepeater[datarate]; } else { - LoRaMacRxWindowSetup( Rx2Channel.Frequency, Rx2Channel.Datarate, bandwidth, symbTimeout, true ); + txInfo->CurrentPayloadSize = MaxPayloadOfDatarate[datarate]; } -} - -/*! - * Function executed on MacStateCheck timer event - */ -static void OnMacStateCheckTimerEvent( void ) -{ - TimerStop( &MacStateCheckTimer ); - - if( LoRaMacEventFlags.Bits.Tx == 1 ) + + if( txInfo->CurrentPayloadSize >= MacCommandsBufferIndex ) { - if( NodeAckRequested == false ) - { - if( LoRaMacEventFlags.Bits.JoinAccept == true ) - { - // Join messages aren't repeated automatically - ChannelsNbRepCounter = ChannelsNbRep; - UpLinkCounter = 0; - } - if( ChannelsNbRepCounter >= ChannelsNbRep ) - { - ChannelsNbRepCounter = 0; - - LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_OK; - - AdrAckCounter++; - if( IsUpLinkCounterFixed == false ) - { - UpLinkCounter++; - } - - LoRaMacState &= ~MAC_TX_RUNNING; - } - else - { - LoRaMacEventFlags.Bits.Tx = 0; - // Sends the same frame again - if( LoRaMacSetNextChannel( ) == 0 ) - { - LoRaMacSendFrameOnChannel( Channels[Channel] ); - } - } - } - - if( LoRaMacEventFlags.Bits.Rx == 1 ) - { - if( ( LoRaMacEventInfo.TxAckReceived == true ) || ( AckTimeoutRetriesCounter > AckTimeoutRetries ) ) - { - AckTimeoutRetry = false; - if( IsUpLinkCounterFixed == false ) - { - UpLinkCounter++; - } - LoRaMacEventInfo.TxNbRetries = AckTimeoutRetriesCounter; - - LoRaMacState &= ~MAC_TX_RUNNING; - } - } - - if( ( AckTimeoutRetry == true ) && ( ( LoRaMacState & MAC_CHANNEL_CHECK ) == 0 ) ) - { - AckTimeoutRetry = false; - if( ( AckTimeoutRetriesCounter < AckTimeoutRetries ) && ( AckTimeoutRetriesCounter <= MAX_ACK_RETRIES ) ) - { - AckTimeoutRetriesCounter++; - - if( ( AckTimeoutRetriesCounter % 2 ) == 1 ) - { - ChannelsDatarate = MAX( ChannelsDatarate - 1, LORAMAC_MIN_DATARATE ); - } - LoRaMacEventFlags.Bits.Tx = 0; - // Sends the same frame again - if( LoRaMacSetNextChannel( ) == 0 ) - { - LoRaMacSendFrameOnChannel( Channels[Channel] ); - } - } - 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 ) - // Re-enable default channels - ChannelsMask[0] = 0xFFFF; - ChannelsMask[1] = 0xFFFF; - ChannelsMask[2] = 0xFFFF; - ChannelsMask[3] = 0xFFFF; - ChannelsMask[4] = 0x00FF; - ChannelsMask[5] = 0x0000; -#elif 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; -#else - #error "Please define a frequency band in the compiler options." -#endif - LoRaMacState &= ~MAC_TX_RUNNING; - - LoRaMacEventInfo.TxAckReceived = false; - LoRaMacEventInfo.TxNbRetries = 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 ) - { - LoRaMacNotify( &LoRaMacEventFlags, &LoRaMacEventInfo ); - LoRaMacEventFlags.Bits.Tx = 0; + txInfo->MaxPossiblePayload = txInfo->CurrentPayloadSize - MacCommandsBufferIndex; } else { - // Operation not finished restart timer - TimerStart( &MacStateCheckTimer ); + return LORAMAC_STATUS_MAC_CMD_LENGTH_ERROR; + } + + if( ValidatePayloadLength( size, datarate, 0 ) == false ) + { + return LORAMAC_STATUS_LENGTH_ERROR; } -} - -static void OnAckTimeoutTimerEvent( void ) -{ - TimerStop( &AckTimeoutTimer ); - - AckTimeoutRetry = true; - LoRaMacState &= ~MAC_ACK_REQ; -} - -/*! - * ============================================================================ - * = LoRaMac utility functions = - * ============================================================================ - */ -static bool ValidatePayloadLength( uint8_t lenN, int8_t datarate ) -{ - bool payloadSizeOk = false; - uint8_t maxN = 0; - - // Get the maximum payload length - if( RepeaterSupport == true ) + + if( ValidatePayloadLength( size, datarate, MacCommandsBufferIndex ) == false ) { - maxN = MaxPayloadOfDatarateRepeater[datarate]; - } - else - { - maxN = MaxPayloadOfDatarate[datarate]; + return LORAMAC_STATUS_MAC_CMD_LENGTH_ERROR; } - // Validation of the application payload size - if( lenN <= maxN ) - { - payloadSizeOk = true; - } - - return payloadSizeOk; + return LORAMAC_STATUS_OK; } -#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) -static uint8_t CountNbEnabled125kHzChannels( uint16_t *channelsMask ) +LoRaMacStatus_t LoRaMacMibGetRequestConfirm( MibRequestConfirm_t *mibGet ) { - uint8_t nb125kHzChannels = 0; - - for( uint8_t i = 0, k = 0; i < LORA_MAX_NB_CHANNELS - 8; i += 16, k++ ) + LoRaMacStatus_t status = LORAMAC_STATUS_OK; + + if( mibGet == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + switch( mibGet->Type ) { - for( uint8_t j = 0; j < 16; j++ ) - {// Verify if the channel is active - if( ( channelsMask[k] & ( 1 << j ) ) == ( 1 << j ) ) - { - nb125kHzChannels++; - } + 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_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 nb125kHzChannels; + return status; } -#endif - -static int8_t LimitTxPower( int8_t txPower ) + +LoRaMacStatus_t LoRaMacMibSetRequestConfirm( MibRequestConfirm_t *mibSet ) { - 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 ); + LoRaMacStatus_t status = LORAMAC_STATUS_OK; + + if( mibSet == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; } - else - { - if( CountNbEnabled125kHzChannels( ChannelsMask ) < 50 ) - {// Limit tx power to max 21dBm - resultTxPower = MAX( txPower, TX_POWER_20_DBM ); - } - } -#endif - return resultTxPower; -} - -void LoRaMacChannelRemove( uint8_t id ) -{ if( ( LoRaMacState & MAC_TX_RUNNING ) == MAC_TX_RUNNING ) { - return; + return LORAMAC_STATUS_BUSY; } -#if ( defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) ) - if( id < 64 ) + + switch( mibSet->Type ) { - if( CountNbEnabled125kHzChannels( ChannelsMask ) <= 6 ) + 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; + 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: { - return; + 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 ) + if( ( CountNbEnabled125kHzChannels( mibSet->Param.ChannelsMask ) < 6 ) && + ( 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++ ) + { + ChannelsMaskRemaining[i] &= ChannelsMask[i]; + } + } #else - if( id < 3 ) - { - return; - } + memcpy1( ( uint8_t* ) ChannelsMask, + ( uint8_t* ) mibSet->Param.ChannelsMask, 2 ); #endif - - uint8_t index = 0; - index = id / 16; - - if( ( index > 4 ) || ( id >= LORA_MAX_NB_CHANNELS ) ) - { - return; + } + 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_DATARATE: + { + if( ValueInRange( mibSet->Param.ChannelsDatarate, + LORAMAC_MIN_DATARATE, LORAMAC_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; + } + default: + status = LORAMAC_STATUS_SERVICE_UNKNOWN; + break; } - // Deactivate channel - ChannelsMask[index] &= ~( 1 << ( id % 16 ) ); - - return; + return status; } -/*! - * ============================================================================ - * = LoRaMac setup functions = - * ============================================================================ - */ -void LoRaMacSetDeviceClass( DeviceClass_t deviceClass ) -{ - LoRaMacDeviceClass = deviceClass; -} - -void LoRaMacSetPublicNetwork( bool enable ) +LoRaMacStatus_t LoRaMacChannelAdd( uint8_t id, ChannelParams_t params ) { - PublicNetwork = enable; - Radio.SetModem( MODEM_LORA ); - if( PublicNetwork == true ) - { - // Change LoRa modem SyncWord - Radio.Write( REG_LR_SYNCWORD, LORA_MAC_PUBLIC_SYNCWORD ); - } - else + if( id >= LORA_MAX_NB_CHANNELS ) { - // Change LoRa modem SyncWord - Radio.Write( REG_LR_SYNCWORD, LORA_MAC_PRIVATE_SYNCWORD ); + return LORAMAC_STATUS_PARAMETER_INVALID; } -} - -void LoRaMacSetChannel( uint8_t id, ChannelParams_t params ) -{ - params.Band = 0; - Channels[id] = params; - // Activate the newly created channel - if( id < 16 ) + if( ( LoRaMacState & MAC_TX_RUNNING ) == MAC_TX_RUNNING ) { - ChannelsMask[0] |= 1 << id; + return LORAMAC_STATUS_BUSY; } - else if( id < 32 ) - { - ChannelsMask[1] |= 1 << ( id - 16 ); - } - else if( id < 48 ) + if( ( params.DrRange.Fields.Min > params.DrRange.Fields.Max ) || + ( ValueInRange( params.DrRange.Fields.Min, LORAMAC_MIN_DATARATE, + LORAMAC_MAX_DATARATE ) == false ) || + ( ValueInRange( params.DrRange.Fields.Max, LORAMAC_MIN_DATARATE, + LORAMAC_MAX_DATARATE ) == false ) ) { - ChannelsMask[2] |= 1 << ( id - 32 ); - } - else if( id < 64 ) - { - ChannelsMask[3] |= 1 << ( id - 48 ); + return LORAMAC_STATUS_PARAMETER_INVALID; } - else if( id < 72 ) - { - ChannelsMask[4] |= 1 << ( id - 64 ); - } - else - { - // Don't activate the channel - } + + params.Band = 0; + +#if ( defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) ) + return LORAMAC_STATUS_PARAMETER_INVALID; +#else #if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) - Channels[id].Band = 0; // 1% duty cycle on EU433 and CN780 bands + Channels[id] = params; + Channels[id].Band = 0; // No duty cycle on EU433 and CN470 bands #elif defined( USE_BAND_868 ) + Channels[id] = params; if( ( Channels[id].Frequency >= 865000000 ) && ( Channels[id].Frequency <= 868000000 ) ) { if( Channels[id].Band != BAND_G1_0 ) @@ -2653,126 +3115,304 @@ Channels[id].Frequency = 0; Channels[id].DrRange.Value = 0; } -#elif ( defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) ) - Channels[id].Band = 0; // No duty cycle on US915 band #else #error "Please define a frequency band in the compiler options." #endif // Check if it is a valid channel - if( Channels[id].Frequency == 0 ) + if( Channels[id].Frequency > 0 ) { - LoRaMacChannelRemove( id ); - } -} - -void LoRaMacSetRx2Channel( Rx2ChannelParams_t param ) -{ - Rx2Channel = param; -} - -void LoRaMacSetChannelsTxPower( int8_t txPower ) -{ - if( ( txPower >= LORAMAC_MAX_TX_POWER ) && - ( txPower <= LORAMAC_MIN_TX_POWER ) ) - { -#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) - int8_t txPwr = LimitTxPower( txPower ); - if( txPwr == txPower ) + // Activate the newly created channel + if( id < 16 ) { - ChannelsTxPower = txPower; + ChannelsMask[0] |= 1 << id; } -#else - ChannelsTxPower = txPower; + } + + return LORAMAC_STATUS_OK; #endif - } -} - -void LoRaMacSetChannelsDatarate( int8_t datarate ) -{ - ChannelsDefaultDatarate = ChannelsDatarate = datarate; } -void LoRaMacSetChannelsMask( uint16_t *mask ) +LoRaMacStatus_t LoRaMacChannelRemove( uint8_t id ) { -#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) - if( ( CountNbEnabled125kHzChannels( mask ) < 6 ) && - ( CountNbEnabled125kHzChannels( mask ) > 0 ) ) + if( ( LoRaMacState & MAC_TX_RUNNING ) == MAC_TX_RUNNING ) { - + return LORAMAC_STATUS_BUSY; } - else +#if ( defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) ) + if( id < 64 ) { - LoRaMacMemCpy( (uint8_t* ) mask, - ( uint8_t* ) ChannelsMask, 10 ); + if( CountNbEnabled125kHzChannels( ChannelsMask ) <= 6 ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } } #else - if( ( mask[0] & 0x0007 ) != 0x0007 ) + if( id < 3 ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } +#endif + + if( DisableChannelInMask( id, ChannelsMask ) == false ) { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + return LORAMAC_STATUS_OK; +} + +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 { - LoRaMacMemCpy( ( uint8_t* ) mask, - ( uint8_t* ) ChannelsMask, 2 ); + 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; } -#endif + + return LORAMAC_STATUS_OK; } -void LoRaMacSetChannelsNbRep( uint8_t nbRep ) +LoRaMacStatus_t LoRaMacMulticastChannelUnlink( MulticastParams_t *channelParam ) { - if( nbRep < 1 ) + if( channelParam == NULL ) { - nbRep = 1; + return LORAMAC_STATUS_PARAMETER_INVALID; } - if( nbRep > 15 ) + if( ( LoRaMacState & MAC_TX_RUNNING ) == MAC_TX_RUNNING ) + { + return LORAMAC_STATUS_BUSY; + } + + if( MulticastChannels != NULL ) { - nbRep = 15; + 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; } - ChannelsNbRep = nbRep; + + return LORAMAC_STATUS_OK; } -void LoRaMacSetMaxRxWindow( uint32_t delay ) -{ - MaxRxWindow = delay; -} - -void LoRaMacSetReceiveDelay1( uint32_t delay ) -{ - ReceiveDelay1 = delay; -} - -void LoRaMacSetReceiveDelay2( uint32_t delay ) +LoRaMacStatus_t LoRaMacMlmeRequest( MlmeReq_t *mlmeRequest ) { - ReceiveDelay2 = delay; -} - -void LoRaMacSetJoinAcceptDelay1( uint32_t delay ) -{ - JoinAcceptDelay1 = delay; + 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 ) + { + status = 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; + + macHdr.Value = 0; + macHdr.Bits.MType = FRAME_TYPE_JOIN_REQ; + + IsLoRaMacNetworkJoined = false; + +#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) + 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; } -void LoRaMacSetJoinAcceptDelay2( uint32_t delay ) -{ - JoinAcceptDelay2 = delay; -} - -uint32_t LoRaMacGetUpLinkCounter( void ) +LoRaMacStatus_t LoRaMacMcpsRequest( McpsReq_t *mcpsRequest ) { - return UpLinkCounter; -} - -uint32_t LoRaMacGetDownLinkCounter( void ) -{ - return DownLinkCounter; -} - -/*! - * ============================================================================ - * = LoRaMac test functions = - * ============================================================================ - */ -void LoRaMacTestSetDutyCycleOn( bool enable ) -{ - DutyCycleOn = enable; + 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.nbRetries; + + 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_MIN_DATARATE, LORAMAC_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; } void LoRaMacTestRxWindowsOn( bool enable ) @@ -2780,8 +3420,13 @@ IsRxWindowsEnabled = enable; } -void LoRaMacTestSetMic( uint16_t upLinkCounter ) +void LoRaMacTestSetMic( uint16_t txPacketCounter ) { - UpLinkCounter = upLinkCounter; + UpLinkCounter = txPacketCounter; IsUpLinkCounterFixed = true; } + +void LoRaMacTestSetDutyCycleOn( bool enable ) +{ + DutyCycleOn = enable; +}
--- a/LoRaMac.h Mon Nov 23 10:09:43 2015 +0000 +++ b/LoRaMac.h Tue Jan 05 16:41:54 2016 +0000 @@ -1,17 +1,48 @@ -/* - / _____) _ | | -( (____ _____ ____ _| |_ _____ ____| |__ - \____ \| ___ | (_ _) ___ |/ ___) _ \ - _____) ) ____| | | || |_| ____( (___| | | | -(______/|_____)_|_|_| \__)_____)\____)_| |_| - (C)2013 Semtech - -Description: LoRa MAC layer implementation - -License: Revised BSD License, see LICENSE.TXT file include in the project - -Maintainer: Miguel Luis and Gregory Cristian -*/ +/*! + * \file LoRaMac.h + * + * \brief LoRa MAC layer implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jäckle ( STACKFORCE ) + * + * \defgroup LORAMAC LoRa MAC layer implementation + * This module specifies the API implementation of the LoRaMAC layer. + * This is a placeholder for a detailed description of the LoRaMac + * layer and the supported features. + * \{ + * + * \example classA/LoRaMote/main.c + * LoRaWAN class A application example for the LoRaMote. + * + * \example classB/LoRaMote/main.c + * LoRaWAN class B application example for the LoRaMote. + * + * \example classC/LoRaMote/main.c + * LoRaWAN class C application example for the LoRaMote. + */ #ifndef __LORAMAC_H__ #define __LORAMAC_H__ @@ -24,15 +55,23 @@ #define BEACON_INTERVAL 128000000 /*! - * Class A&B receive delay in us + * Class A&B receive delay 1 in us */ #define RECEIVE_DELAY1 1000000 + +/*! + * Class A&B receive delay 2 in us + */ #define RECEIVE_DELAY2 2000000 /*! - * Join accept receive delay in us + * Join accept receive delay 1 in us */ #define JOIN_ACCEPT_DELAY1 5000000 + +/*! + * Join accept receive delay 2 in us + */ #define JOIN_ACCEPT_DELAY2 6000000 /*! @@ -58,19 +97,19 @@ /*! * Number of seconds after the start of the second reception window without * receiving an acknowledge. - * AckTimeout = ACK_TIMEOUT + Random( -ACK_TIMEOUT_RND, ACK_TIMEOUT_RND ) + * AckTimeout = \ref ACK_TIMEOUT + Random( -\ref ACK_TIMEOUT_RND, \ref ACK_TIMEOUT_RND ) */ #define ACK_TIMEOUT 2000000 /*! * Random number of seconds after the start of the second reception window without * receiving an acknowledge - * AckTimeout = ACK_TIMEOUT + Random( -ACK_TIMEOUT_RND, ACK_TIMEOUT_RND ) + * AckTimeout = \ref ACK_TIMEOUT + Random( -\ref ACK_TIMEOUT_RND, \ref ACK_TIMEOUT_RND ) */ #define ACK_TIMEOUT_RND 1000000 /*! - * Check the Mac layer state every MAC_STATE_CHECK_TIMEOUT + * Check the Mac layer state every MAC_STATE_CHECK_TIMEOUT in us */ #define MAC_STATE_CHECK_TIMEOUT 1000000 @@ -80,14 +119,18 @@ #define MAX_ACK_RETRIES 8 /*! - * RSSI free threshold + * RSSI free threshold [dBm] */ -#define RSSI_FREE_TH ( int8_t )( -90 ) // [dBm] +#define RSSI_FREE_TH ( int8_t )( -90 ) -/*! - * Frame direction definition +/*! + * Frame direction definition for up-link communications */ #define UP_LINK 0 + +/*! + * Frame direction definition for down-link communications + */ #define DOWN_LINK 1 /*! @@ -106,480 +149,1503 @@ */ #define LORA_MAC_PUBLIC_SYNCWORD 0x34 + /*! + * LoRaMac internal state + */ +//uint32_t LoRaMacState; + /*! * LoRaWAN devices classes definition */ -typedef enum +typedef enum eDeviceClass { + /*! + * LoRaWAN device class A + * + * LoRaWAN Specification V1.0, chapter 3ff + */ CLASS_A, + /*! + * LoRaWAN device class B + * + * LoRaWAN Specification V1.0, chapter 8ff + */ CLASS_B, + /*! + * LoRaWAN device class C + * + * LoRaWAN Specification V1.0, chapter 17ff + */ CLASS_C, }DeviceClass_t; /*! * LoRaMAC channels parameters definition */ -typedef union +typedef union uDrRange { + /*! + * Byte-access to the bits + */ int8_t Value; - struct + /*! + * Structure to store the minimum and the maximum datarate + */ + struct sFields { + /*! + * Minimum data rate + * + * EU868 - [DR_0, DR_1, DR_2, DR_3, DR_4, DR_5, DR_6, DR_7] + * + * US915 - [DR_0, DR_1, DR_2, DR_3, DR_4] + */ int8_t Min : 4; + /*! + * Maximum data rate + * + * EU868 - [DR_0, DR_1, DR_2, DR_3, DR_4, DR_5, DR_6, DR_7] + * + * US915 - [DR_0, DR_1, DR_2, DR_3, DR_4] + */ int8_t Max : 4; }Fields; }DrRange_t; -typedef struct +/*! + * LoRaMAC band parameters definition + */ +typedef struct sBand { + /*! + * Duty cycle + */ uint16_t DCycle; + /*! + * Maximum Tx power + */ int8_t TxMaxPower; - uint64_t LastTxDoneTime; - uint64_t TimeOff; + /*! + * Time stamp of the last Tx frame + */ + TimerTime_t LastTxDoneTime; + /*! + * Holds the time where the device is off + */ + TimerTime_t TimeOff; }Band_t; -typedef struct +/*! + * LoRaMAC channel definition + */ +typedef struct sChannelParams { - uint32_t Frequency; // Hz - DrRange_t DrRange; // Max datarate [0: SF12, 1: SF11, 2: SF10, 3: SF9, 4: SF8, 5: SF7, 6: SF7, 7: FSK] - // Min datarate [0: SF12, 1: SF11, 2: SF10, 3: SF9, 4: SF8, 5: SF7, 6: SF7, 7: FSK] - uint8_t Band; // Band index + /*! + * Frequency in Hz + */ + uint32_t Frequency; + /*! + * Data rate definition + */ + DrRange_t DrRange; + /*! + * Band index + */ + uint8_t Band; }ChannelParams_t; -typedef struct +/*! + * LoRaMAC receive window 2 channel parameters + */ +typedef struct sRx2ChannelParams { - uint32_t Frequency; // Hz - uint8_t Datarate; // [0: SF12, 1: SF11, 2: SF10, 3: SF9, 4: SF8, 5: SF7, 6: SF7, 7: FSK] + /*! + * Frequency in Hz + */ + uint32_t Frequency; + /*! + * Data rate + * + * EU868 - [DR_0, DR_1, DR_2, DR_3, DR_4, DR_5, DR_6, DR_7] + * + * US915 - [DR_8, DR_9, DR_10, DR_11, DR_12, DR_13] + */ + uint8_t Datarate; }Rx2ChannelParams_t; -typedef struct MulticastParams_s +/*! + * LoRaMAC multicast channel parameter + */ +typedef struct sMulticastParams { + /*! + * Address + */ uint32_t Address; + /*! + * Network session key + */ uint8_t NwkSKey[16]; + /*! + * Application session key + */ uint8_t AppSKey[16]; + /*! + * Downlink counter + */ uint32_t DownLinkCounter; - struct MulticastParams_s *Next; + /*! + * Reference pointer to the next multicast channel parameters in the list + */ + struct sMulticastParams *Next; }MulticastParams_t; /*! * LoRaMAC frame types + * + * LoRaWAN Specification V1.0, chapter 4.2.1, table 1 */ -typedef enum +typedef enum eLoRaMacFrameType { + /*! + * LoRaMAC join request frame + */ FRAME_TYPE_JOIN_REQ = 0x00, + /*! + * LoRaMAC join accept frame + */ FRAME_TYPE_JOIN_ACCEPT = 0x01, + /*! + * LoRaMAC unconfirmed up-link frame + */ FRAME_TYPE_DATA_UNCONFIRMED_UP = 0x02, + /*! + * LoRaMAC unconfirmed down-link frame + */ FRAME_TYPE_DATA_UNCONFIRMED_DOWN = 0x03, + /*! + * LoRaMAC confirmed up-link frame + */ FRAME_TYPE_DATA_CONFIRMED_UP = 0x04, + /*! + * LoRaMAC confirmed down-link frame + */ FRAME_TYPE_DATA_CONFIRMED_DOWN = 0x05, + /*! + * LoRaMAC RFU frame + */ FRAME_TYPE_RFU = 0x06, + /*! + * LoRaMAC proprietary frame + */ FRAME_TYPE_PROPRIETARY = 0x07, }LoRaMacFrameType_t; /*! * LoRaMAC mote MAC commands + * + * LoRaWAN Specification V1.0, chapter 5, table 4 */ -typedef enum +typedef enum eLoRaMacMoteCmd { + /*! + * LinkCheckReq + */ MOTE_MAC_LINK_CHECK_REQ = 0x02, + /*! + * LinkADRAns + */ MOTE_MAC_LINK_ADR_ANS = 0x03, + /*! + * DutyCycleAns + */ MOTE_MAC_DUTY_CYCLE_ANS = 0x04, + /*! + * RXParamSetupAns + */ MOTE_MAC_RX_PARAM_SETUP_ANS = 0x05, + /*! + * DevStatusAns + */ MOTE_MAC_DEV_STATUS_ANS = 0x06, + /*! + * NewChannelAns + */ MOTE_MAC_NEW_CHANNEL_ANS = 0x07, + /*! + * RXTimingSetupAns + */ MOTE_MAC_RX_TIMING_SETUP_ANS = 0x08, }LoRaMacMoteCmd_t; /*! * LoRaMAC server MAC commands + * + * LoRaWAN Specification V1.0, chapter 5, table 4 */ -typedef enum +typedef enum eLoRaMacSrvCmd { + /*! + * LinkCheckAns + */ SRV_MAC_LINK_CHECK_ANS = 0x02, + /*! + * LinkADRReq + */ SRV_MAC_LINK_ADR_REQ = 0x03, + /*! + * DutyCycleReq + */ SRV_MAC_DUTY_CYCLE_REQ = 0x04, + /*! + * RXParamSetupReq + */ SRV_MAC_RX_PARAM_SETUP_REQ = 0x05, + /*! + * DevStatusReq + */ SRV_MAC_DEV_STATUS_REQ = 0x06, + /*! + * NewChannelReq + */ SRV_MAC_NEW_CHANNEL_REQ = 0x07, + /*! + * RXTimingSetupReq + */ SRV_MAC_RX_TIMING_SETUP_REQ = 0x08, }LoRaMacSrvCmd_t; /*! * LoRaMAC Battery level indicator */ -typedef enum +typedef enum eLoRaMacBatteryLevel { + /*! + * External power source + */ BAT_LEVEL_EXT_SRC = 0x00, + /*! + * Battery level empty + */ BAT_LEVEL_EMPTY = 0x01, + /*! + * Battery level full + */ BAT_LEVEL_FULL = 0xFE, + /*! + * Battery level - no measurement available + */ BAT_LEVEL_NO_MEASURE = 0xFF, }LoRaMacBatteryLevel_t; /*! - * LoRaMAC header field definition + * LoRaMAC header field definition (MHDR field) + * + * LoRaWAN Specification V1.0, chapter 4.2 */ -typedef union +typedef union uLoRaMacHeader { + /*! + * Byte-access to the bits + */ uint8_t Value; - struct + /*! + * Structure containing single access to header bits + */ + struct sHdrBits { + /*! + * Major version + */ uint8_t Major : 2; + /*! + * RFU + */ uint8_t RFU : 3; + /*! + * Message type + */ uint8_t MType : 3; }Bits; }LoRaMacHeader_t; /*! - * LoRaMAC frame header field definition + * LoRaMAC frame control field definition (FCtrl) + * + * LoRaWAN Specification V1.0, chapter 4.3.1 */ -typedef union +typedef union uLoRaMacFrameCtrl { + /*! + * Byte-access to the bits + */ uint8_t Value; - struct + struct sCtrlBits { + /*! + * Frame options length + */ uint8_t FOptsLen : 4; + /*! + * Frame pending bit + */ uint8_t FPending : 1; + /*! + * Message acknowledge bit + */ uint8_t Ack : 1; + /*! + * ADR acknowledgment request bit + */ uint8_t AdrAckReq : 1; + /*! + * ADR control in frame header + */ uint8_t Adr : 1; }Bits; }LoRaMacFrameCtrl_t; /*! - * LoRaMAC event flags + * Enumeration containing the status of the operation of a MAC service */ -typedef union +typedef enum eLoRaMacEventInfoStatus { - uint8_t Value; - struct - { - uint8_t Tx : 1; - uint8_t Rx : 1; - uint8_t RxData : 1; - uint8_t Multicast : 1; - uint8_t RxSlot : 2; - uint8_t LinkCheck : 1; - uint8_t JoinAccept : 1; - }Bits; -}LoRaMacEventFlags_t; - -typedef enum -{ + /*! + * Service performed successfully + */ LORAMAC_EVENT_INFO_STATUS_OK = 0, + /*! + * An error occured during the execution of the service + */ LORAMAC_EVENT_INFO_STATUS_ERROR, + /*! + * A Tx timeouit occured + */ LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT, + /*! + * An Rx timeout occured on receive window 2 + */ LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT, + /*! + * An Rx error occured on receive window 2 + */ LORAMAC_EVENT_INFO_STATUS_RX2_ERROR, + /*! + * An error occured in the join procedure + */ LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL, - LORAMAC_EVENT_INFO_STATUS_DOWNLINK_FAIL, + /*! + * A frame with an invalid downlink counter was received. The + * downlink counter of the frame was equal to the local copy + * of the downlink counter of the node. + */ + LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED, + /*! + * An address error occured + */ LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL, + /*! + * message integrity check failure + */ LORAMAC_EVENT_INFO_STATUS_MIC_FAIL, }LoRaMacEventInfoStatus_t; /*! - * LoRaMAC event information + * LoRaMac tx/rx operation state + */ +typedef union eLoRaMacFlags_t +{ + /*! + * Byte-access to the bits + */ + uint8_t Value; + struct sMacFlagBits + { + /*! + * MCPS-Req pending + */ + uint8_t McpsReq : 1; + /*! + * MCPS-Ind pending + */ + uint8_t McpsInd : 1; + /*! + * MLME-Req pending + */ + uint8_t MlmeReq : 1; + /*! + * MAC cycle done + */ + uint8_t MacDone : 1; + }Bits; +}LoRaMacFlags_t; + +/*! + * + * \brief LoRaMAC data services + * + * \details The following table list the primitives which are supported by the + * specific MAC data service: + * + * Name | Request | Indication | Response | Confirm + * --------------------- | :-----: | :--------: | :------: | :-----: + * \ref MCPS_UNCONFIRMED | YES | YES | NO | YES + * \ref MCPS_CONFIRMED | YES | YES | NO | YES + * \ref MCPS_MULTICAST | NO | YES | NO | NO + * \ref MCPS_PROPRIETARY | YES | YES | NO | YES + * + * The following table provides links to the function implementations of the + * related MCPS primitives: + * + * Primitive | Function + * ---------------- | :---------------------: + * MCPS-Request | \ref LoRaMacMlmeRequest + * MCPS-Confirm | MacMcpsConfirm in \ref LoRaMacPrimitives_t + * MCPS-Indication | MacMcpsIndication in \ref LoRaMacPrimitives_t + */ +typedef enum eMcps +{ + /*! + * Unconfirmed LoRaMAC frame + */ + MCPS_UNCONFIRMED, + /*! + * Confirmed LoRaMAC frame + */ + MCPS_CONFIRMED, + /*! + * Multicast LoRaMAC frame + */ + MCPS_MULTICAST, + /*! + * Proprietary frame + */ + MCPS_PROPRIETARY, +}Mcps_t; + +/*! + * LoRaMAC MCPS-Request for an unconfirmed frame + */ +typedef struct sMcpsReqUnconfirmed +{ + /*! + * Frame port field. Must be set if the payload is not empty. Use the + * application specific frame port values: [1...223] + * + * LoRaWAN Specification V1.0, chapter 4.3.2 + */ + uint8_t fPort; + /*! + * Pointer to the buffer of the frame payload + */ + void *fBuffer; + /*! + * Size of the frame payload + */ + uint16_t fBufferSize; + /*! + * Uplink datarate, if ADR is off + */ + int8_t Datarate; +}McpsReqUnconfirmed_t; + +/*! + * LoRaMAC MCPS-Request for a confirmed frame + */ +typedef struct sMcpsReqConfirmed +{ + /*! + * Frame port field. Must be set if the payload is not empty. Use the + * application specific frame port values: [1...223] + * + * LoRaWAN Specification V1.0, chapter 4.3.2 + */ + uint8_t fPort; + /*! + * Pointer to the buffer of the frame payload + */ + void *fBuffer; + /*! + * Size of the frame payload + */ + uint16_t fBufferSize; + /*! + * Uplink datarate, if ADR is off + */ + int8_t Datarate; + /*! + * Number of attempts to transmit the frame, if frame could not be send, or + * if the LoRaMAC layer did not receive an acknowledgment + */ + uint8_t nbRetries; +}McpsReqConfirmed_t; + +/*! + * LoRaMAC MCPS-Request for a proprietary frame + */ +typedef struct sMcpsReqProprietary +{ + /*! + * Pointer to the buffer of the frame payload + */ + void *fBuffer; + /*! + * Size of the frame payload + */ + uint16_t fBufferSize; + /*! + * Uplink datarate, if ADR is off + */ + int8_t Datarate; +}McpsReqProprietary_t; + +/*! + * LoRaMAC MCPS-Request structure + */ +typedef struct sMcpsReq +{ + /*! + * MCPS-Request type + */ + Mcps_t Type; + + /*! + * MCPS-Request parameters + */ + union uMcpsParam + { + /*! + * MCPS-Request parameters for an unconfirmed frame + */ + McpsReqUnconfirmed_t Unconfirmed; + /*! + * MCPS-Request parameters for a confirmed frame + */ + McpsReqConfirmed_t Confirmed; + /*! + * MCPS-Request parameters for a proprietary frame + */ + McpsReqProprietary_t Proprietary; + }Req; +}McpsReq_t; + +/*! + * LoRaMAC MCPS-Confirm */ -typedef struct +typedef struct sMcpsConfirm +{ + /*! + * Holds the previously performed MCPS-Request + */ + Mcps_t McpsRequest; + /*! + * Status of the operation + */ + LoRaMacEventInfoStatus_t Status; + /*! + * Uplink datarate + */ + uint8_t Datarate; + /*! + * Transmission power + */ + int8_t TxPower; + /*! + * Set if an acknowledgement was received + */ + bool AckReceived; + /*! + * Provides the number of retransmissions + */ + uint8_t NbRetries; + /*! + * The transmission time on air of the frame + */ + TimerTime_t TxTimeOnAir; + /*! + * The uplink counter value related to the frame + */ + uint32_t UpLinkCounter; +}McpsConfirm_t; + +/*! + * LoRaMAC MCPS-Indication primitive + */ +typedef struct sMcpsIndication { + /*! + * MCPS-Indication type + */ + Mcps_t McpsIndication; + /*! + * Status of the operation + */ + LoRaMacEventInfoStatus_t Status; + /*! + * Multicast + */ + uint8_t Multicast; + /*! + * Application port + */ + uint8_t Port; + /*! + * Downlink datarate + */ + uint8_t RxDatarate; + /*! + * Frame pending status + */ + uint8_t FramePending; + /*! + * Pointer to the received data stream + */ + uint8_t *Buffer; + /*! + * Size of the received data stream + */ + uint8_t BufferSize; + /*! + * Indicates, if data is available + */ + bool RxData; + /*! + * Rssi of the received packet + */ + int16_t Rssi; + /*! + * Snr of the received packet + */ + uint8_t Snr; + /*! + * Receive window + * + * [0: Rx window 1, 1: Rx window 2] + */ + uint8_t RxSlot; + /*! + * Set if an acknowledgement was received + */ + bool AckReceived; + /*! + * The downlink counter value for the received frame + */ + uint32_t DownLinkCounter; +}McpsIndication_t; + +/*! + * \brief LoRaMAC management services + * + * \details The following table list the primitives which are supported by the + * specific MAC management service: + * + * Name | Request | Indication | Response | Confirm + * --------------------- | :-----: | :--------: | :------: | :-----: + * \ref MLME_JOIN | YES | NO | NO | YES + * \ref MLME_LINK_CHECK | YES | NO | NO | YES + * + * The following table provides links to the function implementations of the + * related MLME primitives. + * + * Primitive | Function + * ---------------- | :---------------------: + * MLME-Request | \ref LoRaMacMlmeRequest + * MLME-Confirm | MacMlmeConfirm in \ref LoRaMacPrimitives_t + */ +typedef enum eMlme +{ + /*! + * Initiates the Over-the-Air activation + * + * LoRaWAN Specification V1.0, chapter 6.2 + */ + MLME_JOIN, + /*! + * LinkCheckReq - Connectivity validation + * + * LoRaWAN Specification V1.0, chapter 5, table 4 + */ + MLME_LINK_CHECK, +}Mlme_t; + +/*! + * LoRaMAC MLME-Request for the join service + */ +typedef struct sMlmeReqJoin +{ + /*! + * Globally unique end-device identifier + * + * LoRaWAN Specification V1.0, chapter 6.2.1 + */ + uint8_t *DevEui; + /*! + * Application identifier + * + * LoRaWAN Specification V1.0, chapter 6.1.2 + */ + uint8_t *AppEui; + /*! + * AES-128 application key + * + * LoRaWAN Specification V1.0, chapter 6.2.2 + */ + uint8_t *AppKey; +}MlmeReqJoin_t; + +/*! + * + */ +typedef struct sMlmeReq +{ + /*! + * MLME-Request type + */ + Mlme_t Type; + + /*! + * MLME-Request parameters + */ + union uMlmeParam + { + /*! + * MLME-Request parameters for a join request + */ + MlmeReqJoin_t Join; + }Req; +}MlmeReq_t; + +/*! + * LoRaMAC MLME-Confirm primitive + */ +typedef struct sMlmeConfirm +{ + /*! + * Holds the previously performed MLME-Request + */ + Mlme_t MlmeRequest; + /*! + * Status of the operation + */ LoRaMacEventInfoStatus_t Status; - bool TxAckReceived; - uint8_t TxNbRetries; - uint8_t TxDatarate; - uint8_t RxPort; - uint8_t *RxBuffer; - uint8_t RxBufferSize; - int16_t RxRssi; - uint8_t RxSnr; - uint16_t Energy; + /*! + * The transmission time on air of the frame + */ + TimerTime_t TxTimeOnAir; + /*! + * Demodulation margin. Contains the link margin [dB] of the last + * successfully received LinkCheckReq + */ uint8_t DemodMargin; + /*! + * Number of gateways which received the last LinkCheckReq + */ uint8_t NbGateways; -}LoRaMacEventInfo_t; +}MlmeConfirm_t; + +/*! + * LoRa Mac Information Base (MIB) + * + * The following table lists the MIB parameters and the related attributes: + * + * Attribute | Get | Set + * --------------------------------- | :-: | :-: + * \ref MIB_DEVICE_CLASS | YES | YES + * \ref MIB_NETWORK_JOINED | YES | YES + * \ref MIB_ADR | YES | YES + * \ref MIB_NET_ID | YES | YES + * \ref MIB_DEV_ADDR | YES | YES + * \ref MIB_NWK_SKEY | YES | YES + * \ref MIB_APP_SKEY | YES | YES + * \ref MIB_PUBLIC_NETWORK | YES | YES + * \ref MIB_CHANNELS | YES | NO + * \ref MIB_RX2_CHANNEL | YES | YES + * \ref MIB_CHANNELS_MASK | YES | YES + * \ref MIB_CHANNELS_NB_REP | YES | YES + * \ref MIB_MAX_RX_WINDOW_DURATION | YES | YES + * \ref MIB_RECEIVE_DELAY_1 | YES | YES + * \ref MIB_RECEIVE_DELAY_2 | YES | YES + * \ref MIB_JOIN_ACCEPT_DELAY_1 | YES | YES + * \ref MIB_JOIN_ACCEPT_DELAY_2 | YES | YES + * \ref MIB_CHANNELS_DATARATE | YES | YES + * \ref MIB_CHANNELS_TX_POWER | YES | YES + * \ref MIB_UPLINK_COUNTER | YES | NO + * \ref MIB_DOWNLINK_COUNTER | YES | NO + * \ref MIB_MULTICAST_CHANNEL | YES | NO + * + * The following table provides links to the function implementations of the + * related MIB primitives: + * + * Primitive | Function + * ---------------- | :---------------------: + * MIB-Set | \ref LoRaMacMibSetRequestConfirm + * MIB-Get | \ref LoRaMacMibGetRequestConfirm + */ +typedef enum eMib +{ + /*! + * LoRaWAN device class + * + * LoRaWAN Specification V1.0 + */ + MIB_DEVICE_CLASS, + /*! + * LoRaWAN Network joined attribute + * + * LoRaWAN Specification V1.0 + */ + MIB_NETWORK_JOINED, + /*! + * Adaptive data rate + * + * LoRaWAN Specification V1.0, chapter 4.3.1.1 + * + * [true: ADR enabled, false: ADR disabled] + */ + MIB_ADR, + /*! + * Network identifier + * + * LoRaWAN Specification V1.0, chapter 6.2.5 + */ + MIB_NET_ID, + /*! + * End-device address + * + * LoRaWAN Specification V1.0, chapter 6.1.2 + */ + MIB_DEV_ADDR, + /*! + * Network session key + * + * LoRaWAN Specification V1.0, chapter 6.1.3 + */ + MIB_NWK_SKEY, + /*! + * Application session key + * + * LoRaWAN Specification V1.0, chapter 6.1.4 + */ + MIB_APP_SKEY, + /*! + * Set the network type to public or private + * + * LoRaWAN Specification V1.0, chapter 7 + * + * [true: public network, false: private network] + */ + MIB_PUBLIC_NETWORK, + /*! + * Support the operation with repeaters + * + * LoRaWAN Specification V1.0, chapter 7 + * + * [true: repeater support enabled, false: repeater support disabled] + */ + MIB_REPEATER_SUPPORT, + /*! + * Communication channels. A get request will return a + * pointer which references the first entry of the channel list. The + * list is of size LORA_MAX_NB_CHANNELS + * + * LoRaWAN Specification V1.0, chapter 7 + */ + MIB_CHANNELS, + /*! + * Set receive window 2 channel + * + * LoRaWAN Specification V1.0, chapter 3.3.2 + */ + MIB_RX2_CHANNEL, + /*! + * LoRaWAN channels mask + * + * LoRaWAN Specification V1.0, chapter 5.2 + */ + MIB_CHANNELS_MASK, + /*! + * Set the number of repetitions on a channel + * + * LoRaWAN Specification V1.0, chapter 5.2 + */ + MIB_CHANNELS_NB_REP, + /*! + * Maximum receive window duration in [us] + * + * LoRaWAN Specification V1.0, chapter 3.3.3 + */ + MIB_MAX_RX_WINDOW_DURATION, + /*! + * Receive delay 1 in [us] + * + * LoRaWAN Specification V1.0, chapter 6 + */ + MIB_RECEIVE_DELAY_1, + /*! + * Receive delay 2 in [us] + * + * LoRaWAN Specification V1.0, chapter 6 + */ + MIB_RECEIVE_DELAY_2, + /*! + * Join accept delay 1 in [us] + * + * LoRaWAN Specification V1.0, chapter 6 + */ + MIB_JOIN_ACCEPT_DELAY_1, + /*! + * Join accept delay 2 in [us] + * + * LoRaWAN Specification V1.0, chapter 6 + */ + MIB_JOIN_ACCEPT_DELAY_2, + /*! + * Data rate of a channel + * + * LoRaWAN Specification V1.0, chapter 7 + * + * EU868 - [DR_0, DR_1, DR_2, DR_3, DR_4, DR_5, DR_6, DR_7] + * + * US915 - [DR_0, DR_1, DR_2, DR_3, DR_4, DR_8, DR_9, DR_10, DR_11, DR_12, DR_13] + */ + MIB_CHANNELS_DATARATE, + /*! + * Transmission power of a channel + * + * LoRaWAN Specification V1.0, chapter 7 + * + * EU868 - [TX_POWER_20_DBM, TX_POWER_14_DBM, TX_POWER_11_DBM, + * TX_POWER_08_DBM, TX_POWER_05_DBM, TX_POWER_02_DBM] + * + * US915 - [TX_POWER_30_DBM, TX_POWER_28_DBM, TX_POWER_26_DBM, + * TX_POWER_24_DBM, TX_POWER_22_DBM, TX_POWER_20_DBM, + * TX_POWER_18_DBM, TX_POWER_14_DBM, TX_POWER_12_DBM, + * TX_POWER_10_DBM] + */ + MIB_CHANNELS_TX_POWER, + /*! + * LoRaWAN Up-link counter + * + * LoRaWAN Specification V1.0, chapter 4.3.1.5 + */ + MIB_UPLINK_COUNTER, + /*! + * LoRaWAN Down-link counter + * + * LoRaWAN Specification V1.0, chapter 4.3.1.5 + */ + MIB_DOWNLINK_COUNTER, + /*! + * Multicast channels. A get request will return a pointer to the first + * entry of the multicast channel linked list. If the pointer is equal to + * NULL, the list is empty. + */ + MIB_MULTICAST_CHANNEL, +}Mib_t; + +/*! + * LoRaMAC MIB parameters + */ +typedef union uMibParam +{ + /*! + * LoRaWAN device class + * + * Related MIB type: \ref MIB_DEVICE_CLASS + */ + DeviceClass_t Class; + /*! + * LoRaWAN network joined attribute + * + * Related MIB type: \ref MIB_NETWORK_JOINED + */ + bool IsNetworkJoined; + /*! + * Activation state of ADR + * + * Related MIB type: \ref MIB_ADR + */ + bool AdrEnable; + /*! + * Network identifier + * + * Related MIB type: \ref MIB_NET_ID + */ + uint32_t NetID; + /*! + * End-device address + * + * Related MIB type: \ref MIB_DEV_ADDR + */ + uint32_t DevAddr; + /*! + * Network session key + * + * Related MIB type: \ref MIB_NWK_SKEY + */ + uint8_t *NwkSKey; + /*! + * Application session key + * + * Related MIB type: \ref MIB_APP_SKEY + */ + uint8_t *AppSKey; + /*! + * Enable or disable a public network + * + * Related MIB type: \ref MIB_PUBLIC_NETWORK + */ + bool EnablePublicNetwork; + /*! + * Enable or disable repeater support + * + * Related MIB type: \ref MIB_REPEATER_SUPPORT + */ + bool EnableRepeaterSupport; + /*! + * LoRaWAN Channel + * + * Related MIB type: \ref MIB_CHANNELS + */ + ChannelParams_t* ChannelList; + /*! + * Channel for the receive window 2 + * + * Related MIB type: \ref MIB_RX2_CHANNEL + */ + Rx2ChannelParams_t Rx2Channel; + /*! + * Channel mask + * + * Related MIB type: \ref MIB_CHANNELS_MASK + */ + uint16_t* ChannelsMask; + /*! + * Number of frame repetitions + * + * Related MIB type: \ref MIB_CHANNELS_NB_REP + */ + uint8_t ChannelNbRep; + /*! + * Maximum receive window duration + * + * Related MIB type: \ref MIB_MAX_RX_WINDOW_DURATION + */ + uint32_t MaxRxWindow; + /*! + * Receive delay 1 + * + * Related MIB type: \ref MIB_RECEIVE_DELAY_1 + */ + uint32_t ReceiveDelay1; + /*! + * Receive delay 2 + * + * Related MIB type: \ref MIB_RECEIVE_DELAY_2 + */ + uint32_t ReceiveDelay2; + /*! + * Join accept delay 1 + * + * Related MIB type: \ref MIB_JOIN_ACCEPT_DELAY_1 + */ + uint32_t JoinAcceptDelay1; + /*! + * Join accept delay 2 + * + * Related MIB type: \ref MIB_JOIN_ACCEPT_DELAY_2 + */ + uint32_t JoinAcceptDelay2; + /*! + * Channels data rate + * + * Related MIB type: \ref MIB_CHANNELS_DATARATE + */ + int8_t ChannelsDatarate; + /*! + * Channels TX power + * + * Related MIB type: \ref MIB_CHANNELS_TX_POWER + */ + int8_t ChannelsTxPower; + /*! + * LoRaWAN Up-link counter + * + * Related MIB type: \ref MIB_UPLINK_COUNTER + */ + uint32_t UpLinkCounter; + /*! + * LoRaWAN Down-link counter + * + * Related MIB type: \ref MIB_DOWNLINK_COUNTER + */ + uint32_t DownLinkCounter; + /*! + * Multicast channel + * + * Related MIB type: \ref MIB_MULTICAST_CHANNEL + */ + MulticastParams_t* MulticastList; +}MibParam_t; + +/*! + * LoRaMAC MIB-RequestConfirm structure + */ +typedef struct eMibRequestConfirm +{ + /*! + * MIB-Request type + */ + Mib_t Type; + + /*! + * MLME-RequestConfirm parameters + */ + MibParam_t Param; +}MibRequestConfirm_t; + +/*! + * LoRaMAC tx information + */ +typedef struct sLoRaMacTxInfo +{ + /*! + * Defines the size of the applicative payload which can be processed + */ + uint8_t MaxPossiblePayload; + /*! + * The current payload size, dependent on the current datarate + */ + uint8_t CurrentPayloadSize; +}LoRaMacTxInfo_t; + +/*! + * LoRaMAC Status + */ +typedef enum eLoRaMacStatus +{ + /*! + * Service started successfully + */ + LORAMAC_STATUS_OK, + /*! + * Service not started - LoRaMAC is busy + */ + LORAMAC_STATUS_BUSY, + /*! + * Service unknown + */ + LORAMAC_STATUS_SERVICE_UNKNOWN, + /*! + * Service not started - invalid parameter + */ + LORAMAC_STATUS_PARAMETER_INVALID, + /*! + * Service not started - the device is not in a LoRaWAN + */ + LORAMAC_STATUS_NO_NETWORK_JOINED, + /*! + * Service not started - playload lenght error + */ + LORAMAC_STATUS_LENGTH_ERROR, + /*! + * Service not started - playload lenght error + */ + LORAMAC_STATUS_MAC_CMD_LENGTH_ERROR, + /*! + * Service not started - the device is switched off + */ + LORAMAC_STATUS_DEVICE_OFF, +}LoRaMacStatus_t; /*! * LoRaMAC events structure * Used to notify upper layers of MAC events */ -typedef struct sLoRaMacCallbacks +typedef struct sLoRaMacPrimitives { /*! - * MAC layer event callback prototype. + * \brief MCPS-Confirm primitive * - * \param [IN] flags Bit field indicating the MAC events occurred - * \param [IN] info Details about MAC events occurred + * \param [OUT] MCPS-Confirm parameters + */ + void ( *MacMcpsConfirm )( McpsConfirm_t *McpsConfirm ); + /*! + * \brief MCPS-Indication primitive + * + * \param [OUT] MCPS-Indication parameters */ - void ( *MacEvent )( LoRaMacEventFlags_t *flags, LoRaMacEventInfo_t *info ); + void ( *MacMcpsIndication )( McpsIndication_t *McpsIndication ); /*! - * Function callback to get the current battery level + * \brief MLME-Confirm primitive * - * \retval batteryLevel Current battery level + * \param [OUT] MLME-Confirm parameters + */ + void ( *MacMlmeConfirm )( MlmeConfirm_t *MlmeConfirm ); +}LoRaMacPrimitives_t; + +typedef struct sLoRaMacCallback +{ + /*! + * \brief Measures the battery level + * + * \retval Battery level [0: node is connected to an external + * power source, 1..254: battery level, where 1 is the minimum + * and 254 is the maximum value, 255: the node was not able + * to measure the battery level] */ uint8_t ( *GetBatteryLevel )( void ); -}LoRaMacCallbacks_t; - -/*! - * LoRaMAC layer initialization - * - * \param [IN] callabcks Pointer to a structure defining the LoRaMAC - * callback functions. - */ -void LoRaMacInit( LoRaMacCallbacks_t *callabcks ); +}LoRaMacCallback_t; /*! - * Enables/Disables the ADR (Adaptive Data Rate) - * - * \param [IN] enable [true: ADR ON, false: ADR OFF] - */ -void LoRaMacSetAdrOn( bool enable ); - -/*! - * Initializes the network IDs. Device address, - * network session AES128 key and application session AES128 key. + * \brief LoRaMAC layer initialization * - * \remark To be only used when Over-the-Air activation isn't used. + * \details In addition to the initialization of the LoRaMAC layer, this + * function initializes the callback primitives of the MCPS and + * MLME services. Every data field of \ref LoRaMacPrimitives_t must be + * set to a valid callback function. + * + * \param [IN] events - Pointer to a structure defining the LoRaMAC + * event functions. Refer to \ref LoRaMacPrimitives_t. * - * \param [IN] netID 24 bits network identifier - * ( provided by network operator ) - * \param [IN] devAddr 32 bits device address on the network - * (must be unique to the network) - * \param [IN] nwkSKey Pointer to the network session AES128 key array - * ( 16 bytes ) - * \param [IN] appSKey Pointer to the application session AES128 key array - * ( 16 bytes ) + * \param [IN] events - Pointer to a structure defining the LoRaMAC + * callback functions. Refer to \ref LoRaMacCallback_t. + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_PARAMETER_INVALID. */ -void LoRaMacInitNwkIds( uint32_t netID, uint32_t devAddr, uint8_t *nwkSKey, uint8_t *appSKey ); - -/* - * TODO: Add documentation - */ -void LoRaMacMulticastChannelAdd( MulticastParams_t *channelParam ); - -/* - * TODO: Add documentation - */ -void LoRaMacMulticastChannelRemove( MulticastParams_t *channelParam ); +LoRaMacStatus_t LoRaMacInitialization( LoRaMacPrimitives_t *primitives, LoRaMacCallback_t *callbacks ); /*! - * Initiates the Over-the-Air activation - * - * \param [IN] devEui Pointer to the device EUI array ( 8 bytes ) - * \param [IN] appEui Pointer to the application EUI array ( 8 bytes ) - * \param [IN] appKey Pointer to the application AES128 key array ( 16 bytes ) + * \brief Queries the LoRaMAC if it is possible to send the next frame with + * a given payload size. The LoRaMAC takes scheduled MAC commands into + * account and reports, when the frame can be send or not. + * + * \param [IN] size - Size of applicative payload to be send next * - * \retval status [0: OK, 1: Tx error, 2: Already joined a network] - */ -uint8_t LoRaMacJoinReq( uint8_t *devEui, uint8_t *appEui, uint8_t *appKey ); - -/*! - * Sends a LinkCheckReq MAC command on the next uplink frame + * \param [OUT] txInfo - The structure \ref LoRaMacTxInfo_t contains + * information about the actual maximum payload possible + * ( according to the configured datarate or the next + * datarate according to ADR ), and the maximum frame + * size, taking the scheduled MAC commands into account. * - * \retval status Function status [0: OK, 1: Busy] + * \retval LoRaMacStatus_t Status of the operation. When the parameters are + * not valid, the function returns \ref LORAMAC_STATUS_PARAMETER_INVALID. + * In case of a length error caused by the applicative payload size, the + * function returns LORAMAC_STATUS_LENGTH_ERROR. In case of a length error + * due to additional MAC commands in the queue, the function returns + * LORAMAC_STATUS_MAC_CMD_LENGTH_ERROR. In case the query is valid, and + * the LoRaMAC is able to send the frame, the function returns LORAMAC_STATUS_OK. * */ -uint8_t LoRaMacLinkCheckReq( void ); - -/*! - * LoRaMAC layer send frame - * - * \param [IN] fPort MAC payload port (must be > 0) - * \param [IN] fBuffer MAC data buffer to be sent - * \param [IN] fBufferSize MAC data buffer size - * - * \retval status [0: OK, 1: Busy, 2: No network joined, - * 3: Length or port error, 4: Unknown MAC command - * 5: Unable to find a free channel - * 6: Device switched off] - */ -uint8_t LoRaMacSendFrame( uint8_t fPort, void *fBuffer, uint16_t fBufferSize ); +LoRaMacStatus_t LoRaMacQueryTxPossible( uint8_t size, LoRaMacTxInfo_t* txInfo ); /*! - * LoRaMAC layer send frame + * \brief LoRaMAC channel add service + * + * \details Adds a new channel to the channel list and activates the id in + * the channel mask. For the US915 band, all channels are enabled + * by default. It is not possible to activate less than 6 125 kHz + * channels. + * + * \param [IN] id - Id of the channel. Possible values are: * - * \param [IN] fPort MAC payload port (must be > 0) - * \param [IN] fBuffer MAC data buffer to be sent - * \param [IN] fBufferSize MAC data buffer size - * \param [IN] fBufferSize MAC data buffer size - * \param [IN] nbRetries Number of retries to receive the acknowledgement + * 0-15 for EU868 + * 0-72 for US915 + * + * \param [IN] params - Channel parameters to set. * - * \retval status [0: OK, 1: Busy, 2: No network joined, - * 3: Length or port error, 4: Unknown MAC command - * 5: Unable to find a free channel - * 6: Device switched off] + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_BUSY, + * \ref LORAMAC_STATUS_PARAMETER_INVALID. */ -uint8_t LoRaMacSendConfirmedFrame( uint8_t fPort, void *fBuffer, uint16_t fBufferSize, uint8_t nbRetries ); +LoRaMacStatus_t LoRaMacChannelAdd( uint8_t id, ChannelParams_t params ); /*! - * ============================================================================ - * = LoRaMac test functions = - * ============================================================================ + * \brief LoRaMAC channel remove service + * + * \details Deactivates the id in the channel mask. + * + * \param [IN] id - Id of the channel. + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_BUSY, + * \ref LORAMAC_STATUS_PARAMETER_INVALID. */ +LoRaMacStatus_t LoRaMacChannelRemove( uint8_t id ); /*! - * LoRaMAC layer generic send frame + * \brief LoRaMAC multicast channel link service + * + * \details Links a multicast channel into the linked list. + * + * \param [IN] channelParam - Multicast channel parameters to link. * - * \param [IN] macHdr MAC header 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 [0: OK, 1: Busy, 2: No network joined, - * 3: Length or port error, 4: Unknown MAC command - * 5: Unable to find a free channel - * 6: Device switched off] + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_BUSY, + * \ref LORAMAC_STATUS_PARAMETER_INVALID. */ -uint8_t LoRaMacSend( LoRaMacHeader_t *macHdr, uint8_t *fOpts, uint8_t fPort, void *fBuffer, uint16_t fBufferSize ); +LoRaMacStatus_t LoRaMacMulticastChannelLink( MulticastParams_t *channelParam ); + +/*! + * \brief LoRaMAC multicast channel unlink service + * + * \details Unlinks a multicast channel from the linked list. + * + * \param [IN] channelParam - Multicast channel parameters to unlink. + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_BUSY, + * \ref LORAMAC_STATUS_PARAMETER_INVALID. + */ +LoRaMacStatus_t LoRaMacMulticastChannelUnlink( MulticastParams_t *channelParam ); /*! - * LoRaMAC layer frame buffer initialization. + * \brief LoRaMAC MIB-Get + * + * \details The mac information base service to get attributes of the LoRaMac + * layer. + * + * The following code-snippet shows how to use the API to get the + * parameter AdrEnable, defined by the enumeration type + * \ref MIB_ADR. + * \code + * MibRequestConfirm_t mibReq; + * mibReq.Type = MIB_ADR; * - * \param [IN] channel Channel parameters - * \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 [0: OK, 1: N/A, 2: No network joined, - * 3: Length or port error, 4: Unknown MAC command] + * if( LoRaMacMibGetRequestConfirm( &mibReq ) == LORAMAC_STATUS_OK ) + * { + * // LoRaMAC updated the parameter mibParam.AdrEnable + * } + * \endcode + * + * \param [IN] mibRequest - MIB-GET-Request to perform. Refer to \ref MibRequestConfirm_t. + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_SERVICE_UNKNOWN, + * \ref LORAMAC_STATUS_PARAMETER_INVALID. */ -uint8_t LoRaMacPrepareFrame( ChannelParams_t channel,LoRaMacHeader_t *macHdr, LoRaMacFrameCtrl_t *fCtrl, uint8_t *fOpts, uint8_t fPort, void *fBuffer, uint16_t fBufferSize ); - -/*! - * LoRaMAC layer prepared frame buffer transmission with channel specification - * - * \remark LoRaMacPrepareFrame must be called at least once before calling this - * function. - * - * \param [IN] channel Channel parameters - * \retval status [0: OK, 1: Busy] - */ -uint8_t LoRaMacSendFrameOnChannel( ChannelParams_t channel ); +LoRaMacStatus_t LoRaMacMibGetRequestConfirm( MibRequestConfirm_t *mibGet ); /*! - * LoRaMAC layer generic send frame with channel specification + * \brief LoRaMAC MIB-Set + * + * \details The mac information base service to set attributes of the LoRaMac + * layer. + * + * The following code-snippet shows how to use the API to set the + * parameter AdrEnable, defined by the enumeration type + * \ref MIB_ADR. + * + * \code + * MibRequestConfirm_t mibReq; + * mibReq.Type = MIB_ADR; + * mibReq.Param.AdrEnable = true; * - * \param [IN] channel Channel parameters - * \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 [0: OK, 1: Busy, 2: No network joined, - * 3: Length or port error, 4: Unknown MAC command] - */ -uint8_t LoRaMacSendOnChannel( ChannelParams_t channel, LoRaMacHeader_t *macHdr, LoRaMacFrameCtrl_t *fCtrl, uint8_t *fOpts, uint8_t fPort, void *fBuffer, uint16_t fBufferSize ); - -/*! - * ============================================================================ - * = LoRaMac setup functions = - * ============================================================================ - */ - -/* - * TODO: Add documentation + * if( LoRaMacMibGetRequestConfirm( &mibReq ) == LORAMAC_STATUS_OK ) + * { + * // LoRaMAC updated the parameter + * } + * \endcode + * + * \param [IN] mibRequest - MIB-SET-Request to perform. Refer to \ref MibRequestConfirm_t. + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_BUSY, + * \ref LORAMAC_STATUS_SERVICE_UNKNOWN, + * \ref LORAMAC_STATUS_PARAMETER_INVALID. */ -void LoRaMacSetDeviceClass( DeviceClass_t deviceClass ); - -/* - * TODO: Add documentation - */ -void LoRaMacSetPublicNetwork( bool enable ); - -/* - * TODO: Add documentation - */ -void LoRaMacSetChannel( uint8_t id, ChannelParams_t params ); - -/* - * TODO: Add documentation - */ -void LoRaMacSetRx2Channel( Rx2ChannelParams_t param ); - -/*! - * Sets channels tx output power - * - * \param [IN] txPower [TX_POWER_20_DBM, TX_POWER_14_DBM, - TX_POWER_11_DBM, TX_POWER_08_DBM, - TX_POWER_05_DBM, TX_POWER_02_DBM] - */ -void LoRaMacSetChannelsTxPower( int8_t txPower ); +LoRaMacStatus_t LoRaMacMibSetRequestConfirm( MibRequestConfirm_t *mibSet ); /*! - * Sets channels datarate + * \brief LoRaMAC MLME-Request + * + * \details The Mac layer management entity handles management services. The + * following code-snippet shows how to use the API to perform a + * network join request. + * + * \code + * static uint8_t DevEui[] = + * { + * 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + * }; + * static uint8_t AppEui[] = + * { + * 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + * }; + * static uint8_t AppKey[] = + * { + * 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, + * 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C + * }; * - * \param [IN] datarate eu868 - [DR_0, DR_1, DR_2, DR_3, DR_4, DR_5, DR_6, DR_7] - * us915 - [DR_0, DR_1, DR_2, DR_3, DR_4] - */ -void LoRaMacSetChannelsDatarate( int8_t datarate ); - -/* - * TODO: Add documentation - */ -void LoRaMacSetChannelsMask( uint16_t *mask ); - -/* - * TODO: Add documentation - */ -void LoRaMacSetChannelsNbRep( uint8_t nbRep ); - -/* - * TODO: Add documentation - */ -void LoRaMacSetMaxRxWindow( uint32_t delay ); - -/* - * TODO: Add documentation + * MlmeReq_t mlmeReq; + * mlmeReq.Type = MLME_JOIN; + * mlmeReq.Req.Join.DevEui = DevEui; + * mlmeReq.Req.Join.AppEui = AppEui; + * mlmeReq.Req.Join.AppKey = AppKey; + * + * if( LoRaMacMlmeRequest( &mlmeReq ) == LORAMAC_STATUS_OK ) + * { + * // Service started successfully. Waiting for the Mlme-Confirm event + * } + * \endcode + * + * \param [IN] mlmeRequest - MLME-Request to perform. Refer to \ref MlmeReq_t. + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_BUSY, + * \ref LORAMAC_STATUS_SERVICE_UNKNOWN, + * \ref LORAMAC_STATUS_PARAMETER_INVALID, + * \ref LORAMAC_STATUS_NO_NETWORK_JOINED, + * \ref LORAMAC_STATUS_LENGTH_ERROR, + * \ref LORAMAC_STATUS_DEVICE_OFF. */ -void LoRaMacSetReceiveDelay1( uint32_t delay ); - -/* - * TODO: Add documentation - */ -void LoRaMacSetReceiveDelay2( uint32_t delay ); - -/* - * TODO: Add documentation - */ -void LoRaMacSetJoinAcceptDelay1( uint32_t delay ); - -/* - * TODO: Add documentation - */ -void LoRaMacSetJoinAcceptDelay2( uint32_t delay ); - -/* - * TODO: Add documentation - */ -uint32_t LoRaMacGetUpLinkCounter( void ); - -/* - * TODO: Add documentation - */ -uint32_t LoRaMacGetDownLinkCounter( void ); - -/* - * ============================================================================ - * = LoRaMac test functions = - * ============================================================================ - */ +LoRaMacStatus_t LoRaMacMlmeRequest( MlmeReq_t *mlmeRequest ); /*! - * Disables/Enables the duty cycle enforcement (EU868) + * \brief LoRaMAC MCPS-Request + * + * \details The Mac Common Part Sublayer handles data services. The following + * code-snippet shows how to use the API to send an unconfirmed + * LoRaMAC frame. * - * \param [IN] enable - Enabled or disables the duty cycle - */ -void LoRaMacTestSetDutyCycleOn( bool enable ); - -/*! - * Disables/Enables the reception windows opening + * \code + * uint8_t myBuffer[] = { 1, 2, 3 }; + * + * McpsReq_t mcpsReq; + * mcpsReq.Type = MCPS_UNCONFIRMED; + * mcpsReq.Req.Unconfirmed.fPort = 1; + * mcpsReq.Req.Unconfirmed.fBuffer = myBuffer; + * mcpsReq.Req.Unconfirmed.fBufferSize = sizeof( myBuffer ); * - * \param [IN] enable [true: enable, false: disable] + * if( LoRaMacMcpsRequest( &mcpsReq ) == LORAMAC_STATUS_OK ) + * { + * // Service started successfully. Waiting for the MCPS-Confirm event + * } + * \endcode + * + * \param [IN] mcpsRequest - MCPS-Request to perform. Refer to \ref McpsReq_t. + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_BUSY, + * \ref LORAMAC_STATUS_SERVICE_UNKNOWN, + * \ref LORAMAC_STATUS_PARAMETER_INVALID, + * \ref LORAMAC_STATUS_NO_NETWORK_JOINED, + * \ref LORAMAC_STATUS_LENGTH_ERROR, + * \ref LORAMAC_STATUS_DEVICE_OFF. */ -void LoRaMacTestRxWindowsOn( bool enable ); +LoRaMacStatus_t LoRaMacMcpsRequest( McpsReq_t *mcpsRequest ); -/*! - * Enables the MIC field test - * - * \param [IN] upLinkCounter Fixed Tx packet counter value - */ -void LoRaMacTestSetMic( uint16_t upLinkCounter ); +/*! \} defgroup LORAMAC */ #endif // __LORAMAC_H__
--- a/LoRaMacCrypto.cpp Mon Nov 23 10:09:43 2015 +0000 +++ b/LoRaMacCrypto.cpp Tue Jan 05 16:41:54 2016 +0000 @@ -5,12 +5,17 @@ _____) ) ____| | | || |_| ____( (___| | | | (______/|_____)_|_|_| \__)_____)\____)_| |_| (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 and Gregory Cristian +Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jäckle ( STACKFORCE ) */ #include <stdlib.h> #include <stdint.h> @@ -185,13 +190,13 @@ memset1( nonce, 0, sizeof( nonce ) ); nonce[0] = 0x01; - LoRaMacMemCpy( appNonce, nonce + 1, 6 ); - LoRaMacMemCpy( pDevNonce, nonce + 7, 2 ); + memcpy1( nonce + 1, appNonce, 6 ); + memcpy1( nonce + 7, pDevNonce, 2 ); aes_encrypt( nonce, nwkSKey, &AesContext ); memset1( nonce, 0, sizeof( nonce ) ); nonce[0] = 0x02; - LoRaMacMemCpy( appNonce, nonce + 1, 6 ); - LoRaMacMemCpy( pDevNonce, nonce + 7, 2 ); + memcpy1( nonce + 1, appNonce, 6 ); + memcpy1( nonce + 7, pDevNonce, 2 ); aes_encrypt( nonce, appSKey, &AesContext ); }
--- a/LoRaMacCrypto.h Mon Nov 23 10:09:43 2015 +0000 +++ b/LoRaMacCrypto.h Tue Jan 05 16:41:54 2016 +0000 @@ -1,99 +1,111 @@ -/* - / _____) _ | | -( (____ _____ ____ _| |_ _____ ____| |__ - \____ \| ___ | (_ _) ___ |/ ___) _ \ - _____) ) ____| | | || |_| ____( (___| | | | -(______/|_____)_|_|_| \__)_____)\____)_| |_| - (C)2013 Semtech - -Description: LoRa MAC layer implementation - -License: Revised BSD License, see LICENSE.TXT file include in the project - -Maintainer: Miguel Luis and Gregory Cristian -*/ +/*! + * \file LoRaMacCrypto.h + * + * \brief LoRa MAC layer cryptography implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jäckle ( STACKFORCE ) + * + * \defgroup LORAMAC_CRYPTO LoRa MAC layer cryptography implementation + * This module covers the implementation of cryptographic functions + * of the LoRaMAC layer. + * \{ + */ #ifndef __LORAMAC_CRYPTO_H__ #define __LORAMAC_CRYPTO_H__ /*! - * Copies size elements of src array to dst array - * - * \remark STM32 Standard memcpy function only works on pointers that are aligned + * Computes the LoRaMAC frame MIC field * - * \param [IN] src Source array - * \param [OUT] dst Destination array - * \param [IN] size Number of bytes to be copied - */ -#define LoRaMacMemCpy( src, dst, size ) memcpy1( dst, src, size ) - -/*! - * Computes the LoRaMAC frame MIC field - * - * \param [IN] buffer Data buffer - * \param [IN] size Data buffer size - * \param [IN] key AES key to be used - * \param [IN] address Frame address - * \param [IN] dir Frame direction [0: uplink, 1: downlink] - * \param [IN] sequenceCounter Frame sequence counter - * \param [OUT] mic Computed MIC field + * \param [IN] buffer - Data buffer + * \param [IN] size - Data buffer size + * \param [IN] key - AES key to be used + * \param [IN] address - Frame address + * \param [IN] dir - Frame direction [0: uplink, 1: downlink] + * \param [IN] sequenceCounter - Frame sequence counter + * \param [OUT] mic - Computed MIC field */ void LoRaMacComputeMic( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint32_t address, uint8_t dir, uint32_t sequenceCounter, uint32_t *mic ); /*! - * Computes the LoRaMAC payload encryption + * Computes the LoRaMAC payload encryption * - * \param [IN] buffer Data buffer - * \param [IN] size Data buffer size - * \param [IN] key AES key to be used - * \param [IN] address Frame address - * \param [IN] dir Frame direction [0: uplink, 1: downlink] - * \param [IN] sequenceCounter Frame sequence counter - * \param [OUT] encBuffer Encrypted buffer + * \param [IN] buffer - Data buffer + * \param [IN] size - Data buffer size + * \param [IN] key - AES key to be used + * \param [IN] address - Frame address + * \param [IN] dir - Frame direction [0: uplink, 1: downlink] + * \param [IN] sequenceCounter - Frame sequence counter + * \param [OUT] encBuffer - Encrypted buffer */ void LoRaMacPayloadEncrypt( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint32_t address, uint8_t dir, uint32_t sequenceCounter, uint8_t *encBuffer ); /*! - * Computes the LoRaMAC payload decryption + * Computes the LoRaMAC payload decryption * - * \param [IN] buffer Data buffer - * \param [IN] size Data buffer size - * \param [IN] key AES key to be used - * \param [IN] address Frame address - * \param [IN] dir Frame direction [0: uplink, 1: downlink] - * \param [IN] sequenceCounter Frame sequence counter - * \param [OUT] decBuffer Decrypted buffer + * \param [IN] buffer - Data buffer + * \param [IN] size - Data buffer size + * \param [IN] key - AES key to be used + * \param [IN] address - Frame address + * \param [IN] dir - Frame direction [0: uplink, 1: downlink] + * \param [IN] sequenceCounter - Frame sequence counter + * \param [OUT] decBuffer - Decrypted buffer */ void LoRaMacPayloadDecrypt( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint32_t address, uint8_t dir, uint32_t sequenceCounter, uint8_t *decBuffer ); /*! - * Computes the LoRaMAC Join Request frame MIC field + * Computes the LoRaMAC Join Request frame MIC field * - * \param [IN] buffer Data buffer - * \param [IN] size Data buffer size - * \param [IN] key AES key to be used - * \param [OUT] mic Computed MIC field + * \param [IN] buffer - Data buffer + * \param [IN] size - Data buffer size + * \param [IN] key - AES key to be used + * \param [OUT] mic - Computed MIC field */ void LoRaMacJoinComputeMic( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint32_t *mic ); /*! - * Computes the LoRaMAC join frame decryption + * Computes the LoRaMAC join frame decryption * - * \param [IN] buffer Data buffer - * \param [IN] size Data buffer size - * \param [IN] key AES key to be used - * \param [OUT] decBuffer Decrypted buffer + * \param [IN] buffer - Data buffer + * \param [IN] size - Data buffer size + * \param [IN] key - AES key to be used + * \param [OUT] decBuffer - Decrypted buffer */ void LoRaMacJoinDecrypt( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint8_t *decBuffer ); /*! - * Computes the LoRaMAC join frame decryption + * Computes the LoRaMAC join frame decryption * - * \param [IN] key AES key to be used - * \param [IN] appNonce Application nonce - * \param [IN] devNonce Device nonce - * \param [OUT] nwkSKey Network session key - * \param [OUT] appSKey Application session key + * \param [IN] key - AES key to be used + * \param [IN] appNonce - Application nonce + * \param [IN] devNonce - Device nonce + * \param [OUT] nwkSKey - Network session key + * \param [OUT] appSKey - Application session key */ void LoRaMacJoinComputeSKeys( const uint8_t *key, const uint8_t *appNonce, uint16_t devNonce, uint8_t *nwkSKey, uint8_t *appSKey ); +/*! \} defgroup LORAMAC */ + #endif // __LORAMAC_CRYPTO_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LoRaMacTest.h Tue Jan 05 16:41:54 2016 +0000 @@ -0,0 +1,71 @@ +/*! + * \file LoRaMacTest.h + * + * \brief LoRa MAC layer test function implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jäckle ( STACKFORCE ) + * + * \defgroup LORAMACTEST LoRa MAC layer test function implementation + * This module specifies the API implementation of test function of the LoRaMAC layer. + * The functions in this file are only for testing purposes only. + * \{ + */ +#ifndef __LORAMACTEST_H__ +#define __LORAMACTEST_H__ + +/*! + * \brief Enabled or disables the reception windows + * + * \details This is a test function. It shall be used for testing purposes only. + * Changing this attribute may lead to a non-conformance LoRaMac operation. + * + * \param [IN] enable - Enabled or disables the reception windows + */ +void LoRaMacTestRxWindowsOn( bool enable ); + +/*! + * \brief Enables the MIC field test + * + * \details This is a test function. It shall be used for testing purposes only. + * Changing this attribute may lead to a non-conformance LoRaMac operation. + * + * \param [IN] txPacketCounter - Fixed Tx packet counter value + */ +void LoRaMacTestSetMic( uint16_t txPacketCounter ); + +/*! + * \brief Enabled or disables the duty cycle + * + * \details This is a test function. It shall be used for testing purposes only. + * Changing this attribute may lead to a non-conformance LoRaMac operation. + * + * \param [IN] enable - Enabled or disables the duty cycle + */ +void LoRaMacTestSetDutyCycleOn( bool enable ); + +/*! \} defgroup LORAMACTEST */ + +#endif // __LORAMACTEST_H__