diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..c798ceea76 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "sysdrv/tools/board/lorawan-bridge/sysdrv/tools/board/lorawan-bridge/b64.c"] + path = sysdrv/tools/board/lorawan-bridge/sysdrv/tools/board/lorawan-bridge/b64.c + url = git@github.com:jwerle/b64.c.git diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMac.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMac.c new file mode 100644 index 0000000000..0b68920946 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMac.c @@ -0,0 +1,5741 @@ +/*! + * \file LoRaMac.c + * + * \brief LoRa MAC layer implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + */ +#include "utilities.h" +#include "region/Region.h" +#include "LoRaMacClassB.h" +#include "LoRaMacCrypto.h" +#include "secure-element.h" +#include "LoRaMacTest.h" +#include "LoRaMacTypes.h" +#include "LoRaMacConfirmQueue.h" +#include "LoRaMacHeaderTypes.h" +#include "LoRaMacMessageTypes.h" +#include "LoRaMacParser.h" +#include "LoRaMacCommands.h" +#include "LoRaMacAdr.h" +#include "LoRaMacSerializer.h" +#include "radio.h" + +#include "LoRaMac.h" + +/*! + * Maximum PHY layer payload size + */ +#define LORAMAC_PHY_MAXPAYLOAD 255 + +/*! + * Maximum length of the fOpts field + */ +#define LORA_MAC_COMMAND_MAX_FOPTS_LENGTH 15 + +/*! + * LoRaMac duty cycle for the back-off procedure during the first hour. + */ +#define BACKOFF_DC_1_HOUR 100 + +/*! + * LoRaMac duty cycle for the back-off procedure during the next 10 hours. + */ +#define BACKOFF_DC_10_HOURS 1000 + +/*! + * LoRaMac duty cycle for the back-off procedure during the next 24 hours. + */ +#define BACKOFF_DC_24_HOURS 10000 + +/*! + * Maximum value for the ADR ack counter + */ +#define ADR_ACK_COUNTER_MAX 0xFFFFFFFF + +/*! + * Delay required to simulate an ABP join like an OTAA join + */ +#define ABP_JOIN_PENDING_DELAY_MS 10 + +/*! + * LoRaMac internal states + */ +enum eLoRaMacState +{ + LORAMAC_IDLE = 0x00000000, + LORAMAC_STOPPED = 0x00000001, + LORAMAC_TX_RUNNING = 0x00000002, + LORAMAC_RX = 0x00000004, + LORAMAC_ACK_RETRY = 0x00000010, + LORAMAC_TX_DELAYED = 0x00000020, + LORAMAC_TX_CONFIG = 0x00000040, + LORAMAC_RX_ABORT = 0x00000080, + LORAMAC_ABP_JOIN_PENDING = 0x00000100, +}; + +/* + * Request permission state + */ +typedef enum eLoRaMacRequestHandling +{ + LORAMAC_REQUEST_HANDLING_OFF = 0, + LORAMAC_REQUEST_HANDLING_ON = !LORAMAC_REQUEST_HANDLING_OFF +}LoRaMacRequestHandling_t; + +typedef struct sLoRaMacCtx +{ + /* + * Length of packet in PktBuffer + */ + uint16_t PktBufferLen; + /* + * Buffer containing the data to be sent or received. + */ + uint8_t PktBuffer[LORAMAC_PHY_MAXPAYLOAD]; + /*! + * Current processed transmit message + */ + LoRaMacMessage_t TxMsg; + /*! + * Buffer containing the data received by the application. + */ + uint8_t AppData[LORAMAC_PHY_MAXPAYLOAD]; + /* + * Size of buffer containing the application data. + */ + uint8_t AppDataSize; + /* + * Buffer containing the upper layer data. + */ + uint8_t RxPayload[LORAMAC_PHY_MAXPAYLOAD]; + SysTime_t LastTxSysTime; + /* + * LoRaMac internal state + */ + uint32_t MacState; + /* + * LoRaMac upper layer event functions + */ + LoRaMacPrimitives_t* MacPrimitives; + /* + * LoRaMac upper layer callback functions + */ + LoRaMacCallback_t* MacCallbacks; + /* + * Radio events function pointer + */ + RadioEvents_t RadioEvents; + /* + * LoRaMac duty cycle delayed Tx timer + */ + TimerEvent_t TxDelayedTimer; + /* + * LoRaMac reception windows timers + */ + TimerEvent_t RxWindowTimer1; + TimerEvent_t RxWindowTimer2; + /* + * LoRaMac reception windows delay + * \remark normal frame: RxWindowXDelay = ReceiveDelayX - RADIO_WAKEUP_TIME + * join frame : RxWindowXDelay = JoinAcceptDelayX - RADIO_WAKEUP_TIME + */ + uint32_t RxWindow1Delay; + uint32_t RxWindow2Delay; + /* + * LoRaMac Rx windows configuration + */ + RxConfigParams_t RxWindow1Config; + RxConfigParams_t RxWindow2Config; + RxConfigParams_t RxWindowCConfig; + /* + * Acknowledge timeout timer. Used for packet retransmissions. + */ + TimerEvent_t RetransmitTimeoutTimer; + /* + * Uplink messages repetitions counter + */ + uint8_t ChannelsNbTransCounter; + /* + * Indicates if the AckTimeout timer has expired or not + */ + bool RetransmitTimeoutRetry; + /* + * If the node has sent a FRAME_TYPE_DATA_CONFIRMED_UP this variable indicates + * if the nodes needs to manage the server acknowledgement. + */ + bool NodeAckRequested; + /* + * Current channel index + */ + uint8_t Channel; + /* + * Last transmission time on air + */ + TimerTime_t TxTimeOnAir; + /* + * Structure to hold an MCPS indication data. + */ + McpsIndication_t McpsIndication; + /* + * Structure to hold MCPS confirm data. + */ + McpsConfirm_t McpsConfirm; + /* + * Structure to hold MLME confirm data. + */ + MlmeConfirm_t MlmeConfirm; + /* + * Structure to hold MLME indication data. + */ + MlmeIndication_t MlmeIndication; + /* + * Holds the current rx window slot + */ + LoRaMacRxSlot_t RxSlot; + /* + * LoRaMac tx/rx operation state + */ + LoRaMacFlags_t MacFlags; + /* + * Data structure indicating if a request is allowed or not. + */ + LoRaMacRequestHandling_t AllowRequests; + /* + * Cycle timer for Type 0 Rejoin requests + */ + TimerEvent_t Rejoin0CycleTimer; + /* + * Cycle timer for Type 1 Rejoin requests + */ + TimerEvent_t Rejoin1CycleTimer; + /* + * Cycle timer for Rejoin requests trigged by ForceRejoinReq MAC command + */ + TimerEvent_t ForceRejoinReqCycleTimer; + /* + * Time of Type 0 Rejoin requests cycles + */ + TimerTime_t Rejoin0CycleTime; + /* + * Time of Type 1 Rejoin requests cycles + */ + TimerTime_t Rejoin1CycleTime; + /* + * Time of Force Rejoin requests cycles + */ + TimerTime_t ForceRejonCycleTime; + /* + * Duty cycle wait time + */ + TimerTime_t DutyCycleWaitTime; + /* + * Start time of the response timeout + */ + TimerTime_t ResponseTimeoutStartTime; + /* + * Timer required to simulate an ABP join like an OTAA join + */ + TimerEvent_t AbpJoinPendingTimer; + /* + * Buffer containing the MAC layer commands + */ + uint8_t MacCommandsBuffer[LORA_MAC_COMMAND_MAX_LENGTH]; +}LoRaMacCtx_t; + +/* + * Module context. + */ +static LoRaMacCtx_t MacCtx; + +static LoRaMacNvmData_t Nvm; + +static Band_t RegionBands[REGION_NVM_MAX_NB_BANDS]; + +/*! + * Defines the LoRaMac radio events status + */ +typedef union uLoRaMacRadioEvents +{ + uint32_t Value; + struct sEvents + { + uint32_t RxProcessPending : 1; + uint32_t RxTimeout : 1; + uint32_t RxError : 1; + uint32_t TxTimeout : 1; + uint32_t RxDone : 1; + uint32_t TxDone : 1; + }Events; +}LoRaMacRadioEvents_t; + +/*! + * LoRaMac radio events status + */ +LoRaMacRadioEvents_t LoRaMacRadioEvents = { .Value = 0 }; + +/*! + * \brief Function to be executed on Radio Tx Done event + */ +static void OnRadioTxDone( void ); + +/*! + * \brief This function prepares the MAC to abort the execution of function + * OnRadioRxDone in case of a reception error. + */ +static void PrepareRxDoneAbort( void ); + +/*! + * \brief Function to be executed on Radio Rx Done event + */ +static void OnRadioRxDone( uint8_t* payload, uint16_t size, int16_t rssi, int8_t snr ); + +/*! + * \brief Function executed on Radio Tx Timeout event + */ +static void OnRadioTxTimeout( void ); + +/*! + * \brief Function executed on Radio Rx error event + */ +static void OnRadioRxError( void ); + +/*! + * \brief Function executed on Radio Rx Timeout event + */ +static void OnRadioRxTimeout( void ); + +/*! + * \brief Function executed on duty cycle delayed Tx timer event + */ +static void OnTxDelayedTimerEvent( void* context ); + +/*! + * \brief Function executed on first Rx window timer event + */ +static void OnRxWindow1TimerEvent( void* context ); + +/*! + * \brief Function executed on second Rx window timer event + */ +static void OnRxWindow2TimerEvent( void* context ); + +/*! + * \brief Function executed on Rejoin Type 0 cycle timer event + */ +static void OnRejoin0CycleTimerEvent( void* context ); + +/*! + * \brief Function executed on Rejoin Type 0 cycle timer event + */ +static void OnRejoin1CycleTimerEvent( void* context ); + +/*! + * \brief Function executed on Rejoin Type 0 or 2 cycle timer event + * which was requested by a ForceRejoinReq MAC command. + */ +static void OnForceRejoinReqCycleTimerEvent( void* context ); + +/*! + * \brief Function executed on AckTimeout timer event + */ +static void OnRetransmitTimeoutTimerEvent( void* context ); + +/*! + * Computes next 32 bit downlink counter value and determines the frame counter ID. + * + * \param[IN] addrID - Address identifier + * \param[IN] fType - Frame type + * \param[IN] macMsg - Data message object, holding the current 16 bit transmitted frame counter + * \param[IN] lrWanVersion - LoRaWAN version + * \param[OUT] fCntID - Frame counter identifier + * \param[OUT] currentDown - Current downlink counter value + * + * \retval - Status of the operation + */ +static LoRaMacCryptoStatus_t GetFCntDown( AddressIdentifier_t addrID, FType_t fType, LoRaMacMessageData_t* macMsg, Version_t lrWanVersion, + FCntIdentifier_t* fCntID, uint32_t* currentDown ); + +/*! + * \brief Switches the device class + * + * \param [IN] deviceClass Device class to switch to + */ +static LoRaMacStatus_t SwitchClass( DeviceClass_t deviceClass ); + +/*! + * \brief Gets the maximum application payload length in the absence of the optional FOpt field. + * + * \param [IN] datarate Current datarate + * + * \retval Max length + */ +static uint8_t GetMaxAppPayloadWithoutFOptsLength( int8_t datarate ); + +/*! + * \brief Validates if the payload fits into the frame, taking the datarate + * into account. + * + * \details Refer to chapter 4.3.2 of the LoRaWAN specification, v1.0 + * + * \param lenN Length of the application payload. The length depends on the + * datarate and is region specific + * + * \param datarate Current datarate + * + * \param fOptsLen Length of the fOpts field + * + * \retval [false: payload does not fit into the frame, true: payload fits into + * the frame] + */ +static bool ValidatePayloadLength( uint8_t lenN, int8_t datarate, uint8_t fOptsLen ); + +/*! + * \brief Decodes MAC commands in the fOpts field and in the payload + * + * \param [IN] payload A pointer to the payload + * \param [IN] macIndex The index of the payload where the MAC commands start + * \param [IN] commandsSize The size of the MAC commands + * \param [IN] snr The SNR value of the frame + * \param [IN] rxSlot The RX slot where the frame was received + */ +static void ProcessMacCommands( uint8_t* payload, uint8_t macIndex, uint8_t commandsSize, int8_t snr, LoRaMacRxSlot_t rxSlot ); + +/*! + * \brief LoRaMAC layer generic send frame + * + * \param [IN] macHdr MAC header field + * \param [IN] fPort MAC payload port + * \param [IN] fBuffer MAC data buffer to be sent + * \param [IN] fBufferSize MAC data buffer size + * \retval status Status of the operation. + */ +LoRaMacStatus_t Send( LoRaMacHeader_t* macHdr, uint8_t fPort, void* fBuffer, uint16_t fBufferSize ); + +/*! + * \brief LoRaMAC layer send join/rejoin request + * + * \param [IN] joinReqType Type of join-request or rejoin + * + * \retval status Status of the operation. + */ +LoRaMacStatus_t SendReJoinReq( JoinReqIdentifier_t joinReqType ); + +/*! + * \brief LoRaMAC layer frame buffer initialization + * + * \param [IN] macHdr MAC header field + * \param [IN] fCtrl MAC frame control field + * \param [IN] fOpts MAC commands buffer + * \param [IN] fPort MAC payload port + * \param [IN] fBuffer MAC data buffer to be sent + * \param [IN] fBufferSize MAC data buffer size + * \retval status Status of the operation. + */ +LoRaMacStatus_t PrepareFrame( LoRaMacHeader_t* macHdr, LoRaMacFrameCtrl_t* fCtrl, uint8_t fPort, void* fBuffer, uint16_t fBufferSize ); + +/* + * \brief Schedules the frame according to the duty cycle + * + * \param [IN] allowDelayedTx When set to true, the a frame will be delayed, + * the duty cycle restriction is active + * \retval Status of the operation + */ +static LoRaMacStatus_t ScheduleTx( bool allowDelayedTx ); + +/* + * \brief Secures the current processed frame ( TxMsg ) + * \param[IN] txDr Data rate used for the transmission + * \param[IN] txCh Index of the channel used for the transmission + * \retval status Status of the operation + */ +static LoRaMacStatus_t SecureFrame( uint8_t txDr, uint8_t txCh ); + +/* + * \brief Calculates the aggregated back off time. + */ +static void CalculateBackOff( void ); + +/* + * \brief Function to remove pending MAC commands + * + * \param [IN] rxSlot The RX slot on which the frame was received + * \param [IN] fCtrl The frame control field of the received frame + * \param [IN] request The request type + */ +static void RemoveMacCommands( LoRaMacRxSlot_t rxSlot, LoRaMacFrameCtrl_t fCtrl, Mcps_t request ); + +/*! + * \brief LoRaMAC layer prepared frame buffer transmission with channel specification + * + * \remark PrepareFrame must be called at least once before calling this + * function. + * + * \param [IN] channel Channel to transmit on + * \retval status Status of the operation. + */ +LoRaMacStatus_t SendFrameOnChannel( uint8_t channel ); + +/*! + * \brief Sets the radio in continuous transmission mode + * + * \remark Uses the radio parameters set on the previous transmission. + * + * \param [IN] timeout Time in seconds while the radio is kept in continuous wave mode + * \param [IN] frequency RF frequency to be set. + * \param [IN] power RF output power to be set. + * \retval status Status of the operation. + */ +LoRaMacStatus_t SetTxContinuousWave( uint16_t timeout, uint32_t frequency, uint8_t power ); + +/*! + * \brief Converts a second based Rejoin Cycle base in the from timer module required format. + * + * \param [IN] rejoinCycleTime The time in second + * \param [out] timeInMiliSec The time in second + * \retval status Status of the operation. + */ +static bool ConvertRejoinCycleTime( uint32_t rejoinCycleTime, uint32_t* timeInMiliSec ); + +/*! + * \brief Resets MAC specific parameters to default + * + * \param [in] isRejoin Reset activation or not. + */ +static void ResetMacParameters( bool isRejoin ); + +/*! + * \brief Checks if it's required to send a Rejoin (Type 0) request. + * + * \retval [false: Rejoin not required, true: Rejoin required] + */ +static bool IsReJoin0Required( void ); + +/*! + * \brief Initializes and opens the reception window + * + * \param [IN] rxTimer Window timer to be topped. + * \param [IN] rxConfig Window parameters to be setup + */ +static void RxWindowSetup( TimerEvent_t* rxTimer, RxConfigParams_t* rxConfig ); + +/*! + * \brief Opens up a continuous RX C window. This is used for + * class c devices. + */ +static void OpenContinuousRxCWindow( void ); + +/*! + * \brief Returns a pointer to the internal contexts structure. + * + * \retval void Points to a structure containing all contexts + */ +static LoRaMacNvmData_t* GetNvmData( void ); + +/*! + * \brief Restoring of internal module contexts + * + * \details This function allows to restore module contexts by a given pointer. + * + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_PARAMETER_INVALID, + */ +static LoRaMacStatus_t RestoreNvmData( LoRaMacNvmData_t* contexts ); + +/*! + * \brief Determines the frame type + * + * \param [IN] macMsg Data message object + * + * \param [OUT] fType Frame type + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_PARAMETER_INVALID, + */ +LoRaMacStatus_t DetermineFrameType( LoRaMacMessageData_t* macMsg, FType_t* fType ); + +/*! + * \brief Verifies, if the retransmission counter has reached the limit + * + * \param [IN] counter Current retransmission counter + * \param [IN] limit Retransmission counter limit + * + * \retval Returns true if the number of retransmissions have reached the limit. + */ +static bool CheckRetrans( uint8_t counter, uint8_t limit ); + +/*! + * \brief Checks if the retransmission should be stopped in case of a unconfirmed uplink + * + * \retval Returns true if it should be stopped. + */ +static bool CheckRetransUnconfirmedUplink( void ); + +/*! + * \brief Checks if the retransmission should be stopped in case of a confirmed uplink + * + * \retval Returns true it should be stopped. + */ +static bool CheckRetransConfirmedUplink( void ); + +/*! + * \brief Increases the ADR ack counter. Takes the maximum + * value into account. + * + * \param [IN] counter Current counter value. + * + * \retval Returns the next counter value. + */ +static uint32_t IncreaseAdrAckCounter( uint32_t counter ); + +/*! + * \brief Stops the uplink retransmission + * + * \retval Returns true if successful. + */ +static bool StopRetransmission( void ); + +/*! + * \brief Calls the MacProcessNotify callback to indicate that a LoRaMacProcess call is pending + */ +static void OnMacProcessNotify( void ); + +/*! + * \brief Calls the callback to indicate that a context changed + */ +static void CallNvmDataChangeCallback( uint16_t notifyFlags ); + +/*! + * \brief Verifies if a request is pending currently + * + * \retval 1: Request pending, 0: request not pending + */ +static uint8_t IsRequestPending( void ); + +/*! + * \brief Enabled the possibility to perform requests + * + * \param [IN] requestState Request permission state + */ +static void LoRaMacEnableRequests( LoRaMacRequestHandling_t requestState ); + +/*! + * \brief This function verifies if a RX abort occurred + */ +static void LoRaMacCheckForRxAbort( void ); + +/*! + * \brief This function verifies if a beacon acquisition MLME + * request was pending + * + * \retval 1: Request pending, 0: no request pending + */ +static uint8_t LoRaMacCheckForBeaconAcquisition( void ); + +/*! + * \brief Returns true, if the device must apply the minium datarate + * + * \param [IN] adr ADR status bit + * + * \param [IN] activation Activation type of the device + * + * \param [IN] datarateChanged Set to true, if the datarate was changed + * with the LinkAdrReq. + */ +static bool CheckForMinimumAbpDatarate( bool adr, ActivationType_t activation, bool datarateChanged ); + +/*! + * \brief This function handles join request + */ +static void LoRaMacHandleMlmeRequest( void ); + +/*! + * \brief This function handles mcps request + */ +static void LoRaMacHandleMcpsRequest( void ); + +/*! + * \brief This function handles callback events for requests + */ +static void LoRaMacHandleRequestEvents( void ); + +/*! + * \brief This function handles callback events for indications + */ +static void LoRaMacHandleIndicationEvents( void ); + +/*! + * \brief This function handles events for re-join procedure + */ +static void LoRaMacHandleRejoinEvents( void ); + +/*! + * \brief This function handles callback events for NVM updates + * + * \param [IN] nvmData Data structure containing NVM data. + */ +static void LoRaMacHandleNvm( LoRaMacNvmData_t* nvmData ); + +/*! + * \brief This function verifies if the response timeout has been elapsed. If + * this is the case, the status of Nvm.MacGroup1.SrvAckRequested will be + * reset. + * + * \param [IN] timeoutInMs Timeout [ms] to be compared. + * + * \param [IN] startTimeInMs Start time [ms] used as a base. If set to 0, + * no comparison will be done. + * + * \retval true: Response timeout has been elapsed, false: Response timeout + * has not been elapsed or startTimeInMs is 0. + */ +static bool LoRaMacHandleResponseTimeout( TimerTime_t timeoutInMs, TimerTime_t startTimeInMs ); + +/*! + * Structure used to store the radio Tx event data + */ +struct +{ + TimerTime_t CurTime; +}TxDoneParams; + +/*! + * Structure used to store the radio Rx event data + */ +struct +{ + TimerTime_t LastRxDone; + uint8_t *Payload; + uint16_t Size; + int16_t Rssi; + int8_t Snr; +}RxDoneParams; + +static void OnRadioTxDone( void ) +{ + TxDoneParams.CurTime = TimerGetCurrentTime( ); + MacCtx.LastTxSysTime = SysTimeGet( ); + + LoRaMacRadioEvents.Events.TxDone = 1; + + OnMacProcessNotify( ); +} + +static void OnRadioRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ) +{ + RxDoneParams.LastRxDone = TimerGetCurrentTime( ); + RxDoneParams.Payload = payload; + RxDoneParams.Size = size; + RxDoneParams.Rssi = rssi; + RxDoneParams.Snr = snr; + + LoRaMacRadioEvents.Events.RxDone = 1; + LoRaMacRadioEvents.Events.RxProcessPending = 1; + + OnMacProcessNotify( ); +} + +static void OnRadioTxTimeout( void ) +{ + LoRaMacRadioEvents.Events.TxTimeout = 1; + + OnMacProcessNotify( ); +} + +static void OnRadioRxError( void ) +{ + LoRaMacRadioEvents.Events.RxError = 1; + + OnMacProcessNotify( ); +} + +static void OnRadioRxTimeout( void ) +{ + LoRaMacRadioEvents.Events.RxTimeout = 1; + + OnMacProcessNotify( ); +} + +static void UpdateRxSlotIdleState( void ) +{ + if( Nvm.MacGroup2.DeviceClass != CLASS_C ) + { + MacCtx.RxSlot = RX_SLOT_NONE; + } + else + { + MacCtx.RxSlot = RX_SLOT_WIN_CLASS_C; + } +} + +static void ProcessRadioTxDone( void ) +{ + GetPhyParams_t getPhy; + PhyParam_t phyParam; + SetBandTxDoneParams_t txDone; + + if( Nvm.MacGroup2.DeviceClass != CLASS_C ) + { + Radio.Sleep( ); + } + + // Setup timers + CRITICAL_SECTION_BEGIN( ); + uint32_t offset = TimerGetCurrentTime( ) - TxDoneParams.CurTime; + TimerSetValue( &MacCtx.RxWindowTimer1, MacCtx.RxWindow1Delay - offset ); + TimerStart( &MacCtx.RxWindowTimer1 ); + TimerSetValue( &MacCtx.RxWindowTimer2, MacCtx.RxWindow2Delay - offset ); + TimerStart( &MacCtx.RxWindowTimer2 ); + CRITICAL_SECTION_END( ); + + if( MacCtx.NodeAckRequested == true ) + { + getPhy.Attribute = PHY_RETRANSMIT_TIMEOUT; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + TimerSetValue( &MacCtx.RetransmitTimeoutTimer, MacCtx.RxWindow2Delay + phyParam.Value ); + TimerStart( &MacCtx.RetransmitTimeoutTimer ); + } + else + { + // Transmission successful, setup status directly + MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK; + } + + // Update Aggregated last tx done time + Nvm.MacGroup1.LastTxDoneTime = TxDoneParams.CurTime; + + // Update last tx done time for the current channel + txDone.Channel = MacCtx.Channel; + txDone.LastTxDoneTime = TxDoneParams.CurTime; + txDone.ElapsedTimeSinceStartUp = SysTimeSub( SysTimeGetMcuTime( ), Nvm.MacGroup2.InitializationTime ); + txDone.LastTxAirTime = MacCtx.TxTimeOnAir; + txDone.Joined = true; + if( Nvm.MacGroup2.NetworkActivation == ACTIVATION_TYPE_NONE ) + { + txDone.Joined = false; + } + + RegionSetBandTxDone( Nvm.MacGroup2.Region, &txDone ); +} + +static void PrepareRxDoneAbort( void ) +{ + MacCtx.MacState |= LORAMAC_RX_ABORT; + + if( MacCtx.NodeAckRequested == true ) + { + OnRetransmitTimeoutTimerEvent( NULL ); + } + + MacCtx.MacFlags.Bits.McpsInd = 1; + MacCtx.MacFlags.Bits.MacDone = 1; + + UpdateRxSlotIdleState( ); +} + +static void ProcessRadioRxDone( void ) +{ + LoRaMacHeader_t macHdr; + ApplyCFListParams_t applyCFList; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + LoRaMacCryptoStatus_t macCryptoStatus = LORAMAC_CRYPTO_ERROR; + + LoRaMacMessageData_t macMsgData; + LoRaMacMessageJoinAccept_t macMsgJoinAccept; + uint8_t *payload = RxDoneParams.Payload; + uint16_t size = RxDoneParams.Size; + int16_t rssi = RxDoneParams.Rssi; + int8_t snr = RxDoneParams.Snr; + + uint8_t pktHeaderLen = 0; + + uint32_t downLinkCounter = 0; + uint32_t address = Nvm.MacGroup2.DevAddr; + uint8_t multicast = 0; + AddressIdentifier_t addrID = UNICAST_DEV_ADDR; + FCntIdentifier_t fCntID; + uint8_t macCmdPayload[2] = { 0 }; + Mlme_t joinType = MLME_JOIN; + + LoRaMacRadioEvents.Events.RxProcessPending = 0; + + MacCtx.McpsConfirm.AckReceived = false; + MacCtx.McpsIndication.Rssi = rssi; + MacCtx.McpsIndication.Snr = snr; + MacCtx.McpsIndication.RxSlot = MacCtx.RxSlot; + MacCtx.McpsIndication.Port = 0; + MacCtx.McpsIndication.Multicast = 0; + MacCtx.McpsIndication.IsUplinkTxPending = 0; + MacCtx.McpsIndication.Buffer = NULL; + MacCtx.McpsIndication.BufferSize = 0; + MacCtx.McpsIndication.RxData = false; + MacCtx.McpsIndication.AckReceived = false; + MacCtx.McpsIndication.DownLinkCounter = 0; + MacCtx.McpsIndication.McpsIndication = MCPS_UNCONFIRMED; + MacCtx.McpsIndication.DevAddress = 0; + MacCtx.McpsIndication.DeviceTimeAnsReceived = false; + MacCtx.McpsIndication.ResponseTimeout = 0; + + Radio.Sleep( ); + + if( MacCtx.McpsIndication.RxSlot == RX_SLOT_WIN_1 ) + { + TimerStop( &MacCtx.RxWindowTimer2 ); + } + + // This function must be called even if we are not in class b mode yet. + if( LoRaMacClassBRxBeacon( payload, size ) == true ) + { + MacCtx.MlmeIndication.BeaconInfo.Rssi = rssi; + MacCtx.MlmeIndication.BeaconInfo.Snr = snr; + return; + } + // Check if we expect a ping or a multicast slot. + if( Nvm.MacGroup2.DeviceClass == CLASS_B ) + { + if( LoRaMacClassBIsPingExpected( ) == true ) + { + LoRaMacClassBSetPingSlotState( PINGSLOT_STATE_CALC_PING_OFFSET ); + LoRaMacClassBPingSlotTimerEvent( NULL ); + MacCtx.McpsIndication.RxSlot = RX_SLOT_WIN_CLASS_B_PING_SLOT; + } + if( LoRaMacClassBIsMulticastExpected( ) == true ) + { + LoRaMacClassBSetMulticastSlotState( PINGSLOT_STATE_CALC_PING_OFFSET ); + LoRaMacClassBMulticastSlotTimerEvent( NULL ); + MacCtx.McpsIndication.RxSlot = RX_SLOT_WIN_CLASS_B_MULTICAST_SLOT; + } + } + + // Abort on empty radio frames + if( size == 0 ) + { + MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + PrepareRxDoneAbort( ); + return; + } + + macHdr.Value = payload[pktHeaderLen++]; + + // Accept frames of LoRaWAN Major Version 1 only + if( macHdr.Bits.Major != 0 ) + { + MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + PrepareRxDoneAbort( ); + return; + } + + switch( macHdr.Bits.MType ) + { + case FRAME_TYPE_JOIN_ACCEPT: + { + // Check if the received frame size is valid + if( size < LORAMAC_JOIN_ACCEPT_FRAME_MIN_SIZE ) + { + MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + PrepareRxDoneAbort( ); + return; + } + macMsgJoinAccept.Buffer = payload; + macMsgJoinAccept.BufSize = size; + + // Abort in case if the device is already joined and no rejoin request is ongoing. + if( ( Nvm.MacGroup2.NetworkActivation != ACTIVATION_TYPE_NONE ) && ( Nvm.MacGroup2.IsRejoinAcceptPending == false ) ) + { + MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + PrepareRxDoneAbort( ); + return; + } + macCryptoStatus = LoRaMacCryptoHandleJoinAccept( JOIN_REQ, SecureElementGetJoinEui( ), &macMsgJoinAccept ); + + if( LORAMAC_CRYPTO_SUCCESS != macCryptoStatus ) + { + macCryptoStatus = LoRaMacCryptoHandleJoinAccept( REJOIN_REQ_0, SecureElementGetJoinEui( ), &macMsgJoinAccept ); + joinType = MLME_REJOIN_0; + } + + if( LORAMAC_CRYPTO_SUCCESS != macCryptoStatus ) + { + macCryptoStatus = LoRaMacCryptoHandleJoinAccept( REJOIN_REQ_1, SecureElementGetJoinEui( ), &macMsgJoinAccept ); + joinType = MLME_REJOIN_1; + } + + if( LORAMAC_CRYPTO_SUCCESS != macCryptoStatus ) + { + macCryptoStatus = LoRaMacCryptoHandleJoinAccept( REJOIN_REQ_2, SecureElementGetJoinEui( ), &macMsgJoinAccept ); + joinType = MLME_REJOIN_2; + } + + if( LORAMAC_CRYPTO_SUCCESS == macCryptoStatus ) + { + VerifyParams_t verifyRxDr; + + if( macMsgJoinAccept.DLSettings.Bits.RX2DataRate != 0x0F ) + { + verifyRxDr.DatarateParams.Datarate = macMsgJoinAccept.DLSettings.Bits.RX2DataRate; + verifyRxDr.DatarateParams.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime; + if( RegionVerify( Nvm.MacGroup2.Region, &verifyRxDr, PHY_RX_DR ) == false ) + { + // MLME handling + if( LoRaMacConfirmQueueIsCmdActive( MLME_JOIN ) == true ) + { + LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL, MLME_JOIN ); + } + break; + } + } + + // Network ID + Nvm.MacGroup2.NetID = ( uint32_t ) macMsgJoinAccept.NetID[0]; + Nvm.MacGroup2.NetID |= ( ( uint32_t ) macMsgJoinAccept.NetID[1] << 8 ); + Nvm.MacGroup2.NetID |= ( ( uint32_t ) macMsgJoinAccept.NetID[2] << 16 ); + + // Device Address + Nvm.MacGroup2.DevAddr = macMsgJoinAccept.DevAddr; + + // DLSettings + Nvm.MacGroup2.MacParams.Rx1DrOffset = macMsgJoinAccept.DLSettings.Bits.RX1DRoffset; + + // Verify if we shall assign the new datarate + if( macMsgJoinAccept.DLSettings.Bits.RX2DataRate != 0x0F ) + { + Nvm.MacGroup2.MacParams.Rx2Channel.Datarate = macMsgJoinAccept.DLSettings.Bits.RX2DataRate; + Nvm.MacGroup2.MacParams.RxCChannel.Datarate = macMsgJoinAccept.DLSettings.Bits.RX2DataRate; + } + + // RxDelay + Nvm.MacGroup2.MacParams.ReceiveDelay1 = macMsgJoinAccept.RxDelay; + if( Nvm.MacGroup2.MacParams.ReceiveDelay1 == 0 ) + { + Nvm.MacGroup2.MacParams.ReceiveDelay1 = 1; + } + Nvm.MacGroup2.MacParams.ReceiveDelay1 *= 1000; + Nvm.MacGroup2.MacParams.ReceiveDelay2 = Nvm.MacGroup2.MacParams.ReceiveDelay1 + 1000; + + // Reset NbTrans to default value + Nvm.MacGroup2.MacParams.ChannelsNbTrans = 1; + + // Is Networkserver's LoRaWAN Version before 1.1.0 ? + if( macMsgJoinAccept.DLSettings.Bits.OptNeg == 0 ) + { + Nvm.MacGroup2.Version.Value = LORAMAC_FALLBACK_VERSION; + } + else + { + Nvm.MacGroup2.Version.Value = LORAMAC_VERSION; + } + + // Apply CF list + applyCFList.Payload = macMsgJoinAccept.CFList; + // Size of the regular payload is 12. Plus 1 byte MHDR and 4 bytes MIC + applyCFList.Size = size - 17; + // Apply the last tx channel + applyCFList.JoinChannel = MacCtx.Channel; + + RegionApplyCFList( Nvm.MacGroup2.Region, &applyCFList ); + + Nvm.MacGroup2.NetworkActivation = ACTIVATION_TYPE_OTAA; + + // Add a RekeyInd MAC command to confirm the security key update. + if( Nvm.MacGroup2.Version.Fields.Minor >= 1 ) + { + Nvm.MacGroup1.RekeyIndUplinksCounter = 0; + macCmdPayload[0] = Nvm.MacGroup2.Version.Fields.Minor; + LoRaMacCommandsAddCmd( MOTE_MAC_REKEY_IND, macCmdPayload, 1 ); + } + + // MLME handling + if( LoRaMacConfirmQueueIsCmdActive( joinType ) == true ) + { + LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, joinType ); + } + + // Rejoin handling + if( Nvm.MacGroup2.IsRejoinAcceptPending == true ) + { + Nvm.MacGroup2.IsRejoinAcceptPending = false; + + // Stop in any case the ForceRejoinReqCycleTimer + TimerStop( &MacCtx.ForceRejoinReqCycleTimer ); + } + + // Reset MAC parameters for specific re-join types + if( ( joinType == MLME_REJOIN_0 ) || ( joinType == MLME_REJOIN_1 ) ) + { + ResetMacParameters( true ); + } + } + else + { + // MLME handling + if( LoRaMacConfirmQueueIsCmdActive( MLME_JOIN ) == true ) + { + LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL, MLME_JOIN ); + } + } + break; + } + case FRAME_TYPE_DATA_CONFIRMED_DOWN: + MacCtx.McpsIndication.McpsIndication = MCPS_CONFIRMED; + // Intentional fall through + case FRAME_TYPE_DATA_UNCONFIRMED_DOWN: + // Check if the received payload size is valid + getPhy.UplinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime; + getPhy.Datarate = MacCtx.McpsIndication.RxDatarate; + getPhy.Attribute = PHY_MAX_PAYLOAD; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + if( ( MAX( 0, ( int16_t )( ( int16_t ) size - ( int16_t ) LORAMAC_FRAME_PAYLOAD_OVERHEAD_SIZE ) ) > ( int16_t )phyParam.Value ) || + ( size < LORAMAC_FRAME_PAYLOAD_MIN_SIZE ) ) + { + MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + PrepareRxDoneAbort( ); + return; + } + macMsgData.Buffer = payload; + macMsgData.BufSize = size; + macMsgData.FRMPayload = MacCtx.RxPayload; + macMsgData.FRMPayloadSize = LORAMAC_PHY_MAXPAYLOAD; + + if( LORAMAC_PARSER_SUCCESS != LoRaMacParserData( &macMsgData ) ) + { + MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + PrepareRxDoneAbort( ); + return; + } + + // Handle Class B + // Check if we expect a ping or a multicast slot. + if( Nvm.MacGroup2.DeviceClass == CLASS_B ) + { + if( LoRaMacClassBIsPingExpected( ) == true ) + { + LoRaMacClassBSetPingSlotState( PINGSLOT_STATE_CALC_PING_OFFSET ); + LoRaMacClassBPingSlotTimerEvent( NULL ); + MacCtx.McpsIndication.RxSlot = RX_SLOT_WIN_CLASS_B_PING_SLOT; + LoRaMacClassBSetFPendingBit( macMsgData.FHDR.DevAddr, ( uint8_t ) macMsgData.FHDR.FCtrl.Bits.FPending ); + } + if( LoRaMacClassBIsMulticastExpected( ) == true ) + { + LoRaMacClassBSetMulticastSlotState( PINGSLOT_STATE_CALC_PING_OFFSET ); + LoRaMacClassBMulticastSlotTimerEvent( NULL ); + MacCtx.McpsIndication.RxSlot = RX_SLOT_WIN_CLASS_B_MULTICAST_SLOT; + LoRaMacClassBSetFPendingBit( macMsgData.FHDR.DevAddr, ( uint8_t ) macMsgData.FHDR.FCtrl.Bits.FPending ); + } + } + + // Store device address + MacCtx.McpsIndication.DevAddress = macMsgData.FHDR.DevAddr; + + FType_t fType; + if( LORAMAC_STATUS_OK != DetermineFrameType( &macMsgData, &fType ) ) + { + MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + PrepareRxDoneAbort( ); + return; + } + + //Check if it is a multicast message + multicast = 0; + downLinkCounter = 0; + for( uint8_t i = 0; i < LORAMAC_MAX_MC_CTX; i++ ) + { + if( ( Nvm.MacGroup2.MulticastChannelList[i].ChannelParams.Address == macMsgData.FHDR.DevAddr ) && + ( Nvm.MacGroup2.MulticastChannelList[i].ChannelParams.IsEnabled == true ) ) + { + multicast = 1; + addrID = Nvm.MacGroup2.MulticastChannelList[i].ChannelParams.GroupID; + downLinkCounter = *( Nvm.MacGroup2.MulticastChannelList[i].DownLinkCounter ); + address = Nvm.MacGroup2.MulticastChannelList[i].ChannelParams.Address; + if( Nvm.MacGroup2.DeviceClass == CLASS_C ) + { + MacCtx.McpsIndication.RxSlot = RX_SLOT_WIN_CLASS_C_MULTICAST; + } + break; + } + } + + // Filter messages according to multicast downlink exceptions + if( ( multicast == 1 ) && ( (macHdr.Bits.MType == FRAME_TYPE_DATA_CONFIRMED_DOWN ) || + ( fType != FRAME_TYPE_D ) || + ( macMsgData.FHDR.FCtrl.Bits.Ack != 0 ) || + ( macMsgData.FHDR.FCtrl.Bits.AdrAckReq != 0 ) ) ) + { + MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + PrepareRxDoneAbort( ); + return; + } + + // Get downlink frame counter value + macCryptoStatus = GetFCntDown( addrID, fType, &macMsgData, Nvm.MacGroup2.Version, &fCntID, &downLinkCounter ); + if( macCryptoStatus != LORAMAC_CRYPTO_SUCCESS ) + { + if( macCryptoStatus == LORAMAC_CRYPTO_FAIL_FCNT_DUPLICATED ) + { + // Catch the case of repeated downlink frame counter + MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED; + } + else + { + // Other errors + MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + } + MacCtx.McpsIndication.DownLinkCounter = downLinkCounter; + PrepareRxDoneAbort( ); + return; + } + + if( multicast == 1 ) + { + if( ( downLinkCounter < Nvm.MacGroup2.MulticastChannelList[addrID].ChannelParams.FCountMin )|| + ( downLinkCounter > Nvm.MacGroup2.MulticastChannelList[addrID].ChannelParams.FCountMax ) ) + { + // Other errors + MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + MacCtx.McpsIndication.DownLinkCounter = downLinkCounter; + PrepareRxDoneAbort( ); + return; + } + } + + macCryptoStatus = LoRaMacCryptoUnsecureMessage( addrID, address, fCntID, downLinkCounter, &macMsgData ); + if( macCryptoStatus != LORAMAC_CRYPTO_SUCCESS ) + { + if( macCryptoStatus == LORAMAC_CRYPTO_FAIL_ADDRESS ) + { + // We are not the destination of this frame. + MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL; + } + else + { + // MIC calculation fail + MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_MIC_FAIL; + } + PrepareRxDoneAbort( ); + return; + } + + MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_OK; + MacCtx.McpsIndication.Multicast = multicast; + MacCtx.McpsIndication.Buffer = NULL; + MacCtx.McpsIndication.BufferSize = 0; + MacCtx.McpsIndication.DownLinkCounter = downLinkCounter; + MacCtx.McpsIndication.AckReceived = macMsgData.FHDR.FCtrl.Bits.Ack; + + MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK; + MacCtx.McpsConfirm.AckReceived = macMsgData.FHDR.FCtrl.Bits.Ack; + + // Reset ADR ACK Counter only, when RX1 or RX2 slot + if( ( MacCtx.McpsIndication.RxSlot == RX_SLOT_WIN_1 ) || + ( MacCtx.McpsIndication.RxSlot == RX_SLOT_WIN_2 ) ) + { + Nvm.MacGroup1.AdrAckCounter = 0; + Nvm.MacGroup2.DownlinkReceived = true; + } + + // MCPS Indication and ack requested handling + if( multicast == 1 ) + { + MacCtx.McpsIndication.McpsIndication = MCPS_MULTICAST; + } + else + { + if( macHdr.Bits.MType == FRAME_TYPE_DATA_CONFIRMED_DOWN ) + { + Nvm.MacGroup1.SrvAckRequested = true; + if( Nvm.MacGroup2.Version.Fields.Minor == 0 ) + { + Nvm.MacGroup1.LastRxMic = macMsgData.MIC; + } + MacCtx.McpsIndication.McpsIndication = MCPS_CONFIRMED; + + // Handle response timeout for class c and class b downlinks + if( ( MacCtx.McpsIndication.RxSlot != RX_SLOT_WIN_1 ) && + ( MacCtx.McpsIndication.RxSlot != RX_SLOT_WIN_2 ) ) + { + // Calculate timeout + MacCtx.McpsIndication.ResponseTimeout = REGION_COMMON_CLASS_B_C_RESP_TIMEOUT; + MacCtx.ResponseTimeoutStartTime = RxDoneParams.LastRxDone; + } + } + else + { + Nvm.MacGroup1.SrvAckRequested = false; + MacCtx.McpsIndication.McpsIndication = MCPS_UNCONFIRMED; + } + } + + // Set the pending status + if( ( ( ( Nvm.MacGroup1.SrvAckRequested == true ) || ( macMsgData.FHDR.FCtrl.Bits.FPending > 0 ) ) && ( Nvm.MacGroup2.DeviceClass == CLASS_A ) ) || + ( MacCtx.McpsIndication.ResponseTimeout > 0 ) ) + { + MacCtx.McpsIndication.IsUplinkTxPending = 1; + } + + RemoveMacCommands( MacCtx.McpsIndication.RxSlot, macMsgData.FHDR.FCtrl, MacCtx.McpsConfirm.McpsRequest ); + + switch( fType ) + { + case FRAME_TYPE_A: + { /* +----------+------+-------+--------------+ + * | FOptsLen | Fopt | FPort | FRMPayload | + * +----------+------+-------+--------------+ + * | > 0 | X | > 0 | X | + * +----------+------+-------+--------------+ + */ + + // Decode MAC commands in FOpts field + ProcessMacCommands( macMsgData.FHDR.FOpts, 0, macMsgData.FHDR.FCtrl.Bits.FOptsLen, snr, MacCtx.McpsIndication.RxSlot ); + MacCtx.McpsIndication.Port = macMsgData.FPort; + MacCtx.McpsIndication.Buffer = macMsgData.FRMPayload; + MacCtx.McpsIndication.BufferSize = macMsgData.FRMPayloadSize; + MacCtx.McpsIndication.RxData = true; + break; + } + case FRAME_TYPE_B: + { /* +----------+------+-------+--------------+ + * | FOptsLen | Fopt | FPort | FRMPayload | + * +----------+------+-------+--------------+ + * | > 0 | X | - | - | + * +----------+------+-------+--------------+ + */ + + // Decode MAC commands in FOpts field + ProcessMacCommands( macMsgData.FHDR.FOpts, 0, macMsgData.FHDR.FCtrl.Bits.FOptsLen, snr, MacCtx.McpsIndication.RxSlot ); + MacCtx.McpsIndication.Port = macMsgData.FPort; + break; + } + case FRAME_TYPE_C: + { /* +----------+------+-------+--------------+ + * | FOptsLen | Fopt | FPort | FRMPayload | + * +----------+------+-------+--------------+ + * | = 0 | - | = 0 | MAC commands | + * +----------+------+-------+--------------+ + */ + + // Decode MAC commands in FRMPayload + ProcessMacCommands( macMsgData.FRMPayload, 0, macMsgData.FRMPayloadSize, snr, MacCtx.McpsIndication.RxSlot ); + MacCtx.McpsIndication.Port = macMsgData.FPort; + break; + } + case FRAME_TYPE_D: + { /* +----------+------+-------+--------------+ + * | FOptsLen | Fopt | FPort | FRMPayload | + * +----------+------+-------+--------------+ + * | = 0 | - | > 0 | X | + * +----------+------+-------+--------------+ + */ + + // No MAC commands just application payload + MacCtx.McpsIndication.Port = macMsgData.FPort; + MacCtx.McpsIndication.Buffer = macMsgData.FRMPayload; + MacCtx.McpsIndication.BufferSize = macMsgData.FRMPayloadSize; + MacCtx.McpsIndication.RxData = true; + break; + } + default: + MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + PrepareRxDoneAbort( ); + break; + } + + // Rejoin handling + if( Nvm.MacGroup2.IsRejoinAcceptPending == true ) + { + Nvm.MacGroup2.IsRejoinAcceptPending = false; + + // Stop in any case the ForceRejoinReqCycleTimer + TimerStop( &MacCtx.ForceRejoinReqCycleTimer ); + + // If the rejoin was trigged by MLME, set confirmation status + if( MacCtx.MacFlags.Bits.MlmeReq == 1 ) + { + MacCtx.MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK; + } + } + + if( ( macMsgData.FPort == LORAMAC_CERT_FPORT ) && (Nvm.MacGroup2.IsCertPortOn == false ) ) + { // Do not notify the upper layer of data reception on FPort LORAMAC_CERT_FPORT if the port + // handling is disabled. + MacCtx.McpsIndication.Port = macMsgData.FPort; + MacCtx.McpsIndication.Buffer = NULL; + MacCtx.McpsIndication.BufferSize = 0; + MacCtx.McpsIndication.RxData = false; + } + + // Provide always an indication, skip the callback to the user application, + // in case of a confirmed downlink retransmission. + MacCtx.MacFlags.Bits.McpsInd = 1; + + break; + case FRAME_TYPE_PROPRIETARY: + memcpy1( MacCtx.RxPayload, &payload[pktHeaderLen], size - pktHeaderLen ); + + MacCtx.McpsIndication.McpsIndication = MCPS_PROPRIETARY; + MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_OK; + MacCtx.McpsIndication.Buffer = MacCtx.RxPayload; + MacCtx.McpsIndication.BufferSize = size - pktHeaderLen; + + MacCtx.MacFlags.Bits.McpsInd = 1; + break; + default: + MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + PrepareRxDoneAbort( ); + break; + } + + // Verify if we need to disable the RetransmitTimeoutTimer + // Only aplies if downlink is received on Rx1 or Rx2 windows. + if( ( MacCtx.McpsIndication.RxSlot == RX_SLOT_WIN_1 ) || + ( MacCtx.McpsIndication.RxSlot == RX_SLOT_WIN_2 ) ) + { + if( MacCtx.NodeAckRequested == true ) + { + if( MacCtx.McpsConfirm.AckReceived == true ) + { + OnRetransmitTimeoutTimerEvent( NULL ); + } + } + } + + if( MacCtx.McpsIndication.RxSlot != RX_SLOT_WIN_CLASS_C ) + { + MacCtx.MacFlags.Bits.MacDone = 1; + } + + UpdateRxSlotIdleState( ); +} + +static void ProcessRadioTxTimeout( void ) +{ + if( Nvm.MacGroup2.DeviceClass != CLASS_C ) + { + Radio.Sleep( ); + } + UpdateRxSlotIdleState( ); + + MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT; + LoRaMacConfirmQueueSetStatusCmn( LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT ); + if( MacCtx.NodeAckRequested == true ) + { + MacCtx.RetransmitTimeoutRetry = true; + } + MacCtx.MacFlags.Bits.MacDone = 1; +} + +static void HandleRadioRxErrorTimeout( LoRaMacEventInfoStatus_t rx1EventInfoStatus, LoRaMacEventInfoStatus_t rx2EventInfoStatus ) +{ + bool classBRx = false; + + if( Nvm.MacGroup2.DeviceClass != CLASS_C ) + { + Radio.Sleep( ); + } + + if( LoRaMacClassBIsBeaconExpected( ) == true ) + { + LoRaMacClassBSetBeaconState( BEACON_STATE_TIMEOUT ); + LoRaMacClassBBeaconTimerEvent( NULL ); + classBRx = true; + } + if( Nvm.MacGroup2.DeviceClass == CLASS_B ) + { + if( LoRaMacClassBIsPingExpected( ) == true ) + { + LoRaMacClassBSetPingSlotState( PINGSLOT_STATE_CALC_PING_OFFSET ); + LoRaMacClassBPingSlotTimerEvent( NULL ); + classBRx = true; + } + if( LoRaMacClassBIsMulticastExpected( ) == true ) + { + LoRaMacClassBSetMulticastSlotState( PINGSLOT_STATE_CALC_PING_OFFSET ); + LoRaMacClassBMulticastSlotTimerEvent( NULL ); + classBRx = true; + } + } + + if( classBRx == false ) + { + if( MacCtx.RxSlot == RX_SLOT_WIN_1 ) + { + if( MacCtx.NodeAckRequested == true ) + { + MacCtx.McpsConfirm.Status = rx1EventInfoStatus; + } + LoRaMacConfirmQueueSetStatusCmn( rx1EventInfoStatus ); + + if( TimerGetElapsedTime( Nvm.MacGroup1.LastTxDoneTime ) >= MacCtx.RxWindow2Delay ) + { + TimerStop( &MacCtx.RxWindowTimer2 ); + MacCtx.MacFlags.Bits.MacDone = 1; + } + } + else + { + if( MacCtx.NodeAckRequested == true ) + { + MacCtx.McpsConfirm.Status = rx2EventInfoStatus; + } + LoRaMacConfirmQueueSetStatusCmn( rx2EventInfoStatus ); + MacCtx.MacFlags.Bits.MacDone = 1; + } + } + + UpdateRxSlotIdleState( ); +} + +static void ProcessRadioRxError( void ) +{ + HandleRadioRxErrorTimeout( LORAMAC_EVENT_INFO_STATUS_RX1_ERROR, LORAMAC_EVENT_INFO_STATUS_RX2_ERROR ); +} + +static void ProcessRadioRxTimeout( void ) +{ + HandleRadioRxErrorTimeout( LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT, LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT ); +} + +static void LoRaMacHandleIrqEvents( void ) +{ + LoRaMacRadioEvents_t events; + + CRITICAL_SECTION_BEGIN( ); + events = LoRaMacRadioEvents; + LoRaMacRadioEvents.Value = 0; + CRITICAL_SECTION_END( ); + + if( events.Value != 0 ) + { + if( events.Events.TxDone == 1 ) + { + ProcessRadioTxDone( ); + } + if( events.Events.RxDone == 1 ) + { + ProcessRadioRxDone( ); + } + if( events.Events.TxTimeout == 1 ) + { + ProcessRadioTxTimeout( ); + } + if( events.Events.RxError == 1 ) + { + ProcessRadioRxError( ); + } + if( events.Events.RxTimeout == 1 ) + { + ProcessRadioRxTimeout( ); + } + } +} + +bool LoRaMacIsBusy( void ) +{ + if( MacCtx.MacState == LORAMAC_STOPPED ) + { + return false; + } + + if( LoRaMacRadioEvents.Events.RxProcessPending == 1 ) + { + return true; + } + + if( ( MacCtx.MacState == LORAMAC_IDLE ) && + ( MacCtx.AllowRequests == LORAMAC_REQUEST_HANDLING_ON ) ) + { + return false; + } + return true; +} + + +static void LoRaMacEnableRequests( LoRaMacRequestHandling_t requestState ) +{ + MacCtx.AllowRequests = requestState; +} + +static void LoRaMacHandleRequestEvents( void ) +{ + // Handle events + LoRaMacFlags_t reqEvents = MacCtx.MacFlags; + + if( MacCtx.MacState == LORAMAC_IDLE ) + { + // Update event bits + if( MacCtx.MacFlags.Bits.McpsReq == 1 ) + { + MacCtx.MacFlags.Bits.McpsReq = 0; + } + + if( MacCtx.MacFlags.Bits.MlmeReq == 1 ) + { + MacCtx.MacFlags.Bits.MlmeReq = 0; + } + + // Allow requests again + LoRaMacEnableRequests( LORAMAC_REQUEST_HANDLING_ON ); + + // Handle callbacks + if( reqEvents.Bits.McpsReq == 1 ) + { + MacCtx.MacPrimitives->MacMcpsConfirm( &MacCtx.McpsConfirm ); + } + + if( reqEvents.Bits.MlmeReq == 1 ) + { + LoRaMacConfirmQueueHandleCb( &MacCtx.MlmeConfirm ); + if( LoRaMacConfirmQueueGetCnt( ) > 0 ) + { + MacCtx.MacFlags.Bits.MlmeReq = 1; + } + } + + // Start beaconing again + LoRaMacClassBResumeBeaconing( ); + + // Procedure done. Reset variables. + MacCtx.MacFlags.Bits.MacDone = 0; + } +} + +static void LoRaMacHandleIndicationEvents( void ) +{ + // Handle MLME indication + if( MacCtx.MacFlags.Bits.MlmeInd == 1 ) + { + MacCtx.MacFlags.Bits.MlmeInd = 0; + MacCtx.MacPrimitives->MacMlmeIndication( &MacCtx.MlmeIndication ); + } + + // Handle MCPS indication + if( MacCtx.MacFlags.Bits.McpsInd == 1 ) + { + MacCtx.MacFlags.Bits.McpsInd = 0; + MacCtx.MacPrimitives->MacMcpsIndication( &MacCtx.McpsIndication ); + } +} + +static void LoRaMacHandleRejoinEvents( void ) +{ + if( MacCtx.MacState == LORAMAC_IDLE ) + { + MlmeReq_t mlmeReq; + if( IsReJoin0Required( ) == true ) + { + mlmeReq.Type = MLME_REJOIN_0; + LoRaMacMlmeRequest( &mlmeReq ); + } + else if( Nvm.MacGroup2.IsRejoin0RequestQueued == true ) + { + mlmeReq.Type = MLME_REJOIN_0; + if( LoRaMacMlmeRequest( &mlmeReq ) == LORAMAC_STATUS_OK ) + { + Nvm.MacGroup2.IsRejoin0RequestQueued = false; + } + } + else if( Nvm.MacGroup2.IsRejoin1RequestQueued == true ) + { + mlmeReq.Type = MLME_REJOIN_1; + if( LoRaMacMlmeRequest( &mlmeReq ) == LORAMAC_STATUS_OK ) + { + Nvm.MacGroup2.IsRejoin1RequestQueued = false; + } + } + else if( Nvm.MacGroup2.IsRejoin2RequestQueued == true ) + { + mlmeReq.Type = MLME_REJOIN_2; + if( LoRaMacMlmeRequest( &mlmeReq ) == LORAMAC_STATUS_OK ) + { + Nvm.MacGroup2.IsRejoin2RequestQueued = false; + } + } + } +} + +static void LoRaMacHandleMcpsRequest( void ) +{ + // Handle MCPS uplinks + if( MacCtx.MacFlags.Bits.McpsReq == 1 ) + { + bool stopRetransmission = false; + bool waitForRetransmission = false; + + if( ( MacCtx.McpsConfirm.McpsRequest == MCPS_UNCONFIRMED ) || + ( MacCtx.McpsConfirm.McpsRequest == MCPS_PROPRIETARY ) ) + { + stopRetransmission = CheckRetransUnconfirmedUplink( ); + } + else if( MacCtx.McpsConfirm.McpsRequest == MCPS_CONFIRMED ) + { + if( MacCtx.RetransmitTimeoutRetry == true ) + { + stopRetransmission = CheckRetransConfirmedUplink( ); + } + else + { + waitForRetransmission = true; + } + } + + if( stopRetransmission == true ) + {// Stop retransmission + TimerStop( &MacCtx.TxDelayedTimer ); + MacCtx.MacState &= ~LORAMAC_TX_DELAYED; + StopRetransmission( ); + } + else if( waitForRetransmission == false ) + {// Arrange further retransmission + MacCtx.MacFlags.Bits.MacDone = 0; + // Reset the state of the AckTimeout + MacCtx.RetransmitTimeoutRetry = false; + // Sends the same frame again + OnTxDelayedTimerEvent( NULL ); + } + } +} + +static void LoRaMacHandleMlmeRequest( void ) +{ + // Handle join request + if( MacCtx.MacFlags.Bits.MlmeReq == 1 ) + { + if( ( LoRaMacConfirmQueueIsCmdActive( MLME_JOIN ) == true ) || + ( LoRaMacConfirmQueueIsCmdActive( MLME_REJOIN_0 ) == true ) || + ( LoRaMacConfirmQueueIsCmdActive( MLME_REJOIN_1 ) == true ) || + ( LoRaMacConfirmQueueIsCmdActive( MLME_REJOIN_2 ) == true ) ) + { + MacCtx.ChannelsNbTransCounter = 0; + MacCtx.MacState &= ~LORAMAC_TX_RUNNING; + } + else if( LoRaMacConfirmQueueIsCmdActive( MLME_TXCW ) == true ) + { + MacCtx.MacState &= ~LORAMAC_TX_RUNNING; + } + } +} + +static uint8_t LoRaMacCheckForBeaconAcquisition( void ) +{ + if( ( LoRaMacConfirmQueueIsCmdActive( MLME_BEACON_ACQUISITION ) == true ) && + ( MacCtx.MacFlags.Bits.McpsReq == 0 ) ) + { + if( MacCtx.MacFlags.Bits.MlmeReq == 1 ) + { + MacCtx.MacState &= ~LORAMAC_TX_RUNNING; + return 0x01; + } + } + return 0x00; +} + +static bool CheckForMinimumAbpDatarate( bool adr, ActivationType_t activation, bool datarateChanged ) +{ + if( ( adr == true ) && + ( activation == ACTIVATION_TYPE_ABP ) && + ( datarateChanged == false ) ) + { + return true; + } + return false; +} + +static void LoRaMacCheckForRxAbort( void ) +{ + // A error occurs during receiving + if( ( MacCtx.MacState & LORAMAC_RX_ABORT ) == LORAMAC_RX_ABORT ) + { + MacCtx.MacState &= ~LORAMAC_RX_ABORT; + MacCtx.MacState &= ~LORAMAC_TX_RUNNING; + } +} + +static void LoRaMacHandleNvm( LoRaMacNvmData_t* nvmData ) +{ + uint32_t crc = 0; + uint16_t notifyFlags = LORAMAC_NVM_NOTIFY_FLAG_NONE; + + if( MacCtx.MacState != LORAMAC_IDLE ) + { + return; + } + + // Crypto + crc = Crc32( ( uint8_t* ) &nvmData->Crypto, sizeof( nvmData->Crypto ) - + sizeof( nvmData->Crypto.Crc32 ) ); + if( crc != nvmData->Crypto.Crc32 ) + { + nvmData->Crypto.Crc32 = crc; + notifyFlags |= LORAMAC_NVM_NOTIFY_FLAG_CRYPTO; + } + + // MacGroup1 + crc = Crc32( ( uint8_t* ) &nvmData->MacGroup1, sizeof( nvmData->MacGroup1 ) - + sizeof( nvmData->MacGroup1.Crc32 ) ); + if( crc != nvmData->MacGroup1.Crc32 ) + { + nvmData->MacGroup1.Crc32 = crc; + notifyFlags |= LORAMAC_NVM_NOTIFY_FLAG_MAC_GROUP1; + } + + // MacGroup2 + crc = Crc32( ( uint8_t* ) &nvmData->MacGroup2, sizeof( nvmData->MacGroup2 ) - + sizeof( nvmData->MacGroup2.Crc32 ) ); + if( crc != nvmData->MacGroup2.Crc32 ) + { + nvmData->MacGroup2.Crc32 = crc; + notifyFlags |= LORAMAC_NVM_NOTIFY_FLAG_MAC_GROUP2; + } + + // Secure Element + crc = Crc32( ( uint8_t* ) &nvmData->SecureElement, sizeof( nvmData->SecureElement ) - + sizeof( nvmData->SecureElement.Crc32 ) ); + if( crc != nvmData->SecureElement.Crc32 ) + { + nvmData->SecureElement.Crc32 = crc; + notifyFlags |= LORAMAC_NVM_NOTIFY_FLAG_SECURE_ELEMENT; + } + + // Region + crc = Crc32( ( uint8_t* ) &nvmData->RegionGroup1, sizeof( nvmData->RegionGroup1 ) - + sizeof( nvmData->RegionGroup1.Crc32 ) ); + if( crc != nvmData->RegionGroup1.Crc32 ) + { + nvmData->RegionGroup1.Crc32 = crc; + notifyFlags |= LORAMAC_NVM_NOTIFY_FLAG_REGION_GROUP1; + } + + crc = Crc32( ( uint8_t* ) &nvmData->RegionGroup2, sizeof( nvmData->RegionGroup2 ) - + sizeof( nvmData->RegionGroup2.Crc32 ) ); + if( crc != nvmData->RegionGroup2.Crc32 ) + { + nvmData->RegionGroup2.Crc32 = crc; + notifyFlags |= LORAMAC_NVM_NOTIFY_FLAG_REGION_GROUP2; + } + + // ClassB + crc = Crc32( ( uint8_t* ) &nvmData->ClassB, sizeof( nvmData->ClassB ) - + sizeof( nvmData->ClassB.Crc32 ) ); + if( crc != nvmData->ClassB.Crc32 ) + { + nvmData->ClassB.Crc32 = crc; + notifyFlags |= LORAMAC_NVM_NOTIFY_FLAG_CLASS_B; + } + + CallNvmDataChangeCallback( notifyFlags ); +} + +static bool LoRaMacHandleResponseTimeout( TimerTime_t timeoutInMs, TimerTime_t startTimeInMs ) +{ + if( startTimeInMs != 0 ) + { + TimerTime_t elapsedTime = TimerGetElapsedTime( startTimeInMs ); + if( elapsedTime > timeoutInMs ) + { + Nvm.MacGroup1.SrvAckRequested = false; + return true; + } + } + return false; +} + +void LoRaMacProcess( void ) +{ + uint8_t noTx = false; + + LoRaMacHandleIrqEvents( ); + LoRaMacClassBProcess( ); + + // MAC proceeded a state and is ready to check + if( MacCtx.MacFlags.Bits.MacDone == 1 ) + { + LoRaMacEnableRequests( LORAMAC_REQUEST_HANDLING_OFF ); + LoRaMacCheckForRxAbort( ); + + // An error occurs during transmitting + if( IsRequestPending( ) > 0 ) + { + noTx |= LoRaMacCheckForBeaconAcquisition( ); + } + + if( noTx == 0x00 ) + { + LoRaMacHandleMlmeRequest( ); + LoRaMacHandleMcpsRequest( ); + } + LoRaMacHandleRequestEvents( ); + LoRaMacEnableRequests( LORAMAC_REQUEST_HANDLING_ON ); + MacCtx.MacFlags.Bits.NvmHandle = 1; + } + LoRaMacHandleIndicationEvents( ); + LoRaMacHandleRejoinEvents( ); + + if( MacCtx.RxSlot == RX_SLOT_WIN_CLASS_C ) + { + OpenContinuousRxCWindow( ); + } + if( MacCtx.MacFlags.Bits.NvmHandle == 1 ) + { + MacCtx.MacFlags.Bits.NvmHandle = 0; + LoRaMacHandleNvm( &Nvm ); + } +} + +static void OnTxDelayedTimerEvent( void* context ) +{ + TimerStop( &MacCtx.TxDelayedTimer ); + MacCtx.MacState &= ~LORAMAC_TX_DELAYED; + + if( LoRaMacHandleResponseTimeout( REGION_COMMON_CLASS_B_C_RESP_TIMEOUT, + MacCtx.ResponseTimeoutStartTime ) == true ) + { + // Skip retransmission + return; + } + + // Schedule frame, allow delayed frame transmissions + switch( ScheduleTx( true ) ) + { + case LORAMAC_STATUS_OK: + case LORAMAC_STATUS_DUTYCYCLE_RESTRICTED: + { + break; + } + default: + { + // Stop retransmission attempt + MacCtx.McpsConfirm.Datarate = Nvm.MacGroup1.ChannelsDatarate; + MacCtx.McpsConfirm.NbTrans = MacCtx.ChannelsNbTransCounter; + MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR; + LoRaMacConfirmQueueSetStatusCmn( LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR ); + StopRetransmission( ); + break; + } + } +} + +static void OnRxWindow1TimerEvent( void* context ) +{ + MacCtx.RxWindow1Config.Channel = MacCtx.Channel; + MacCtx.RxWindow1Config.DrOffset = Nvm.MacGroup2.MacParams.Rx1DrOffset; + MacCtx.RxWindow1Config.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime; + MacCtx.RxWindow1Config.RxContinuous = false; + MacCtx.RxWindow1Config.RxSlot = RX_SLOT_WIN_1; + MacCtx.RxWindow1Config.NetworkActivation = Nvm.MacGroup2.NetworkActivation; + + RxWindowSetup( &MacCtx.RxWindowTimer1, &MacCtx.RxWindow1Config ); +} + +static void OnRxWindow2TimerEvent( void* context ) +{ + // Check if we are processing Rx1 window. + // If yes, we don't setup the Rx2 window. + if( MacCtx.RxSlot == RX_SLOT_WIN_1 ) + { + return; + } + MacCtx.RxWindow2Config.Channel = MacCtx.Channel; + MacCtx.RxWindow2Config.Frequency = Nvm.MacGroup2.MacParams.Rx2Channel.Frequency; + MacCtx.RxWindow2Config.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime; + MacCtx.RxWindow2Config.RxContinuous = false; + MacCtx.RxWindow2Config.RxSlot = RX_SLOT_WIN_2; + MacCtx.RxWindow2Config.NetworkActivation = Nvm.MacGroup2.NetworkActivation; + + RxWindowSetup( &MacCtx.RxWindowTimer2, &MacCtx.RxWindow2Config ); +} + +static void OnRetransmitTimeoutTimerEvent( void* context ) +{ + TimerStop( &MacCtx.RetransmitTimeoutTimer ); + + if( MacCtx.NodeAckRequested == true ) + { + MacCtx.RetransmitTimeoutRetry = true; + } + OnMacProcessNotify( ); +} + +static LoRaMacCryptoStatus_t GetFCntDown( AddressIdentifier_t addrID, FType_t fType, LoRaMacMessageData_t* macMsg, Version_t lrWanVersion, + FCntIdentifier_t* fCntID, uint32_t* currentDown ) +{ + if( ( macMsg == NULL ) || ( fCntID == NULL ) || + ( currentDown == NULL ) ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + // Determine the frame counter identifier and choose counter from FCntList + switch( addrID ) + { + case UNICAST_DEV_ADDR: + if( lrWanVersion.Fields.Minor == 1 ) + { + if( ( fType == FRAME_TYPE_A ) || ( fType == FRAME_TYPE_D ) ) + { + *fCntID = A_FCNT_DOWN; + } + else + { + *fCntID = N_FCNT_DOWN; + } + } + else + { // For LoRaWAN 1.0.X + *fCntID = FCNT_DOWN; + } + break; + case MULTICAST_0_ADDR: + *fCntID = MC_FCNT_DOWN_0; + break; + case MULTICAST_1_ADDR: + *fCntID = MC_FCNT_DOWN_1; + break; + case MULTICAST_2_ADDR: + *fCntID = MC_FCNT_DOWN_2; + break; + case MULTICAST_3_ADDR: + *fCntID = MC_FCNT_DOWN_3; + break; + default: + return LORAMAC_CRYPTO_FAIL_FCNT_ID; + } + + return LoRaMacCryptoGetFCntDown( *fCntID, macMsg->FHDR.FCnt, currentDown ); +} + +static LoRaMacStatus_t SwitchClass( DeviceClass_t deviceClass ) +{ + LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID; + + switch( Nvm.MacGroup2.DeviceClass ) + { + case CLASS_A: + { + if( deviceClass == CLASS_A ) + { + // Revert back RxC parameters + Nvm.MacGroup2.MacParams.RxCChannel = Nvm.MacGroup2.MacParams.Rx2Channel; + + status = LORAMAC_STATUS_OK; + } + if( deviceClass == CLASS_B ) + { + status = LoRaMacClassBSwitchClass( deviceClass ); + if( status == LORAMAC_STATUS_OK ) + { + Nvm.MacGroup2.DeviceClass = deviceClass; + } + } + + if( deviceClass == CLASS_C ) + { + Nvm.MacGroup2.DeviceClass = deviceClass; + + MacCtx.RxWindowCConfig = MacCtx.RxWindow2Config; + MacCtx.RxWindowCConfig.RxSlot = RX_SLOT_WIN_CLASS_C; + + for( int8_t i = 0; i < LORAMAC_MAX_MC_CTX; i++ ) + { + if( ( Nvm.MacGroup2.MulticastChannelList[i].ChannelParams.IsEnabled == true ) && + ( Nvm.MacGroup2.MulticastChannelList[i].ChannelParams.RxParams.Class == CLASS_C ) ) + { + Nvm.MacGroup2.MacParams.RxCChannel.Frequency = Nvm.MacGroup2.MulticastChannelList[i].ChannelParams.RxParams.Params.ClassC.Frequency; + Nvm.MacGroup2.MacParams.RxCChannel.Datarate = Nvm.MacGroup2.MulticastChannelList[i].ChannelParams.RxParams.Params.ClassC.Datarate; + + MacCtx.RxWindowCConfig.Channel = MacCtx.Channel; + MacCtx.RxWindowCConfig.Frequency = Nvm.MacGroup2.MacParams.RxCChannel.Frequency; + MacCtx.RxWindowCConfig.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime; + MacCtx.RxWindowCConfig.RxSlot = RX_SLOT_WIN_CLASS_C_MULTICAST; + MacCtx.RxWindowCConfig.RxContinuous = true; + break; + } + } + + // Set the NodeAckRequested indicator to default + MacCtx.NodeAckRequested = false; + // Set the radio into sleep mode in case we are still in RX mode + Radio.Sleep( ); + + OpenContinuousRxCWindow( ); + + // Add a DeviceModeInd MAC Command to indicate the network a device mode change. + if( Nvm.MacGroup2.Version.Fields.Minor >= 1 ) + { + LoRaMacCommandsAddCmd( MOTE_MAC_DEVICE_MODE_IND, ( uint8_t* )&Nvm.MacGroup2.DeviceClass, 1 ); + } + + status = LORAMAC_STATUS_OK; + } + break; + } + case CLASS_B: + { + status = LoRaMacClassBSwitchClass( deviceClass ); + if( status == LORAMAC_STATUS_OK ) + { + Nvm.MacGroup2.DeviceClass = deviceClass; + } + break; + } + case CLASS_C: + { + if( deviceClass == CLASS_A ) + { + // Reset RxSlot to NONE + MacCtx.RxSlot = RX_SLOT_NONE; + + Nvm.MacGroup2.DeviceClass = deviceClass; + + // Set the radio into sleep to setup a defined state + Radio.Sleep( ); + + status = LORAMAC_STATUS_OK; + + // Add a DeviceModeInd MAC Command to indicate the network a device mode change. + if( Nvm.MacGroup2.Version.Fields.Minor >= 1 ) + { + LoRaMacCommandsAddCmd( MOTE_MAC_DEVICE_MODE_IND, ( uint8_t* )&Nvm.MacGroup2.DeviceClass, 1 ); + } + } + break; + } + } + + return status; +} + +static uint8_t GetMaxAppPayloadWithoutFOptsLength( int8_t datarate ) +{ + GetPhyParams_t getPhy; + PhyParam_t phyParam; + + // Setup PHY request + getPhy.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime; + getPhy.Datarate = datarate; + getPhy.Attribute = PHY_MAX_PAYLOAD; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + + return phyParam.Value; +} + +static bool ValidatePayloadLength( uint8_t lenN, int8_t datarate, uint8_t fOptsLen ) +{ + uint16_t maxN = 0; + uint16_t payloadSize = 0; + + maxN = GetMaxAppPayloadWithoutFOptsLength( datarate ); + + // Calculate the resulting payload size + payloadSize = ( lenN + fOptsLen ); + + // Validation of the application payload size + if( ( payloadSize <= maxN ) && ( payloadSize <= LORAMAC_PHY_MAXPAYLOAD ) ) + { + return true; + } + return false; +} + +static void ProcessMacCommands( uint8_t *payload, uint8_t macIndex, uint8_t commandsSize, int8_t snr, LoRaMacRxSlot_t rxSlot ) +{ + uint8_t status = 0; + bool adrBlockFound = false; + uint8_t macCmdPayload[2] = { 0x00, 0x00 }; + MacCommand_t* macCmd; + + if( ( rxSlot != RX_SLOT_WIN_1 ) && ( rxSlot != RX_SLOT_WIN_2 ) ) + { + // Do only parse MAC commands for Class A RX windows + return; + } + + while( macIndex < commandsSize ) + { + // Make sure to parse only complete MAC commands + if( ( LoRaMacCommandsGetCmdSize( payload[macIndex] ) + macIndex ) > commandsSize ) + { + return; + } + + // Decode Frame MAC commands + switch( payload[macIndex++] ) + { + case SRV_MAC_RESET_CONF: + { + uint8_t serverMinorVersion = payload[macIndex++]; + + // Compare own LoRaWAN Version with server's + if( Nvm.MacGroup2.Version.Fields.Minor >= serverMinorVersion ) + { + // If they equal remove the sticky ResetInd MAC-Command. + if( LoRaMacCommandsGetCmd( MOTE_MAC_RESET_IND, &macCmd) == LORAMAC_COMMANDS_SUCCESS ) + { + LoRaMacCommandsRemoveCmd( macCmd ); + } + } + break; + } + case SRV_MAC_LINK_CHECK_ANS: + { + if( LoRaMacConfirmQueueIsCmdActive( MLME_LINK_CHECK ) == true ) + { + LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_LINK_CHECK ); + MacCtx.MlmeConfirm.DemodMargin = payload[macIndex++]; + MacCtx.MlmeConfirm.NbGateways = payload[macIndex++]; + } + break; + } + case SRV_MAC_LINK_ADR_REQ: + { + LinkAdrReqParams_t linkAdrReq; + int8_t linkAdrDatarate = DR_0; + int8_t linkAdrTxPower = TX_POWER_0; + uint8_t linkAdrNbRep = 0; + uint8_t linkAdrNbBytesParsed = 0; + + // The end node is allowed to process one block of LinkAdrRequests. + // It must ignore subsequent blocks + if( adrBlockFound == false ) + { + adrBlockFound = true; + + do + { + // Fill parameter structure + linkAdrReq.Payload = &payload[macIndex - 1]; + linkAdrReq.AdrEnabled = Nvm.MacGroup2.AdrCtrlOn; + linkAdrReq.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime; + linkAdrReq.CurrentDatarate = Nvm.MacGroup1.ChannelsDatarate; + linkAdrReq.CurrentTxPower = Nvm.MacGroup1.ChannelsTxPower; + linkAdrReq.CurrentNbRep = Nvm.MacGroup2.MacParams.ChannelsNbTrans; + linkAdrReq.Version = Nvm.MacGroup2.Version; + + // There is a fundamental difference in reporting the status + // of the LinkAdrRequests when ADR is on or off. When ADR is on, every + // LinkAdrAns contains the same value. This does not hold when ADR is off, + // where every LinkAdrAns requires an individual status. + if( Nvm.MacGroup2.AdrCtrlOn == true ) + { + // When ADR is on, the function RegionLinkAdrReq will take care + // about the parsing and interpretation of the LinkAdrRequest block and + // it provides one status which shall be applied to every LinkAdrAns + linkAdrReq.PayloadSize = commandsSize - ( macIndex - 1 ); + } + else + { + // When ADR is off, this function will loop over the individual LinkAdrRequests + // and will call RegionLinkAdrReq for each individually, as every request + // requires an individual answer. + // When ADR is off, the function RegionLinkAdrReq ignores the new values for + // ChannelsDatarate, ChannelsTxPower and ChannelsNbTrans. + linkAdrReq.PayloadSize = 5; + } + + // Process the ADR requests + status = RegionLinkAdrReq( Nvm.MacGroup2.Region, &linkAdrReq, &linkAdrDatarate, + &linkAdrTxPower, &linkAdrNbRep, &linkAdrNbBytesParsed ); + + if( ( status & 0x07 ) == 0x07 ) + { + // Set the status that the datarate has been increased + if( linkAdrDatarate > Nvm.MacGroup1.ChannelsDatarate ) + { + Nvm.MacGroup2.ChannelsDatarateChangedLinkAdrReq = true; + } + Nvm.MacGroup1.ChannelsDatarate = linkAdrDatarate; + Nvm.MacGroup1.ChannelsTxPower = linkAdrTxPower; + Nvm.MacGroup2.MacParams.ChannelsNbTrans = linkAdrNbRep; + } + + // Add the answers to the buffer + for( uint8_t i = 0; i < ( linkAdrNbBytesParsed / 5 ); i++ ) + { + LoRaMacCommandsAddCmd( MOTE_MAC_LINK_ADR_ANS, &status, 1 ); + } + // Update MAC index + macIndex += linkAdrNbBytesParsed - 1; + + // Check to prevent invalid access + if( macIndex >= commandsSize ) + break; + + } while( payload[macIndex++] == SRV_MAC_LINK_ADR_REQ ); + + if( macIndex < commandsSize ) + { + // Decrease the index such that it points to the next MAC command + macIndex--; + } + } + else + { + // Increase the index by the MAC command size (without command) + macIndex += 4; + } + break; + } + case SRV_MAC_DUTY_CYCLE_REQ: + { + Nvm.MacGroup2.MaxDCycle = payload[macIndex++] & 0x0F; + Nvm.MacGroup2.AggregatedDCycle = 1 << Nvm.MacGroup2.MaxDCycle; + LoRaMacCommandsAddCmd( MOTE_MAC_DUTY_CYCLE_ANS, macCmdPayload, 0 ); + break; + } + case SRV_MAC_RX_PARAM_SETUP_REQ: + { + RxParamSetupReqParams_t rxParamSetupReq; + status = 0x07; + + rxParamSetupReq.DrOffset = ( payload[macIndex] >> 4 ) & 0x07; + rxParamSetupReq.Datarate = payload[macIndex] & 0x0F; + macIndex++; + + if( rxParamSetupReq.Datarate == 0x0F ) + { + // Keep the current datarate + rxParamSetupReq.Datarate = Nvm.MacGroup2.MacParams.Rx2Channel.Datarate; + } + + rxParamSetupReq.Frequency = ( uint32_t ) payload[macIndex++]; + rxParamSetupReq.Frequency |= ( uint32_t ) payload[macIndex++] << 8; + rxParamSetupReq.Frequency |= ( uint32_t ) payload[macIndex++] << 16; + rxParamSetupReq.Frequency *= 100; + + // Perform request on region + status = RegionRxParamSetupReq( Nvm.MacGroup2.Region, &rxParamSetupReq ); + + if( ( status & 0x07 ) == 0x07 ) + { + Nvm.MacGroup2.MacParams.Rx2Channel.Datarate = rxParamSetupReq.Datarate; + Nvm.MacGroup2.MacParams.RxCChannel.Datarate = rxParamSetupReq.Datarate; + Nvm.MacGroup2.MacParams.Rx2Channel.Frequency = rxParamSetupReq.Frequency; + Nvm.MacGroup2.MacParams.RxCChannel.Frequency = rxParamSetupReq.Frequency; + Nvm.MacGroup2.MacParams.Rx1DrOffset = rxParamSetupReq.DrOffset; + } + macCmdPayload[0] = status; + LoRaMacCommandsAddCmd( MOTE_MAC_RX_PARAM_SETUP_ANS, macCmdPayload, 1 ); + break; + } + case SRV_MAC_DEV_STATUS_REQ: + { + uint8_t batteryLevel = BAT_LEVEL_NO_MEASURE; + if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->GetBatteryLevel != NULL ) ) + { + batteryLevel = MacCtx.MacCallbacks->GetBatteryLevel( ); + } + macCmdPayload[0] = batteryLevel; + macCmdPayload[1] = ( uint8_t )( snr & 0x3F ); + LoRaMacCommandsAddCmd( MOTE_MAC_DEV_STATUS_ANS, macCmdPayload, 2 ); + break; + } + case SRV_MAC_NEW_CHANNEL_REQ: + { + NewChannelReqParams_t newChannelReq; + ChannelParams_t chParam; + status = 0x03; + + newChannelReq.ChannelId = payload[macIndex++]; + newChannelReq.NewChannel = &chParam; + + chParam.Frequency = ( uint32_t ) payload[macIndex++]; + chParam.Frequency |= ( uint32_t ) payload[macIndex++] << 8; + chParam.Frequency |= ( uint32_t ) payload[macIndex++] << 16; + chParam.Frequency *= 100; + chParam.Rx1Frequency = 0; + chParam.DrRange.Value = payload[macIndex++]; + + status = ( uint8_t )RegionNewChannelReq( Nvm.MacGroup2.Region, &newChannelReq ); + + if( ( int8_t )status >= 0 ) + { + macCmdPayload[0] = status; + LoRaMacCommandsAddCmd( MOTE_MAC_NEW_CHANNEL_ANS, macCmdPayload, 1 ); + } + break; + } + case SRV_MAC_RX_TIMING_SETUP_REQ: + { + uint8_t delay = payload[macIndex++] & 0x0F; + + if( delay == 0 ) + { + delay++; + } + Nvm.MacGroup2.MacParams.ReceiveDelay1 = delay * 1000; + Nvm.MacGroup2.MacParams.ReceiveDelay2 = Nvm.MacGroup2.MacParams.ReceiveDelay1 + 1000; + LoRaMacCommandsAddCmd( MOTE_MAC_RX_TIMING_SETUP_ANS, macCmdPayload, 0 ); + break; + } + case SRV_MAC_TX_PARAM_SETUP_REQ: + { + TxParamSetupReqParams_t txParamSetupReq; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + uint8_t eirpDwellTime = payload[macIndex++]; + + txParamSetupReq.UplinkDwellTime = 0; + txParamSetupReq.DownlinkDwellTime = 0; + + if( ( eirpDwellTime & 0x20 ) == 0x20 ) + { + txParamSetupReq.DownlinkDwellTime = 1; + } + if( ( eirpDwellTime & 0x10 ) == 0x10 ) + { + txParamSetupReq.UplinkDwellTime = 1; + } + txParamSetupReq.MaxEirp = eirpDwellTime & 0x0F; + + // Check the status for correctness + if( RegionTxParamSetupReq( Nvm.MacGroup2.Region, &txParamSetupReq ) != -1 ) + { + // Accept command + Nvm.MacGroup2.MacParams.UplinkDwellTime = txParamSetupReq.UplinkDwellTime; + Nvm.MacGroup2.MacParams.DownlinkDwellTime = txParamSetupReq.DownlinkDwellTime; + Nvm.MacGroup2.MacParams.MaxEirp = LoRaMacMaxEirpTable[txParamSetupReq.MaxEirp]; + // Update the datarate in case of the new configuration limits it + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup1.ChannelsDatarate = MAX( Nvm.MacGroup1.ChannelsDatarate, ( int8_t )phyParam.Value ); + + // Add command response + LoRaMacCommandsAddCmd( MOTE_MAC_TX_PARAM_SETUP_ANS, macCmdPayload, 0 ); + } + break; + } + case SRV_MAC_REKEY_CONF: + { + uint8_t serverMinorVersion = payload[macIndex++]; + + // Compare own LoRaWAN Version with server's + if( Nvm.MacGroup2.Version.Fields.Minor >= serverMinorVersion ) + { + // If they equal remove the sticky RekeyInd MAC-Command. + if( LoRaMacCommandsGetCmd( MOTE_MAC_REKEY_IND, &macCmd) == LORAMAC_COMMANDS_SUCCESS ) + { + LoRaMacCommandsRemoveCmd( macCmd ); + } + } + break; + } + case SRV_MAC_DL_CHANNEL_REQ: + { + DlChannelReqParams_t dlChannelReq; + status = 0x03; + + dlChannelReq.ChannelId = payload[macIndex++]; + dlChannelReq.Rx1Frequency = ( uint32_t ) payload[macIndex++]; + dlChannelReq.Rx1Frequency |= ( uint32_t ) payload[macIndex++] << 8; + dlChannelReq.Rx1Frequency |= ( uint32_t ) payload[macIndex++] << 16; + dlChannelReq.Rx1Frequency *= 100; + + status = ( uint8_t )RegionDlChannelReq( Nvm.MacGroup2.Region, &dlChannelReq ); + + if( ( int8_t )status >= 0 ) + { + macCmdPayload[0] = status; + LoRaMacCommandsAddCmd( MOTE_MAC_DL_CHANNEL_ANS, macCmdPayload, 1 ); + } + break; + } + case SRV_MAC_ADR_PARAM_SETUP_REQ: + { + /* ADRParamSetupReq Payload: ADRparam + * +----------------+---------------+ + * | 7:4 Limit_exp | 3:0 Delay_exp | + * +----------------+---------------+ + */ + + uint8_t delayExp = 0x0F & payload[macIndex]; + uint8_t limitExp = 0x0F & ( payload[macIndex] >> 4 ); + macIndex++; + + // ADR_ACK_ DELAY = 2^Delay_exp + Nvm.MacGroup2.MacParams.AdrAckDelay = 0x01 << delayExp; + + // ADR_ACK_LIMIT = 2^Limit_exp + Nvm.MacGroup2.MacParams.AdrAckLimit = 0x01 << limitExp; + + LoRaMacCommandsAddCmd( MOTE_MAC_ADR_PARAM_SETUP_ANS, macCmdPayload, 0 ); + break; + } + case SRV_MAC_FORCE_REJOIN_REQ: + { + /* ForceRejoinReq Payload: + * +--------------+------------------+-------+----------------+--------+ + * | 13:11 Period | 10:8 Max_Retries | 7 RFU | 6:4 RejoinType | 3:0 DR | + * +--------------+------------------+-------+----------------+--------+ + */ + + // Parse payload + uint8_t period = ( 0x38 & payload[macIndex] ) >> 3; + Nvm.MacGroup2.ForceRejoinMaxRetries = 0x07 & payload[macIndex]; + macIndex++; + Nvm.MacGroup2.ForceRejoinType = ( 0x70 & payload[macIndex] ) >> 4; + Nvm.MacGroup1.ChannelsDatarate = 0x0F & payload[macIndex]; + macIndex ++; + + // Calc delay between retransmissions: 32 seconds x 2^Period + Rand32 + uint32_t rejoinCycleInSec = 32 * ( 0x01 << period ) + randr( 0, 32 ); + + MacCtx.ForceRejonCycleTime = 0; + Nvm.MacGroup1.ForceRejoinRetriesCounter = 0; + ConvertRejoinCycleTime( rejoinCycleInSec, &MacCtx.ForceRejonCycleTime ); + OnForceRejoinReqCycleTimerEvent( NULL ); + break; + } + case SRV_MAC_REJOIN_PARAM_REQ: + { + /* RejoinParamSetupReq Payload: + * +----------------+---------------+ + * | 7:4 MaxTimeN | 3:0 MaxCountN | + * +----------------+---------------+ + */ + uint8_t maxCountN = 0x0F & payload[macIndex]; + uint8_t maxTimeN = 0x0F & ( payload[macIndex] >> 4 ); + uint32_t cycleInSec = 0x01 << ( maxTimeN + 10 ); + uint32_t timeInMs = 0; + uint16_t uplinkLimit = 0x01 << ( maxCountN + 4 ); + macIndex++; + macCmdPayload[0] = 0; + + if( ConvertRejoinCycleTime( cycleInSec, &timeInMs ) == true ) + { + // Calc delay between retransmissions: 2^(maxTimeN+10) + Nvm.MacGroup2.Rejoin0CycleInSec = cycleInSec; + // Calc number if uplinks without rejoin request: 2^(maxCountN+4) + Nvm.MacGroup2.Rejoin0UplinksLimit = uplinkLimit; + MacCtx.Rejoin0CycleTime = timeInMs; + + macCmdPayload[0] = 0x01; + TimerStop( &MacCtx.Rejoin0CycleTimer ); + TimerSetValue( &MacCtx.Rejoin0CycleTimer, MacCtx.Rejoin0CycleTime ); + TimerStart( &MacCtx.Rejoin0CycleTimer ); + } + LoRaMacCommandsAddCmd( MOTE_MAC_REJOIN_PARAM_ANS, macCmdPayload, 1 ); + break; + } + case SRV_MAC_DEVICE_MODE_CONF: + { + // 1 byte payload which we do not handle. + macIndex++; + if( LoRaMacCommandsGetCmd( MOTE_MAC_DEVICE_MODE_IND, &macCmd) == LORAMAC_COMMANDS_SUCCESS ) + { + LoRaMacCommandsRemoveCmd( macCmd ); + } + break; + } + case SRV_MAC_DEVICE_TIME_ANS: + { + // The mote time can be updated only when the time is received in classA + // receive windows only. + if( LoRaMacConfirmQueueIsCmdActive( MLME_DEVICE_TIME ) == true ) + { + LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_DEVICE_TIME ); + + SysTime_t gpsEpochTime = { 0 }; + SysTime_t sysTime = { 0 }; + SysTime_t sysTimeCurrent = { 0 }; + + gpsEpochTime.Seconds = ( uint32_t )payload[macIndex++]; + gpsEpochTime.Seconds |= ( uint32_t )payload[macIndex++] << 8; + gpsEpochTime.Seconds |= ( uint32_t )payload[macIndex++] << 16; + gpsEpochTime.Seconds |= ( uint32_t )payload[macIndex++] << 24; + gpsEpochTime.SubSeconds = payload[macIndex++]; + + // Convert the fractional second received in ms + // round( pow( 0.5, 8.0 ) * 1000 ) = 3.90625 + gpsEpochTime.SubSeconds = ( int16_t )( ( ( int32_t )gpsEpochTime.SubSeconds * 1000 ) >> 8 ); + + // Copy received GPS Epoch time into system time + sysTime = gpsEpochTime; + // Add Unix to Gps epoch offset. The system time is based on Unix time. + sysTime.Seconds += UNIX_GPS_EPOCH_OFFSET; + + // Compensate time difference between Tx Done time and now + sysTimeCurrent = SysTimeGet( ); + sysTime = SysTimeAdd( sysTimeCurrent, SysTimeSub( sysTime, MacCtx.LastTxSysTime ) ); + + // Apply the new system time. + SysTimeSet( sysTime ); + LoRaMacClassBDeviceTimeAns( ); + MacCtx.McpsIndication.DeviceTimeAnsReceived = true; + } + else + { + // Incase of other receive windows the Device Time Answer is not received. + MacCtx.McpsIndication.DeviceTimeAnsReceived = false; + } + break; + } + case SRV_MAC_PING_SLOT_INFO_ANS: + { + if( LoRaMacConfirmQueueIsCmdActive( MLME_PING_SLOT_INFO ) == true ) + { + LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_PING_SLOT_INFO ); + // According to the specification, it is not allowed to process this answer in + // a ping or multicast slot + if( ( MacCtx.RxSlot != RX_SLOT_WIN_CLASS_B_PING_SLOT ) && ( MacCtx.RxSlot != RX_SLOT_WIN_CLASS_B_MULTICAST_SLOT ) ) + { + LoRaMacClassBPingSlotInfoAns( ); + } + } + break; + } + case SRV_MAC_PING_SLOT_CHANNEL_REQ: + { + uint8_t status = 0x03; + uint32_t frequency = 0; + uint8_t datarate; + + frequency = ( uint32_t )payload[macIndex++]; + frequency |= ( uint32_t )payload[macIndex++] << 8; + frequency |= ( uint32_t )payload[macIndex++] << 16; + frequency *= 100; + datarate = payload[macIndex++] & 0x0F; + + status = LoRaMacClassBPingSlotChannelReq( datarate, frequency ); + macCmdPayload[0] = status; + LoRaMacCommandsAddCmd( MOTE_MAC_PING_SLOT_CHANNEL_ANS, macCmdPayload, 1 ); + break; + } + case SRV_MAC_BEACON_TIMING_ANS: + { + if( LoRaMacConfirmQueueIsCmdActive( MLME_BEACON_TIMING ) == true ) + { + LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_BEACON_TIMING ); + uint16_t beaconTimingDelay = 0; + uint8_t beaconTimingChannel = 0; + + beaconTimingDelay = ( uint16_t )payload[macIndex++]; + beaconTimingDelay |= ( uint16_t )payload[macIndex++] << 8; + beaconTimingChannel = payload[macIndex++]; + + LoRaMacClassBBeaconTimingAns( beaconTimingDelay, beaconTimingChannel, RxDoneParams.LastRxDone ); + } + break; + } + case SRV_MAC_BEACON_FREQ_REQ: + { + uint32_t frequency = 0; + + frequency = ( uint32_t )payload[macIndex++]; + frequency |= ( uint32_t )payload[macIndex++] << 8; + frequency |= ( uint32_t )payload[macIndex++] << 16; + frequency *= 100; + + if( LoRaMacClassBBeaconFreqReq( frequency ) == true ) + { + macCmdPayload[0] = 1; + } + else + { + macCmdPayload[0] = 0; + } + LoRaMacCommandsAddCmd( MOTE_MAC_BEACON_FREQ_ANS, macCmdPayload, 1 ); + } + break; + default: + // Unknown command. ABORT MAC commands processing + return; + } + } +} + +LoRaMacStatus_t Send( LoRaMacHeader_t* macHdr, uint8_t fPort, void* fBuffer, uint16_t fBufferSize ) +{ + LoRaMacFrameCtrl_t fCtrl; + LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID; + int8_t datarate = Nvm.MacGroup1.ChannelsDatarate; + int8_t txPower = Nvm.MacGroup1.ChannelsTxPower; + uint32_t adrAckCounter = Nvm.MacGroup1.AdrAckCounter; + CalcNextAdrParams_t adrNext; + + // Check if we are joined + if( Nvm.MacGroup2.NetworkActivation == ACTIVATION_TYPE_NONE ) + { + return LORAMAC_STATUS_NO_NETWORK_JOINED; + } + if( Nvm.MacGroup2.MaxDCycle == 0 ) + { + Nvm.MacGroup1.AggregatedTimeOff = 0; + } + + fCtrl.Value = 0; + fCtrl.Bits.FOptsLen = 0; + fCtrl.Bits.Adr = Nvm.MacGroup2.AdrCtrlOn; + + // Check class b + if( Nvm.MacGroup2.DeviceClass == CLASS_B ) + { + fCtrl.Bits.FPending = 1; + } + else + { + fCtrl.Bits.FPending = 0; + } + + // Check server ack + if( Nvm.MacGroup1.SrvAckRequested == true ) + { + fCtrl.Bits.Ack = 1; + } + + // ADR next request + adrNext.UpdateChanMask = true; + adrNext.AdrEnabled = fCtrl.Bits.Adr; + adrNext.AdrAckCounter = Nvm.MacGroup1.AdrAckCounter; + adrNext.AdrAckLimit = Nvm.MacGroup2.MacParams.AdrAckLimit; + adrNext.AdrAckDelay = Nvm.MacGroup2.MacParams.AdrAckDelay; + adrNext.Datarate = Nvm.MacGroup1.ChannelsDatarate; + adrNext.TxPower = Nvm.MacGroup1.ChannelsTxPower; + adrNext.NbTrans = Nvm.MacGroup2.MacParams.ChannelsNbTrans; + adrNext.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime; + adrNext.Region = Nvm.MacGroup2.Region; + + fCtrl.Bits.AdrAckReq = LoRaMacAdrCalcNext( &adrNext, &Nvm.MacGroup1.ChannelsDatarate, + &Nvm.MacGroup1.ChannelsTxPower, + &Nvm.MacGroup2.MacParams.ChannelsNbTrans, &adrAckCounter ); + + // Prepare the frame + status = PrepareFrame( macHdr, &fCtrl, fPort, fBuffer, fBufferSize ); + + // Validate status + if( ( status == LORAMAC_STATUS_OK ) || ( status == LORAMAC_STATUS_SKIPPED_APP_DATA ) ) + { + // Schedule frame, do not allow delayed transmissions + status = ScheduleTx( false ); + } + + // Post processing + if( status != LORAMAC_STATUS_OK ) + { + // Bad case - restore + // Store local variables + Nvm.MacGroup1.ChannelsDatarate = datarate; + Nvm.MacGroup1.ChannelsTxPower = txPower; + } + else + { + // Good case + Nvm.MacGroup1.SrvAckRequested = false; + Nvm.MacGroup1.AdrAckCounter = adrAckCounter; + // Remove all none sticky MAC commands + if( LoRaMacCommandsRemoveNoneStickyCmds( ) != LORAMAC_COMMANDS_SUCCESS ) + { + return LORAMAC_STATUS_MAC_COMMAD_ERROR; + } + } + return status; +} + +LoRaMacStatus_t SendReJoinReq( JoinReqIdentifier_t joinReqType ) +{ + LoRaMacStatus_t status = LORAMAC_STATUS_OK; + LoRaMacHeader_t macHdr; + macHdr.Value = 0; + bool allowDelayedTx = true; + + // Setup join/rejoin message + switch( joinReqType ) + { + case REJOIN_REQ_1: + { + Nvm.MacGroup2.IsRejoinAcceptPending = true; + + MacCtx.TxMsg.Type = LORAMAC_MSG_TYPE_RE_JOIN_1; + MacCtx.TxMsg.Message.ReJoin1.Buffer = MacCtx.PktBuffer; + MacCtx.TxMsg.Message.ReJoin1.BufSize = LORAMAC_PHY_MAXPAYLOAD; + + macHdr.Bits.MType = FRAME_TYPE_REJOIN; + MacCtx.TxMsg.Message.ReJoin1.MHDR.Value = macHdr.Value; + + MacCtx.TxMsg.Message.ReJoin1.ReJoinType = 1; + + memcpy1( MacCtx.TxMsg.Message.ReJoin1.JoinEUI, SecureElementGetJoinEui( ), LORAMAC_JOIN_EUI_FIELD_SIZE ); + memcpy1( MacCtx.TxMsg.Message.ReJoin1.DevEUI, SecureElementGetDevEui( ), LORAMAC_DEV_EUI_FIELD_SIZE ); + + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoGetRJcount( RJ_COUNT_1, &MacCtx.TxMsg.Message.ReJoin1.RJcount1 ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + + break; + } + case REJOIN_REQ_0: + case REJOIN_REQ_2: + { + if( joinReqType == REJOIN_REQ_0 ) + { + MacCtx.TxMsg.Message.ReJoin0or2.ReJoinType = 0; + } + else + { + MacCtx.TxMsg.Message.ReJoin0or2.ReJoinType = 2; + } + + Nvm.MacGroup2.IsRejoinAcceptPending = true; + + MacCtx.TxMsg.Type = LORAMAC_MSG_TYPE_RE_JOIN_0_2; + MacCtx.TxMsg.Message.ReJoin0or2.Buffer = MacCtx.PktBuffer; + MacCtx.TxMsg.Message.ReJoin0or2.BufSize = LORAMAC_PHY_MAXPAYLOAD; + + macHdr.Bits.MType = FRAME_TYPE_REJOIN; + MacCtx.TxMsg.Message.ReJoin0or2.MHDR.Value = macHdr.Value; + + MacCtx.TxMsg.Message.ReJoin0or2.NetID[0] = Nvm.MacGroup2.NetID & 0xFF; + MacCtx.TxMsg.Message.ReJoin0or2.NetID[1] = ( Nvm.MacGroup2.NetID >> 8 ) & 0xFF; + MacCtx.TxMsg.Message.ReJoin0or2.NetID[2] = ( Nvm.MacGroup2.NetID >> 16 ) & 0xFF; + + memcpy1( MacCtx.TxMsg.Message.ReJoin0or2.DevEUI, SecureElementGetDevEui( ), LORAMAC_DEV_EUI_FIELD_SIZE ); + + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoGetRJcount( RJ_COUNT_0, &MacCtx.TxMsg.Message.ReJoin0or2.RJcount0 ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + + break; + } + case JOIN_REQ: + { + SwitchClass( CLASS_A ); + + MacCtx.TxMsg.Type = LORAMAC_MSG_TYPE_JOIN_REQUEST; + MacCtx.TxMsg.Message.JoinReq.Buffer = MacCtx.PktBuffer; + MacCtx.TxMsg.Message.JoinReq.BufSize = LORAMAC_PHY_MAXPAYLOAD; + + macHdr.Bits.MType = FRAME_TYPE_JOIN_REQ; + MacCtx.TxMsg.Message.JoinReq.MHDR.Value = macHdr.Value; + + memcpy1( MacCtx.TxMsg.Message.JoinReq.JoinEUI, SecureElementGetJoinEui( ), LORAMAC_JOIN_EUI_FIELD_SIZE ); + memcpy1( MacCtx.TxMsg.Message.JoinReq.DevEUI, SecureElementGetDevEui( ), LORAMAC_DEV_EUI_FIELD_SIZE ); + + allowDelayedTx = false; + + break; + } + default: + status = LORAMAC_STATUS_SERVICE_UNKNOWN; + break; + } + + // Schedule frame + status = ScheduleTx( allowDelayedTx ); + return status; +} + +static LoRaMacStatus_t CheckForClassBCollision( void ) +{ + if( LoRaMacClassBIsBeaconExpected( ) == true ) + { + return LORAMAC_STATUS_BUSY_BEACON_RESERVED_TIME; + } + + if( Nvm.MacGroup2.DeviceClass == CLASS_B ) + { + if( LoRaMacClassBIsPingExpected( ) == true ) + { + return LORAMAC_STATUS_BUSY_PING_SLOT_WINDOW_TIME; + } + else if( LoRaMacClassBIsMulticastExpected( ) == true ) + { + return LORAMAC_STATUS_BUSY_PING_SLOT_WINDOW_TIME; + } + } + return LORAMAC_STATUS_OK; +} + +static void ComputeRxWindowParameters( void ) +{ + // Compute Rx1 windows parameters + RegionComputeRxWindowParameters( Nvm.MacGroup2.Region, + RegionApplyDrOffset( Nvm.MacGroup2.Region, + Nvm.MacGroup2.MacParams.DownlinkDwellTime, + Nvm.MacGroup1.ChannelsDatarate, + Nvm.MacGroup2.MacParams.Rx1DrOffset ), + Nvm.MacGroup2.MacParams.MinRxSymbols, + Nvm.MacGroup2.MacParams.SystemMaxRxError, + &MacCtx.RxWindow1Config ); + // Compute Rx2 windows parameters + RegionComputeRxWindowParameters( Nvm.MacGroup2.Region, + Nvm.MacGroup2.MacParams.Rx2Channel.Datarate, + Nvm.MacGroup2.MacParams.MinRxSymbols, + Nvm.MacGroup2.MacParams.SystemMaxRxError, + &MacCtx.RxWindow2Config ); + + // Default setup, in case the device joined + MacCtx.RxWindow1Delay = Nvm.MacGroup2.MacParams.ReceiveDelay1 + MacCtx.RxWindow1Config.WindowOffset; + MacCtx.RxWindow2Delay = Nvm.MacGroup2.MacParams.ReceiveDelay2 + MacCtx.RxWindow2Config.WindowOffset; + + if( MacCtx.TxMsg.Type != LORAMAC_MSG_TYPE_DATA ) + { + MacCtx.RxWindow1Delay = Nvm.MacGroup2.MacParams.JoinAcceptDelay1 + MacCtx.RxWindow1Config.WindowOffset; + MacCtx.RxWindow2Delay = Nvm.MacGroup2.MacParams.JoinAcceptDelay2 + MacCtx.RxWindow2Config.WindowOffset; + } +} + +static LoRaMacStatus_t VerifyTxFrame( void ) +{ + size_t macCmdsSize = 0; + + if( Nvm.MacGroup2.NetworkActivation != ACTIVATION_TYPE_NONE ) + { + if( LoRaMacCommandsGetSizeSerializedCmds( &macCmdsSize ) != LORAMAC_COMMANDS_SUCCESS ) + { + return LORAMAC_STATUS_MAC_COMMAD_ERROR; + } + + if( ValidatePayloadLength( MacCtx.AppDataSize, Nvm.MacGroup1.ChannelsDatarate, macCmdsSize ) == false ) + { + return LORAMAC_STATUS_LENGTH_ERROR; + } + } + return LORAMAC_STATUS_OK; +} + +static LoRaMacStatus_t SerializeTxFrame( void ) +{ + LoRaMacSerializerStatus_t serializeStatus; + + switch( MacCtx.TxMsg.Type ) + { + case LORAMAC_MSG_TYPE_JOIN_REQUEST: + serializeStatus = LoRaMacSerializerJoinRequest( &MacCtx.TxMsg.Message.JoinReq ); + if( LORAMAC_SERIALIZER_SUCCESS != serializeStatus ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + MacCtx.PktBufferLen = MacCtx.TxMsg.Message.JoinReq.BufSize; + break; + case LORAMAC_MSG_TYPE_RE_JOIN_1: + serializeStatus = LoRaMacSerializerReJoinType1( &MacCtx.TxMsg.Message.ReJoin1 ); + if( LORAMAC_SERIALIZER_SUCCESS != serializeStatus ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + MacCtx.PktBufferLen = MacCtx.TxMsg.Message.ReJoin1.BufSize; + break; + case LORAMAC_MSG_TYPE_RE_JOIN_0_2: + serializeStatus = LoRaMacSerializerReJoinType0or2( &MacCtx.TxMsg.Message.ReJoin0or2 ); + if( LORAMAC_SERIALIZER_SUCCESS != serializeStatus ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + MacCtx.PktBufferLen = MacCtx.TxMsg.Message.ReJoin0or2.BufSize; + break; + case LORAMAC_MSG_TYPE_DATA: + serializeStatus = LoRaMacSerializerData( &MacCtx.TxMsg.Message.Data ); + if( LORAMAC_SERIALIZER_SUCCESS != serializeStatus ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + MacCtx.PktBufferLen = MacCtx.TxMsg.Message.Data.BufSize; + break; + case LORAMAC_MSG_TYPE_JOIN_ACCEPT: + case LORAMAC_MSG_TYPE_UNDEF: + default: + return LORAMAC_STATUS_PARAMETER_INVALID; + } + return LORAMAC_STATUS_OK; +} + +static LoRaMacStatus_t ScheduleTx( bool allowDelayedTx ) +{ + LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID; + NextChanParams_t nextChan; + + // Check class b collisions + status = CheckForClassBCollision( ); + if( status != LORAMAC_STATUS_OK ) + { + return status; + } + + // Update back-off + CalculateBackOff( ); + + // Serialize frame + status = SerializeTxFrame( ); + if( status != LORAMAC_STATUS_OK ) + { + return status; + } + + nextChan.AggrTimeOff = Nvm.MacGroup1.AggregatedTimeOff; + nextChan.Datarate = Nvm.MacGroup1.ChannelsDatarate; + nextChan.DutyCycleEnabled = Nvm.MacGroup2.DutyCycleOn; + nextChan.ElapsedTimeSinceStartUp = SysTimeSub( SysTimeGetMcuTime( ), Nvm.MacGroup2.InitializationTime ); + nextChan.LastAggrTx = Nvm.MacGroup1.LastTxDoneTime; + nextChan.LastTxIsJoinRequest = false; + nextChan.Joined = true; + nextChan.PktLen = MacCtx.PktBufferLen; + + // Setup the parameters based on the join status + if( Nvm.MacGroup2.NetworkActivation == ACTIVATION_TYPE_NONE ) + { + nextChan.LastTxIsJoinRequest = true; + nextChan.Joined = false; + } + + // Select channel + status = RegionNextChannel( Nvm.MacGroup2.Region, &nextChan, &MacCtx.Channel, &MacCtx.DutyCycleWaitTime, &Nvm.MacGroup1.AggregatedTimeOff ); + + if( status != LORAMAC_STATUS_OK ) + { + if( status == LORAMAC_STATUS_DUTYCYCLE_RESTRICTED ) + { + if( MacCtx.DutyCycleWaitTime != 0 ) + { + if( allowDelayedTx == true ) + { + // Allow delayed transmissions. We have to allow it in case + // the MAC must retransmit a frame with the frame repetitions + MacCtx.MacState |= LORAMAC_TX_DELAYED; + TimerSetValue( &MacCtx.TxDelayedTimer, MacCtx.DutyCycleWaitTime ); + TimerStart( &MacCtx.TxDelayedTimer ); + return LORAMAC_STATUS_OK; + } + // Need to delay, but allowDelayedTx does not allow it + return status; + } + } + else + {// State where the MAC cannot send a frame + return status; + } + } + + // Compute window parameters, offsets, rx symbols, system errors etc. + ComputeRxWindowParameters( ); + + // Verify TX frame + status = VerifyTxFrame( ); + if( status != LORAMAC_STATUS_OK ) + { + return status; + } + + // Try to send now + return SendFrameOnChannel( MacCtx.Channel ); +} + +static LoRaMacStatus_t SecureFrame( uint8_t txDr, uint8_t txCh ) +{ + LoRaMacCryptoStatus_t macCryptoStatus = LORAMAC_CRYPTO_ERROR; + uint32_t fCntUp = 0; + + switch( MacCtx.TxMsg.Type ) + { + case LORAMAC_MSG_TYPE_JOIN_REQUEST: + macCryptoStatus = LoRaMacCryptoPrepareJoinRequest( &MacCtx.TxMsg.Message.JoinReq ); + if( LORAMAC_CRYPTO_SUCCESS != macCryptoStatus ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + MacCtx.PktBufferLen = MacCtx.TxMsg.Message.JoinReq.BufSize; + break; + case LORAMAC_MSG_TYPE_RE_JOIN_1: + macCryptoStatus = LoRaMacCryptoPrepareReJoinType1( &MacCtx.TxMsg.Message.ReJoin1 ); + if( LORAMAC_CRYPTO_SUCCESS != macCryptoStatus ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + MacCtx.PktBufferLen = MacCtx.TxMsg.Message.ReJoin1.BufSize; + break; + case LORAMAC_MSG_TYPE_RE_JOIN_0_2: + macCryptoStatus = LoRaMacCryptoPrepareReJoinType0or2( &MacCtx.TxMsg.Message.ReJoin0or2 ); + if( LORAMAC_CRYPTO_SUCCESS != macCryptoStatus ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + MacCtx.PktBufferLen = MacCtx.TxMsg.Message.ReJoin0or2.BufSize; + break; + case LORAMAC_MSG_TYPE_DATA: + + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoGetFCntUp( &fCntUp ) ) + { + return LORAMAC_STATUS_FCNT_HANDLER_ERROR; + } + + if( MacCtx.ChannelsNbTransCounter >= 1 ) + { + fCntUp -= 1; + } + + macCryptoStatus = LoRaMacCryptoSecureMessage( fCntUp, txDr, txCh, &MacCtx.TxMsg.Message.Data ); + if( LORAMAC_CRYPTO_SUCCESS != macCryptoStatus ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + MacCtx.PktBufferLen = MacCtx.TxMsg.Message.Data.BufSize; + break; + case LORAMAC_MSG_TYPE_JOIN_ACCEPT: + case LORAMAC_MSG_TYPE_UNDEF: + default: + return LORAMAC_STATUS_PARAMETER_INVALID; + } + return LORAMAC_STATUS_OK; +} + +static void CalculateBackOff( void ) +{ + // Make sure that the calculation of the backoff time for the aggregated time off will only be done in + // case the value is zero. It will be set to zero in the function RegionNextChannel. + if( Nvm.MacGroup1.AggregatedTimeOff == 0 ) + { + // Update aggregated time-off. This must be an assignment and no incremental + // update as we do only calculate the time-off based on the last transmission + Nvm.MacGroup1.AggregatedTimeOff = ( MacCtx.TxTimeOnAir * Nvm.MacGroup2.AggregatedDCycle - MacCtx.TxTimeOnAir ); + } +} + +static void RemoveMacCommands( LoRaMacRxSlot_t rxSlot, LoRaMacFrameCtrl_t fCtrl, Mcps_t request ) +{ + if( rxSlot == RX_SLOT_WIN_1 || rxSlot == RX_SLOT_WIN_2 ) + { + // Remove all sticky MAC commands answers since we can assume + // that they have been received by the server. + if( request == MCPS_CONFIRMED ) + { + if( fCtrl.Bits.Ack == 1 ) + { // For confirmed uplinks only if we have received an ACK. + LoRaMacCommandsRemoveStickyAnsCmds( ); + } + } + else + { + LoRaMacCommandsRemoveStickyAnsCmds( ); + } + } +} + + +static void ResetMacParameters( bool isRejoin ) +{ + LoRaMacClassBCallback_t classBCallbacks; + LoRaMacClassBParams_t classBParams; + + if( isRejoin == false ) + { + Nvm.MacGroup2.NetworkActivation = ACTIVATION_TYPE_NONE; + } + + // ADR counter + Nvm.MacGroup1.AdrAckCounter = 0; + + MacCtx.ChannelsNbTransCounter = 0; + MacCtx.RetransmitTimeoutRetry = false; + MacCtx.ResponseTimeoutStartTime = 0; + + Nvm.MacGroup2.MaxDCycle = 0; + Nvm.MacGroup2.AggregatedDCycle = 1; + + Nvm.MacGroup1.ChannelsTxPower = Nvm.MacGroup2.ChannelsTxPowerDefault; + Nvm.MacGroup1.ChannelsDatarate = Nvm.MacGroup2.ChannelsDatarateDefault; + Nvm.MacGroup2.MacParams.Rx1DrOffset = Nvm.MacGroup2.MacParamsDefaults.Rx1DrOffset; + Nvm.MacGroup2.MacParams.Rx2Channel = Nvm.MacGroup2.MacParamsDefaults.Rx2Channel; + Nvm.MacGroup2.MacParams.RxCChannel = Nvm.MacGroup2.MacParamsDefaults.RxCChannel; + Nvm.MacGroup2.MacParams.UplinkDwellTime = Nvm.MacGroup2.MacParamsDefaults.UplinkDwellTime; + Nvm.MacGroup2.MacParams.DownlinkDwellTime = Nvm.MacGroup2.MacParamsDefaults.DownlinkDwellTime; + Nvm.MacGroup2.MacParams.MaxEirp = Nvm.MacGroup2.MacParamsDefaults.MaxEirp; + Nvm.MacGroup2.MacParams.AntennaGain = Nvm.MacGroup2.MacParamsDefaults.AntennaGain; + Nvm.MacGroup2.MacParams.AdrAckLimit = Nvm.MacGroup2.MacParamsDefaults.AdrAckLimit; + Nvm.MacGroup2.MacParams.AdrAckDelay = Nvm.MacGroup2.MacParamsDefaults.AdrAckDelay; + + MacCtx.NodeAckRequested = false; + Nvm.MacGroup1.SrvAckRequested = false; + Nvm.MacGroup2.ChannelsDatarateChangedLinkAdrReq = false; + Nvm.MacGroup2.DownlinkReceived = false; + + Nvm.MacGroup2.Rejoin0UplinksLimit = 0; + Nvm.MacGroup2.ForceRejoinMaxRetries = 0; + Nvm.MacGroup2.ForceRejoinType = 0; + Nvm.MacGroup2.Rejoin0CycleInSec = 0; + Nvm.MacGroup2.Rejoin1CycleInSec = 0; + Nvm.MacGroup2.IsRejoin0RequestQueued = 0; + Nvm.MacGroup2.IsRejoin1RequestQueued = 0; + Nvm.MacGroup2.IsRejoin2RequestQueued = 0; + + // Reset to application defaults + InitDefaultsParams_t params; + params.Type = INIT_TYPE_RESET_TO_DEFAULT_CHANNELS; + params.NvmGroup1 = &Nvm.RegionGroup1; + params.NvmGroup2 = &Nvm.RegionGroup2; + params.Bands = &RegionBands; + RegionInitDefaults( Nvm.MacGroup2.Region, ¶ms ); + + // Initialize channel index. + MacCtx.Channel = 0; + + // Initialize Rx2 config parameters. + MacCtx.RxWindow2Config.Channel = MacCtx.Channel; + MacCtx.RxWindow2Config.Frequency = Nvm.MacGroup2.MacParams.Rx2Channel.Frequency; + MacCtx.RxWindow2Config.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime; + MacCtx.RxWindow2Config.RxContinuous = false; + MacCtx.RxWindow2Config.RxSlot = RX_SLOT_WIN_2; + MacCtx.RxWindow2Config.NetworkActivation = Nvm.MacGroup2.NetworkActivation; + + // Initialize RxC config parameters. + MacCtx.RxWindowCConfig = MacCtx.RxWindow2Config; + MacCtx.RxWindowCConfig.RxContinuous = true; + MacCtx.RxWindowCConfig.RxSlot = RX_SLOT_WIN_CLASS_C; + + // Initialize class b + // Apply callback + classBCallbacks.GetTemperatureLevel = NULL; + classBCallbacks.MacProcessNotify = NULL; + + if( MacCtx.MacCallbacks != NULL ) + { + classBCallbacks.GetTemperatureLevel = MacCtx.MacCallbacks->GetTemperatureLevel; + classBCallbacks.MacProcessNotify = MacCtx.MacCallbacks->MacProcessNotify; + } + + // Must all be static. Don't use local references. + classBParams.MlmeIndication = &MacCtx.MlmeIndication; + classBParams.McpsIndication = &MacCtx.McpsIndication; + classBParams.MlmeConfirm = &MacCtx.MlmeConfirm; + classBParams.LoRaMacFlags = &MacCtx.MacFlags; + classBParams.LoRaMacDevAddr = &Nvm.MacGroup2.DevAddr; + classBParams.LoRaMacRegion = &Nvm.MacGroup2.Region; + classBParams.LoRaMacParams = &Nvm.MacGroup2.MacParams; + classBParams.MulticastChannels = &Nvm.MacGroup2.MulticastChannelList[0]; + classBParams.NetworkActivation = &Nvm.MacGroup2.NetworkActivation; + + LoRaMacClassBInit( &classBParams, &classBCallbacks, &Nvm.ClassB ); +} + +static bool IsReJoin0Required( ) +{ + + if( ( Nvm.MacGroup2.Rejoin0UplinksLimit == Nvm.MacGroup1.Rejoin0UplinksCounter ) && + ( Nvm.MacGroup2.Version.Fields.Minor >= 1 ) && + ( Nvm.MacGroup2.Rejoin0UplinksLimit != 0 ) ) + { + Nvm.MacGroup1.Rejoin0UplinksCounter = 0; + return true; + } + return false; +} + +/*! + * \brief Initializes and opens the reception window + * + * \param [IN] rxTimer Window timer to be topped. + * \param [IN] rxConfig Window parameters to be setup + */ +static void RxWindowSetup( TimerEvent_t* rxTimer, RxConfigParams_t* rxConfig ) +{ + TimerStop( rxTimer ); + + // Ensure the radio is Idle + Radio.Standby( ); + + if( RegionRxConfig( Nvm.MacGroup2.Region, rxConfig, ( int8_t* )&MacCtx.McpsIndication.RxDatarate ) == true ) + { + Radio.Rx( Nvm.MacGroup2.MacParams.MaxRxWindow ); + MacCtx.RxSlot = rxConfig->RxSlot; + } +} + +static void OpenContinuousRxCWindow( void ) +{ + // Compute RxC windows parameters + RegionComputeRxWindowParameters( Nvm.MacGroup2.Region, + Nvm.MacGroup2.MacParams.RxCChannel.Datarate, + Nvm.MacGroup2.MacParams.MinRxSymbols, + Nvm.MacGroup2.MacParams.SystemMaxRxError, + &MacCtx.RxWindowCConfig ); + + MacCtx.RxWindowCConfig.RxSlot = RX_SLOT_WIN_CLASS_C; + MacCtx.RxWindowCConfig.NetworkActivation = Nvm.MacGroup2.NetworkActivation; + // Setup continuous listening + MacCtx.RxWindowCConfig.RxContinuous = true; + + // At this point the Radio should be idle. + // Thus, there is no need to set the radio in standby mode. + if( RegionRxConfig( Nvm.MacGroup2.Region, &MacCtx.RxWindowCConfig, ( int8_t* )&MacCtx.McpsIndication.RxDatarate ) == true ) + { + Radio.Rx( 0 ); // Continuous mode + MacCtx.RxSlot = MacCtx.RxWindowCConfig.RxSlot; + } +} + +LoRaMacStatus_t PrepareFrame( LoRaMacHeader_t* macHdr, LoRaMacFrameCtrl_t* fCtrl, uint8_t fPort, void* fBuffer, uint16_t fBufferSize ) +{ + MacCtx.PktBufferLen = 0; + MacCtx.NodeAckRequested = false; + uint32_t fCntUp = 0; + size_t macCmdsSize = 0; + uint8_t availableSize = 0; + + if( fBuffer == NULL ) + { + fBufferSize = 0; + } + + memcpy1( MacCtx.AppData, ( uint8_t* ) fBuffer, fBufferSize ); + MacCtx.AppDataSize = fBufferSize; + MacCtx.PktBuffer[0] = macHdr->Value; + + switch( macHdr->Bits.MType ) + { + case FRAME_TYPE_DATA_CONFIRMED_UP: + MacCtx.NodeAckRequested = true; + // Intentional fall through + case FRAME_TYPE_DATA_UNCONFIRMED_UP: + MacCtx.TxMsg.Type = LORAMAC_MSG_TYPE_DATA; + MacCtx.TxMsg.Message.Data.Buffer = MacCtx.PktBuffer; + MacCtx.TxMsg.Message.Data.BufSize = LORAMAC_PHY_MAXPAYLOAD; + MacCtx.TxMsg.Message.Data.MHDR.Value = macHdr->Value; + MacCtx.TxMsg.Message.Data.FPort = fPort; + MacCtx.TxMsg.Message.Data.FHDR.DevAddr = Nvm.MacGroup2.DevAddr; + MacCtx.TxMsg.Message.Data.FHDR.FCtrl.Value = fCtrl->Value; + MacCtx.TxMsg.Message.Data.FRMPayloadSize = MacCtx.AppDataSize; + MacCtx.TxMsg.Message.Data.FRMPayload = MacCtx.AppData; + + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoGetFCntUp( &fCntUp ) ) + { + return LORAMAC_STATUS_FCNT_HANDLER_ERROR; + } + MacCtx.TxMsg.Message.Data.FHDR.FCnt = ( uint16_t )fCntUp; + + // Reset confirm parameters + MacCtx.McpsConfirm.NbTrans = 0; + MacCtx.McpsConfirm.AckReceived = false; + MacCtx.McpsConfirm.UpLinkCounter = fCntUp; + + // Handle the MAC commands if there are any available + if( LoRaMacCommandsGetSizeSerializedCmds( &macCmdsSize ) != LORAMAC_COMMANDS_SUCCESS ) + { + return LORAMAC_STATUS_MAC_COMMAD_ERROR; + } + + if( macCmdsSize > 0 ) + { + availableSize = GetMaxAppPayloadWithoutFOptsLength( Nvm.MacGroup1.ChannelsDatarate ); + + // There is application payload available and the MAC commands fit into FOpts field. + if( ( MacCtx.AppDataSize > 0 ) && ( macCmdsSize <= LORA_MAC_COMMAND_MAX_FOPTS_LENGTH ) ) + { + if( LoRaMacCommandsSerializeCmds( LORA_MAC_COMMAND_MAX_FOPTS_LENGTH, &macCmdsSize, MacCtx.TxMsg.Message.Data.FHDR.FOpts ) != LORAMAC_COMMANDS_SUCCESS ) + { + return LORAMAC_STATUS_MAC_COMMAD_ERROR; + } + fCtrl->Bits.FOptsLen = macCmdsSize; + // Update FCtrl field with new value of FOptionsLength + MacCtx.TxMsg.Message.Data.FHDR.FCtrl.Value = fCtrl->Value; + } + // There is application payload available but the MAC commands does NOT fit into FOpts field. + else if( ( MacCtx.AppDataSize > 0 ) && ( macCmdsSize > LORA_MAC_COMMAND_MAX_FOPTS_LENGTH ) ) + { + + if( LoRaMacCommandsSerializeCmds( availableSize, &macCmdsSize, MacCtx.MacCommandsBuffer ) != LORAMAC_COMMANDS_SUCCESS ) + { + return LORAMAC_STATUS_MAC_COMMAD_ERROR; + } + return LORAMAC_STATUS_SKIPPED_APP_DATA; + } + // No application payload available therefore add all mac commands to the FRMPayload. + else + { + if( LoRaMacCommandsSerializeCmds( availableSize, &macCmdsSize, MacCtx.MacCommandsBuffer ) != LORAMAC_COMMANDS_SUCCESS ) + { + return LORAMAC_STATUS_MAC_COMMAD_ERROR; + } + // Force FPort to be zero + MacCtx.TxMsg.Message.Data.FPort = 0; + + MacCtx.TxMsg.Message.Data.FRMPayload = MacCtx.MacCommandsBuffer; + MacCtx.TxMsg.Message.Data.FRMPayloadSize = macCmdsSize; + } + } + + break; + case FRAME_TYPE_PROPRIETARY: + if( ( fBuffer != NULL ) && ( MacCtx.AppDataSize > 0 ) ) + { + memcpy1( MacCtx.PktBuffer + LORAMAC_MHDR_FIELD_SIZE, ( uint8_t* ) fBuffer, MacCtx.AppDataSize ); + MacCtx.PktBufferLen = LORAMAC_MHDR_FIELD_SIZE + MacCtx.AppDataSize; + } + break; + default: + return LORAMAC_STATUS_SERVICE_UNKNOWN; + } + + return LORAMAC_STATUS_OK; +} + +LoRaMacStatus_t SendFrameOnChannel( uint8_t channel ) +{ + LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID; + TxConfigParams_t txConfig; + int8_t txPower = 0; + + txConfig.Channel = channel; + txConfig.Datarate = Nvm.MacGroup1.ChannelsDatarate; + txConfig.TxPower = Nvm.MacGroup1.ChannelsTxPower; + txConfig.MaxEirp = Nvm.MacGroup2.MacParams.MaxEirp; + txConfig.AntennaGain = Nvm.MacGroup2.MacParams.AntennaGain; + txConfig.PktLen = MacCtx.PktBufferLen; + + RegionTxConfig( Nvm.MacGroup2.Region, &txConfig, &txPower, &MacCtx.TxTimeOnAir ); + + MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + MacCtx.McpsConfirm.Datarate = Nvm.MacGroup1.ChannelsDatarate; + MacCtx.McpsConfirm.TxPower = txPower; + MacCtx.McpsConfirm.Channel = channel; + + // Store the time on air + MacCtx.McpsConfirm.TxTimeOnAir = MacCtx.TxTimeOnAir; + MacCtx.MlmeConfirm.TxTimeOnAir = MacCtx.TxTimeOnAir; + + if( LoRaMacClassBIsBeaconModeActive( ) == true ) + { + // Currently, the Time-On-Air can only be computed when the radio is configured with + // the TX configuration + TimerTime_t collisionTime = LoRaMacClassBIsUplinkCollision( MacCtx.TxTimeOnAir ); + + if( collisionTime > 0 ) + { + return LORAMAC_STATUS_BUSY_UPLINK_COLLISION; + } + } + + if( Nvm.MacGroup2.DeviceClass == CLASS_B ) + { + // Stop slots for class b + LoRaMacClassBStopRxSlots( ); + } + + LoRaMacClassBHaltBeaconing( ); + + // Secure frame + status = SecureFrame( Nvm.MacGroup1.ChannelsDatarate, MacCtx.Channel ); + if( status != LORAMAC_STATUS_OK ) + { + return status; + } + + MacCtx.MacState |= LORAMAC_TX_RUNNING; + + MacCtx.ChannelsNbTransCounter++; + MacCtx.McpsConfirm.NbTrans = MacCtx.ChannelsNbTransCounter; + MacCtx.ResponseTimeoutStartTime = 0; + + // Send now + Radio.Send( MacCtx.PktBuffer, MacCtx.PktBufferLen ); + + return LORAMAC_STATUS_OK; +} + +LoRaMacStatus_t SetTxContinuousWave( uint16_t timeout, uint32_t frequency, uint8_t power ) +{ + Radio.SetTxContinuousWave( frequency, power, timeout ); + + MacCtx.MacState |= LORAMAC_TX_RUNNING; + + return LORAMAC_STATUS_OK; +} + +LoRaMacNvmData_t* GetNvmData( void ) +{ + return &Nvm; +} + +LoRaMacStatus_t RestoreNvmData( LoRaMacNvmData_t* nvm ) +{ + uint32_t crc = 0; + + // Status and parameter validation + if( nvm == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + if( MacCtx.MacState != LORAMAC_STOPPED ) + { + return LORAMAC_STATUS_BUSY; + } + + // Crypto + crc = Crc32( ( uint8_t* ) &nvm->Crypto, sizeof( nvm->Crypto ) - + sizeof( nvm->Crypto.Crc32 ) ); + if( crc == nvm->Crypto.Crc32 ) + { + memcpy1( ( uint8_t* ) &Nvm.Crypto, ( uint8_t* ) &nvm->Crypto, + sizeof( Nvm.Crypto ) ); + } + + // MacGroup1 + crc = Crc32( ( uint8_t* ) &nvm->MacGroup1, sizeof( nvm->MacGroup1 ) - + sizeof( nvm->MacGroup1.Crc32 ) ); + if( crc == nvm->MacGroup1.Crc32 ) + { + memcpy1( ( uint8_t* ) &Nvm.MacGroup1, ( uint8_t* ) &nvm->MacGroup1, + sizeof( Nvm.MacGroup1 ) ); + } + + // MacGroup2 + crc = Crc32( ( uint8_t* ) &nvm->MacGroup2, sizeof( nvm->MacGroup2 ) - + sizeof( nvm->MacGroup2.Crc32 ) ); + if( crc == nvm->MacGroup2.Crc32 ) + { + memcpy1( ( uint8_t* ) &Nvm.MacGroup2, ( uint8_t* ) &nvm->MacGroup2, + sizeof( Nvm.MacGroup2 ) ); + + // Initialize RxC config parameters. + MacCtx.RxWindowCConfig.Channel = MacCtx.Channel; + MacCtx.RxWindowCConfig.Frequency = Nvm.MacGroup2.MacParams.RxCChannel.Frequency; + MacCtx.RxWindowCConfig.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime; + MacCtx.RxWindowCConfig.RxContinuous = true; + MacCtx.RxWindowCConfig.RxSlot = RX_SLOT_WIN_CLASS_C; + + // The public/private network flag may change upon reloading MacGroup2 + // from NVM and we thus need to synchronize the radio. The same function + // is invoked in LoRaMacInitialization. + Radio.SetPublicNetwork( Nvm.MacGroup2.PublicNetwork ); + } + + // Secure Element + crc = Crc32( ( uint8_t* ) &nvm->SecureElement, sizeof( nvm->SecureElement ) - + sizeof( nvm->SecureElement.Crc32 ) ); + if( crc == nvm->SecureElement.Crc32 ) + { + memcpy1( ( uint8_t* ) &Nvm.SecureElement,( uint8_t* ) &nvm->SecureElement, + sizeof( Nvm.SecureElement ) ); + } + + // RegionGroup1 + crc = Crc32( ( uint8_t* ) &nvm->RegionGroup1, sizeof( nvm->RegionGroup1 ) - + sizeof( nvm->RegionGroup1.Crc32 ) ); + if( crc == nvm->RegionGroup1.Crc32 ) + { + memcpy1( ( uint8_t* ) &Nvm.RegionGroup1,( uint8_t* ) &nvm->RegionGroup1, + sizeof( Nvm.RegionGroup1 ) ); + } + + // RegionGroup2 + crc = Crc32( ( uint8_t* ) &nvm->RegionGroup2, sizeof( nvm->RegionGroup2 ) - + sizeof( nvm->RegionGroup2.Crc32 ) ); + if( crc == nvm->RegionGroup2.Crc32 ) + { + memcpy1( ( uint8_t* ) &Nvm.RegionGroup2,( uint8_t* ) &nvm->RegionGroup2, + sizeof( Nvm.RegionGroup2 ) ); + } + + crc = Crc32( ( uint8_t* ) &nvm->ClassB, sizeof( nvm->ClassB ) - + sizeof( nvm->ClassB.Crc32 ) ); + if( crc == nvm->ClassB.Crc32 ) + { + memcpy1( ( uint8_t* ) &Nvm.ClassB,( uint8_t* ) &nvm->ClassB, + sizeof( Nvm.ClassB ) ); + } + + return LORAMAC_STATUS_OK; +} + +LoRaMacStatus_t DetermineFrameType( LoRaMacMessageData_t* macMsg, FType_t* fType ) +{ + if( ( macMsg == NULL ) || ( fType == NULL ) ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + /* The LoRaWAN specification allows several possible configurations how data up/down frames are built up. + * In sake of clearness the following naming is applied. Please keep in mind that this is + * implementation specific since there is no definition in the LoRaWAN specification included. + * + * X -> Field is available + * - -> Field is not available + * + * +-------+ +----------+------+-------+--------------+ + * | FType | | FOptsLen | Fopt | FPort | FRMPayload | + * +-------+ +----------+------+-------+--------------+ + * | A | | > 0 | X | > 0 | X | + * +-------+ +----------+------+-------+--------------+ + * | B | | >= 0 | X/- | - | - | + * +-------+ +----------+------+-------+--------------+ + * | C | | = 0 | - | = 0 | MAC commands | + * +-------+ +----------+------+-------+--------------+ + * | D | | = 0 | - | > 0 | X | + * +-------+ +----------+------+-------+--------------+ + */ + + if( ( macMsg->FHDR.FCtrl.Bits.FOptsLen > 0 ) && ( macMsg->FPort > 0 ) ) + { + *fType = FRAME_TYPE_A; + } + else if( macMsg->FRMPayloadSize == 0 ) + { + *fType = FRAME_TYPE_B; + } + else if( ( macMsg->FHDR.FCtrl.Bits.FOptsLen == 0 ) && ( macMsg->FPort == 0 ) ) + { + *fType = FRAME_TYPE_C; + } + else if( ( macMsg->FHDR.FCtrl.Bits.FOptsLen == 0 ) && ( macMsg->FPort > 0 ) ) + { + *fType = FRAME_TYPE_D; + } + else + { + // Should never happen. + return LORAMAC_STATUS_ERROR; + } + + return LORAMAC_STATUS_OK; +} + +static bool CheckRetrans( uint8_t counter, uint8_t limit ) +{ + if( counter >= limit ) + { + return true; + } + return false; +} + +static bool CheckRetransUnconfirmedUplink( void ) +{ + // Verify, if the max number of retransmissions have been reached + if( CheckRetrans( MacCtx.ChannelsNbTransCounter, + Nvm.MacGroup2.MacParams.ChannelsNbTrans ) == true ) + { + return true; + } + + if( MacCtx.MacFlags.Bits.McpsInd == 1 ) + { + // Stop the retransmissions, if a valid downlink is received + // a class A RX window. This holds also for class B and C. + if( ( MacCtx.McpsIndication.RxSlot == RX_SLOT_WIN_1 ) || + ( MacCtx.McpsIndication.RxSlot == RX_SLOT_WIN_2 ) ) + { + return true; + } + } + return false; +} + +static bool CheckRetransConfirmedUplink( void ) +{ + // Verify, if the max number of retransmissions have been reached + if( CheckRetrans( MacCtx.ChannelsNbTransCounter, + Nvm.MacGroup2.MacParams.ChannelsNbTrans ) == true ) + { + return true; + } + + if( MacCtx.MacFlags.Bits.McpsInd == 1 ) + { + if( MacCtx.McpsConfirm.AckReceived == true ) + { + return true; + } + } + return false; +} + +static uint32_t IncreaseAdrAckCounter( uint32_t counter ) +{ + if( counter < ADR_ACK_COUNTER_MAX ) + { + counter++; + } + return counter; +} + +static bool StopRetransmission( void ) +{ + // Increase Rejoin Uplinks counter + if( Nvm.MacGroup2.Rejoin0UplinksLimit != 0 ) + { + Nvm.MacGroup1.Rejoin0UplinksCounter++; + } + + if( Nvm.MacGroup2.Version.Fields.Minor >= 1 ) + { + MacCommand_t* macCmd; + if( LoRaMacCommandsGetCmd( MOTE_MAC_REKEY_IND, &macCmd ) == LORAMAC_COMMANDS_SUCCESS ) + { + // Increase the Rekey Uplinks counter + Nvm.MacGroup1.RekeyIndUplinksCounter++; + + /* + * If the device has not received a RekeyConf within + * the first ADR_ACK_LIMIT uplinks it SHALL revert to the Join state. + */ + if( Nvm.MacGroup1.RekeyIndUplinksCounter == Nvm.MacGroup2.MacParams.AdrAckLimit ) + { + Nvm.MacGroup2.NetworkActivation = ACTIVATION_TYPE_NONE; + MacCtx.MacFlags.Bits.MlmeInd = 1; + MacCtx.MlmeIndication.MlmeIndication = MLME_REVERT_JOIN; + } + } + } + + if( ( MacCtx.MacFlags.Bits.McpsInd == 0 ) || + ( ( MacCtx.McpsIndication.RxSlot != RX_SLOT_WIN_1 ) && + ( MacCtx.McpsIndication.RxSlot != RX_SLOT_WIN_2 ) ) ) + { // Maximum repetitions without downlink. Increase ADR Ack counter. + // Only process the case when the MAC did not receive a downlink. + if( Nvm.MacGroup2.AdrCtrlOn == true ) + { + Nvm.MacGroup1.AdrAckCounter = IncreaseAdrAckCounter( Nvm.MacGroup1.AdrAckCounter ); + } + } + + MacCtx.ChannelsNbTransCounter = 0; + MacCtx.NodeAckRequested = false; + MacCtx.RetransmitTimeoutRetry = false; + MacCtx.MacState &= ~LORAMAC_TX_RUNNING; + + return true; +} + +static void OnMacProcessNotify( void ) +{ + if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->MacProcessNotify != NULL ) ) + { + MacCtx.MacCallbacks->MacProcessNotify( ); + } +} + +static void CallNvmDataChangeCallback( uint16_t notifyFlags ) +{ + if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->NvmDataChange != NULL ) ) + { + MacCtx.MacCallbacks->NvmDataChange ( notifyFlags ); + } +} + +static uint8_t IsRequestPending( void ) +{ + if( ( MacCtx.MacFlags.Bits.MlmeReq == 1 ) || + ( MacCtx.MacFlags.Bits.McpsReq == 1 ) ) + { + return 1; + } + return 0; +} + + +LoRaMacStatus_t LoRaMacInitialization( LoRaMacPrimitives_t* primitives, LoRaMacCallback_t* callbacks, LoRaMacRegion_t region ) +{ + GetPhyParams_t getPhy; + PhyParam_t phyParam; + + if( ( primitives == NULL ) || + ( callbacks == NULL ) ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + if( ( primitives->MacMcpsConfirm == NULL ) || + ( primitives->MacMcpsIndication == NULL ) || + ( primitives->MacMlmeConfirm == NULL ) || + ( primitives->MacMlmeIndication == NULL ) ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + // Verify if the region is supported + if( RegionIsActive( region ) == false ) + { + return LORAMAC_STATUS_REGION_NOT_SUPPORTED; + } + + // Confirm queue reset + LoRaMacConfirmQueueInit( primitives ); + + // Initialize the module context with zeros + memset1( ( uint8_t* ) &Nvm, 0x00, sizeof( LoRaMacNvmData_t ) ); + memset1( ( uint8_t* ) &MacCtx, 0x00, sizeof( LoRaMacCtx_t ) ); + + // Set non zero variables to its default value + Nvm.MacGroup2.Region = region; + Nvm.MacGroup2.DeviceClass = CLASS_A; + + // Setup version + Nvm.MacGroup2.Version.Value = LORAMAC_VERSION; + + InitDefaultsParams_t params; + params.Type = INIT_TYPE_DEFAULTS; + params.NvmGroup1 = &Nvm.RegionGroup1; + params.NvmGroup2 = &Nvm.RegionGroup2; + params.Bands = &RegionBands; + RegionInitDefaults( Nvm.MacGroup2.Region, ¶ms ); + + // Reset to defaults + getPhy.Attribute = PHY_DUTY_CYCLE; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup2.DutyCycleOn = ( bool ) phyParam.Value; + + getPhy.Attribute = PHY_DEF_TX_POWER; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup2.ChannelsTxPowerDefault = phyParam.Value; + + getPhy.Attribute = PHY_DEF_TX_DR; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup2.ChannelsDatarateDefault = phyParam.Value; + + getPhy.Attribute = PHY_MAX_RX_WINDOW; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup2.MacParamsDefaults.MaxRxWindow = phyParam.Value; + + getPhy.Attribute = PHY_RECEIVE_DELAY1; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup2.MacParamsDefaults.ReceiveDelay1 = phyParam.Value; + + getPhy.Attribute = PHY_RECEIVE_DELAY2; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup2.MacParamsDefaults.ReceiveDelay2 = phyParam.Value; + + getPhy.Attribute = PHY_JOIN_ACCEPT_DELAY1; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup2.MacParamsDefaults.JoinAcceptDelay1 = phyParam.Value; + + getPhy.Attribute = PHY_JOIN_ACCEPT_DELAY2; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup2.MacParamsDefaults.JoinAcceptDelay2 = phyParam.Value; + + getPhy.Attribute = PHY_DEF_DR1_OFFSET; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup2.MacParamsDefaults.Rx1DrOffset = phyParam.Value; + + getPhy.Attribute = PHY_DEF_RX2_FREQUENCY; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup2.MacParamsDefaults.Rx2Channel.Frequency = phyParam.Value; + Nvm.MacGroup2.MacParamsDefaults.RxCChannel.Frequency = phyParam.Value; + + getPhy.Attribute = PHY_DEF_RX2_DR; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup2.MacParamsDefaults.Rx2Channel.Datarate = phyParam.Value; + Nvm.MacGroup2.MacParamsDefaults.RxCChannel.Datarate = phyParam.Value; + + getPhy.Attribute = PHY_DEF_UPLINK_DWELL_TIME; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup2.MacParamsDefaults.UplinkDwellTime = phyParam.Value; + + getPhy.Attribute = PHY_DEF_DOWNLINK_DWELL_TIME; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup2.MacParamsDefaults.DownlinkDwellTime = phyParam.Value; + + getPhy.Attribute = PHY_DEF_MAX_EIRP; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup2.MacParamsDefaults.MaxEirp = phyParam.fValue; + + getPhy.Attribute = PHY_DEF_ANTENNA_GAIN; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup2.MacParamsDefaults.AntennaGain = phyParam.fValue; + + getPhy.Attribute = PHY_DEF_ADR_ACK_LIMIT; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup2.MacParamsDefaults.AdrAckLimit = phyParam.Value; + + getPhy.Attribute = PHY_DEF_ADR_ACK_DELAY; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + Nvm.MacGroup2.MacParamsDefaults.AdrAckDelay = phyParam.Value; + + // Init parameters which are not set in function ResetMacParameters + Nvm.MacGroup2.MacParamsDefaults.ChannelsNbTrans = 1; + Nvm.MacGroup2.MacParamsDefaults.SystemMaxRxError = 10; + Nvm.MacGroup2.MacParamsDefaults.MinRxSymbols = 6; + + Nvm.MacGroup2.MacParams.SystemMaxRxError = Nvm.MacGroup2.MacParamsDefaults.SystemMaxRxError; + Nvm.MacGroup2.MacParams.MinRxSymbols = Nvm.MacGroup2.MacParamsDefaults.MinRxSymbols; + Nvm.MacGroup2.MacParams.MaxRxWindow = Nvm.MacGroup2.MacParamsDefaults.MaxRxWindow; + Nvm.MacGroup2.MacParams.ReceiveDelay1 = Nvm.MacGroup2.MacParamsDefaults.ReceiveDelay1; + Nvm.MacGroup2.MacParams.ReceiveDelay2 = Nvm.MacGroup2.MacParamsDefaults.ReceiveDelay2; + Nvm.MacGroup2.MacParams.JoinAcceptDelay1 = Nvm.MacGroup2.MacParamsDefaults.JoinAcceptDelay1; + Nvm.MacGroup2.MacParams.JoinAcceptDelay2 = Nvm.MacGroup2.MacParamsDefaults.JoinAcceptDelay2; + Nvm.MacGroup2.MacParams.ChannelsNbTrans = Nvm.MacGroup2.MacParamsDefaults.ChannelsNbTrans; + + // FPort 224 is enabled by default. + Nvm.MacGroup2.IsCertPortOn = true; + + ResetMacParameters( false ); + + Nvm.MacGroup2.PublicNetwork = true; + + MacCtx.MacPrimitives = primitives; + MacCtx.MacCallbacks = callbacks; + MacCtx.MacFlags.Value = 0; + MacCtx.MacState = LORAMAC_STOPPED; + + // Reset duty cycle times + Nvm.MacGroup1.LastTxDoneTime = 0; + Nvm.MacGroup1.AggregatedTimeOff = 0; + + // Initialize timers + TimerInit( &MacCtx.TxDelayedTimer, OnTxDelayedTimerEvent ); + TimerInit( &MacCtx.RxWindowTimer1, OnRxWindow1TimerEvent ); + TimerInit( &MacCtx.RxWindowTimer2, OnRxWindow2TimerEvent ); + TimerInit( &MacCtx.RetransmitTimeoutTimer, OnRetransmitTimeoutTimerEvent ); + TimerInit( &MacCtx.Rejoin0CycleTimer, OnRejoin0CycleTimerEvent ); + TimerInit( &MacCtx.Rejoin1CycleTimer, OnRejoin1CycleTimerEvent ); + TimerInit( &MacCtx.ForceRejoinReqCycleTimer, OnForceRejoinReqCycleTimerEvent ); + + // Store the current initialization time + Nvm.MacGroup2.InitializationTime = SysTimeGetMcuTime( ); + + // Initialize MAC radio events + LoRaMacRadioEvents.Value = 0; + + // Initialize Radio driver + MacCtx.RadioEvents.TxDone = OnRadioTxDone; + MacCtx.RadioEvents.RxDone = OnRadioRxDone; + MacCtx.RadioEvents.RxError = OnRadioRxError; + MacCtx.RadioEvents.TxTimeout = OnRadioTxTimeout; + MacCtx.RadioEvents.RxTimeout = OnRadioRxTimeout; + Radio.Init( &MacCtx.RadioEvents ); + + // Initialize the Secure Element driver + if( SecureElementInit( &Nvm.SecureElement ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + + // Initialize Crypto module + if( LoRaMacCryptoInit( &Nvm.Crypto ) != LORAMAC_CRYPTO_SUCCESS ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + + // Initialize MAC commands module + if( LoRaMacCommandsInit( ) != LORAMAC_COMMANDS_SUCCESS ) + { + return LORAMAC_STATUS_MAC_COMMAD_ERROR; + } + + // Set multicast downlink counter reference + if( LoRaMacCryptoSetMulticastReference( Nvm.MacGroup2.MulticastChannelList ) != LORAMAC_CRYPTO_SUCCESS ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + + // Random seed initialization + srand1( Radio.Random( ) ); + + Radio.SetPublicNetwork( Nvm.MacGroup2.PublicNetwork ); + Radio.Sleep( ); + + LoRaMacEnableRequests( LORAMAC_REQUEST_HANDLING_ON ); + + return LORAMAC_STATUS_OK; +} + +LoRaMacStatus_t LoRaMacStart( void ) +{ + MacCtx.MacState = LORAMAC_IDLE; + UpdateRxSlotIdleState(); + return LORAMAC_STATUS_OK; +} + +LoRaMacStatus_t LoRaMacStop( void ) +{ + if( LoRaMacIsBusy( ) == false ) + { + if( Nvm.MacGroup2.DeviceClass == CLASS_C ) + { + Radio.Sleep( ); + } + MacCtx.MacState = LORAMAC_STOPPED; + return LORAMAC_STATUS_OK; + } + else if( MacCtx.MacState == LORAMAC_STOPPED ) + { + return LORAMAC_STATUS_OK; + } + return LORAMAC_STATUS_BUSY; +} + +LoRaMacStatus_t LoRaMacQueryTxPossible( uint8_t size, LoRaMacTxInfo_t* txInfo ) +{ + CalcNextAdrParams_t adrNext; + uint32_t adrAckCounter = Nvm.MacGroup1.AdrAckCounter; + int8_t datarate = Nvm.MacGroup2.ChannelsDatarateDefault; + int8_t txPower = Nvm.MacGroup2.ChannelsTxPowerDefault; + uint8_t nbTrans = MacCtx.ChannelsNbTransCounter; + size_t macCmdsSize = 0; + + if( txInfo == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + // Setup ADR request + adrNext.UpdateChanMask = false; + adrNext.AdrEnabled = Nvm.MacGroup2.AdrCtrlOn; + adrNext.AdrAckCounter = Nvm.MacGroup1.AdrAckCounter; + adrNext.AdrAckLimit = Nvm.MacGroup2.MacParams.AdrAckLimit; + adrNext.AdrAckDelay = Nvm.MacGroup2.MacParams.AdrAckDelay; + adrNext.Datarate = Nvm.MacGroup1.ChannelsDatarate; + adrNext.TxPower = Nvm.MacGroup1.ChannelsTxPower; + adrNext.NbTrans = MacCtx.ChannelsNbTransCounter; + adrNext.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime; + adrNext.Region = Nvm.MacGroup2.Region; + + // We call the function for information purposes only. We don't want to + // apply the datarate, the tx power and the ADR ack counter. + LoRaMacAdrCalcNext( &adrNext, &datarate, &txPower, &nbTrans, &adrAckCounter ); + + txInfo->CurrentPossiblePayloadSize = GetMaxAppPayloadWithoutFOptsLength( datarate ); + + if( LoRaMacCommandsGetSizeSerializedCmds( &macCmdsSize ) != LORAMAC_COMMANDS_SUCCESS ) + { + return LORAMAC_STATUS_MAC_COMMAD_ERROR; + } + + // Verify if the MAC commands fit into the FOpts and into the maximum payload. + if( ( LORA_MAC_COMMAND_MAX_FOPTS_LENGTH >= macCmdsSize ) && ( txInfo->CurrentPossiblePayloadSize >= macCmdsSize ) ) + { + txInfo->MaxPossibleApplicationDataSize = txInfo->CurrentPossiblePayloadSize - macCmdsSize; + + // Verify if the application data together with MAC command fit into the maximum payload. + if( txInfo->CurrentPossiblePayloadSize >= ( macCmdsSize + size ) ) + { + return LORAMAC_STATUS_OK; + } + else + { + return LORAMAC_STATUS_LENGTH_ERROR; + } + } + else + { + txInfo->MaxPossibleApplicationDataSize = 0; + return LORAMAC_STATUS_LENGTH_ERROR; + } +} + +LoRaMacStatus_t LoRaMacMibGetRequestConfirm( MibRequestConfirm_t* mibGet ) +{ + LoRaMacStatus_t status = LORAMAC_STATUS_OK; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + + if( mibGet == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + switch( mibGet->Type ) + { + case MIB_DEVICE_CLASS: + { + mibGet->Param.Class = Nvm.MacGroup2.DeviceClass; + break; + } + case MIB_NETWORK_ACTIVATION: + { + mibGet->Param.NetworkActivation = Nvm.MacGroup2.NetworkActivation; + break; + } + case MIB_DEV_EUI: + { + mibGet->Param.DevEui = SecureElementGetDevEui( ); + break; + } + case MIB_JOIN_EUI: + { + mibGet->Param.JoinEui = SecureElementGetJoinEui( ); + break; + } + case MIB_SE_PIN: + { + mibGet->Param.SePin = SecureElementGetPin( ); + break; + } + case MIB_ADR: + { + mibGet->Param.AdrEnable = Nvm.MacGroup2.AdrCtrlOn; + break; + } + case MIB_NET_ID: + { + mibGet->Param.NetID = Nvm.MacGroup2.NetID; + break; + } + case MIB_DEV_ADDR: + { + mibGet->Param.DevAddr = Nvm.MacGroup2.DevAddr; + break; + } + case MIB_PUBLIC_NETWORK: + { + mibGet->Param.EnablePublicNetwork = Nvm.MacGroup2.PublicNetwork; + break; + } + case MIB_CHANNELS: + { + getPhy.Attribute = PHY_CHANNELS; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + + mibGet->Param.ChannelList = phyParam.Channels; + break; + } + case MIB_RX2_CHANNEL: + { + mibGet->Param.Rx2Channel = Nvm.MacGroup2.MacParams.Rx2Channel; + break; + } + case MIB_RX2_DEFAULT_CHANNEL: + { + mibGet->Param.Rx2Channel = Nvm.MacGroup2.MacParamsDefaults.Rx2Channel; + break; + } + case MIB_RXC_CHANNEL: + { + mibGet->Param.RxCChannel = Nvm.MacGroup2.MacParams.RxCChannel; + break; + } + case MIB_RXC_DEFAULT_CHANNEL: + { + mibGet->Param.RxCChannel = Nvm.MacGroup2.MacParamsDefaults.RxCChannel; + break; + } + case MIB_CHANNELS_DEFAULT_MASK: + { + getPhy.Attribute = PHY_CHANNELS_DEFAULT_MASK; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + + mibGet->Param.ChannelsDefaultMask = phyParam.ChannelsMask; + break; + } + case MIB_CHANNELS_MASK: + { + getPhy.Attribute = PHY_CHANNELS_MASK; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + + mibGet->Param.ChannelsMask = phyParam.ChannelsMask; + break; + } + case MIB_CHANNELS_NB_TRANS: + { + mibGet->Param.ChannelsNbTrans = Nvm.MacGroup2.MacParams.ChannelsNbTrans; + break; + } + case MIB_MAX_RX_WINDOW_DURATION: + { + mibGet->Param.MaxRxWindow = Nvm.MacGroup2.MacParams.MaxRxWindow; + break; + } + case MIB_RECEIVE_DELAY_1: + { + mibGet->Param.ReceiveDelay1 = Nvm.MacGroup2.MacParams.ReceiveDelay1; + break; + } + case MIB_RECEIVE_DELAY_2: + { + mibGet->Param.ReceiveDelay2 = Nvm.MacGroup2.MacParams.ReceiveDelay2; + break; + } + case MIB_JOIN_ACCEPT_DELAY_1: + { + mibGet->Param.JoinAcceptDelay1 = Nvm.MacGroup2.MacParams.JoinAcceptDelay1; + break; + } + case MIB_JOIN_ACCEPT_DELAY_2: + { + mibGet->Param.JoinAcceptDelay2 = Nvm.MacGroup2.MacParams.JoinAcceptDelay2; + break; + } + case MIB_CHANNELS_MIN_TX_DATARATE: + { + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + + mibGet->Param.ChannelsMinTxDatarate = phyParam.Value; + break; + } + case MIB_CHANNELS_DEFAULT_DATARATE: + { + mibGet->Param.ChannelsDefaultDatarate = Nvm.MacGroup2.ChannelsDatarateDefault; + break; + } + case MIB_CHANNELS_DATARATE: + { + mibGet->Param.ChannelsDatarate = Nvm.MacGroup1.ChannelsDatarate; + break; + } + case MIB_CHANNELS_DEFAULT_TX_POWER: + { + mibGet->Param.ChannelsDefaultTxPower = Nvm.MacGroup2.ChannelsTxPowerDefault; + break; + } + case MIB_CHANNELS_TX_POWER: + { + mibGet->Param.ChannelsTxPower = Nvm.MacGroup1.ChannelsTxPower; + break; + } + case MIB_SYSTEM_MAX_RX_ERROR: + { + mibGet->Param.SystemMaxRxError = Nvm.MacGroup2.MacParams.SystemMaxRxError; + break; + } + case MIB_MIN_RX_SYMBOLS: + { + mibGet->Param.MinRxSymbols = Nvm.MacGroup2.MacParams.MinRxSymbols; + break; + } + case MIB_ANTENNA_GAIN: + { + mibGet->Param.AntennaGain = Nvm.MacGroup2.MacParams.AntennaGain; + break; + } + case MIB_NVM_CTXS: + { + mibGet->Param.Contexts = GetNvmData( ); + break; + } + case MIB_DEFAULT_ANTENNA_GAIN: + { + mibGet->Param.DefaultAntennaGain = Nvm.MacGroup2.MacParamsDefaults.AntennaGain; + break; + } + case MIB_LORAWAN_VERSION: + { + mibGet->Param.LrWanVersion.LoRaWan = Nvm.MacGroup2.Version; + mibGet->Param.LrWanVersion.LoRaWanRegion = RegionGetVersion( ); + break; + } + case MIB_IS_CERT_FPORT_ON: + { + mibGet->Param.IsCertPortOn = Nvm.MacGroup2.IsCertPortOn; + break; + } + case MIB_REJOIN_0_CYCLE: + { + mibGet->Param.Rejoin0CycleInSec = Nvm.MacGroup2.Rejoin0CycleInSec; + break; + } + case MIB_REJOIN_1_CYCLE: + { + mibGet->Param.Rejoin1CycleInSec = Nvm.MacGroup2.Rejoin1CycleInSec; + break; + } + case MIB_ADR_ACK_LIMIT: + { + mibGet->Param.AdrAckLimit = Nvm.MacGroup2.MacParams.AdrAckLimit; + break; + } + case MIB_ADR_ACK_DELAY: + { + mibGet->Param.AdrAckDelay = Nvm.MacGroup2.MacParams.AdrAckDelay; + break; + } + case MIB_ADR_ACK_DEFAULT_LIMIT: + { + mibGet->Param.AdrAckLimit = Nvm.MacGroup2.MacParamsDefaults.AdrAckLimit; + break; + } + case MIB_ADR_ACK_DEFAULT_DELAY: + { + mibGet->Param.AdrAckDelay = Nvm.MacGroup2.MacParamsDefaults.AdrAckDelay; + break; + } + case MIB_RSSI_FREE_THRESHOLD: + { +#if defined(REGION_KR920) || defined(REGION_AS923) + if( Nvm.MacGroup2.Region != LORAMAC_REGION_AS923 && Nvm.MacGroup2.Region != LORAMAC_REGION_KR920 ) + { + status = LORAMAC_STATUS_ERROR; + } + else + { + mibGet->Param.RssiFreeThreshold = Nvm.RegionGroup2.RssiFreeThreshold; + } +#else + status = LORAMAC_STATUS_ERROR; +#endif + break; + } + case MIB_CARRIER_SENSE_TIME: + { +#if defined(REGION_KR920) || defined(REGION_AS923) + if( Nvm.MacGroup2.Region != LORAMAC_REGION_AS923 && Nvm.MacGroup2.Region != LORAMAC_REGION_KR920 ) + { + status = LORAMAC_STATUS_ERROR; + } + else + { + mibGet->Param.CarrierSenseTime = Nvm.RegionGroup2.CarrierSenseTime; + } +#else + status = LORAMAC_STATUS_ERROR; +#endif + break; + } + default: + { + status = LoRaMacClassBMibGetRequestConfirm( mibGet ); + break; + } + } + return status; +} + +LoRaMacStatus_t LoRaMacMibSetRequestConfirm( MibRequestConfirm_t* mibSet ) +{ + LoRaMacStatus_t status = LORAMAC_STATUS_OK; + ChanMaskSetParams_t chanMaskSet; + VerifyParams_t verify; + + if( mibSet == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING ) + { + return LORAMAC_STATUS_BUSY; + } + + switch( mibSet->Type ) + { + case MIB_DEVICE_CLASS: + { + status = SwitchClass( mibSet->Param.Class ); + break; + } + case MIB_NETWORK_ACTIVATION: + { + if( mibSet->Param.NetworkActivation != ACTIVATION_TYPE_OTAA ) + { + Nvm.MacGroup2.NetworkActivation = mibSet->Param.NetworkActivation; + } + else + { // Do not allow to set ACTIVATION_TYPE_OTAA since the MAC will set it automatically after a successful join process. + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_DEV_EUI: + { + if( SecureElementSetDevEui( mibSet->Param.DevEui ) != SECURE_ELEMENT_SUCCESS ) + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_JOIN_EUI: + { + if( SecureElementSetJoinEui( mibSet->Param.JoinEui ) != SECURE_ELEMENT_SUCCESS ) + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_SE_PIN: + { + if( SecureElementSetPin( mibSet->Param.SePin ) != SECURE_ELEMENT_SUCCESS ) + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_ADR: + { + Nvm.MacGroup2.AdrCtrlOn = mibSet->Param.AdrEnable; + break; + } + case MIB_NET_ID: + { + Nvm.MacGroup2.NetID = mibSet->Param.NetID; + break; + } + case MIB_DEV_ADDR: + { + Nvm.MacGroup2.DevAddr = mibSet->Param.DevAddr; + break; + } + case MIB_APP_KEY: + { + if( mibSet->Param.AppKey != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( APP_KEY, mibSet->Param.AppKey ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_NWK_KEY: + { + if( mibSet->Param.NwkKey != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( NWK_KEY, mibSet->Param.NwkKey ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_J_S_INT_KEY: + { + if( mibSet->Param.JSIntKey != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( J_S_INT_KEY, mibSet->Param.JSIntKey ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_J_S_ENC_KEY: + { + if( mibSet->Param.JSEncKey != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( J_S_ENC_KEY, mibSet->Param.JSEncKey ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_F_NWK_S_INT_KEY: + { + if( mibSet->Param.FNwkSIntKey != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( F_NWK_S_INT_KEY, mibSet->Param.FNwkSIntKey ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_S_NWK_S_INT_KEY: + { + if( mibSet->Param.SNwkSIntKey != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( S_NWK_S_INT_KEY, mibSet->Param.SNwkSIntKey ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_NWK_S_ENC_KEY: + { + if( mibSet->Param.NwkSEncKey != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( NWK_S_ENC_KEY, mibSet->Param.NwkSEncKey ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_APP_S_KEY: + { + if( mibSet->Param.AppSKey != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( APP_S_KEY, mibSet->Param.AppSKey ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_MC_KE_KEY: + { + if( mibSet->Param.McKEKey != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_KE_KEY, mibSet->Param.McKEKey ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_MC_KEY_0: + { + if( mibSet->Param.McKey0 != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_KEY_0, mibSet->Param.McKey0 ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_MC_APP_S_KEY_0: + { + if( mibSet->Param.McAppSKey0 != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_APP_S_KEY_0, mibSet->Param.McAppSKey0 ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_MC_NWK_S_KEY_0: + { + if( mibSet->Param.McNwkSKey0 != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_NWK_S_KEY_0, mibSet->Param.McNwkSKey0 ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_MC_KEY_1: + { + if( mibSet->Param.McKey1 != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_KEY_1, mibSet->Param.McKey1 ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_MC_APP_S_KEY_1: + { + if( mibSet->Param.McAppSKey1 != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_APP_S_KEY_1, mibSet->Param.McAppSKey1 ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_MC_NWK_S_KEY_1: + { + if( mibSet->Param.McNwkSKey1 != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_NWK_S_KEY_1, mibSet->Param.McNwkSKey1 ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_MC_KEY_2: + { + if( mibSet->Param.McKey2 != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_KEY_2, mibSet->Param.McKey2 ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_MC_APP_S_KEY_2: + { + if( mibSet->Param.McAppSKey2 != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_APP_S_KEY_2, mibSet->Param.McAppSKey2 ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_MC_NWK_S_KEY_2: + { + if( mibSet->Param.McNwkSKey2 != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_NWK_S_KEY_2, mibSet->Param.McNwkSKey2 ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_MC_KEY_3: + { + if( mibSet->Param.McKey3 != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_KEY_3, mibSet->Param.McKey3 ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_MC_APP_S_KEY_3: + { + if( mibSet->Param.McAppSKey3 != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_APP_S_KEY_3, mibSet->Param.McAppSKey3 ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_MC_NWK_S_KEY_3: + { + if( mibSet->Param.McNwkSKey3 != NULL ) + { + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_NWK_S_KEY_3, mibSet->Param.McNwkSKey3 ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_PUBLIC_NETWORK: + { + Nvm.MacGroup2.PublicNetwork = mibSet->Param.EnablePublicNetwork; + Radio.SetPublicNetwork( Nvm.MacGroup2.PublicNetwork ); + break; + } + case MIB_RX2_CHANNEL: + { + verify.DatarateParams.Datarate = mibSet->Param.Rx2Channel.Datarate; + verify.DatarateParams.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime; + + if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_RX_DR ) == true ) + { + Nvm.MacGroup2.MacParams.Rx2Channel = mibSet->Param.Rx2Channel; + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_RX2_DEFAULT_CHANNEL: + { + verify.DatarateParams.Datarate = mibSet->Param.Rx2Channel.Datarate; + verify.DatarateParams.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime; + + if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_RX_DR ) == true ) + { + Nvm.MacGroup2.MacParamsDefaults.Rx2Channel = mibSet->Param.Rx2DefaultChannel; + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_RXC_CHANNEL: + { + verify.DatarateParams.Datarate = mibSet->Param.RxCChannel.Datarate; + verify.DatarateParams.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime; + + if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_RX_DR ) == true ) + { + Nvm.MacGroup2.MacParams.RxCChannel = mibSet->Param.RxCChannel; + + if( ( Nvm.MacGroup2.DeviceClass == CLASS_C ) && ( Nvm.MacGroup2.NetworkActivation != ACTIVATION_TYPE_NONE ) ) + { + // We can only compute the RX window parameters directly, if we are already + // in class c mode and joined. We cannot setup an RX window in case of any other + // class type. + // Set the radio into sleep mode in case we are still in RX mode + Radio.Sleep( ); + + OpenContinuousRxCWindow( ); + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_RXC_DEFAULT_CHANNEL: + { + verify.DatarateParams.Datarate = mibSet->Param.RxCChannel.Datarate; + verify.DatarateParams.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime; + + if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_RX_DR ) == true ) + { + Nvm.MacGroup2.MacParamsDefaults.RxCChannel = mibSet->Param.RxCDefaultChannel; + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_CHANNELS_DEFAULT_MASK: + { + chanMaskSet.ChannelsMaskIn = mibSet->Param.ChannelsDefaultMask; + chanMaskSet.ChannelsMaskType = CHANNELS_DEFAULT_MASK; + + if( RegionChanMaskSet( Nvm.MacGroup2.Region, &chanMaskSet ) == false ) + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_CHANNELS_MASK: + { + chanMaskSet.ChannelsMaskIn = mibSet->Param.ChannelsMask; + chanMaskSet.ChannelsMaskType = CHANNELS_MASK; + + if( RegionChanMaskSet( Nvm.MacGroup2.Region, &chanMaskSet ) == false ) + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_CHANNELS_NB_TRANS: + { + if( ( mibSet->Param.ChannelsNbTrans >= 1 ) && + ( mibSet->Param.ChannelsNbTrans <= 15 ) ) + { + Nvm.MacGroup2.MacParams.ChannelsNbTrans = mibSet->Param.ChannelsNbTrans; + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_MAX_RX_WINDOW_DURATION: + { + Nvm.MacGroup2.MacParams.MaxRxWindow = mibSet->Param.MaxRxWindow; + break; + } + case MIB_RECEIVE_DELAY_1: + { + Nvm.MacGroup2.MacParams.ReceiveDelay1 = mibSet->Param.ReceiveDelay1; + break; + } + case MIB_RECEIVE_DELAY_2: + { + Nvm.MacGroup2.MacParams.ReceiveDelay2 = mibSet->Param.ReceiveDelay2; + break; + } + case MIB_JOIN_ACCEPT_DELAY_1: + { + Nvm.MacGroup2.MacParams.JoinAcceptDelay1 = mibSet->Param.JoinAcceptDelay1; + break; + } + case MIB_JOIN_ACCEPT_DELAY_2: + { + Nvm.MacGroup2.MacParams.JoinAcceptDelay2 = mibSet->Param.JoinAcceptDelay2; + break; + } + case MIB_CHANNELS_DEFAULT_DATARATE: + { + verify.DatarateParams.Datarate = mibSet->Param.ChannelsDefaultDatarate; + + if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_DEF_TX_DR ) == true ) + { + Nvm.MacGroup2.ChannelsDatarateDefault = verify.DatarateParams.Datarate; + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_CHANNELS_DATARATE: + { + verify.DatarateParams.Datarate = mibSet->Param.ChannelsDatarate; + verify.DatarateParams.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime; + + if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_TX_DR ) == true ) + { + Nvm.MacGroup1.ChannelsDatarate = verify.DatarateParams.Datarate; + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_CHANNELS_DEFAULT_TX_POWER: + { + verify.TxPower = mibSet->Param.ChannelsDefaultTxPower; + + if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_DEF_TX_POWER ) == true ) + { + Nvm.MacGroup2.ChannelsTxPowerDefault = verify.TxPower; + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_CHANNELS_TX_POWER: + { + verify.TxPower = mibSet->Param.ChannelsTxPower; + + if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_TX_POWER ) == true ) + { + Nvm.MacGroup1.ChannelsTxPower = verify.TxPower; + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_SYSTEM_MAX_RX_ERROR: + { + if( mibSet->Param.SystemMaxRxError <= 500 ) + { // Only apply the new value if in range 0..500 ms else keep current value. + Nvm.MacGroup2.MacParams.SystemMaxRxError = Nvm.MacGroup2.MacParamsDefaults.SystemMaxRxError = mibSet->Param.SystemMaxRxError; + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_MIN_RX_SYMBOLS: + { + Nvm.MacGroup2.MacParams.MinRxSymbols = Nvm.MacGroup2.MacParamsDefaults.MinRxSymbols = mibSet->Param.MinRxSymbols; + break; + } + case MIB_ANTENNA_GAIN: + { + Nvm.MacGroup2.MacParams.AntennaGain = mibSet->Param.AntennaGain; + break; + } + case MIB_DEFAULT_ANTENNA_GAIN: + { + Nvm.MacGroup2.MacParamsDefaults.AntennaGain = mibSet->Param.DefaultAntennaGain; + break; + } + case MIB_NVM_CTXS: + { + if( mibSet->Param.Contexts != 0 ) + { + status = RestoreNvmData( mibSet->Param.Contexts ); + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_ABP_LORAWAN_VERSION: + { + if( mibSet->Param.AbpLrWanVersion.Fields.Minor <= 1 ) + { + Nvm.MacGroup2.Version = mibSet->Param.AbpLrWanVersion; + + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetLrWanVersion( mibSet->Param.AbpLrWanVersion ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_IS_CERT_FPORT_ON: + { + Nvm.MacGroup2.IsCertPortOn = mibSet->Param.IsCertPortOn; + break; + } + case MIB_REJOIN_0_CYCLE: + { + uint32_t cycleTime = 0; + if( ( ConvertRejoinCycleTime( mibSet->Param.Rejoin0CycleInSec, &cycleTime ) == true ) && + ( Nvm.MacGroup2.NetworkActivation == ACTIVATION_TYPE_OTAA ) ) + { + Nvm.MacGroup2.Rejoin0CycleInSec = mibSet->Param.Rejoin0CycleInSec; + MacCtx.Rejoin0CycleTime = cycleTime; + TimerStop( &MacCtx.Rejoin0CycleTimer ); + TimerSetValue( &MacCtx.Rejoin0CycleTimer, MacCtx.Rejoin0CycleTime ); + TimerStart( &MacCtx.Rejoin0CycleTimer ); + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_REJOIN_1_CYCLE: + { + uint32_t cycleTime = 0; + if( ( ConvertRejoinCycleTime( mibSet->Param.Rejoin1CycleInSec, &cycleTime ) == true ) && + ( Nvm.MacGroup2.NetworkActivation == ACTIVATION_TYPE_OTAA ) ) + { + Nvm.MacGroup2.Rejoin1CycleInSec = mibSet->Param.Rejoin1CycleInSec; + MacCtx.Rejoin0CycleTime = cycleTime; + TimerStop( &MacCtx.Rejoin1CycleTimer ); + TimerSetValue( &MacCtx.Rejoin1CycleTimer, MacCtx.Rejoin1CycleTime ); + TimerStart( &MacCtx.Rejoin1CycleTimer ); + } + else + { + status = LORAMAC_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_ADR_ACK_LIMIT: + { + Nvm.MacGroup2.MacParams.AdrAckLimit = mibSet->Param.AdrAckLimit; + break; + } + case MIB_ADR_ACK_DELAY: + { + Nvm.MacGroup2.MacParams.AdrAckDelay = mibSet->Param.AdrAckDelay; + break; + } + case MIB_ADR_ACK_DEFAULT_LIMIT: + { + Nvm.MacGroup2.MacParamsDefaults.AdrAckLimit = mibSet->Param.AdrAckLimit; + break; + } + case MIB_ADR_ACK_DEFAULT_DELAY: + { + Nvm.MacGroup2.MacParamsDefaults.AdrAckDelay = mibSet->Param.AdrAckDelay; + break; + } + case MIB_RSSI_FREE_THRESHOLD: + { +#if defined(REGION_KR920) || defined(REGION_AS923) + if( Nvm.MacGroup2.Region != LORAMAC_REGION_AS923 && Nvm.MacGroup2.Region != LORAMAC_REGION_KR920 ) + { + status = LORAMAC_STATUS_ERROR; + } + else + { + Nvm.RegionGroup2.RssiFreeThreshold = mibSet->Param.RssiFreeThreshold; + } +#else + status = LORAMAC_STATUS_ERROR; +#endif + break; + } + case MIB_CARRIER_SENSE_TIME: + { +#if defined(REGION_KR920) || defined(REGION_AS923) + if( Nvm.MacGroup2.Region != LORAMAC_REGION_AS923 && Nvm.MacGroup2.Region != LORAMAC_REGION_KR920 ) + { + status = LORAMAC_STATUS_ERROR; + } + else + { + Nvm.RegionGroup2.CarrierSenseTime = mibSet->Param.CarrierSenseTime; + } +#else + status = LORAMAC_STATUS_ERROR; +#endif + break; + } + default: + { + status = LoRaMacMibClassBSetRequestConfirm( mibSet ); + break; + } + } + + if( status == LORAMAC_STATUS_OK ) + { + // Handle NVM potential changes + MacCtx.MacFlags.Bits.NvmHandle = 1; + } + return status; +} + +LoRaMacStatus_t LoRaMacChannelAdd( uint8_t id, ChannelParams_t params ) +{ + ChannelAddParams_t channelAdd; + + // Validate if the MAC is in a correct state + if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING ) + { + if( ( MacCtx.MacState & LORAMAC_TX_CONFIG ) != LORAMAC_TX_CONFIG ) + { + return LORAMAC_STATUS_BUSY; + } + } + + channelAdd.NewChannel = ¶ms; + channelAdd.ChannelId = id; + return RegionChannelAdd( Nvm.MacGroup2.Region, &channelAdd ); +} + +LoRaMacStatus_t LoRaMacChannelRemove( uint8_t id ) +{ + ChannelRemoveParams_t channelRemove; + + if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING ) + { + if( ( MacCtx.MacState & LORAMAC_TX_CONFIG ) != LORAMAC_TX_CONFIG ) + { + return LORAMAC_STATUS_BUSY; + } + } + + channelRemove.ChannelId = id; + + if( RegionChannelsRemove( Nvm.MacGroup2.Region, &channelRemove ) == false ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + return LORAMAC_STATUS_OK; +} + +LoRaMacStatus_t LoRaMacMcChannelSetup( McChannelParams_t *channel ) +{ + if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING ) + { + return LORAMAC_STATUS_BUSY; + } + + if( channel->GroupID >= LORAMAC_MAX_MC_CTX ) + { + return LORAMAC_STATUS_MC_GROUP_UNDEFINED; + } + + Nvm.MacGroup2.MulticastChannelList[channel->GroupID].ChannelParams = *channel; + MacCtx.MacFlags.Bits.NvmHandle = 1; + + if( channel->IsRemotelySetup == true ) + { + const KeyIdentifier_t mcKeys[LORAMAC_MAX_MC_CTX] = { MC_KEY_0, MC_KEY_1, MC_KEY_2, MC_KEY_3 }; + if( LoRaMacCryptoSetKey( mcKeys[channel->GroupID], channel->McKeys.McKeyE ) != LORAMAC_CRYPTO_SUCCESS ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + + if( LoRaMacCryptoDeriveMcSessionKeyPair( channel->GroupID, channel->Address ) != LORAMAC_CRYPTO_SUCCESS ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + else + { + const KeyIdentifier_t mcAppSKeys[LORAMAC_MAX_MC_CTX] = { MC_APP_S_KEY_0, MC_APP_S_KEY_1, MC_APP_S_KEY_2, MC_APP_S_KEY_3 }; + const KeyIdentifier_t mcNwkSKeys[LORAMAC_MAX_MC_CTX] = { MC_NWK_S_KEY_0, MC_NWK_S_KEY_1, MC_NWK_S_KEY_2, MC_NWK_S_KEY_3 }; + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( mcAppSKeys[channel->GroupID], channel->McKeys.Session.McAppSKey ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( mcNwkSKeys[channel->GroupID], channel->McKeys.Session.McNwkSKey ) ) + { + return LORAMAC_STATUS_CRYPTO_ERROR; + } + } + + // Reset multicast channel downlink counter to initial value. + *Nvm.MacGroup2.MulticastChannelList[channel->GroupID].DownLinkCounter = FCNT_DOWN_INITIAL_VALUE; + return LORAMAC_STATUS_OK; +} + +LoRaMacStatus_t LoRaMacMcChannelDelete( AddressIdentifier_t groupID ) +{ + if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING ) + { + return LORAMAC_STATUS_BUSY; + } + + if( ( groupID >= LORAMAC_MAX_MC_CTX ) || + ( Nvm.MacGroup2.MulticastChannelList[groupID].ChannelParams.IsEnabled == false ) ) + { + return LORAMAC_STATUS_MC_GROUP_UNDEFINED; + } + + McChannelParams_t channel; + + // Set all channel fields with 0 + memset1( ( uint8_t* )&channel, 0, sizeof( McChannelParams_t ) ); + + Nvm.MacGroup2.MulticastChannelList[groupID].ChannelParams = channel; + MacCtx.MacFlags.Bits.NvmHandle = 1; + return LORAMAC_STATUS_OK; +} + +uint8_t LoRaMacMcChannelGetGroupId( uint32_t mcAddress ) +{ + for( uint8_t i = 0; i < LORAMAC_MAX_MC_CTX; i++ ) + { + if( mcAddress == Nvm.MacGroup2.MulticastChannelList[i].ChannelParams.Address ) + { + return i; + } + } + return 0xFF; +} + +LoRaMacStatus_t LoRaMacMcChannelSetupRxParams( AddressIdentifier_t groupID, McRxParams_t *rxParams, uint8_t *status ) +{ + *status = 0x1C + ( groupID & 0x03 ); + + if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING ) + { + return LORAMAC_STATUS_BUSY; + } + + if( ( rxParams->Class == CLASS_A ) || ( rxParams->Class > CLASS_C ) ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + if( ( groupID >= LORAMAC_MAX_MC_CTX ) || + ( Nvm.MacGroup2.MulticastChannelList[groupID].ChannelParams.IsEnabled == false ) ) + { + return LORAMAC_STATUS_MC_GROUP_UNDEFINED; + } + *status &= 0x0F; // groupID OK + + VerifyParams_t verify; + // Check datarate + if( rxParams->Class == CLASS_B ) + { + verify.DatarateParams.Datarate = rxParams->Params.ClassB.Datarate; + } + else + { + verify.DatarateParams.Datarate = rxParams->Params.ClassC.Datarate; + } + verify.DatarateParams.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime; + + if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_RX_DR ) == true ) + { + *status &= 0xFB; // datarate OK + } + + // Check frequency + if( rxParams->Class == CLASS_B ) + { + verify.Frequency = rxParams->Params.ClassB.Frequency; + } + else + { + verify.Frequency = rxParams->Params.ClassC.Frequency; + } + if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_FREQUENCY ) == true ) + { + *status &= 0xF7; // frequency OK + } + + if( *status == ( groupID & 0x03 ) ) + { + // Apply parameters + Nvm.MacGroup2.MulticastChannelList[groupID].ChannelParams.RxParams = *rxParams; + MacCtx.MacFlags.Bits.NvmHandle = 1; + } + else + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + if( rxParams->Class == CLASS_B ) + { + // Calculate class b parameters + LoRaMacClassBSetMulticastPeriodicity( &Nvm.MacGroup2.MulticastChannelList[groupID] ); + } + return LORAMAC_STATUS_OK; +} + +/*! + * \brief Function executed on AbpJoinPendingTimer timer event + */ +static void OnAbpJoinPendingTimerEvent( void *context ) +{ + MacCtx.MacState &= ~LORAMAC_ABP_JOIN_PENDING; + MacCtx.MacFlags.Bits.MacDone = 1; + OnMacProcessNotify( ); +} + +/*! + * \brief Start ABP join simulation + */ +static void AbpJoinPendingStart( void ) +{ + static bool initialized = false; + + if( initialized == false ) + { + initialized = true; + TimerInit( &MacCtx.AbpJoinPendingTimer, OnAbpJoinPendingTimerEvent ); + } + + MacCtx.MacState |= LORAMAC_ABP_JOIN_PENDING; + + TimerStop( &MacCtx.AbpJoinPendingTimer ); + TimerSetValue( &MacCtx.AbpJoinPendingTimer, ABP_JOIN_PENDING_DELAY_MS ); + TimerStart( &MacCtx.AbpJoinPendingTimer ); +} + +LoRaMacStatus_t LoRaMacMlmeRequest( MlmeReq_t* mlmeRequest ) +{ + LoRaMacStatus_t status = LORAMAC_STATUS_SERVICE_UNKNOWN; + MlmeConfirmQueue_t queueElement; + bool isAbpJoinPending = false; + uint8_t macCmdPayload[2] = { 0x00, 0x00 }; + + if( mlmeRequest == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + // Initialize mlmeRequest->ReqReturn.DutyCycleWaitTime to 0 in order to + // return a valid value in case the MAC is busy. + mlmeRequest->ReqReturn.DutyCycleWaitTime = 0; + + if( LoRaMacIsBusy( ) == true ) + { + return LORAMAC_STATUS_BUSY; + } + if( LoRaMacConfirmQueueIsFull( ) == true ) + { + return LORAMAC_STATUS_BUSY; + } + + if( LoRaMacConfirmQueueGetCnt( ) == 0 ) + { + memset1( ( uint8_t* ) &MacCtx.MlmeConfirm, 0, sizeof( MacCtx.MlmeConfirm ) ); + } + MacCtx.MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + + MacCtx.MacFlags.Bits.MlmeReq = 1; + queueElement.Request = mlmeRequest->Type; + queueElement.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + queueElement.RestrictCommonReadyToHandle = false; + queueElement.ReadyToHandle = false; + + switch( mlmeRequest->Type ) + { + case MLME_JOIN: + { + if( ( MacCtx.MacState & LORAMAC_TX_DELAYED ) == LORAMAC_TX_DELAYED ) + { + return LORAMAC_STATUS_BUSY; + } + + if( mlmeRequest->Req.Join.NetworkActivation == ACTIVATION_TYPE_OTAA ) + { + ResetMacParameters( false ); + + Nvm.MacGroup1.ChannelsDatarate = RegionAlternateDr( Nvm.MacGroup2.Region, mlmeRequest->Req.Join.Datarate, ALTERNATE_DR ); + + queueElement.Status = LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL; + + status = SendReJoinReq( JOIN_REQ ); + + if( status != LORAMAC_STATUS_OK ) + { + // Revert back the previous datarate ( mainly used for US915 like regions ) + Nvm.MacGroup1.ChannelsDatarate = RegionAlternateDr( Nvm.MacGroup2.Region, mlmeRequest->Req.Join.Datarate, ALTERNATE_DR_RESTORE ); + } + } + else if( mlmeRequest->Req.Join.NetworkActivation == ACTIVATION_TYPE_ABP ) + { + // Restore default value for ChannelsDatarateChangedLinkAdrReq + Nvm.MacGroup2.ChannelsDatarateChangedLinkAdrReq = false; + + // Activate the default channels + InitDefaultsParams_t params; + params.Type = INIT_TYPE_ACTIVATE_DEFAULT_CHANNELS; + RegionInitDefaults( Nvm.MacGroup2.Region, ¶ms ); + + Nvm.MacGroup2.NetworkActivation = mlmeRequest->Req.Join.NetworkActivation; + queueElement.Status = LORAMAC_EVENT_INFO_STATUS_OK; + queueElement.ReadyToHandle = true; + isAbpJoinPending = true; + status = LORAMAC_STATUS_OK; + } + break; + } + case MLME_REJOIN_0: + { + MacCtx.MacFlags.Bits.MlmeReq = 1; + MacCtx.MlmeConfirm.MlmeRequest = mlmeRequest->Type; + + status = SendReJoinReq( REJOIN_REQ_0 ); + + break; + } + case MLME_REJOIN_1: + { + MacCtx.MacFlags.Bits.MlmeReq = 1; + MacCtx.MlmeConfirm.MlmeRequest = mlmeRequest->Type; + + status = SendReJoinReq( REJOIN_REQ_1 ); + + break; + } + case MLME_REJOIN_2: + { + MacCtx.MacFlags.Bits.MlmeReq = 1; + MacCtx.MlmeConfirm.MlmeRequest = mlmeRequest->Type; + + status = SendReJoinReq( REJOIN_REQ_2 ); + + break; + } + case MLME_LINK_CHECK: + { + // LoRaMac will send this command piggy-pack + status = LORAMAC_STATUS_OK; + if( LoRaMacCommandsAddCmd( MOTE_MAC_LINK_CHECK_REQ, macCmdPayload, 0 ) != LORAMAC_COMMANDS_SUCCESS ) + { + status = LORAMAC_STATUS_MAC_COMMAD_ERROR; + } + break; + } + case MLME_TXCW: + { + status = SetTxContinuousWave( mlmeRequest->Req.TxCw.Timeout, mlmeRequest->Req.TxCw.Frequency, mlmeRequest->Req.TxCw.Power ); + break; + } + case MLME_DEVICE_TIME: + { + // LoRaMac will send this command piggy-pack + status = LORAMAC_STATUS_OK; + if( LoRaMacCommandsAddCmd( MOTE_MAC_DEVICE_TIME_REQ, macCmdPayload, 0 ) != LORAMAC_COMMANDS_SUCCESS ) + { + status = LORAMAC_STATUS_MAC_COMMAD_ERROR; + } + break; + } + case MLME_PING_SLOT_INFO: + { + if( Nvm.MacGroup2.DeviceClass == CLASS_A ) + { + uint8_t value = mlmeRequest->Req.PingSlotInfo.PingSlot.Value; + + // LoRaMac will send this command piggy-pack + LoRaMacClassBSetPingSlotInfo( mlmeRequest->Req.PingSlotInfo.PingSlot.Fields.Periodicity ); + macCmdPayload[0] = value; + status = LORAMAC_STATUS_OK; + if( LoRaMacCommandsAddCmd( MOTE_MAC_PING_SLOT_INFO_REQ, macCmdPayload, 1 ) != LORAMAC_COMMANDS_SUCCESS ) + { + status = LORAMAC_STATUS_MAC_COMMAD_ERROR; + } + } + break; + } + case MLME_BEACON_TIMING: + { + // LoRaMac will send this command piggy-pack + status = LORAMAC_STATUS_OK; + if( LoRaMacCommandsAddCmd( MOTE_MAC_BEACON_TIMING_REQ, macCmdPayload, 0 ) != LORAMAC_COMMANDS_SUCCESS ) + { + status = LORAMAC_STATUS_MAC_COMMAD_ERROR; + } + break; + } + case MLME_BEACON_ACQUISITION: + { + // Apply the request + queueElement.RestrictCommonReadyToHandle = true; + + if( LoRaMacClassBIsAcquisitionInProgress( ) == false ) + { + // Start class B algorithm + LoRaMacClassBSetBeaconState( BEACON_STATE_ACQUISITION ); + LoRaMacClassBBeaconTimerEvent( NULL ); + + status = LORAMAC_STATUS_OK; + } + else + { + status = LORAMAC_STATUS_BUSY; + } + break; + } + default: + break; + } + + // Fill return structure + mlmeRequest->ReqReturn.DutyCycleWaitTime = MacCtx.DutyCycleWaitTime; + + if( status != LORAMAC_STATUS_OK ) + { + if( LoRaMacConfirmQueueGetCnt( ) == 0 ) + { + MacCtx.NodeAckRequested = false; + MacCtx.MacFlags.Bits.MlmeReq = 0; + } + } + else + { + LoRaMacConfirmQueueAdd( &queueElement ); + if( isAbpJoinPending == true ) + { + AbpJoinPendingStart( ); + } + } + return status; +} + +LoRaMacStatus_t LoRaMacMcpsRequest( McpsReq_t* mcpsRequest ) +{ + GetPhyParams_t getPhy; + PhyParam_t phyParam; + LoRaMacStatus_t status = LORAMAC_STATUS_SERVICE_UNKNOWN; + LoRaMacHeader_t macHdr; + VerifyParams_t verify; + uint8_t fPort = 0; + void* fBuffer; + uint16_t fBufferSize; + int8_t datarate = DR_0; + bool readyToSend = false; + + if( mcpsRequest == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + // Initialize mcpsRequest->ReqReturn.DutyCycleWaitTime to 0 in order to + // return a valid value in case the MAC is busy. + mcpsRequest->ReqReturn.DutyCycleWaitTime = 0; + + if( LoRaMacIsBusy( ) == true ) + { + return LORAMAC_STATUS_BUSY; + } + + McpsReq_t request = *mcpsRequest; + + macHdr.Value = 0; + memset1( ( uint8_t* ) &MacCtx.McpsConfirm, 0, sizeof( MacCtx.McpsConfirm ) ); + MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; + + // Apply confirmed uplinks, if the device has not received a valid + // downlink after a join accept. + if( ( Nvm.MacGroup2.NetworkActivation == ACTIVATION_TYPE_OTAA ) && + ( Nvm.MacGroup2.DeviceClass == CLASS_C ) && + ( Nvm.MacGroup2.DownlinkReceived == false ) && + ( request.Type == MCPS_UNCONFIRMED ) ) + { + request.Type = MCPS_CONFIRMED; + } + + switch( request.Type ) + { + case MCPS_UNCONFIRMED: + { + readyToSend = true; + + macHdr.Bits.MType = FRAME_TYPE_DATA_UNCONFIRMED_UP; + fPort = request.Req.Unconfirmed.fPort; + fBuffer = request.Req.Unconfirmed.fBuffer; + fBufferSize = request.Req.Unconfirmed.fBufferSize; + datarate = request.Req.Unconfirmed.Datarate; + break; + } + case MCPS_CONFIRMED: + { + readyToSend = true; + + macHdr.Bits.MType = FRAME_TYPE_DATA_CONFIRMED_UP; + fPort = request.Req.Confirmed.fPort; + fBuffer = request.Req.Confirmed.fBuffer; + fBufferSize = request.Req.Confirmed.fBufferSize; + datarate = request.Req.Confirmed.Datarate; + break; + } + case MCPS_PROPRIETARY: + { + readyToSend = true; + + macHdr.Bits.MType = FRAME_TYPE_PROPRIETARY; + fBuffer = request.Req.Proprietary.fBuffer; + fBufferSize = request.Req.Proprietary.fBufferSize; + datarate = request.Req.Proprietary.Datarate; + break; + } + default: + break; + } + + // Make sure that the input datarate is compliant + // to the regional specification. + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime; + phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy ); + // Apply the minimum possible datarate. + // Some regions have limitations for the minimum datarate. + datarate = MAX( datarate, ( int8_t )phyParam.Value ); + + // Apply minimum datarate in this special case. + if( CheckForMinimumAbpDatarate( Nvm.MacGroup2.AdrCtrlOn, Nvm.MacGroup2.NetworkActivation, + Nvm.MacGroup2.ChannelsDatarateChangedLinkAdrReq ) == true ) + { + datarate = ( int8_t )phyParam.Value; + } + + if( readyToSend == true ) + { + if( ( Nvm.MacGroup2.AdrCtrlOn == false ) || + ( CheckForMinimumAbpDatarate( Nvm.MacGroup2.AdrCtrlOn, Nvm.MacGroup2.NetworkActivation, + Nvm.MacGroup2.ChannelsDatarateChangedLinkAdrReq ) == true ) ) + { + verify.DatarateParams.Datarate = datarate; + verify.DatarateParams.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime; + + if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_TX_DR ) == true ) + { + Nvm.MacGroup1.ChannelsDatarate = verify.DatarateParams.Datarate; + } + else + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + } + + // Verification of response timeout for class b and class c + LoRaMacHandleResponseTimeout( REGION_COMMON_CLASS_B_C_RESP_TIMEOUT, + MacCtx.ResponseTimeoutStartTime ); + + status = Send( &macHdr, fPort, fBuffer, fBufferSize ); + if( status == LORAMAC_STATUS_OK ) + { + MacCtx.McpsConfirm.McpsRequest = request.Type; + MacCtx.MacFlags.Bits.McpsReq = 1; + } + else + { + MacCtx.NodeAckRequested = false; + } + } + + // Fill return structure + mcpsRequest->ReqReturn.DutyCycleWaitTime = MacCtx.DutyCycleWaitTime; + + return status; +} + +static bool ConvertRejoinCycleTime( uint32_t rejoinCycleTime, uint32_t* timeInMiliSec ) +{ + // Our timer implementation do not allow longer times than 4294967295 ms + if( rejoinCycleTime <= 4294967 ) + { + *timeInMiliSec = rejoinCycleTime * 1000; + return true; + } + else + { + return false; + } +} + +static void OnRejoin0CycleTimerEvent( void* context ) +{ + TimerStop( &MacCtx.Rejoin0CycleTimer ); + ConvertRejoinCycleTime( Nvm.MacGroup2.Rejoin0CycleInSec, &MacCtx.Rejoin0CycleTime ); + + OnMacProcessNotify( ); + + Nvm.MacGroup2.IsRejoin0RequestQueued = true; + + TimerSetValue( &MacCtx.Rejoin0CycleTimer, MacCtx.Rejoin0CycleTime ); + TimerStart( &MacCtx.Rejoin0CycleTimer ); +} + +static void OnRejoin1CycleTimerEvent( void* context ) +{ + TimerStop( &MacCtx.Rejoin1CycleTimer ); + ConvertRejoinCycleTime( Nvm.MacGroup2.Rejoin1CycleInSec, &MacCtx.Rejoin1CycleTime ); + + OnMacProcessNotify( ); + + Nvm.MacGroup2.IsRejoin1RequestQueued = true; + + TimerSetValue( &MacCtx.Rejoin1CycleTimer, MacCtx.Rejoin1CycleTime ); + TimerStart( &MacCtx.Rejoin1CycleTimer ); +} + +static void OnForceRejoinReqCycleTimerEvent( void* context ) +{ + Nvm.MacGroup1.ForceRejoinRetriesCounter++; + if( ( Nvm.MacGroup2.ForceRejoinType == 0 ) || ( Nvm.MacGroup2.ForceRejoinType == 1 ) ) + { + Nvm.MacGroup2.IsRejoin0RequestQueued = true; + } + else + { + Nvm.MacGroup2.IsRejoin2RequestQueued = true; + } + + if( Nvm.MacGroup1.ForceRejoinRetriesCounter >= Nvm.MacGroup2.ForceRejoinMaxRetries ) + { + TimerStop( &MacCtx.ForceRejoinReqCycleTimer ); + Nvm.MacGroup1.ForceRejoinRetriesCounter = 0; + } + else + { + TimerSetValue( &MacCtx.ForceRejoinReqCycleTimer, MacCtx.ForceRejonCycleTime ); + TimerStart( &MacCtx.ForceRejoinReqCycleTimer ); + } + + OnMacProcessNotify( ); +} + +void LoRaMacTestSetDutyCycleOn( bool enable ) +{ + VerifyParams_t verify; + + verify.DutyCycle = enable; + + if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_DUTY_CYCLE ) == true ) + { + Nvm.MacGroup2.DutyCycleOn = enable; + // Handle NVM potential changes + MacCtx.MacFlags.Bits.NvmHandle = 1; + } +} + +LoRaMacStatus_t LoRaMacDeInitialization( void ) +{ + // Check the current state of the LoRaMac + if ( LoRaMacStop( ) == LORAMAC_STATUS_OK ) + { + // Stop Timers + TimerStop( &MacCtx.TxDelayedTimer ); + TimerStop( &MacCtx.RxWindowTimer1 ); + TimerStop( &MacCtx.RxWindowTimer2 ); + + // Take care about class B + LoRaMacClassBHaltBeaconing( ); + + // Reset Mac parameters + ResetMacParameters( false ); + + // Switch off Radio + Radio.Sleep( ); + + // Return success + return LORAMAC_STATUS_OK; + } + else + { + return LORAMAC_STATUS_BUSY; + } +} + +void LoRaMacReset( void ) +{ + // Reset state machine + MacCtx.MacState &= ~LORAMAC_TX_RUNNING; + MacCtx.MacFlags.Bits.MacDone = 1; + + // Stop Timers + TimerStop( &MacCtx.TxDelayedTimer ); + TimerStop( &MacCtx.RxWindowTimer1 ); + TimerStop( &MacCtx.RxWindowTimer2 ); + + // Stop retransmissions + MacCtx.ChannelsNbTransCounter = Nvm.MacGroup2.MacParams.ChannelsNbTrans; + + // Inform application layer + OnMacProcessNotify( ); +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMac.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMac.h new file mode 100644 index 0000000000..e768d09261 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMac.h @@ -0,0 +1,2937 @@ +/*! + * \file LoRaMac.h + * + * \brief LoRa MAC layer implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * \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 periodic-uplink-lpp/B-L072Z-LRWAN1/main.c + * LoRaWAN class A/B/C application example for the B-L072Z-LRWAN1. + * + * \example periodic-uplink-lpp/NAMote72/main.c + * LoRaWAN class A/B/C application example for the NAMote72. + * + * \example periodic-uplink-lpp/NucleoL073/main.c + * LoRaWAN class A/B/C application example for the NucleoL073. + * + * \example periodic-uplink-lpp/NucleoL152/main.c + * LoRaWAN class A/B/C application example for the NucleoL152. + * + * \example periodic-uplink-lpp/NucleoL476/main.c + * LoRaWAN class A/B/C application example for the NucleoL476. + * + * \example periodic-uplink-lpp/SAMR34/main.c + * LoRaWAN class A/B/C application example for the SAMR34. + * + * \example periodic-uplink-lpp/SKiM880B/main.c + * LoRaWAN class A/B/C application example for the SKiM880B. + * + * \example periodic-uplink-lpp/SKiM881AXL/main.c + * LoRaWAN class A/B/C application example for the SKiM881AXL. + * + * \example periodic-uplink-lpp/SKiM980A/main.c + * LoRaWAN class A/B/C application example for the SKiM980A. + */ +#ifndef __LORAMAC_H__ +#define __LORAMAC_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include + +#include "timer.h" +#include "systime.h" +#include "LoRaMacTypes.h" + +#include "RegionNvm.h" +#include "LoRaMacCryptoNvm.h" +#include "secure-element-nvm.h" +#include "LoRaMacClassBNvm.h" + +/*! + * LoRaWAN version definition. + */ +#define LORAMAC_VERSION 0x01010100 + +/*! + * LoRaWAN fallback version definition. + */ +#define LORAMAC_FALLBACK_VERSION 0x01000400 + +/*! + * Maximum number of times the MAC layer tries to get an acknowledge. + */ +#define MAX_ACK_RETRIES 8 + +/*! + * Frame direction definition for up-link communications + */ +#define UP_LINK 0 + +/*! + * Frame direction definition for down-link communications + */ +#define DOWN_LINK 1 + +/*! + * LoRaMac MLME-Confirm queue length + */ +#define LORA_MAC_MLME_CONFIRM_QUEUE_LEN 5 + +/*! + * Start value for multicast keys enumeration + */ +#define LORAMAC_CRYPTO_MULTICAST_KEYS 127 + +/*! + * Maximum MAC commands buffer size + */ +#define LORA_MAC_COMMAND_MAX_LENGTH 128 + + +/*! + * Bitmap value + */ +#define LORAMAC_NVM_NOTIFY_FLAG_NONE 0x00 + +/*! + * Bitmap value for the NVM group crypto. + */ +#define LORAMAC_NVM_NOTIFY_FLAG_CRYPTO 0x01 + +/*! + * Bitmap value for the NVM group MAC 1. + */ +#define LORAMAC_NVM_NOTIFY_FLAG_MAC_GROUP1 0x02 + +/*! + * Bitmap value for the NVM group MAC 2. + */ +#define LORAMAC_NVM_NOTIFY_FLAG_MAC_GROUP2 0x04 + +/*! + * Bitmap value for the NVM group secure element. + */ +#define LORAMAC_NVM_NOTIFY_FLAG_SECURE_ELEMENT 0x08 + +/*! + * Bitmap value for the NVM group 1 region. + */ +#define LORAMAC_NVM_NOTIFY_FLAG_REGION_GROUP1 0x10 + +/*! + * Bitmap value for the NVM group 2 region. + */ +#define LORAMAC_NVM_NOTIFY_FLAG_REGION_GROUP2 0x20 + +/*! + * Bitmap value for the NVM group class b. + */ +#define LORAMAC_NVM_NOTIFY_FLAG_CLASS_B 0x40 + +/*! + * LoRaWAN compliance certification protocol port number. + * + * LoRaWAN Specification V1.x.x, chapter 4.3.2 + */ +#define LORAMAC_CERT_FPORT 224 + +/*! + * End-Device activation type + */ +typedef enum eActivationType +{ + /*! + * None + */ + ACTIVATION_TYPE_NONE = 0, + /*! + * Activation By Personalization (ACTIVATION_TYPE_ABP) + */ + ACTIVATION_TYPE_ABP = 1, + /*! + * Over-The-Air Activation (ACTIVATION_TYPE_OTAA) + */ + ACTIVATION_TYPE_OTAA = 2, +}ActivationType_t; + +/*! + * LoRaMAC receive window channel parameters + */ +typedef struct sRxChannelParams +{ + /*! + * Frequency in Hz + */ + uint32_t Frequency; + /*! + * Data rate + * + * LoRaWAN Regional Parameters V1.0.2rB + * + * The allowed ranges are region specific. Please refer to \ref DR_0 to \ref DR_15 for details. + */ + uint8_t Datarate; +}RxChannelParams_t; + +/*! + * LoRaMAC receive window enumeration + */ +typedef enum eLoRaMacRxSlot +{ + /*! + * LoRaMAC receive window 1 + */ + RX_SLOT_WIN_1, + /*! + * LoRaMAC receive window 2 + */ + RX_SLOT_WIN_2, + /*! + * LoRaMAC receive window 2 for class c - continuous listening + */ + RX_SLOT_WIN_CLASS_C, + /*! + * LoRaMAC class c multicast downlink + */ + RX_SLOT_WIN_CLASS_C_MULTICAST, + /*! + * LoRaMAC class b ping slot window + */ + RX_SLOT_WIN_CLASS_B_PING_SLOT, + /*! + * LoRaMAC class b multicast slot window + */ + RX_SLOT_WIN_CLASS_B_MULTICAST_SLOT, + /*! + * LoRaMAC no active receive window + */ + RX_SLOT_NONE, +}LoRaMacRxSlot_t; + +/*! + * Global MAC layer parameters + */ +typedef struct sLoRaMacParams +{ + /*! + * System overall timing error in milliseconds. + * [-SystemMaxRxError : +SystemMaxRxError] + * Default: +/-10 ms + */ + uint32_t SystemMaxRxError; + /*! + * Minimum required number of symbols to detect an Rx frame + * Default: 6 symbols + */ + uint8_t MinRxSymbols; + /*! + * LoRaMac maximum time a reception window stays open + */ + uint32_t MaxRxWindow; + /*! + * Receive delay 1 + */ + uint32_t ReceiveDelay1; + /*! + * Receive delay 2 + */ + uint32_t ReceiveDelay2; + /*! + * Join accept delay 1 + */ + uint32_t JoinAcceptDelay1; + /*! + * Join accept delay 1 + */ + uint32_t JoinAcceptDelay2; + /*! + * Number of uplink messages repetitions [1:15] + */ + uint8_t ChannelsNbTrans; + /*! + * Datarate offset between uplink and downlink on first window + */ + uint8_t Rx1DrOffset; + /*! + * LoRaMAC 2nd reception window settings + */ + RxChannelParams_t Rx2Channel; + /*! + * LoRaMAC continuous reception window settings + */ + RxChannelParams_t RxCChannel; + /*! + * Uplink dwell time configuration. 0: No limit, 1: 400ms + */ + uint8_t UplinkDwellTime; + /*! + * Downlink dwell time configuration. 0: No limit, 1: 400ms + */ + uint8_t DownlinkDwellTime; + /*! + * Maximum possible EIRP + */ + float MaxEirp; + /*! + * Antenna gain of the node + */ + float AntennaGain; + /*! + * Limit of uplinks without any donwlink response before the ADRACKReq bit + * will be set. + */ + uint16_t AdrAckLimit; + /*! + * Limit of uplinks without any donwlink response after a the first frame + * with set ADRACKReq bit before the trying to regain the connectivity. + */ + uint16_t AdrAckDelay; +}LoRaMacParams_t; + +/*! + * LoRaMAC data structure for a PingSlotInfoReq \ref MLME_PING_SLOT_INFO + * + * LoRaWAN Specification + */ +typedef union uPingSlotInfo +{ + /*! + * Parameter for byte access + */ + uint8_t Value; + /*! + * Structure containing the parameters for the PingSlotInfoReq + */ + struct sInfoFields + { + /*! + * Periodicity = 0: ping slot every second + * Periodicity = 7: ping slot every 128 seconds + */ + uint8_t Periodicity : 3; + /*! + * RFU + */ + uint8_t RFU : 5; + }Fields; +}PingSlotInfo_t; + +/*! + * LoRaMAC data structure for the \ref MLME_BEACON MLME-Indication + * + * LoRaWAN Specification + */ +typedef struct sBeaconInfo +{ + /*! + * Timestamp in seconds since 00:00:00, Sunday 6th of January 1980 + * (start of the GPS epoch) modulo 2^32 + */ + SysTime_t Time; + /*! + * Frequency + */ + uint32_t Frequency; + /*! + * Datarate + */ + uint8_t Datarate; + /*! + * RSSI + */ + int16_t Rssi; + /*! + * SNR + */ + int8_t Snr; + /*! + * Param + * | Bits | 7:2 | 1:0 | + * |-------|-----|------| + * | Param | RFU | Prec | + * + * Prec field is used to interpret the precision of beacon's transmit time + * as 10^(-6+prec) and the default value is 0. + * RFU will be set to Zero and Prec can take values between 0:3. + */ + uint8_t Param; + /*! + * Data structure for the gateway specific part. The + * content of the values may differ for each gateway + */ + struct sGwSpecific + { + /*! + * Info descriptor - can differ for each gateway + */ + uint8_t InfoDesc; + /*! + * Info - can differ for each gateway + */ + uint8_t Info[6]; + }GwSpecific; +}BeaconInfo_t; + +/*! + * Enumeration containing the status of the operation of a MAC service + */ +typedef enum eLoRaMacEventInfoStatus +{ + /*! + * Service performed successfully + */ + LORAMAC_EVENT_INFO_STATUS_OK = 0, + /*! + * An error occurred during the execution of the service + */ + LORAMAC_EVENT_INFO_STATUS_ERROR, + /*! + * A Tx timeout occurred + */ + LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT, + /*! + * An Rx timeout occurred on receive window 1 + */ + LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT, + /*! + * An Rx timeout occurred on receive window 2 + */ + LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT, + /*! + * An Rx error occurred on receive window 1 + */ + LORAMAC_EVENT_INFO_STATUS_RX1_ERROR, + /*! + * An Rx error occurred on receive window 2 + */ + LORAMAC_EVENT_INFO_STATUS_RX2_ERROR, + /*! + * An error occurred in the join procedure + */ + LORAMAC_EVENT_INFO_STATUS_JOIN_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, + /*! + * The MAC could not retransmit a frame since the MAC decreased the datarate. The + * payload size is not applicable for the datarate. + */ + LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR, + /*! + * An address error occurred + */ + LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL, + /*! + * Message integrity check failure + */ + LORAMAC_EVENT_INFO_STATUS_MIC_FAIL, + /*! + * ToDo + */ + LORAMAC_EVENT_INFO_STATUS_MULTICAST_FAIL, + /*! + * ToDo + */ + LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED, + /*! + * ToDo + */ + LORAMAC_EVENT_INFO_STATUS_BEACON_LOST, + /*! + * ToDo + */ + LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND, +}LoRaMacEventInfoStatus_t; + +/*! + * LoRaMac tx/rx operation state + */ +typedef union eLoRaMacFlags_t +{ + /*! + * Byte-access to the bits + */ + uint8_t Value; + /*! + * Structure containing single access to bits + */ + struct sMacFlagBits + { + /*! + * MCPS-Req pending + */ + uint8_t McpsReq : 1; + /*! + * MCPS-Ind pending + */ + uint8_t McpsInd : 1; + /*! + * MLME-Req pending + */ + uint8_t MlmeReq : 1; + /*! + * MLME-Ind pending + */ + uint8_t MlmeInd : 1; + /*! + * MAC cycle done + */ + uint8_t MacDone : 1; + /*! + * Indicate if a NVM handling is required + */ + uint8_t NvmHandle : 1; + }Bits; +}LoRaMacFlags_t; + +/*! + * LoRaMAC region enumeration + */ +typedef enum eLoRaMacRegion +{ + /*! + * AS band on 923MHz + */ + LORAMAC_REGION_AS923, + /*! + * Australian band on 915MHz + */ + LORAMAC_REGION_AU915, + /*! + * Chinese band on 470MHz + */ + LORAMAC_REGION_CN470, + /*! + * Chinese band on 779MHz + */ + LORAMAC_REGION_CN779, + /*! + * European band on 433MHz + */ + LORAMAC_REGION_EU433, + /*! + * European band on 868MHz + */ + LORAMAC_REGION_EU868, + /*! + * South korean band on 920MHz + */ + LORAMAC_REGION_KR920, + /*! + * India band on 865MHz + */ + LORAMAC_REGION_IN865, + /*! + * North american band on 915MHz + */ + LORAMAC_REGION_US915, + /*! + * Russia band on 864MHz + */ + LORAMAC_REGION_RU864, +}LoRaMacRegion_t; + +typedef struct sLoRaMacNvmDataGroup1 +{ + /*! + * Counts the number of missed ADR acknowledgements + */ + uint32_t AdrAckCounter; + /*! + * Last transmission time. + */ + TimerTime_t LastTxDoneTime; + /*! + * Aggregated time off. + */ + TimerTime_t AggregatedTimeOff; + /*! + * Last received Message integrity Code (MIC) + */ + uint32_t LastRxMic; + /*! + * Channels TX power + */ + int8_t ChannelsTxPower; + /*! + * Channels data rate + */ + int8_t ChannelsDatarate; + /*! + * If the server has sent a FRAME_TYPE_DATA_CONFIRMED_DOWN this variable indicates + * if the ACK bit must be set for the next transmission + */ + bool SrvAckRequested; + /*! + * Counts the number if uplinks to know when the next Rejoin request type 0 is required. + * ( If requested by the server through RejoinParamSetupReq MAC command ) + */ + uint32_t Rejoin0UplinksCounter; + /*! + * Counter of Rejoin Request of retries. + * ( If requested by the server through ForceRejoinReq MAC command ) + */ + uint8_t ForceRejoinRetriesCounter; + /*! + * Counts the number of uplinks containing a RekeyInd MAC command to know + * when the end device should reverted to join state because it didn't + * received a RekeyConf. + */ + uint16_t RekeyIndUplinksCounter; + /*! + * CRC32 value of the MacGroup1 data structure. + */ + uint32_t Crc32; +}LoRaMacNvmDataGroup1_t; + +typedef struct sLoRaMacNvmDataGroup2 +{ + /* + * LoRaMac region. + */ + LoRaMacRegion_t Region; + /* + * LoRaMac parameters + */ + LoRaMacParams_t MacParams; + /* + * LoRaMac default parameters + */ + LoRaMacParams_t MacParamsDefaults; + /*! + * Channels TX power + */ + int8_t ChannelsTxPowerDefault; + /*! + * Channels data rate + */ + int8_t ChannelsDatarateDefault; + /* + * Network ID ( 3 bytes ) + */ + uint32_t NetID; + /* + * Mote Address + */ + uint32_t DevAddr; + /*! + * Multicast channel list + */ + MulticastCtx_t MulticastChannelList[LORAMAC_MAX_MC_CTX]; + /* + * Actual device class + */ + DeviceClass_t DeviceClass; + /* + * Indicates if the node is connected to + * a private or public network + */ + bool PublicNetwork; + /* + * LoRaMac ADR control status + */ + bool AdrCtrlOn; + /* + * Maximum duty cycle + * \remark Possibility to shutdown the device. + */ + uint8_t MaxDCycle; + /* + * Enables/Disables duty cycle management (Test only) + */ + bool DutyCycleOn; + /* + * Set to true, if the datarate was increased + * with a link adr request. + */ + bool ChannelsDatarateChangedLinkAdrReq; + /* + * The stack will set this variable to true, if a downlink has been received. + */ + bool DownlinkReceived; + /* + * Enables/disable FPort 224 processing (certification port) + */ + bool IsCertPortOn; + /* + * Aggregated duty cycle management + */ + uint16_t AggregatedDCycle; + /* + * Stores the time at LoRaMac initialization. + * + * \remark Used for the BACKOFF_DC computation. + */ + SysTime_t InitializationTime; + /* + * Current LoRaWAN Version + */ + Version_t Version; + /* + * End-Device network activation + */ + ActivationType_t NetworkActivation; + /*! + * Number of uplinks without Rejoin request type 0. + * ( If requested by the server through RejoinParamSetupReq MAC command ) + * When it's set to 0, Rejoin0UplinksCounter won't be incremented + */ + uint32_t Rejoin0UplinksLimit; + /*! + * The total number of times the device will retry the Rejoin Request. + * ( If requested by the server through ForceRejoinReq MAC command ) + */ + uint8_t ForceRejoinMaxRetries; + /*! + * Rejoin Request Type + * ( If requested by the server through ForceRejoinReq MAC command ) + */ + uint8_t ForceRejoinType; + /*! + * Time in seconds between cyclic transmission of Type 0 Rejoin requests. + */ + uint32_t Rejoin0CycleInSec; + /*! + * Time in seconds between cyclic transmission of Type 1 Rejoin requests. + */ + uint32_t Rejoin1CycleInSec; + /*! + * Indicates if a Rejoin request was sent and no join-accept or any downlink + * has been received yet. + */ + bool IsRejoinAcceptPending; + /*! + * Indicates if a reqjoin request 0 is in the queue to send. + */ + bool IsRejoin0RequestQueued; + /*! + * Indicates if a reqjoin request 1 is in the queue to send. + */ + bool IsRejoin1RequestQueued; + /*! + * Indicates if a reqjoin request 2 is in the queue to send. + */ + bool IsRejoin2RequestQueued; + /*! + * CRC32 value of the MacGroup2 data structure. + */ + uint32_t Crc32; +}LoRaMacNvmDataGroup2_t; + +/*! + * LoRaMAC data structure for non-volatile memory (NVM). + * This structure contains data which must be stored in NVM. + */ +typedef struct sLoRaMacNvmData +{ + /*! + * Parameters related to the crypto layer. Change with every TX/RX + * procedure. + */ + LoRaMacCryptoNvmData_t Crypto; + /*! + * Parameters related to the MAC which change with high probability after + * every TX/RX procedure. + */ + LoRaMacNvmDataGroup1_t MacGroup1; + /*! + * Parameters related to the MAC which do not change very likely with every + * TX/RX procedure. + */ + LoRaMacNvmDataGroup2_t MacGroup2; + /*! + * Parameters related to the secure-element. + */ + SecureElementNvmData_t SecureElement; + /*! + * Parameters related to the regional implementation which change with high + * probability after every TX/RX procedure. + */ + RegionNvmDataGroup1_t RegionGroup1; + /*! + * Parameters related to the regional implementation which do not change + * very likely with every TX/RX procedure. + */ + RegionNvmDataGroup2_t RegionGroup2; + /*! + * Parameters related to class b. + */ + LoRaMacClassBNvmData_t ClassB; +}LoRaMacNvmData_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; + +/*! + * Structure which defines return parameters for requests. + */ +typedef struct sRequestReturnParam +{ + /*! + * This value reports the time in milliseconds which + * an application must wait before its possible to send + * the next uplink. + */ + TimerTime_t DutyCycleWaitTime; +}RequestReturnParam_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.2, 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.2, 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; +}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; + + /*! + * MCPS-Request return parameters + */ + RequestReturnParam_t ReqReturn; +}McpsReq_t; + +/*! + * LoRaMAC MCPS-Confirm + */ +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 NbTrans; + /*! + * The transmission time on air of the frame + */ + TimerTime_t TxTimeOnAir; + /*! + * The uplink counter value related to the frame + */ + uint32_t UpLinkCounter; + /*! + * The uplink channel related to the frame + */ + uint32_t Channel; +}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 IsUplinkTxPending; + /*! + * 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 + */ + int8_t Snr; + /*! + * Receive window + */ + LoRaMacRxSlot_t RxSlot; + /*! + * Set if an acknowledgement was received + */ + bool AckReceived; + /*! + * The downlink counter value for the received frame + */ + uint32_t DownLinkCounter; + /*! + * The device address of the frame + */ + uint32_t DevAddress; + /*! + * Set if a DeviceTimeAns MAC command was received. + */ + bool DeviceTimeAnsReceived; + /*! + * Response timeout for a class b or c device when a + * confirmed downlink has been received. In all other + * cases this variable is 0. + */ + TimerTime_t ResponseTimeout; +}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_REJOIN_0 | YES | NO | NO | YES + * \ref MLME_REJOIN_1 | YES | NO | NO | YES + * \ref MLME_LINK_CHECK | YES | NO | NO | YES + * \ref MLME_TXCW | YES | NO | NO | YES + * \ref MLME_DERIVE_MC_KE_KEY | YES | NO | NO | YES + * \ref MLME_DERIVE_MC_KEY_PAIR | YES | NO | NO | YES + * \ref MLME_REVERT_JOIN | NO | YES | NO | NO + * + * 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 + * MLME-Indication | MacMlmeIndication in \ref LoRaMacPrimitives_t + */ +typedef enum eMlme +{ + /*! + * An unknown MLME service + */ + MLME_UNKNOWN, + /*! + * Initiates the Over-the-Air activation + * + * LoRaWAN Specification V1.0.2, chapter 6.2 + */ + MLME_JOIN, + /*! + * Initiates sending a ReJoin-request type 0 + * + * LoRaWAN Specification V1.1.0, chapter 6.2.4.1 + */ + MLME_REJOIN_0, + /*! + * Initiates sending a ReJoin-request type 1 + * + * LoRaWAN Specification V1.1.0, chapter 6.2.4.2 + */ + MLME_REJOIN_1, + /*! + * Initiates sending a ReJoin-request type 2 + * + * LoRaWAN Specification V1.1.0, chapter 6.2.4.2 + */ + MLME_REJOIN_2, + /*! + * LinkCheckReq - Connectivity validation + * + * LoRaWAN Specification V1.0.2, chapter 5, table 4 + */ + MLME_LINK_CHECK, + /*! + * Sets Tx continuous wave mode + * + * LoRaWAN end-device certification + */ + MLME_TXCW, + /*! + * Derives the McKEKey from the AppKey or NwkKey. + */ + MLME_DERIVE_MC_KE_KEY, + /*! + * Derives a Multicast group key pair ( McAppSKey, McNwkSKey ) from McKey + */ + MLME_DERIVE_MC_KEY_PAIR, + /*! + * Initiates a DeviceTimeReq + * + * LoRaWAN end-device certification + */ + MLME_DEVICE_TIME, + /*! + * The MAC uses this MLME primitive to indicate a beacon reception + * status. + * + * LoRaWAN end-device certification + */ + MLME_BEACON, + /*! + * Initiate a beacon acquisition. The MAC will search for a beacon. + * It will search for XX_BEACON_INTERVAL milliseconds. + * + * LoRaWAN end-device certification + */ + MLME_BEACON_ACQUISITION, + /*! + * Initiates a PingSlotInfoReq + * + * LoRaWAN end-device certification + */ + MLME_PING_SLOT_INFO, + /*! + * Initiates a BeaconTimingReq + * + * LoRaWAN end-device certification + */ + MLME_BEACON_TIMING, + /*! + * Primitive which indicates that the beacon has been lost + * + * \remark The upper layer is required to switch the device class to ClassA + * + * LoRaWAN end-device certification + */ + MLME_BEACON_LOST, + /*! + * + * Indicates that the device hasn't received a RekeyConf and it reverts to the join state. + * + * \remark The upper layer is required to trigger the Join process again. + */ + MLME_REVERT_JOIN, +}Mlme_t; + +/*! + * LoRaMAC MLME-Request for the join service + */ +typedef struct sMlmeReqJoin +{ + /*! + * LoRaWAN Network End-Device Activation ( ACTIVATION_TYPE_NONE, ACTIVATION_TYPE_ABP or OTTA ) + * + * Related MIB type: \ref MIB_NETWORK_ACTIVATION + */ + ActivationType_t NetworkActivation; + /*! + * Datarate used for join request. + */ + uint8_t Datarate; +}MlmeReqJoin_t; + +/*! + * LoRaMAC MLME-Request for Tx continuous wave mode + */ +typedef struct sMlmeReqTxCw +{ + /*! + * Time in seconds while the radio is kept in continuous wave mode + */ + uint16_t Timeout; + /*! + * RF frequency to set (Only used with new way) + */ + uint32_t Frequency; + /*! + * RF output power to set (Only used with new way) + */ + int8_t Power; +}MlmeReqTxCw_t; + +/*! + * LoRaMAC MLME-Request for the ping slot info service + */ +typedef struct sMlmeReqPingSlotInfo +{ + PingSlotInfo_t PingSlot; +}MlmeReqPingSlotInfo_t; + +/*! + * LoRaMAC MLME-Request to derive the McKEKey from the AppKey or NwkKey + */ +typedef struct sMlmeReqDeriveMcKEKey +{ + /*! + * Key identifier of the root key to use to perform the derivation ( NwkKey or AppKey ) + */ + KeyIdentifier_t KeyID; + /*! + * Nonce value ( nonce <= 15) + */ + uint16_t Nonce; + /*! + * DevEUI Value + */ + uint8_t* DevEUI; +}MlmeReqDeriveMcKEKey_t; + +/*! + * LoRaMAC MLME-Request to derive a Multicast group key pair ( McAppSKey, McNwkSKey ) from McKey + */ +typedef struct sMlmeReqDeriveMcSessionKeyPair +{ + /*! + * Address identifier to select the multicast group + */ + AddressIdentifier_t GroupID; +}MlmeReqDeriveMcSessionKeyPair_t; + +/*! + * LoRaMAC MLME-Request structure + */ +typedef struct sMlmeReq +{ + /*! + * MLME-Request type + */ + Mlme_t Type; + + /*! + * MLME-Request parameters + */ + union uMlmeParam + { + /*! + * MLME-Request parameters for a join request + */ + MlmeReqJoin_t Join; + /*! + * MLME-Request parameters for Tx continuous mode request + */ + MlmeReqTxCw_t TxCw; + /*! + * MLME-Request parameters for a ping slot info request + */ + MlmeReqPingSlotInfo_t PingSlotInfo; + /*! + * MLME-Request to derive the McKEKey from the AppKey or NwkKey + */ + MlmeReqDeriveMcKEKey_t DeriveMcKEKey; + /*! + * MLME-Request to derive a Multicast group key pair ( McAppSKey, McNwkSKey ) from McKey + */ + MlmeReqDeriveMcSessionKeyPair_t DeriveMcSessionKeyPair; + }Req; + + /*! + * MLME-Request return parameters + */ + RequestReturnParam_t ReqReturn; +}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; + /*! + * 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; + /*! + * Provides the number of retransmissions + */ + uint8_t NbRetries; + /*! + * The delay which we have received through the + * BeaconTimingAns + */ + TimerTime_t BeaconTimingDelay; + /*! + * The channel of the next beacon + */ + uint8_t BeaconTimingChannel; +}MlmeConfirm_t; + +/*! + * LoRaMAC MLME-Indication primitive + */ +typedef struct sMlmeIndication +{ + /*! + * MLME-Indication type + */ + Mlme_t MlmeIndication; + /*! + * Status of the operation + */ + LoRaMacEventInfoStatus_t Status; + /*! + * Beacon information. Only valid for \ref MLME_BEACON, + * status \ref LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED + */ + BeaconInfo_t BeaconInfo; +}MlmeIndication_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_ACTIVATION | YES | YES + * \ref MIB_DEV_EUI | YES | YES + * \ref MIB_JOIN_EUI | YES | YES + * \ref MIB_SE_PIN | YES | YES + * \ref MIB_ADR | YES | YES + * \ref MIB_NET_ID | YES | YES + * \ref MIB_DEV_ADDR | YES | YES + * \ref MIB_APP_KEY | NO | YES + * \ref MIB_NWK_KEY | NO | YES + * \ref MIB_J_S_INT_KEY | NO | YES + * \ref MIB_J_S_ENC_KEY | NO | YES + * \ref MIB_F_NWK_S_INT_KEY | NO | YES + * \ref MIB_S_NWK_S_INT_KEY | NO | YES + * \ref MIB_NWK_S_ENC_KEY | NO | YES + * \ref MIB_APP_S_KEY | NO | YES + * \ref MIB_MC_KE_KEY | NO | YES + * \ref MIB_MC_KEY_0 | NO | YES + * \ref MIB_MC_APP_S_KEY_0 | NO | YES + * \ref MIB_MC_NWK_S_KEY_0 | NO | YES + * \ref MIB_MC_KEY_1 | NO | YES + * \ref MIB_MC_APP_S_KEY_1 | NO | YES + * \ref MIB_MC_NWK_S_KEY_1 | NO | YES + * \ref MIB_MC_KEY_2 | NO | YES + * \ref MIB_MC_APP_S_KEY_2 | NO | YES + * \ref MIB_MC_NWK_S_KEY_2 | NO | YES + * \ref MIB_MC_KEY_3 | NO | YES + * \ref MIB_MC_APP_S_KEY_3 | NO | YES + * \ref MIB_MC_NWK_S_KEY_3 | NO | YES + * \ref MIB_PUBLIC_NETWORK | YES | YES + * \ref MIB_CHANNELS | YES | NO + * \ref MIB_RX2_CHANNEL | YES | YES + * \ref MIB_RX2_DFAULT_CHANNEL | YES | YES + * \ref MIB_RXC_CHANNEL | YES | YES + * \ref MIB_RXC_DFAULT_CHANNEL | YES | YES + * \ref MIB_CHANNELS_MASK | YES | YES + * \ref MIB_CHANNELS_DEFAULT_MASK | YES | YES + * \ref MIB_CHANNELS_NB_TRANS | 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_MIN_TX_DATARATE | YES | NO + * \ref MIB_CHANNELS_DEFAULT_DATARATE | YES | YES + * \ref MIB_CHANNELS_TX_POWER | YES | YES + * \ref MIB_CHANNELS_DEFAULT_TX_POWER | YES | YES + * \ref MIB_SYSTEM_MAX_RX_ERROR | YES | YES + * \ref MIB_MIN_RX_SYMBOLS | YES | YES + * \ref MIB_BEACON_INTERVAL | YES | YES + * \ref MIB_BEACON_RESERVED | YES | YES + * \ref MIB_BEACON_GUARD | YES | YES + * \ref MIB_BEACON_WINDOW | YES | YES + * \ref MIB_BEACON_WINDOW_SLOTS | YES | YES + * \ref MIB_PING_SLOT_WINDOW | YES | YES + * \ref MIB_BEACON_SYMBOL_TO_DEFAULT | YES | YES + * \ref MIB_BEACON_SYMBOL_TO_EXPANSION_MAX | YES | YES + * \ref MIB_PING_SLOT_SYMBOL_TO_EXPANSION_MAX | YES | YES + * \ref MIB_BEACON_SYMBOL_TO_EXPANSION_FACTOR | YES | YES + * \ref MIB_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR | YES | YES + * \ref MIB_MAX_BEACON_LESS_PERIOD | YES | YES + * \ref MIB_ANTENNA_GAIN | YES | YES + * \ref MIB_DEFAULT_ANTENNA_GAIN | YES | YES + * \ref MIB_NVM_CTXS | YES | YES + * \ref MIB_ABP_LORAWAN_VERSION | NO | YES + * \ref MIB_LORAWAN_VERSION | YES | NO + * \ref MIB_IS_CERT_FPORT_ON | YES | YES + * \ref MIB_REJOIN_0_CYCLE | YES | YES + * \ref MIB_REJOIN_1_CYCLE | YES | YES + * \ref MIB_REJOIN_2_CYCLE | YES | NO + * \ref MIB_ADR_ACK_LIMIT | YES | YES + * \ref MIB_ADR_ACK_DELAY | YES | YES + * \ref MIB_ADR_ACK_DEFAULT_LIMIT | YES | YES + * \ref MIB_ADR_ACK_DEFAULT_DELAY | YES | YES + * \ref MIB_RSSI_FREE_THRESHOLD | YES | YES + * \ref MIB_CARRIER_SENSE_TIME | YES | YES + * + * 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.2 + */ + MIB_DEVICE_CLASS, + /*! + * LoRaWAN Network End-Device Activation + * + * LoRaWAN Specification V1.0.2 + */ + MIB_NETWORK_ACTIVATION, + /*! + * LoRaWAN device EUI + * + * LoRaWAN Specification V1.0.2 + */ + MIB_DEV_EUI, + /*! + * LoRaWAN join EUI + * + * LoRaWAN Specification V1.0.2 + */ + MIB_JOIN_EUI, + /*! + * Secure-element pin + */ + MIB_SE_PIN, + /*! + * Adaptive data rate + * + * LoRaWAN Specification V1.0.2, chapter 4.3.1.1 + * + * [true: ADR enabled, false: ADR disabled] + */ + MIB_ADR, + /*! + * Network identifier + * + * LoRaWAN Specification V1.0.2, chapter 6.1.1 + */ + MIB_NET_ID, + /*! + * End-device address + * + * LoRaWAN Specification V1.0.2, chapter 6.1.1 + */ + MIB_DEV_ADDR, + /*! + * Application root key + * + * LoRaWAN Specification V1.1.0, chapter 6.1.1.3 + */ + MIB_APP_KEY, + /*! + * Network root key + * + * LoRaWAN Specification V1.1.0, chapter 6.1.1.3 + */ + MIB_NWK_KEY, + /*! + * Join session integrity key + * + * LoRaWAN Specification V1.1.0, chapter 6.1.1.4 + */ + MIB_J_S_INT_KEY, + /*! + * Join session encryption key + * + * LoRaWAN Specification V1.1.0, chapter 6.1.1.4 + */ + MIB_J_S_ENC_KEY, + /*! + * Forwarding Network session integrity key + * + * LoRaWAN Specification V1.1.0, chapter 6.1.2.2 + */ + MIB_F_NWK_S_INT_KEY, + /*! + * Serving Network session integrity key + * + * LoRaWAN Specification V1.1.0, chapter 6.1.2.3 + */ + MIB_S_NWK_S_INT_KEY, + /*! + * Network session encryption key + * + * LoRaWAN Specification V1.1.0, chapter 6.1.2.4 + */ + MIB_NWK_S_ENC_KEY, + /*! + * Application session key + * + * LoRaWAN Specification V1.1.0, chapter 6.1.1.3 + */ + MIB_APP_S_KEY, + /*! + * Multicast key encryption key + * + * LoRaWAN - Secure element specification v1 + */ + MIB_MC_KE_KEY, + /*! + * Multicast root key index 0 + * + * LoRaWAN - Secure element specification v1 + */ + MIB_MC_KEY_0, + /*! + * Multicast Application session key index 0 + * + * LoRaWAN - Secure element specification v1 + */ + MIB_MC_APP_S_KEY_0, + /*! + * Multicast Network session key index 0 + * + * LoRaWAN - Secure element specification v1 + */ + MIB_MC_NWK_S_KEY_0, + /*! + * Multicast root key index 1 + * + * LoRaWAN - Secure element specification v1 + */ + MIB_MC_KEY_1, + /*! + * Multicast Application session key index 1 + * + * LoRaWAN - Secure element specification v1 + */ + MIB_MC_APP_S_KEY_1, + /*! + * Multicast Network session key index 1 + * + * LoRaWAN - Secure element specification v1 + */ + MIB_MC_NWK_S_KEY_1, + /*! + * Multicast root key index 2 + * + * LoRaWAN - Secure element specification v1 + */ + MIB_MC_KEY_2, + /*! + * Multicast Application session key index 2 + * + * LoRaWAN - Secure element specification v1 + */ + MIB_MC_APP_S_KEY_2, + /*! + * Multicast Network session key index 2 + * + * LoRaWAN - Secure element specification v1 + */ + MIB_MC_NWK_S_KEY_2, + /*! + * Multicast root key index 3 + * + * LoRaWAN - Secure element specification v1 + */ + MIB_MC_KEY_3, + /*! + * Multicast Application session key index 3 + * + * LoRaWAN - Secure element specification v1 + */ + MIB_MC_APP_S_KEY_3, + /*! + * Multicast Network session key index 3 + * + * LoRaWAN - Secure element specification v1 + */ + MIB_MC_NWK_S_KEY_3, + /*! + * Set the network type to public or private + * + * LoRaWAN Regional Parameters V1.0.2rB + * + * [true: public network, false: private network] + */ + MIB_PUBLIC_NETWORK, + /*! + * 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 Regional Parameters V1.0.2rB + */ + MIB_CHANNELS, + /*! + * Set receive window 2 channel + * + * LoRaWAN Specification V1.0.2, chapter 3.3.1 + */ + MIB_RX2_CHANNEL, + /*! + * Set receive window 2 channel + * + * LoRaWAN Specification V1.0.2, chapter 3.3.2 + */ + MIB_RX2_DEFAULT_CHANNEL, + /*! + * Set receive window C channel + * + * LoRaWAN Specification V1.0.2, chapter 3.3.1 + */ + MIB_RXC_CHANNEL, + /*! + * Set receive window C channel + * + * LoRaWAN Specification V1.0.2, chapter 3.3.2 + */ + MIB_RXC_DEFAULT_CHANNEL, + /*! + * LoRaWAN channels mask + * + * LoRaWAN Regional Parameters V1.0.2rB + */ + MIB_CHANNELS_MASK, + /*! + * LoRaWAN default channels mask + * + * LoRaWAN Regional Parameters V1.0.2rB + */ + MIB_CHANNELS_DEFAULT_MASK, + /*! + * Set the number of repetitions on a channel + * + * LoRaWAN Specification V1.0.2, chapter 5.2, V1.1.0, chapter 5.3 + */ + MIB_CHANNELS_NB_TRANS, + /*! + * Maximum receive window duration in [ms] + * + * LoRaWAN Specification V1.0.2, chapter 3.3.3 + */ + MIB_MAX_RX_WINDOW_DURATION, + /*! + * Receive delay 1 in [ms] + * + * LoRaWAN Regional Parameters V1.0.2rB + */ + MIB_RECEIVE_DELAY_1, + /*! + * Receive delay 2 in [ms] + * + * LoRaWAN Regional Parameters V1.0.2rB + */ + MIB_RECEIVE_DELAY_2, + /*! + * Join accept delay 1 in [ms] + * + * LoRaWAN Regional Parameters V1.0.2rB + */ + MIB_JOIN_ACCEPT_DELAY_1, + /*! + * Join accept delay 2 in [ms] + * + * LoRaWAN Regional Parameters V1.0.2rB + */ + MIB_JOIN_ACCEPT_DELAY_2, + /*! + * Minimum Data rate of a channel + * + * LoRaWAN Regional Parameters V1.0.2rB + * + * The possible values are region specific. Please refer to \ref DR_0 to \ref DR_15 for details. + */ + MIB_CHANNELS_MIN_TX_DATARATE, + /*! + * Default Data rate of a channel + * + * LoRaWAN Regional Parameters V1.0.2rB + * + * The allowed ranges are region specific. Please refer to \ref DR_0 to \ref DR_15 for details. + */ + MIB_CHANNELS_DEFAULT_DATARATE, + /*! + * Data rate of a channel + * + * LoRaWAN Regional Parameters V1.0.2rB + * + * The allowed ranges are region specific. Please refer to \ref DR_0 to \ref DR_15 for details. + */ + MIB_CHANNELS_DATARATE, + /*! + * Transmission power of a channel + * + * LoRaWAN Regional Parameters V1.0.2rB + * + * The allowed ranges are region specific. Please refer to \ref TX_POWER_0 to \ref TX_POWER_15 for details. + */ + MIB_CHANNELS_TX_POWER, + /*! + * Transmission power of a channel + * + * LoRaWAN Regional Parameters V1.0.2rB + * + * The allowed ranges are region specific. Please refer to \ref TX_POWER_0 to \ref TX_POWER_15 for details. + */ + MIB_CHANNELS_DEFAULT_TX_POWER, + /*! + * System overall timing error in milliseconds. + * [-SystemMaxRxError : +SystemMaxRxError] + * Default: +/-10 ms + */ + MIB_SYSTEM_MAX_RX_ERROR, + /*! + * Minimum required number of symbols to detect an Rx frame + * Default: 6 symbols + */ + MIB_MIN_RX_SYMBOLS, + /*! + * Antenna gain of the node. Default value is region specific. + * The antenna gain is used to calculate the TX power of the node. + * The formula is: + * radioTxPower = ( int8_t )floor( maxEirp - antennaGain ) + * + * \remark The antenna gain value is referenced to the isotropic antenna. + * The value is in dBi. + * MIB_ANTENNA_GAIN[dBi] = measuredAntennaGain[dBd] + 2.15 + */ + MIB_ANTENNA_GAIN, + /*! + * Default antenna gain of the node. Default value is region specific. + * The antenna gain is used to calculate the TX power of the node. + * The formula is: + * radioTxPower = ( int8_t )floor( maxEirp - antennaGain ) + * + * \remark The antenna gain value is referenced to the isotropic antenna. + * The value is in dBi. + * MIB_DEFAULT_ANTENNA_GAIN[dBi] = measuredAntennaGain[dBd] + 2.15 + */ + MIB_DEFAULT_ANTENNA_GAIN, + /*! + * Structure holding pointers to internal contexts and its size + */ + MIB_NVM_CTXS, + /*! + * LoRaWAN MAC layer operating version when activated by ABP. + */ + MIB_ABP_LORAWAN_VERSION, + /*! + * LoRaWAN MAC and regional parameter version. + */ + MIB_LORAWAN_VERSION, + /*! + * Time between periodic transmission of a Type 0 Rejoin request. + */ + MIB_REJOIN_0_CYCLE, + /*! + * Time between periodic transmission of a Type 1 Rejoin request. + */ + MIB_REJOIN_1_CYCLE, + /*! + * Beacon interval in ms + */ + MIB_BEACON_INTERVAL, + /*! + * Beacon reserved time in ms + */ + MIB_BEACON_RESERVED, + /*! + * Beacon guard time in ms + */ + MIB_BEACON_GUARD, + /*! + * Beacon window time in ms + */ + MIB_BEACON_WINDOW, + /*! + * Beacon window time in number of slots + */ + MIB_BEACON_WINDOW_SLOTS, + /*! + * Ping slot length time in ms + */ + MIB_PING_SLOT_WINDOW, + /*! + * Default symbol timeout for beacons and ping slot windows + */ + MIB_BEACON_SYMBOL_TO_DEFAULT, + /*! + * Maximum symbol timeout for beacons + */ + MIB_BEACON_SYMBOL_TO_EXPANSION_MAX, + /*! + * Maximum symbol timeout for ping slots + */ + MIB_PING_SLOT_SYMBOL_TO_EXPANSION_MAX, + /*! + * Symbol expansion value for beacon windows in case of beacon + * loss in symbols + */ + MIB_BEACON_SYMBOL_TO_EXPANSION_FACTOR, + /*! + * Symbol expansion value for ping slot windows in case of beacon + * loss in symbols + */ + MIB_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR, + /*! + * Maximum allowed beacon less time in ms + */ + MIB_MAX_BEACON_LESS_PERIOD, + /*! + * Ping slot data rate + * + * LoRaWAN Regional Parameters V1.0.2rB + * + * The allowed ranges are region specific. Please refer to \ref DR_0 to \ref DR_15 for details. + */ + MIB_PING_SLOT_DATARATE, + /*! + * LoRaWAN certification FPort handling state (ON/OFF) + */ + MIB_IS_CERT_FPORT_ON, + /*! + * ADR ack limit value + */ + MIB_ADR_ACK_LIMIT, + /*! + * ADR ack delay value + */ + MIB_ADR_ACK_DELAY, + /*! + * ADR ack default limit value + */ + MIB_ADR_ACK_DEFAULT_LIMIT, + /*! + * ADR ack default delay value + */ + MIB_ADR_ACK_DEFAULT_DELAY, + /*! + * RSSI free channel threshold value (KR920 and AS923 only) + */ + MIB_RSSI_FREE_THRESHOLD, + /*! + * Carrier sense time value (KR920 and AS923 only) + */ + MIB_CARRIER_SENSE_TIME +}Mib_t; + +/*! + * LoRaMAC MIB parameters + */ +typedef union uMibParam +{ + /*! + * LoRaWAN device class + * + * Related MIB type: \ref MIB_DEVICE_CLASS + */ + DeviceClass_t Class; + /*! + * LoRaWAN Network End-Device Activation ( ACTIVATION_TYPE_NONE, ACTIVATION_TYPE_ABP or OTTA ) + * + * Related MIB type: \ref MIB_NETWORK_ACTIVATION + */ + ActivationType_t NetworkActivation; + /*! + * LoRaWAN device EUI + * + * Related MIB type: \ref MIB_DEV_EUI + */ + uint8_t* DevEui; + /*! + * LoRaWAN Join server EUI + * + * Related MIB type: \ref MIB_JOIN_EUI + */ + uint8_t* JoinEui; + /*! + * Secure-element pin + * + * Related MIB type: \ref MIB_SE_PIN + */ + uint8_t* SePin; + /*! + * 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; + /*! + * Application root key + * + * Related MIB type: \ref MIB_APP_KEY + */ + uint8_t* AppKey; + /*! + * Network root key + * + * Related MIB type: \ref MIB_NWK_KEY + */ + uint8_t* NwkKey; + /*! + * Join session integrity key + * + * Related MIB type: \ref MIB_J_S_INT_KEY + */ + uint8_t* JSIntKey; + /*! + * Join session encryption key + * + * Related MIB type: \ref MIB_J_S_ENC_KEY + */ + uint8_t* JSEncKey; + /*! + * Forwarding Network session integrity key + * + * Related MIB type: \ref MIB_F_NWK_S_INT_KEY + */ + uint8_t* FNwkSIntKey; + /*! + * Serving Network session integrity key + * + * Related MIB type: \ref MIB_S_NWK_S_INT_KEY + */ + uint8_t* SNwkSIntKey; + /*! + * Network session encryption key + * + * Related MIB type: \ref MIB_NWK_S_ENC_KEY + */ + uint8_t* NwkSEncKey; + /*! + * Application session key + * + * Related MIB type: \ref MIB_APP_S_KEY + */ + uint8_t* AppSKey; + /*! + * Multicast key encryption key + * + * Related MIB type: \ref MIB_MC_KE_KEY + */ + uint8_t* McKEKey; + /*! + * Multicast root key index 0 + * + * Related MIB type: \ref MIB_MC_KEY_0 + */ + uint8_t* McKey0; + /*! + * Multicast Application session key index 0 + * + * Related MIB type: \ref MIB_MC_APP_S_KEY_0 + */ + uint8_t* McAppSKey0; + /*! + * Multicast Network session key index 0 + * + * Related MIB type: \ref MIB_MC_NWK_S_KEY_0 + */ + uint8_t* McNwkSKey0; + /*! + * Multicast root key index 0 + * + * Related MIB type: \ref MIB_MC_KEY_0 + */ + uint8_t* McKey1; + /*! + * Multicast Application session key index 1 + * + * Related MIB type: \ref MIB_MC_APP_S_KEY_1 + */ + uint8_t* McAppSKey1; + /*! + * Multicast Network session key index 1 + * + * Related MIB type: \ref MIB_MC_NWK_S_KEY_1 + */ + uint8_t* McNwkSKey1; + /*! + * Multicast root key index 2 + * + * Related MIB type: \ref MIB_MC_KEY_2 + */ + uint8_t* McKey2; + /*! + * Multicast Application session key index 2 + * + * Related MIB type: \ref MIB_MC_APP_S_KEY_2 + */ + uint8_t* McAppSKey2; + /*! + * Multicast Network session key index 2 + * + * Related MIB type: \ref MIB_MC_NWK_S_KEY_2 + */ + uint8_t* McNwkSKey2; + /*! + * Multicast root key index 2 + * + * Related MIB type: \ref MIB_MC_KEY_2 + */ + uint8_t* McKey3; + /*! + * Multicast Application session key index 2 + * + * Related MIB type: \ref MIB_MC_APP_S_KEY_2 + */ + uint8_t* McAppSKey3; + /*! + * Multicast Network session key index 2 + * + * Related MIB type: \ref MIB_MC_NWK_S_KEY_2 + */ + uint8_t* McNwkSKey3; + /*! + * Enable or disable a public network + * + * Related MIB type: \ref MIB_PUBLIC_NETWORK + */ + bool EnablePublicNetwork; + /*! + * LoRaWAN Channel + * + * Related MIB type: \ref MIB_CHANNELS + */ + ChannelParams_t* ChannelList; + /*! + * Channel for the receive window 2 + * + * Related MIB type: \ref MIB_RX2_CHANNEL + */ + RxChannelParams_t Rx2Channel; + /*! + * Channel for the receive window 2 + * + * Related MIB type: \ref MIB_RX2_DEFAULT_CHANNEL + */ + RxChannelParams_t Rx2DefaultChannel; + /*! + * Channel for the receive window C + * + * Related MIB type: \ref MIB_RXC_CHANNEL + */ + RxChannelParams_t RxCChannel; + /*! + * Channel for the receive window C + * + * Related MIB type: \ref MIB_RXC_DEFAULT_CHANNEL + */ + RxChannelParams_t RxCDefaultChannel; + /*! + * Channel mask + * + * Related MIB type: \ref MIB_CHANNELS_MASK + */ + uint16_t* ChannelsMask; + /*! + * Default channel mask + * + * Related MIB type: \ref MIB_CHANNELS_DEFAULT_MASK + */ + uint16_t* ChannelsDefaultMask; + /*! + * Number of frame repetitions + * + * Related MIB type: \ref MIB_CHANNELS_NB_TRANS + */ + uint8_t ChannelsNbTrans; + /*! + * 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 minimum tx data rate + * + * Related MIB type: \ref MIB_CHANNELS_MIN_TX_DATARATE + */ + int8_t ChannelsMinTxDatarate; + /*! + * Channels data rate + * + * Related MIB type: \ref MIB_CHANNELS_DEFAULT_DATARATE + */ + int8_t ChannelsDefaultDatarate; + /*! + * Channels data rate + * + * Related MIB type: \ref MIB_CHANNELS_DATARATE + */ + int8_t ChannelsDatarate; + /*! + * Channels TX power + * + * Related MIB type: \ref MIB_CHANNELS_DEFAULT_TX_POWER + */ + int8_t ChannelsDefaultTxPower; + /*! + * Channels TX power + * + * Related MIB type: \ref MIB_CHANNELS_TX_POWER + */ + int8_t ChannelsTxPower; + /*! + * Multicast channels + * + * Related MIB type: \ref MIB_MULTICAST_CHANNEL + */ + McChannelParams_t MulticastChannel; + /*! + * System overall timing error in milliseconds. + * + * Related MIB type: \ref MIB_SYSTEM_MAX_RX_ERROR + */ + uint32_t SystemMaxRxError; + /*! + * Minimum required number of symbols to detect an Rx frame + * + * Related MIB type: \ref MIB_MIN_RX_SYMBOLS + */ + uint8_t MinRxSymbols; + /*! + * Antenna gain + * + * Related MIB type: \ref MIB_ANTENNA_GAIN + */ + float AntennaGain; + /*! + * Default antenna gain + * + * Related MIB type: \ref MIB_DEFAULT_ANTENNA_GAIN + */ + float DefaultAntennaGain; + /*! + * Returns a pointer to the structure holding all data which shall be stored + * in the NVM. + * + * Related MIB type: \ref MIB_NVM_CTXS + */ + LoRaMacNvmData_t* Contexts; + /* + * LoRaWAN MAC layer operating version when activated by ABP. + * + * Related MIB type: \ref MIB_ABP_LORAWAN_VERSION + */ + Version_t AbpLrWanVersion; + /* + * LoRaWAN MAC regional parameter version. + * + * Related MIB type: \ref MIB_LORAWAN_VERSION + */ + struct sLrWanVersion + { + Version_t LoRaWan; + Version_t LoRaWanRegion; + }LrWanVersion; + /*! + * Time in seconds between cyclic transmission of Type 0 Rejoin requests. + */ + uint32_t Rejoin0CycleInSec; + /*! + * Time in seconds between cyclic transmission of Type 1 Rejoin requests. + */ + uint32_t Rejoin1CycleInSec; + /*! + * Time in seconds between cyclic transmission of Type 2 Rejoin requests. + */ + uint32_t Rejoin2CycleInSec; + /*! + * Beacon interval in ms + * + * Related MIB type: \ref MIB_BEACON_INTERVAL + */ + uint32_t BeaconInterval; + /*! + * Beacon reserved time in ms + * + * Related MIB type: \ref MIB_BEACON_RESERVED + */ + uint32_t BeaconReserved; + /*! + * Beacon guard time in ms + * + * Related MIB type: \ref MIB_BEACON_GUARD + */ + uint32_t BeaconGuard; + /*! + * Beacon window time in ms + * + * Related MIB type: \ref MIB_BEACON_WINDOW + */ + uint32_t BeaconWindow; + /*! + * Beacon window time in number of slots + * + * Related MIB type: \ref MIB_BEACON_WINDOW_SLOTS + */ + uint32_t BeaconWindowSlots; + /*! + * Ping slot length time in ms + * + * Related MIB type: \ref MIB_PING_SLOT_WINDOW + */ + uint32_t PingSlotWindow; + /*! + * Default symbol timeout for beacons and ping slot windows + * + * Related MIB type: \ref MIB_BEACON_SYMBOL_TO_DEFAULT + */ + uint32_t BeaconSymbolToDefault; + /*! + * Maximum symbol timeout for beacons + * + * Related MIB type: \ref MIB_BEACON_SYMBOL_TO_EXPANSION_MAX + */ + uint32_t BeaconSymbolToExpansionMax; + /*! + * Maximum symbol timeout for ping slots + * + * Related MIB type: \ref MIB_PING_SLOT_SYMBOL_TO_EXPANSION_MAX + */ + uint32_t PingSlotSymbolToExpansionMax; + /*! + * Symbol expansion value for beacon windows in case of beacon + * loss in symbols + * + * Related MIB type: \ref MIB_BEACON_SYMBOL_TO_EXPANSION_FACTOR + */ + uint32_t BeaconSymbolToExpansionFactor; + /*! + * Symbol expansion value for ping slot windows in case of beacon + * loss in symbols + * + * Related MIB type: \ref MIB_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR + */ + uint32_t PingSlotSymbolToExpansionFactor; + /*! + * Maximum allowed beacon less time in ms + * + * Related MIB type: \ref MIB_MAX_BEACON_LESS_PERIOD + */ + uint32_t MaxBeaconLessPeriod; + /*! + * Ping slots data rate + * + * Related MIB type: \ref MIB_PING_SLOT_DATARATE + */ + int8_t PingSlotDatarate; + /*! + * LoRaWAN certification FPort handling state (ON/OFF) + * + * Related MIB type: \ref MIB_IS_CERT_FPORT_ON + */ + bool IsCertPortOn; + /*! + * ADR ack limit value + * + * Related MIB types: \ref MIB_ADR_ACK_LIMIT, MIB_ADR_ACK_DEFAULT_LIMIT + */ + uint16_t AdrAckLimit; + /*! + * ADR ack delay value + * + * Related MIB types: \ref MIB_ADR_ACK_DELAY, MIB_ADR_ACK_DEFAULT_DELAY + */ + uint16_t AdrAckDelay; + /*! + * RSSI free channel threshold (KR920 and AS923 only) + * + * Related MIB type: \ref MIB_RSSI_FREE_THRESHOLD + */ + int16_t RssiFreeThreshold; + /*! + * Carrier sense time (KR920 and AS923 only) + * + * Related MIB type: \ref MIB_CARRIER_SENSE_TIME + */ + uint32_t CarrierSenseTime; +}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 +{ + /*! + * Size of the application data payload which can be transmitted. + */ + uint8_t MaxPossibleApplicationDataSize; + /*! + * The current maximum possible payload size without MAC commands + * which is dependent on the current datarate. + */ + uint8_t CurrentPossiblePayloadSize; +}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 - invalid frequency + */ + LORAMAC_STATUS_FREQUENCY_INVALID, + /*! + * Service not started - invalid datarate + */ + LORAMAC_STATUS_DATARATE_INVALID, + /*! + * Service not started - invalid frequency and datarate + */ + LORAMAC_STATUS_FREQ_AND_DR_INVALID, + /*! + * Service not started - the device is not in a LoRaWAN + */ + LORAMAC_STATUS_NO_NETWORK_JOINED, + /*! + * Service not started - payload length error + */ + LORAMAC_STATUS_LENGTH_ERROR, + /*! + * Service not started - the specified region is not supported + * or not activated with preprocessor definitions. + */ + LORAMAC_STATUS_REGION_NOT_SUPPORTED, + /*! + * The application data was not transmitted + * because prioritized pending MAC commands had to be sent. + */ + LORAMAC_STATUS_SKIPPED_APP_DATA, + /*! + * An MCPS or MLME request can return this status. In this case, + * the MAC cannot send the frame, as the duty cycle limits all + * available bands. When a request returns this value, the + * variable "DutyCycleWaitTime" in "ReqReturn" of the input + * parameters contains the remaining time to wait. If the + * value is constant and does not change, the expected time + * on air for this frame is exceeding the maximum permitted + * time according to the duty cycle time period, defined + * in Region.h, DUTY_CYCLE_TIME_PERIOD. By default this time + * is 1 hour, and a band with 1% duty cycle is then allowed + * to use an air time of 36 seconds. + */ + LORAMAC_STATUS_DUTYCYCLE_RESTRICTED, + /*! + * + */ + LORAMAC_STATUS_NO_CHANNEL_FOUND, + /*! + * + */ + LORAMAC_STATUS_NO_FREE_CHANNEL_FOUND, + /*! + * ToDo + */ + LORAMAC_STATUS_BUSY_BEACON_RESERVED_TIME, + /*! + * ToDo + */ + LORAMAC_STATUS_BUSY_PING_SLOT_WINDOW_TIME, + /*! + * ToDo + */ + LORAMAC_STATUS_BUSY_UPLINK_COLLISION, + /*! + * An error in the cryptographic module is occurred + */ + LORAMAC_STATUS_CRYPTO_ERROR, + /*! + * An error in the frame counter handler module is occurred + */ + LORAMAC_STATUS_FCNT_HANDLER_ERROR, + /*! + * An error in the MAC command module is occurred + */ + LORAMAC_STATUS_MAC_COMMAD_ERROR, + /*! + * An error in the Class B module is occurred + */ + LORAMAC_STATUS_CLASS_B_ERROR, + /*! + * An error in the Confirm Queue module is occurred + */ + LORAMAC_STATUS_CONFIRM_QUEUE_ERROR, + /*! + * The multicast group doesn't exist + */ + LORAMAC_STATUS_MC_GROUP_UNDEFINED, + /*! + * Undefined error occurred + */ + LORAMAC_STATUS_ERROR +}LoRaMacStatus_t; + +/*! + * LoRaMAC events structure + * Used to notify upper layers of MAC events + */ +typedef struct sLoRaMacPrimitives +{ + /*! + * \brief MCPS-Confirm primitive + * + * \param [OUT] MCPS-Confirm parameters + */ + void ( *MacMcpsConfirm )( McpsConfirm_t* McpsConfirm ); + /*! + * \brief MCPS-Indication primitive + * + * \param [OUT] MCPS-Indication parameters + */ + void ( *MacMcpsIndication )( McpsIndication_t* McpsIndication ); + /*! + * \brief MLME-Confirm primitive + * + * \param [OUT] MLME-Confirm parameters + */ + void ( *MacMlmeConfirm )( MlmeConfirm_t* MlmeConfirm ); + /*! + * \brief MLME-Indication primitive + * + * \param [OUT] MLME-Indication parameters + */ + void ( *MacMlmeIndication )( MlmeIndication_t* MlmeIndication ); +}LoRaMacPrimitives_t; + +/*! + * LoRaMAC callback structure + */ +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 ); + /*! + * \brief Measures the temperature level + * + * \retval Temperature level + */ + float ( *GetTemperatureLevel )( void ); + /*! + * \brief Will be called when an attribute has changed in one of the context. + * + * \param notifyFlags Bitmap that contains the modules which changed. + * Refer to \ref LoRaMacNvmData_t. + */ + void ( *NvmDataChange )( uint16_t notifyFlags ); + /*! + *\brief Will be called each time a Radio IRQ is handled by the MAC + * layer. + * + *\warning Runs in a IRQ context. Should only change variables state. + */ + void ( *MacProcessNotify )( void ); +}LoRaMacCallback_t; + + +/*! + * LoRaMAC Max EIRP (dBm) table + */ +static const uint8_t LoRaMacMaxEirpTable[] = { 8, 10, 12, 13, 14, 16, 18, 20, 21, 24, 26, 27, 29, 30, 33, 36 }; + +/*! + * \brief LoRaMAC layer initialization + * + * \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] primitives - Pointer to a structure defining the LoRaMAC + * event functions. Refer to \ref LoRaMacPrimitives_t. + * + * \param [IN] callbacks - Pointer to a structure defining the LoRaMAC + * callback functions. Refer to \ref LoRaMacCallback_t. + * + * \param [IN] region - The region to start. + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_PARAMETER_INVALID, + * \ref LORAMAC_STATUS_REGION_NOT_SUPPORTED. + */ +LoRaMacStatus_t LoRaMacInitialization( LoRaMacPrimitives_t* primitives, LoRaMacCallback_t* callbacks, LoRaMacRegion_t region ); + +/*! + * \brief Starts LoRaMAC layer + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * returns are: + * \ref LORAMAC_STATUS_OK, + */ +LoRaMacStatus_t LoRaMacStart( void ); + +/*! + * \brief Stops LoRaMAC layer + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * returns are: + * \ref LORAMAC_STATUS_OK, + */ +LoRaMacStatus_t LoRaMacStop( void ); + +/*! + * \brief Returns a value indicating if the MAC layer is busy or not. + * + * \retval isBusy Mac layer is busy. + */ +bool LoRaMacIsBusy( void ); + +/*! + * Processes the LoRaMac events. + * + * \remark This function must be called in the main loop. + */ +void LoRaMacProcess( void ); + +/*! + * \brief Queries the LoRaMAC if it is possible to send the next frame with + * a given application data 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 application data payload to be send next + * + * \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 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 application data payload in combination + * with the MAC commands, the function returns \ref LORAMAC_STATUS_LENGTH_ERROR. + * In this case its recommended to send a frame without application data to flush + * the MAC commands. Otherwise the LoRaMAC will prioritize the MAC commands and + * if needed it will skip the application data. Please note that if MAC commands do + * not fit at all into the payload size on the related datarate, the LoRaMAC will + * automatically clip the MAC commands. + * In case the query is valid, and the LoRaMAC is able to send the frame, + * the function returns \ref LORAMAC_STATUS_OK. + */ +LoRaMacStatus_t LoRaMacQueryTxPossible( uint8_t size, LoRaMacTxInfo_t* txInfo ); + +/*! + * \brief LoRaMAC channel add service + * + * \details Adds a new channel to the channel list and activates the id in + * the channel mask. Please note that this functionality is not available + * on all regions. Information about allowed ranges are available at the LoRaWAN Regional Parameters V1.0.2rB + * + * \param [IN] id - Id of the channel. + * + * \param [IN] params - Channel parameters to set. + * + * \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 LoRaMacChannelAdd( uint8_t id, ChannelParams_t params ); + +/*! + * \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 ); + +/*! + * \brief LoRaMAC multicast channel setup service + * + * \details Sets up a multicast channel. + * + * \param [IN] channel - Multicast channel to set. + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_BUSY, + * \ref LORAMAC_STATUS_PARAMETER_INVALID, + * \ref LORAMAC_STATUS_MC_GROUP_UNDEFINED. + */ +LoRaMacStatus_t LoRaMacMcChannelSetup( McChannelParams_t *channel ); + +/*! + * \brief LoRaMAC multicast channel removal service + * + * \details Removes/Disables a multicast channel. + * + * \param [IN] groupID - Multicast channel ID to be removed/disabled + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_BUSY, + * \ref LORAMAC_STATUS_MC_GROUP_UNDEFINED. + */ +LoRaMacStatus_t LoRaMacMcChannelDelete( AddressIdentifier_t groupID ); + +/*! + * \brief LoRaMAC multicast channel get groupId from MC address. + * + * \param [IN] mcAddress - Multicast address to be checked + * + * \retval groupID Multicast channel ID associated to the address. + * Returns 0xFF if the address isn't found. + */ +uint8_t LoRaMacMcChannelGetGroupId( uint32_t mcAddress ); + +/*! + * \brief LoRaMAC multicast channel Rx parameters setup service + * + * \details Sets up a multicast channel reception parameters. + * + * \param [IN] groupID - Multicast channel ID + * \param [IN] rxParams - Reception parameters + * \param [OUT] status - Status mask [UNDEF_ID | FREQ_ERR | DR_ERR | GROUP_ID] + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_BUSY, + * \ref LORAMAC_STATUS_PARAMETER_INVALID, + * \ref LORAMAC_STATUS_MC_GROUP_UNDEFINED. + */ +LoRaMacStatus_t LoRaMacMcChannelSetupRxParams( AddressIdentifier_t groupID, McRxParams_t *rxParams, uint8_t *status ); + +/*! + * \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; + * + * 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. + */ +LoRaMacStatus_t LoRaMacMibGetRequestConfirm( MibRequestConfirm_t* mibGet ); + +/*! + * \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; + * + * 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. + */ +LoRaMacStatus_t LoRaMacMibSetRequestConfirm( MibRequestConfirm_t* mibSet ); + +/*! + * \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. Please note that for a join request, the + * DevEUI and the JoinEUI must be set previously via the MIB. Please + * also refer to the sample implementations. + * + * \code + * + * MlmeReq_t mlmeReq; + * mlmeReq.Type = MLME_JOIN; + * mlmeReq.Req.Join.Datarate = LORAWAN_DEFAULT_DATARATE; + * + * 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, + */ +LoRaMacStatus_t LoRaMacMlmeRequest( MlmeReq_t* mlmeRequest ); + +/*! + * \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. + * + * \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 ); + * + * 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, + */ +LoRaMacStatus_t LoRaMacMcpsRequest( McpsReq_t* mcpsRequest ); + +/*! + * \brief LoRaMAC deinitialization + * + * \details This function stops the timers, re-initializes MAC & regional parameters to default + * and sets radio into sleep state. + * + * \retval LoRaMacStatus_t Status of the operation. Possible returns are: + * \ref LORAMAC_STATUS_OK, + * \ref LORAMAC_STATUS_BUSY + */ +LoRaMacStatus_t LoRaMacDeInitialization( void ); + +/*! + * \brief Resets the internal state machine. + * + * \details Resets the internal state machine to force the MAC to finalize a procedure. + */ +void LoRaMacReset( void ); + +/*! \} defgroup LORAMAC */ + +#ifdef __cplusplus +} +#endif + +#endif // __LORAMAC_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacAdr.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacAdr.c new file mode 100644 index 0000000000..4bac1e0f5f --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacAdr.c @@ -0,0 +1,109 @@ +/*! + * \file LoRaMacAdr.c + * + * \brief LoRa MAC ADR implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + */ + +#include "region/Region.h" +#include "LoRaMacAdr.h" + +bool LoRaMacAdrCalcNext( CalcNextAdrParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, + uint8_t* nbTransOut, uint32_t* adrAckCounter ) +{ + bool adrAckReq = false; + int8_t datarate = adrNext->Datarate; + int8_t txPower = adrNext->TxPower; + uint8_t nbTrans = adrNext->NbTrans; + int8_t minTxDatarate; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + + // Report back the adr ack counter + *adrAckCounter = adrNext->AdrAckCounter; + + if( adrNext->AdrEnabled == true ) + { + // Query minimum TX Datarate + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = adrNext->UplinkDwellTime; + phyParam = RegionGetPhyParam( adrNext->Region, &getPhy ); + minTxDatarate = phyParam.Value; + datarate = MAX( datarate, minTxDatarate ); + + // Verify if ADR ack req bit needs to be set. + if( adrNext->AdrAckCounter >= adrNext->AdrAckLimit ) + { + adrAckReq = true; + } + + // Verify, if we need to set the TX power to default + if( adrNext->AdrAckCounter >= ( adrNext->AdrAckLimit + adrNext->AdrAckDelay ) ) + { + // Set TX Power to default + getPhy.Attribute = PHY_DEF_TX_POWER; + phyParam = RegionGetPhyParam( adrNext->Region, &getPhy ); + txPower = phyParam.Value; + } + + // Verify, if we need to decrease the data rate + if( adrNext->AdrAckCounter >= ( uint32_t )( adrNext->AdrAckLimit + ( adrNext->AdrAckDelay << 1 ) ) ) + { + // Perform actions with every adrNext->AdrAckDelay only + if( ( ( adrNext->AdrAckCounter - adrNext->AdrAckLimit ) % adrNext->AdrAckDelay ) == 0 ) + { + if( datarate == minTxDatarate ) + { + // Restore the channel mask + if( adrNext->UpdateChanMask == true ) + { + InitDefaultsParams_t params; + params.Type = INIT_TYPE_ACTIVATE_DEFAULT_CHANNELS; + RegionInitDefaults( adrNext->Region, ¶ms ); + } + + // Restore NbTrans + nbTrans = 1; + } + + // Decrease the datarate + getPhy.Attribute = PHY_NEXT_LOWER_TX_DR; + getPhy.Datarate = datarate; + getPhy.UplinkDwellTime = adrNext->UplinkDwellTime; + phyParam = RegionGetPhyParam( adrNext->Region, &getPhy ); + datarate = phyParam.Value; + } + } + } + + *drOut = datarate; + *txPowOut = txPower; + *nbTransOut = nbTrans; + return adrAckReq; +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacAdr.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacAdr.h new file mode 100644 index 0000000000..eec637aa1c --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacAdr.h @@ -0,0 +1,126 @@ +/*! + * \file LoRaMacAdr.h + * + * \brief LoRa MAC ADR implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * \defgroup LORAMACADR LoRa MAC ADR implementation + * Implementation of the ADR algorithm for LoRa. + * \{ + */ +#ifndef __LORAMACADR_H__ +#define __LORAMACADR_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*! \} defgroup LORAMACADR */ + +/* + * Parameter structure for the function CalcNextAdr. + */ +typedef struct sCalcNextAdrParams +{ + /*! + * Set to true, if the function should update the channels mask. + */ + bool UpdateChanMask; + /*! + * Set to true, if ADR is enabled. + */ + bool AdrEnabled; + /*! + * ADR ack counter. + */ + uint32_t AdrAckCounter; + /*! + * ADR Ack limit + */ + uint16_t AdrAckLimit; + /*! + * ADR Ack delay + */ + uint16_t AdrAckDelay; + /*! + * Datarate used currently. + */ + int8_t Datarate; + /*! + * TX power used currently. + */ + int8_t TxPower; + /*! + * NbTrans counter used currently. + */ + uint8_t NbTrans; + /*! + * UplinkDwellTime + */ + uint8_t UplinkDwellTime; + /*! + * Region + */ + LoRaMacRegion_t Region; +}CalcNextAdrParams_t; + +/*! + * \brief Calculates the next datarate to set, when ADR is on or off. + * + * \details Here is a summary of the actions: + * + * | ADR_ACK_CNT | Action | + * | ----------- | --------------------------------------------------------- | + * | 0... 63 | Do nothing | + * | 64...95 | Set ADR ack bit | + * | 96...127 | Set TX power to default (if already default, do nothing) | + * | 128...159 | Set data rate to default (if already default, do nothing) | + * | >=160 | Set NbTrans to 1, re-enable default channels | + * + * \param [IN] adrNext Pointer to the function parameters. + * + * \param [OUT] drOut The calculated datarate for the next TX. + * + * \param [OUT] txPowOut The TX power for the next TX. + * + * \param [OUT] nbTransOut The NbTrans counter. + * + * \param [OUT] adrAckCounter The calculated ADR acknowledgement counter. + * + * \retval Returns true, if an ADR request should be performed. + */ +bool LoRaMacAdrCalcNext( CalcNextAdrParams_t* adrNext, int8_t* drOut, int8_t* txPowOut, + uint8_t* nbTransOut, uint32_t* adrAckCounter ); + +#ifdef __cplusplus +} +#endif + +#endif // __LORAMACADR_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacClassB.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacClassB.c new file mode 100644 index 0000000000..5efd83c033 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacClassB.c @@ -0,0 +1,1887 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: LoRa MAC Class B layer implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE ) +*/ +#include +#include "utilities.h" +#include "secure-element.h" +#include "LoRaMac.h" +#include "LoRaMacClassB.h" +#include "LoRaMacClassBNvm.h" +#include "LoRaMacClassBConfig.h" +#include "LoRaMacCrypto.h" +#include "LoRaMacConfirmQueue.h" +#include "radio.h" +#include "region/Region.h" + +#ifdef LORAMAC_CLASSB_ENABLED + + +/* + * LoRaMac Class B Context structure + */ +typedef struct sLoRaMacClassBCtx +{ + /*! + * Class B ping slot context + */ + PingSlotContext_t PingSlotCtx; + /*! + * Class B beacon context + */ + BeaconContext_t BeaconCtx; + /*! + * State of the beaconing mechanism + */ + BeaconState_t BeaconState; + /*! + * State of the ping slot mechanism + */ + PingSlotState_t PingSlotState; + /*! + * State of the multicast slot mechanism + */ + PingSlotState_t MulticastSlotState; + /*! + * Timer for CLASS B beacon acquisition and tracking. + */ + TimerEvent_t BeaconTimer; + /*! + * Timer for CLASS B ping slot timer. + */ + TimerEvent_t PingSlotTimer; + /*! + * Timer for CLASS B multicast ping slot timer. + */ + TimerEvent_t MulticastSlotTimer; + /*! + * Container for the callbacks related to class b. + */ + LoRaMacClassBCallback_t LoRaMacClassBCallbacks; + /*! + * Data structure which holds the parameters which needs to be set + * in class b operation. + */ + LoRaMacClassBParams_t LoRaMacClassBParams; +} LoRaMacClassBCtx_t; + +/*! + * Defines the LoRaMac radio events status + */ +typedef union uLoRaMacClassBEvents +{ + uint32_t Value; + struct sEvents + { + uint32_t Beacon : 1; + uint32_t PingSlot : 1; + uint32_t MulticastSlot : 1; + }Events; +}LoRaMacClassBEvents_t; + +LoRaMacClassBEvents_t LoRaMacClassBEvents = { .Value = 0 }; + +/* + * Module context. + */ +static LoRaMacClassBCtx_t Ctx; + +/* + * Beacon transmit time precision in milliseconds. + * The usage of these values shall be determined by the + * prec value in param field received in a beacon frame. + * As the time base is milli seconds, the precision will be either 0 ms or 1 ms. + */ +static const uint8_t BeaconPrecTimeValue[4] = { 0, 1, 1, 1 }; + +/*! + * Data structure which holds the parameters which needs to be stored + * in the NVM. + */ +static LoRaMacClassBNvmData_t* ClassBNvm; + +/*! + * Computes the Ping Offset + * + * \param [IN] beaconTime - Time of the recent received beacon + * \param [IN] address - Frame address + * \param [IN] pingPeriod - Ping period of the node + * \param [OUT] pingOffset - Pseudo random ping offset + */ +static void ComputePingOffset( uint64_t beaconTime, uint32_t address, uint16_t pingPeriod, uint16_t *pingOffset ) +{ + uint8_t buffer[16]; + uint8_t cipher[16]; + uint32_t result = 0; + /* Refer to chapter 15.2 of the LoRaWAN specification v1.1. The beacon time + * GPS time in seconds modulo 2^32 + */ + uint32_t time = ( beaconTime % ( ( ( uint64_t ) 1 ) << 32 ) ); + + memset1( buffer, 0, 16 ); + memset1( cipher, 0, 16 ); + + buffer[0] = ( time ) & 0xFF; + buffer[1] = ( time >> 8 ) & 0xFF; + buffer[2] = ( time >> 16 ) & 0xFF; + buffer[3] = ( time >> 24 ) & 0xFF; + + buffer[4] = ( address ) & 0xFF; + buffer[5] = ( address >> 8 ) & 0xFF; + buffer[6] = ( address >> 16 ) & 0xFF; + buffer[7] = ( address >> 24 ) & 0xFF; + + SecureElementAesEncrypt( buffer, 16, SLOT_RAND_ZERO_KEY, cipher ); + + result = ( ( ( uint32_t ) cipher[0] ) + ( ( ( uint32_t ) cipher[1] ) * 256 ) ); + + *pingOffset = ( uint16_t )( result % pingPeriod ); +} + +/*! + * \brief Calculates the downlink frequency for a given channel. + * + * \param [IN] channel The channel according to the channel plan. + * + * \param [IN] isBeacon Set to true, if the function shall + * calculate the frequency for a beacon. + * + * \retval The downlink frequency + */ +static uint32_t CalcDownlinkFrequency( uint8_t channel, bool isBeacon ) +{ + GetPhyParams_t getPhy; + PhyParam_t phyParam; + + getPhy.Attribute = PHY_PING_SLOT_CHANNEL_FREQ; + + if( isBeacon == true ) + { + getPhy.Attribute = PHY_BEACON_CHANNEL_FREQ; + } + getPhy.Channel = channel; + phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy ); + + return phyParam.Value; +} + +/*! + * \brief Calculates the downlink channel for the beacon and for + * ping slot downlinks. + * + * \param [IN] devAddr The address of the device. Assign 0 if its a beacon. + * + * \param [IN] beaconTime The beacon time of the beacon. + * + * \param [IN] beaconInterval The beacon interval. + * + * \param [IN] isBeacon Set to true, if the function shall + * calculate the frequency for a beacon. + * + * \retval The downlink channel + */ +static uint32_t CalcDownlinkChannelAndFrequency( uint32_t devAddr, TimerTime_t beaconTime, + TimerTime_t beaconInterval, bool isBeacon ) +{ + GetPhyParams_t getPhy; + PhyParam_t phyParam; + uint32_t channel = 0; + uint8_t nbChannels = 0; + uint8_t offset = 0; + + // Default initialization - ping slot channels + getPhy.Attribute = PHY_PING_SLOT_NB_CHANNELS; + + if( isBeacon == true ) + { + // Beacon channels + getPhy.Attribute = PHY_BEACON_NB_CHANNELS; + } + phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy ); + nbChannels = ( uint8_t ) phyParam.Value; + + // nbChannels is > 1, when the channel plan requires more than one possible channel + // defined by the calculation below. + if( nbChannels > 1 ) + { + getPhy.Attribute = PHY_BEACON_CHANNEL_OFFSET; + phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy ); + offset = ( uint8_t ) phyParam.Value; + + // Calculate the channel for the next downlink + channel = devAddr + ( beaconTime / ( beaconInterval / 1000 ) ); + channel = channel % nbChannels; + channel += offset; + } + + // Calculate the frequency for the next downlink. This holds + // for beacons and ping slots. + return CalcDownlinkFrequency( channel, isBeacon ); +} + +/*! + * \brief Calculates the correct frequency and opens up the beacon reception window. Please + * note that the variable WindowTimeout and WindowOffset will be updated according + * to the current settings. Also, the function perform a calculation only, when + * Ctx.BeaconCtx.Ctrl.BeaconAcquired OR Ctx.BeaconCtx.Ctrl.AcquisitionPending is + * set to 1. + * + * \param [IN] rxConfig Reception parameters for the beacon window. + * + * \param [IN] currentSymbolTimeout Current symbol timeout. + */ +static void CalculateBeaconRxWindowConfig( RxConfigParams_t* rxConfig, uint16_t currentSymbolTimeout ) +{ + GetPhyParams_t getPhy; + PhyParam_t phyParam; + uint32_t maxRxError = 0; + + rxConfig->WindowTimeout = currentSymbolTimeout; + rxConfig->WindowOffset = 0; + + if( ( Ctx.BeaconCtx.Ctrl.BeaconAcquired == 1 ) || ( Ctx.BeaconCtx.Ctrl.AcquisitionPending == 1 ) ) + { + // Apply the symbol timeout only if we have acquired the beacon + // Otherwise, take the window enlargement into account + // Read beacon datarate + getPhy.Attribute = PHY_BEACON_CHANNEL_DR; + phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy ); + + // Compare and assign the maximum between the region specific rx error window time + // and time precision received from beacon frame format. + maxRxError = MAX( Ctx.LoRaMacClassBParams.LoRaMacParams->SystemMaxRxError, + ( uint32_t ) Ctx.BeaconCtx.BeaconTimePrecision.SubSeconds ); + + // Calculate downlink symbols + RegionComputeRxWindowParameters( *Ctx.LoRaMacClassBParams.LoRaMacRegion, + ( int8_t )phyParam.Value, // datarate + Ctx.LoRaMacClassBParams.LoRaMacParams->MinRxSymbols, + maxRxError, + rxConfig ); + } +} + +/*! + * \brief Calculates the correct frequency and opens up the beacon reception window. + * + * \param [IN] rxTime The reception time which should be setup + * + * \param [IN] activateDefaultChannel Set to true, if the function shall setup the default channel + * + * \param [IN] symbolTimeout Symbol timeout + */ +static void RxBeaconSetup( TimerTime_t rxTime, bool activateDefaultChannel, uint16_t symbolTimeout ) +{ + RxBeaconSetup_t rxBeaconSetup; + uint32_t frequency = 0; + + if( activateDefaultChannel == true ) + { + // This is the default frequency in case we don't know when the next + // beacon will be transmitted. We select channel 0 as default. + frequency = CalcDownlinkFrequency( 0, true ); + } + else + { + // This is the frequency according to the channel plan + frequency = CalcDownlinkChannelAndFrequency( 0, Ctx.BeaconCtx.BeaconTime.Seconds + ( CLASSB_BEACON_INTERVAL / 1000 ), + CLASSB_BEACON_INTERVAL, true ); + } + + if( ClassBNvm->BeaconCtx.Ctrl.CustomFreq == 1 ) + { + // Set the frequency from the BeaconFreqReq + frequency = ClassBNvm->BeaconCtx.Frequency; + } + + if( Ctx.BeaconCtx.Ctrl.BeaconChannelSet == 1 ) + { + // Set the frequency which was provided by BeaconTimingAns MAC command + Ctx.BeaconCtx.Ctrl.BeaconChannelSet = 0; + frequency = CalcDownlinkFrequency( Ctx.BeaconCtx.BeaconTimingChannel, true ); + } + + rxBeaconSetup.SymbolTimeout = symbolTimeout; + rxBeaconSetup.RxTime = rxTime; + rxBeaconSetup.Frequency = frequency; + + RegionRxBeaconSetup( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &rxBeaconSetup, &Ctx.LoRaMacClassBParams.McpsIndication->RxDatarate ); + + Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.Frequency = frequency; + Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.Datarate = Ctx.LoRaMacClassBParams.McpsIndication->RxDatarate; +} + +/*! + * \brief Calculates the next ping slot time. + * + * \param [IN] slotOffset The ping slot offset + * \param [IN] pingPeriod The ping period + * \param [OUT] timeOffset Time offset of the next slot, based on current time + * + * \retval [true: ping slot found, false: no ping slot found] + */ +static bool CalcNextSlotTime( uint16_t slotOffset, uint16_t pingPeriod, uint16_t pingNb, TimerTime_t* timeOffset ) +{ + uint8_t currentPingSlot = 0; + TimerTime_t slotTime = 0; + TimerTime_t currentTime = TimerGetCurrentTime( ); + + // Calculate the point in time of the last beacon even if we missed it + slotTime = ( ( currentTime - SysTimeToMs( Ctx.BeaconCtx.LastBeaconRx ) ) % CLASSB_BEACON_INTERVAL ); + slotTime = currentTime - slotTime; + + // Add the reserved time and the ping offset + slotTime += CLASSB_BEACON_RESERVED; + slotTime += slotOffset * CLASSB_PING_SLOT_WINDOW; + + if( slotTime < currentTime ) + { + currentPingSlot = ( ( currentTime - slotTime ) / + ( pingPeriod * CLASSB_PING_SLOT_WINDOW ) ) + 1; + slotTime += ( ( TimerTime_t )( currentPingSlot * pingPeriod ) * + CLASSB_PING_SLOT_WINDOW ); + } + + if( currentPingSlot < pingNb ) + { + if( slotTime <= ( SysTimeToMs( Ctx.BeaconCtx.NextBeaconRx ) - CLASSB_BEACON_GUARD - CLASSB_PING_SLOT_WINDOW ) ) + { + // Calculate the relative ping slot time + slotTime -= currentTime; + slotTime -= Radio.GetWakeupTime( ); + slotTime = TimerTempCompensation( slotTime, Ctx.BeaconCtx.Temperature ); + *timeOffset = slotTime; + return true; + } + } + return false; +} + +/*! + * \brief Calculates CRC's of the beacon frame + * + * \param [IN] buffer Pointer to the data + * \param [IN] length Length of the data + * + * \retval CRC + */ +static uint16_t BeaconCrc( uint8_t *buffer, uint16_t length ) +{ + // The CRC calculation follows CCITT + const uint16_t polynom = 0x1021; + // CRC initial value + uint16_t crc = 0x0000; + + if( buffer == NULL ) + { + return 0; + } + + for( uint16_t i = 0; i < length; ++i ) + { + crc ^= ( uint16_t ) buffer[i] << 8; + for( uint16_t j = 0; j < 8; ++j ) + { + crc = ( crc & 0x8000 ) ? ( crc << 1 ) ^ polynom : ( crc << 1 ); + } + } + + return crc; +} + +static void GetTemperatureLevel( LoRaMacClassBCallback_t *callbacks, BeaconContext_t *beaconCtx ) +{ + // Measure temperature, if available + if( ( callbacks != NULL ) && ( callbacks->GetTemperatureLevel != NULL ) ) + { + beaconCtx->Temperature = callbacks->GetTemperatureLevel( ); + } +} + +static void OnClassBMacProcessNotify( void ) +{ + if( Ctx.LoRaMacClassBCallbacks.MacProcessNotify != NULL ) + { + Ctx.LoRaMacClassBCallbacks.MacProcessNotify( ); + } +} + +static void InitClassB( void ) +{ + GetPhyParams_t getPhy; + PhyParam_t phyParam; + + // Init events + LoRaMacClassBEvents.Value = 0; + + // Init variables to default + memset1( ( uint8_t* ) ClassBNvm, 0, sizeof( LoRaMacClassBNvmData_t ) ); + memset1( ( uint8_t* ) &Ctx.PingSlotCtx, 0, sizeof( PingSlotContext_t ) ); + memset1( ( uint8_t* ) &Ctx.BeaconCtx, 0, sizeof( BeaconContext_t ) ); + + // Setup default temperature + Ctx.BeaconCtx.Temperature = 25.0; + GetTemperatureLevel( &Ctx.LoRaMacClassBCallbacks, &Ctx.BeaconCtx ); + + // Setup default ping slot datarate + getPhy.Attribute = PHY_PING_SLOT_CHANNEL_DR; + phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy ); + ClassBNvm->PingSlotCtx.Datarate = ( int8_t )( phyParam.Value ); + + // Setup default FPending bit + ClassBNvm->PingSlotCtx.FPendingSet = 0; + + // Setup default states + Ctx.BeaconState = BEACON_STATE_ACQUISITION; + Ctx.PingSlotState = PINGSLOT_STATE_CALC_PING_OFFSET; + Ctx.MulticastSlotState = PINGSLOT_STATE_CALC_PING_OFFSET; +} + +static void InitClassBDefaults( void ) +{ + // This function shall reset the Class B settings to default, + // but should keep important configurations + LoRaMacClassBBeaconNvmData_t beaconCtx = ClassBNvm->BeaconCtx; + LoRaMacClassBPingSlotNvmData_t pingSlotCtx = ClassBNvm->PingSlotCtx; + + InitClassB( ); + + // Parameters from BeaconFreqReq + ClassBNvm->BeaconCtx.Frequency = beaconCtx.Frequency; + ClassBNvm->BeaconCtx.Ctrl.CustomFreq = beaconCtx.Ctrl.CustomFreq; + + // Parameters from PingSlotChannelReq + ClassBNvm->PingSlotCtx.Ctrl.CustomFreq = pingSlotCtx.Ctrl.CustomFreq; + ClassBNvm->PingSlotCtx.Frequency = pingSlotCtx.Frequency; + ClassBNvm->PingSlotCtx.Datarate = pingSlotCtx.Datarate; +} + +static void EnlargeWindowTimeout( void ) +{ + // Update beacon movement + Ctx.BeaconCtx.BeaconWindowMovement *= CLASSB_WINDOW_MOVE_EXPANSION_FACTOR; + if( Ctx.BeaconCtx.BeaconWindowMovement > CLASSB_WINDOW_MOVE_EXPANSION_MAX ) + { + Ctx.BeaconCtx.BeaconWindowMovement = CLASSB_WINDOW_MOVE_EXPANSION_MAX; + } + // Update symbol timeout + Ctx.BeaconCtx.SymbolTimeout *= CLASSB_BEACON_SYMBOL_TO_EXPANSION_FACTOR; + if( Ctx.BeaconCtx.SymbolTimeout > CLASSB_BEACON_SYMBOL_TO_EXPANSION_MAX ) + { + Ctx.BeaconCtx.SymbolTimeout = CLASSB_BEACON_SYMBOL_TO_EXPANSION_MAX; + } + Ctx.PingSlotCtx.SymbolTimeout *= CLASSB_BEACON_SYMBOL_TO_EXPANSION_FACTOR; + if( Ctx.PingSlotCtx.SymbolTimeout > CLASSB_PING_SLOT_SYMBOL_TO_EXPANSION_MAX ) + { + Ctx.PingSlotCtx.SymbolTimeout = CLASSB_PING_SLOT_SYMBOL_TO_EXPANSION_MAX; + } +} + +static void ResetWindowTimeout( void ) +{ + Ctx.BeaconCtx.SymbolTimeout = CLASSB_BEACON_SYMBOL_TO_DEFAULT; + Ctx.PingSlotCtx.SymbolTimeout = CLASSB_BEACON_SYMBOL_TO_DEFAULT; + Ctx.BeaconCtx.BeaconWindowMovement = CLASSB_WINDOW_MOVE_DEFAULT; +} + +static TimerTime_t CalcDelayForNextBeacon( TimerTime_t currentTime, TimerTime_t lastBeaconRx ) +{ + TimerTime_t nextBeaconRxTime = 0; + + // Calculate the point in time of the next beacon + nextBeaconRxTime = ( ( currentTime - lastBeaconRx ) % CLASSB_BEACON_INTERVAL ); + return ( CLASSB_BEACON_INTERVAL - nextBeaconRxTime ); +} + +static void IndicateBeaconStatus( LoRaMacEventInfoStatus_t status ) +{ + if( Ctx.BeaconCtx.Ctrl.ResumeBeaconing == 0 ) + { + Ctx.LoRaMacClassBParams.MlmeIndication->MlmeIndication = MLME_BEACON; + Ctx.LoRaMacClassBParams.MlmeIndication->Status = status; + Ctx.LoRaMacClassBParams.LoRaMacFlags->Bits.MlmeInd = 1; + + Ctx.LoRaMacClassBParams.LoRaMacFlags->Bits.MacDone = 1; + } + Ctx.BeaconCtx.Ctrl.ResumeBeaconing = 0; +} + +static TimerTime_t ApplyGuardTime( TimerTime_t beaconEventTime ) +{ + TimerTime_t timeGuard = beaconEventTime; + + if( timeGuard > CLASSB_BEACON_GUARD ) + { + timeGuard -= CLASSB_BEACON_GUARD; + } + return timeGuard; +} + +static TimerTime_t UpdateBeaconState( LoRaMacEventInfoStatus_t status, + TimerTime_t windowMovement, TimerTime_t currentTime ) + +{ + TimerTime_t beaconEventTime = 0; + + // Calculate the next beacon RX time + beaconEventTime = CalcDelayForNextBeacon( currentTime, SysTimeToMs( Ctx.BeaconCtx.LastBeaconRx ) ); + Ctx.BeaconCtx.NextBeaconRx = SysTimeFromMs( currentTime + beaconEventTime ); + + // Take temperature compensation into account + beaconEventTime = TimerTempCompensation( beaconEventTime, Ctx.BeaconCtx.Temperature ); + + // Move the window + if( beaconEventTime > windowMovement ) + { + beaconEventTime -= windowMovement; + } + Ctx.BeaconCtx.NextBeaconRxAdjusted = currentTime + beaconEventTime; + + // Start the RX slot state machine for ping and multicast slots + LoRaMacClassBStartRxSlots( ); + + // Setup an MLME_BEACON indication to inform the upper layer + IndicateBeaconStatus( status ); + + // Apply guard time + return ApplyGuardTime( beaconEventTime ); +} + +static uint8_t CalcPingNb( uint16_t periodicity ) +{ + return 128 / ( 1 << periodicity ); +} + +static uint16_t CalcPingPeriod( uint8_t pingNb ) +{ + return CLASSB_BEACON_WINDOW_SLOTS / pingNb; +} + +static bool CheckSlotPriority( uint32_t currentAddress, uint8_t currentFPendingSet, uint8_t currentIsMulticast, + uint32_t address, uint8_t fPendingSet, uint8_t isMulticast ) +{ + if( currentFPendingSet != fPendingSet ) + { + if( currentFPendingSet < fPendingSet ) + { + // New slot sequence has priority. It does not matter + // which type it is + return true; + } + return false; + } + else + { + // FPendingSet has the same priority level, decide + // based on multicast or unicast setting + if( currentIsMulticast != isMulticast ) + { + if( currentIsMulticast < isMulticast ) + { + // New slot sequence has priority. Multicasts have + // more priority than unicasts + return true; + } + return false; + } + else + { + // IsMulticast has the same priority level, decide + // based on the highest address + if( currentAddress < address ) + { + // New slot sequence has priority. The sequence with + // the highest address has priority + return true; + } + } + } + return false; +} + +#endif // LORAMAC_CLASSB_ENABLED + +void LoRaMacClassBInit( LoRaMacClassBParams_t *classBParams, LoRaMacClassBCallback_t *callbacks, LoRaMacClassBNvmData_t* nvm ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + // Assign non-volatile context + if( nvm == NULL ) + { + return; + } + ClassBNvm = nvm; + + // Store callbacks + Ctx.LoRaMacClassBCallbacks = *callbacks; + + // Store parameter pointers + Ctx.LoRaMacClassBParams = *classBParams; + + // Initialize timers + TimerInit( &Ctx.BeaconTimer, LoRaMacClassBBeaconTimerEvent ); + TimerInit( &Ctx.PingSlotTimer, LoRaMacClassBPingSlotTimerEvent ); + TimerInit( &Ctx.MulticastSlotTimer, LoRaMacClassBMulticastSlotTimerEvent ); + + InitClassB( ); +#endif // LORAMAC_CLASSB_ENABLED +} + +void LoRaMacClassBSetBeaconState( BeaconState_t beaconState ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + if( beaconState == BEACON_STATE_ACQUISITION ) + { + // If the MAC has received a time reference for the beacon, + // apply the state BEACON_STATE_ACQUISITION_BY_TIME. + if( ( Ctx.BeaconCtx.Ctrl.BeaconDelaySet == 1 ) && + ( LoRaMacClassBIsAcquisitionPending( ) == false ) ) + { + Ctx.BeaconState = BEACON_STATE_ACQUISITION_BY_TIME; + } + else + { + Ctx.BeaconState = beaconState; + } + } + else + { + if( ( Ctx.BeaconState != BEACON_STATE_ACQUISITION ) && + ( Ctx.BeaconState != BEACON_STATE_ACQUISITION_BY_TIME ) ) + { + Ctx.BeaconState = beaconState; + } + } +#endif // LORAMAC_CLASSB_ENABLED +} + +void LoRaMacClassBSetPingSlotState( PingSlotState_t pingSlotState ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + CRITICAL_SECTION_BEGIN( ); + Ctx.PingSlotState = pingSlotState; + CRITICAL_SECTION_END( ); +#endif // LORAMAC_CLASSB_ENABLED +} + +void LoRaMacClassBSetMulticastSlotState( PingSlotState_t multicastSlotState ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + CRITICAL_SECTION_BEGIN( ); + Ctx.MulticastSlotState = multicastSlotState; + CRITICAL_SECTION_END( ); +#endif // LORAMAC_CLASSB_ENABLED +} + +bool LoRaMacClassBIsAcquisitionInProgress( void ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + if( Ctx.BeaconState == BEACON_STATE_ACQUISITION_BY_TIME ) + { + // In this case the acquisition is in progress, as the MAC has + // a time reference for the next beacon RX. + return true; + } + if( LoRaMacClassBIsAcquisitionPending( ) == true ) + { + // In this case the acquisition is in progress, as the MAC + // searches for a beacon. + return true; + } + return false; +#else + return false; +#endif // LORAMAC_CLASSB_ENABLED +} + +void LoRaMacClassBBeaconTimerEvent( void* context ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + Ctx.BeaconCtx.TimeStamp = TimerGetCurrentTime( ); + TimerStop( &Ctx.BeaconTimer ); + LoRaMacClassBEvents.Events.Beacon = 1; + + OnClassBMacProcessNotify( ); +#endif // LORAMAC_CLASSB_ENABLED +} + +#ifdef LORAMAC_CLASSB_ENABLED +static void LoRaMacClassBProcessBeacon( void ) +{ + bool activateTimer = false; + TimerTime_t beaconEventTime = 1; + RxConfigParams_t beaconRxConfig; + TimerTime_t beaconTimestamp = Ctx.BeaconCtx.TimeStamp; + + // Beacon state machine + switch( Ctx.BeaconState ) + { + case BEACON_STATE_ACQUISITION_BY_TIME: + { + activateTimer = true; + + if( Ctx.BeaconCtx.Ctrl.AcquisitionPending == 1 ) + { + Radio.Sleep(); + Ctx.BeaconState = BEACON_STATE_LOST; + } + else + { + // Default symbol timeouts + ResetWindowTimeout( ); + + if( Ctx.BeaconCtx.Ctrl.BeaconDelaySet == 1 ) + { + // The goal is to calculate beaconRxConfig.WindowTimeout + CalculateBeaconRxWindowConfig( &beaconRxConfig, Ctx.BeaconCtx.SymbolTimeout ); + + if( Ctx.BeaconCtx.BeaconTimingDelay > 0 ) + { + uint32_t now = TimerGetCurrentTime( ); + if( SysTimeToMs( Ctx.BeaconCtx.NextBeaconRx ) > now ) + { + // Calculate the time when we expect the next beacon + beaconEventTime = TimerTempCompensation( SysTimeToMs( Ctx.BeaconCtx.NextBeaconRx ) - now, Ctx.BeaconCtx.Temperature ); + + if( ( int32_t ) beaconEventTime > beaconRxConfig.WindowOffset ) + { + // Apply the offset of the system error respectively beaconing precision setting + beaconEventTime += beaconRxConfig.WindowOffset; + } + } + else + { + // Reset status provides by BeaconTimingAns + Ctx.BeaconCtx.Ctrl.BeaconDelaySet = 0; + Ctx.BeaconCtx.Ctrl.BeaconChannelSet = 0; + Ctx.BeaconState = BEACON_STATE_ACQUISITION; + } + Ctx.BeaconCtx.BeaconTimingDelay = 0; + } + else + { + activateTimer = false; + + // Reset status provides by BeaconTimingAns + Ctx.BeaconCtx.Ctrl.BeaconDelaySet = 0; + // Set the node into acquisition mode + Ctx.BeaconCtx.Ctrl.AcquisitionPending = 1; + + // Don't use the default channel. We know on which + // channel the next beacon will be transmitted + RxBeaconSetup( CLASSB_BEACON_RESERVED, false, beaconRxConfig.WindowTimeout ); + } + } + else + { + Ctx.BeaconCtx.NextBeaconRx.Seconds = 0; + Ctx.BeaconCtx.NextBeaconRx.SubSeconds = 0; + Ctx.BeaconCtx.BeaconTimingDelay = 0; + + Ctx.BeaconState = BEACON_STATE_ACQUISITION; + } + } + break; + } + case BEACON_STATE_ACQUISITION: + { + activateTimer = true; + + if( Ctx.BeaconCtx.Ctrl.AcquisitionPending == 1 ) + { + Radio.Sleep(); + Ctx.BeaconState = BEACON_STATE_LOST; + } + else + { + // Default symbol timeouts + ResetWindowTimeout( ); + + Ctx.BeaconCtx.Ctrl.AcquisitionPending = 1; + beaconEventTime = CLASSB_BEACON_INTERVAL; + + // The goal is to calculate beaconRxConfig.WindowTimeout + CalculateBeaconRxWindowConfig( &beaconRxConfig, Ctx.BeaconCtx.SymbolTimeout ); + + // Start the beacon acquisition. When the MAC has received a beacon in function + // RxBeacon successfully, the next state is BEACON_STATE_LOCKED. If the MAC does not + // find a beacon, the state machine will stay in state BEACON_STATE_ACQUISITION. + // This state detects that a acquisition was pending previously and will change the next + // state to BEACON_STATE_LOST. + RxBeaconSetup( 0, true, beaconRxConfig.WindowTimeout ); + } + break; + } + case BEACON_STATE_TIMEOUT: + { + // We have to update the beacon time, since we missed a beacon + Ctx.BeaconCtx.BeaconTime.Seconds += ( CLASSB_BEACON_INTERVAL / 1000 ); + Ctx.BeaconCtx.BeaconTime.SubSeconds = 0; + + // Enlarge window timeouts to increase the chance to receive the next beacon + EnlargeWindowTimeout( ); + + // Setup next state + Ctx.BeaconState = BEACON_STATE_REACQUISITION; + } + // Intentional fall through + case BEACON_STATE_REACQUISITION: + { + activateTimer = true; + + // The beacon is no longer acquired + Ctx.BeaconCtx.Ctrl.BeaconAcquired = 0; + + // Verify if the maximum beacon less period has been elapsed + if( ( beaconTimestamp - SysTimeToMs( Ctx.BeaconCtx.LastBeaconRx ) ) > CLASSB_MAX_BEACON_LESS_PERIOD ) + { + Ctx.BeaconState = BEACON_STATE_LOST; + } + else + { + // Handle beacon miss + beaconEventTime = UpdateBeaconState( LORAMAC_EVENT_INFO_STATUS_BEACON_LOST, + Ctx.BeaconCtx.BeaconWindowMovement, beaconTimestamp ); + + // Setup next state + Ctx.BeaconState = BEACON_STATE_IDLE; + } + break; + } + case BEACON_STATE_LOCKED: + { + activateTimer = true; + + // We have received a beacon. Acquisition is no longer pending. + Ctx.BeaconCtx.Ctrl.AcquisitionPending = 0; + + // Handle beacon reception + beaconEventTime = UpdateBeaconState( LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED, + 0, beaconTimestamp ); + + // Setup the MLME confirm for the MLME_BEACON_ACQUISITION + if( Ctx.LoRaMacClassBParams.LoRaMacFlags->Bits.MlmeReq == 1 ) + { + if( LoRaMacConfirmQueueIsCmdActive( MLME_BEACON_ACQUISITION ) == true ) + { + LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_BEACON_ACQUISITION ); + Ctx.LoRaMacClassBParams.MlmeConfirm->TxTimeOnAir = 0; + } + } + + // Setup next state + Ctx.BeaconState = BEACON_STATE_IDLE; + break; + } + case BEACON_STATE_IDLE: + { + activateTimer = true; + GetTemperatureLevel( &Ctx.LoRaMacClassBCallbacks, &Ctx.BeaconCtx ); + beaconEventTime = Ctx.BeaconCtx.NextBeaconRxAdjusted - Radio.GetWakeupTime( ); + uint32_t now = TimerGetCurrentTime( ); + + // The goal is to calculate beaconRxConfig.WindowTimeout and beaconRxConfig.WindowOffset + CalculateBeaconRxWindowConfig( &beaconRxConfig, Ctx.BeaconCtx.SymbolTimeout ); + + if( beaconEventTime > now ) + { + Ctx.BeaconState = BEACON_STATE_GUARD; + beaconEventTime -= now; + beaconEventTime = TimerTempCompensation( beaconEventTime, Ctx.BeaconCtx.Temperature ); + + if( ( int32_t ) beaconEventTime > beaconRxConfig.WindowOffset ) + { + // Apply the offset of the system error respectively beaconing precision setting + beaconEventTime += beaconRxConfig.WindowOffset; + } + } + else + { + Ctx.BeaconState = BEACON_STATE_REACQUISITION; + beaconEventTime = 1; + } + break; + } + case BEACON_STATE_GUARD: + { + Ctx.BeaconState = BEACON_STATE_RX; + + // Stop slot timers + LoRaMacClassBStopRxSlots( ); + + // Don't use the default channel. We know on which + // channel the next beacon will be transmitted + RxBeaconSetup( CLASSB_BEACON_RESERVED, false, beaconRxConfig.WindowTimeout ); + break; + } + case BEACON_STATE_LOST: + { + // Handle events + if( Ctx.LoRaMacClassBParams.LoRaMacFlags->Bits.MlmeReq == 1 ) + { + if( LoRaMacConfirmQueueIsCmdActive( MLME_BEACON_ACQUISITION ) == true ) + { + LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND, MLME_BEACON_ACQUISITION ); + } + } + else + { + Ctx.LoRaMacClassBParams.MlmeIndication->MlmeIndication = MLME_BEACON_LOST; + Ctx.LoRaMacClassBParams.MlmeIndication->Status = LORAMAC_EVENT_INFO_STATUS_OK; + Ctx.LoRaMacClassBParams.LoRaMacFlags->Bits.MlmeInd = 1; + } + + // Stop slot timers + LoRaMacClassBStopRxSlots( ); + + // Initialize default state for class b + InitClassBDefaults( ); + + Ctx.LoRaMacClassBParams.LoRaMacFlags->Bits.MacDone = 1; + + break; + } + default: + { + Ctx.BeaconState = BEACON_STATE_ACQUISITION; + break; + } + } + + if( activateTimer == true ) + { + TimerSetValue( &Ctx.BeaconTimer, beaconEventTime ); + TimerStart( &Ctx.BeaconTimer ); + } +} +#endif // LORAMAC_CLASSB_ENABLED + +void LoRaMacClassBPingSlotTimerEvent( void* context ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + LoRaMacClassBEvents.Events.PingSlot = 1; + + OnClassBMacProcessNotify( ); +#endif // LORAMAC_CLASSB_ENABLED +} + +#ifdef LORAMAC_CLASSB_ENABLED +static void LoRaMacClassBProcessPingSlot( void ) +{ + static RxConfigParams_t pingSlotRxConfig; + TimerTime_t pingSlotTime = 0; + uint32_t maxRxError = 0; + bool slotHasPriority = false; + + switch( Ctx.PingSlotState ) + { + case PINGSLOT_STATE_CALC_PING_OFFSET: + { + ComputePingOffset( Ctx.BeaconCtx.BeaconTime.Seconds, + *Ctx.LoRaMacClassBParams.LoRaMacDevAddr, + ClassBNvm->PingSlotCtx.PingPeriod, + &( Ctx.PingSlotCtx.PingOffset ) ); + LoRaMacClassBSetPingSlotState( PINGSLOT_STATE_SET_TIMER ); + } + // Intentional fall through + case PINGSLOT_STATE_SET_TIMER: + { + if( CalcNextSlotTime( Ctx.PingSlotCtx.PingOffset, ClassBNvm->PingSlotCtx.PingPeriod, ClassBNvm->PingSlotCtx.PingNb, &pingSlotTime ) == true ) + { + if( Ctx.BeaconCtx.Ctrl.BeaconAcquired == 1 ) + { + // Compare and assign the maximum between the region specific rx error window time + // and time precision received from beacon frame format. + maxRxError = MAX( Ctx.LoRaMacClassBParams.LoRaMacParams->SystemMaxRxError , + ( uint32_t ) Ctx.BeaconCtx.BeaconTimePrecision.SubSeconds ); + + // Compute the symbol timeout. Apply it only, if the beacon is acquired + // Otherwise, take the enlargement of the symbols into account. + RegionComputeRxWindowParameters( *Ctx.LoRaMacClassBParams.LoRaMacRegion, + ClassBNvm->PingSlotCtx.Datarate, + Ctx.LoRaMacClassBParams.LoRaMacParams->MinRxSymbols, + maxRxError, + &pingSlotRxConfig ); + Ctx.PingSlotCtx.SymbolTimeout = pingSlotRxConfig.WindowTimeout; + + if( ( int32_t )pingSlotTime > pingSlotRxConfig.WindowOffset ) + {// Apply the window offset + pingSlotTime += pingSlotRxConfig.WindowOffset; + } + } + if( pingSlotTime < CLASSB_BEACON_INTERVAL ) + { + // Start the timer if the ping slot time is in range + LoRaMacClassBSetPingSlotState( PINGSLOT_STATE_IDLE ); + TimerSetValue( &Ctx.PingSlotTimer, pingSlotTime ); + TimerStart( &Ctx.PingSlotTimer ); + } + else + { + LoRaMacClassBSetMulticastSlotState( PINGSLOT_STATE_CALC_PING_OFFSET ); + TimerSetValue( &Ctx.PingSlotTimer, 1 ); + TimerStart( &Ctx.PingSlotTimer ); + } + } + break; + } + case PINGSLOT_STATE_IDLE: + { + uint32_t frequency = ClassBNvm->PingSlotCtx.Frequency; + + // Apply a custom frequency if the following bit is set + if( ClassBNvm->PingSlotCtx.Ctrl.CustomFreq == 0 ) + { + // Restore floor plan + frequency = CalcDownlinkChannelAndFrequency( *Ctx.LoRaMacClassBParams.LoRaMacDevAddr, Ctx.BeaconCtx.BeaconTime.Seconds, + CLASSB_BEACON_INTERVAL, false ); + } + + if( Ctx.PingSlotCtx.NextMulticastChannel != NULL ) + { + // Verify, if the unicast has priority. + slotHasPriority = CheckSlotPriority( *Ctx.LoRaMacClassBParams.LoRaMacDevAddr, ClassBNvm->PingSlotCtx.FPendingSet, 0, + Ctx.PingSlotCtx.NextMulticastChannel->ChannelParams.Address, Ctx.PingSlotCtx.NextMulticastChannel->FPendingSet, 1 ); + } + + // Open the ping slot window only, if there is no multicast ping slot + // open or if the unicast has priority. + if( ( Ctx.MulticastSlotState != PINGSLOT_STATE_RX ) || ( slotHasPriority == true ) ) + { + if( Ctx.MulticastSlotState == PINGSLOT_STATE_RX ) + { + // Close multicast slot window, if necessary. Multicast slots have priority + Radio.Standby( ); + LoRaMacClassBSetMulticastSlotState( PINGSLOT_STATE_CALC_PING_OFFSET ); + TimerSetValue( &Ctx.MulticastSlotTimer, CLASSB_PING_SLOT_WINDOW ); + TimerStart( &Ctx.MulticastSlotTimer ); + } + + LoRaMacClassBSetPingSlotState( PINGSLOT_STATE_RX ); + + pingSlotRxConfig.Datarate = ClassBNvm->PingSlotCtx.Datarate; + pingSlotRxConfig.DownlinkDwellTime = Ctx.LoRaMacClassBParams.LoRaMacParams->DownlinkDwellTime; + pingSlotRxConfig.Frequency = frequency; + pingSlotRxConfig.RxContinuous = false; + pingSlotRxConfig.RxSlot = RX_SLOT_WIN_CLASS_B_PING_SLOT; + pingSlotRxConfig.NetworkActivation = *Ctx.LoRaMacClassBParams.NetworkActivation; + + RegionRxConfig( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &pingSlotRxConfig, ( int8_t* )&Ctx.LoRaMacClassBParams.McpsIndication->RxDatarate ); + + if( pingSlotRxConfig.RxContinuous == false ) + { + Radio.Rx( Ctx.LoRaMacClassBParams.LoRaMacParams->MaxRxWindow ); + } + else + { + Radio.Rx( 0 ); // Continuous mode + } + } + else + { + // Multicast slots have priority. Skip Rx + LoRaMacClassBSetPingSlotState( PINGSLOT_STATE_CALC_PING_OFFSET ); + TimerSetValue( &Ctx.PingSlotTimer, CLASSB_PING_SLOT_WINDOW ); + TimerStart( &Ctx.PingSlotTimer ); + } + break; + } + default: + { + LoRaMacClassBSetPingSlotState( PINGSLOT_STATE_CALC_PING_OFFSET ); + break; + } + } +} +#endif // LORAMAC_CLASSB_ENABLED + +void LoRaMacClassBMulticastSlotTimerEvent( void* context ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + LoRaMacClassBEvents.Events.MulticastSlot = 1; + + OnClassBMacProcessNotify( ); +#endif // LORAMAC_CLASSB_ENABLED +} + +#ifdef LORAMAC_CLASSB_ENABLED +static void LoRaMacClassBProcessMulticastSlot( void ) +{ + static RxConfigParams_t multicastSlotRxConfig; + TimerTime_t multicastSlotTime = 0; + TimerTime_t slotTime = 0; + uint32_t maxRxError = 0; + MulticastCtx_t *cur = Ctx.LoRaMacClassBParams.MulticastChannels; + bool slotHasPriority = false; + + if( cur == NULL ) + { + return; + } + + if( Ctx.MulticastSlotState == PINGSLOT_STATE_RX ) + { + // A multicast slot is already open + return; + } + + switch( Ctx.MulticastSlotState ) + { + case PINGSLOT_STATE_CALC_PING_OFFSET: + { + // Compute all offsets for every multicast slots + for( uint8_t i = 0; i < LORAMAC_MAX_MC_CTX; i++ ) + { + if( cur->ChannelParams.IsEnabled ) + { + ComputePingOffset( Ctx.BeaconCtx.BeaconTime.Seconds, + cur->ChannelParams.Address, + cur->PingPeriod, + &( cur->PingOffset ) ); + } + cur++; + } + LoRaMacClassBSetMulticastSlotState( PINGSLOT_STATE_SET_TIMER ); + } + // Intentional fall through + case PINGSLOT_STATE_SET_TIMER: + { + cur = Ctx.LoRaMacClassBParams.MulticastChannels; + Ctx.PingSlotCtx.NextMulticastChannel = NULL; + + for( uint8_t i = 0; i < LORAMAC_MAX_MC_CTX; i++ ) + { + if( cur->ChannelParams.IsEnabled ) + { + // Calculate the next slot time for every multicast slot + if( CalcNextSlotTime( cur->PingOffset, cur->PingPeriod, cur->PingNb, &slotTime ) == true ) + { + if( ( multicastSlotTime == 0 ) || ( multicastSlotTime > slotTime ) ) + { + // Update the slot time and the next multicast channel + multicastSlotTime = slotTime; + Ctx.PingSlotCtx.NextMulticastChannel = cur; + } + } + } + cur++; + } + + // Schedule the next multicast slot + if( Ctx.PingSlotCtx.NextMulticastChannel != NULL ) + { + if( Ctx.BeaconCtx.Ctrl.BeaconAcquired == 1 ) + { + + // Compare and assign the maximum between the region specific rx error window time + // and time precision received from beacon frame format. + maxRxError = MAX( Ctx.LoRaMacClassBParams.LoRaMacParams->SystemMaxRxError , + ( uint32_t ) Ctx.BeaconCtx.BeaconTimePrecision.SubSeconds ); + + RegionComputeRxWindowParameters( *Ctx.LoRaMacClassBParams.LoRaMacRegion, + Ctx.PingSlotCtx.NextMulticastChannel->ChannelParams.RxParams.Params.ClassB.Datarate, + Ctx.LoRaMacClassBParams.LoRaMacParams->MinRxSymbols, + maxRxError, + &multicastSlotRxConfig ); + Ctx.PingSlotCtx.SymbolTimeout = multicastSlotRxConfig.WindowTimeout; + } + + if( ( int32_t )multicastSlotTime > multicastSlotRxConfig.WindowOffset ) + {// Apply the window offset + multicastSlotTime += multicastSlotRxConfig.WindowOffset; + } + if( multicastSlotTime < CLASSB_BEACON_INTERVAL ) + { + // Start the timer if the ping slot time is in range + LoRaMacClassBSetMulticastSlotState( PINGSLOT_STATE_IDLE ); + TimerSetValue( &Ctx.MulticastSlotTimer, multicastSlotTime ); + TimerStart( &Ctx.MulticastSlotTimer ); + } + else + { + LoRaMacClassBSetMulticastSlotState( PINGSLOT_STATE_CALC_PING_OFFSET ); + TimerSetValue( &Ctx.MulticastSlotTimer, 1 ); + TimerStart( &Ctx.MulticastSlotTimer ); + } + } + break; + } + case PINGSLOT_STATE_IDLE: + { + uint32_t frequency = 0; + + // Verify if the multicast channel is valid + if( Ctx.PingSlotCtx.NextMulticastChannel == NULL ) + { + LoRaMacClassBSetMulticastSlotState( PINGSLOT_STATE_CALC_PING_OFFSET ); + TimerSetValue( &Ctx.MulticastSlotTimer, 1 ); + TimerStart( &Ctx.MulticastSlotTimer ); + break; + } + + // Apply frequency + frequency = Ctx.PingSlotCtx.NextMulticastChannel->ChannelParams.RxParams.Params.ClassB.Frequency; + + // Restore the floor plan frequency if there is no individual frequency assigned + if( frequency == 0 ) + { + // Restore floor plan + frequency = CalcDownlinkChannelAndFrequency( Ctx.PingSlotCtx.NextMulticastChannel->ChannelParams.Address, + Ctx.BeaconCtx.BeaconTime.Seconds, CLASSB_BEACON_INTERVAL, false ); + } + + // Verify, if the unicast has priority. + slotHasPriority = CheckSlotPriority( Ctx.PingSlotCtx.NextMulticastChannel->ChannelParams.Address, Ctx.PingSlotCtx.NextMulticastChannel->FPendingSet, 1, + *Ctx.LoRaMacClassBParams.LoRaMacDevAddr, ClassBNvm->PingSlotCtx.FPendingSet, 0 ); + + // Open the ping slot window only, if there is no multicast ping slot + // open or if the unicast has priority. + if( ( Ctx.PingSlotState != PINGSLOT_STATE_RX ) || ( slotHasPriority == true ) ) + { + if( Ctx.PingSlotState == PINGSLOT_STATE_RX ) + { + // Close ping slot window, if necessary. Multicast slots have priority + Radio.Standby( ); + LoRaMacClassBSetPingSlotState( PINGSLOT_STATE_CALC_PING_OFFSET ); + TimerSetValue( &Ctx.PingSlotTimer, CLASSB_PING_SLOT_WINDOW ); + TimerStart( &Ctx.PingSlotTimer ); + } + + LoRaMacClassBSetMulticastSlotState( PINGSLOT_STATE_RX ); + + multicastSlotRxConfig.Datarate = Ctx.PingSlotCtx.NextMulticastChannel->ChannelParams.RxParams.Params.ClassB.Datarate; + multicastSlotRxConfig.DownlinkDwellTime = Ctx.LoRaMacClassBParams.LoRaMacParams->DownlinkDwellTime; + multicastSlotRxConfig.Frequency = frequency; + multicastSlotRxConfig.RxContinuous = false; + multicastSlotRxConfig.RxSlot = RX_SLOT_WIN_CLASS_B_MULTICAST_SLOT; + multicastSlotRxConfig.NetworkActivation = *Ctx.LoRaMacClassBParams.NetworkActivation; + + RegionRxConfig( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &multicastSlotRxConfig, ( int8_t* )&Ctx.LoRaMacClassBParams.McpsIndication->RxDatarate ); + + if( multicastSlotRxConfig.RxContinuous == false ) + { + Radio.Rx( Ctx.LoRaMacClassBParams.LoRaMacParams->MaxRxWindow ); + } + else + { + Radio.Rx( 0 ); // Continuous mode + } + } + else + { + // Unicast slots have priority. Skip Rx + LoRaMacClassBSetMulticastSlotState( PINGSLOT_STATE_CALC_PING_OFFSET ); + TimerSetValue( &Ctx.MulticastSlotTimer, CLASSB_PING_SLOT_WINDOW ); + TimerStart( &Ctx.MulticastSlotTimer ); + } + break; + } + default: + { + LoRaMacClassBSetMulticastSlotState( PINGSLOT_STATE_CALC_PING_OFFSET ); + break; + } + } +} +#endif // LORAMAC_CLASSB_ENABLED + +bool LoRaMacClassBRxBeacon( uint8_t *payload, uint16_t size ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + GetPhyParams_t getPhy; + PhyParam_t phyParam; + bool beaconProcessed = false; + uint16_t crc0 = 0; + uint16_t crc1 = 0; + uint16_t beaconCrc0 = 0; + uint16_t beaconCrc1 = 0; + + getPhy.Attribute = PHY_BEACON_FORMAT; + phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy ); + + // Verify if we are in the state where we expect a beacon + if( ( Ctx.BeaconState == BEACON_STATE_RX ) || ( Ctx.BeaconCtx.Ctrl.AcquisitionPending == 1 ) ) + { + if( size == phyParam.BeaconFormat.BeaconSize ) + { + // A beacon frame is defined as: + // Bytes: | x | 1 | 4 | 2 | 7 | y | 2 | + // |------|-------|------|------|------------|------|------| + // Field: | RFU1 | Param | Time | CRC1 | GwSpecific | RFU2 | CRC2 | + // + // Field RFU1 and RFU2 have variable sizes. It depends on the region specific implementation + + // Read CRC1 field from the frame + beaconCrc0 = ( ( uint16_t )payload[phyParam.BeaconFormat.Rfu1Size + 1 + 4] ) & 0x00FF; + beaconCrc0 |= ( ( uint16_t )payload[phyParam.BeaconFormat.Rfu1Size + 1 + 4 + 1] << 8 ) & 0xFF00; + crc0 = BeaconCrc( payload, phyParam.BeaconFormat.Rfu1Size + 1 + 4 ); + + // Validate the first crc of the beacon frame + if( crc0 == beaconCrc0 ) + { + // Copy the param field for app layer + Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.Param = ( payload[phyParam.BeaconFormat.Rfu1Size] ); + // Fetch the precise time value in milliseconds that will be used for Rx ping slot delay. + Ctx.BeaconCtx.BeaconTimePrecision.SubSeconds = BeaconPrecTimeValue[Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.Param]; + + // Read Time field from the frame + Ctx.BeaconCtx.BeaconTime.Seconds = ( ( uint32_t )payload[phyParam.BeaconFormat.Rfu1Size + 1] ) & 0x000000FF; + Ctx.BeaconCtx.BeaconTime.Seconds |= ( ( uint32_t )( payload[phyParam.BeaconFormat.Rfu1Size + 2] << 8 ) ) & 0x0000FF00; + Ctx.BeaconCtx.BeaconTime.Seconds |= ( ( uint32_t )( payload[phyParam.BeaconFormat.Rfu1Size + 3] << 16 ) ) & 0x00FF0000; + Ctx.BeaconCtx.BeaconTime.Seconds |= ( ( uint32_t )( payload[phyParam.BeaconFormat.Rfu1Size + 4] << 24 ) ) & 0xFF000000; + Ctx.BeaconCtx.BeaconTime.SubSeconds = 0; + Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.Time = Ctx.BeaconCtx.BeaconTime; + beaconProcessed = true; + } + + // Read CRC2 field from the frame + beaconCrc1 = ( ( uint16_t )payload[phyParam.BeaconFormat.Rfu1Size + 1 + 4 + 2 + 7 + phyParam.BeaconFormat.Rfu2Size] ) & 0x00FF; + beaconCrc1 |= ( ( uint16_t )payload[phyParam.BeaconFormat.Rfu1Size + 1 + 4 + 2 + 7 + phyParam.BeaconFormat.Rfu2Size + 1] << 8 ) & 0xFF00; + crc1 = BeaconCrc( &payload[phyParam.BeaconFormat.Rfu1Size + 1 + 4 + 2], 7 + phyParam.BeaconFormat.Rfu2Size ); + + // Validate the second crc of the beacon frame + if( crc1 == beaconCrc1 ) + { + // Read GwSpecific field from the frame + // The GwSpecific field contains 1 byte InfoDesc and 6 bytes Info + Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.GwSpecific.InfoDesc = payload[phyParam.BeaconFormat.Rfu1Size + 1 + 4 + 2]; + memcpy1( Ctx.LoRaMacClassBParams.MlmeIndication->BeaconInfo.GwSpecific.Info, &payload[phyParam.BeaconFormat.Rfu1Size + 1 + 4 + 2 + 1], 6 ); + } + + // Reset beacon variables, if one of the crc is valid + if( beaconProcessed == true ) + { + uint32_t spreadingFactor = 0; + uint32_t bandwith = 0; + + getPhy.Attribute = PHY_BEACON_CHANNEL_DR; + phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy ); + + getPhy.Attribute = PHY_SF_FROM_DR; + getPhy.Datarate = phyParam.Value; + phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy ); + spreadingFactor = phyParam.Value; + + getPhy.Attribute = PHY_BW_FROM_DR; + phyParam = RegionGetPhyParam( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &getPhy ); + bandwith = phyParam.Value; + + TimerTime_t time = Radio.TimeOnAir( MODEM_LORA, bandwith, spreadingFactor, 1, 10, true, size, false ); + SysTime_t timeOnAir; + timeOnAir.Seconds = time / 1000; + timeOnAir.SubSeconds = time - timeOnAir.Seconds * 1000; + + Ctx.BeaconCtx.LastBeaconRx = Ctx.BeaconCtx.BeaconTime; + Ctx.BeaconCtx.LastBeaconRx.Seconds += UNIX_GPS_EPOCH_OFFSET; + + // Update system time. + SysTimeSet( SysTimeAdd( Ctx.BeaconCtx.LastBeaconRx, timeOnAir ) ); + + Ctx.BeaconCtx.Ctrl.BeaconAcquired = 1; + Ctx.BeaconCtx.Ctrl.BeaconMode = 1; + ResetWindowTimeout( ); + Ctx.BeaconState = BEACON_STATE_LOCKED; + + LoRaMacClassBBeaconTimerEvent( NULL ); + } + } + + if( Ctx.BeaconState == BEACON_STATE_RX ) + { + Ctx.BeaconState = BEACON_STATE_TIMEOUT; + LoRaMacClassBBeaconTimerEvent( NULL ); + } + // When the MAC listens for a beacon, it is not allowed to process any other + // downlink except the beacon frame itself. The reason for this is that no valid downlink window is open. + // If it receives a frame which is + // 1. not a beacon or + // 2. a beacon with a crc fail + // the MAC shall ignore the frame completely. Thus, the function must always return true, even if no + // valid beacon has been received. + beaconProcessed = true; + } + return beaconProcessed; +#else + return false; +#endif // LORAMAC_CLASSB_ENABLED +} + +bool LoRaMacClassBIsBeaconExpected( void ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + if( ( Ctx.BeaconCtx.Ctrl.AcquisitionPending == 1 ) || + ( Ctx.BeaconState == BEACON_STATE_RX ) ) + { + return true; + } + return false; +#else + return false; +#endif // LORAMAC_CLASSB_ENABLED +} + +bool LoRaMacClassBIsPingExpected( void ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + if( Ctx.PingSlotState == PINGSLOT_STATE_RX ) + { + return true; + } + return false; +#else + return false; +#endif // LORAMAC_CLASSB_ENABLED +} + +bool LoRaMacClassBIsMulticastExpected( void ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + if( Ctx.MulticastSlotState == PINGSLOT_STATE_RX ) + { + return true; + } + return false; +#else + return false; +#endif // LORAMAC_CLASSB_ENABLED +} + +bool LoRaMacClassBIsAcquisitionPending( void ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + if( Ctx.BeaconCtx.Ctrl.AcquisitionPending == 1 ) + { + return true; + } + return false; +#else + return false; +#endif // LORAMAC_CLASSB_ENABLED +} + +bool LoRaMacClassBIsBeaconModeActive( void ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + if( ( Ctx.BeaconCtx.Ctrl.BeaconMode == 1 ) || + ( Ctx.BeaconState == BEACON_STATE_ACQUISITION_BY_TIME ) ) + { + return true; + } + return false; +#else + return false; +#endif // LORAMAC_CLASSB_ENABLED +} + +void LoRaMacClassBSetPingSlotInfo( uint8_t periodicity ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + ClassBNvm->PingSlotCtx.PingNb = CalcPingNb( periodicity ); + ClassBNvm->PingSlotCtx.PingPeriod = CalcPingPeriod( ClassBNvm->PingSlotCtx.PingNb ); +#endif // LORAMAC_CLASSB_ENABLED +} + +void LoRaMacClassBHaltBeaconing( void ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + if( Ctx.BeaconCtx.Ctrl.BeaconMode == 1 ) + { + if( ( Ctx.BeaconState == BEACON_STATE_TIMEOUT ) || + ( Ctx.BeaconState == BEACON_STATE_LOST ) ) + { + // Update the state machine before halt + LoRaMacClassBBeaconTimerEvent( NULL ); + } + + CRITICAL_SECTION_BEGIN( ); + LoRaMacClassBEvents.Events.Beacon = 0; + CRITICAL_SECTION_END( ); + + // Halt ping slot state machine + TimerStop( &Ctx.BeaconTimer ); + + // Halt beacon state machine + Ctx.BeaconState = BEACON_STATE_HALT; + + // Halt ping and multicast slot state machines + LoRaMacClassBStopRxSlots( ); + } +#endif // LORAMAC_CLASSB_ENABLED +} + +void LoRaMacClassBResumeBeaconing( void ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + if( Ctx.BeaconState == BEACON_STATE_HALT ) + { + Ctx.BeaconCtx.Ctrl.ResumeBeaconing = 1; + + // Set default state + Ctx.BeaconState = BEACON_STATE_LOCKED; + + if( Ctx.BeaconCtx.Ctrl.BeaconAcquired == 0 ) + { + // Set the default state for beacon less operation + Ctx.BeaconState = BEACON_STATE_REACQUISITION; + } + + LoRaMacClassBBeaconTimerEvent( NULL ); + } +#endif // LORAMAC_CLASSB_ENABLED +} + +LoRaMacStatus_t LoRaMacClassBSwitchClass( DeviceClass_t nextClass ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + if( nextClass == CLASS_B ) + {// Switch to from class a to class b + if( ( Ctx.BeaconCtx.Ctrl.BeaconMode == 1 ) && ( ClassBNvm->PingSlotCtx.Ctrl.Assigned == 1 ) ) + { + return LORAMAC_STATUS_OK; + } + } + if( nextClass == CLASS_A ) + {// Switch from class b to class a + LoRaMacClassBHaltBeaconing( ); + + // Initialize default state for class b + InitClassBDefaults( ); + + return LORAMAC_STATUS_OK; + } + return LORAMAC_STATUS_SERVICE_UNKNOWN; +#else + return LORAMAC_STATUS_SERVICE_UNKNOWN; +#endif // LORAMAC_CLASSB_ENABLED +} + +LoRaMacStatus_t LoRaMacClassBMibGetRequestConfirm( MibRequestConfirm_t *mibGet ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + LoRaMacStatus_t status; + + switch( mibGet->Type ) + { + case MIB_PING_SLOT_DATARATE: + { + mibGet->Param.PingSlotDatarate = ClassBNvm->PingSlotCtx.Datarate; + break; + } + default: + { + status = LORAMAC_STATUS_SERVICE_UNKNOWN; + break; + } + } + return status; +#else + return LORAMAC_STATUS_SERVICE_UNKNOWN; +#endif // LORAMAC_CLASSB_ENABLED +} + +LoRaMacStatus_t LoRaMacMibClassBSetRequestConfirm( MibRequestConfirm_t *mibSet ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + LoRaMacStatus_t status; + + switch( mibSet->Type ) + { + case MIB_PING_SLOT_DATARATE: + { + ClassBNvm->PingSlotCtx.Datarate = mibSet->Param.PingSlotDatarate; + break; + } + default: + { + status = LORAMAC_STATUS_SERVICE_UNKNOWN; + break; + } + } + return status; +#else + return LORAMAC_STATUS_SERVICE_UNKNOWN; +#endif // LORAMAC_CLASSB_ENABLED +} + +void LoRaMacClassBPingSlotInfoAns( void ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + if( LoRaMacConfirmQueueIsCmdActive( MLME_PING_SLOT_INFO ) == true ) + { + LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_PING_SLOT_INFO ); + ClassBNvm->PingSlotCtx.Ctrl.Assigned = 1; + } +#endif // LORAMAC_CLASSB_ENABLED +} + +uint8_t LoRaMacClassBPingSlotChannelReq( uint8_t datarate, uint32_t frequency ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + uint8_t status = 0x03; + VerifyParams_t verify; + bool isCustomFreq = false; + + if( frequency != 0 ) + { + isCustomFreq = true; + verify.Frequency = frequency; + if( RegionVerify( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &verify, PHY_FREQUENCY ) == false ) + { + status &= 0xFE; // Channel frequency KO + } + } + + verify.DatarateParams.Datarate = datarate; + verify.DatarateParams.DownlinkDwellTime = Ctx.LoRaMacClassBParams.LoRaMacParams->DownlinkDwellTime; + + if( RegionVerify( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &verify, PHY_RX_DR ) == false ) + { + status &= 0xFD; // Datarate range KO + } + + if( status == 0x03 ) + { + if( isCustomFreq == true ) + { + ClassBNvm->PingSlotCtx.Ctrl.CustomFreq = 1; + ClassBNvm->PingSlotCtx.Frequency = frequency; + } + else + { + ClassBNvm->PingSlotCtx.Ctrl.CustomFreq = 0; + ClassBNvm->PingSlotCtx.Frequency = 0; + } + ClassBNvm->PingSlotCtx.Datarate = datarate; + } + + return status; +#else + return 0; +#endif // LORAMAC_CLASSB_ENABLED +} + +void LoRaMacClassBBeaconTimingAns( uint16_t beaconTimingDelay, uint8_t beaconTimingChannel, TimerTime_t lastRxDone ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + Ctx.BeaconCtx.BeaconTimingDelay = ( CLASSB_BEACON_DELAY_BEACON_TIMING_ANS * beaconTimingDelay ); + Ctx.BeaconCtx.BeaconTimingChannel = beaconTimingChannel; + + if( LoRaMacConfirmQueueIsCmdActive( MLME_BEACON_TIMING ) == true ) + { + if( Ctx.BeaconCtx.BeaconTimingDelay > CLASSB_BEACON_INTERVAL ) + { + // We missed the beacon already + Ctx.BeaconCtx.BeaconTimingDelay = 0; + Ctx.BeaconCtx.BeaconTimingChannel = 0; + LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND, MLME_BEACON_TIMING ); + } + else + { + Ctx.BeaconCtx.Ctrl.BeaconDelaySet = 1; + Ctx.BeaconCtx.Ctrl.BeaconChannelSet = 1; + Ctx.BeaconCtx.NextBeaconRx = SysTimeFromMs( lastRxDone + Ctx.BeaconCtx.BeaconTimingDelay ); + LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_BEACON_TIMING ); + } + + Ctx.LoRaMacClassBParams.MlmeConfirm->BeaconTimingDelay = Ctx.BeaconCtx.BeaconTimingDelay; + Ctx.LoRaMacClassBParams.MlmeConfirm->BeaconTimingChannel = Ctx.BeaconCtx.BeaconTimingChannel; + } +#endif // LORAMAC_CLASSB_ENABLED +} + +void LoRaMacClassBDeviceTimeAns( void ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + + SysTime_t nextBeacon = SysTimeGet( ); + TimerTime_t currentTimeMs = SysTimeToMs( nextBeacon ); + + nextBeacon.Seconds = nextBeacon.Seconds + ( 128 - ( nextBeacon.Seconds % 128 ) ); + nextBeacon.SubSeconds = 0; + + Ctx.BeaconCtx.NextBeaconRx = nextBeacon; + Ctx.BeaconCtx.LastBeaconRx = SysTimeSub( Ctx.BeaconCtx.NextBeaconRx, ( SysTime_t ){ .Seconds = CLASSB_BEACON_INTERVAL / 1000, .SubSeconds = 0 } ); + + if( LoRaMacConfirmQueueIsCmdActive( MLME_DEVICE_TIME ) == true ) + { + if( currentTimeMs > SysTimeToMs( Ctx.BeaconCtx.NextBeaconRx ) ) + { + // We missed the beacon already + Ctx.BeaconCtx.LastBeaconRx.Seconds = 0; + Ctx.BeaconCtx.LastBeaconRx.SubSeconds = 0; + Ctx.BeaconCtx.NextBeaconRx.Seconds = 0; + Ctx.BeaconCtx.NextBeaconRx.SubSeconds = 0; + LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND, MLME_DEVICE_TIME ); + } + else + { + Ctx.BeaconCtx.Ctrl.BeaconDelaySet = 1; + Ctx.BeaconCtx.BeaconTimingDelay = SysTimeToMs( Ctx.BeaconCtx.NextBeaconRx ) - currentTimeMs; + Ctx.BeaconCtx.BeaconTime.Seconds = nextBeacon.Seconds - UNIX_GPS_EPOCH_OFFSET - 128; + Ctx.BeaconCtx.BeaconTime.SubSeconds = 0; + LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_DEVICE_TIME ); + } + } +#endif // LORAMAC_CLASSB_ENABLED +} + +bool LoRaMacClassBBeaconFreqReq( uint32_t frequency ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + VerifyParams_t verify; + + if( frequency != 0 ) + { + verify.Frequency = frequency; + + if( RegionVerify( *Ctx.LoRaMacClassBParams.LoRaMacRegion, &verify, PHY_FREQUENCY ) == true ) + { + ClassBNvm->BeaconCtx.Ctrl.CustomFreq = 1; + ClassBNvm->BeaconCtx.Frequency = frequency; + return true; + } + } + else + { + ClassBNvm->BeaconCtx.Ctrl.CustomFreq = 0; + return true; + } + return false; +#else + return false; +#endif // LORAMAC_CLASSB_ENABLED +} + +TimerTime_t LoRaMacClassBIsUplinkCollision( TimerTime_t txTimeOnAir ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + TimerTime_t currentTime = TimerGetCurrentTime( ); + TimerTime_t beaconReserved = 0; + TimerTime_t nextBeacon = SysTimeToMs( Ctx.BeaconCtx.NextBeaconRx ); + + beaconReserved = nextBeacon - + CLASSB_BEACON_GUARD - + Ctx.LoRaMacClassBParams.LoRaMacParams->ReceiveDelay1 - + Ctx.LoRaMacClassBParams.LoRaMacParams->ReceiveDelay2 - + txTimeOnAir; + + // Check if the next beacon will be received during the next uplink. + if( ( currentTime >= beaconReserved ) && ( currentTime < ( nextBeacon + CLASSB_BEACON_RESERVED ) ) ) + {// Next beacon will be sent during the next uplink. + return CLASSB_BEACON_RESERVED; + } + return 0; +#else + return 0; +#endif // LORAMAC_CLASSB_ENABLED +} + +void LoRaMacClassBStopRxSlots( void ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + TimerStop( &Ctx.PingSlotTimer ); + TimerStop( &Ctx.MulticastSlotTimer ); + + CRITICAL_SECTION_BEGIN( ); + LoRaMacClassBEvents.Events.PingSlot = 0; + LoRaMacClassBEvents.Events.MulticastSlot = 0; + CRITICAL_SECTION_END( ); +#endif // LORAMAC_CLASSB_ENABLED +} + +void LoRaMacClassBStartRxSlots( void ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + if( ClassBNvm->PingSlotCtx.Ctrl.Assigned == 1 ) + { + LoRaMacClassBSetPingSlotState( PINGSLOT_STATE_CALC_PING_OFFSET ); + TimerSetValue( &Ctx.PingSlotTimer, 1 ); + TimerStart( &Ctx.PingSlotTimer ); + + LoRaMacClassBSetMulticastSlotState( PINGSLOT_STATE_CALC_PING_OFFSET ); + TimerSetValue( &Ctx.MulticastSlotTimer, 1 ); + TimerStart( &Ctx.MulticastSlotTimer ); + } +#endif // LORAMAC_CLASSB_ENABLED +} + +void LoRaMacClassBSetMulticastPeriodicity( MulticastCtx_t* multicastChannel ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + if( multicastChannel != NULL ) + { + multicastChannel->PingNb = CalcPingNb( multicastChannel->ChannelParams.RxParams.Params.ClassB.Periodicity ); + multicastChannel->PingPeriod = CalcPingPeriod( multicastChannel->PingNb ); + } +#endif // LORAMAC_CLASSB_ENABLED +} + +void LoRaMacClassBSetFPendingBit( uint32_t address, uint8_t fPendingSet ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + MulticastCtx_t *cur = Ctx.LoRaMacClassBParams.MulticastChannels; + + if( address == *Ctx.LoRaMacClassBParams.LoRaMacDevAddr ) + { + // Unicast + ClassBNvm->PingSlotCtx.FPendingSet = fPendingSet; + } + else + { + for( uint8_t i = 0; i < LORAMAC_MAX_MC_CTX; i++ ) + { + if( cur != NULL ) + { + // Set the fPending bit, if its a multicast + if( address == cur->ChannelParams.Address ) + { + cur->FPendingSet = fPendingSet; + } + } + cur++; + } + } +#endif +} + +void LoRaMacClassBProcess( void ) +{ +#ifdef LORAMAC_CLASSB_ENABLED + LoRaMacClassBEvents_t events; + + CRITICAL_SECTION_BEGIN( ); + events = LoRaMacClassBEvents; + LoRaMacClassBEvents.Value = 0; + CRITICAL_SECTION_END( ); + + if( events.Value != 0 ) + { + if( events.Events.Beacon == 1 ) + { + LoRaMacClassBProcessBeacon( ); + } + if( events.Events.PingSlot == 1 ) + { + LoRaMacClassBProcessPingSlot( ); + } + if( events.Events.MulticastSlot == 1 ) + { + LoRaMacClassBProcessMulticastSlot( ); + } + } +#endif // LORAMAC_CLASSB_ENABLED +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacClassB.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacClassB.h new file mode 100644 index 0000000000..8dfc1f4391 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacClassB.h @@ -0,0 +1,544 @@ +/*! + * \file LoRaMacClassB.h + * + * \brief LoRa MAC Class B 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 Jaeckle ( STACKFORCE ) + * + * \defgroup LORAMACCLASSB LoRa MAC Class B layer implementation + * This module specifies the API implementation of the LoRaMAC Class B layer. + * This is a placeholder for a detailed description of the LoRaMac + * layer and the supported features. + * \{ + */ +#ifndef __LORAMACCLASSB_H__ +#define __LORAMACCLASSB_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "systime.h" +#include "LoRaMacTypes.h" + +/*! + * States of the class B beacon acquisition and tracking + */ +typedef enum eBeaconState +{ + /*! + * Initial state to acquire the beacon + */ + BEACON_STATE_ACQUISITION, + /*! + * Beacon acquisition state when a time reference is available + */ + BEACON_STATE_ACQUISITION_BY_TIME, + /*! + * Handles the state when the beacon reception fails + */ + BEACON_STATE_TIMEOUT, + /*! + * Handles the state when the beacon was missed due to an uplink + */ + BEACON_STATE_BEACON_MISSED, + /*! + * Reacquisition state which applies the algorithm to enlarge the reception + * windows + */ + BEACON_STATE_REACQUISITION, + /*! + * The node has locked a beacon successfully + */ + BEACON_STATE_LOCKED, + /*! + * The beacon state machine is stopped due to operations with higher priority + */ + BEACON_STATE_HALT, + /*! + * The node currently operates in the beacon window and is idle. In this + * state, the temperature measurement takes place + */ + BEACON_STATE_IDLE, + /*! + * The node operates in the guard time of class B + */ + BEACON_STATE_GUARD, + /*! + * The node is in receive mode to lock a beacon + */ + BEACON_STATE_RX, + /*! + * The nodes switches the device class + */ + BEACON_STATE_LOST, +}BeaconState_t; + +/*! + * States of the class B ping slot mechanism + */ +typedef enum ePingSlotState +{ + /*! + * Calculation of the ping slot offset + */ + PINGSLOT_STATE_CALC_PING_OFFSET, + /*! + * State to set the timer to open the next ping slot + */ + PINGSLOT_STATE_SET_TIMER, + /*! + * The node is in idle state + */ + PINGSLOT_STATE_IDLE, + /*! + * The node opens up a ping slot window + */ + PINGSLOT_STATE_RX, +}PingSlotState_t; + +/*! + * Class B ping slot context structure + */ +typedef struct sPingSlotContext +{ + + /*! + * Ping slot length time in ms + */ + uint32_t PingSlotWindow; + /*! + * Ping offset + */ + uint16_t PingOffset; + /*! + * Current symbol timeout. The node enlarges this variable in case of beacon + * loss. + */ + uint16_t SymbolTimeout; + /*! + * The multicast channel which will be enabled next. + */ + MulticastCtx_t *NextMulticastChannel; +}PingSlotContext_t; + + +/*! + * Class B beacon context structure + */ +typedef struct sBeaconContext +{ + struct sBeaconCtrl + { + /*! + * Set if the node receives beacons + */ + uint8_t BeaconMode : 1; + /*! + * Set if the node has acquired the beacon + */ + uint8_t BeaconAcquired : 1; + /*! + * Set if a beacon delay was set for the beacon acquisition + */ + uint8_t BeaconDelaySet : 1; + /*! + * Set if a beacon channel was set for the beacon acquisition + */ + uint8_t BeaconChannelSet : 1; + /*! + * Set if beacon acquisition is pending + */ + uint8_t AcquisitionPending : 1; + /*! + * Set if the beacon state machine will be resumed + */ + uint8_t ResumeBeaconing : 1; + }Ctrl; + + /*! + * Current temperature + */ + float Temperature; + /*! + * Beacon time received with the beacon frame + */ + SysTime_t BeaconTime; + /*! + * Time when the last beacon was received + */ + SysTime_t LastBeaconRx; + /*! + * Time when the next beacon will be received + */ + SysTime_t NextBeaconRx; + /*! + * This is the time where the RX window will be opened. + * Its base is NextBeaconRx with temperature compensations + * and RX window movement. + */ + TimerTime_t NextBeaconRxAdjusted; + /*! + * Current symbol timeout. The node enlarges this variable in case of beacon + * loss. + */ + uint16_t SymbolTimeout; + /*! + * Specifies how much time the beacon window will be moved. + */ + TimerTime_t BeaconWindowMovement; + /*! + * Beacon timing channel for next beacon + */ + uint8_t BeaconTimingChannel; + /*! + * Delay for next beacon in ms + */ + TimerTime_t BeaconTimingDelay; + TimerTime_t TimeStamp; + /*! + * Beacons transmit time precision determined using + * param field of beacon frame format. + */ + SysTime_t BeaconTimePrecision; +}BeaconContext_t; + +/*! + * Data structure which contains the callbacks + */ +typedef struct sLoRaMacClassBCallback +{ + /*! + * \brief Measures the temperature level + * + * \retval Temperature level + */ + float ( *GetTemperatureLevel )( void ); + /*! + *\brief Will be called each time a Radio IRQ is handled by the MAC + * layer. + * + *\warning Runs in a IRQ context. Should only change variables state. + */ + void ( *MacProcessNotify )( void ); +}LoRaMacClassBCallback_t; + +/*! + * Data structure which pointers to the properties LoRaMAC + */ +typedef struct sLoRaMacClassBParams +{ + /*! + * Pointer to the MlmeIndication structure + */ + MlmeIndication_t *MlmeIndication; + /*! + * Pointer to the McpsIndication structure + */ + McpsIndication_t *McpsIndication; + /*! + * Pointer to the MlmeConfirm structure + */ + MlmeConfirm_t *MlmeConfirm; + /*! + * Pointer to the LoRaMacFlags structure + */ + LoRaMacFlags_t *LoRaMacFlags; + /*! + * Pointer to the LoRaMac device address + */ + uint32_t *LoRaMacDevAddr; + /*! + * Pointer to the LoRaMac region definition + */ + LoRaMacRegion_t *LoRaMacRegion; + /*! + * Pointer to the LoRaMacParams structure + */ + LoRaMacParams_t *LoRaMacParams; + /*! + * Pointer to the multicast channel list + */ + MulticastCtx_t *MulticastChannels; + /*! + * Pointer to the activation type + */ + ActivationType_t *NetworkActivation; +}LoRaMacClassBParams_t; + +/*! + * Signature of callback function to be called by this module when the + * non-volatile needs to be saved. + */ +typedef void ( *LoRaMacClassBNvmEvent )( void ); + +/*! + * \brief Initialize LoRaWAN Class B + * + * \param [IN] classBParams Information and feedback parameter + * \param [IN] callbacks Contains the callback which the Class B implementation needs + * \param [IN] nvm Pointer to an external non-volatile memory data structure. + */ +void LoRaMacClassBInit( LoRaMacClassBParams_t *classBParams, LoRaMacClassBCallback_t *callbacks, + LoRaMacClassBNvmData_t* nvm ); + +/*! + * \brief Set the state of the beacon state machine + * + * \param [IN] beaconState Beacon state. + */ +void LoRaMacClassBSetBeaconState( BeaconState_t beaconState ); + +/*! + * \brief Set the state of the ping slot state machine + * + * \param [IN] pingSlotState Ping slot state. + */ +void LoRaMacClassBSetPingSlotState( PingSlotState_t pingSlotState ); + +/*! + * \brief Set the state of the multicast slot state machine + * + * \param [IN] pingSlotState multicast slot state. + */ +void LoRaMacClassBSetMulticastSlotState( PingSlotState_t multicastSlotState ); + +/*! + * \brief Verifies if an acquisition procedure is in progress + * + * \retval [true, if the acquisition is in progress; false, if not] + */ +bool LoRaMacClassBIsAcquisitionInProgress( void ); + +/*! + * \brief State machine of the Class B for beaconing + */ +void LoRaMacClassBBeaconTimerEvent( void* context ); + +/*! + * \brief State machine of the Class B for ping slots + */ +void LoRaMacClassBPingSlotTimerEvent( void* context ); + +/*! + * \brief State machine of the Class B for multicast slots + */ +void LoRaMacClassBMulticastSlotTimerEvent( void* context ); + +/*! + * \brief Receives and decodes the beacon frame + * + * \param [IN] payload Pointer to the payload + * \param [IN] size Size of the payload + * \retval [true, if the node has received a beacon; false, if not] + */ +bool LoRaMacClassBRxBeacon( uint8_t *payload, uint16_t size ); + +/*! + * \brief The function validates, if the node expects a beacon + * at the current time. + * + * \retval [true, if the node expects a beacon; false, if not] + */ +bool LoRaMacClassBIsBeaconExpected( void ); + +/*! + * \brief The function validates, if the node expects a ping slot + * at the current time. + * + * \retval [true, if the node expects a ping slot; false, if not] + */ +bool LoRaMacClassBIsPingExpected( void ); + +/*! + * \brief The function validates, if the node expects a multicast slot + * at the current time. + * + * \retval [true, if the node expects a multicast slot; false, if not] + */ +bool LoRaMacClassBIsMulticastExpected( void ); + +/*! + * \brief Verifies if the acquisition pending bit is set + * + * \retval [true, if the bit is set; false, if not] + */ +bool LoRaMacClassBIsAcquisitionPending( void ); + +/*! + * \brief Verifies if the beacon mode active bit is set + * + * \retval [true, if the bit is set; false, if not] + */ +bool LoRaMacClassBIsBeaconModeActive( void ); + +/*! + * \brief Stops the beacon and ping slot operation + */ +void LoRaMacClassBHaltBeaconing( void ); + +/*! + * \brief Resumes the beacon and ping slot operation + */ +void LoRaMacClassBResumeBeaconing( void ); + +/*! + * \brief Sets the periodicity of the ping slots + * + * \param [IN] periodicity Periodicity + */ +void LoRaMacClassBSetPingSlotInfo( uint8_t periodicity ); + +/*! + * \brief Switches the device class + * + * \param [IN] nextClass Device class to switch to + * + * \retval LoRaMacStatus_t Status of the operation. + */ +LoRaMacStatus_t LoRaMacClassBSwitchClass( DeviceClass_t nextClass ); + +/*! + * \brief LoRaMAC ClassB MIB-Get + * + * \details The mac information base service to get attributes of the LoRaMac + * Class B layer. + * + * \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. + */ +LoRaMacStatus_t LoRaMacClassBMibGetRequestConfirm( MibRequestConfirm_t *mibGet ); + +/*! + * \brief LoRaMAC Class B MIB-Set + * + * \details The mac information base service to set attributes of the LoRaMac + * Class B layer. + * + * \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. + */ +LoRaMacStatus_t LoRaMacMibClassBSetRequestConfirm( MibRequestConfirm_t *mibSet ); + +/*! + * \brief This function handles the PING_SLOT_FREQ_ANS + */ +void LoRaMacClassBPingSlotInfoAns( void ); + +/*! + * \brief This function handles the PING_SLOT_CHANNEL_REQ + * + * \param [IN] datarate Device class to switch to + * \param [IN] frequency Device class to switch to + * + * \retval Status for the MAC answer. + */ +uint8_t LoRaMacClassBPingSlotChannelReq( uint8_t datarate, uint32_t frequency ); + +/*! + * \brief This function handles the BEACON_TIMING_ANS + * + * \param [IN] beaconTimingDelay The beacon timing delay + * \param [IN] beaconTimingChannel The beacon timing channel + * \param [IN] lastRxDone The time of the last frame reception + */ +void LoRaMacClassBBeaconTimingAns( uint16_t beaconTimingDelay, uint8_t beaconTimingChannel, TimerTime_t lastRxDone ); + +/*! + * \brief This function handles the ClassB DEVICE_TIME_ANS + */ +void LoRaMacClassBDeviceTimeAns( void ); + +/*! + * \brief This function handles the BEACON_FREQ_REQ + * + * \param [IN] frequency Frequency to set + * + * \retval [true, if MAC shall send an answer; false, if not] + */ +bool LoRaMacClassBBeaconFreqReq( uint32_t frequency ); + +/*! + * \brief Queries the ping slot window time + * + * \param [IN] txTimeOnAir TX time on air for the next uplink + * + * \retval Returns the time the uplink should be delayed + */ +TimerTime_t LoRaMacClassBIsUplinkCollision( TimerTime_t txTimeOnAir ); + +/*! + * \brief Stops the timers for the RX slots. This includes the + * timers for ping and multicast slots. + */ +void LoRaMacClassBStopRxSlots( void ); + +/*! + * \brief Starts the timers for the RX slots. This includes the + * timers for ping and multicast slots. + */ +void LoRaMacClassBStartRxSlots( void ); + +/*! + * \brief Starts the timers for the RX slots. This includes the + * timers for ping and multicast slots. + * + * \param [IN] periodicity Downlink periodicity + * + * \param [IN] multicastChannel Related multicast channel + */ +void LoRaMacClassBSetMulticastPeriodicity( MulticastCtx_t* multicastChannel ); + +/*! + * \brief Sets the FPending bit status of the related downlink slot + * + * \param [IN] address Slot address, could be unicast or multicast + * + * \param [IN] fPendingSet Set to 1, if the fPending bit in the + * sequence is set, otherwise 0. + */ +void LoRaMacClassBSetFPendingBit( uint32_t address, uint8_t fPendingSet ); + +/*! + * \brief Class B process function. + */ +void LoRaMacClassBProcess( void ); + +#ifdef __cplusplus +} +#endif + +#endif // __LORAMACCLASSB_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacClassBConfig.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacClassBConfig.h new file mode 100644 index 0000000000..058dc2f5bb --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacClassBConfig.h @@ -0,0 +1,124 @@ +/*! + * \file LoRaMacClassBConfig.h + * + * \brief LoRa MAC Class B configuration + * + * \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 Jaeckle ( STACKFORCE ) + * + * \defgroup LORAMACCLASSB LoRa MAC Class B configuration + * This header file contains parameters to configure the class b operation. + * By default, all parameters are set according to the specification. + * \{ + */ +#ifndef __LORAMACCLASSBCONFIG_H__ +#define __LORAMACCLASSBCONFIG_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*! + * Defines the beacon interval in ms + */ +#define CLASSB_BEACON_INTERVAL 128000 + +/*! + * Beacon reserved time in ms + */ +#define CLASSB_BEACON_RESERVED 2120 + +/*! + * Beacon guard time in ms + */ +#define CLASSB_BEACON_GUARD 3000 + +/*! + * Beacon window time in ms + */ +#define CLASSB_BEACON_WINDOW 122880 + +/*! + * Beacon window time in numer of slots + */ +#define CLASSB_BEACON_WINDOW_SLOTS 4096 + +/*! + * Ping slot length time in ms + */ +#define CLASSB_PING_SLOT_WINDOW 30 + +/*! + * Maximum allowed beacon less time in ms + */ +#define CLASSB_MAX_BEACON_LESS_PERIOD 7200000 + +/*! + * Delay time for the BeaconTimingAns in ms + */ +#define CLASSB_BEACON_DELAY_BEACON_TIMING_ANS 30 + +/*! + * Default symbol timeout for beacons and ping slot windows + */ +#define CLASSB_BEACON_SYMBOL_TO_DEFAULT 8 + +/*! + * Maximum symbol timeout for beacons + */ +#define CLASSB_BEACON_SYMBOL_TO_EXPANSION_MAX 255 + +/*! + * Maximum symbol timeout for ping slots + */ +#define CLASSB_PING_SLOT_SYMBOL_TO_EXPANSION_MAX 30 + +/*! + * Symbol expansion value for beacon windows in case of beacon + * loss in symbols + */ +#define CLASSB_BEACON_SYMBOL_TO_EXPANSION_FACTOR 2 + +/*! + * Defines the default window movement time + */ +#define CLASSB_WINDOW_MOVE_DEFAULT 2 + +/*! + * Defines the maximum time for the beacon movement + */ +#define CLASSB_WINDOW_MOVE_EXPANSION_MAX 256 + +/*! + * Defines the expansion factor for the beacon movement + */ +#define CLASSB_WINDOW_MOVE_EXPANSION_FACTOR 2 + +#ifdef __cplusplus +} +#endif + +#endif // __LORAMACCLASSBCONFIG_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacClassBNvm.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacClassBNvm.h new file mode 100644 index 0000000000..255f2d45e4 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacClassBNvm.h @@ -0,0 +1,124 @@ +/*! + * \file LoRaMacClassBNvm.h + * + * \brief LoRa MAC Class B non-volatile data. + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \addtogroup LORAMACCLASSB + * + * \{ + */ +#ifndef __LORAMACCLASSBNVM_H__ +#define __LORAMACCLASSBNVM_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +/*! + * LoRaMac Class B Context structure for NVM parameters + * related to ping slots + */ +typedef struct sLoRaMacClassBPingSlotNvmData +{ + struct sPingSlotCtrlNvm + { + /*! + * Set when the server assigned a ping slot to the node + */ + uint8_t Assigned : 1; + /*! + * Set when a custom frequency is used + */ + uint8_t CustomFreq : 1; + }Ctrl; + /*! + * Number of ping slots + */ + uint8_t PingNb; + /*! + * Period of the ping slots + */ + uint16_t PingPeriod; + /*! + * Reception frequency of the ping slot windows + */ + uint32_t Frequency; + /*! + * Datarate of the ping slot + */ + int8_t Datarate; + /*! + * Set to 1, if the FPending bit is set + */ + uint8_t FPendingSet; +} LoRaMacClassBPingSlotNvmData_t; + +/*! + * LoRaMac Class B Context structure for NVM parameters + * related to beaconing + */ +typedef struct sLoRaMacClassBBeaconNvmData +{ + struct sBeaconCtrlNvm + { + /*! + * Set if the node has a custom frequency for beaconing and ping slots + */ + uint8_t CustomFreq : 1; + }Ctrl; + /*! + * Beacon reception frequency + */ + uint32_t Frequency; +} LoRaMacClassBBeaconNvmData_t; + +/*! + * LoRaMac Class B Context structure + */ +typedef struct sLoRaMacClassBNvmData +{ + /*! + * Class B ping slot context + */ + LoRaMacClassBPingSlotNvmData_t PingSlotCtx; + /*! + * Class B beacon context + */ + LoRaMacClassBBeaconNvmData_t BeaconCtx; + /*! + * CRC32 value of the ClassB data structure. + */ + uint32_t Crc32; +} LoRaMacClassBNvmData_t; + +#ifdef __cplusplus +} +#endif + +#endif // __LORAMACCLASSBNVM_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacCommands.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacCommands.c new file mode 100644 index 0000000000..a4697b10d5 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacCommands.c @@ -0,0 +1,640 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: LoRa MAC commands + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Daniel Jaeckle ( STACKFORCE ), Johannes Bruder ( STACKFORCE ) +*/ +#include + +#include "utilities.h" +#include "LoRaMacCommands.h" +#include "LoRaMacConfirmQueue.h" + +#ifndef NUM_OF_MAC_COMMANDS +/*! + * Number of MAC Command slots + */ +#define NUM_OF_MAC_COMMANDS 32 +#endif + +/*! + * Size of the CID field of MAC commands + */ +#define CID_FIELD_SIZE 1 + +/*! + * Mac Commands list structure + */ +typedef struct sMacCommandsList +{ + /* + * First element of MAC command list. + */ + MacCommand_t* First; + /* + * Last element of MAC command list. + */ + MacCommand_t* Last; +} MacCommandsList_t; + +/*! + * LoRaMac Commands Context structure + */ +typedef struct sLoRaMacCommandsCtx +{ + /* + * List of MAC command elements + */ + MacCommandsList_t MacCommandList; + /* + * Buffer to store MAC command elements + */ + MacCommand_t MacCommandSlots[NUM_OF_MAC_COMMANDS]; + /* + * Size of all MAC commands serialized as buffer + */ + size_t SerializedCmdsSize; +} LoRaMacCommandsCtx_t; + +/*! + * Non-volatile module context. + */ +static LoRaMacCommandsCtx_t CommandsCtx; + +/* Memory management functions */ + +/*! + * \brief Determines if a MAC command slot is free + * + * \param[IN] slot - Slot to check + * \retval - Status of the operation + */ +static bool IsSlotFree( const MacCommand_t* slot ) +{ + uint8_t* mem = ( uint8_t* )slot; + + for( uint16_t size = 0; size < sizeof( MacCommand_t ); size++ ) + { + if( mem[size] != 0x00 ) + { + return false; + } + } + return true; +} + +/*! + * \brief Allocates a new MAC command memory slot + * + * \retval - Pointer to slot + */ +static MacCommand_t* MallocNewMacCommandSlot( void ) +{ + uint8_t itr = 0; + + while( IsSlotFree( ( const MacCommand_t* )&CommandsCtx.MacCommandSlots[itr] ) == false ) + { + itr++; + if( itr == NUM_OF_MAC_COMMANDS ) + { + return NULL; + } + } + + return &CommandsCtx.MacCommandSlots[itr]; +} + +/*! + * \brief Free memory slot + * + * \param[IN] slot - Slot to free + * + * \retval - Status of the operation + */ +static bool FreeMacCommandSlot( MacCommand_t* slot ) +{ + if( slot == NULL ) + { + return false; + } + + memset1( ( uint8_t* )slot, 0x00, sizeof( MacCommand_t ) ); + + return true; +} + +/* Linked list functions */ + +/*! + * \brief Initialize list + * + * \param[IN] list - List that shall be initialized + * \retval - Status of the operation + */ +static bool LinkedListInit( MacCommandsList_t* list ) +{ + if( list == NULL ) + { + return false; + } + + list->First = NULL; + list->Last = NULL; + + return true; +} + +/*! + * \brief Add an element to the list + * + * \param[IN] list - List where the element shall be added. + * \param[IN] element - Element to add + * \retval - Status of the operation + */ +static bool LinkedListAdd( MacCommandsList_t* list, MacCommand_t* element ) +{ + if( ( list == NULL ) || ( element == NULL ) ) + { + return false; + } + + // Check if this is the first entry to enter the list. + if( list->First == NULL ) + { + list->First = element; + } + + // Check if the last entry exists and update its next point. + if( list->Last ) + { + list->Last->Next = element; + } + + // Update the next point of this entry. + element->Next = NULL; + + // Update the last entry of the list. + list->Last = element; + + return true; +} + +/*! + * \brief Return the previous element in the list. + * + * \param[IN] list - List + * \param[IN] element - Element where the previous element shall be searched + * \retval - Status of the operation + */ +static MacCommand_t* LinkedListGetPrevious( MacCommandsList_t* list, MacCommand_t* element ) +{ + if( ( list == NULL ) || ( element == NULL ) ) + { + return NULL; + } + + MacCommand_t* curElement; + + // Start at the head of the list + curElement = list->First; + + // When current element is the first of the list, there's no previous element so we can return NULL immediately. + if( element != curElement ) + { + // Loop through all elements until the end is reached or the next of current is the current element. + while( ( curElement != NULL ) && ( curElement->Next != element ) ) + { + curElement = curElement->Next; + } + } + else + { + curElement = NULL; + } + + return curElement; +} + +/*! + * \brief Remove an element from the list + * + * \param[IN] list - List where the element shall be removed from. + * \param[IN] element - Element to remove + * \retval - Status of the operation + */ +static bool LinkedListRemove( MacCommandsList_t* list, MacCommand_t* element ) +{ + if( ( list == NULL ) || ( element == NULL ) ) + { + return false; + } + + MacCommand_t* PrevElement = LinkedListGetPrevious( list, element ); + + if( list->First == element ) + { + list->First = element->Next; + } + + if( list->Last == element ) + { + list->Last = PrevElement; + } + + if( PrevElement != NULL ) + { + PrevElement->Next = element->Next; + } + + element->Next = NULL; + + return true; +} + +/* + * \brief Determines if a MAC command is sticky or not + * + * \param[IN] cid - MAC command identifier + * + * \retval - Status of the operation + */ +static bool IsSticky( uint8_t cid ) +{ + switch( cid ) + { + case MOTE_MAC_RESET_IND: + case MOTE_MAC_REKEY_IND: + case MOTE_MAC_DEVICE_MODE_IND: + case MOTE_MAC_DL_CHANNEL_ANS: + case MOTE_MAC_RX_PARAM_SETUP_ANS: + case MOTE_MAC_RX_TIMING_SETUP_ANS: + case MOTE_MAC_TX_PARAM_SETUP_ANS: + case MOTE_MAC_PING_SLOT_CHANNEL_ANS: + return true; + default: + return false; + } +} + +/* + * \brief Determines if a MAC command requires an explicit confirmation + * + * \param[IN] cid - MAC command identifier + * + * \retval - Status of the operation + */ +static bool IsConfirmationRequired( uint8_t cid ) +{ + switch( cid ) + { + case MOTE_MAC_RESET_IND: + case MOTE_MAC_REKEY_IND: + case MOTE_MAC_DEVICE_MODE_IND: + return true; + default: + return false; + } +} + +LoRaMacCommandStatus_t LoRaMacCommandsInit( void ) +{ + // Initialize with default + memset1( ( uint8_t* )&CommandsCtx, 0, sizeof( CommandsCtx ) ); + + LinkedListInit( &CommandsCtx.MacCommandList ); + + return LORAMAC_COMMANDS_SUCCESS; +} + +LoRaMacCommandStatus_t LoRaMacCommandsAddCmd( uint8_t cid, uint8_t* payload, size_t payloadSize ) +{ + if( payload == NULL ) + { + return LORAMAC_COMMANDS_ERROR_NPE; + } + MacCommand_t* newCmd; + + // Allocate a memory slot + newCmd = MallocNewMacCommandSlot( ); + + if( newCmd == NULL ) + { + return LORAMAC_COMMANDS_ERROR_MEMORY; + } + + // Add it to the list of Mac commands + if( LinkedListAdd( &CommandsCtx.MacCommandList, newCmd ) == false ) + { + return LORAMAC_COMMANDS_ERROR; + } + + // Set Values + newCmd->CID = cid; + newCmd->PayloadSize = payloadSize; + memcpy1( ( uint8_t* )newCmd->Payload, payload, payloadSize ); + newCmd->IsSticky = IsSticky( cid ); + newCmd->IsConfirmationRequired = IsConfirmationRequired( cid ); + + CommandsCtx.SerializedCmdsSize += ( CID_FIELD_SIZE + payloadSize ); + + return LORAMAC_COMMANDS_SUCCESS; +} + +LoRaMacCommandStatus_t LoRaMacCommandsRemoveCmd( MacCommand_t* macCmd ) +{ + if( macCmd == NULL ) + { + return LORAMAC_COMMANDS_ERROR_NPE; + } + + // Remove the Mac command element from MacCommandList + if( LinkedListRemove( &CommandsCtx.MacCommandList, macCmd ) == false ) + { + return LORAMAC_COMMANDS_ERROR_CMD_NOT_FOUND; + } + + CommandsCtx.SerializedCmdsSize -= ( CID_FIELD_SIZE + macCmd->PayloadSize ); + + // Free the MacCommand Slot + if( FreeMacCommandSlot( macCmd ) == false ) + { + return LORAMAC_COMMANDS_ERROR; + } + + return LORAMAC_COMMANDS_SUCCESS; +} + +LoRaMacCommandStatus_t LoRaMacCommandsGetCmd( uint8_t cid, MacCommand_t** macCmd ) +{ + MacCommand_t* curElement; + + // Start at the head of the list + curElement = CommandsCtx.MacCommandList.First; + + // Loop through all elements until we find the element with the given CID + while( ( curElement != NULL ) && ( curElement->CID != cid ) ) + { + curElement = curElement->Next; + } + + // Update the pointer anyway + *macCmd = curElement; + + // Handle error in case if we reached the end without finding it. + if( curElement == NULL ) + { + return LORAMAC_COMMANDS_ERROR_CMD_NOT_FOUND; + } + return LORAMAC_COMMANDS_SUCCESS; +} + +LoRaMacCommandStatus_t LoRaMacCommandsRemoveNoneStickyCmds( void ) +{ + MacCommand_t* curElement; + MacCommand_t* nexElement; + + // Start at the head of the list + curElement = CommandsCtx.MacCommandList.First; + + // Loop through all elements + while( curElement != NULL ) + { + if( curElement->IsSticky == false ) + { + nexElement = curElement->Next; + LoRaMacCommandsRemoveCmd( curElement ); + curElement = nexElement; + } + else + { + curElement = curElement->Next; + } + } + + return LORAMAC_COMMANDS_SUCCESS; +} + +LoRaMacCommandStatus_t LoRaMacCommandsRemoveStickyAnsCmds( void ) +{ + MacCommand_t* curElement; + MacCommand_t* nexElement; + + // Start at the head of the list + curElement = CommandsCtx.MacCommandList.First; + + // Loop through all elements + while( curElement != NULL ) + { + nexElement = curElement->Next; + if( ( IsSticky( curElement->CID ) == true ) && + ( IsConfirmationRequired( curElement->CID ) == false ) ) + { + LoRaMacCommandsRemoveCmd( curElement ); + } + curElement = nexElement; + } + + return LORAMAC_COMMANDS_SUCCESS; +} + +LoRaMacCommandStatus_t LoRaMacCommandsGetSizeSerializedCmds( size_t* size ) +{ + if( size == NULL ) + { + return LORAMAC_COMMANDS_ERROR_NPE; + } + *size = CommandsCtx.SerializedCmdsSize; + return LORAMAC_COMMANDS_SUCCESS; +} + +LoRaMacCommandStatus_t LoRaMacCommandsSerializeCmds( size_t availableSize, size_t* effectiveSize, uint8_t* buffer ) +{ + MacCommand_t* curElement = CommandsCtx.MacCommandList.First; + MacCommand_t* nextElement; + uint8_t itr = 0; + + if( ( buffer == NULL ) || ( effectiveSize == NULL ) ) + { + return LORAMAC_COMMANDS_ERROR_NPE; + } + + // Loop through all elements which fits into the buffer + while( curElement != NULL ) + { + // If the next MAC command still fits into the buffer, add it. + if( ( availableSize - itr ) >= ( CID_FIELD_SIZE + curElement->PayloadSize ) ) + { + buffer[itr++] = curElement->CID; + memcpy1( &buffer[itr], curElement->Payload, curElement->PayloadSize ); + itr += curElement->PayloadSize; + } + else + { + break; + } + curElement = curElement->Next; + } + + // Remove all commands which do not fit into the buffer + while( curElement != NULL ) + { + // Store the next element before removing the current one + nextElement = curElement->Next; + LoRaMacCommandsRemoveCmd( curElement ); + curElement = nextElement; + } + + // Fetch the effective size of the mac commands + LoRaMacCommandsGetSizeSerializedCmds( effectiveSize ); + + return LORAMAC_COMMANDS_SUCCESS; +} + +uint8_t LoRaMacCommandsGetCmdSize( uint8_t cid ) +{ + uint8_t cidSize = 0; + + // Decode Frame MAC commands + switch( cid ) + { + case SRV_MAC_RESET_CONF: + { + // cid + Serv_LoRaWAN_version + cidSize = 2; + break; + } + case SRV_MAC_LINK_CHECK_ANS: + { + // cid + Margin + GwCnt + cidSize = 3; + break; + } + case SRV_MAC_LINK_ADR_REQ: + { + // cid + DataRate_TXPower + ChMask (2) + Redundancy + cidSize = 5; + break; + } + case SRV_MAC_DUTY_CYCLE_REQ: + { + // cid + DutyCyclePL + cidSize = 2; + break; + } + case SRV_MAC_RX_PARAM_SETUP_REQ: + { + // cid + DLsettings + Frequency (3) + cidSize = 5; + break; + } + case SRV_MAC_DEV_STATUS_REQ: + { + // cid + cidSize = 1; + break; + } + case SRV_MAC_NEW_CHANNEL_REQ: + { + // cid + ChIndex + Frequency (3) + DrRange + cidSize = 6; + break; + } + case SRV_MAC_RX_TIMING_SETUP_REQ: + { + // cid + Settings + cidSize = 2; + break; + } + case SRV_MAC_TX_PARAM_SETUP_REQ: + { + // cid + EIRP_DwellTime + cidSize = 2; + break; + } + case SRV_MAC_DL_CHANNEL_REQ: + { + // cid + ChIndex + Frequency (3) + cidSize = 5; + break; + } + case SRV_MAC_REKEY_CONF: + { + // cid + Serv_LoRaWAN_version + cidSize = 2; + break; + } + case SRV_MAC_ADR_PARAM_SETUP_REQ: + { + // cid + ADRparam + cidSize = 2; + break; + } + case SRV_MAC_FORCE_REJOIN_REQ: + { + // cid + Payload (2) + cidSize = 3; + break; + } + case SRV_MAC_REJOIN_PARAM_REQ: + { + // cid + Payload (1) + cidSize = 2; + break; + } + case SRV_MAC_DEVICE_MODE_CONF: + { + // cid + Class + cidSize = 2; + break; + } + case SRV_MAC_DEVICE_TIME_ANS: + { + // cid + Seconds (4) + Fractional seconds (1) + cidSize = 6; + break; + } + case SRV_MAC_PING_SLOT_INFO_ANS: + { + // cid + cidSize = 1; + break; + } + case SRV_MAC_PING_SLOT_CHANNEL_REQ: + { + // cid + Frequency (3) + DR + cidSize = 5; + break; + } + case SRV_MAC_BEACON_TIMING_ANS: + { + // cid + TimingDelay (2) + Channel + cidSize = 4; + break; + } + case SRV_MAC_BEACON_FREQ_REQ: + { + // cid + Frequency (3) + cidSize = 4; + break; + } + default: + { + // Unknown command. ABORT MAC commands processing + break; + } + } + return cidSize; +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacCommands.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacCommands.h new file mode 100644 index 0000000000..144b1fd36b --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacCommands.h @@ -0,0 +1,210 @@ +/*! + * \file LoRaMacCommands.h + * + * \brief LoRa MAC commands + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * addtogroup LORAMAC + * \{ + * + */ +#ifndef __LORAMAC_COMMANDS_H__ +#define __LORAMAC_COMMANDS_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include +#include "LoRaMacTypes.h" + + +/* + * Number of MAC Command slots + */ +#define LORAMAC_COMMADS_MAX_NUM_OF_PARAMS 2 + +/*! + * LoRaWAN MAC Command element + */ +typedef struct sMacCommand MacCommand_t; + +struct sMacCommand +{ + /*! + * The pointer to the next MAC Command element in the list + */ + MacCommand_t* Next; + /*! + * MAC command identifier + */ + uint8_t CID; + /*! + * MAC command payload + */ + uint8_t Payload[LORAMAC_COMMADS_MAX_NUM_OF_PARAMS]; + /*! + * Size of MAC command payload + */ + size_t PayloadSize; + /*! + * Indicates if it's a sticky MAC command + */ + bool IsSticky; + /*! + * The command requires an explicit confirmation + */ + bool IsConfirmationRequired; +}; + +/*! + * LoRaMac Commands Status + */ +typedef enum eLoRaMacCommandsStatus +{ + /*! + * No error occurred + */ + LORAMAC_COMMANDS_SUCCESS = 0, + /*! + * Null pointer exception + */ + LORAMAC_COMMANDS_ERROR_NPE, + /*! + * There is no memory left to add a further MAC command + */ + LORAMAC_COMMANDS_ERROR_MEMORY, + /*! + * MAC command not found. + */ + LORAMAC_COMMANDS_ERROR_CMD_NOT_FOUND, + /*! + * Unknown or corrupted command error occurred. + */ + LORAMAC_COMMANDS_ERROR_UNKNOWN_CMD, + /*! + * Undefined Error occurred + */ + LORAMAC_COMMANDS_ERROR, +}LoRaMacCommandStatus_t; + +/*! + * Signature of callback function to be called by this module when the + * non-volatile needs to be saved. + */ +typedef void ( *LoRaMacCommandsNvmEvent )( void ); + +/*! + * \brief Initialization of LoRaMac MAC commands module + * + * \retval - Status of the operation + */ +LoRaMacCommandStatus_t LoRaMacCommandsInit( void ); + +/*! + * \brief Adds a new MAC command to be sent. + * + * \param[IN] cid - MAC command identifier + * \param[IN] payload - MAC command payload containing parameters + * \param[IN] payloadSize - Size of MAC command payload + * + * \retval - Status of the operation + */ +LoRaMacCommandStatus_t LoRaMacCommandsAddCmd( uint8_t cid, uint8_t* payload, size_t payloadSize ); + +/*! + * \brief Remove a MAC command. + * + * \param[OUT] cmd - MAC command + * + * \retval - Status of the operation + */ +LoRaMacCommandStatus_t LoRaMacCommandsRemoveCmd( MacCommand_t* macCmd ); + +/*! + * \brief Get the MAC command with corresponding CID. + * + * \param[IN] cid - MAC command identifier + * \param[OUT] cmd - MAC command + * + * \retval - Status of the operation + */ +LoRaMacCommandStatus_t LoRaMacCommandsGetCmd( uint8_t cid, MacCommand_t** macCmd ); + +/*! + * \brief Remove all none sticky MAC commands. + * + * \retval - Status of the operation + */ +LoRaMacCommandStatus_t LoRaMacCommandsRemoveNoneStickyCmds( void ); + +/*! + * \brief Remove all sticky answer MAC commands. + * + * \retval - Status of the operation + */ +LoRaMacCommandStatus_t LoRaMacCommandsRemoveStickyAnsCmds( void ); + +/*! + * \brief Get size of all MAC commands serialized as buffer + * + * \param[out] size - Available size of memory for MAC commands + * + * \retval - Status of the operation + */ +LoRaMacCommandStatus_t LoRaMacCommandsGetSizeSerializedCmds( size_t* size ); + +/*! + * \brief Get as many as possible MAC commands serialized + * + * \param[IN] availableSize - Available size of memory for MAC commands + * \param[out] effectiveSize - Size of memory which was effectively used for serializing. + * \param[out] buffer - Destination data buffer + * + * \retval - Status of the operation + */ +LoRaMacCommandStatus_t LoRaMacCommandsSerializeCmds( size_t availableSize, size_t* effectiveSize, uint8_t* buffer ); + +/*! + * \brief Get the MAC command size with corresponding CID. + * + * \param[IN] cid - MAC command identifier + * + * \retval Size of the command. + */ +uint8_t LoRaMacCommandsGetCmdSize( uint8_t cid ); + +/*! \} addtogroup LORAMAC */ + +#ifdef __cplusplus +} +#endif + +#endif // __LORAMAC_COMMANDS_H__ + diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacConfirmQueue.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacConfirmQueue.c new file mode 100644 index 0000000000..cedd18b5de --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacConfirmQueue.c @@ -0,0 +1,332 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: LoRa MAC confirm queue implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE ) +*/ +#include +#include +#include + +#include "timer.h" +#include "utilities.h" +#include "LoRaMac.h" +#include "LoRaMacConfirmQueue.h" + + +/* + * LoRaMac Confirm Queue Context NVM structure + */ +typedef struct sLoRaMacConfirmQueueNvmData +{ + /*! + * MlmeConfirm queue data structure + */ + MlmeConfirmQueue_t MlmeConfirmQueue[LORA_MAC_MLME_CONFIRM_QUEUE_LEN]; + /*! + * Counts the number of MlmeConfirms to process + */ + uint8_t MlmeConfirmQueueCnt; + /*! + * Variable which holds a common status + */ + LoRaMacEventInfoStatus_t CommonStatus; +} LoRaMacConfirmQueueNvmData_t; + +/* + * LoRaMac Confirm Queue Context structure + */ +typedef struct sLoRaMacConfirmQueueCtx +{ + /*! + * LoRaMac callback function primitives + */ + LoRaMacPrimitives_t* Primitives; + /*! + * Pointer to the first element of the ring buffer + */ + MlmeConfirmQueue_t* BufferStart; + /*! + * Pointer to the last element of the ring buffer + */ + MlmeConfirmQueue_t* BufferEnd; + /*! + * Non-volatile module context. + */ + LoRaMacConfirmQueueNvmData_t Nvm; +} LoRaMacConfirmQueueCtx_t; + +/* + * Module context. + */ +static LoRaMacConfirmQueueCtx_t ConfirmQueueCtx; + +static MlmeConfirmQueue_t* IncreaseBufferPointer( MlmeConfirmQueue_t* bufferPointer ) +{ + if( bufferPointer == &ConfirmQueueCtx.Nvm.MlmeConfirmQueue[LORA_MAC_MLME_CONFIRM_QUEUE_LEN - 1] ) + { + // Reset to the first element + bufferPointer = ConfirmQueueCtx.Nvm.MlmeConfirmQueue; + } + else + { + // Increase + bufferPointer++; + } + return bufferPointer; +} + +static MlmeConfirmQueue_t* DecreaseBufferPointer( MlmeConfirmQueue_t* bufferPointer ) +{ + if( bufferPointer == ConfirmQueueCtx.Nvm.MlmeConfirmQueue ) + { + // Reset to the last element + bufferPointer = &ConfirmQueueCtx.Nvm.MlmeConfirmQueue[LORA_MAC_MLME_CONFIRM_QUEUE_LEN - 1]; + } + else + { + bufferPointer--; + } + return bufferPointer; +} + +static bool IsListEmpty( uint8_t count ) +{ + if( count == 0 ) + { + return true; + } + return false; +} + +static bool IsListFull( uint8_t count ) +{ + if( count >= LORA_MAC_MLME_CONFIRM_QUEUE_LEN ) + { + return true; + } + return false; +} + +static MlmeConfirmQueue_t* GetElement( Mlme_t request, MlmeConfirmQueue_t* bufferStart, MlmeConfirmQueue_t* bufferEnd ) +{ + MlmeConfirmQueue_t* element = bufferStart; + + if( IsListEmpty( ConfirmQueueCtx.Nvm.MlmeConfirmQueueCnt ) == true ) + { + return NULL; + } + + for( uint8_t elementCnt = 0; elementCnt < ConfirmQueueCtx.Nvm.MlmeConfirmQueueCnt; elementCnt++ ) + { + if( element->Request == request ) + { + // We have found the element + return element; + } + element = IncreaseBufferPointer( element ); + } + + return NULL; +} + +void LoRaMacConfirmQueueInit( LoRaMacPrimitives_t* primitives ) +{ + ConfirmQueueCtx.Primitives = primitives; + + // Init counter + ConfirmQueueCtx.Nvm.MlmeConfirmQueueCnt = 0; + + // Init buffer + ConfirmQueueCtx.BufferStart = ConfirmQueueCtx.Nvm.MlmeConfirmQueue; + ConfirmQueueCtx.BufferEnd = ConfirmQueueCtx.Nvm.MlmeConfirmQueue; + + memset1( ( uint8_t* )ConfirmQueueCtx.Nvm.MlmeConfirmQueue, 0xFF, sizeof( ConfirmQueueCtx.Nvm.MlmeConfirmQueue ) ); + + // Common status + ConfirmQueueCtx.Nvm.CommonStatus = LORAMAC_EVENT_INFO_STATUS_ERROR; +} + +bool LoRaMacConfirmQueueAdd( MlmeConfirmQueue_t* mlmeConfirm ) +{ + if( IsListFull( ConfirmQueueCtx.Nvm.MlmeConfirmQueueCnt ) == true ) + { + // Protect the buffer against overwrites + return false; + } + + // Add the element to the ring buffer + ConfirmQueueCtx.BufferEnd->Request = mlmeConfirm->Request; + ConfirmQueueCtx.BufferEnd->Status = mlmeConfirm->Status; + ConfirmQueueCtx.BufferEnd->RestrictCommonReadyToHandle = mlmeConfirm->RestrictCommonReadyToHandle; + ConfirmQueueCtx.BufferEnd->ReadyToHandle = mlmeConfirm->ReadyToHandle; + // Increase counter + ConfirmQueueCtx.Nvm.MlmeConfirmQueueCnt++; + // Update end pointer + ConfirmQueueCtx.BufferEnd = IncreaseBufferPointer( ConfirmQueueCtx.BufferEnd ); + + return true; +} + +bool LoRaMacConfirmQueueRemoveLast( void ) +{ + if( IsListEmpty( ConfirmQueueCtx.Nvm.MlmeConfirmQueueCnt ) == true ) + { + return false; + } + + // Increase counter + ConfirmQueueCtx.Nvm.MlmeConfirmQueueCnt--; + // Update start pointer + ConfirmQueueCtx.BufferEnd = DecreaseBufferPointer( ConfirmQueueCtx.BufferEnd ); + + return true; +} + +bool LoRaMacConfirmQueueRemoveFirst( void ) +{ + if( IsListEmpty( ConfirmQueueCtx.Nvm.MlmeConfirmQueueCnt ) == true ) + { + return false; + } + + // Increase counter + ConfirmQueueCtx.Nvm.MlmeConfirmQueueCnt--; + // Update start pointer + ConfirmQueueCtx.BufferStart = IncreaseBufferPointer( ConfirmQueueCtx.BufferStart ); + + return true; +} + +void LoRaMacConfirmQueueSetStatus( LoRaMacEventInfoStatus_t status, Mlme_t request ) +{ + MlmeConfirmQueue_t* element = NULL; + + if( IsListEmpty( ConfirmQueueCtx.Nvm.MlmeConfirmQueueCnt ) == false ) + { + element = GetElement( request, ConfirmQueueCtx.BufferStart, ConfirmQueueCtx.BufferEnd ); + if( element != NULL ) + { + element->Status = status; + element->ReadyToHandle = true; + } + } +} + +LoRaMacEventInfoStatus_t LoRaMacConfirmQueueGetStatus( Mlme_t request ) +{ + MlmeConfirmQueue_t* element = NULL; + + if( IsListEmpty( ConfirmQueueCtx.Nvm.MlmeConfirmQueueCnt ) == false ) + { + element = GetElement( request, ConfirmQueueCtx.BufferStart, ConfirmQueueCtx.BufferEnd ); + if( element != NULL ) + { + return element->Status; + } + } + return LORAMAC_EVENT_INFO_STATUS_ERROR; +} + +void LoRaMacConfirmQueueSetStatusCmn( LoRaMacEventInfoStatus_t status ) +{ + MlmeConfirmQueue_t* element = ConfirmQueueCtx.BufferStart; + + ConfirmQueueCtx.Nvm.CommonStatus = status; + + if( IsListEmpty( ConfirmQueueCtx.Nvm.MlmeConfirmQueueCnt ) == false ) + { + do + { + element->Status = status; + // Set the status if it is allowed to set it with a call to + // LoRaMacConfirmQueueSetStatusCmn. + if( element->RestrictCommonReadyToHandle == false ) + { + element->ReadyToHandle = true; + } + element = IncreaseBufferPointer( element ); + }while( element != ConfirmQueueCtx.BufferEnd ); + } +} + +LoRaMacEventInfoStatus_t LoRaMacConfirmQueueGetStatusCmn( void ) +{ + return ConfirmQueueCtx.Nvm.CommonStatus; +} + +bool LoRaMacConfirmQueueIsCmdActive( Mlme_t request ) +{ + if( GetElement( request, ConfirmQueueCtx.BufferStart, ConfirmQueueCtx.BufferEnd ) != NULL ) + { + return true; + } + return false; +} + +void LoRaMacConfirmQueueHandleCb( MlmeConfirm_t* mlmeConfirm ) +{ + uint8_t nbElements = ConfirmQueueCtx.Nvm.MlmeConfirmQueueCnt; + bool readyToHandle = false; + MlmeConfirmQueue_t mlmeConfirmToStore; + + memset1( ( uint8_t* ) &mlmeConfirmToStore, 0, sizeof( MlmeConfirmQueue_t ) ); + + for( uint8_t i = 0; i < nbElements; i++ ) + { + mlmeConfirm->MlmeRequest = ConfirmQueueCtx.BufferStart->Request; + mlmeConfirm->Status = ConfirmQueueCtx.BufferStart->Status; + readyToHandle = ConfirmQueueCtx.BufferStart->ReadyToHandle; + + if( readyToHandle == true ) + { + ConfirmQueueCtx.Primitives->MacMlmeConfirm( mlmeConfirm ); + } + else + { + // The request is not processed yet. Store the state. + mlmeConfirmToStore.Request = ConfirmQueueCtx.BufferStart->Request; + mlmeConfirmToStore.Status = ConfirmQueueCtx.BufferStart->Status; + mlmeConfirmToStore.RestrictCommonReadyToHandle = ConfirmQueueCtx.BufferStart->RestrictCommonReadyToHandle; + } + + // Increase the pointer afterwards to prevent overwrites + LoRaMacConfirmQueueRemoveFirst( ); + + if( readyToHandle == false ) + { + // Add a request which has not been finished again to the queue + LoRaMacConfirmQueueAdd( &mlmeConfirmToStore ); + } + } +} + +uint8_t LoRaMacConfirmQueueGetCnt( void ) +{ + return ConfirmQueueCtx.Nvm.MlmeConfirmQueueCnt; +} + +bool LoRaMacConfirmQueueIsFull( void ) +{ + if( IsListFull( ConfirmQueueCtx.Nvm.MlmeConfirmQueueCnt ) == true ) + { + return true; + } + else + { + return false; + } +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacConfirmQueue.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacConfirmQueue.h new file mode 100644 index 0000000000..b42799e017 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacConfirmQueue.h @@ -0,0 +1,177 @@ +/*! + * \file LoRaMacConfirmQueue.h + * + * \brief LoRa MAC confirm queue 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 Jaeckle ( STACKFORCE ) + * + * \defgroup LORAMACCONFIRMQUEUE LoRa MAC confirm queue implementation + * This module specifies the API implementation of the LoRaMAC confirm queue. + * The confirm queue is implemented with as a ring buffer. The number of + * elements can be defined with \ref LORA_MAC_MLME_CONFIRM_QUEUE_LEN. The + * current implementation does not support multiple elements of the same + * Mlme_t type. + * \{ + */ +#ifndef __LORAMAC_CONFIRMQUEUE_H__ +#define __LORAMAC_CONFIRMQUEUE_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include + +#include "LoRaMac.h" + +/*! + * LoRaMac MLME-Confirm queue length + */ +#define LORA_MAC_MLME_CONFIRM_QUEUE_LEN 5 + +/*! + * Structure to hold multiple MLME request confirm data + */ +typedef struct sMlmeConfirmQueue +{ + /*! + * Holds the previously performed MLME-Request + */ + Mlme_t Request; + /*! + * Status of the operation + */ + LoRaMacEventInfoStatus_t Status; + /*! + * Set to true, if the request is ready to be handled + */ + bool ReadyToHandle; + /*! + * Set to true, if it is not permitted to set the ReadyToHandle variable + * with a function call to LoRaMacConfirmQueueSetStatusCmn. + */ + bool RestrictCommonReadyToHandle; +}MlmeConfirmQueue_t; + +/*! + * \brief Initializes the confirm queue + * + * \param [IN] primitives - Pointer to the LoRaMac primitives. + */ +void LoRaMacConfirmQueueInit( LoRaMacPrimitives_t* primitive ); + +/*! + * \brief Adds an element to the confirm queue. + * + * \param [IN] mlmeConfirm - Pointer to the element to add. + * + * \retval [true - operation was successful, false - operation failed] + */ +bool LoRaMacConfirmQueueAdd( MlmeConfirmQueue_t* mlmeConfirm ); + +/*! + * \brief Removes the last element which was added into the queue. + * + * \retval [true - operation was successful, false - operation failed] + */ +bool LoRaMacConfirmQueueRemoveLast( void ); + +/*! + * \brief Removes the first element which was added to the confirm queue. + * + * \retval [true - operation was successful, false - operation failed] + */ +bool LoRaMacConfirmQueueRemoveFirst( void ); + +/*! + * \brief Sets the status of an element. + * + * \param [IN] status - The status to set. + * + * \param [IN] request - The related request to set the status. + */ +void LoRaMacConfirmQueueSetStatus( LoRaMacEventInfoStatus_t status, Mlme_t request ); + +/*! + * \brief Gets the status of an element. + * + * \param [IN] request - The request to query the status. + * + * \retval The status of the related MlmeRequest. + */ +LoRaMacEventInfoStatus_t LoRaMacConfirmQueueGetStatus( Mlme_t request ); + +/*! + * \brief Sets a common status for all elements in the queue. + * + * \param [IN] status - The status to set. + */ +void LoRaMacConfirmQueueSetStatusCmn( LoRaMacEventInfoStatus_t status ); + +/*! + * \brief Gets the common status of all elements. + * + * \retval The common status of all elements. + */ +LoRaMacEventInfoStatus_t LoRaMacConfirmQueueGetStatusCmn( void ); + +/*! + * \brief Verifies if a request is in the queue and active. + * + * \param [IN] request - The request to verify. + * + * \retval [true - element is in the queue, false - element is not in the queue]. + */ +bool LoRaMacConfirmQueueIsCmdActive( Mlme_t request ); + +/*! + * \brief Handles all callbacks of active requests + * + * \param [IN] mlmeConfirm - Pointer to the generic mlmeConfirm structure. + */ +void LoRaMacConfirmQueueHandleCb( MlmeConfirm_t* mlmeConfirm ); + +/*! + * \brief Query number of elements in the queue. + * + * \retval Number of elements. + */ +uint8_t LoRaMacConfirmQueueGetCnt( void ); + +/*! + * \brief Verify if the confirm queue is full. + * + * \retval [true - queue is full, false - queue is not full]. + */ +bool LoRaMacConfirmQueueIsFull( void ); + +#ifdef __cplusplus +} +#endif + +#endif // __LORAMAC_CONFIRMQUEUE_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacCrypto.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacCrypto.c new file mode 100644 index 0000000000..90510db446 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacCrypto.c @@ -0,0 +1,1551 @@ +/*! + * \file LoRaMacCrypto.c + * + * \brief LoRa MAC layer cryptography implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + */ +#include +#include +#include + +#include "utilities.h" +#include "secure-element.h" + +#include "LoRaMacParser.h" +#include "LoRaMacSerializer.h" +#include "LoRaMacCrypto.h" + +/* + * Frame direction definition for uplink communications + */ +#define UPLINK 0 + +/* + * Frame direction definition for downlink communications + */ +#define DOWNLINK 1 + +/* + * CMAC/AES Message Integrity Code (MIC) Block B0 size + */ +#define MIC_BLOCK_BX_SIZE 16 + +/* + * Number of security context entries + */ +#define NUM_OF_SEC_CTX 5 + +/* + * Maximum size of the message that can be handled by the crypto operations + */ +#define CRYPTO_MAXMESSAGE_SIZE 256 + +/* + * Maximum size of the buffer for crypto operations + */ +#define CRYPTO_BUFFER_SIZE CRYPTO_MAXMESSAGE_SIZE + MIC_BLOCK_BX_SIZE + +/* + * Key-Address item + */ +typedef struct sKeyAddr +{ + /* + * Address identifier + */ + AddressIdentifier_t AddrID; + /* + * Application session key + */ + KeyIdentifier_t AppSkey; + /* + * Network session key + */ + KeyIdentifier_t NwkSkey; + /* + * Rootkey (Multicast only) + */ + KeyIdentifier_t RootKey; +}KeyAddr_t; + +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) +/* + * RJcount0 is a counter incremented with every Type 0 or 2 Rejoin frame transmitted. + */ +static uint16_t RJcount0; +#endif + +/* + * Non volatile module context. + */ +static LoRaMacCryptoNvmData_t* CryptoNvm; + +/* + * Key-Address list + */ +static KeyAddr_t KeyAddrList[NUM_OF_SEC_CTX] = + { + { MULTICAST_0_ADDR, MC_APP_S_KEY_0, MC_NWK_S_KEY_0, MC_KEY_0 }, + { MULTICAST_1_ADDR, MC_APP_S_KEY_1, MC_NWK_S_KEY_1, MC_KEY_1 }, + { MULTICAST_2_ADDR, MC_APP_S_KEY_2, MC_NWK_S_KEY_2, MC_KEY_2 }, + { MULTICAST_3_ADDR, MC_APP_S_KEY_3, MC_NWK_S_KEY_3, MC_KEY_3 }, + { UNICAST_DEV_ADDR, APP_S_KEY, S_NWK_S_INT_KEY, NO_KEY } + }; + +/* + * Encrypts the payload + * + * \param[IN] keyID - Key identifier + * \param[IN] address - Address + * \param[IN] dir - Frame direction ( Uplink or Downlink ) + * \param[IN] frameCounter - Frame counter + * \param[IN] size - Size of data + * \param[IN/OUT] buffer - Data buffer + * \retval - Status of the operation + */ +static LoRaMacCryptoStatus_t PayloadEncrypt( uint8_t* buffer, int16_t size, KeyIdentifier_t keyID, uint32_t address, uint8_t dir, uint32_t frameCounter ) +{ + if( buffer == 0 ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + uint8_t bufferIndex = 0; + uint16_t ctr = 1; + uint8_t sBlock[16] = { 0 }; + uint8_t aBlock[16] = { 0 }; + + aBlock[0] = 0x01; + + aBlock[5] = dir; + + aBlock[6] = address & 0xFF; + aBlock[7] = ( address >> 8 ) & 0xFF; + aBlock[8] = ( address >> 16 ) & 0xFF; + aBlock[9] = ( address >> 24 ) & 0xFF; + + aBlock[10] = frameCounter & 0xFF; + aBlock[11] = ( frameCounter >> 8 ) & 0xFF; + aBlock[12] = ( frameCounter >> 16 ) & 0xFF; + aBlock[13] = ( frameCounter >> 24 ) & 0xFF; + + while( size > 0 ) + { + aBlock[15] = ctr & 0xFF; + ctr++; + if( SecureElementAesEncrypt( aBlock, 16, keyID, sBlock ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + + for( uint8_t i = 0; i < ( ( size > 16 ) ? 16 : size ); i++ ) + { + buffer[bufferIndex + i] = buffer[bufferIndex + i] ^ sBlock[i]; + } + size -= 16; + bufferIndex += 16; + } + + return LORAMAC_CRYPTO_SUCCESS; +} + +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) +/* + * Encrypts the FOpts + * + * \param[IN] address - Address + * \param[IN] dir - Frame direction ( Uplink or Downlink ) + * \param[IN] fCntID - Frame counter identifier + * \param[IN] frameCounter - Frame counter + * \param[IN] size - Size of data + * \param[IN/OUT] buffer - Data buffer + * \retval - Status of the operation + */ +static LoRaMacCryptoStatus_t FOptsEncrypt( uint16_t size, uint32_t address, uint8_t dir, FCntIdentifier_t fCntID, uint32_t frameCounter, uint8_t* buffer ) +{ + if( buffer == 0 ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + uint8_t bufferIndex = 0; + uint8_t sBlock[16] = { 0 }; + uint8_t aBlock[16] = { 0 }; + + aBlock[0] = 0x01; + + if( CryptoNvm->LrWanVersion.Value > 0x01010000 ) + { + // Introduced in LoRaWAN 1.1.1 specification + switch( fCntID ) + { + case FCNT_UP: + { + aBlock[4] = 0x01; + break; + } + case N_FCNT_DOWN: + { + aBlock[4] = 0x01; + break; + } + case A_FCNT_DOWN: + { + aBlock[4] = 0x02; + break; + } + default: + return LORAMAC_CRYPTO_FAIL_PARAM; + } + } + + aBlock[5] = dir; + + aBlock[6] = address & 0xFF; + aBlock[7] = ( address >> 8 ) & 0xFF; + aBlock[8] = ( address >> 16 ) & 0xFF; + aBlock[9] = ( address >> 24 ) & 0xFF; + + aBlock[10] = frameCounter & 0xFF; + aBlock[11] = ( frameCounter >> 8 ) & 0xFF; + aBlock[12] = ( frameCounter >> 16 ) & 0xFF; + aBlock[13] = ( frameCounter >> 24 ) & 0xFF; + + if( CryptoNvm->LrWanVersion.Value > 0x01010000 ) + { + // Introduced in LoRaWAN 1.1.1 specification + aBlock[15] = 0x01; + } + + if( size > 0 ) + { + if( SecureElementAesEncrypt( aBlock, 16, NWK_S_ENC_KEY, sBlock ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + for( uint8_t i = 0; i < size; i++ ) + { + buffer[bufferIndex + i] = buffer[bufferIndex + i] ^ sBlock[i]; + } + } + + return LORAMAC_CRYPTO_SUCCESS; +} +#endif + +/* + * Prepares B0 block for cmac computation. + * + * \param[IN] msgLen - Length of message + * \param[IN] keyID - Key identifier + * \param[IN] isAck - True if it is a acknowledge frame ( Sets ConfFCnt in B0 block ) + * \param[IN] devAddr - Device address + * \param[IN] dir - Frame direction ( Uplink:0, Downlink:1 ) + * \param[IN] fCnt - Frame counter + * \param[IN/OUT] b0 - B0 block + * \retval - Status of the operation + */ +static LoRaMacCryptoStatus_t PrepareB0( uint16_t msgLen, KeyIdentifier_t keyID, bool isAck, uint8_t dir, uint32_t devAddr, uint32_t fCnt, uint8_t* b0 ) +{ + if( b0 == 0 ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + b0[0] = 0x49; + +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) + if( ( isAck == true ) && ( dir == DOWNLINK ) ) + { + // confFCnt contains the frame counter value modulo 2^16 of the "confirmed" uplink or downlink frame that is being acknowledged + uint16_t confFCnt = 0; + + confFCnt = ( uint16_t )( CryptoNvm->FCntList.FCntUp % 65536 ); + + b0[1] = confFCnt & 0xFF; + b0[2] = ( confFCnt >> 8 ) & 0xFF; + } + else +#endif + { + b0[1] = 0x00; + b0[2] = 0x00; + } + + b0[3] = 0x00; + b0[4] = 0x00; + + b0[5] = dir; + + b0[6] = devAddr & 0xFF; + b0[7] = ( devAddr >> 8 ) & 0xFF; + b0[8] = ( devAddr >> 16 ) & 0xFF; + b0[9] = ( devAddr >> 24 ) & 0xFF; + + b0[10] = fCnt & 0xFF; + b0[11] = ( fCnt >> 8 ) & 0xFF; + b0[12] = ( fCnt >> 16 ) & 0xFF; + b0[13] = ( fCnt >> 24 ) & 0xFF; + + b0[14] = 0x00; + + b0[15] = msgLen & 0xFF; + + return LORAMAC_CRYPTO_SUCCESS; +} + +/* + * Computes cmac with adding B0 block in front. + * + * cmac = aes128_cmac(keyID, B0 | msg) + * + * \param[IN] msg - Message to compute the integrity code + * \param[IN] len - Length of message + * \param[IN] keyID - Key identifier + * \param[IN] isAck - True if it is a acknowledge frame ( Sets ConfFCnt in B0 block ) + * \param[IN] devAddr - Device address + * \param[IN] dir - Frame direction ( Uplink:0, Downlink:1 ) + * \param[IN] fCnt - Frame counter + * \param[OUT] cmac - Computed cmac + * \retval - Status of the operation + */ +static LoRaMacCryptoStatus_t ComputeCmacB0( uint8_t* msg, uint16_t len, KeyIdentifier_t keyID, bool isAck, uint8_t dir, uint32_t devAddr, uint32_t fCnt, uint32_t* cmac ) +{ + if( ( msg == 0 ) || ( cmac == 0 ) ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + if( len > CRYPTO_MAXMESSAGE_SIZE ) + { + return LORAMAC_CRYPTO_ERROR_BUF_SIZE; + } + + uint8_t micBuff[MIC_BLOCK_BX_SIZE]; + + // Initialize the first Block + PrepareB0( len, keyID, isAck, dir, devAddr, fCnt, micBuff ); + + if( SecureElementComputeAesCmac( micBuff, msg, len, keyID, cmac ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + return LORAMAC_CRYPTO_SUCCESS; +} + +/*! + * Verifies cmac with adding B0 block in front. + * + * \param[IN] msg - Message to compute the integrity code + * \param[IN] len - Length of message + * \param[IN] keyID - Key identifier + * \param[IN] isAck - True if it is a acknowledge frame ( Sets ConfFCnt in B0 block ) + * \param[IN] devAddr - Device address + * \param[IN] dir - Frame direction ( Uplink:0, Downlink:1 ) + * \param[IN] fCnt - Frame counter + * \param[in] expectedCmac - Expected cmac + * \retval - Status of the operation + */ +static LoRaMacCryptoStatus_t VerifyCmacB0( uint8_t* msg, uint16_t len, KeyIdentifier_t keyID, bool isAck, uint8_t dir, uint32_t devAddr, uint32_t fCnt, uint32_t expectedCmac ) +{ + if( msg == 0 ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + if( len > CRYPTO_MAXMESSAGE_SIZE ) + { + return LORAMAC_CRYPTO_ERROR_BUF_SIZE; + } + + uint8_t micBuff[CRYPTO_BUFFER_SIZE]; + memset1( micBuff, 0, CRYPTO_BUFFER_SIZE ); + + // Initialize the first Block + PrepareB0( len, keyID, isAck, dir, devAddr, fCnt, micBuff ); + + // Copy the given data to the mic computation buffer + memcpy1( ( micBuff + MIC_BLOCK_BX_SIZE ), msg, len ); + + SecureElementStatus_t retval = SECURE_ELEMENT_ERROR; + retval = SecureElementVerifyAesCmac( micBuff, ( len + MIC_BLOCK_BX_SIZE ), expectedCmac, keyID ); + + if( retval == SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_SUCCESS; + } + else if( retval == SECURE_ELEMENT_FAIL_CMAC ) + { + return LORAMAC_CRYPTO_FAIL_MIC; + } + + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; +} + +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) +/* + * Prpares B1 block for cmac computation. + * + * \param[IN] msgLen - Length of message + * \param[IN] keyID - Key identifier + * \param[IN] isAck - True if it is a acknowledge frame ( Sets ConfFCnt in B0 block ) + * \param[IN] txDr - Data rate used for the transmission + * \param[IN] txCh - Index of the channel used for the transmission + * \param[IN] devAddr - Device address + * \param[IN] fCntUp - Frame counter + * \param[IN/OUT] b0 - B0 block + * \retval - Status of the operation + */ +static LoRaMacCryptoStatus_t PrepareB1( uint16_t msgLen, KeyIdentifier_t keyID, bool isAck, uint8_t txDr, uint8_t txCh, uint32_t devAddr, uint32_t fCntUp, uint8_t* b1 ) +{ + if( b1 == 0 ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + b1[0] = 0x49; + + if( isAck == true ) + { + // confFCnt contains the frame counter value modulo 2^16 of the "confirmed" uplink frame that is being acknowledged + uint16_t confFCnt = ( uint16_t )( CryptoNvm->LastDownFCnt % 65536 ); + b1[1] = confFCnt & 0xFF; + b1[2] = ( confFCnt >> 8 ) & 0xFF; + } + else + { + b1[1] = 0x00; + b1[2] = 0x00; + } + + b1[3] = txDr; + b1[4] = txCh; + b1[5] = UPLINK; // dir = Uplink + + b1[6] = devAddr & 0xFF; + b1[7] = ( devAddr >> 8 ) & 0xFF; + b1[8] = ( devAddr >> 16 ) & 0xFF; + b1[9] = ( devAddr >> 24 ) & 0xFF; + + b1[10] = fCntUp & 0xFF; + b1[11] = ( fCntUp >> 8 ) & 0xFF; + b1[12] = ( fCntUp >> 16 ) & 0xFF; + b1[13] = ( fCntUp >> 24 ) & 0xFF; + + b1[14] = 0x00; + + b1[15] = msgLen & 0xFF; + + return LORAMAC_CRYPTO_SUCCESS; +} + +/* + * Computes cmac with adding B1 block in front ( only for Uplink frames LoRaWAN 1.1 ) + * + * cmac = aes128_cmac(keyID, B1 | msg) + * + * \param[IN] msg - Message to calculate the Integrity code + * \param[IN] len - Length of message + * \param[IN] keyID - Key identifier + * \param[IN] isAck - True if it is a acknowledge frame ( Sets ConfFCnt in B0 block ) + * \param[IN] txDr - Data rate used for the transmission + * \param[IN] txCh - Index of the channel used for the transmission + * \param[IN] devAddr - Device address + * \param[IN] fCntUp - Uplink Frame counter + * \param[OUT] cmac - Computed cmac + * \retval - Status of the operation + */ +static LoRaMacCryptoStatus_t ComputeCmacB1( uint8_t* msg, uint16_t len, KeyIdentifier_t keyID, bool isAck, uint8_t txDr, uint8_t txCh, uint32_t devAddr, uint32_t fCntUp, uint32_t* cmac ) +{ + if( ( msg == 0 ) || ( cmac == 0 ) ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + if( len > CRYPTO_MAXMESSAGE_SIZE ) + { + return LORAMAC_CRYPTO_ERROR_BUF_SIZE; + } + + uint8_t micBuff[MIC_BLOCK_BX_SIZE]; + + // Initialize the first Block + PrepareB1( len, keyID, isAck, txDr, txCh, devAddr, fCntUp, micBuff ); + + if( SecureElementComputeAesCmac( micBuff, msg, len, keyID, cmac ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + return LORAMAC_CRYPTO_SUCCESS; +} +#endif + +/* + * Gets security item from list. + * + * \param[IN] addrID - Address identifier + * \param[OUT] keyItem - Key item reference + * \retval - Status of the operation + */ +static LoRaMacCryptoStatus_t GetKeyAddrItem( AddressIdentifier_t addrID, KeyAddr_t** item ) +{ + for( uint8_t i = 0; i < NUM_OF_SEC_CTX; i++ ) + { + if( KeyAddrList[i].AddrID == addrID ) + { + *item = &( KeyAddrList[i] ); + return LORAMAC_CRYPTO_SUCCESS; + } + } + return LORAMAC_CRYPTO_ERROR_INVALID_ADDR_ID; +} + +/* + * Derives a session key as of LoRaWAN versions prior to 1.1.0 + * + * \param[IN] keyID - Key Identifier for the key to be calculated + * \param[IN] joinNonce - Sever nonce + * \param[IN] netID - Network Identifier + * \param[IN] deviceNonce - Device nonce + * \retval - Status of the operation + */ +static LoRaMacCryptoStatus_t DeriveSessionKey10x( KeyIdentifier_t keyID, uint32_t joinNonce, uint32_t netID, uint16_t devNonce ) +{ + uint8_t compBase[16] = { 0 }; + + switch( keyID ) + { + case F_NWK_S_INT_KEY: + case S_NWK_S_INT_KEY: + case NWK_S_ENC_KEY: + compBase[0] = 0x01; + break; + case APP_S_KEY: + compBase[0] = 0x02; + break; + default: + return LORAMAC_CRYPTO_ERROR_INVALID_KEY_ID; + } + + compBase[1] = ( uint8_t )( ( joinNonce >> 0 ) & 0xFF ); + compBase[2] = ( uint8_t )( ( joinNonce >> 8 ) & 0xFF ); + compBase[3] = ( uint8_t )( ( joinNonce >> 16 ) & 0xFF ); + + compBase[4] = ( uint8_t )( ( netID >> 0 ) & 0xFF ); + compBase[5] = ( uint8_t )( ( netID >> 8 ) & 0xFF ); + compBase[6] = ( uint8_t )( ( netID >> 16 ) & 0xFF ); + + compBase[7] = ( uint8_t )( ( devNonce >> 0 ) & 0xFF ); + compBase[8] = ( uint8_t )( ( devNonce >> 8 ) & 0xFF ); + + if( SecureElementDeriveAndStoreKey( compBase, NWK_KEY, keyID ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + + return LORAMAC_CRYPTO_SUCCESS; +} + +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) +/* + * Derives a session key as of LoRaWAN 1.1.0 + * + * \param[IN] keyID - Key Identifier for the key to be calculated + * \param[IN] joinNonce - Sever nonce + * \param[IN] joinEUI - Join Server EUI + * \param[IN] deviceNonce - Device nonce + * \retval - Status of the operation + */ +static LoRaMacCryptoStatus_t DeriveSessionKey11x( KeyIdentifier_t keyID, uint32_t joinNonce, uint8_t* joinEUI, uint16_t devNonce ) +{ + if( joinEUI == 0 ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + uint8_t compBase[16] = { 0 }; + KeyIdentifier_t rootKeyId = NWK_KEY; + + switch( keyID ) + { + case F_NWK_S_INT_KEY: + compBase[0] = 0x01; + break; + case S_NWK_S_INT_KEY: + compBase[0] = 0x03; + break; + case NWK_S_ENC_KEY: + compBase[0] = 0x04; + break; + case APP_S_KEY: + rootKeyId = APP_KEY; + compBase[0] = 0x02; + break; + default: + return LORAMAC_CRYPTO_ERROR_INVALID_KEY_ID; + } + + compBase[1] = ( uint8_t )( ( joinNonce >> 0 ) & 0xFF ); + compBase[2] = ( uint8_t )( ( joinNonce >> 8 ) & 0xFF ); + compBase[3] = ( uint8_t )( ( joinNonce >> 16 ) & 0xFF ); + + memcpyr( compBase + 4, joinEUI, 8 ); + + compBase[12] = ( uint8_t )( ( devNonce >> 0 ) & 0xFF ); + compBase[13] = ( uint8_t )( ( devNonce >> 8 ) & 0xFF ); + + if( SecureElementDeriveAndStoreKey( compBase, rootKeyId, keyID ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + + return LORAMAC_CRYPTO_SUCCESS; +} + +/* + * Derives a life time session key (JSIntKey or JSEncKey) as of LoRaWAN 1.1.0 + * + * \param[IN] keyID - Key Identifier for the key to be calculated + * \param[IN] devEUI - Device EUI + * \retval - Status of the operation + */ +static LoRaMacCryptoStatus_t DeriveLifeTimeSessionKey( KeyIdentifier_t keyID, uint8_t* devEUI ) +{ + if( devEUI == 0 ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + uint8_t compBase[16] = { 0 }; + + switch( keyID ) + { + case J_S_INT_KEY: + compBase[0] = 0x06; + break; + case J_S_ENC_KEY: + compBase[0] = 0x05; + break; + default: + return LORAMAC_CRYPTO_ERROR_INVALID_KEY_ID; + } + + memcpyr( compBase + 1, devEUI, 8 ); + + if( SecureElementDeriveAndStoreKey( compBase, NWK_KEY, keyID ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + + return LORAMAC_CRYPTO_SUCCESS; +} +#endif + +/* + * Gets the last received frame counter + * + * \param[IN] fCntID - Frame counter identifier + * \param[IN] lastDown - Last downlink counter value + * + * \retval - Status of the operation + */ +static LoRaMacCryptoStatus_t GetLastFcntDown( FCntIdentifier_t fCntID, uint32_t* lastDown ) +{ + if( lastDown == NULL ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + switch( fCntID ) + { + case N_FCNT_DOWN: + *lastDown = CryptoNvm->FCntList.NFCntDown; + break; + case A_FCNT_DOWN: + *lastDown = CryptoNvm->FCntList.AFCntDown; + break; + case FCNT_DOWN: + *lastDown = CryptoNvm->FCntList.FCntDown; + break; +#if ( LORAMAC_MAX_MC_CTX > 0 ) + case MC_FCNT_DOWN_0: + *lastDown = CryptoNvm->FCntList.McFCntDown[0]; + break; +#endif +#if ( LORAMAC_MAX_MC_CTX > 1 ) + case MC_FCNT_DOWN_1: + *lastDown = CryptoNvm->FCntList.McFCntDown[1]; + break; +#endif +#if ( LORAMAC_MAX_MC_CTX > 2 ) + case MC_FCNT_DOWN_2: + *lastDown = CryptoNvm->FCntList.McFCntDown[2]; + break; +#endif +#if ( LORAMAC_MAX_MC_CTX > 3 ) + case MC_FCNT_DOWN_3: + *lastDown = CryptoNvm->FCntList.McFCntDown[3]; + break; +#endif + default: + return LORAMAC_CRYPTO_FAIL_FCNT_ID; + } + return LORAMAC_CRYPTO_SUCCESS; +} + +/* + * Checks the downlink counter value + * + * \param[IN] fCntID - Frame counter identifier + * \param[IN] currentDown - Current downlink counter value + * + * \retval - Status of the operation + */ +static bool CheckFCntDown( FCntIdentifier_t fCntID, uint32_t currentDown ) +{ + uint32_t lastDown = 0; + if( GetLastFcntDown( fCntID, &lastDown ) != LORAMAC_CRYPTO_SUCCESS ) + { + return false; + } + if( ( currentDown > lastDown ) || + // For LoRaWAN 1.0.X only. Allow downlink frames of 0 + ( lastDown == FCNT_DOWN_INITIAL_VALUE ) ) + { + return true; + } + else + { + return false; + } +} + +/*! + * Updates the reference downlink counter + * + * \param[IN] fCntID - Frame counter identifier + * \param[IN] currentDown - Current downlink counter value + * + * \retval - Status of the operation + */ +static void UpdateFCntDown( FCntIdentifier_t fCntID, uint32_t currentDown ) +{ + switch( fCntID ) + { + case N_FCNT_DOWN: + CryptoNvm->FCntList.NFCntDown = currentDown; + CryptoNvm->LastDownFCnt = currentDown; + break; + case A_FCNT_DOWN: + CryptoNvm->FCntList.AFCntDown = currentDown; + CryptoNvm->LastDownFCnt = currentDown; + break; + case FCNT_DOWN: + CryptoNvm->FCntList.FCntDown = currentDown; + CryptoNvm->LastDownFCnt = currentDown; + break; +#if ( LORAMAC_MAX_MC_CTX > 0 ) + case MC_FCNT_DOWN_0: + CryptoNvm->FCntList.McFCntDown[0] = currentDown; + break; +#endif +#if ( LORAMAC_MAX_MC_CTX > 1 ) + case MC_FCNT_DOWN_1: + CryptoNvm->FCntList.McFCntDown[1] = currentDown; + break; +#endif +#if ( LORAMAC_MAX_MC_CTX > 2 ) + case MC_FCNT_DOWN_2: + CryptoNvm->FCntList.McFCntDown[2] = currentDown; + break; +#endif +#if ( LORAMAC_MAX_MC_CTX > 3 ) + case MC_FCNT_DOWN_3: + CryptoNvm->FCntList.McFCntDown[3] = currentDown; + break; +#endif + default: + break; + } +} + +/*! + * Resets the frame counters + */ +static void ResetFCnts( void ) +{ + CryptoNvm->FCntList.FCntUp = 0; + CryptoNvm->FCntList.NFCntDown = FCNT_DOWN_INITIAL_VALUE; + CryptoNvm->FCntList.AFCntDown = FCNT_DOWN_INITIAL_VALUE; + CryptoNvm->FCntList.FCntDown = FCNT_DOWN_INITIAL_VALUE; + CryptoNvm->LastDownFCnt = CryptoNvm->FCntList.FCntDown; + + for( int32_t i = 0; i < LORAMAC_MAX_MC_CTX; i++ ) + { + CryptoNvm->FCntList.McFCntDown[i] = FCNT_DOWN_INITIAL_VALUE; + } +} + +static bool IsJoinNonce10xOk( uint32_t joinNonce ) +{ +#if( USE_10X_JOIN_NONCE_COUNTER_CHECK == 1 ) + // Check if the JoinNonce is greater as the previous one + return ( joinNonce > CryptoNvm->JoinNonce ) ? true : false; +#else + // Check if the JoinNonce is different from the previous one + return( joinNonce != CryptoNvm->JoinNonce ) ? true : false; +#endif +} + +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) +static bool IsJoinNonce11xOk( uint32_t joinNonce ) +{ + return ( joinNonce > CryptoNvm->JoinNonce ) ? true : false; +} +#endif + +/* + * API functions + */ +LoRaMacCryptoStatus_t LoRaMacCryptoInit( LoRaMacCryptoNvmData_t* nvm ) +{ + if( nvm == NULL ) + { + return LORAMAC_CRYPTO_FAIL_PARAM; + } + + // Assign non volatile context + CryptoNvm = nvm; + + // Initialize with default + memset1( ( uint8_t* )CryptoNvm, 0, sizeof( LoRaMacCryptoNvmData_t ) ); + + // Set default LoRaWAN version + CryptoNvm->LrWanVersion.Fields.Major = 1; + CryptoNvm->LrWanVersion.Fields.Minor = 1; + CryptoNvm->LrWanVersion.Fields.Patch = 1; + CryptoNvm->LrWanVersion.Fields.Revision = 0; + + // Reset frame counters + ResetFCnts( ); + + return LORAMAC_CRYPTO_SUCCESS; +} + +LoRaMacCryptoStatus_t LoRaMacCryptoSetLrWanVersion( Version_t version ) +{ + CryptoNvm->LrWanVersion = version; + return LORAMAC_CRYPTO_SUCCESS; +} + +LoRaMacCryptoStatus_t LoRaMacCryptoGetFCntUp( uint32_t* currentUp ) +{ + if( currentUp == NULL ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + *currentUp = CryptoNvm->FCntList.FCntUp + 1; + + return LORAMAC_CRYPTO_SUCCESS; +} + +LoRaMacCryptoStatus_t LoRaMacCryptoGetFCntDown( FCntIdentifier_t fCntID, uint32_t frameFcnt, uint32_t* currentDown ) +{ + uint32_t lastDown = 0; + int32_t fCntDiff = 0; + LoRaMacCryptoStatus_t cryptoStatus = LORAMAC_CRYPTO_ERROR; + + if( currentDown == NULL ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + cryptoStatus = GetLastFcntDown( fCntID, &lastDown ); + if( cryptoStatus != LORAMAC_CRYPTO_SUCCESS ) + { + return cryptoStatus; + } + + // For LoRaWAN 1.0.X only, allow downlink frames of 0 + if( lastDown == FCNT_DOWN_INITIAL_VALUE ) + { + *currentDown = frameFcnt; + } + else + { + // Add difference, consider roll-over + fCntDiff = ( int32_t )( ( int64_t )frameFcnt - ( int64_t )( lastDown & 0x0000FFFF ) ); + + if( fCntDiff > 0 ) + { // Positive difference + *currentDown = lastDown + fCntDiff; + } + else if( fCntDiff == 0 ) + { // Duplicate FCnt value, keep the current value. + *currentDown = lastDown; + return LORAMAC_CRYPTO_FAIL_FCNT_DUPLICATED; + } + else + { // Negative difference, assume a roll-over of one uint16_t + *currentDown = ( lastDown & 0xFFFF0000 ) + 0x10000 + frameFcnt; + } + } + + return LORAMAC_CRYPTO_SUCCESS; +} + +LoRaMacCryptoStatus_t LoRaMacCryptoGetRJcount( FCntIdentifier_t fCntID, uint16_t* rJcount ) +{ +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) + if( rJcount == 0 ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + switch( fCntID ) + { + case RJ_COUNT_0: + *rJcount = RJcount0 + 1; + break; + case RJ_COUNT_1: + *rJcount = CryptoNvm->FCntList.RJcount1 + 1; + break; + default: + return LORAMAC_CRYPTO_FAIL_FCNT_ID; + } + return LORAMAC_CRYPTO_SUCCESS; +#else + return LORAMAC_CRYPTO_ERROR; +#endif +} + +LoRaMacCryptoStatus_t LoRaMacCryptoSetMulticastReference( MulticastCtx_t* multicastList ) +{ + if( multicastList == NULL ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + for( int32_t i = 0; i < LORAMAC_MAX_MC_CTX; i++ ) + { + multicastList[i].DownLinkCounter = &CryptoNvm->FCntList.McFCntDown[i]; + } + + return LORAMAC_CRYPTO_SUCCESS; +} + +LoRaMacCryptoStatus_t LoRaMacCryptoSetKey( KeyIdentifier_t keyID, uint8_t* key ) +{ + if( SecureElementSetKey( keyID, key ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + if( keyID == APP_KEY ) + { + // Derive lifetime keys + if( LoRaMacCryptoDeriveMcRootKey( CryptoNvm->LrWanVersion.Fields.Minor, keyID ) != LORAMAC_CRYPTO_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + if( LoRaMacCryptoDeriveMcKEKey( MC_ROOT_KEY ) != LORAMAC_CRYPTO_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + } + return LORAMAC_CRYPTO_SUCCESS; +} + +LoRaMacCryptoStatus_t LoRaMacCryptoPrepareJoinRequest( LoRaMacMessageJoinRequest_t* macMsg ) +{ + if( macMsg == 0 ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + KeyIdentifier_t micComputationKeyID = NWK_KEY; + + // Add device nonce +#if ( USE_RANDOM_DEV_NONCE == 1 ) + uint32_t devNonce = 0; + SecureElementRandomNumber( &devNonce ); + CryptoNvm->DevNonce = devNonce; +#else + CryptoNvm->DevNonce++; +#endif + macMsg->DevNonce = CryptoNvm->DevNonce; + +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) + // Derive lifetime session keys + if( DeriveLifeTimeSessionKey( J_S_INT_KEY, macMsg->DevEUI ) != LORAMAC_CRYPTO_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR; + } + if( DeriveLifeTimeSessionKey( J_S_ENC_KEY, macMsg->DevEUI ) != LORAMAC_CRYPTO_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR; + } +#endif + + // Serialize message + if( LoRaMacSerializerJoinRequest( macMsg ) != LORAMAC_SERIALIZER_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SERIALIZER; + } + + // Compute mic + if( SecureElementComputeAesCmac( NULL, macMsg->Buffer, ( LORAMAC_JOIN_REQ_MSG_SIZE - LORAMAC_MIC_FIELD_SIZE ), micComputationKeyID, &macMsg->MIC ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + + // Reserialize message to add the MIC + if( LoRaMacSerializerJoinRequest( macMsg ) != LORAMAC_SERIALIZER_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SERIALIZER; + } + + return LORAMAC_CRYPTO_SUCCESS; +} + +LoRaMacCryptoStatus_t LoRaMacCryptoPrepareReJoinType1( LoRaMacMessageReJoinType1_t* macMsg ) +{ +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) + if( macMsg == 0 ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + // Check for RJcount1 overflow + if( CryptoNvm->FCntList.RJcount1 == 65535 ) + { + return LORAMAC_CRYPTO_ERROR_RJCOUNT1_OVERFLOW; + } + + // Serialize message + if( LoRaMacSerializerReJoinType1( macMsg ) != LORAMAC_SERIALIZER_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SERIALIZER; + } + + // Compute mic + // cmac = aes128_cmac(JSIntKey, MHDR | RejoinType | JoinEUI| DevEUI | RJcount1) + if( SecureElementComputeAesCmac( NULL, macMsg->Buffer, ( LORAMAC_RE_JOIN_1_MSG_SIZE - LORAMAC_MIC_FIELD_SIZE ), J_S_INT_KEY, &macMsg->MIC ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + + // Reserialize message to add the MIC + if( LoRaMacSerializerReJoinType1( macMsg ) != LORAMAC_SERIALIZER_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SERIALIZER; + } + + // Increment RJcount1 + CryptoNvm->FCntList.RJcount1++; + + return LORAMAC_CRYPTO_SUCCESS; +#else + return LORAMAC_CRYPTO_ERROR; +#endif +} + +LoRaMacCryptoStatus_t LoRaMacCryptoPrepareReJoinType0or2( LoRaMacMessageReJoinType0or2_t* macMsg ) +{ +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) + if( macMsg == 0 ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + // Check for RJcount0 overflow + if( RJcount0 == 65535 ) + { + return LORAMAC_CRYPTO_FAIL_RJCOUNT0_OVERFLOW; + } + + // Serialize message + if( LoRaMacSerializerReJoinType0or2( macMsg ) != LORAMAC_SERIALIZER_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SERIALIZER; + } + + // Compute mic + // cmac = aes128_cmac(SNwkSIntKey, MHDR | Rejoin Type | NetID | DevEUI | RJcount0) + if( SecureElementComputeAesCmac( NULL, macMsg->Buffer, ( LORAMAC_RE_JOIN_0_2_MSG_SIZE - LORAMAC_MIC_FIELD_SIZE ), S_NWK_S_INT_KEY, &macMsg->MIC ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + + // Re-serialize message to add the MIC + if( LoRaMacSerializerReJoinType0or2( macMsg ) != LORAMAC_SERIALIZER_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SERIALIZER; + } + + // Increment RJcount0 + RJcount0++; + + return LORAMAC_CRYPTO_SUCCESS; +#else + return LORAMAC_CRYPTO_ERROR; +#endif +} + +LoRaMacCryptoStatus_t LoRaMacCryptoHandleJoinAccept( JoinReqIdentifier_t joinReqType, uint8_t* joinEUI, LoRaMacMessageJoinAccept_t* macMsg ) +{ + if( ( macMsg == 0 ) || ( joinEUI == 0 ) ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + LoRaMacCryptoStatus_t retval = LORAMAC_CRYPTO_ERROR; + uint8_t decJoinAccept[LORAMAC_JOIN_ACCEPT_FRAME_MAX_SIZE] = { 0 }; + uint8_t versionMinor = 0; + uint16_t nonce = CryptoNvm->DevNonce; + + // Nonce selection depending on JoinReqType + // JOIN_REQ : CryptoNvm->DevNonce + // REJOIN_REQ_0 : RJcount0 + // REJOIN_REQ_1 : CryptoCtx.RJcount1 + // REJOIN_REQ_2 : RJcount0 + if( joinReqType == JOIN_REQ ) + { + // Nothing to be done + } +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) + else + { + // If Join-accept is a reply to a rejoin, the RJcount(0 or 1) replaces DevNonce in the key derivation process. + if( ( joinReqType == REJOIN_REQ_0 ) || ( joinReqType == REJOIN_REQ_2 ) ) + { + nonce = RJcount0; + } + else + { + nonce = CryptoNvm->FCntList.RJcount1; + } + } +#endif + + if( SecureElementProcessJoinAccept( joinReqType, joinEUI, nonce, macMsg->Buffer, + macMsg->BufSize, decJoinAccept, + &versionMinor ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + + memcpy1( macMsg->Buffer, decJoinAccept, macMsg->BufSize ); + + // Parse the message + if( LoRaMacParserJoinAccept( macMsg ) != LORAMAC_PARSER_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_PARSER; + } + + uint32_t currentJoinNonce; + bool isJoinNonceOk = false; + + currentJoinNonce = ( uint32_t )macMsg->JoinNonce[0]; + currentJoinNonce |= ( ( uint32_t )macMsg->JoinNonce[1] << 8 ); + currentJoinNonce |= ( ( uint32_t )macMsg->JoinNonce[2] << 16 ); + +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) + if( versionMinor == 1 ) + { + isJoinNonceOk = IsJoinNonce11xOk( currentJoinNonce ); + } + else +#endif + { + isJoinNonceOk = IsJoinNonce10xOk( currentJoinNonce ); + } + + if( isJoinNonceOk == true ) + { + CryptoNvm->JoinNonce = currentJoinNonce; + } + else + { + return LORAMAC_CRYPTO_FAIL_JOIN_NONCE; + } + + // Derive lifetime keys + retval = LoRaMacCryptoDeriveMcRootKey( versionMinor, APP_KEY ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + + retval = LoRaMacCryptoDeriveMcKEKey( MC_ROOT_KEY ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) + if( versionMinor == 1 ) + { + // Operating in LoRaWAN 1.1.x mode + + retval = DeriveSessionKey11x( F_NWK_S_INT_KEY, currentJoinNonce, joinEUI, nonce ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + + retval = DeriveSessionKey11x( S_NWK_S_INT_KEY, currentJoinNonce, joinEUI, nonce ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + + retval = DeriveSessionKey11x( NWK_S_ENC_KEY, currentJoinNonce, joinEUI, nonce ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + + retval = DeriveSessionKey11x( APP_S_KEY, currentJoinNonce, joinEUI, nonce ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + } + else +#endif + { + // Operating in LoRaWAN 1.0.x mode + + uint32_t netID; + + netID = ( uint32_t )macMsg->NetID[0]; + netID |= ( ( uint32_t )macMsg->NetID[1] << 8 ); + netID |= ( ( uint32_t )macMsg->NetID[2] << 16 ); + + retval = DeriveSessionKey10x( APP_S_KEY, currentJoinNonce, netID, nonce ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + + retval = DeriveSessionKey10x( NWK_S_ENC_KEY, currentJoinNonce, netID, nonce ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + + retval = DeriveSessionKey10x( F_NWK_S_INT_KEY, currentJoinNonce, netID, nonce ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + + retval = DeriveSessionKey10x( S_NWK_S_INT_KEY, currentJoinNonce, netID, nonce ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + } + + // Join-Accept is successfully processed + // Save LoRaWAN specification version + CryptoNvm->LrWanVersion.Fields.Minor = versionMinor; + + // Reset frame counters +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) + RJcount0 = 0; +#endif + CryptoNvm->FCntList.FCntUp = 0; + CryptoNvm->FCntList.FCntDown = FCNT_DOWN_INITIAL_VALUE; + CryptoNvm->FCntList.NFCntDown = FCNT_DOWN_INITIAL_VALUE; + CryptoNvm->FCntList.AFCntDown = FCNT_DOWN_INITIAL_VALUE; + + return LORAMAC_CRYPTO_SUCCESS; +} + +LoRaMacCryptoStatus_t LoRaMacCryptoSecureMessage( uint32_t fCntUp, uint8_t txDr, uint8_t txCh, LoRaMacMessageData_t* macMsg ) +{ + LoRaMacCryptoStatus_t retval = LORAMAC_CRYPTO_ERROR; + KeyIdentifier_t payloadDecryptionKeyID = APP_S_KEY; + + if( macMsg == NULL ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + if( fCntUp < CryptoNvm->FCntList.FCntUp ) + { + return LORAMAC_CRYPTO_FAIL_FCNT_SMALLER; + } + + // Encrypt payload + if( macMsg->FPort == 0 ) + { + // Use network session key + payloadDecryptionKeyID = NWK_S_ENC_KEY; + } + + if( fCntUp > CryptoNvm->FCntList.FCntUp ) + { + retval = PayloadEncrypt( macMsg->FRMPayload, macMsg->FRMPayloadSize, payloadDecryptionKeyID, macMsg->FHDR.DevAddr, UPLINK, fCntUp ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) + if( CryptoNvm->LrWanVersion.Fields.Minor == 1 ) + { + // Encrypt FOpts + retval = FOptsEncrypt( macMsg->FHDR.FCtrl.Bits.FOptsLen, macMsg->FHDR.DevAddr, UPLINK, FCNT_UP, fCntUp, macMsg->FHDR.FOpts ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + } +#endif + } + + // Serialize message + if( LoRaMacSerializerData( macMsg ) != LORAMAC_SERIALIZER_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SERIALIZER; + } + + // Compute mic +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) + if( CryptoNvm->LrWanVersion.Fields.Minor == 1 ) + { + uint32_t cmacS = 0; + uint32_t cmacF = 0; + + // cmacS = aes128_cmac(SNwkSIntKey, B1 | msg) + retval = ComputeCmacB1( macMsg->Buffer, ( macMsg->BufSize - LORAMAC_MIC_FIELD_SIZE ), S_NWK_S_INT_KEY, macMsg->FHDR.FCtrl.Bits.Ack, txDr, txCh, macMsg->FHDR.DevAddr, fCntUp, &cmacS ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + //cmacF = aes128_cmac(FNwkSIntKey, B0 | msg) + retval = ComputeCmacB0( macMsg->Buffer, ( macMsg->BufSize - LORAMAC_MIC_FIELD_SIZE ), F_NWK_S_INT_KEY, macMsg->FHDR.FCtrl.Bits.Ack, UPLINK, macMsg->FHDR.DevAddr, fCntUp, &cmacF ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + // MIC = cmacS[0..1] | cmacF[0..1] + macMsg->MIC = ( ( cmacF << 16 ) & 0xFFFF0000 ) | ( cmacS & 0x0000FFFF ); + } + else +#endif + { + // MIC = cmacF[0..3] + // The IsAck parameter is every time false since the ConfFCnt field is not used in legacy mode. + retval = ComputeCmacB0( macMsg->Buffer, ( macMsg->BufSize - LORAMAC_MIC_FIELD_SIZE ), NWK_S_ENC_KEY, false, UPLINK, macMsg->FHDR.DevAddr, fCntUp, &macMsg->MIC ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + } + + // Re-serialize message to add the MIC + if( LoRaMacSerializerData( macMsg ) != LORAMAC_SERIALIZER_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SERIALIZER; + } + + CryptoNvm->FCntList.FCntUp = fCntUp; + + return LORAMAC_CRYPTO_SUCCESS; +} + +LoRaMacCryptoStatus_t LoRaMacCryptoUnsecureMessage( AddressIdentifier_t addrID, uint32_t address, FCntIdentifier_t fCntID, uint32_t fCntDown, LoRaMacMessageData_t* macMsg ) +{ + if( macMsg == 0 ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + if( CheckFCntDown( fCntID, fCntDown ) == false ) + { + return LORAMAC_CRYPTO_FAIL_FCNT_SMALLER; + } + + LoRaMacCryptoStatus_t retval = LORAMAC_CRYPTO_ERROR; + KeyIdentifier_t payloadDecryptionKeyID = APP_S_KEY; + KeyIdentifier_t micComputationKeyID = S_NWK_S_INT_KEY; + KeyAddr_t* curItem; + + // Parse the message + if( LoRaMacParserData( macMsg ) != LORAMAC_PARSER_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_PARSER; + } + + // Determine current security context + retval = GetKeyAddrItem( addrID, &curItem ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + + payloadDecryptionKeyID = curItem->AppSkey; + micComputationKeyID = curItem->NwkSkey; + + // Check if it is our address + if( address != macMsg->FHDR.DevAddr ) + { + return LORAMAC_CRYPTO_FAIL_ADDRESS; + } + + // Compute mic + bool isAck = macMsg->FHDR.FCtrl.Bits.Ack; + if( CryptoNvm->LrWanVersion.Fields.Minor == 0 ) + { + // In legacy mode the IsAck parameter is forced to be false since the ConfFCnt field is not used. + isAck = false; + } + + // Verify mic + retval = VerifyCmacB0( macMsg->Buffer, ( macMsg->BufSize - LORAMAC_MIC_FIELD_SIZE ), micComputationKeyID, isAck, DOWNLINK, address, fCntDown, macMsg->MIC ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + + // Decrypt payload + if( macMsg->FPort == 0 ) + { + // Use network session encryption key + payloadDecryptionKeyID = NWK_S_ENC_KEY; + } + retval = PayloadEncrypt( macMsg->FRMPayload, macMsg->FRMPayloadSize, payloadDecryptionKeyID, address, DOWNLINK, fCntDown ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) + if( CryptoNvm->LrWanVersion.Fields.Minor == 1 ) + { + if( addrID == UNICAST_DEV_ADDR ) + { + // Decrypt FOpts + retval = FOptsEncrypt( macMsg->FHDR.FCtrl.Bits.FOptsLen, address, DOWNLINK, fCntID, fCntDown, macMsg->FHDR.FOpts ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + } + } +#endif + + UpdateFCntDown( fCntID, fCntDown ); + + return LORAMAC_CRYPTO_SUCCESS; +} + +LoRaMacCryptoStatus_t LoRaMacCryptoDeriveMcRootKey( uint8_t versionMinor, KeyIdentifier_t keyID ) +{ + // Prevent other keys than AppKey + if( keyID != APP_KEY ) + { + return LORAMAC_CRYPTO_ERROR_INVALID_KEY_ID; + } + uint8_t compBase[16] = { 0 }; + + if( versionMinor == 1 ) + { + compBase[0] = 0x20; + } + if( SecureElementDeriveAndStoreKey( compBase, keyID, MC_ROOT_KEY ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + + return LORAMAC_CRYPTO_SUCCESS; +} + +LoRaMacCryptoStatus_t LoRaMacCryptoDeriveMcKEKey( KeyIdentifier_t keyID ) +{ + // Prevent other keys than McRootKey + if( keyID != MC_ROOT_KEY ) + { + return LORAMAC_CRYPTO_ERROR_INVALID_KEY_ID; + } + uint8_t compBase[16] = { 0 }; + + if( SecureElementDeriveAndStoreKey( compBase, keyID, MC_KE_KEY ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + + return LORAMAC_CRYPTO_SUCCESS; +} + +LoRaMacCryptoStatus_t LoRaMacCryptoDeriveMcSessionKeyPair( AddressIdentifier_t addrID, uint32_t mcAddr ) +{ + if( mcAddr == 0 ) + { + return LORAMAC_CRYPTO_ERROR_NPE; + } + + LoRaMacCryptoStatus_t retval = LORAMAC_CRYPTO_ERROR; + + // Determine current security context + KeyAddr_t* curItem; + retval = GetKeyAddrItem( addrID, &curItem ); + if( retval != LORAMAC_CRYPTO_SUCCESS ) + { + return retval; + } + + // McAppSKey = aes128_encrypt(McKey, 0x01 | McAddr | pad16) + // McNwkSKey = aes128_encrypt(McKey, 0x02 | McAddr | pad16) + + uint8_t compBaseAppS[16] = { 0 }; + uint8_t compBaseNwkS[16] = { 0 }; + + compBaseAppS[0] = 0x01; + compBaseAppS[1] = mcAddr & 0xFF; + compBaseAppS[2] = ( mcAddr >> 8 ) & 0xFF; + compBaseAppS[3] = ( mcAddr >> 16 ) & 0xFF; + compBaseAppS[4] = ( mcAddr >> 24 ) & 0xFF; + + compBaseNwkS[0] = 0x02; + compBaseNwkS[1] = mcAddr & 0xFF; + compBaseNwkS[2] = ( mcAddr >> 8 ) & 0xFF; + compBaseNwkS[3] = ( mcAddr >> 16 ) & 0xFF; + compBaseNwkS[4] = ( mcAddr >> 24 ) & 0xFF; + + if( SecureElementDeriveAndStoreKey( compBaseAppS, curItem->RootKey, curItem->AppSkey ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + + if( SecureElementDeriveAndStoreKey( compBaseNwkS, curItem->RootKey, curItem->NwkSkey ) != SECURE_ELEMENT_SUCCESS ) + { + return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC; + } + + return LORAMAC_CRYPTO_SUCCESS; +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacCrypto.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacCrypto.h new file mode 100644 index 0000000000..b8e67d5312 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacCrypto.h @@ -0,0 +1,341 @@ +/*! + * \file LoRaMacCrypto.h + * + * \brief LoRa MAC layer cryptographic functionality implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * addtogroup LORAMAC + * \{ + * + */ +#ifndef __LORAMAC_CRYPTO_H__ +#define __LORAMAC_CRYPTO_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include +#include +#include "utilities.h" +#include "LoRaMacTypes.h" +#include "LoRaMacMessageTypes.h" +#include "LoRaMacCryptoNvm.h" + +/*! + * Indicates if LoRaWAN 1.1.x crypto scheme is enabled + */ +#ifndef USE_LRWAN_1_1_X_CRYPTO +#define USE_LRWAN_1_1_X_CRYPTO 1 +#endif + +/*! + * Indicates if a random devnonce must be used or not + */ +#define USE_RANDOM_DEV_NONCE 0 + +/*! + * Indicates if JoinNonce is counter based and requires to be checked on 1.0.x devices + * \remark Only applies to LoRaWAN 1.0.x when following recomendations provided + * by "Technical Recommendations for Preventing State Synchronization + * Issues around LoRaWAN® 1.0.x Join Procedure" + * https://lora-alliance.org/wp-content/uploads/2020/11/lorawan-1.0.x-join-synch-issues-remedies-v1.0.0.pdf + */ +#define USE_10X_JOIN_NONCE_COUNTER_CHECK 0 + +/*! + * Initial value of the frame counters + */ +#define FCNT_DOWN_INITIAL_VALUE 0xFFFFFFFF + +/*! + * LoRaMac Crypto Status + */ +typedef enum eLoRaMacCryptoStatus +{ + /*! + * No error occurred + */ + LORAMAC_CRYPTO_SUCCESS = 0, + /*! + * MIC does not match + */ + LORAMAC_CRYPTO_FAIL_MIC, + /*! + * Address does not match + */ + LORAMAC_CRYPTO_FAIL_ADDRESS, + /*! + * JoinNonce was not greater than previous one. + */ + LORAMAC_CRYPTO_FAIL_JOIN_NONCE, + /*! + * RJcount0 reached 2^16-1 + */ + LORAMAC_CRYPTO_FAIL_RJCOUNT0_OVERFLOW, + /*! + * FCNT_ID is not supported + */ + LORAMAC_CRYPTO_FAIL_FCNT_ID, + /*! + * FCntUp/Down check failed (new FCnt is smaller than previous one) + */ + LORAMAC_CRYPTO_FAIL_FCNT_SMALLER, + /*! + * FCntUp/Down check failed (duplicated) + */ + LORAMAC_CRYPTO_FAIL_FCNT_DUPLICATED, + /*! + * Not allowed parameter value + */ + LORAMAC_CRYPTO_FAIL_PARAM, + /*! + * Null pointer exception + */ + LORAMAC_CRYPTO_ERROR_NPE, + /*! + * Invalid key identifier exception + */ + LORAMAC_CRYPTO_ERROR_INVALID_KEY_ID, + /*! + * Invalid address identifier exception + */ + LORAMAC_CRYPTO_ERROR_INVALID_ADDR_ID, + /*! + * Invalid LoRaWAN specification version + */ + LORAMAC_CRYPTO_ERROR_INVALID_VERSION, + /*! + * Incompatible buffer size + */ + LORAMAC_CRYPTO_ERROR_BUF_SIZE, + /*! + * The secure element reports an error + */ + LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC, + /*! + * Error from parser reported + */ + LORAMAC_CRYPTO_ERROR_PARSER, + /*! + * Error from serializer reported + */ + LORAMAC_CRYPTO_ERROR_SERIALIZER, + /*! + * RJcount1 reached 2^16-1 which should never happen + */ + LORAMAC_CRYPTO_ERROR_RJCOUNT1_OVERFLOW, + /*! + * Undefined Error occurred + */ + LORAMAC_CRYPTO_ERROR, +}LoRaMacCryptoStatus_t; + +/*! + * Signature of callback function to be called by the LoRaMac Crypto module when the + * non-volatile context have to be stored. It is also possible to save the entire + * crypto module context. + * + */ +typedef void ( *LoRaMacCryptoNvmEvent )( void ); + +/*! + * Initialization of LoRaMac Crypto module + * It sets initial values of volatile variables and assigns the non-volatile context. + * + * \param[IN] nvm - Pointer to the non-volatile memory data + * structure. + * \retval - Status of the operation + */ +LoRaMacCryptoStatus_t LoRaMacCryptoInit( LoRaMacCryptoNvmData_t* nvm ); + +/*! + * Sets the LoRaWAN specification version to be used. + * + * \warning This function should be used for ABP only. In case of OTA the version will be set automatically. + * + * \param[IN] version - LoRaWAN specification version to be used. + * + * \retval - Status of the operation + */ +LoRaMacCryptoStatus_t LoRaMacCryptoSetLrWanVersion( Version_t version ); + +/*! + * Returns updated fCntID downlink counter value. + * + * \param[IN] fCntID - Frame counter identifier + * \param[IN] frameFcnt - Received frame counter (used to update current counter value) + * \param[OUT] currentDown - Current downlink counter value + * \retval - Status of the operation + */ +LoRaMacCryptoStatus_t LoRaMacCryptoGetFCntDown( FCntIdentifier_t fCntID, uint32_t frameFcnt, uint32_t* currentDown ); + +/*! + * Returns updated fCntUp uplink counter value. + * + * \param[IN] currentUp - Uplink counter value + * \retval - Status of the operation + */ +LoRaMacCryptoStatus_t LoRaMacCryptoGetFCntUp( uint32_t* currentUp ); + +/*! + * Computes next RJcount0 or RJcount1 counter value. + * + * \param[IN] fCntID - Frame counter identifier + * \param[OUT] rJcount - RJcount value + * + * \retval - Status of the operation + */ +LoRaMacCryptoStatus_t LoRaMacCryptoGetRJcount( FCntIdentifier_t fCntID, uint16_t* rJcount ); + +/*! + * Provides multicast context. + * + * \param[IN] multicastList - Pointer to the multicast context list + * + * \retval - Status of the operation + */ +LoRaMacCryptoStatus_t LoRaMacCryptoSetMulticastReference( MulticastCtx_t* multicastList ); + +/*! + * Sets a key + * + * \param[IN] keyID - Key identifier + * \param[IN] key - Key value (16 byte), if its a multicast key it must be encrypted with McKEKey + * \retval - Status of the operation + */ +LoRaMacCryptoStatus_t LoRaMacCryptoSetKey( KeyIdentifier_t keyID, uint8_t* key ); + +/*! + * Prepares the join-request message. + * It computes the mic and add it to the message. + * + * \param[IN/OUT] macMsg - Join-request message object + * \retval - Status of the operation + */ +LoRaMacCryptoStatus_t LoRaMacCryptoPrepareJoinRequest( LoRaMacMessageJoinRequest_t* macMsg ); + +/*! + * Prepares a rejoin-request type 1 message. + * It computes the mic and add it to the message. + * + * \param[IN/OUT] macMsg - Rejoin message object + * \retval - Status of the operation + */ +LoRaMacCryptoStatus_t LoRaMacCryptoPrepareReJoinType1( LoRaMacMessageReJoinType1_t* macMsg ); + +/*! + * Prepares a rejoin-request type 0 or 2 message. + * It computes the mic and add it to the message. + * + * \param[IN/OUT] macMsg - Rejoin message object + * \retval - Status of the operation + */ +LoRaMacCryptoStatus_t LoRaMacCryptoPrepareReJoinType0or2( LoRaMacMessageReJoinType0or2_t* macMsg ); + +/*! + * Handles the join-accept message. + * It decrypts the message, verifies the MIC and if successful derives the session keys. + * + * \param[IN] joinReqType - Type of last join-request or rejoin which triggered the join-accept response + * \param[IN] joinEUI - Join server EUI (8 byte) + * \param[IN/OUT] macMsg - Join-accept message object + * \retval - Status of the operation + */ +LoRaMacCryptoStatus_t LoRaMacCryptoHandleJoinAccept( JoinReqIdentifier_t joinReqType, uint8_t* joinEUI, LoRaMacMessageJoinAccept_t* macMsg ); + +/*! + * Secures a message (encryption + integrity). + * + * \param[IN] fCntUp - Uplink sequence counter + * \param[IN] txDr - Data rate used for the transmission + * \param[IN] txCh - Index of the channel used for the transmission + * \param[IN/OUT] macMsg - Data message object + * \retval - Status of the operation + */ +LoRaMacCryptoStatus_t LoRaMacCryptoSecureMessage( uint32_t fCntUp, uint8_t txDr, uint8_t txCh, LoRaMacMessageData_t* macMsg ); + +/*! + * Unsecures a message (decryption + integrity verification). + * + * \param[IN] addrID - Address identifier + * \param[IN] address - Address + * \param[IN] fCntID - Frame counter identifier + * \param[IN] fCntDown - Downlink sequence counter + * \param[IN/OUT] macMsg - Data message object + * \retval - Status of the operation + */ +LoRaMacCryptoStatus_t LoRaMacCryptoUnsecureMessage( AddressIdentifier_t addrID, uint32_t address, FCntIdentifier_t fCntID, uint32_t fCntDown, LoRaMacMessageData_t* macMsg ); + +/*! + * Derives the McRootKey from the AppKey. + * + * 1.0.x + * McRootKey = aes128_encrypt(AppKey, 0x00 | pad16) + * + * 1.1.x + * McRootKey = aes128_encrypt(AppKey, 0x20 | pad16) + * + * \param[IN] versionMinor - LoRaWAN specification minor version to be used. + * \param[IN] keyID - Key identifier of the root key to use to perform the derivation ( AppKey ) + * \retval - Status of the operation + */ +LoRaMacCryptoStatus_t LoRaMacCryptoDeriveMcRootKey( uint8_t versionMinor, KeyIdentifier_t keyID ); + +/*! + * Derives the McKEKey from the McRootKey. + * + * McKEKey = aes128_encrypt(McRootKey , 0x00 | pad16) + * + * \param[IN] keyID - Key identifier of the root key to use to perform the derivation ( McRootKey ) + * \retval - Status of the operation + */ +LoRaMacCryptoStatus_t LoRaMacCryptoDeriveMcKEKey( KeyIdentifier_t keyID ); + +/*! + * Derives a Multicast group key pair ( McAppSKey, McNwkSKey ) from McKey + * + * McAppSKey = aes128_encrypt(McKey, 0x01 | McAddr | pad16) + * McNwkSKey = aes128_encrypt(McKey, 0x02 | McAddr | pad16) + * + * \param[IN] addrID - Address identifier to select the multicast group + * \param[IN] mcAddr - Multicast group address (4 bytes) + * \retval - Status of the operation + */ +LoRaMacCryptoStatus_t LoRaMacCryptoDeriveMcSessionKeyPair( AddressIdentifier_t addrID, uint32_t mcAddr ); + +/*! \} addtogroup LORAMAC */ + +#ifdef __cplusplus +} +#endif + +#endif // __LORAMAC_CRYPTO_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacCryptoNvm.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacCryptoNvm.h new file mode 100644 index 0000000000..8b604e8dd4 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacCryptoNvm.h @@ -0,0 +1,123 @@ +/*! + * \file LoRaMacCryptoNvm.h + * + * \brief LoRa MAC layer cryptographic NVM data. + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * addtogroup LORAMAC + * \{ + * + */ +#ifndef __LORAMAC_CRYPTO_NVM_H__ +#define __LORAMAC_CRYPTO_NVM_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include "utilities.h" +#include "LoRaMacTypes.h" + + +/*! + * LoRaWAN Frame counter list. + */ +typedef struct sFCntList +{ + /*! + * Uplink frame counter which is incremented with each uplink. + */ + uint32_t FCntUp; + /*! + * Network downlink frame counter which is incremented with each downlink on FPort 0 + * or when the FPort field is missing. + */ + uint32_t NFCntDown; + /*! + * Application downlink frame counter which is incremented with each downlink + * on a port different than 0. + */ + uint32_t AFCntDown; + /*! + * In case if the device is connected to a LoRaWAN 1.0 Server, + * this counter is used for every kind of downlink frame. + */ + uint32_t FCntDown; + /*! + * Multicast downlink counters + */ + uint32_t McFCntDown[LORAMAC_MAX_MC_CTX]; + /*! + * RJcount1 is a counter incremented with every Rejoin request Type 1 + * frame transmitted. + */ + uint16_t RJcount1; +}FCntList_t; + +/*! + * LoRaMac Crypto Non Volatile Context structure + */ +typedef struct sLoRaMacCryptoNvmData +{ + /*! + * Stores the information if the device is connected to a LoRaWAN network + * server with prior to 1.1.0 implementation. + */ + Version_t LrWanVersion; + /*! + * Device nonce is a counter starting at 0 when the device is initially + * powered up and incremented with every JoinRequest. + */ + uint16_t DevNonce; + /*! + * JoinNonce is a device specific counter value (that never repeats itself) + * provided by the join server and incremented with every JoinAccept message. + */ + uint32_t JoinNonce; + /*! + * Frame counter list + */ + FCntList_t FCntList; + /*! + * LastDownFCnt stores the information which frame counter was used to + * decrypt the last frame. This information is needed to compute ConfFCnt in + * B1 block for the MIC. + */ + uint32_t LastDownFCnt; + /*! + * CRC32 value of the Crypto data structure. + */ + uint32_t Crc32; +}LoRaMacCryptoNvmData_t; + +/*! \} addtogroup LORAMAC */ + +#ifdef __cplusplus +} +#endif + +#endif // __LORAMAC_CRYPTO_NVM_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacHeaderTypes.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacHeaderTypes.h new file mode 100644 index 0000000000..6a865c0315 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacHeaderTypes.h @@ -0,0 +1,327 @@ +/*! + * \file LoRaMacHeaderTypes.h + * + * \brief LoRa MAC layer header type definitions + * + * \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 Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * addtogroup LORAMAC + * \{ + * + */ +#ifndef __LORAMAC_HEADER_TYPES_H__ +#define __LORAMAC_HEADER_TYPES_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +/*! MAC header field size */ +#define LORAMAC_MHDR_FIELD_SIZE 1 + +/*! ReJoinType field size */ +#define LORAMAC_JOIN_TYPE_FIELD_SIZE 1 + +/*! Join EUI field size */ +#define LORAMAC_JOIN_EUI_FIELD_SIZE 8 + +/*! Device EUI field size */ +#define LORAMAC_DEV_EUI_FIELD_SIZE 8 + +/*! End-device nonce field size */ +#define LORAMAC_DEV_NONCE_FIELD_SIZE 2 + +/*! Join-server nonce field size */ +#define LORAMAC_JOIN_NONCE_FIELD_SIZE 3 + +/*! RJcount0 field size */ +#define LORAMAC_RJCOUNT_0_FIELD_SIZE 2 + +/*! RJcount1 field size */ +#define LORAMAC_RJCOUNT_1_FIELD_SIZE 2 + +/*! Network ID field size */ +#define LORAMAC_NET_ID_FIELD_SIZE 3 + +/*! Device address field size */ +#define LORAMAC_DEV_ADDR_FIELD_SIZE 4 + +/*! DLSettings field size */ +#define LORAMAC_DL_SETTINGS_FIELD_SIZE 1 + +/*! RxDelay field size */ +#define LORAMAC_RX_DELAY_FIELD_SIZE 1 + +/*! CFList field size */ +#define LORAMAC_CF_LIST_FIELD_SIZE 16 + +/*! FHDR Device address field size */ +#define LORAMAC_FHDR_DEV_ADDR_FIELD_SIZE LORAMAC_DEV_ADDR_FIELD_SIZE + +/*! FHDR Frame control field size */ +#define LORAMAC_FHDR_F_CTRL_FIELD_SIZE 1 + +/*! FHDR Frame control field size */ +#define LORAMAC_FHDR_F_CNT_FIELD_SIZE 2 + +/*! FOpts maximum field size */ +#define LORAMAC_FHDR_F_OPTS_MAX_FIELD_SIZE 15 + +/*! Port field size */ +#define LORAMAC_F_PORT_FIELD_SIZE 1 + +/*! Port field size */ +#define LORAMAC_MAC_PAYLOAD_FIELD_MAX_SIZE 242 + +/*! MIC field size */ +#define LORAMAC_MIC_FIELD_SIZE 4 + +/*! + * JoinRequest frame size + * + * MHDR(1) + JoinEUI(8) + DevEUI(8) + DevNonce(2) + MIC(4) + */ +#define LORAMAC_JOIN_REQ_MSG_SIZE ( LORAMAC_MHDR_FIELD_SIZE + LORAMAC_JOIN_EUI_FIELD_SIZE + \ + LORAMAC_DEV_EUI_FIELD_SIZE + LORAMAC_DEV_NONCE_FIELD_SIZE + \ + LORAMAC_MIC_FIELD_SIZE ) + +/*! + * ReJoinRequest type 1 frame size + * + * MHDR(1) + ReJoinType(1) + JoinEUI(8) + DevEUI(8) + RJcount1(2) + MIC(4) + */ +#define LORAMAC_RE_JOIN_1_MSG_SIZE ( LORAMAC_MHDR_FIELD_SIZE + LORAMAC_JOIN_TYPE_FIELD_SIZE + \ + LORAMAC_JOIN_EUI_FIELD_SIZE + LORAMAC_DEV_EUI_FIELD_SIZE + \ + LORAMAC_RJCOUNT_1_FIELD_SIZE + \ + LORAMAC_MIC_FIELD_SIZE ) + +/*! + * ReJoinRequest type 0 or 2 frame size + * + * MHDR(1) + ReJoinType(1) + NetID(3) + DevEUI(8) + RJcount0(2) + MIC(4) + */ +#define LORAMAC_RE_JOIN_0_2_MSG_SIZE ( LORAMAC_MHDR_FIELD_SIZE + LORAMAC_JOIN_TYPE_FIELD_SIZE + \ + LORAMAC_NET_ID_FIELD_SIZE + LORAMAC_DEV_EUI_FIELD_SIZE + \ + LORAMAC_RJCOUNT_0_FIELD_SIZE + \ + LORAMAC_MIC_FIELD_SIZE ) + +/*! + * JoinAccept frame minimum size + * + * MHDR(1) + AppNonce(3) + NetID(3) + DevAddr(4) + DLSettings(1) + RxDelay(1) + MIC(4) + */ +#define LORAMAC_JOIN_ACCEPT_FRAME_MIN_SIZE ( LORAMAC_MHDR_FIELD_SIZE + LORAMAC_JOIN_NONCE_FIELD_SIZE + \ + LORAMAC_NET_ID_FIELD_SIZE + LORAMAC_DEV_ADDR_FIELD_SIZE + \ + LORAMAC_DL_SETTINGS_FIELD_SIZE + LORAMAC_RX_DELAY_FIELD_SIZE + \ + LORAMAC_MIC_FIELD_SIZE ) + +/*! + * JoinAccept frame maximum size + * + * MHDR(1) + AppNonce(3) + NetID(3) + DevAddr(4) + DLSettings(1) + RxDelay(1) + CFList(16) + MIC(4) + */ +#define LORAMAC_JOIN_ACCEPT_FRAME_MAX_SIZE ( LORAMAC_MHDR_FIELD_SIZE + LORAMAC_JOIN_NONCE_FIELD_SIZE + \ + LORAMAC_NET_ID_FIELD_SIZE + LORAMAC_DEV_ADDR_FIELD_SIZE + \ + LORAMAC_DL_SETTINGS_FIELD_SIZE + LORAMAC_RX_DELAY_FIELD_SIZE + \ + LORAMAC_CF_LIST_FIELD_SIZE + LORAMAC_MIC_FIELD_SIZE ) + +/*! + * MIC computation offset + * \remark required for 1.1.x support + */ +#define JOIN_ACCEPT_MIC_COMPUTATION_OFFSET \ + ( LORAMAC_MHDR_FIELD_SIZE + LORAMAC_JOIN_TYPE_FIELD_SIZE + LORAMAC_JOIN_EUI_FIELD_SIZE + \ + LORAMAC_DEV_NONCE_FIELD_SIZE ) + +/*! + * FRMPayload overhead to be used when setting the Radio.SetMaxPayloadLength + * + * Overhead to be used when setting the Radio.SetMaxPayloadLength in RxWindowSetup function. + * + * MHDR(1) + FHDR(7) + Port(1) + MIC(4) + * + * Maximum PHYPayload = MaxPayloadOfDatarate + LORAMAC_FRAME_PAYLOAD_OVERHEAD_SIZE + */ +#define LORAMAC_FRAME_PAYLOAD_OVERHEAD_SIZE ( LORAMAC_MHDR_FIELD_SIZE + ( LORAMAC_FHDR_DEV_ADDR_FIELD_SIZE + \ + LORAMAC_FHDR_F_CTRL_FIELD_SIZE + LORAMAC_FHDR_F_CNT_FIELD_SIZE ) + \ + LORAMAC_F_PORT_FIELD_SIZE + LORAMAC_MIC_FIELD_SIZE ) + +/*! + * FRMPayload minimum size + * + * MHDR(1) + FHDR(7) + MIC(4) + */ +#define LORAMAC_FRAME_PAYLOAD_MIN_SIZE ( LORAMAC_MHDR_FIELD_SIZE + ( LORAMAC_FHDR_DEV_ADDR_FIELD_SIZE + \ + LORAMAC_FHDR_F_CTRL_FIELD_SIZE + LORAMAC_FHDR_F_CNT_FIELD_SIZE ) + \ + LORAMAC_MIC_FIELD_SIZE ) +/*! + * FRMPayload maximum possible size + * + * MHDR(1) + FHDR(7) + Port(1) + MACPayload(242) + MIC(4) + */ +#define LORAMAC_FRAME_PAYLOAD_MAX_SIZE ( LORAMAC_MHDR_FIELD_SIZE + ( LORAMAC_FHDR_DEV_ADDR_FIELD_SIZE + \ + LORAMAC_FHDR_F_CTRL_FIELD_SIZE + LORAMAC_FHDR_F_CNT_FIELD_SIZE ) + \ + LORAMAC_F_PORT_FIELD_SIZE + LORAMAC_MAC_PAYLOAD_FIELD_MAX_SIZE + \ + LORAMAC_MIC_FIELD_SIZE ) + +/*! + * LoRaMAC field definition of DLSettings + * + * LoRaWAN Specification V1.0.2, chapter 5.4 + */ +typedef union uLoRaMacDLSettings +{ + /*! + * Byte-access to the bits + */ + uint8_t Value; + /*! + * Structure containing single access to header bits + */ + struct sDLSettingsBits + { + /*! + * Data rate of a downlink using the second receive window + */ + uint8_t RX2DataRate : 4; + /*! + * Offset between up and downlink datarate of first reception slot + */ + uint8_t RX1DRoffset : 3; + /*! + * Indicates network server LoRaWAN implementation version 1.1 or later. + */ + uint8_t OptNeg : 1; + }Bits; +}LoRaMacDLSettings_t; + +/*! + * LoRaMAC header field definition (MHDR field) + * + * LoRaWAN Specification V1.0.2, chapter 4.2 + */ +typedef union uLoRaMacHeader +{ + /*! + * Byte-access to the bits + */ + uint8_t Value; + /*! + * Structure containing single access to header bits + */ + struct sMacHeaderBits + { + /*! + * Major version + */ + uint8_t Major : 2; + /*! + * RFU + */ + uint8_t RFU : 3; + /*! + * Message type + */ + uint8_t MType : 3; + }Bits; +}LoRaMacHeader_t; + +/*! + * LoRaMAC frame control field definition (FCtrl) + * + * LoRaWAN Specification V1.0.2, chapter 4.3.1 + */ +typedef union uLoRaMacFrameCtrl +{ + /*! + * Byte-access to the bits + */ + uint8_t Value; + /*! + * Structure containing single access to bits + */ + 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 Frame header (FHDR) + * + * LoRaWAN Specification V1.0.2, chapter 4.3.1 + */ +typedef struct sLoRaMacFrameHeader +{ + /*! + * Device address + */ + uint32_t DevAddr; + /*! + * Frame control field + */ + LoRaMacFrameCtrl_t FCtrl; + /*! + * Frame counter + */ + uint16_t FCnt; + /*! + * FOpts field may transport MAC commands (opt. 0-15 Bytes) + */ + uint8_t FOpts[LORAMAC_FHDR_F_OPTS_MAX_FIELD_SIZE]; +}LoRaMacFrameHeader_t; + +/*! \} addtogroup LORAMAC */ + +#ifdef __cplusplus +} +#endif + +#endif // __LORAMAC_HEADER_TYPES_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacMessageTypes.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacMessageTypes.h new file mode 100644 index 0000000000..c16385e8c4 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacMessageTypes.h @@ -0,0 +1,302 @@ +/*! + * \file LoRaMacMessageTypes.h + * + * \brief LoRa MAC layer message type definitions + * + * \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 Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * addtogroup LORAMAC + * \{ + * + */ +#ifndef __LORAMAC_MESSAGE_TYPES_H__ +#define __LORAMAC_MESSAGE_TYPES_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include "LoRaMacHeaderTypes.h" + +/*! + * LoRaMac type for Join-request message + */ +typedef struct sLoRaMacMessageJoinRequest +{ + /*! + * Serialized message buffer + */ + uint8_t* Buffer; + /*! + * Size of serialized message buffer + */ + uint8_t BufSize; + /*! + * MAC header + */ + LoRaMacHeader_t MHDR; + /*! + * Join EUI + */ + uint8_t JoinEUI[LORAMAC_JOIN_EUI_FIELD_SIZE]; + /*! + * Device EUI + */ + uint8_t DevEUI[LORAMAC_DEV_EUI_FIELD_SIZE]; + /*! + * Device Nonce + */ + uint16_t DevNonce; + /*! + * Message integrity code (MIC) + */ + uint32_t MIC; +}LoRaMacMessageJoinRequest_t; + +/*! + * LoRaMac type for rejoin-request type 1 message + */ +typedef struct sLoRaMacMessageReJoinType1 +{ + /*! + * Serialized message buffer + */ + uint8_t* Buffer; + /*! + * Size of serialized message buffer + */ + uint8_t BufSize; + /*! + * MAC header + */ + LoRaMacHeader_t MHDR; + /*! + * Rejoin-request type ( 1 ) + */ + uint8_t ReJoinType; + /*! + * Join EUI + */ + uint8_t JoinEUI[LORAMAC_JOIN_EUI_FIELD_SIZE]; + /*! + * Device EUI + */ + uint8_t DevEUI[LORAMAC_DEV_EUI_FIELD_SIZE]; + /*! + * ReJoin Type 1 counter + */ + uint16_t RJcount1; + /*! + * Message integrity code (MIC) + */ + uint32_t MIC; +}LoRaMacMessageReJoinType1_t; + +/*! + * LoRaMac type for rejoin-request type 0 or 2 message + */ +typedef struct sLoRaMacMessageReJoinType0or2 +{ + /*! + * Serialized message buffer + */ + uint8_t* Buffer; + /*! + * Size of serialized message buffer + */ + uint8_t BufSize; + /*! + * MAC header + */ + LoRaMacHeader_t MHDR; + /*! + * Rejoin-request type ( 0 or 2 ) + */ + uint8_t ReJoinType; + /*! + * Network ID ( 3 bytes ) + */ + uint8_t NetID[LORAMAC_NET_ID_FIELD_SIZE]; + /*! + * Device EUI + */ + uint8_t DevEUI[LORAMAC_DEV_EUI_FIELD_SIZE]; + /*! + * ReJoin Type 0 and 2 frame counter + */ + uint16_t RJcount0; + /*! + * Message integrity code (MIC) + */ + uint32_t MIC; +}LoRaMacMessageReJoinType0or2_t; + +/*! + * LoRaMac type for Join-accept message + */ +typedef struct sLoRaMacMessageJoinAccept +{ + /*! + * Serialized message buffer + */ + uint8_t* Buffer; + /*! + * Size of serialized message buffer + */ + uint8_t BufSize; + /*! + * MAC header + */ + LoRaMacHeader_t MHDR; + /*! + * Server Nonce ( 3 bytes ) + */ + uint8_t JoinNonce[LORAMAC_JOIN_NONCE_FIELD_SIZE]; + /*! + * Network ID ( 3 bytes ) + */ + uint8_t NetID[LORAMAC_NET_ID_FIELD_SIZE]; + /*! + * Device address + */ + uint32_t DevAddr; + /*! + * Device address + */ + LoRaMacDLSettings_t DLSettings; + /*! + * Delay between TX and RX + */ + uint8_t RxDelay; + /*! + * List of channel frequencies (opt.) + */ + uint8_t CFList[16]; + /*! + * Message integrity code (MIC) + */ + uint32_t MIC; +}LoRaMacMessageJoinAccept_t; + + +/*! + * LoRaMac type for Data MAC messages + * (Unconfirmed Data Up, Confirmed Data Up, Unconfirmed Data Down, Confirmed Data Down) + */ +typedef struct sLoRaMacMessageData +{ + /*! + * Serialized message buffer + */ + uint8_t* Buffer; + /*! + * Size of serialized message buffer + */ + uint8_t BufSize; + /*! + * MAC header + */ + LoRaMacHeader_t MHDR; + /*! + * Frame header (FHDR) + */ + LoRaMacFrameHeader_t FHDR; + /*! + * Port field (opt.) + */ + uint8_t FPort; + /*! + * Frame payload may contain MAC commands or data (opt.) + */ + uint8_t* FRMPayload; + /*! + * Size of frame payload (not included in LoRaMac messages) + */ + uint8_t FRMPayloadSize; + /*! + * Message integrity code (MIC) + */ + uint32_t MIC; +}LoRaMacMessageData_t; + +/*! + * LoRaMac message type enumerator + */ +typedef enum eLoRaMacMessageType +{ + /*! + * Join-request message + */ + LORAMAC_MSG_TYPE_JOIN_REQUEST, + /*! + * Rejoin-request type 1 message + */ + LORAMAC_MSG_TYPE_RE_JOIN_1, + /*! + * Rejoin-request type 1 message + */ + LORAMAC_MSG_TYPE_RE_JOIN_0_2, + /*! + * Join-accept message + */ + LORAMAC_MSG_TYPE_JOIN_ACCEPT, + /*! + * Data MAC messages + */ + LORAMAC_MSG_TYPE_DATA, + /*! + * Undefined message type + */ + LORAMAC_MSG_TYPE_UNDEF, +}LoRaMacMessageType_t; + +/*! + * LoRaMac general message type + */ +typedef struct sLoRaMacMessage +{ + LoRaMacMessageType_t Type; + union uMessage + { + LoRaMacMessageJoinRequest_t JoinReq; + LoRaMacMessageReJoinType1_t ReJoin1; + LoRaMacMessageReJoinType0or2_t ReJoin0or2; + LoRaMacMessageJoinAccept_t JoinAccept; + LoRaMacMessageData_t Data; + }Message; +}LoRaMacMessage_t; + +/*! \} addtogroup LORAMAC */ + +#ifdef __cplusplus +} +#endif + +#endif // __LORAMAC_MESSAGE_TYPES_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacParser.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacParser.c new file mode 100644 index 0000000000..deb522f8ce --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacParser.c @@ -0,0 +1,111 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: LoRa MAC layer message parser functionality implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ), + Daniel Jaeckle ( STACKFORCE ), Johannes Bruder ( STACKFORCE ) +*/ +#include "LoRaMacParser.h" +#include "utilities.h" + +LoRaMacParserStatus_t LoRaMacParserJoinAccept( LoRaMacMessageJoinAccept_t* macMsg ) +{ + if( ( macMsg == 0 ) || ( macMsg->Buffer == 0 ) ) + { + return LORAMAC_PARSER_ERROR_NPE; + } + + uint16_t bufItr = 0; + + macMsg->MHDR.Value = macMsg->Buffer[bufItr++]; + + memcpy1( macMsg->JoinNonce, &macMsg->Buffer[bufItr], 3 ); + bufItr = bufItr + 3; + + memcpy1( macMsg->NetID, &macMsg->Buffer[bufItr], 3 ); + bufItr = bufItr + 3; + + macMsg->DevAddr = ( uint32_t ) macMsg->Buffer[bufItr++]; + macMsg->DevAddr |= ( ( uint32_t ) macMsg->Buffer[bufItr++] << 8 ); + macMsg->DevAddr |= ( ( uint32_t ) macMsg->Buffer[bufItr++] << 16 ); + macMsg->DevAddr |= ( ( uint32_t ) macMsg->Buffer[bufItr++] << 24 ); + + macMsg->DLSettings.Value = macMsg->Buffer[bufItr++]; + + macMsg->RxDelay = macMsg->Buffer[bufItr++]; + + if( ( macMsg->BufSize - LORAMAC_MIC_FIELD_SIZE - bufItr ) == LORAMAC_CF_LIST_FIELD_SIZE ) + { + memcpy1( macMsg->CFList, &macMsg->Buffer[bufItr], LORAMAC_CF_LIST_FIELD_SIZE ); + bufItr = bufItr + LORAMAC_CF_LIST_FIELD_SIZE; + } + else if( ( macMsg->BufSize - LORAMAC_MIC_FIELD_SIZE - bufItr ) > 0 ) + { + return LORAMAC_PARSER_FAIL; + } + + macMsg->MIC = ( uint32_t ) macMsg->Buffer[bufItr++]; + macMsg->MIC |= ( ( uint32_t ) macMsg->Buffer[bufItr++] << 8 ); + macMsg->MIC |= ( ( uint32_t ) macMsg->Buffer[bufItr++] << 16 ); + macMsg->MIC |= ( ( uint32_t ) macMsg->Buffer[bufItr++] << 24 ); + + return LORAMAC_PARSER_SUCCESS; +} + +LoRaMacParserStatus_t LoRaMacParserData( LoRaMacMessageData_t* macMsg ) +{ + if( ( macMsg == 0 ) || ( macMsg->Buffer == 0 ) ) + { + return LORAMAC_PARSER_ERROR_NPE; + } + + uint16_t bufItr = 0; + + macMsg->MHDR.Value = macMsg->Buffer[bufItr++]; + + macMsg->FHDR.DevAddr = macMsg->Buffer[bufItr++]; + macMsg->FHDR.DevAddr |= ( ( uint32_t ) macMsg->Buffer[bufItr++] << 8 ); + macMsg->FHDR.DevAddr |= ( ( uint32_t ) macMsg->Buffer[bufItr++] << 16 ); + macMsg->FHDR.DevAddr |= ( ( uint32_t ) macMsg->Buffer[bufItr++] << 24 ); + + macMsg->FHDR.FCtrl.Value = macMsg->Buffer[bufItr++]; + + macMsg->FHDR.FCnt = macMsg->Buffer[bufItr++]; + macMsg->FHDR.FCnt |= macMsg->Buffer[bufItr++] << 8; + + memcpy1( macMsg->FHDR.FOpts, &macMsg->Buffer[bufItr], macMsg->FHDR.FCtrl.Bits.FOptsLen ); + bufItr = bufItr + macMsg->FHDR.FCtrl.Bits.FOptsLen; + + // Initialize anyway with zero. + macMsg->FPort = 0; + macMsg->FRMPayloadSize = 0; + + if( ( macMsg->BufSize - bufItr - LORAMAC_MIC_FIELD_SIZE ) > 0 ) + { + macMsg->FPort = macMsg->Buffer[bufItr++]; + + macMsg->FRMPayloadSize = ( macMsg->BufSize - bufItr - LORAMAC_MIC_FIELD_SIZE ); + memcpy1( macMsg->FRMPayload, &macMsg->Buffer[bufItr], macMsg->FRMPayloadSize ); + bufItr = bufItr + macMsg->FRMPayloadSize; + } + + macMsg->MIC = ( uint32_t ) macMsg->Buffer[( macMsg->BufSize - LORAMAC_MIC_FIELD_SIZE )]; + macMsg->MIC |= ( ( uint32_t ) macMsg->Buffer[( macMsg->BufSize - LORAMAC_MIC_FIELD_SIZE ) + 1] << 8 ); + macMsg->MIC |= ( ( uint32_t ) macMsg->Buffer[( macMsg->BufSize - LORAMAC_MIC_FIELD_SIZE ) + 2] << 16 ); + macMsg->MIC |= ( ( uint32_t ) macMsg->Buffer[( macMsg->BufSize - LORAMAC_MIC_FIELD_SIZE ) + 3] << 24 ); + + return LORAMAC_PARSER_SUCCESS; +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacParser.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacParser.h new file mode 100644 index 0000000000..e9a0a22c99 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacParser.h @@ -0,0 +1,95 @@ +/*! + * \file LoRaMacParser.h + * + * \brief LoRa MAC layer message parser functionality 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 Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * addtogroup LORAMAC + * \{ + * + */ +#ifndef __LORAMAC_PARSER_H__ +#define __LORAMAC_PARSER_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include "LoRaMacMessageTypes.h" + +/*! + * LoRaMac Parser Status + */ +typedef enum eLoRaMacParserStatus +{ + /*! + * No error occurred + */ + LORAMAC_PARSER_SUCCESS = 0, + /*! + * Failure during parsing occurred + */ + LORAMAC_PARSER_FAIL, + /*! + * Null pointer exception + */ + LORAMAC_PARSER_ERROR_NPE, + /*! + * Undefined Error occurred + */ + LORAMAC_PARSER_ERROR, +}LoRaMacParserStatus_t; + + +/*! + * Parse a serialized join-accept message and fills the structured object. + * + * \param[IN/OUT] macMsg - Join-accept message object + * \retval - Status of the operation + */ +LoRaMacParserStatus_t LoRaMacParserJoinAccept( LoRaMacMessageJoinAccept_t *macMsg ); + +/*! + * Parse a serialized data message and fills the structured object. + * + * \param[IN/OUT] macMsg - Data message object + * \retval - Status of the operation + */ +LoRaMacParserStatus_t LoRaMacParserData( LoRaMacMessageData_t *macMsg ); + +/*! \} addtogroup LORAMAC */ + +#ifdef __cplusplus +} +#endif + +#endif // __LORAMAC_PARSER_H__ + diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacSerializer.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacSerializer.c new file mode 100644 index 0000000000..b593a25942 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacSerializer.c @@ -0,0 +1,195 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: LoRa MAC layer message serializer functionality implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ), + Daniel Jaeckle ( STACKFORCE ), Johannes Bruder ( STACKFORCE ) +*/ +#include "LoRaMacSerializer.h" +#include "utilities.h" + +LoRaMacSerializerStatus_t LoRaMacSerializerJoinRequest( LoRaMacMessageJoinRequest_t* macMsg ) +{ + if( ( macMsg == 0 ) || ( macMsg->Buffer == 0 ) ) + { + return LORAMAC_SERIALIZER_ERROR_NPE; + } + + uint16_t bufItr = 0; + + // Check macMsg->BufSize + if( macMsg->BufSize < LORAMAC_JOIN_REQ_MSG_SIZE ) + { + return LORAMAC_SERIALIZER_ERROR_BUF_SIZE; + } + + macMsg->Buffer[bufItr++] = macMsg->MHDR.Value; + + memcpyr( &macMsg->Buffer[bufItr], macMsg->JoinEUI, LORAMAC_JOIN_EUI_FIELD_SIZE ); + bufItr += LORAMAC_JOIN_EUI_FIELD_SIZE; + + memcpyr( &macMsg->Buffer[bufItr], macMsg->DevEUI, LORAMAC_DEV_EUI_FIELD_SIZE ); + bufItr += LORAMAC_DEV_EUI_FIELD_SIZE; + + macMsg->Buffer[bufItr++] = macMsg->DevNonce & 0xFF; + macMsg->Buffer[bufItr++] = ( macMsg->DevNonce >> 8 ) & 0xFF; + + macMsg->Buffer[bufItr++] = macMsg->MIC & 0xFF; + macMsg->Buffer[bufItr++] = ( macMsg->MIC >> 8 ) & 0xFF; + macMsg->Buffer[bufItr++] = ( macMsg->MIC >> 16 ) & 0xFF; + macMsg->Buffer[bufItr++] = ( macMsg->MIC >> 24 ) & 0xFF; + + macMsg->BufSize = bufItr; + + return LORAMAC_SERIALIZER_SUCCESS; +} + +LoRaMacSerializerStatus_t LoRaMacSerializerReJoinType1( LoRaMacMessageReJoinType1_t* macMsg ) +{ + if( ( macMsg == 0 ) || ( macMsg->Buffer == 0 ) ) + { + return LORAMAC_SERIALIZER_ERROR_NPE; + } + + uint16_t bufItr = 0; + + // Check macMsg->BufSize + if( macMsg->BufSize < LORAMAC_RE_JOIN_1_MSG_SIZE ) + { + return LORAMAC_SERIALIZER_ERROR_BUF_SIZE; + } + + macMsg->Buffer[bufItr++] = macMsg->MHDR.Value; + macMsg->Buffer[bufItr++] = macMsg->ReJoinType; + + memcpyr( &macMsg->Buffer[bufItr], macMsg->JoinEUI, LORAMAC_JOIN_EUI_FIELD_SIZE ); + bufItr += LORAMAC_JOIN_EUI_FIELD_SIZE; + + memcpyr( &macMsg->Buffer[bufItr], macMsg->DevEUI, LORAMAC_DEV_EUI_FIELD_SIZE ); + bufItr += LORAMAC_DEV_EUI_FIELD_SIZE; + + macMsg->Buffer[bufItr++] = macMsg->RJcount1 & 0xFF; + macMsg->Buffer[bufItr++] = ( macMsg->RJcount1 >> 8 ) & 0xFF; + + macMsg->Buffer[bufItr++] = macMsg->MIC & 0xFF; + macMsg->Buffer[bufItr++] = ( macMsg->MIC >> 8 ) & 0xFF; + macMsg->Buffer[bufItr++] = ( macMsg->MIC >> 16 ) & 0xFF; + macMsg->Buffer[bufItr++] = ( macMsg->MIC >> 24 ) & 0xFF; + + macMsg->BufSize = bufItr; + + return LORAMAC_SERIALIZER_SUCCESS; +} + +LoRaMacSerializerStatus_t LoRaMacSerializerReJoinType0or2( LoRaMacMessageReJoinType0or2_t* macMsg ) +{ + if( ( macMsg == 0 ) || ( macMsg->Buffer == 0 ) ) + { + return LORAMAC_SERIALIZER_ERROR_NPE; + } + + uint16_t bufItr = 0; + + // Check macMsg->BufSize + if( macMsg->BufSize < LORAMAC_RE_JOIN_0_2_MSG_SIZE ) + { + return LORAMAC_SERIALIZER_ERROR_BUF_SIZE; + } + + macMsg->Buffer[bufItr++] = macMsg->MHDR.Value; + macMsg->Buffer[bufItr++] = macMsg->ReJoinType; + + memcpy1( &macMsg->Buffer[bufItr], macMsg->NetID, LORAMAC_NET_ID_FIELD_SIZE ); + bufItr += LORAMAC_NET_ID_FIELD_SIZE; + + memcpyr( &macMsg->Buffer[bufItr], macMsg->DevEUI, LORAMAC_DEV_EUI_FIELD_SIZE ); + bufItr += LORAMAC_DEV_EUI_FIELD_SIZE; + + macMsg->Buffer[bufItr++] = macMsg->RJcount0 & 0xFF; + macMsg->Buffer[bufItr++] = ( macMsg->RJcount0 >> 8 ) & 0xFF; + + macMsg->Buffer[bufItr++] = macMsg->MIC & 0xFF; + macMsg->Buffer[bufItr++] = ( macMsg->MIC >> 8 ) & 0xFF; + macMsg->Buffer[bufItr++] = ( macMsg->MIC >> 16 ) & 0xFF; + macMsg->Buffer[bufItr++] = ( macMsg->MIC >> 24 ) & 0xFF; + + macMsg->BufSize = bufItr; + + return LORAMAC_SERIALIZER_SUCCESS; +} + +LoRaMacSerializerStatus_t LoRaMacSerializerData( LoRaMacMessageData_t* macMsg ) +{ + if( ( macMsg == 0 ) || ( macMsg->Buffer == 0 ) ) + { + return LORAMAC_SERIALIZER_ERROR_NPE; + } + + uint16_t bufItr = 0; + + // Check macMsg->BufSize + uint16_t computedBufSize = LORAMAC_MHDR_FIELD_SIZE + + LORAMAC_FHDR_DEV_ADDR_FIELD_SIZE + + LORAMAC_FHDR_F_CTRL_FIELD_SIZE + + LORAMAC_FHDR_F_CNT_FIELD_SIZE; + + computedBufSize += macMsg->FHDR.FCtrl.Bits.FOptsLen; + + if( macMsg->FRMPayloadSize > 0 ) + { + computedBufSize += LORAMAC_F_PORT_FIELD_SIZE; + } + + computedBufSize += macMsg->FRMPayloadSize; + computedBufSize += LORAMAC_MIC_FIELD_SIZE; + + if( macMsg->BufSize < computedBufSize ) + { + return LORAMAC_SERIALIZER_ERROR_BUF_SIZE; + } + + macMsg->Buffer[bufItr++] = macMsg->MHDR.Value; + + macMsg->Buffer[bufItr++] = ( macMsg->FHDR.DevAddr ) & 0xFF; + macMsg->Buffer[bufItr++] = ( macMsg->FHDR.DevAddr >> 8 ) & 0xFF; + macMsg->Buffer[bufItr++] = ( macMsg->FHDR.DevAddr >> 16 ) & 0xFF; + macMsg->Buffer[bufItr++] = ( macMsg->FHDR.DevAddr >> 24 ) & 0xFF; + + macMsg->Buffer[bufItr++] = macMsg->FHDR.FCtrl.Value; + + macMsg->Buffer[bufItr++] = macMsg->FHDR.FCnt & 0xFF; + macMsg->Buffer[bufItr++] = ( macMsg->FHDR.FCnt >> 8 ) & 0xFF; + + memcpy1( &macMsg->Buffer[bufItr], macMsg->FHDR.FOpts, macMsg->FHDR.FCtrl.Bits.FOptsLen ); + bufItr = bufItr + macMsg->FHDR.FCtrl.Bits.FOptsLen; + + if( macMsg->FRMPayloadSize > 0 ) + { + macMsg->Buffer[bufItr++] = macMsg->FPort; + } + + memcpy1( &macMsg->Buffer[bufItr], macMsg->FRMPayload, macMsg->FRMPayloadSize ); + bufItr = bufItr + macMsg->FRMPayloadSize; + + macMsg->Buffer[bufItr++] = macMsg->MIC & 0xFF; + macMsg->Buffer[bufItr++] = ( macMsg->MIC >> 8 ) & 0xFF; + macMsg->Buffer[bufItr++] = ( macMsg->MIC >> 16 ) & 0xFF; + macMsg->Buffer[bufItr++] = ( macMsg->MIC >> 24 ) & 0xFF; + + macMsg->BufSize = bufItr; + + return LORAMAC_SERIALIZER_SUCCESS; +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacSerializer.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacSerializer.h new file mode 100644 index 0000000000..8588062ab0 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacSerializer.h @@ -0,0 +1,110 @@ +/*! + * \file LoRaMacSerializer.h + * + * \brief LoRa MAC layer message serializer functionality 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 Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * addtogroup LORAMAC + * \{ + * + */ +#ifndef __LORAMAC_SERIALIZER_H__ +#define __LORAMAC_SERIALIZER_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif +#include +#include "LoRaMacMessageTypes.h" + + +/*! + * LoRaMac Serializer Status + */ +typedef enum eLoRaMacSerializerStatus +{ + /*! + * No error occurred + */ + LORAMAC_SERIALIZER_SUCCESS = 0, + /*! + * Null pointer exception + */ + LORAMAC_SERIALIZER_ERROR_NPE, + /*! + * Incompatible buffer size + */ + LORAMAC_SERIALIZER_ERROR_BUF_SIZE, + /*! + * Undefined Error occurred + */ + LORAMAC_SERIALIZER_ERROR, +}LoRaMacSerializerStatus_t; + +/*! + * Creates serialized MAC message of structured object. + * + * \param[IN/OUT] macMsg - Join-request message object + * \retval - Status of the operation + */ +LoRaMacSerializerStatus_t LoRaMacSerializerJoinRequest( LoRaMacMessageJoinRequest_t* macMsg ); + +/*! + * Creates serialized MAC message of structured object. + * + * \param[IN/OUT] macMsg - Join-request message object + * \retval - Status of the operation + */ +LoRaMacSerializerStatus_t LoRaMacSerializerReJoinType1( LoRaMacMessageReJoinType1_t* macMsg ); + +/*! + * Creates serialized MAC message of structured object. + * + * \param[IN/OUT] macMsg - Join-request message object + * \retval - Status of the operation + */ +LoRaMacSerializerStatus_t LoRaMacSerializerReJoinType0or2( LoRaMacMessageReJoinType0or2_t* macMsg ); + +/*! + * Creates serialized MAC message of structured object. + * + * \param[IN/OUT] macMsg - Data message object + * \retval - Status of the operation + */ +LoRaMacSerializerStatus_t LoRaMacSerializerData( LoRaMacMessageData_t* macMsg ); + +/*! \} addtogroup LORAMAC */ + +#ifdef __cplusplus +} +#endif + +#endif // __LORAMAC_SERIALIZER_H__ + diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacTest.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacTest.h new file mode 100644 index 0000000000..aa2cf2480c --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacTest.h @@ -0,0 +1,60 @@ +/*! + * \file LoRaMacTest.h + * + * \brief LoRa MAC layer test function implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \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__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*! + * \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 */ + +#ifdef __cplusplus +} +#endif + +#endif // __LORAMACTEST_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacTypes.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacTypes.h new file mode 100644 index 0000000000..1896a3760c --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/LoRaMacTypes.h @@ -0,0 +1,1309 @@ +/*! + * \file LoRaMacTypes.h + * + * \brief LoRa MAC layer internal types definition. Please do not include in application sources. + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * addtogroup LORAMAC + * \{ + * + */ +#ifndef __LORAMAC_TYPES_H__ +#define __LORAMAC_TYPES_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include +#include "timer.h" +#include "systime.h" + +/*! + * Start value for unicast keys enumeration + */ +#define LORAMAC_CRYPTO_UNICAST_KEYS 0 + +/*! + * Start value for multicast keys enumeration + */ +#define LORAMAC_CRYPTO_MULTICAST_KEYS 127 + +/*! + * Maximum number of multicast context + */ +#define LORAMAC_MAX_MC_CTX 4 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | SF12 - BW125 + * AU915 | SF10 - BW125 + * CN470 | SF12 - BW125 + * CN779 | SF12 - BW125 + * EU433 | SF12 - BW125 + * EU868 | SF12 - BW125 + * IN865 | SF12 - BW125 + * KR920 | SF12 - BW125 + * US915 | SF10 - BW125 + * RU864 | SF12 - BW125 + */ +#define DR_0 0 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | SF11 - BW125 + * AU915 | SF9 - BW125 + * CN470 | SF11 - BW125 + * CN779 | SF11 - BW125 + * EU433 | SF11 - BW125 + * EU868 | SF11 - BW125 + * IN865 | SF11 - BW125 + * KR920 | SF11 - BW125 + * US915 | SF9 - BW125 + * RU864 | SF11 - BW125 + */ +#define DR_1 1 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | SF10 - BW125 + * AU915 | SF8 - BW125 + * CN470 | SF10 - BW125 + * CN779 | SF10 - BW125 + * EU433 | SF10 - BW125 + * EU868 | SF10 - BW125 + * IN865 | SF10 - BW125 + * KR920 | SF10 - BW125 + * US915 | SF8 - BW125 + * RU864 | SF10 - BW125 + */ +#define DR_2 2 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | SF9 - BW125 + * AU915 | SF7 - BW125 + * CN470 | SF9 - BW125 + * CN779 | SF9 - BW125 + * EU433 | SF9 - BW125 + * EU868 | SF9 - BW125 + * IN865 | SF9 - BW125 + * KR920 | SF9 - BW125 + * US915 | SF7 - BW125 + * RU864 | SF9 - BW125 + */ +#define DR_3 3 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | SF8 - BW125 + * AU915 | SF8 - BW500 + * CN470 | SF8 - BW125 + * CN779 | SF8 - BW125 + * EU433 | SF8 - BW125 + * EU868 | SF8 - BW125 + * IN865 | SF8 - BW125 + * KR920 | SF8 - BW125 + * US915 | SF8 - BW500 + * RU864 | SF8 - BW125 + */ +#define DR_4 4 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | SF7 - BW125 + * AU915 | RFU + * CN470 | SF7 - BW125 + * CN779 | SF7 - BW125 + * EU433 | SF7 - BW125 + * EU868 | SF7 - BW125 + * IN865 | SF7 - BW125 + * KR920 | SF7 - BW125 + * US915 | RFU + * RU864 | SF7 - BW125 + */ +#define DR_5 5 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | SF7 - BW250 + * AU915 | RFU + * CN470 | SF12 - BW125 + * CN779 | SF7 - BW250 + * EU433 | SF7 - BW250 + * EU868 | SF7 - BW250 + * IN865 | SF7 - BW250 + * KR920 | RFU + * US915 | RFU + * RU864 | SF7 - BW250 + */ +#define DR_6 6 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | FSK + * AU915 | RFU + * CN470 | SF12 - BW125 + * CN779 | FSK + * EU433 | FSK + * EU868 | FSK + * IN865 | FSK + * KR920 | RFU + * US915 | RFU + * RU864 | FSK + */ +#define DR_7 7 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | RFU + * AU915 | SF12 - BW500 + * CN470 | RFU + * CN779 | RFU + * EU433 | RFU + * EU868 | RFU + * IN865 | RFU + * KR920 | RFU + * US915 | SF12 - BW500 + * RU864 | RFU + */ +#define DR_8 8 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | RFU + * AU915 | SF11 - BW500 + * CN470 | RFU + * CN779 | RFU + * EU433 | RFU + * EU868 | RFU + * IN865 | RFU + * KR920 | RFU + * US915 | SF11 - BW500 + * RU864 | RFU + */ +#define DR_9 9 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | RFU + * AU915 | SF10 - BW500 + * CN470 | RFU + * CN779 | RFU + * EU433 | RFU + * EU868 | RFU + * IN865 | RFU + * KR920 | RFU + * US915 | SF10 - BW500 + * RU864 | RFU + */ +#define DR_10 10 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | RFU + * AU915 | SF9 - BW500 + * CN470 | RFU + * CN779 | RFU + * EU433 | RFU + * EU868 | RFU + * IN865 | RFU + * KR920 | RFU + * US915 | SF9 - BW500 + * RU864 | RFU + */ +#define DR_11 11 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | RFU + * AU915 | SF8 - BW500 + * CN470 | RFU + * CN779 | RFU + * EU433 | RFU + * EU868 | RFU + * IN865 | RFU + * KR920 | RFU + * US915 | SF8 - BW500 + * RU864 | RFU + */ +#define DR_12 12 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | RFU + * AU915 | SF7 - BW500 + * CN470 | RFU + * CN779 | RFU + * EU433 | RFU + * EU868 | RFU + * IN865 | RFU + * KR920 | RFU + * US915 | SF7 - BW500 + * RU864 | RFU + */ +#define DR_13 13 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | RFU + * AU915 | RFU + * CN470 | RFU + * CN779 | RFU + * EU433 | RFU + * EU868 | RFU + * IN865 | RFU + * KR920 | RFU + * US915 | RFU + * RU864 | RFU + */ +#define DR_14 14 + +/*! + * Region | SF + * ------------ | :-----: + * AS923 | RFU + * AU915 | RFU + * CN470 | RFU + * CN779 | RFU + * EU433 | RFU + * EU868 | RFU + * IN865 | RFU + * KR920 | RFU + * US915 | RFU + * RU864 | RFU + */ +#define DR_15 15 + + + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | Max EIRP + * AU915 | Max EIRP + * CN470 | Max EIRP + * CN779 | Max EIRP + * EU433 | Max EIRP + * EU868 | Max EIRP + * IN865 | Max EIRP + * KR920 | Max EIRP + * US915 | Max ERP + * RU864 | Max EIRP + */ +#define TX_POWER_0 0 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | Max EIRP - 2 + * AU915 | Max EIRP - 2 + * CN470 | Max EIRP - 2 + * CN779 | Max EIRP - 2 + * EU433 | Max EIRP - 2 + * EU868 | Max EIRP - 2 + * IN865 | Max EIRP - 2 + * KR920 | Max EIRP - 2 + * US915 | Max ERP - 2 + * RU864 | Max EIRP - 2 + */ +#define TX_POWER_1 1 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | Max EIRP - 4 + * AU915 | Max EIRP - 4 + * CN470 | Max EIRP - 4 + * CN779 | Max EIRP - 4 + * EU433 | Max EIRP - 4 + * EU868 | Max EIRP - 4 + * IN865 | Max EIRP - 4 + * KR920 | Max EIRP - 4 + * US915 | Max ERP - 4 + * RU864 | Max EIRP - 4 + */ +#define TX_POWER_2 2 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | Max EIRP - 6 + * AU915 | Max EIRP - 6 + * CN470 | Max EIRP - 6 + * CN779 | Max EIRP - 6 + * EU433 | Max EIRP - 6 + * EU868 | Max EIRP - 6 + * IN865 | Max EIRP - 6 + * KR920 | Max EIRP - 6 + * US915 | Max ERP - 6 + * RU864 | Max EIRP - 6 + */ +#define TX_POWER_3 3 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | Max EIRP - 8 + * AU915 | Max EIRP - 8 + * CN470 | Max EIRP - 8 + * CN779 | Max EIRP - 8 + * EU433 | Max EIRP - 8 + * EU868 | Max EIRP - 8 + * IN865 | Max EIRP - 8 + * KR920 | Max EIRP - 8 + * US915 | Max ERP - 8 + * RU864 | Max EIRP - 8 + */ +#define TX_POWER_4 4 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | Max EIRP - 10 + * AU915 | Max EIRP - 10 + * CN470 | Max EIRP - 10 + * CN779 | Max EIRP - 10 + * EU433 | Max EIRP - 10 + * EU868 | Max EIRP - 10 + * IN865 | Max EIRP - 10 + * KR920 | Max EIRP - 10 + * US915 | Max ERP - 10 + * RU864 | Max EIRP - 10 + */ +#define TX_POWER_5 5 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | Max EIRP - 12 + * AU915 | Max EIRP - 12 + * CN470 | Max EIRP - 12 + * CN779 | - + * EU433 | - + * EU868 | Max EIRP - 12 + * IN865 | Max EIRP - 12 + * KR920 | Max EIRP - 12 + * US915 | Max ERP - 12 + * RU864 | Max EIRP - 12 + */ +#define TX_POWER_6 6 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | Max EIRP - 14 + * AU915 | Max EIRP - 14 + * CN470 | Max EIRP - 14 + * CN779 | - + * EU433 | - + * EU868 | Max EIRP - 14 + * IN865 | Max EIRP - 14 + * KR920 | Max EIRP - 14 + * US915 | Max ERP - 14 + * RU864 | Max EIRP - 14 + */ +#define TX_POWER_7 7 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | - + * AU915 | Max EIRP - 16 + * CN470 | - + * CN779 | - + * EU433 | - + * EU868 | - + * IN865 | Max EIRP - 16 + * KR920 | - + * US915 | Max ERP - 16 + * RU864 | - + */ +#define TX_POWER_8 8 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | - + * AU915 | Max EIRP - 18 + * CN470 | - + * CN779 | - + * EU433 | - + * EU868 | - + * IN865 | Max EIRP - 18 + * KR920 | - + * US915 | Max ERP - 18 + * RU864 | - + */ +#define TX_POWER_9 9 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | - + * AU915 | Max EIRP - 20 + * CN470 | - + * CN779 | - + * EU433 | - + * EU868 | - + * IN865 | Max EIRP - 20 + * KR920 | - + * US915 | Max ERP - 20 + * RU864 | - + */ +#define TX_POWER_10 10 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | - + * AU915 | Max EIRP - 22 + * CN470 | - + * CN779 | - + * EU433 | - + * EU868 | - + * IN865 | - + * KR920 | - + * US915 | Max ERP - 22 + * RU864 | - + */ +#define TX_POWER_11 11 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | - + * AU915 | Max EIRP - 24 + * CN470 | - + * CN779 | - + * EU433 | - + * EU868 | - + * IN865 | - + * KR920 | - + * US915 | Max ERP - 24 + * RU864 | - + */ +#define TX_POWER_12 12 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | - + * AU915 | Max EIRP - 26 + * CN470 | - + * CN779 | - + * EU433 | - + * EU868 | - + * IN865 | - + * KR920 | - + * US915 | Max ERP - 26 + * RU864 | - + */ +#define TX_POWER_13 13 + +/*! + * Region | dBM + * ------------ | :-----: + * AS923 | - + * AU915 | Max EIRP - 28 + * CN470 | - + * CN779 | - + * EU433 | - + * EU868 | - + * IN865 | - + * KR920 | - + * US915 | Max ERP - 28 + * RU864 | - + */ +#define TX_POWER_14 14 + +/*! + * RFU + */ +#define TX_POWER_15 15 + +/*! + * LoRaWAN devices classes definition + * + * LoRaWAN Specification V1.0.2, chapter 2.1 + */ +typedef enum DeviceClass_e +{ + /*! + * LoRaWAN device class A + * + * LoRaWAN Specification V1.0.2, chapter 3 + */ + CLASS_A = 0x00, + /*! + * LoRaWAN device class B + * + * LoRaWAN Specification V1.0.2, chapter 8 + */ + CLASS_B = 0x01, + /*! + * LoRaWAN device class C + * + * LoRaWAN Specification V1.0.2, chapter 17 + */ + CLASS_C = 0x02, +}DeviceClass_t; + +/*! + * LoRaWAN Frame type enumeration to differ between the possible data up/down frame configurations. + * + * Note: The naming is implementation specific since there is no definition + * in the LoRaWAN specification included. + */ +typedef enum eFType +{ + /*! + * Frame type A + * + * FOptsLen > 0, Fopt present, FPort > 0, FRMPayload present + */ + FRAME_TYPE_A, + /*! + * Frame type B + * + * FOptsLen > 0, Fopt present, FPort not present, FRMPayload not present + */ + FRAME_TYPE_B, + /*! + * Frame type C + * + * FOptsLen = 0, Fopt not present, FPort = 0 , FRMPayload containing MAC commands + */ + FRAME_TYPE_C, + /*! + * Frame type D + * + * FOptsLen = 0, Fopt not present, FPort > 0 , FRMPayload present + */ + FRAME_TYPE_D, +}FType_t; + +/*! + * LoRaWAN Frame counter identifier. + */ +typedef enum eFCntIdentifier +{ + /*! + * Uplink frame counter which is incremented with each uplink. + */ + FCNT_UP = 0, + /*! + * Network downlink frame counter which is incremented with each downlink on FPort 0 + * or when the FPort field is missing. + */ + N_FCNT_DOWN, + /*! + * Application downlink frame counter which is incremented with each downlink + * on a port different than 0. + */ + A_FCNT_DOWN, + /*! + * In case if the device is connected to a LoRaWAN 1.0 Server, + * this counter is used for every kind of downlink frame. + */ + FCNT_DOWN, + /*! + * Multicast downlink counter for index 0 + */ + MC_FCNT_DOWN_0, + /*! + * Multicast downlink counter for index 1 + */ + MC_FCNT_DOWN_1, + /*! + * Multicast downlink counter for index 2 + */ + MC_FCNT_DOWN_2, + /*! + * Multicast downlink counter for index 3 + */ + MC_FCNT_DOWN_3, + /*! + * RJcount1 is a counter incremented with every transmitted Type 1 Rejoin request. + */ + RJ_COUNT_0, + /*! + * RJcount0 is a counter incremented with every transmitted Type 0 or 2 Rejoin request. + */ + RJ_COUNT_1, +}FCntIdentifier_t; + +/*! + * LoRaMac Key identifier + */ +typedef enum eKeyIdentifier +{ + /*! + * Application root key + */ + APP_KEY = 0, + /*! + * Network root key + */ + NWK_KEY, + /*! + * Join session integrity key + */ + J_S_INT_KEY, + /*! + * Join session encryption key + */ + J_S_ENC_KEY, + /*! + * Forwarding Network session integrity key + */ + F_NWK_S_INT_KEY, + /*! + * Serving Network session integrity key + */ + S_NWK_S_INT_KEY, + /*! + * Network session encryption key + */ + NWK_S_ENC_KEY, + /*! + * Application session key + */ + APP_S_KEY, + /*! + * Multicast root key + */ + MC_ROOT_KEY, + /*! + * Multicast key encryption key + */ + MC_KE_KEY = LORAMAC_CRYPTO_MULTICAST_KEYS, + /*! + * Multicast root key index 0 + */ + MC_KEY_0, + /*! + * Multicast Application session key index 0 + */ + MC_APP_S_KEY_0, + /*! + * Multicast Network session key index 0 + */ + MC_NWK_S_KEY_0, + /*! + * Multicast root key index 1 + */ + MC_KEY_1, + /*! + * Multicast Application session key index 1 + */ + MC_APP_S_KEY_1, + /*! + * Multicast Network session key index 1 + */ + MC_NWK_S_KEY_1, + /*! + * Multicast root key index 2 + */ + MC_KEY_2, + /*! + * Multicast Application session key index 2 + */ + MC_APP_S_KEY_2, + /*! + * Multicast Network session key index 2 + */ + MC_NWK_S_KEY_2, + /*! + * Multicast root key index 3 + */ + MC_KEY_3, + /*! + * Multicast Application session key index 3 + */ + MC_APP_S_KEY_3, + /*! + * Multicast Network session key index 3 + */ + MC_NWK_S_KEY_3, + /*! + * Zero key for slot randomization in class B + */ + SLOT_RAND_ZERO_KEY, + /*! + * No Key + */ + NO_KEY, +}KeyIdentifier_t; + +/*! + * LoRaMac Crypto address identifier + */ +typedef enum eAddressIdentifier +{ + /*! + * Multicast Address for index 0 + */ + MULTICAST_0_ADDR = 0, + /*! + * Multicast Address for index 1 + */ + MULTICAST_1_ADDR = 1, + /*! + * Multicast Address for index 2 + */ + MULTICAST_2_ADDR = 2, + /*! + * Multicast Address for index 3 + */ + MULTICAST_3_ADDR = 3, + /*! + * Unicast End-device address + */ + UNICAST_DEV_ADDR = 4, +}AddressIdentifier_t; + +/* + * Multicast Rx window parameters + */ +typedef struct sMcRxParams +{ + /*! + * Multicats channel LoRaWAN class B or C + */ + DeviceClass_t Class; + union + { + struct + { + /*! + * Reception frequency of the ping slot windows + */ + uint32_t Frequency; + /*! + * Datarate of the ping slot + */ + int8_t Datarate; + /*! + * This parameter is necessary for class B operation. It defines the + * periodicity of the multicast downlink slots + */ + uint16_t Periodicity; + }ClassB; + struct + { + /*! + * Reception frequency of the ping slot windows + */ + uint32_t Frequency; + /*! + * Datarate of the ping slot + */ + int8_t Datarate; + }ClassC; + }Params; +}McRxParams_t; + +/*! + * Multicast channel + */ +typedef struct sMcChannelParams +{ + /*! + * Indicate if the multicast channel is being setup remotely or locally. + * Indicates which set of keys are to be used. \ref uMcKeys + */ + bool IsRemotelySetup; + /*! + * True if the entry is active + */ + bool IsEnabled; + /* + * Address identifier + */ + AddressIdentifier_t GroupID; + /*! + * Address + */ + uint32_t Address; + /*! + * Multicast keys + */ + union uMcKeys + { + /*! + * Encrypted multicast key - Used when IsRemotelySetup equals `true`. + * MC_KEY is decrypted and then the session keys ar derived. + */ + uint8_t *McKeyE; + /*! + * Multicast Session keys - Used when IsRemotelySetup equals `false` + */ + struct + { + /*! + * Multicast application session key + */ + uint8_t *McAppSKey; + /*! + * Multicast network session key + */ + uint8_t *McNwkSKey; + }Session; + }McKeys; + /*! + * Minimum multicast frame counter value + */ + uint32_t FCountMin; + /*! + * Maximum multicast frame counter value + */ + uint32_t FCountMax; + /*! + * Multicast reception parameters + */ + McRxParams_t RxParams; +}McChannelParams_t; + +/*! + * Multicast context + */ +typedef struct sMulticastCtx +{ + /*! + * Multicast channel parameters + */ + McChannelParams_t ChannelParams; + /*! + * Downlink counter + */ + uint32_t* DownLinkCounter; + /* + * Following parameters are only used for ClassB multicast channels + */ + /*! + * Number of multicast slots. The variable can be + * calculated as follows: + * PingNb = 128 / ( 1 << periodicity ), where + * 0 <= periodicity <= 7 + */ + uint8_t PingNb; + /*! + * Period of the multicast slots. The variable can be + * calculated as follows: + * PingPeriod = 4096 / PingNb + */ + uint16_t PingPeriod; + /*! + * Ping offset of the multicast channel for Class B + */ + uint16_t PingOffset; + /*! + * Set to 1, if the FPending bit is set + */ + uint8_t FPendingSet; +}MulticastCtx_t; + +/*! + * LoRaMac join-request / rejoin type identifier + */ +typedef enum eJoinReqIdentifier +{ + /*! + * Rejoin type 0 + */ + REJOIN_REQ_0 = 0x00, + /*! + * Rejoin type 1 + */ + REJOIN_REQ_1 = 0x01, + /*! + * Rejoin type 2 + */ + REJOIN_REQ_2 = 0x02, + /*! + * Join-request + */ + JOIN_REQ = 0xFF, +}JoinReqIdentifier_t; + +/*! + * LoRaMAC mote MAC commands + * + * LoRaWAN Specification V1.1.0, chapter 5, table 4 + */ +typedef enum eLoRaMacMoteCmd +{ + /*! + * ResetInd + */ + MOTE_MAC_RESET_IND = 0x01, + /*! + * 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, + /*! + * TXParamSetupAns + */ + MOTE_MAC_TX_PARAM_SETUP_ANS = 0x09, + /*! + * DlChannelAns + */ + MOTE_MAC_DL_CHANNEL_ANS = 0x0A, + /*! + * RekeyInd + */ + MOTE_MAC_REKEY_IND = 0x0B, + /*! + * DeviceTimeReq + */ + MOTE_MAC_DEVICE_TIME_REQ = 0x0D, + /*! + * ADRParamSetupAns + */ + MOTE_MAC_ADR_PARAM_SETUP_ANS = 0x0C, + /*! + * RejoinParamSetupAns + */ + MOTE_MAC_REJOIN_PARAM_ANS = 0x0F, + /*! + * DeviceModeInd ( Class C only ) + */ + MOTE_MAC_DEVICE_MODE_IND = 0x20, + /*! + * PingSlotInfoReq + */ + MOTE_MAC_PING_SLOT_INFO_REQ = 0x10, + /*! + * PingSlotFreqAns + */ + MOTE_MAC_PING_SLOT_CHANNEL_ANS = 0x11, + /*! + * BeaconTimingReq + */ + MOTE_MAC_BEACON_TIMING_REQ = 0x12, + /*! + * BeaconFreqAns + */ + MOTE_MAC_BEACON_FREQ_ANS = 0x13, +}LoRaMacMoteCmd_t; + +/*! + * LoRaMAC server MAC commands + * + * LoRaWAN Specification V1.1.0 chapter 5, table 4 + */ +typedef enum eLoRaMacSrvCmd +{ + /*! + * ResetInd + */ + SRV_MAC_RESET_CONF = 0x01, + /*! + * 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, + /*! + * NewChannelReq + */ + SRV_MAC_TX_PARAM_SETUP_REQ = 0x09, + /*! + * DlChannelReq + */ + SRV_MAC_DL_CHANNEL_REQ = 0x0A, + /*! + * RekeyConf + */ + SRV_MAC_REKEY_CONF = 0x0B, + /*! + * ADRParamSetupReq + */ + SRV_MAC_ADR_PARAM_SETUP_REQ = 0x0C, + /*! + * ForceRejoinReq + */ + SRV_MAC_FORCE_REJOIN_REQ = 0x0E, + /*! + * RejoinParamSetupReq + */ + SRV_MAC_REJOIN_PARAM_REQ = 0x0F, + /*! + * DeviceModeConf ( Class C only ) + */ + SRV_MAC_DEVICE_MODE_CONF = 0x20, + /*! + * DeviceTimeAns + */ + SRV_MAC_DEVICE_TIME_ANS = 0x0D, + /*! + * PingSlotInfoAns + */ + SRV_MAC_PING_SLOT_INFO_ANS = 0x10, + /*! + * PingSlotChannelReq + */ + SRV_MAC_PING_SLOT_CHANNEL_REQ = 0x11, + /*! + * BeaconTimingAns + */ + SRV_MAC_BEACON_TIMING_ANS = 0x12, + /*! + * BeaconFreqReq + */ + SRV_MAC_BEACON_FREQ_REQ = 0x13, +}LoRaMacSrvCmd_t; + +/*! + * LoRaMAC band parameters definition + */ +typedef struct sBand +{ + /*! + * Duty cycle + */ + uint16_t DCycle; + /*! + * Maximum Tx power + */ + int8_t TxMaxPower; + /*! + * The last time the band has been + * synchronized with the current time + */ + TimerTime_t LastBandUpdateTime; + /*! + * The last time we have assigned the max + * credits for the 24h interval. + */ + TimerTime_t LastMaxCreditAssignTime; + /*! + * Current time credits which are available. This + * is a value in ms + */ + TimerTime_t TimeCredits; + /*! + * Maximum time credits which are available. This + * is a value in ms + */ + TimerTime_t MaxTimeCredits; + /*! + * Set to true when the band is ready for use. + */ + bool ReadyForTransmission; +}Band_t; + +/*! + * LoRaMAC channels parameters definition + */ +typedef union uDrRange +{ + /*! + * Byte-access to the bits + */ + int8_t Value; + /*! + * Structure to store the minimum and the maximum datarate + */ + struct sFields + { + /*! + * Minimum data rate + * + * LoRaWAN Regional Parameters V1.0.2rB + * + * The allowed ranges are region specific. Please refer to \ref DR_0 to \ref DR_15 for details. + */ + int8_t Min : 4; + /*! + * Maximum data rate + * + * LoRaWAN Regional Parameters V1.0.2rB + * + * The allowed ranges are region specific. Please refer to \ref DR_0 to \ref DR_15 for details. + */ + int8_t Max : 4; + }Fields; +}DrRange_t; + +/*! + * LoRaMAC channel definition + */ +typedef struct sChannelParams +{ + /*! + * Frequency in Hz + */ + uint32_t Frequency; + /*! + * Alternative frequency for RX window 1 + */ + uint32_t Rx1Frequency; + /*! + * Data rate definition + */ + DrRange_t DrRange; + /*! + * Band index + */ + uint8_t Band; +}ChannelParams_t; + +/*! + * LoRaMAC frame types + * + * LoRaWAN Specification V1.0.2, chapter 4.2.1, table 1 + */ +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 Rejoin Request + */ + FRAME_TYPE_REJOIN = 0x06, + /*! + * LoRaMAC proprietary frame + */ + FRAME_TYPE_PROPRIETARY = 0x07, +}LoRaMacFrameType_t; + +/*! + * LoRaMAC Battery level indicator + */ +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; + +#ifdef __cplusplus +} +#endif + +#endif // __LORAMAC_TYPES_H__ + diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/board-config.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/board-config.h new file mode 100644 index 0000000000..d780a55af6 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/board-config.h @@ -0,0 +1,108 @@ +/*! + * \file board-config.h + * + * \brief Board configuration + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Alistair Jordan + * + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + */ +#ifndef __BOARD_CONFIG_H__ +#define __BOARD_CONFIG_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*! + * Defines the time required for the TCXO to wakeup [ms]. + */ +#define BOARD_TCXO_WAKEUP_TIME 5 + +/*! + * Board MCU pins definitions + */ +#define RADIO_RESET PC_0 + +#define RADIO_MOSI PA_7 +#define RADIO_MISO PA_6 +#define RADIO_SCLK PB_3 + +#define RADIO_NSS PA_15 + +#define RADIO_DIO_0 PB_4 +#define RADIO_DIO_1 PB_1 +#define RADIO_DIO_2 PB_0 +#define RADIO_DIO_3 PC_13 +#define RADIO_DIO_4 PA_5 +#define RADIO_DIO_5 PA_4 + +#define RADIO_TCXO_POWER PA_12 + +#define RADIO_ANT_SWITCH_RX PA_1 +#define RADIO_ANT_SWITCH_TX_BOOST PC_1 +#define RADIO_ANT_SWITCH_TX_RFO PC_2 + +#define LED_1 PB_5 +#define LED_2 PA_5 +#define LED_3 PB_6 +#define LED_4 PB_7 + +#define LED_GREEN LED_1 +#define LED_RED1 LED_2 +#define LED_BLUE LED_3 +#define LED_RED2 LED_4 + +#define BTN_1 PB_2 + +#define OSC_LSE_IN PC_14 +#define OSC_LSE_OUT PC_15 + +#define OSC_HSE_IN PH_0 +#define OSC_HSE_OUT PH_1 + +#define SWCLK PA_14 +#define SWDAT PA_13 + +#define I2C_SCL PB_8 +#define I2C_SDA PB_9 + +#define UART_TX PA_2 +#define UART_RX PA_3 + +// Debug pins definition. +#define RADIO_DBG_PIN_TX PB_13 +#define RADIO_DBG_PIN_RX PB_14 + +#ifdef __cplusplus +} +#endif + +#endif // __BOARD_CONFIG_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/board.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/board.c new file mode 100644 index 0000000000..849a298eaa --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/board.c @@ -0,0 +1,148 @@ +/*! + * \file board.c + * + * \brief Target board general functions implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * \endcode + * + * \author Alistair Jordan + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + */ +#include +#include "utilities.h" +#include "spi.h" +#include "board-config.h" +#include "sx1276-board.h" +#include "board.h" +#include +#include + +void BoardCriticalSectionBegin( uint32_t *mask ) +{ +} + +void BoardCriticalSectionEnd( uint32_t *mask ) +{ +} + +void BoardInitPeriph( void ) +{ + +} + +void BoardInitMcu( void ) +{ + + SpiInit( &SX1276.Spi, SPI_1); + SX1276IoInit( ); + + SX1276IoDbgInit( ); + SX1276IoTcxoInit( ); + +} + +void BoardResetMcu( void ) +{ +} + +void BoardDeInitMcu( void ) +{ + SpiDeInit( &SX1276.Spi ); + SX1276IoDeInit( ); +} + +uint32_t BoardGetRandomSeed( void ) +{ + srand(time(NULL)); // Initialization, should only be called once. + int r = rand(); // Returns a pseudo-random integer between 0 and RAND_MAX. + return (uint32_t)r; +} + +void BoardGetUniqueId( uint8_t *id ) +{ + *id = 127; +} + +uint16_t BoardBatteryMeasureVoltage( void ) +{ + return 0; +} + +uint32_t BoardGetBatteryVoltage( void ) +{ + return 0; +} + +uint8_t BoardGetBatteryLevel( void ) +{ + return 0; +} + +uint8_t GetBoardPowerSource( void ) +{ + return 255; +} + +/** + * \brief Enters Low Power Stop Mode + * + * \note ARM exists the function when waking up + */ +void LpmEnterStopMode( void) +{ +} + +/*! + * \brief Exists Low Power Stop Mode + */ +void LpmExitStopMode( void ) +{ +} + +/*! + * \brief Enters Low Power Sleep Mode + * + * \note ARM exits the function when waking up + */ +void LpmEnterSleepMode( void) +{ +} + +void BoardLowPowerHandler( void ) +{ +} + +/* + * Function Name : assert_failed + * Description : Reports the name of the source file and the source line number + * where the assert_param error has occurred. + * Input : - file: pointer to the source file name + * - line: assert_param error line source number + * Output : None + * Return : None + */ +void assert_failed( uint8_t* file, uint32_t line ) +{ + /* User can add his own implementation to report the file name and line number, + ex: printf("Wrong parameters value: file %s on line %lu\n", file, line) */ + + printf( "Wrong parameters value: file %s on line %u\n", ( const char* )file, line ); + /* Infinite loop */ + while( 1 ) + { + } +} \ No newline at end of file diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/board.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/board.h new file mode 100644 index 0000000000..3c816422ca --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/board.h @@ -0,0 +1,132 @@ +/*! + * \file board.h + * + * \brief Target board general functions implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + */ +#ifndef __BOARD_H__ +#define __BOARD_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include "utilities.h" +/*! + * Possible power sources + */ +enum BoardPowerSources +{ + USB_POWER = 0, + BATTERY_POWER, +}; + +/*! + * \brief Initializes the mcu. + */ +void BoardInitMcu( void ); + +/*! + * \brief Resets the mcu. + */ +void BoardResetMcu( void ); + +/*! + * \brief Initializes the boards peripherals. + */ +void BoardInitPeriph( void ); + +/*! + * \brief De-initializes the target board peripherals to decrease power + * consumption. + */ +void BoardDeInitMcu( void ); + +/*! + * \brief Gets the current potentiometer level value + * + * \retval value Potentiometer level ( value in percent ) + */ +uint8_t BoardGetPotiLevel( void ); + +/*! + * \brief Measure the Battery voltage + * + * \retval value battery voltage in volts + */ +uint32_t BoardGetBatteryVoltage( void ); + +/*! + * \brief Get the current battery level + * + * \retval value battery level [ 0: USB, + * 1: Min level, + * x: level + * 254: fully charged, + * 255: Error] + */ +uint8_t BoardGetBatteryLevel( void ); + +/*! + * \brief Get the current MCU temperature in degree celcius * 256 + * + * \retval temperature * 256 + */ +int16_t BoardGetTemperature( void ); + +/*! + * Returns a pseudo random seed generated using the MCU Unique ID + * + * \retval seed Generated pseudo random seed + */ +uint32_t BoardGetRandomSeed( void ); + +/*! + * \brief Gets the board 64 bits unique ID + * + * \param [IN] id Pointer to an array that will contain the Unique ID + */ +void BoardGetUniqueId( uint8_t *id ); + +/*! + * \brief Manages the entry into ARM cortex deep-sleep mode + */ +void BoardLowPowerHandler( void ); + +/*! + * \brief Get the board power source + * + * \retval value power source [0: USB_POWER, 1: BATTERY_POWER] + */ +uint8_t GetBoardPowerSource( void ); + +/*! + * \brief Get the board version + * + * \retval value Version + */ +Version_t BoardGetVersion( void ); + +#ifdef __cplusplus +} +#endif + +#endif // __BOARD_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/common/CayenneLpp.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/CayenneLpp.c new file mode 100644 index 0000000000..a2e33cf820 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/CayenneLpp.c @@ -0,0 +1,257 @@ +/*! + * \file CayenneLpp.c + * + * \brief Implements the Cayenne Low Power Protocol + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2018 Semtech + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + */ +#include + +#include "utilities.h" +#include "CayenneLpp.h" + +#define CAYENNE_LPP_MAXBUFFER_SIZE 242 + +static uint8_t CayenneLppBuffer[CAYENNE_LPP_MAXBUFFER_SIZE]; +static uint8_t CayenneLppCursor = 0; + +void CayenneLppInit( void ) +{ + CayenneLppCursor = 0; +} + +void CayenneLppReset( void ) +{ + CayenneLppCursor = 0; +} + +uint8_t CayenneLppGetSize( void ) +{ + return CayenneLppCursor; +} + +uint8_t* CayenneLppGetBuffer( void ) +{ + return CayenneLppBuffer; +} + +uint8_t CayenneLppCopy( uint8_t* dst ) +{ + memcpy1( dst, CayenneLppBuffer, CayenneLppCursor ); + + return CayenneLppCursor; +} + + +uint8_t CayenneLppAddDigitalInput( uint8_t channel, uint8_t value ) +{ + if( ( CayenneLppCursor + LPP_DIGITAL_INPUT_SIZE ) > CAYENNE_LPP_MAXBUFFER_SIZE ) + { + return 0; + } + CayenneLppBuffer[CayenneLppCursor++] = channel; + CayenneLppBuffer[CayenneLppCursor++] = LPP_DIGITAL_INPUT; + CayenneLppBuffer[CayenneLppCursor++] = value; + + return CayenneLppCursor; +} + +uint8_t CayenneLppAddDigitalOutput( uint8_t channel, uint8_t value ) +{ + if( ( CayenneLppCursor + LPP_DIGITAL_OUTPUT_SIZE ) > CAYENNE_LPP_MAXBUFFER_SIZE ) + { + return 0; + } + CayenneLppBuffer[CayenneLppCursor++] = channel; + CayenneLppBuffer[CayenneLppCursor++] = LPP_DIGITAL_OUTPUT; + CayenneLppBuffer[CayenneLppCursor++] = value; + + return CayenneLppCursor; +} + + +uint8_t CayenneLppAddAnalogInput( uint8_t channel, float value ) +{ + if( ( CayenneLppCursor + LPP_ANALOG_INPUT_SIZE ) > CAYENNE_LPP_MAXBUFFER_SIZE ) + { + return 0; + } + + int16_t val = ( int16_t ) ( value * 100 ); + CayenneLppBuffer[CayenneLppCursor++] = channel; + CayenneLppBuffer[CayenneLppCursor++] = LPP_ANALOG_INPUT; + CayenneLppBuffer[CayenneLppCursor++] = val >> 8; + CayenneLppBuffer[CayenneLppCursor++] = val; + + return CayenneLppCursor; +} + +uint8_t CayenneLppAddAnalogOutput( uint8_t channel, float value ) +{ + if( ( CayenneLppCursor + LPP_ANALOG_OUTPUT_SIZE ) > CAYENNE_LPP_MAXBUFFER_SIZE ) + { + return 0; + } + int16_t val = ( int16_t ) ( value * 100 ); + CayenneLppBuffer[CayenneLppCursor++] = channel; + CayenneLppBuffer[CayenneLppCursor++] = LPP_ANALOG_OUTPUT; + CayenneLppBuffer[CayenneLppCursor++] = val >> 8; + CayenneLppBuffer[CayenneLppCursor++] = val; + + return CayenneLppCursor; +} + + +uint8_t CayenneLppAddLuminosity( uint8_t channel, uint16_t lux ) +{ + if( ( CayenneLppCursor + LPP_LUMINOSITY_SIZE ) > CAYENNE_LPP_MAXBUFFER_SIZE ) + { + return 0; + } + CayenneLppBuffer[CayenneLppCursor++] = channel; + CayenneLppBuffer[CayenneLppCursor++] = LPP_LUMINOSITY; + CayenneLppBuffer[CayenneLppCursor++] = lux >> 8; + CayenneLppBuffer[CayenneLppCursor++] = lux; + + return CayenneLppCursor; +} + +uint8_t CayenneLppAddPresence( uint8_t channel, uint8_t value ) +{ + if( ( CayenneLppCursor + LPP_PRESENCE_SIZE ) > CAYENNE_LPP_MAXBUFFER_SIZE ) + { + return 0; + } + CayenneLppBuffer[CayenneLppCursor++] = channel; + CayenneLppBuffer[CayenneLppCursor++] = LPP_PRESENCE; + CayenneLppBuffer[CayenneLppCursor++] = value; + + return CayenneLppCursor; +} + +uint8_t CayenneLppAddTemperature( uint8_t channel, float celsius ) +{ + if( ( CayenneLppCursor + LPP_TEMPERATURE_SIZE ) > CAYENNE_LPP_MAXBUFFER_SIZE ) + { + return 0; + } + int16_t val = ( int16_t) ( celsius * 10 ); + CayenneLppBuffer[CayenneLppCursor++] = channel; + CayenneLppBuffer[CayenneLppCursor++] = LPP_TEMPERATURE; + CayenneLppBuffer[CayenneLppCursor++] = val >> 8; + CayenneLppBuffer[CayenneLppCursor++] = val; + + return CayenneLppCursor; +} + +uint8_t CayenneLppAddRelativeHumidity( uint8_t channel, float rh ) +{ + if( ( CayenneLppCursor + LPP_RELATIVE_HUMIDITY_SIZE ) > CAYENNE_LPP_MAXBUFFER_SIZE ) + { + return 0; + } + CayenneLppBuffer[CayenneLppCursor++] = channel; + CayenneLppBuffer[CayenneLppCursor++] = LPP_RELATIVE_HUMIDITY; + CayenneLppBuffer[CayenneLppCursor++] = (uint8_t ) ( rh * 2 ); + + return CayenneLppCursor; +} + +uint8_t CayenneLppAddAccelerometer( uint8_t channel, float x, float y, float z ) +{ + if( ( CayenneLppCursor + LPP_ACCELEROMETER_SIZE ) > CAYENNE_LPP_MAXBUFFER_SIZE ) + { + return 0; + } + int16_t vx = ( int16_t ) ( x * 1000 ); + int16_t vy = ( int16_t ) ( y * 1000 ); + int16_t vz = ( int16_t ) ( z * 1000 ); + + CayenneLppBuffer[CayenneLppCursor++] = channel; + CayenneLppBuffer[CayenneLppCursor++] = LPP_ACCELEROMETER; + CayenneLppBuffer[CayenneLppCursor++] = vx >> 8; + CayenneLppBuffer[CayenneLppCursor++] = vx; + CayenneLppBuffer[CayenneLppCursor++] = vy >> 8; + CayenneLppBuffer[CayenneLppCursor++] = vy; + CayenneLppBuffer[CayenneLppCursor++] = vz >> 8; + CayenneLppBuffer[CayenneLppCursor++] = vz; + + return CayenneLppCursor; +} + +uint8_t CayenneLppAddBarometricPressure( uint8_t channel, float hpa ) +{ + if( ( CayenneLppCursor + LPP_BAROMETRIC_PRESSURE_SIZE ) > CAYENNE_LPP_MAXBUFFER_SIZE ) + { + return 0; + } + int16_t val = ( int16_t ) ( hpa * 10 ); + + CayenneLppBuffer[CayenneLppCursor++] = channel; + CayenneLppBuffer[CayenneLppCursor++] = LPP_BAROMETRIC_PRESSURE; + CayenneLppBuffer[CayenneLppCursor++] = val >> 8; + CayenneLppBuffer[CayenneLppCursor++] = val; + + return CayenneLppCursor; +} + +uint8_t CayenneLppAddGyrometer( uint8_t channel, float x, float y, float z ) +{ + if( ( CayenneLppCursor + LPP_GYROMETER_SIZE ) > CAYENNE_LPP_MAXBUFFER_SIZE ) + { + return 0; + } + int16_t vx = ( int16_t ) ( x * 100 ); + int16_t vy = ( int16_t ) ( y * 100 ); + int16_t vz = ( int16_t ) ( z * 100 ); + + CayenneLppBuffer[CayenneLppCursor++] = channel; + CayenneLppBuffer[CayenneLppCursor++] = LPP_GYROMETER; + CayenneLppBuffer[CayenneLppCursor++] = vx >> 8; + CayenneLppBuffer[CayenneLppCursor++] = vx; + CayenneLppBuffer[CayenneLppCursor++] = vy >> 8; + CayenneLppBuffer[CayenneLppCursor++] = vy; + CayenneLppBuffer[CayenneLppCursor++] = vz >> 8; + CayenneLppBuffer[CayenneLppCursor++] = vz; + + return CayenneLppCursor; +} + +uint8_t CayenneLppAddGps( uint8_t channel, float latitude, float longitude, float meters ) +{ + if( ( CayenneLppCursor + LPP_GPS_SIZE ) > CAYENNE_LPP_MAXBUFFER_SIZE ) + { + return 0; + } + int32_t lat = ( int32_t ) ( latitude * 10000 ); + int32_t lon = ( int32_t ) ( longitude * 10000 ); + int32_t alt = ( int32_t ) ( meters * 100 ); + + CayenneLppBuffer[CayenneLppCursor++] = channel; + CayenneLppBuffer[CayenneLppCursor++] = LPP_GPS; + + CayenneLppBuffer[CayenneLppCursor++] = lat >> 16; + CayenneLppBuffer[CayenneLppCursor++] = lat >> 8; + CayenneLppBuffer[CayenneLppCursor++] = lat; + CayenneLppBuffer[CayenneLppCursor++] = lon >> 16; + CayenneLppBuffer[CayenneLppCursor++] = lon >> 8; + CayenneLppBuffer[CayenneLppCursor++] = lon; + CayenneLppBuffer[CayenneLppCursor++] = alt >> 16; + CayenneLppBuffer[CayenneLppCursor++] = alt >> 8; + CayenneLppBuffer[CayenneLppCursor++] = alt; + + return CayenneLppCursor; +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/common/CayenneLpp.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/CayenneLpp.h new file mode 100644 index 0000000000..01cec2e9af --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/CayenneLpp.h @@ -0,0 +1,84 @@ +/*! + * \file CayenneLpp.h + * + * \brief Implements the Cayenne Low Power Protocol + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2018 Semtech + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + */ +#ifndef __CAYENNE_LPP_H__ +#define __CAYENNE_LPP_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define LPP_DIGITAL_INPUT 0 // 1 byte +#define LPP_DIGITAL_OUTPUT 1 // 1 byte +#define LPP_ANALOG_INPUT 2 // 2 bytes, 0.01 signed +#define LPP_ANALOG_OUTPUT 3 // 2 bytes, 0.01 signed +#define LPP_LUMINOSITY 101 // 2 bytes, 1 lux unsigned +#define LPP_PRESENCE 102 // 1 byte, 1 +#define LPP_TEMPERATURE 103 // 2 bytes, 0.1°C signed +#define LPP_RELATIVE_HUMIDITY 104 // 1 byte, 0.5% unsigned +#define LPP_ACCELEROMETER 113 // 2 bytes per axis, 0.001G +#define LPP_BAROMETRIC_PRESSURE 115 // 2 bytes 0.1 hPa Unsigned +#define LPP_GYROMETER 134 // 2 bytes per axis, 0.01 °/s +#define LPP_GPS 136 // 3 byte lon/lat 0.0001 °, 3 bytes alt 0.01m + + +// Data ID + Data Type + Data Size +#define LPP_DIGITAL_INPUT_SIZE 3 +#define LPP_DIGITAL_OUTPUT_SIZE 3 +#define LPP_ANALOG_INPUT_SIZE 4 +#define LPP_ANALOG_OUTPUT_SIZE 4 +#define LPP_LUMINOSITY_SIZE 4 +#define LPP_PRESENCE_SIZE 3 +#define LPP_TEMPERATURE_SIZE 4 +#define LPP_RELATIVE_HUMIDITY_SIZE 3 +#define LPP_ACCELEROMETER_SIZE 8 +#define LPP_BAROMETRIC_PRESSURE_SIZE 4 +#define LPP_GYROMETER_SIZE 8 +#define LPP_GPS_SIZE 11 + +void CayenneLppInit( void ); + +void CayenneLppReset( void ); +uint8_t CayenneLppGetSize( void ); +uint8_t* CayenneLppGetBuffer( void ); +uint8_t CayenneLppCopy( uint8_t* buffer ); + +uint8_t CayenneLppAddDigitalInput( uint8_t channel, uint8_t value ); +uint8_t CayenneLppAddDigitalOutput( uint8_t channel, uint8_t value ); + +uint8_t CayenneLppAddAnalogInput( uint8_t channel, float value ); +uint8_t CayenneLppAddAnalogOutput( uint8_t channel, float value ); + +uint8_t CayenneLppAddLuminosity( uint8_t channel, uint16_t lux ); +uint8_t CayenneLppAddPresence( uint8_t channel, uint8_t value ); +uint8_t CayenneLppAddTemperature( uint8_t channel, float celsius ); +uint8_t CayenneLppAddRelativeHumidity( uint8_t channel, float rh ); +uint8_t CayenneLppAddAccelerometer( uint8_t channel, float x, float y, float z ); +uint8_t CayenneLppAddBarometricPressure( uint8_t channel, float hpa ); +uint8_t CayenneLppAddGyrometer( uint8_t channel, float x, float y, float z ); +uint8_t CayenneLppAddGps( uint8_t channel, float latitude, float longitude, float meters ); + +#ifdef __cplusplus +} +#endif + +#endif // __CAYENNE_LPP_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/common/Commissioning.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/Commissioning.h new file mode 100644 index 0000000000..bb5ce36b1c --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/Commissioning.h @@ -0,0 +1,72 @@ +/*! + * \file Commissioning.h + * + * \brief End-device commissioning parameters + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2020 Semtech + * + * \endcode + */ +#ifndef __COMMISSIONING_H__ +#define __COMMISSIONING_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + ****************************************************************************** + ********************************** WARNING *********************************** + ****************************************************************************** + + The LoRaWAN AES128 keys are stored and provisionned on secure-elements. + + This project providdes a software emulated secure-element. + The LoRaWAN AES128 keys SHALL be updated under + src/peripherals/-se\se-identity.h file. + + ****************************************************************************** + ****************************************************************************** + ****************************************************************************** + */ +#include "se-identity.h" + +/*! + * When set to 1 the application uses the Over-the-Air activation procedure + * When set to 0 the application uses the Personalization activation procedure + */ +#define OVER_THE_AIR_ACTIVATION 1 + +/*! + * When using ABP activation the MAC layer must know in advance to which server + * version it will be connected. + */ +#define ABP_ACTIVATION_LRWAN_VERSION_V10x 0x01000400 // 1.0.4.0 +#define ABP_ACTIVATION_LRWAN_VERSION_V11x 0x01010100 // 1.1.1.0 + +#define ABP_ACTIVATION_LRWAN_VERSION ABP_ACTIVATION_LRWAN_VERSION_V11x + +/*! + * Indicates if the end-device is to be connected to a private or public network + */ +#define LORAWAN_PUBLIC_NETWORK true + +/*! + * Current network ID + */ +#define LORAWAN_NETWORK_ID ( uint32_t )0 + +#ifdef __cplusplus +} +#endif + +#endif // __COMMISSIONING_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/LmHandler.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/LmHandler.c new file mode 100644 index 0000000000..ebe59fefe1 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/LmHandler.c @@ -0,0 +1,1100 @@ +/*! + * \file LmHandler.c + * + * \brief Implements the LoRaMac layer handling. + * Provides the possibility to register applicative packages. + * + * \remark Inspired by the examples provided on the en.i-cube_lrwan fork. + * MCD Application Team ( STMicroelectronics International ) + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2018 Semtech + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + */ +#include +#include +#include +#include "utilities.h" +#include "timer.h" +#include "Commissioning.h" +//#include "NvmDataMgmt.h" +#include "radio.h" +#include "LmHandler.h" +#include "LmhPackage.h" +#include "LmhpCompliance.h" +#include "LmhpClockSync.h" +#include "LmhpRemoteMcastSetup.h" +#include "LmhpFragmentation.h" + +#include "LoRaMacTest.h" + +static CommissioningParams_t CommissioningParams = +{ + .IsOtaaActivation = OVER_THE_AIR_ACTIVATION, + .DevEui = { 0 }, // Automatically filed from secure-element + .JoinEui = { 0 }, // Automatically filed from secure-element + .SePin = { 0 }, // Automatically filed from secure-element + .NetworkId = LORAWAN_NETWORK_ID, + .DevAddr = LORAWAN_DEVICE_ADDRESS, +}; + +static LmhPackage_t *LmHandlerPackages[PKG_MAX_NUMBER]; + +/*! + * Upper layer LoRaMac parameters + */ +static LmHandlerParams_t *LmHandlerParams; + +/*! + * Upper layer callbacks + */ +static LmHandlerCallbacks_t *LmHandlerCallbacks; + +/*! + * Used to notify LmHandler of LoRaMac events + */ +static LoRaMacPrimitives_t LoRaMacPrimitives; + +/*! + * LoRaMac callbacks + */ +static LoRaMacCallback_t LoRaMacCallbacks; + +static LmHandlerJoinParams_t JoinParams = +{ + .CommissioningParams = &CommissioningParams, + .Datarate = DR_0, + .Status = LORAMAC_HANDLER_ERROR +}; + +static LmHandlerTxParams_t TxParams = +{ + .CommissioningParams = &CommissioningParams, + .MsgType = LORAMAC_HANDLER_UNCONFIRMED_MSG, + .AckReceived = 0, + .Datarate = DR_0, + .UplinkCounter = 0, + .AppData = + { + .Port = 0, + .BufferSize = 0, + .Buffer = NULL, + }, + .TxPower = TX_POWER_0, + .Channel = 0, +}; + +static LmHandlerRxParams_t RxParams = +{ + .CommissioningParams = &CommissioningParams, + .Rssi = 0, + .Snr = 0, + .DownlinkCounter = 0, + .RxSlot = -1, +}; + +static LoRaMacHandlerBeaconParams_t BeaconParams = +{ + .State = LORAMAC_HANDLER_BEACON_ACQUIRING, + .Info = + { + .Time = { .Seconds = 0, .SubSeconds = 0 }, + .Frequency = 0, + .Datarate = 0, + .Rssi = 0, + .Snr = 0, + .GwSpecific = + { + .InfoDesc = 0, + .Info = { 0 }, + }, + }, +}; + +/*! + * Indicates if a switch to Class B operation is pending or not. + * + * TODO: Create a new structure to store the current handler states/status + * and add the below variable to it. + */ +static bool IsClassBSwitchPending = false; + +/*! + * Stores the time to wait before next transmission + * + * TODO: Create a new structure to store the current handler states/status + * and add the below variable to it. + */ +static TimerTime_t DutyCycleWaitTime = 0; + +/*! + * Indicates if an uplink is pending upon MAC layer request + * + * TODO: Create a new structure to store the current handler states/status + * and add the below variable to it. + */ +static bool IsUplinkTxPending = false; + +/*! + * \brief MCPS-Confirm event function + * + * \param [IN] mcpsConfirm - Pointer to the confirm structure, + * containing confirm attributes. + */ +static void McpsConfirm( McpsConfirm_t *mcpsConfirm ); + +/*! + * \brief MCPS-Indication event function + * + * \param [IN] mcpsIndication - Pointer to the indication structure, + * containing indication attributes. + */ +static void McpsIndication( McpsIndication_t *mcpsIndication ); + +/*! + * \brief MLME-Confirm event function + * + * \param [IN] MlmeConfirm - Pointer to the confirm structure, + * containing confirm attributes. + */ +static void MlmeConfirm( MlmeConfirm_t *mlmeConfirm ); + +/*! + * \brief MLME-Indication event function + * + * \param [IN] mlmeIndication - Pointer to the indication structure, + * containing indication attributes. + */ +static void MlmeIndication( MlmeIndication_t *mlmeIndication ); + +/*! + * Calls the OnClassChange callback to indicate the new active device class + * + * \param [in] deviceClass Active device class + */ +static void OnClassChangeNotify( DeviceClass_t deviceClass ) +{ + if( LmHandlerCallbacks->OnClassChange != NULL ) + { + LmHandlerCallbacks->OnClassChange( deviceClass ); + } +} + +/*! + * Calls the OnBeaconStatusChange callback to indicate current beacon status + * + * \param [in] params Current beacon parameters + */ +static void OnBeaconStatusChangeNotify( LoRaMacHandlerBeaconParams_t *params ) +{ + if( LmHandlerCallbacks->OnBeaconStatusChange != NULL ) + { + LmHandlerCallbacks->OnBeaconStatusChange( params ); + } +} + +/*! + * Starts the beacon search + * + * \retval status Returns \ref LORAMAC_HANDLER_SET if joined else \ref LORAMAC_HANDLER_RESET + */ +static LmHandlerErrorStatus_t LmHandlerBeaconReq( void ); + +/* + *============================================================================= + * PACKAGES HANDLING + *============================================================================= + */ +typedef enum PackageNotifyTypes_e +{ + PACKAGE_MCPS_CONFIRM, + PACKAGE_MCPS_INDICATION, + PACKAGE_MLME_CONFIRM, + PACKAGE_MLME_INDICATION, +}PackageNotifyTypes_t; + +/*! + * Notifies the package to process the LoRaMac callbacks. + * + * \param [IN] notifyType MAC notification type [PACKAGE_MCPS_CONFIRM, + * PACKAGE_MCPS_INDICATION, + * PACKAGE_MLME_CONFIRM, + * PACKAGE_MLME_INDICATION] + * \param[IN] params Notification parameters. The params type can be + * [McpsConfirm_t, McpsIndication_t, MlmeConfirm_t, MlmeIndication_t] + */ +static void LmHandlerPackagesNotify( PackageNotifyTypes_t notifyType, void *params ); + +static bool LmHandlerPackageIsTxPending( void ); + +static void LmHandlerPackagesProcess( void ); + +LmHandlerErrorStatus_t LmHandlerInit( LmHandlerCallbacks_t *handlerCallbacks, + LmHandlerParams_t *handlerParams ) +{ + // + uint16_t nbNvmData = 0; + MibRequestConfirm_t mibReq; + LmHandlerParams = handlerParams; + LmHandlerCallbacks = handlerCallbacks; + + LoRaMacPrimitives.MacMcpsConfirm = McpsConfirm; + LoRaMacPrimitives.MacMcpsIndication = McpsIndication; + LoRaMacPrimitives.MacMlmeConfirm = MlmeConfirm; + LoRaMacPrimitives.MacMlmeIndication = MlmeIndication; + LoRaMacCallbacks.GetBatteryLevel = LmHandlerCallbacks->GetBatteryLevel; + LoRaMacCallbacks.GetTemperatureLevel = LmHandlerCallbacks->GetTemperature; + // LoRaMacCallbacks.NvmDataChange = NvmDataMgmtEvent; + LoRaMacCallbacks.MacProcessNotify = LmHandlerCallbacks->OnMacProcess; + + IsClassBSwitchPending = false; + IsUplinkTxPending = false; + + if( LoRaMacInitialization( &LoRaMacPrimitives, &LoRaMacCallbacks, LmHandlerParams->Region ) != LORAMAC_STATUS_OK ) + { + return LORAMAC_HANDLER_ERROR; + } + + // // Restore data if required + // nbNvmData = NvmDataMgmtRestore( ); + + // // Try to restore from NVM and query the mac if possible. + // if( ( LmHandlerCallbacks->OnNvmDataChange != NULL ) && ( nbNvmData > 0 ) ) + // { + // LmHandlerCallbacks->OnNvmDataChange( LORAMAC_HANDLER_NVM_RESTORE, nbNvmData ); + // } + // else + { + // Configure the default datarate + mibReq.Type = MIB_CHANNELS_DEFAULT_DATARATE; + mibReq.Param.ChannelsDefaultDatarate = LmHandlerParams->TxDatarate; + LoRaMacMibSetRequestConfirm( &mibReq ); + + mibReq.Type = MIB_CHANNELS_DATARATE; + mibReq.Param.ChannelsDatarate = LmHandlerParams->TxDatarate; + LoRaMacMibSetRequestConfirm( &mibReq ); + +#if( OVER_THE_AIR_ACTIVATION == 0 ) + // Tell the MAC layer which network server version are we connecting too. + mibReq.Type = MIB_ABP_LORAWAN_VERSION; + mibReq.Param.AbpLrWanVersion.Value = ABP_ACTIVATION_LRWAN_VERSION; + LoRaMacMibSetRequestConfirm( &mibReq ); + + mibReq.Type = MIB_NET_ID; + mibReq.Param.NetID = LORAWAN_NETWORK_ID; + LoRaMacMibSetRequestConfirm( &mibReq ); + +#if( STATIC_DEVICE_ADDRESS != 1 ) + // TODO: Remove STATIC_DEVICE_ADDRESS = 0. Up to the application to decide what to do + // Random seed initialization + if( LmHandlerCallbacks->GetRandomSeed != NULL ) + { + srand1( LmHandlerCallbacks->GetRandomSeed( ) ); + } + // Choose a random device address + CommissioningParams.DevAddr = randr( 0, 0x01FFFFFF ); +#endif + + mibReq.Type = MIB_DEV_ADDR; + mibReq.Param.DevAddr = CommissioningParams.DevAddr; + LoRaMacMibSetRequestConfirm( &mibReq ); +#endif // #if( OVER_THE_AIR_ACTIVATION == 0 ) + } + + // Read secure-element DEV_EUI, JOI_EUI and SE_PIN values. + mibReq.Type = MIB_DEV_EUI; + LoRaMacMibGetRequestConfirm( &mibReq ); + memcpy1( CommissioningParams.DevEui, mibReq.Param.DevEui, 8 ); + + mibReq.Type = MIB_JOIN_EUI; + LoRaMacMibGetRequestConfirm( &mibReq ); + memcpy1( CommissioningParams.JoinEui, mibReq.Param.JoinEui, 8 ); + + mibReq.Type = MIB_SE_PIN; + LoRaMacMibGetRequestConfirm( &mibReq ); + memcpy1( CommissioningParams.SePin, mibReq.Param.SePin, 4 ); + + mibReq.Type = MIB_PUBLIC_NETWORK; + mibReq.Param.EnablePublicNetwork = LmHandlerParams->PublicNetworkEnable; + LoRaMacMibSetRequestConfirm( &mibReq ); + + mibReq.Type = MIB_ADR; + mibReq.Param.AdrEnable = LmHandlerParams->AdrEnable; + LoRaMacMibSetRequestConfirm( &mibReq ); + + LoRaMacTestSetDutyCycleOn( LmHandlerParams->DutyCycleEnabled ); + + LoRaMacStart( ); + + mibReq.Type = MIB_NETWORK_ACTIVATION; + if( LoRaMacMibGetRequestConfirm( &mibReq ) == LORAMAC_STATUS_OK ) + { + if( mibReq.Param.NetworkActivation == ACTIVATION_TYPE_NONE ) + { + if( LmHandlerCallbacks->OnNetworkParametersChange != NULL ) + { + LmHandlerCallbacks->OnNetworkParametersChange( &CommissioningParams ); + } + } + } + return LORAMAC_HANDLER_SUCCESS; +} + +bool LmHandlerIsBusy( void ) +{ + if( LoRaMacIsBusy( ) == true ) + { + return true; + } + if( LmHandlerJoinStatus( ) != LORAMAC_HANDLER_SET ) + { + // The network isn't yet joined, try again later. + LmHandlerJoin( ); + return true; + } + + if( LmHandlerPackageIsTxPending( ) == true ) + { + return true; + } + + return false; +} + +void LmHandlerProcess( void ) +{ + uint16_t size = 0; + + // Process Radio IRQ + if( Radio.IrqProcess != NULL ) + { + Radio.IrqProcess( ); + } + + // Processes the LoRaMac events + LoRaMacProcess( ); + + // // Store to NVM if required + // size = NvmDataMgmtStore( ); + + // if( size > 0 ) + // { + // if( LmHandlerCallbacks->OnNvmDataChange != NULL ) + // { + // LmHandlerCallbacks->OnNvmDataChange( LORAMAC_HANDLER_NVM_STORE, size ); + // } + // } + + // Call all packages process functions + LmHandlerPackagesProcess( ); + + // Check if a package transmission is pending. + // If it is the case exit function earlier + if( LmHandlerPackageIsTxPending( ) == true ) + { + return; + } + + // If a MAC layer scheduled uplink is still pending try to send it. + if( IsUplinkTxPending == true ) + { + // Send an empty message + LmHandlerAppData_t appData = + { + .Buffer = NULL, + .BufferSize = 0, + .Port = 0, + }; + + if( LmHandlerSend( &appData, LmHandlerParams->IsTxConfirmed ) == LORAMAC_HANDLER_SUCCESS ) + { + IsUplinkTxPending = false; + } + } +} + +TimerTime_t LmHandlerGetDutyCycleWaitTime( void ) +{ + return DutyCycleWaitTime; +} + +/*! + * Join a LoRa Network in classA + * + * \Note if the device is ABP, this is a pass through function + * + * \param [IN] isOtaa Indicates which activation mode must be used + */ +static void LmHandlerJoinRequest( bool isOtaa ) +{ + MlmeReq_t mlmeReq; + + mlmeReq.Type = MLME_JOIN; + mlmeReq.Req.Join.Datarate = LmHandlerParams->TxDatarate; + + if( isOtaa == true ) + { + mlmeReq.Req.Join.NetworkActivation = ACTIVATION_TYPE_OTAA; + // Update commissioning parameters activation type variable. + CommissioningParams.IsOtaaActivation = true; + } + else + { + mlmeReq.Req.Join.NetworkActivation = ACTIVATION_TYPE_ABP; + // Update commissioning parameters activation type variable. + CommissioningParams.IsOtaaActivation = false; + } + // Starts the join procedure + LoRaMacStatus_t status = LoRaMacMlmeRequest( &mlmeReq ); + if( LmHandlerCallbacks->OnMacMlmeRequest != NULL ) + { + LmHandlerCallbacks->OnMacMlmeRequest( status, &mlmeReq, mlmeReq.ReqReturn.DutyCycleWaitTime ); + } + DutyCycleWaitTime = mlmeReq.ReqReturn.DutyCycleWaitTime; +} + +void LmHandlerJoin( void ) +{ + LmHandlerJoinRequest( CommissioningParams.IsOtaaActivation ); +} + +LmHandlerFlagStatus_t LmHandlerJoinStatus( void ) +{ + MibRequestConfirm_t mibReq; + LoRaMacStatus_t status; + + mibReq.Type = MIB_NETWORK_ACTIVATION; + status = LoRaMacMibGetRequestConfirm( &mibReq ); + + if( status == LORAMAC_STATUS_OK ) + { + if( mibReq.Param.NetworkActivation == ACTIVATION_TYPE_NONE ) + { + return LORAMAC_HANDLER_RESET; + } + else + { + return LORAMAC_HANDLER_SET; + } + } + else + { + return LORAMAC_HANDLER_RESET; + } +} + +LmHandlerErrorStatus_t LmHandlerSend( LmHandlerAppData_t *appData, LmHandlerMsgTypes_t isTxConfirmed ) +{ + LoRaMacStatus_t status; + McpsReq_t mcpsReq; + LoRaMacTxInfo_t txInfo; + + if( LmHandlerJoinStatus( ) != LORAMAC_HANDLER_SET ) + { + // The network isn't joined, try again. + LmHandlerJoinRequest( CommissioningParams.IsOtaaActivation ); + return LORAMAC_HANDLER_ERROR; + } + + TxParams.MsgType = isTxConfirmed; + mcpsReq.Type = ( isTxConfirmed == LORAMAC_HANDLER_UNCONFIRMED_MSG ) ? MCPS_UNCONFIRMED : MCPS_CONFIRMED; + mcpsReq.Req.Unconfirmed.Datarate = LmHandlerParams->TxDatarate; + if( LoRaMacQueryTxPossible( appData->BufferSize, &txInfo ) != LORAMAC_STATUS_OK ) + { + // Send empty frame in order to flush MAC commands + mcpsReq.Type = MCPS_UNCONFIRMED; + mcpsReq.Req.Unconfirmed.fBuffer = NULL; + mcpsReq.Req.Unconfirmed.fBufferSize = 0; + } + else + { + mcpsReq.Req.Unconfirmed.fPort = appData->Port; + mcpsReq.Req.Unconfirmed.fBufferSize = appData->BufferSize; + mcpsReq.Req.Unconfirmed.fBuffer = appData->Buffer; + } + + TxParams.AppData = *appData; + TxParams.Datarate = LmHandlerParams->TxDatarate; + + status = LoRaMacMcpsRequest( &mcpsReq ); + if( LmHandlerCallbacks->OnMacMcpsRequest != NULL ) + { + LmHandlerCallbacks->OnMacMcpsRequest( status, &mcpsReq, mcpsReq.ReqReturn.DutyCycleWaitTime ); + } + DutyCycleWaitTime = mcpsReq.ReqReturn.DutyCycleWaitTime; + + if( status == LORAMAC_STATUS_OK ) + { + IsUplinkTxPending = false; + return LORAMAC_HANDLER_SUCCESS; + } + else + { + return LORAMAC_HANDLER_ERROR; + } +} + +LmHandlerErrorStatus_t LmHandlerDeviceTimeReq( void ) +{ + LoRaMacStatus_t status; + MlmeReq_t mlmeReq; + + mlmeReq.Type = MLME_DEVICE_TIME; + + status = LoRaMacMlmeRequest( &mlmeReq ); + if( LmHandlerCallbacks->OnMacMlmeRequest != NULL ) + { + LmHandlerCallbacks->OnMacMlmeRequest( status, &mlmeReq, mlmeReq.ReqReturn.DutyCycleWaitTime ); + } + DutyCycleWaitTime = mlmeReq.ReqReturn.DutyCycleWaitTime; + + if( status == LORAMAC_STATUS_OK ) + { + return LORAMAC_HANDLER_SUCCESS; + } + else + { + return LORAMAC_HANDLER_ERROR; + } +} + +static LmHandlerErrorStatus_t LmHandlerBeaconReq( void ) +{ + LoRaMacStatus_t status; + MlmeReq_t mlmeReq; + + mlmeReq.Type = MLME_BEACON_ACQUISITION; + + status = LoRaMacMlmeRequest( &mlmeReq ); + if( LmHandlerCallbacks->OnMacMlmeRequest != NULL ) + { + LmHandlerCallbacks->OnMacMlmeRequest( status, &mlmeReq, mlmeReq.ReqReturn.DutyCycleWaitTime ); + } + DutyCycleWaitTime = mlmeReq.ReqReturn.DutyCycleWaitTime; + + if( status == LORAMAC_STATUS_OK ) + { + return LORAMAC_HANDLER_SUCCESS; + } + else + { + return LORAMAC_HANDLER_ERROR; + } +} + +LmHandlerErrorStatus_t LmHandlerPingSlotReq( uint8_t periodicity ) +{ + LoRaMacStatus_t status; + MlmeReq_t mlmeReq; + + mlmeReq.Type = MLME_PING_SLOT_INFO; + mlmeReq.Req.PingSlotInfo.PingSlot.Fields.Periodicity = periodicity; + mlmeReq.Req.PingSlotInfo.PingSlot.Fields.RFU = 0; + + status = LoRaMacMlmeRequest( &mlmeReq ); + if( LmHandlerCallbacks->OnMacMlmeRequest != NULL ) + { + LmHandlerCallbacks->OnMacMlmeRequest( status, &mlmeReq, mlmeReq.ReqReturn.DutyCycleWaitTime ); + } + DutyCycleWaitTime = mlmeReq.ReqReturn.DutyCycleWaitTime; + + if( status == LORAMAC_STATUS_OK ) + { + // Send an empty message + LmHandlerAppData_t appData = + { + .Buffer = NULL, + .BufferSize = 0, + .Port = 0, + }; + return LmHandlerSend( &appData, LmHandlerParams->IsTxConfirmed ); + } + else + { + return LORAMAC_HANDLER_ERROR; + } +} + +LmHandlerErrorStatus_t LmHandlerRequestClass( DeviceClass_t newClass ) +{ + MibRequestConfirm_t mibReq; + DeviceClass_t currentClass; + LmHandlerErrorStatus_t errorStatus = LORAMAC_HANDLER_SUCCESS; + + mibReq.Type = MIB_DEVICE_CLASS; + LoRaMacMibGetRequestConfirm( &mibReq ); + currentClass = mibReq.Param.Class; + + // Attempt to switch only if class update + if( currentClass != newClass ) + { + switch( newClass ) + { + case CLASS_A: + { + if( currentClass != CLASS_A ) + { + mibReq.Param.Class = CLASS_A; + if( LoRaMacMibSetRequestConfirm( &mibReq ) == LORAMAC_STATUS_OK ) + { + // Switch is instantaneous + OnClassChangeNotify( CLASS_A ); + } + else + { + errorStatus = LORAMAC_HANDLER_ERROR; + } + } + } + break; + case CLASS_B: + { + if( IsClassBSwitchPending == false ) + { + if( currentClass != CLASS_A ) + { + errorStatus = LORAMAC_HANDLER_ERROR; + } + // Beacon must first be acquired + errorStatus = LmHandlerDeviceTimeReq( ); + if( errorStatus == LORAMAC_HANDLER_SUCCESS) + { + IsClassBSwitchPending = true; + } + } + } + break; + case CLASS_C: + { + if( currentClass != CLASS_A ) + { + errorStatus = LORAMAC_HANDLER_ERROR; + } + // Switch is instantaneous + mibReq.Param.Class = CLASS_C; + if( LoRaMacMibSetRequestConfirm( &mibReq ) == LORAMAC_STATUS_OK ) + { + OnClassChangeNotify( CLASS_C ); + } + else + { + errorStatus = LORAMAC_HANDLER_ERROR; + } + } + break; + default: + break; + } + } + return errorStatus; +} + +DeviceClass_t LmHandlerGetCurrentClass( void ) +{ + MibRequestConfirm_t mibReq; + + mibReq.Type = MIB_DEVICE_CLASS; + LoRaMacMibGetRequestConfirm( &mibReq ); + + return mibReq.Param.Class; +} + +int8_t LmHandlerGetCurrentDatarate( void ) +{ + MibRequestConfirm_t mibGet; + + mibGet.Type = MIB_CHANNELS_DATARATE; + LoRaMacMibGetRequestConfirm( &mibGet ); + + return mibGet.Param.ChannelsDatarate; +} + +LoRaMacRegion_t LmHandlerGetActiveRegion( void ) +{ + return LmHandlerParams->Region; +} + +LmHandlerErrorStatus_t LmHandlerSetSystemMaxRxError( uint32_t maxErrorInMs ) +{ + MibRequestConfirm_t mibReq; + + mibReq.Type = MIB_SYSTEM_MAX_RX_ERROR; + mibReq.Param.SystemMaxRxError = maxErrorInMs; + if( LoRaMacMibSetRequestConfirm( &mibReq ) != LORAMAC_STATUS_OK ) + { + return LORAMAC_HANDLER_ERROR; + } + return LORAMAC_HANDLER_SUCCESS; +} + +/* + *============================================================================= + * LORAMAC NOTIFICATIONS HANDLING + *============================================================================= + */ + +static void McpsConfirm( McpsConfirm_t *mcpsConfirm ) +{ + TxParams.IsMcpsConfirm = 1; + TxParams.Status = mcpsConfirm->Status; + TxParams.Datarate = mcpsConfirm->Datarate; + TxParams.UplinkCounter = mcpsConfirm->UpLinkCounter; + TxParams.TxPower = mcpsConfirm->TxPower; + TxParams.Channel = mcpsConfirm->Channel; + TxParams.AckReceived = mcpsConfirm->AckReceived; + + if( LmHandlerCallbacks->OnTxData != NULL ) + { + LmHandlerCallbacks->OnTxData( &TxParams ); + } + + LmHandlerPackagesNotify( PACKAGE_MCPS_CONFIRM, mcpsConfirm ); +} + +static void McpsIndication( McpsIndication_t *mcpsIndication ) +{ + LmHandlerAppData_t appData; + + RxParams.IsMcpsIndication = 1; + RxParams.Status = mcpsIndication->Status; + + if( RxParams.Status != LORAMAC_EVENT_INFO_STATUS_OK ) + { + return; + } + + RxParams.Datarate = mcpsIndication->RxDatarate; + RxParams.Rssi = mcpsIndication->Rssi; + RxParams.Snr = mcpsIndication->Snr; + RxParams.DownlinkCounter = mcpsIndication->DownLinkCounter; + RxParams.RxSlot = mcpsIndication->RxSlot; + + appData.Port = mcpsIndication->Port; + appData.BufferSize = mcpsIndication->BufferSize; + appData.Buffer = mcpsIndication->Buffer; + + if( LmHandlerCallbacks->OnRxData != NULL ) + { + LmHandlerCallbacks->OnRxData( &appData, &RxParams ); + } + + if( mcpsIndication->DeviceTimeAnsReceived == true ) + { + if( LmHandlerCallbacks->OnSysTimeUpdate != NULL ) + { +#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) + // Provide fix values. DeviceTimeAns is accurate + LmHandlerCallbacks->OnSysTimeUpdate( true, 0 ); +#else + LmHandlerCallbacks->OnSysTimeUpdate( ); +#endif + } + } + // Call packages RxProcess function + LmHandlerPackagesNotify( PACKAGE_MCPS_INDICATION, mcpsIndication ); + + if( mcpsIndication->IsUplinkTxPending != 0 ) + { + // The server signals that it has pending data to be sent. + // We schedule an uplink as soon as possible to flush the server. + IsUplinkTxPending = true; + } +} + +static void MlmeConfirm( MlmeConfirm_t *mlmeConfirm ) +{ + TxParams.IsMcpsConfirm = 0; + TxParams.Status = mlmeConfirm->Status; + if( LmHandlerCallbacks->OnTxData != NULL ) + { + LmHandlerCallbacks->OnTxData( &TxParams ); + } + + LmHandlerPackagesNotify( PACKAGE_MLME_CONFIRM, mlmeConfirm ); + + switch( mlmeConfirm->MlmeRequest ) + { + case MLME_JOIN: + { + MibRequestConfirm_t mibReq; + mibReq.Type = MIB_DEV_ADDR; + LoRaMacMibGetRequestConfirm( &mibReq ); + JoinParams.CommissioningParams->DevAddr = mibReq.Param.DevAddr; + JoinParams.Datarate = LmHandlerGetCurrentDatarate( ); + + if( mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK ) + { + // Status is OK, node has joined the network + JoinParams.Status = LORAMAC_HANDLER_SUCCESS; + } + else + { + // Join was not successful. Try to join again + JoinParams.Status = LORAMAC_HANDLER_ERROR; + } + // Notify upper layer + if( LmHandlerCallbacks->OnJoinRequest != NULL ) + { + LmHandlerCallbacks->OnJoinRequest( &JoinParams ); + } + } + break; + case MLME_LINK_CHECK: + { + // Check DemodMargin + // Check NbGateways + } + break; + case MLME_DEVICE_TIME: + { + if( IsClassBSwitchPending == true ) + { + LmHandlerBeaconReq( ); + } + } + break; + case MLME_BEACON_ACQUISITION: + { + if( mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK ) + { + // Beacon has been acquired + // Request server for ping slot + LmHandlerPingSlotReq( LmHandlerParams->PingSlotPeriodicity ); + } + else + { + // Beacon not acquired + // Request Device Time again. + LmHandlerDeviceTimeReq( ); + } + } + break; + case MLME_PING_SLOT_INFO: + { + if( mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK ) + { + MibRequestConfirm_t mibReq; + + // Class B is now activated + mibReq.Type = MIB_DEVICE_CLASS; + mibReq.Param.Class = CLASS_B; + LoRaMacMibSetRequestConfirm( &mibReq ); + // Notify upper layer + OnClassChangeNotify( CLASS_B ); + IsClassBSwitchPending = false; + } + else + { + LmHandlerPingSlotReq( LmHandlerParams->PingSlotPeriodicity ); + } + } + break; + default: + break; + } +} + +static void MlmeIndication( MlmeIndication_t *mlmeIndication ) +{ + RxParams.IsMcpsIndication = 0; + RxParams.Status = mlmeIndication->Status; + if( RxParams.Status != LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED ) + { + if( LmHandlerCallbacks->OnRxData != NULL ) + { + LmHandlerCallbacks->OnRxData( NULL, &RxParams ); + } + } + + // Call packages RxProcess function + LmHandlerPackagesNotify( PACKAGE_MLME_INDICATION, mlmeIndication ); + + switch( mlmeIndication->MlmeIndication ) + { + case MLME_BEACON_LOST: + { + MibRequestConfirm_t mibReq; + // Switch to class A again + mibReq.Type = MIB_DEVICE_CLASS; + mibReq.Param.Class = CLASS_A; + LoRaMacMibSetRequestConfirm( &mibReq ); + + BeaconParams.State = LORAMAC_HANDLER_BEACON_LOST; + BeaconParams.Info.Time.Seconds = 0; + BeaconParams.Info.GwSpecific.InfoDesc = 0; + memset1( BeaconParams.Info.GwSpecific.Info, 0, 6 ); + + OnClassChangeNotify( CLASS_A ); + OnBeaconStatusChangeNotify( &BeaconParams ); + + LmHandlerDeviceTimeReq( ); + } + break; + case MLME_BEACON: + { + if( mlmeIndication->Status == LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED ) + { + BeaconParams.State = LORAMAC_HANDLER_BEACON_RX; + BeaconParams.Info = mlmeIndication->BeaconInfo; + + OnBeaconStatusChangeNotify( &BeaconParams ); + } + else + { + BeaconParams.State = LORAMAC_HANDLER_BEACON_NRX; + BeaconParams.Info = mlmeIndication->BeaconInfo; + + OnBeaconStatusChangeNotify( &BeaconParams ); + } + break; + } + default: + break; + } +} + +/* + *============================================================================= + * PACKAGES HANDLING + *============================================================================= + */ + +LmHandlerErrorStatus_t LmHandlerPackageRegister( uint8_t id, void *params ) +{ + LmhPackage_t *package = NULL; + switch( id ) + { + case PACKAGE_ID_COMPLIANCE: + { + package = LmphCompliancePackageFactory( ); + break; + } + case PACKAGE_ID_CLOCK_SYNC: + { + package = LmphClockSyncPackageFactory( ); + break; + } + case PACKAGE_ID_REMOTE_MCAST_SETUP: + { + package = LmhpRemoteMcastSetupPackageFactory( ); + break; + } + case PACKAGE_ID_FRAGMENTATION: + { + package = LmhpFragmentationPackageFactory( ); + break; + } + } + if( package != NULL ) + { + LmHandlerPackages[id] = package; + LmHandlerPackages[id]->OnMacMcpsRequest = LmHandlerCallbacks->OnMacMcpsRequest; + LmHandlerPackages[id]->OnMacMlmeRequest = LmHandlerCallbacks->OnMacMlmeRequest; + LmHandlerPackages[id]->OnJoinRequest = LmHandlerJoinRequest; + LmHandlerPackages[id]->OnDeviceTimeRequest = LmHandlerDeviceTimeReq; + LmHandlerPackages[id]->OnSysTimeUpdate = LmHandlerCallbacks->OnSysTimeUpdate; + LmHandlerPackages[id]->Init( params, LmHandlerParams->DataBuffer, LmHandlerParams->DataBufferMaxSize ); + + return LORAMAC_HANDLER_SUCCESS; + } + else + { + return LORAMAC_HANDLER_ERROR; + } +} + +bool LmHandlerPackageIsInitialized( uint8_t id ) +{ + if( LmHandlerPackages[id]->IsInitialized != NULL ) + { + return LmHandlerPackages[id]->IsInitialized( ); + } + else + { + return false; + } +} + +static void LmHandlerPackagesNotify( PackageNotifyTypes_t notifyType, void *params ) +{ + for( int8_t i = 0; i < PKG_MAX_NUMBER; i++ ) + { + if( LmHandlerPackages[i] != NULL ) + { + switch( notifyType ) + { + case PACKAGE_MCPS_CONFIRM: + { + if( LmHandlerPackages[i]->OnMcpsConfirmProcess != NULL ) + { + LmHandlerPackages[i]->OnMcpsConfirmProcess( ( McpsConfirm_t* ) params ); + } + break; + } + case PACKAGE_MCPS_INDICATION: + { + if( LmHandlerPackages[i]->OnMcpsIndicationProcess != NULL ) + { + LmHandlerPackages[i]->OnMcpsIndicationProcess( ( McpsIndication_t* )params ); + } + break; + } + case PACKAGE_MLME_CONFIRM: + { + if( LmHandlerPackages[i]->OnMlmeConfirmProcess != NULL ) + { + LmHandlerPackages[i]->OnMlmeConfirmProcess( ( MlmeConfirm_t* )params ); + } + break; + } + case PACKAGE_MLME_INDICATION: + { + if( LmHandlerPackages[i]->OnMlmeIndicationProcess != NULL ) + { + LmHandlerPackages[i]->OnMlmeIndicationProcess( params ); + } + break; + } + } + } + } +} + +static bool LmHandlerPackageIsTxPending( void ) +{ + for( int8_t i = 0; i < PKG_MAX_NUMBER; i++ ) + { + if( LmHandlerPackages[i] != NULL ) + { + if( LmHandlerPackages[i]->IsTxPending( ) == true ) + { + return true; + } + } + } + return false; +} + +static void LmHandlerPackagesProcess( void ) +{ + for( int8_t i = 0; i < PKG_MAX_NUMBER; i++ ) + { + if( ( LmHandlerPackages[i] != NULL ) && + ( LmHandlerPackages[i]->Process != NULL ) && + ( LmHandlerPackageIsInitialized( i ) != false ) ) + { + LmHandlerPackages[i]->Process( ); + } + } +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/LmHandler.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/LmHandler.h new file mode 100644 index 0000000000..eefd388fb3 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/LmHandler.h @@ -0,0 +1,356 @@ +/*! + * \file LmHandler.h + * + * \brief Implements the LoRaMac layer handling. + * Provides the possibility to register applicative packages. + * + * \remark Inspired by the examples provided on the en.i-cube_lrwan fork. + * MCD Application Team ( STMicroelectronics International ) + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2018 Semtech + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + */ +#ifndef __LORAMAC_HANDLER_H__ +#define __LORAMAC_HANDLER_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "LmHandlerTypes.h" + +typedef struct LmHandlerJoinParams_s +{ + CommissioningParams_t *CommissioningParams; + int8_t Datarate; + LmHandlerErrorStatus_t Status; +}LmHandlerJoinParams_t; + +typedef struct LmHandlerTxParams_s +{ + uint8_t IsMcpsConfirm; + LoRaMacEventInfoStatus_t Status; + CommissioningParams_t *CommissioningParams; + LmHandlerMsgTypes_t MsgType; + uint8_t AckReceived; + int8_t Datarate; + uint32_t UplinkCounter; + LmHandlerAppData_t AppData; + int8_t TxPower; + uint8_t Channel; +}LmHandlerTxParams_t; + +typedef struct LmHandlerRxParams_s +{ + uint8_t IsMcpsIndication; + LoRaMacEventInfoStatus_t Status; + CommissioningParams_t *CommissioningParams; + int8_t Datarate; + int8_t Rssi; + int8_t Snr; + uint32_t DownlinkCounter; + int8_t RxSlot; +}LmHandlerRxParams_t; + +typedef struct LoRaMacHandlerBeaconParams_s +{ + LoRaMacEventInfoStatus_t Status; + LmHandlerBeaconState_t State; + BeaconInfo_t Info; +}LoRaMacHandlerBeaconParams_t; + +typedef struct LmHandlerParams_s +{ + /*! + * Region + */ + LoRaMacRegion_t Region; + /*! + * Holds the ADR state + */ + bool AdrEnable; + /*! + * Uplink frame type + */ + LmHandlerMsgTypes_t IsTxConfirmed; + /*! + * Uplink datarate, when \ref AdrEnable is OFF + */ + int8_t TxDatarate; + /*! + * Enables/Disables a public network usage + */ + bool PublicNetworkEnable; + /*! + * LoRaWAN ETSI duty cycle control enable/disable + * + * \remark Please note that ETSI mandates duty cycled transmissions. Use only for test purposes + */ + bool DutyCycleEnabled; + /*! + * Application data buffer maximum size + */ + uint8_t DataBufferMaxSize; + /*! + * Application data buffer pointer + */ + uint8_t *DataBuffer; + /*! + * Class B ping-slot periodicity. + */ + uint8_t PingSlotPeriodicity; +}LmHandlerParams_t; + +typedef struct LmHandlerCallbacks_s +{ + /*! + * Get the current battery level + * + * \retval value Battery level ( 0: very low, 254: fully charged ) + */ + uint8_t ( *GetBatteryLevel )( void ); + /*! + * Get the current temperature + * + * \retval value Temperature in degree Celsius + */ + float ( *GetTemperature )( void ); + /*! + * Returns a pseudo random seed generated using the MCU Unique ID + * + * \retval seed Generated pseudo random seed + */ + uint32_t ( *GetRandomSeed )( void ); + /*! + *\brief Will be called each time a Radio IRQ is handled by the MAC + * layer. + * + *\warning Runs in a IRQ context. Should only change variables state. + */ + void ( *OnMacProcess )( void ); + /*! + * Notifies the upper layer that the NVM context has changed + * + * \param [IN] state Indicates if we are storing (true) or + * restoring (false) the NVM context + * + * \param [IN] size Number of data bytes which were stored or restored. + */ + void ( *OnNvmDataChange )( LmHandlerNvmContextStates_t state, uint16_t size ); + /*! + * Notifies the upper layer that a network parameters have been set + * + * \param [IN] params notification parameters + */ + void ( *OnNetworkParametersChange )( CommissioningParams_t *params ); + /*! + * Notifies the upper layer that a MCPS request has been made to the MAC layer + * + * \param [IN] status - Request returned status + * \param [IN] mcpsRequest - Performed MCPS-Request. Refer to \ref McpsReq_t. + * \param [IN] nextTxDelay - Time to wait until another TX is possible. + */ + void ( *OnMacMcpsRequest )( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxDelay ); + /*! + * Notifies the upper layer that a MLME request has been made to the MAC layer + * + * \param [IN] status - Request returned status + * \param [IN] mlmeRequest - Performed MLME-Request. Refer to \ref MlmeReq_t. + * \param [IN] nextTxDelay - Time to wait until another TX is possible. + */ + void ( *OnMacMlmeRequest )( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxDelay ); + /*! + * Notifies the upper layer that a network has been joined + * + * \param [IN] params notification parameters + */ + void ( *OnJoinRequest )( LmHandlerJoinParams_t *params ); + /*! + * Notifies upper layer that a frame has been transmitted + * + * \param [IN] params notification parameters + */ + void ( *OnTxData )( LmHandlerTxParams_t *params ); + /*! + * Notifies the upper layer that an applicative frame has been received + * + * \param [IN] appData Received applicative data + * \param [IN] params notification parameters + */ + void ( *OnRxData )( LmHandlerAppData_t *appData, LmHandlerRxParams_t *params ); + /*! + * Confirms the LoRaWAN device class change + * + * \param [IN] deviceClass New end-device class + */ + void ( *OnClassChange )( DeviceClass_t deviceClass ); + /*! + * Notifies the upper layer that the beacon status has changed + * + * \param [IN] params notification parameters + */ + void ( *OnBeaconStatusChange )( LoRaMacHandlerBeaconParams_t *params ); +#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) + /*! + * Notifies the upper layer that the system time has been updated. + * + * \param [in] isSynchronized Indicates if the system time is synchronized in the range +/-1 second + * \param [in] timeCorrection Received time correction value + */ + void ( *OnSysTimeUpdate )( bool isSynchronized, int32_t timeCorrection ); +#else + /*! + * Notifies the upper layer that the system time has been updated. + */ + void ( *OnSysTimeUpdate )( void ); +#endif +}LmHandlerCallbacks_t; + +/*! + * LoRaMac handler initialisation + * + * \param [IN] callbacks LoRaMac handler callbacks + * \param [IN] handlerParams LoRaMac handler parameters + * + * \retval none + */ +LmHandlerErrorStatus_t LmHandlerInit( LmHandlerCallbacks_t *callbacks, + LmHandlerParams_t *handlerParams ); + +/*! + * Indicates if the LoRaMacHandler is busy + * + * \retval status [true] Busy, [false] free + */ +bool LmHandlerIsBusy( void ); + +/*! + * Processes the LoRaMac and Radio events. + * When no pendig operation asks to go in low power mode. + * + * \remark This function must be called in the main loop. + */ +void LmHandlerProcess( void ); + +/*! + * Gets current duty-cycle wait time + * + * \retval time to wait in ms + */ +TimerTime_t LmHandlerGetDutyCycleWaitTime( void ); + +/*! + * Instructs the MAC layer to send a ClassA uplink + * + * \param [IN] appData Data to be sent + * \param [IN] isTxConfirmed Indicates if the uplink requires an acknowledgement + * + * \retval status Returns \ref LORAMAC_HANDLER_SUCCESS if request has been + * processed else \ref LORAMAC_HANDLER_ERROR + */ +LmHandlerErrorStatus_t LmHandlerSend( LmHandlerAppData_t *appData, LmHandlerMsgTypes_t isTxConfirmed ); + +/*! + * Join a LoRa Network in classA + * + * \Note if the device is ABP, this is a pass through function + */ +void LmHandlerJoin( void ); + +/*! + * Check whether the Device is joined to the network + * + * \param [IN] none + * + * \retval status Returns \ref LORAMAC_HANDLER_SET if joined else \ref LORAMAC_HANDLER_RESET + */ +LmHandlerFlagStatus_t LmHandlerJoinStatus( void ); + +/*! + * Informs the server on the ping-slot periodicity to use + * + * \param [IN] periodicity Is equal to 2^periodicity seconds. + * Example: 2^3 = 8 seconds. The end-device will open an Rx slot every 8 seconds. + * + * \retval status Returns \ref LORAMAC_HANDLER_SUCCESS if request has been + * processed else \ref LORAMAC_HANDLER_ERROR + */ +LmHandlerErrorStatus_t LmHandlerPingSlotReq( uint8_t periodicity ); + +/*! + * Request the MAC layer to change LoRaWAN class + * + * \Note Callback \ref LmHandlerConfirmClass informs upper layer that the change has occurred + * \Note Only switch from class A to class B/C OR from class B/C to class A is allowed + * + * \param [IN] newClass New class to be requested + * + * \retval status Returns \ref LORAMAC_HANDLER_SUCCESS if request has been + * processed else \ref LORAMAC_HANDLER_ERROR + */ +LmHandlerErrorStatus_t LmHandlerRequestClass( DeviceClass_t newClass ); + +/*! + * Gets the current LoRaWAN class + * + * \retval currentClass Current LoRaWAN class + */ +DeviceClass_t LmHandlerGetCurrentClass( void ); + +/*! + * Gets the current datarate + * + * \retval currentDatarate Current datarate + */ +int8_t LmHandlerGetCurrentDatarate( void ); + +/*! + * Gets the current active region + * + * \retval currentRegion Current active region + */ +LoRaMacRegion_t LmHandlerGetActiveRegion( void ); + +/*! + * Set system maximum tolerated rx error in milliseconds + * + * \param [IN] maxErrorInMs Maximum tolerated error in milliseconds + * + * \retval status Returns \ref LORAMAC_HANDLER_SUCCESS if request has been + * processed else \ref LORAMAC_HANDLER_ERROR + */ +LmHandlerErrorStatus_t LmHandlerSetSystemMaxRxError( uint32_t maxErrorInMs ); + +/*! + * Requests network server time update + * + * \retval status Returns \ref LORAMAC_HANDLER_SET if joined else \ref LORAMAC_HANDLER_RESET + */ +LmHandlerErrorStatus_t LmHandlerDeviceTimeReq( void ); + +/* + *============================================================================= + * PACKAGES HANDLING + *============================================================================= + */ +LmHandlerErrorStatus_t LmHandlerPackageRegister( uint8_t id, void *params ); +bool LmHandlerPackageIsInitialized( uint8_t id ); + +#ifdef __cplusplus +} +#endif + +#endif // __LORAMAC_HANDLER_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/LmHandlerTypes.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/LmHandlerTypes.h new file mode 100644 index 0000000000..e51dcb3043 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/LmHandlerTypes.h @@ -0,0 +1,132 @@ +/*! + * \file LmHandlerTypes.h + * + * \brief Defines the types used by LmHandler + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2018 Semtech + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + */ +#ifndef __LORAMAC_HANDLER_TYPES_H__ +#define __LORAMAC_HANDLER_TYPES_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "LoRaMac.h" + +/*! + * If set to 1 the new API defining \ref OnSysTimeUpdate callback is used. + */ +#define LMH_SYS_TIME_UPDATE_NEW_API 1 + +/*! + * + */ +typedef enum +{ + LORAMAC_HANDLER_ADR_OFF = 0, + LORAMAC_HANDLER_ADR_ON = !LORAMAC_HANDLER_ADR_OFF +}LmHandlerAdrStates_t; + +/*! + * + */ +typedef enum +{ + LORAMAC_HANDLER_RESET = 0, + LORAMAC_HANDLER_SET = !LORAMAC_HANDLER_RESET +}LmHandlerFlagStatus_t; + +/*! + * + */ +typedef enum +{ + LORAMAC_HANDLER_ERROR = -1, + LORAMAC_HANDLER_SUCCESS = 0 +}LmHandlerErrorStatus_t; + +/*! + * + */ +typedef enum +{ + LORAMAC_HANDLER_UNCONFIRMED_MSG = 0, + LORAMAC_HANDLER_CONFIRMED_MSG = !LORAMAC_HANDLER_UNCONFIRMED_MSG +}LmHandlerMsgTypes_t; + +/*! + * + */ +typedef enum +{ + LORAMAC_HANDLER_FALSE = 0, + LORAMAC_HANDLER_TRUE = !LORAMAC_HANDLER_FALSE +}LmHandlerBoolean_t; + +typedef enum +{ + LORAMAC_HANDLER_BEACON_ACQUIRING, + LORAMAC_HANDLER_BEACON_LOST, + LORAMAC_HANDLER_BEACON_RX, + LORAMAC_HANDLER_BEACON_NRX +}LmHandlerBeaconState_t; + +typedef enum +{ + LORAMAC_HANDLER_NVM_RESTORE, + LORAMAC_HANDLER_NVM_STORE, +}LmHandlerNvmContextStates_t; + +/*! + * Commissioning parameters + */ +typedef struct CommissioningParams_s +{ + bool IsOtaaActivation; + uint8_t DevEui[8]; + uint8_t JoinEui[8]; + uint8_t SePin[4]; + uint32_t NetworkId; + uint32_t DevAddr; +}CommissioningParams_t; + +/*! + * Application data structure + */ +typedef struct LmHandlerAppData_s +{ + uint8_t Port; + uint8_t BufferSize; + uint8_t *Buffer; +}LmHandlerAppData_t; + +typedef struct LmHandlerRequestParams_s +{ + uint8_t IsMcpsRequest; + LoRaMacStatus_t Status; + union + { + Mcps_t Mcps; + Mlme_t Mlme; + }RequestType; +}LmHandlerRequestParams_t; + +#ifdef __cplusplus +} +#endif + +#endif // __LORAMAC_HANDLER_TYPES_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/packages/FragDecoder.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/packages/FragDecoder.c new file mode 100644 index 0000000000..439fba9c2e --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/packages/FragDecoder.c @@ -0,0 +1,751 @@ +/*! + * \file FragDecoder.c + * + * \brief Implements the LoRa-Alliance fragmentation decoder + * Specification: https://lora-alliance.org/sites/default/files/2018-09/fragmented_data_block_transport_v1.0.0.pdf + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2018 Semtech + * + * \endcode + * + * \author Fabien Holin ( Semtech ) + * \author Miguel Luis ( Semtech ) + */ +#include +#include +#include "utilities.h" +#include "FragDecoder.h" + +#define DBG_TRACE 0 + +#if DBG_TRACE == 1 + #include + /*! + * Works in the same way as the printf function does. + */ + #define DBG( ... ) \ + do \ + { \ + printf( __VA_ARGS__ ); \ + }while( 0 ) +#else + #define DBG( fmt, ... ) +#endif + + +/* + *============================================================================= + * Fragmentation decoder algorithm utilities + *============================================================================= + */ + +typedef struct +{ +#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) + FragDecoderCallbacks_t *Callbacks; +#else + uint8_t *File; + uint32_t FileSize; +#endif + uint16_t FragNb; + uint8_t FragSize; + + uint32_t M2BLine; + uint8_t MatrixM2B[( ( FRAG_MAX_REDUNDANCY >> 3 ) + 1 ) * FRAG_MAX_REDUNDANCY]; + uint16_t FragNbMissingIndex[FRAG_MAX_NB]; + + uint8_t S[( FRAG_MAX_REDUNDANCY >> 3 ) + 1]; + + FragDecoderStatus_t Status; +}FragDecoder_t; + +#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) +/*! + * \brief Sets a row from source into file destination + * + * \param [IN] src Source buffer pointer + * \param [IN] row Destination index of the row to be copied + * \param [IN] size Source number of bytes to be copied + */ +static void SetRow( uint8_t *src, uint16_t row, uint16_t size ); +#else +/*! + * \brief Sets a row from source into destination + * + * \param [IN] dst Destination buffer pointer + * \param [IN] src Source buffer pointer + * \param [IN] row Destination index of the row to be copied + * \param [IN] size Source number of bytes to be copied + */ +static void SetRow( uint8_t *dst, uint8_t *src, uint16_t row, uint16_t size ); +#endif + +#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) +/*! + * \brief Gets a row from source and stores it into file destination + * + * \param [IN] src Source buffer pointer + * \param [IN] row Source index of the row to be copied + * \param [IN] size Source number of bytes to be copied + */ +static void GetRow( uint8_t *src, uint16_t row, uint16_t size ); +#else +/*! + * \brief Gets a row from source and stores it into destination + * + * \param [IN] dst Destination buffer pointer + * \param [IN] src Source buffer pointer + * \param [IN] row Source index of the row to be copied + * \param [IN] size Source number of bytes to be copied + */ +static void GetRow( uint8_t *dst, uint8_t *src, uint16_t row, uint16_t size ); +#endif + +/*! + * \brief Gets the parity value from a given row of the parity matrix + * + * \param [IN] index The index of the row to be computed + * \param [IN] matrixRow Pointer to the parity matrix (parity bit array) + * + * \retval parity Parity value at the given index + */ +static uint8_t GetParity( uint16_t index, uint8_t *matrixRow ); + +/*! + * \brief Sets the parity value on the given row of the parity matrix + * + * \param [IN] index The index of the row to be computed + * \param [IN/OUT] matrixRow Pointer to the parity matrix. + * \param [IN] parity The parity value to be set in the parity matrix + */ +static void SetParity( uint16_t index, uint8_t *matrixRow, uint8_t parity ); + +/*! + * \brief Check if the provided value is a power of 2 + * + * \param [IN] x Value to be tested + * + * \retval status Return true if frame is a power of two + */ +static bool IsPowerOfTwo( uint32_t x ); + +/*! + * \brief XOrs two data lines + * + * \param [IN] line1 1st Data line to be XORed + * \param [IN] line2 2nd Data line to be XORed + * \param [IN] size Number of elements in line1 + * + * \param [OUT] result XOR( line1, line2 ) result stored in line1 + */ +static void XorDataLine( uint8_t *line1, uint8_t *line2, int32_t size ); + +/*! + * \brief XORs two parity lines + * + * \param [IN] line1 1st Parity line to be XORed + * \param [IN] line2 2nd Parity line to be XORed + * \param [IN] size Number of elements in line1 + * + * \param [OUT] result XOR( line1, line2 ) result stored in line1 + */ +static void XorParityLine( uint8_t* line1, uint8_t* line2, int32_t size ); + +/*! + * \brief Generates a pseudo random number : PRBS23 + * + * \param [IN] value The input of the PRBS23 generator + * + * \retval nextValue Returns the next pseudo random number + */ +static int32_t FragPrbs23( int32_t value ); + +/*! + * \brief Gets and fills the parity matrix + * + * \param [IN] n Fragment N + * \param [IN] m Fragment number + * \param [OUT] matrixRow Parity matrix + */ +static void FragGetParityMatrixRow( int32_t n, int32_t m, uint8_t *matrixRow ); + +/*! + * \brief Finds the index of the first one in a bit array + * + * \param [IN] bitArray Pointer to the bit array + * \param [IN] size Bit array size + * \retval index The index of the first 1 in the bit array + */ +static uint16_t BitArrayFindFirstOne( uint8_t *bitArray, uint16_t size ); + +/*! + * \brief Checks if the provided bit array only contains zeros + * + * \param [IN] bitArray Pointer to the bit array + * \param [IN] size Bit array size + * \retval isAllZeros [0: Contains ones, 1: Contains all zeros] + */ +static uint8_t BitArrayIsAllZeros( uint8_t *bitArray, uint16_t size ); + +/*! + * \brief Finds & marks missing fragments + * + * \param [IN] counter Current fragment counter + * \param [OUT] FragDecoder.FragNbMissingIndex[] array is updated in place + */ +static void FragFindMissingFrags( uint16_t counter ); + +/*! + * \brief Finds the index (frag counter) of the x th missing frag + * + * \param [IN] x x th missing frag + * + * \retval counter The counter value associated to the x th missing frag + */ +static uint16_t FragFindMissingIndex( uint16_t x ); + +/*! + * \brief Extacts a row from the binary matrix and expands it to a bitArray + * + * \param [IN] bitArray Pointer to the bit array + * \param [IN] rowIndex Matrix row index + * \param [IN] bitsInRow Number of bits in one row + */ +static void FragExtractLineFromBinaryMatrix( uint8_t* bitArray, uint16_t rowIndex, uint16_t bitsInRow ); + +/*! + * \brief Collapses and Pushs a row of a bit array to the matrix + * + * \param [IN] bitArray Pointer to the bit array + * \param [IN] rowIndex Matrix row index + * \param [IN] bitsInRow Number of bits in one row + */ +static void FragPushLineToBinaryMatrix( uint8_t *bitArray, uint16_t rowIndex, uint16_t bitsInRow ); + +/* + *============================================================================= + * Fragmentation decoder algorithm + *============================================================================= + */ + +static FragDecoder_t FragDecoder; + +#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) +void FragDecoderInit( uint16_t fragNb, uint8_t fragSize, FragDecoderCallbacks_t *callbacks ) +#else +void FragDecoderInit( uint16_t fragNb, uint8_t fragSize, uint8_t *file, uint32_t fileSize ) +#endif +{ +#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) + FragDecoder.Callbacks = callbacks; +#else + FragDecoder.File = file; + FragDecoder.FileSize = fileSize; +#endif + FragDecoder.FragNb = fragNb; // FragNb = FRAG_MAX_SIZE + FragDecoder.FragSize = fragSize; // number of byte on a row + FragDecoder.Status.FragNbLastRx = 0; + FragDecoder.Status.FragNbLost = 0; + FragDecoder.M2BLine = 0; + + // Initialize missing fragments index array + for( uint16_t i = 0; i < FRAG_MAX_NB; i++ ) + { + FragDecoder.FragNbMissingIndex[i] = 1; + } + + // Initialize parity matrix + for( uint32_t i = 0; i < ( ( FRAG_MAX_REDUNDANCY >> 3 ) + 1 ); i++ ) + { + FragDecoder.S[i] = 0; + } + + for( uint32_t i = 0; i < ( ( ( FRAG_MAX_REDUNDANCY >> 3 ) + 1 ) * FRAG_MAX_REDUNDANCY ); i++ ) + { + FragDecoder.MatrixM2B[i] = 0xFF; + } + + // Initialize final uncoded data buffer ( FRAG_MAX_NB * FRAG_MAX_SIZE ) + for( uint32_t i = 0; i < ( fragNb * fragSize ); i++ ) + { +#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) + if( ( FragDecoder.Callbacks != NULL ) && ( FragDecoder.Callbacks->FragDecoderWrite != NULL ) ) + { + uint8_t buffer[1] = { 0xFF }; + FragDecoder.Callbacks->FragDecoderWrite( i, buffer, 1 ); + } +#else + FragDecoder.File[i] = 0xFF; +#endif + } + FragDecoder.Status.FragNbLost = 0; + FragDecoder.Status.FragNbLastRx = 0; +} + +#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) +uint32_t FragDecoderGetMaxFileSize( void ) +{ + return FRAG_MAX_NB * FRAG_MAX_SIZE; +} +#endif + +int32_t FragDecoderProcess( uint16_t fragCounter, uint8_t *rawData ) +{ + uint16_t firstOneInRow = 0; + int32_t first = 0; + int32_t noInfo = 0; + + uint8_t matrixRow[(FRAG_MAX_NB >> 3 ) + 1]; + uint8_t matrixDataTemp[FRAG_MAX_SIZE]; + uint8_t dataTempVector[( FRAG_MAX_REDUNDANCY >> 3 ) + 1]; + uint8_t dataTempVector2[( FRAG_MAX_REDUNDANCY >> 3 ) + 1]; + + memset1( matrixRow, 0, ( FRAG_MAX_NB >> 3 ) + 1 ); + memset1( matrixDataTemp, 0, FRAG_MAX_SIZE ); + memset1( dataTempVector, 0, ( FRAG_MAX_REDUNDANCY >> 3 ) + 1 ); + memset1( dataTempVector2, 0, ( FRAG_MAX_REDUNDANCY >> 3 ) + 1 ); + + FragDecoder.Status.FragNbRx = fragCounter; + + if( fragCounter < FragDecoder.Status.FragNbLastRx ) + { + return FRAG_SESSION_ONGOING; // Drop frame out of order + } + + // The M (FragNb) first packets aren't encoded or in other words they are + // encoded with the unitary matrix + if( fragCounter < ( FragDecoder.FragNb + 1 ) ) + { + // The M first frame are not encoded store them +#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) + SetRow( rawData, fragCounter - 1, FragDecoder.FragSize ); +#else + SetRow( FragDecoder.File, rawData, fragCounter - 1, FragDecoder.FragSize ); +#endif + + FragDecoder.FragNbMissingIndex[fragCounter - 1] = 0; + + // Update the FragDecoder.FragNbMissingIndex with the loosing frame + FragFindMissingFrags( fragCounter ); + + if( ( FragDecoder.Status.FragNbLost == 0 ) && ( fragCounter == FragDecoder.FragNb ) ) + { + // the case : all the M(FragNb) first rows have been transmitted with no error + return FragDecoder.Status.FragNbLost; + } + } + else + { + if( FragDecoder.Status.FragNbLost > FRAG_MAX_REDUNDANCY ) + { + FragDecoder.Status.MatrixError = 1; + return FRAG_SESSION_FINISHED; + } + // At this point we receive encoded frames and the number of loosing frames + // is well known: FragDecoder.FragNbLost - 1; + + // In case of the end of true data is missing + FragFindMissingFrags( fragCounter ); + + // fragCounter - FragDecoder.FragNb + FragGetParityMatrixRow( fragCounter - FragDecoder.FragNb, FragDecoder.FragNb, matrixRow ); + + for( int32_t i = 0; i < FragDecoder.FragNb; i++ ) + { + if( GetParity( i , matrixRow ) == 1 ) + { + if( FragDecoder.FragNbMissingIndex[i] == 0 ) + { + // XOR with already receive frag + SetParity( i, matrixRow, 0 ); +#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) + GetRow( matrixDataTemp, i, FragDecoder.FragSize ); +#else + GetRow( matrixDataTemp, FragDecoder.File, i, FragDecoder.FragSize ); +#endif + XorDataLine( rawData, matrixDataTemp, FragDecoder.FragSize ); + } + else + { + // Fill the "little" boolean matrix m2b + SetParity( FragDecoder.FragNbMissingIndex[i] - 1, dataTempVector, 1 ); + if( first == 0 ) + { + first = 1; + } + } + } + } + + firstOneInRow = BitArrayFindFirstOne( dataTempVector, FragDecoder.Status.FragNbLost ); + + if( first > 0 ) + { + int32_t li; + int32_t lj; + + // Manage a new line in MatrixM2B + while( GetParity( firstOneInRow, FragDecoder.S ) == 1 ) + { + // Row already diagonalized exist & ( FragDecoder.MatrixM2B[firstOneInRow][0] ) + FragExtractLineFromBinaryMatrix( dataTempVector2, firstOneInRow, FragDecoder.Status.FragNbLost ); + XorParityLine( dataTempVector, dataTempVector2, FragDecoder.Status.FragNbLost ); + // Have to store it in the mi th position of the missing frag + li = FragFindMissingIndex( firstOneInRow ); +#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) + GetRow( matrixDataTemp, li, FragDecoder.FragSize ); +#else + GetRow( matrixDataTemp, FragDecoder.File, li, FragDecoder.FragSize ); +#endif + XorDataLine( rawData, matrixDataTemp, FragDecoder.FragSize ); + if( BitArrayIsAllZeros( dataTempVector, FragDecoder.Status.FragNbLost ) ) + { + noInfo = 1; + break; + } + firstOneInRow = BitArrayFindFirstOne( dataTempVector, FragDecoder.Status.FragNbLost ); + } + + if( noInfo == 0 ) + { + FragPushLineToBinaryMatrix( dataTempVector, firstOneInRow, FragDecoder.Status.FragNbLost ); + li = FragFindMissingIndex( firstOneInRow ); +#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) + SetRow( rawData, li, FragDecoder.FragSize ); +#else + SetRow( FragDecoder.File, rawData, li, FragDecoder.FragSize ); +#endif + SetParity( firstOneInRow, FragDecoder.S, 1 ); + FragDecoder.M2BLine++; + } + + if( FragDecoder.M2BLine == FragDecoder.Status.FragNbLost ) + { + // Then last step diagonalized + if( FragDecoder.Status.FragNbLost > 1 ) + { + int32_t i, j; + + for( i = ( FragDecoder.Status.FragNbLost - 2 ); i >= 0 ; i-- ) + { + li = FragFindMissingIndex( i ); +#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) + GetRow( matrixDataTemp, li, FragDecoder.FragSize ); +#else + GetRow( matrixDataTemp, FragDecoder.File, li, FragDecoder.FragSize ); +#endif + for( j = ( FragDecoder.Status.FragNbLost - 1 ); j > i; j--) + { + FragExtractLineFromBinaryMatrix( dataTempVector2, i, FragDecoder.Status.FragNbLost ); + FragExtractLineFromBinaryMatrix( dataTempVector, j, FragDecoder.Status.FragNbLost ); + if( GetParity( j, dataTempVector2 ) == 1 ) + { + XorParityLine( dataTempVector2, dataTempVector, FragDecoder.Status.FragNbLost ); + + lj = FragFindMissingIndex( j ); + +#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) + GetRow( rawData, lj, FragDecoder.FragSize ); +#else + GetRow( rawData, FragDecoder.File, lj, FragDecoder.FragSize ); +#endif + XorDataLine( matrixDataTemp , rawData , FragDecoder.FragSize ); + } + } +#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) + SetRow( matrixDataTemp, li, FragDecoder.FragSize ); +#else + SetRow( FragDecoder.File, matrixDataTemp, li, FragDecoder.FragSize ); +#endif + } + return FragDecoder.Status.FragNbLost; + } + else + { + //If not ( FragDecoder.FragNbLost > 1 ) + return FragDecoder.Status.FragNbLost; + } + } + } + } + return FRAG_SESSION_ONGOING; +} + +FragDecoderStatus_t FragDecoderGetStatus( void ) +{ + return FragDecoder.Status; +} + +/* + *============================================================================= + * Fragmentation decoder algorithm utilities + *============================================================================= + */ + +#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) +static void SetRow( uint8_t *src, uint16_t row, uint16_t size ) +{ + if( ( FragDecoder.Callbacks != NULL ) && ( FragDecoder.Callbacks->FragDecoderWrite != NULL ) ) + { + FragDecoder.Callbacks->FragDecoderWrite( row * size, src, size ); + } +} + +static void GetRow( uint8_t *dst, uint16_t row, uint16_t size ) +{ + if( ( FragDecoder.Callbacks != NULL ) && ( FragDecoder.Callbacks->FragDecoderRead != NULL ) ) + { + FragDecoder.Callbacks->FragDecoderRead( row * size, dst, size ); + } +} +#else +static void SetRow( uint8_t *dst, uint8_t *src, uint16_t row, uint16_t size ) +{ + memcpy1( &dst[row * size], src, size ); +} + +static void GetRow( uint8_t *dst, uint8_t *src, uint16_t row, uint16_t size ) +{ + memcpy1( dst, &src[row * size], size ); +} +#endif + +static uint8_t GetParity( uint16_t index, uint8_t *matrixRow ) +{ + uint8_t parity; + parity = matrixRow[index >> 3]; + parity = ( parity >> ( 7 - ( index % 8 ) ) ) & 0x01; + return parity; +} + +static void SetParity( uint16_t index, uint8_t *matrixRow, uint8_t parity ) +{ + uint8_t mask = 0xFF - ( 1 << ( 7 - ( index % 8 ) ) ); + parity = parity << ( 7 - ( index % 8 ) ); + matrixRow[index >> 3] = ( matrixRow[index >> 3] & mask ) + parity; +} + +static bool IsPowerOfTwo( uint32_t x ) +{ + uint8_t sumBit = 0; + + for( uint8_t i = 0; i < 32; i++ ) + { + sumBit += ( x & ( 1 << i ) ) >> i; + } + if( sumBit == 1 ) + { + return true; + } + return false; +} + +static void XorDataLine( uint8_t *line1, uint8_t *line2, int32_t size ) +{ + for( int32_t i = 0; i < size; i++ ) + { + line1[i] = line1[i] ^ line2[i]; + } +} + +static void XorParityLine( uint8_t* line1, uint8_t* line2, int32_t size ) +{ + for( int32_t i = 0; i < size; i++ ) + { + SetParity( i, line1, ( GetParity( i, line1 ) ^ GetParity( i, line2 ) ) ); + } +} + +static int32_t FragPrbs23( int32_t value ) +{ + int32_t b0 = value & 0x01; + int32_t b1 = ( value & 0x20 ) >> 5; + return ( value >> 1 ) + ( ( b0 ^ b1 ) << 22 ); +} + +static void FragGetParityMatrixRow( int32_t n, int32_t m, uint8_t *matrixRow ) +{ + int32_t mTemp; + int32_t x; + int32_t nbCoeff = 0; + int32_t r; + + if( IsPowerOfTwo( m ) != false ) + { + mTemp = 1; + } + else + { + mTemp = 0; + } + + x = 1 + ( 1001 * n ); + for( int32_t i = 0; i < ( ( m >> 3 ) + 1 ); i++ ) + { + matrixRow[i] = 0; + } + while( nbCoeff < ( m >> 1 ) ) + { + r = 1 << 16; + while( r >= m ) + { + x = FragPrbs23( x ); + r = x % ( m + mTemp ); + } + SetParity( r, matrixRow, 1 ); + nbCoeff += 1; + } +} + +static uint16_t BitArrayFindFirstOne( uint8_t *bitArray, uint16_t size ) +{ + for( uint16_t i = 0; i < size; i++) + { + if ( GetParity( i, bitArray ) == 1 ) + { + return i; + } + } + return 0; +} + +static uint8_t BitArrayIsAllZeros( uint8_t *bitArray, uint16_t size ) +{ + for( uint16_t i = 0; i < size; i++ ) + { + if( GetParity( i, bitArray ) == 1 ) + { + return 0; + } + } + return 1; +} + +/*! + * \brief Finds & marks missing fragments + * + * \param [IN] counter Current fragment counter + * \param [OUT] FragDecoder.FragNbMissingIndex[] array is updated in place + */ +static void FragFindMissingFrags( uint16_t counter ) +{ + int32_t i; + for( i = FragDecoder.Status.FragNbLastRx; i < ( counter - 1 ); i++ ) + { + if( i < FragDecoder.FragNb ) + { + FragDecoder.Status.FragNbLost++; + FragDecoder.FragNbMissingIndex[i] = FragDecoder.Status.FragNbLost; + } + } + if( i < FragDecoder.FragNb ) + { + FragDecoder.Status.FragNbLastRx = counter; + } + else + { + FragDecoder.Status.FragNbLastRx = FragDecoder.FragNb + 1; + } + DBG( "RECEIVED : %5d / %5d Fragments\n", FragDecoder.Status.FragNbRx, FragDecoder.FragNb ); + DBG( " %5d / %5d Bytes\n", FragDecoder.Status.FragNbRx * FragDecoder.FragSize, FragDecoder.FragNb * FragDecoder.FragSize ); + DBG( "LOST : %7d Fragments\n\n", FragDecoder.Status.FragNbLost ); +} + +/*! + * \brief Finds the index (frag counter) of the x th missing frag + * + * \param [IN] x x th missing frag + * + * \retval counter The counter value associated to the x th missing frag + */ +static uint16_t FragFindMissingIndex( uint16_t x ) +{ + for( uint16_t i = 0; i < FragDecoder.FragNb; i++ ) + { + if( FragDecoder.FragNbMissingIndex[i] == ( x + 1 ) ) + { + return i; + } + } + return 0; +} + +/*! + * \brief Extacts a row from the binary matrix and expands it to a bitArray + * + * \param [IN] bitArray Pointer to the bit array + * \param [IN] rowIndex Matrix row index + * \param [IN] bitsInRow Number of bits in one row + */ +static void FragExtractLineFromBinaryMatrix( uint8_t* bitArray, uint16_t rowIndex, uint16_t bitsInRow ) +{ + uint32_t findByte = 0; + uint32_t findBitInByte = 0; + + if( rowIndex > 0 ) + { + findByte = ( rowIndex * bitsInRow - ( ( rowIndex * ( rowIndex - 1 ) ) >> 1 ) ) >> 3; + findBitInByte = ( rowIndex * bitsInRow - ( ( rowIndex * ( rowIndex - 1 ) ) >> 1 ) ) % 8; + } + if( rowIndex > 0 ) + { + for( uint16_t i = 0; i < rowIndex; i++ ) + { + SetParity( i, bitArray, 0 ); + } + } + for( uint16_t i = rowIndex; i < bitsInRow; i++ ) + { + SetParity( i, + bitArray, + ( FragDecoder.MatrixM2B[findByte] >> ( 7 - findBitInByte ) ) & 0x01 ); + + findBitInByte++; + if( findBitInByte == 8 ) + { + findBitInByte = 0; + findByte++; + } + } +} + +/*! + * \brief Collapses and Pushs a row of a bit array to the matrix + * + * \param [IN] bitArray Pointer to the bit array + * \param [IN] rowIndex Matrix row index + * \param [IN] bitsInRow Number of bits in one row + */ +static void FragPushLineToBinaryMatrix( uint8_t *bitArray, uint16_t rowIndex, uint16_t bitsInRow ) +{ + uint32_t findByte = 0; + uint32_t findBitInByte = 0; + + if ( rowIndex > 0) { + findByte = ( rowIndex * bitsInRow - ( ( rowIndex * ( rowIndex - 1 ) ) >> 1 ) ) >> 3; + findBitInByte = ( rowIndex * bitsInRow - ( ( rowIndex * ( rowIndex - 1 ) ) >> 1 ) ) % 8; + + } + for( uint16_t i = rowIndex; i < bitsInRow; i++ ) + { + if( GetParity( i, bitArray ) == 0 ) + { + FragDecoder.MatrixM2B[findByte] = FragDecoder.MatrixM2B[findByte] & ( 0xFF - ( 1 << ( 7 - findBitInByte ) ) ); + } + findBitInByte++; + if( findBitInByte == 8 ) + { + findBitInByte = 0; + findByte++; + } + } +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/packages/FragDecoder.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/packages/FragDecoder.h new file mode 100644 index 0000000000..b47b1da9c0 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/packages/FragDecoder.h @@ -0,0 +1,151 @@ +/*! + * \file FragDecoder.h + * + * \brief Implements the LoRa-Alliance fragmentation decoder + * Specification: https://lora-alliance.org/sites/default/files/2018-09/fragmented_data_block_transport_v1.0.0.pdf + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2018 Semtech + * + * \endcode + * + * \author Fabien Holin ( Semtech ) + * \author Miguel Luis ( Semtech ) + */ +#ifndef __FRAG_DECODER_H__ +#define __FRAG_DECODER_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/*! + * If set to 1 the new API defining \ref FragDecoderWrite and + * \ref FragDecoderReadfunction callbacks is used. + */ +#define FRAG_DECODER_FILE_HANDLING_NEW_API 1 + +/*! + * Maximum number of fragment that can be handled. + * + * \remark This parameter has an impact on the memory footprint. + */ +#define FRAG_MAX_NB 21 + +/*! + * Maximum fragment size that can be handled. + * + * \remark This parameter has an impact on the memory footprint. + */ +#define FRAG_MAX_SIZE 50 + +/*! + * Maximum number of extra frames that can be handled. + * + * \remark This parameter has an impact on the memory footprint. + */ +#define FRAG_MAX_REDUNDANCY 5 + +#define FRAG_SESSION_FINISHED ( int32_t )0 +#define FRAG_SESSION_NOT_STARTED ( int32_t )-2 +#define FRAG_SESSION_ONGOING ( int32_t )-1 + +typedef struct sFragDecoderStatus +{ + uint16_t FragNbRx; + uint16_t FragNbLost; + uint16_t FragNbLastRx; + uint8_t MatrixError; +}FragDecoderStatus_t; + +#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) +typedef struct sFragDecoderCallbacks +{ + /*! + * Writes `data` buffer of `size` starting at address `addr` + * + * \param [IN] addr Address start index to write to. + * \param [IN] data Data buffer to be written. + * \param [IN] size Size of data buffer to be written. + * + * \retval status Write operation status [0: Success, -1 Fail] + */ + int8_t ( *FragDecoderWrite )( uint32_t addr, uint8_t *data, uint32_t size ); + /*! + * Reads `data` buffer of `size` starting at address `addr` + * + * \param [IN] addr Address start index to read from. + * \param [IN] data Data buffer to be read. + * \param [IN] size Size of data buffer to be read. + * + * \retval status Read operation status [0: Success, -1 Fail] + */ + int8_t ( *FragDecoderRead )( uint32_t addr, uint8_t *data, uint32_t size ); +}FragDecoderCallbacks_t; +#endif + +#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) +/*! + * \brief Initializes the fragmentation decoder + * + * \param [IN] fragNb Number of expected fragments (without redundancy packets) + * \param [IN] fragSize Size of a fragment + * \param [IN] callbacks Pointer to the Write/Read functions. + */ +void FragDecoderInit( uint16_t fragNb, uint8_t fragSize, FragDecoderCallbacks_t *callbacks ); +#else +/*! + * \brief Initializes the fragmentation decoder + * + * \param [IN] fragNb Number of expected fragments (without redundancy packets) + * \param [IN] fragSize Size of a fragment + * \param [IN] file Pointer to file buffer size + * \param [IN] fileSize File buffer size + */ +void FragDecoderInit( uint16_t fragNb, uint8_t fragSize, uint8_t *file, uint32_t fileSize ); +#endif + +#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) +/*! + * \brief Gets the maximum file size that can be received + * + * \retval size FileSize + */ +uint32_t FragDecoderGetMaxFileSize( void ); +#endif + +/*! + * \brief Function to decode and reconstruct the binary file + * Called for each receive frame + * + * \param [IN] fragCounter Fragment counter [1..(FragDecoder.FragNb + FragDecoder.Redundancy)] + * \param [IN] rawData Pointer to the fragment to be processed (length = FragDecoder.FragSize) + * + * \retval status Process status. [FRAG_SESSION_ONGOING, + * FRAG_SESSION_FINISHED or + * FragDecoder.Status.FragNbLost] + */ +int32_t FragDecoderProcess( uint16_t fragCounter, uint8_t *rawData ); + +/*! + * \brief Gets the current fragmentation status + * + * \retval status Fragmentation decoder status + */ +FragDecoderStatus_t FragDecoderGetStatus( void ); + +#ifdef __cplusplus +} +#endif + +#endif // __FRAG_DECODER_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/packages/LmhPackage.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/packages/LmhPackage.h new file mode 100644 index 0000000000..d797607c2b --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/packages/LmhPackage.h @@ -0,0 +1,154 @@ +/*! + * \file LmPackage.h + * + * \brief Defines the packages API + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2018 Semtech + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + */ +#ifndef __LMH_PACKAGE_H__ +#define __LMH_PACKAGE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "LmHandlerTypes.h" + +/*! + * Maximum number of packages + */ +#define PKG_MAX_NUMBER 4 + +typedef struct LmhPackage_s +{ + uint8_t Port; + /* + *========================================================================= + * Below callbacks must be initialized in package variable declaration + *========================================================================= + */ + + /*! + * Initializes the package with provided parameters + * + * \param [IN] params Pointer to the package parameters + * \param [IN] dataBuffer Pointer to main application buffer + * \param [IN] dataBufferMaxSize Main application buffer maximum size + */ + void ( *Init )( void *params, uint8_t *dataBuffer, uint8_t dataBufferMaxSize ); + /*! + * Returns the current package initialization status. + * + * \retval status Package initialization status + * [true: Initialized, false: Not initialized] + */ + bool ( *IsInitialized )( void ); + /*! + * Returns if a package transmission is pending or not. + * + * \retval status Package transmission status + * [true: pending, false: Not pending] + */ + bool ( *IsTxPending )( void ); + /*! + * Processes the internal package events. + */ + void ( *Process )( void ); + /*! + * Processes the MCSP Confirm + * + * \param [IN] mcpsConfirm MCPS confirmation primitive data + */ + void ( *OnMcpsConfirmProcess )( McpsConfirm_t *mcpsConfirm ); + /*! + * Processes the MCPS Indication + * + * \param [IN] mcpsIndication MCPS indication primitive data + */ + void ( *OnMcpsIndicationProcess )( McpsIndication_t *mcpsIndication ); + /*! + * Processes the MLME Confirm + * + * \param [IN] mlmeConfirm MLME confirmation primitive data + */ + void ( *OnMlmeConfirmProcess )( MlmeConfirm_t *mlmeConfirm ); + /*! + * Processes the MLME Indication + * + * \param [IN] mlmeIndication MLME indication primitive data + */ + void ( *OnMlmeIndicationProcess )( MlmeIndication_t *mlmeIndication ); + + /* + *========================================================================= + * Below callbacks must be initialized in LmHandler initialization with + * provideded LmHandlerSend and OnMacRequest functions + *========================================================================= + */ + + /*! + * Notifies the upper layer that a MCPS request has been made to the MAC layer + * + * \param [IN] status - Request returned status + * \param [IN] mcpsRequest - Performed MCPS-Request. Refer to \ref McpsReq_t. + * \param [IN] nextTxDelay - Time to wait until another TX is possible. + */ + void ( *OnMacMcpsRequest )( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxDelay ); + /*! + * Notifies the upper layer that a MLME request has been made to the MAC layer + * + * \param [IN] status - Request returned status + * \param [IN] mlmeRequest - Performed MLME-Request. Refer to \ref MlmeReq_t. + * \param [IN] nextTxDelay - Time to wait until another TX is possible. + */ + void ( *OnMacMlmeRequest )( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxDelay ); + /*! + * Join a LoRa Network in classA + * + * \Note if the device is ABP, this is a pass through function + * + * \param [IN] isOtaa Indicates which activation mode must be used + */ + void ( *OnJoinRequest )( bool isOtaa ); + /*! + * Requests network server time update + * + * \retval status Returns \ref LORAMAC_HANDLER_SET if joined else \ref LORAMAC_HANDLER_RESET + */ + LmHandlerErrorStatus_t ( *OnDeviceTimeRequest )( void ); +#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) + /*! + * Notifies the upper layer that the system time has been updated. + * + * \param [in] isSynchronized Indicates if the system time is synchronized in the range +/-1 second + * \param [in] timeCorrection Received time correction value + */ + void ( *OnSysTimeUpdate )( bool isSynchronized, int32_t timeCorrection ); +#else + /*! + * Notifies the upper layer that the system time has been updated. + */ + void ( *OnSysTimeUpdate )( void ); +#endif +}LmhPackage_t; + +#ifdef __cplusplus +} +#endif + +#endif // __LMH_PACKAGE_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/packages/LmhpClockSync.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/packages/LmhpClockSync.c new file mode 100644 index 0000000000..1c3326fb10 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/packages/LmhpClockSync.c @@ -0,0 +1,372 @@ +/*! + * \file LmhpClockSync.c + * + * \brief Implements the LoRa-Alliance clock synchronization package + * Specification: https://lora-alliance.org/sites/default/files/2018-09/application_layer_clock_synchronization_v1.0.0.pdf + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2018 Semtech + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + */ +#include "LmHandler.h" +#include "LmhpClockSync.h" + +/*! + * LoRaWAN Application Layer Clock Synchronization Specification + */ +#define CLOCK_SYNC_PORT 202 + +#define CLOCK_SYNC_ID 1 +#define CLOCK_SYNC_VERSION 1 + +/*! + * Package current context + */ +typedef struct LmhpClockSyncState_s +{ + bool Initialized; + bool IsTxPending; + uint8_t DataBufferMaxSize; + uint8_t *DataBuffer; + union + { + uint8_t Value; + struct + { + uint8_t TokenReq: 4; + uint8_t AnsRequired: 1; + uint8_t RFU: 3; + }Fields; + }TimeReqParam; + bool AppTimeReqPending; + bool AdrEnabledPrev; + uint8_t NbTransPrev; + uint8_t DataratePrev; + uint8_t NbTransmissions; +}LmhpClockSyncState_t; + +typedef enum LmhpClockSyncMoteCmd_e +{ + CLOCK_SYNC_PKG_VERSION_ANS = 0x00, + CLOCK_SYNC_APP_TIME_REQ = 0x01, + CLOCK_SYNC_APP_TIME_PERIOD_ANS = 0x02, + CLOCK_SYNC_FORCE_RESYNC_ANS = 0x03, +}LmhpClockSyncMoteCmd_t; + +typedef enum LmhpClockSyncSrvCmd_e +{ + CLOCK_SYNC_PKG_VERSION_REQ = 0x00, + CLOCK_SYNC_APP_TIME_ANS = 0x01, + CLOCK_SYNC_APP_TIME_PERIOD_REQ = 0x02, + CLOCK_SYNC_FORCE_RESYNC_REQ = 0x03, +}LmhpClockSyncSrvCmd_t; + +/*! + * Initializes the package with provided parameters + * + * \param [IN] params Pointer to the package parameters + * \param [IN] dataBuffer Pointer to main application buffer + * \param [IN] dataBufferMaxSize Main application buffer maximum size + */ +static void LmhpClockSyncInit( void *params, uint8_t *dataBuffer, uint8_t dataBufferMaxSize ); + +/*! + * Returns the current package initialization status. + * + * \retval status Package initialization status + * [true: Initialized, false: Not initialized] + */ +static bool LmhpClockSyncIsInitialized( void ); + +/*! + * Returns if a package transmission is pending or not. + * + * \retval status Package transmission status + * [true: pending, false: Not pending] + */ +static bool LmhpClockSyncIsTxPending( void ); + +/*! + * Processes the internal package events. + */ +static void LmhpClockSyncProcess( void ); + +/*! + * Processes the MCSP Confirm + * + * \param [IN] mcpsConfirm MCPS confirmation primitive data + */ +static void LmhpClockSyncOnMcpsConfirm( McpsConfirm_t *mcpsConfirm ); + +/*! + * Processes the MCPS Indication + * + * \param [IN] mcpsIndication MCPS indication primitive data + */ +static void LmhpClockSyncOnMcpsIndication( McpsIndication_t *mcpsIndication ); + +static LmhpClockSyncState_t LmhpClockSyncState = +{ + .Initialized = false, + .IsTxPending = false, + .TimeReqParam.Value = 0, + .AppTimeReqPending = false, + .AdrEnabledPrev = false, + .NbTransPrev = 0, + .NbTransmissions = 0, +}; + +static LmhPackage_t LmhpClockSyncPackage = +{ + .Port = CLOCK_SYNC_PORT, + .Init = LmhpClockSyncInit, + .IsInitialized = LmhpClockSyncIsInitialized, + .IsTxPending = LmhpClockSyncIsTxPending, + .Process = LmhpClockSyncProcess, + .OnMcpsConfirmProcess = LmhpClockSyncOnMcpsConfirm, + .OnMcpsIndicationProcess = LmhpClockSyncOnMcpsIndication, + .OnMlmeConfirmProcess = NULL, // Not used in this package + .OnMlmeIndicationProcess = NULL, // Not used in this package + .OnMacMcpsRequest = NULL, // To be initialized by LmHandler + .OnMacMlmeRequest = NULL, // To be initialized by LmHandler + .OnJoinRequest = NULL, // To be initialized by LmHandler + .OnDeviceTimeRequest = NULL, // To be initialized by LmHandler + .OnSysTimeUpdate = NULL, // To be initialized by LmHandler +}; + +LmhPackage_t *LmphClockSyncPackageFactory( void ) +{ + return &LmhpClockSyncPackage; +} + +static void LmhpClockSyncInit( void * params, uint8_t *dataBuffer, uint8_t dataBufferMaxSize ) +{ + if( dataBuffer != NULL ) + { + LmhpClockSyncState.DataBuffer = dataBuffer; + LmhpClockSyncState.DataBufferMaxSize = dataBufferMaxSize; + LmhpClockSyncState.Initialized = true; + } + else + { + LmhpClockSyncState.Initialized = false; + } + LmhpClockSyncState.IsTxPending = false; +} + +static bool LmhpClockSyncIsInitialized( void ) +{ + return LmhpClockSyncState.Initialized; +} + +static bool LmhpClockSyncIsTxPending( void ) +{ + return LmhpClockSyncState.IsTxPending; +} + +static void LmhpClockSyncProcess( void ) +{ + if( LmhpClockSyncState.NbTransmissions > 0 ) + { + if( LmhpClockSyncAppTimeReq( ) == LORAMAC_HANDLER_SUCCESS ) + { + LmhpClockSyncState.NbTransmissions--; + } + } +} + +static void LmhpClockSyncOnMcpsConfirm( McpsConfirm_t *mcpsConfirm ) +{ + MibRequestConfirm_t mibReq; + + if( LmhpClockSyncState.AppTimeReqPending == true ) + { + // Revert ADR setting + mibReq.Type = MIB_ADR; + mibReq.Param.AdrEnable = LmhpClockSyncState.AdrEnabledPrev; + LoRaMacMibSetRequestConfirm( &mibReq ); + + // Revert NbTrans setting + mibReq.Type = MIB_CHANNELS_NB_TRANS; + mibReq.Param.ChannelsNbTrans = LmhpClockSyncState.NbTransPrev; + LoRaMacMibSetRequestConfirm( &mibReq ); + + // Revert data rate setting + mibReq.Type = MIB_CHANNELS_DATARATE; + mibReq.Param.ChannelsDatarate = LmhpClockSyncState.DataratePrev; + LoRaMacMibSetRequestConfirm( &mibReq ); + + LmhpClockSyncState.AppTimeReqPending = false; + } +} + +static void LmhpClockSyncOnMcpsIndication( McpsIndication_t *mcpsIndication ) +{ + uint8_t cmdIndex = 0; + uint8_t dataBufferIndex = 0; + + if( mcpsIndication->Port != CLOCK_SYNC_PORT ) + { + return; + } + + while( cmdIndex < mcpsIndication->BufferSize ) + { + switch( mcpsIndication->Buffer[cmdIndex++] ) + { + case CLOCK_SYNC_PKG_VERSION_REQ: + { + LmhpClockSyncState.DataBuffer[dataBufferIndex++] = CLOCK_SYNC_PKG_VERSION_ANS; + LmhpClockSyncState.DataBuffer[dataBufferIndex++] = CLOCK_SYNC_ID; + LmhpClockSyncState.DataBuffer[dataBufferIndex++] = CLOCK_SYNC_VERSION; + break; + } + case CLOCK_SYNC_APP_TIME_ANS: + { + LmhpClockSyncState.NbTransmissions = 0; + + // Check if a more precise time correction has been received. + // If yes then don't process and ignore this answer. + if( mcpsIndication->DeviceTimeAnsReceived == true ) + { + cmdIndex += 5; + break; + } + int32_t timeCorrection = 0; + timeCorrection = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF; + timeCorrection += ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00; + timeCorrection += ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000; + timeCorrection += ( mcpsIndication->Buffer[cmdIndex++] << 24 ) & 0xFF000000; + if( ( mcpsIndication->Buffer[cmdIndex++] & 0x0F ) == LmhpClockSyncState.TimeReqParam.Fields.TokenReq ) + { + SysTime_t curTime = { .Seconds = 0, .SubSeconds = 0 }; + curTime = SysTimeGet( ); + curTime.Seconds += timeCorrection; + SysTimeSet( curTime ); + LmhpClockSyncState.TimeReqParam.Fields.TokenReq = ( LmhpClockSyncState.TimeReqParam.Fields.TokenReq + 1 ) & 0x0F; + if( LmhpClockSyncPackage.OnSysTimeUpdate != NULL ) + { +#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) + LmhpClockSyncPackage.OnSysTimeUpdate( + ( timeCorrection >= -1 ) && ( timeCorrection <= 1 ), + timeCorrection ); +#else + if( ( timeCorrection >= -1 ) && ( timeCorrection <= 1 ) ) + { + LmhpClockSyncPackage.OnSysTimeUpdate( ); + } +#endif + } + } + break; + } + case CLOCK_SYNC_APP_TIME_PERIOD_REQ: + { + // Increment index + cmdIndex++; + // TODO implement command prosessing and handling + LmhpClockSyncState.DataBuffer[dataBufferIndex++] = CLOCK_SYNC_APP_TIME_PERIOD_ANS; + // Answer status not supported. + LmhpClockSyncState.DataBuffer[dataBufferIndex++] = 0x01; + + SysTime_t curTime = SysTimeGet( ); + // Substract Unix to Gps epcoh offset. The system time is based on Unix time. + curTime.Seconds -= UNIX_GPS_EPOCH_OFFSET; + LmhpClockSyncState.DataBuffer[dataBufferIndex++] = ( curTime.Seconds >> 0 ) & 0xFF; + LmhpClockSyncState.DataBuffer[dataBufferIndex++] = ( curTime.Seconds >> 8 ) & 0xFF; + LmhpClockSyncState.DataBuffer[dataBufferIndex++] = ( curTime.Seconds >> 16 ) & 0xFF; + LmhpClockSyncState.DataBuffer[dataBufferIndex++] = ( curTime.Seconds >> 24 ) & 0xFF; + break; + } + case CLOCK_SYNC_FORCE_RESYNC_REQ: + { + LmhpClockSyncState.NbTransmissions = mcpsIndication->Buffer[cmdIndex++] & 0X07; + break; + } + } + } + + if( dataBufferIndex != 0 ) + { + // Answer commands + LmHandlerAppData_t appData = + { + .Buffer = LmhpClockSyncState.DataBuffer, + .BufferSize = dataBufferIndex, + .Port = CLOCK_SYNC_PORT + }; + LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG ); + } +} + +LmHandlerErrorStatus_t LmhpClockSyncAppTimeReq( void ) +{ + if( LmHandlerIsBusy( ) == true ) + { + return LORAMAC_HANDLER_ERROR; + } + + if( LmhpClockSyncState.AppTimeReqPending == false ) + { + MibRequestConfirm_t mibReq; + + // Disable ADR + mibReq.Type = MIB_ADR; + LoRaMacMibGetRequestConfirm( &mibReq ); + LmhpClockSyncState.AdrEnabledPrev = mibReq.Param.AdrEnable; + mibReq.Param.AdrEnable = false; + LoRaMacMibSetRequestConfirm( &mibReq ); + + // Set NbTrans = 1 + mibReq.Type = MIB_CHANNELS_NB_TRANS; + LoRaMacMibGetRequestConfirm( &mibReq ); + LmhpClockSyncState.NbTransPrev = mibReq.Param.ChannelsNbTrans; + mibReq.Param.ChannelsNbTrans = 1; + LoRaMacMibSetRequestConfirm( &mibReq ); + + // Store data rate + mibReq.Type = MIB_CHANNELS_DATARATE; + LoRaMacMibGetRequestConfirm( &mibReq ); + LmhpClockSyncState.DataratePrev = mibReq.Param.ChannelsDatarate; + + // Add DeviceTimeReq MAC command. + // In case the network server supports this more precise command + // this package will use DeviceTimeAns answer as clock synchronization + // mechanism. + LmhpClockSyncPackage.OnDeviceTimeRequest( ); + } + + SysTime_t curTime = SysTimeGet( ); + uint8_t dataBufferIndex = 0; + + // Substract Unix to Gps epcoh offset. The system time is based on Unix time. + curTime.Seconds -= UNIX_GPS_EPOCH_OFFSET; + + LmhpClockSyncState.DataBuffer[dataBufferIndex++] = CLOCK_SYNC_APP_TIME_REQ; + LmhpClockSyncState.DataBuffer[dataBufferIndex++] = ( curTime.Seconds >> 0 ) & 0xFF; + LmhpClockSyncState.DataBuffer[dataBufferIndex++] = ( curTime.Seconds >> 8 ) & 0xFF; + LmhpClockSyncState.DataBuffer[dataBufferIndex++] = ( curTime.Seconds >> 16 ) & 0xFF; + LmhpClockSyncState.DataBuffer[dataBufferIndex++] = ( curTime.Seconds >> 24 ) & 0xFF; + LmhpClockSyncState.TimeReqParam.Fields.AnsRequired = 0; + LmhpClockSyncState.DataBuffer[dataBufferIndex++] = LmhpClockSyncState.TimeReqParam.Value; + + LmHandlerAppData_t appData = + { + .Buffer = LmhpClockSyncState.DataBuffer, + .BufferSize = dataBufferIndex, + .Port = CLOCK_SYNC_PORT + }; + LmhpClockSyncState.AppTimeReqPending = true; + return LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG ); +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/packages/LmhpClockSync.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/packages/LmhpClockSync.h new file mode 100644 index 0000000000..87730e63bc --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/packages/LmhpClockSync.h @@ -0,0 +1,57 @@ +/*! + * \file LmhpClockSync.h + * + * \brief Implements the LoRa-Alliance clock synchronization package + * Specification: https://lora-alliance.org/sites/default/files/2018-09/application_layer_clock_synchronization_v1.0.0.pdf + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2018 Semtech + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + */ +#ifndef __LMHP_CLOCK_SYNC_H__ +#define __LMHP_CLOCK_SYNC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "LoRaMac.h" +#include "LmHandlerTypes.h" +#include "LmhPackage.h" + +/*! + * Clock sync package identifier. + * + * \remark This value must be unique amongst the packages + */ +#define PACKAGE_ID_CLOCK_SYNC 1 + +/*! + * Clock sync package parameters + * + * This package doesn't require parameters + */ +//typedef struct LmphClockSyncParams_s +//{ +//}LmphClockSyncParams_t; + +LmhPackage_t *LmphClockSyncPackageFactory( void ); + +LmHandlerErrorStatus_t LmhpClockSyncAppTimeReq( void ); + +#ifdef __cplusplus +} +#endif + +#endif // __LMHP_CLOCK_SYNC_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/packages/LmhpCompliance.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/packages/LmhpCompliance.c new file mode 100644 index 0000000000..cfd810628f --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/packages/LmhpCompliance.c @@ -0,0 +1,617 @@ +/*! + * \file LmhpCompliance.c + * + * \brief Implements the LoRa-Alliance certification protocol handling + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2018 Semtech + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + */ +#include +#include +#include +#include "board.h" +#include "LoRaMacTest.h" +#include "LmHandler.h" +#include "LmhpCompliance.h" + +/*! + * LoRaWAN compliance certification protocol port number. + * + * LoRaWAN Specification V1.x.x, chapter 4.3.2 + */ +#define COMPLIANCE_PORT 224 + +#define COMPLIANCE_ID 6 +#define COMPLIANCE_VERSION 1 + +typedef struct ClassBStatus_s +{ + bool IsBeaconRxOn; + uint8_t PingSlotPeriodicity; + uint16_t BeaconCnt; + BeaconInfo_t Info; +} ClassBStatus_t; + +/*! + * LoRaWAN compliance tests support data + */ +typedef struct ComplianceTestState_s +{ + bool Initialized; + bool IsTxPending; + TimerTime_t TxPendingTimestamp; + LmHandlerMsgTypes_t IsTxConfirmed; + uint8_t DataBufferMaxSize; + uint8_t DataBufferSize; + uint8_t* DataBuffer; + uint16_t RxAppCnt; + bool IsBeaconRxStatusIndOn; + ClassBStatus_t ClassBStatus; + bool IsResetCmdPending; + bool IsClassReqCmdPending; + DeviceClass_t NewClass; +} ComplianceTestState_t; + +typedef enum ComplianceMoteCmd_e +{ + COMPLIANCE_PKG_VERSION_ANS = 0x00, + COMPLIANCE_ECHO_PAYLOAD_ANS = 0x08, + COMPLIANCE_RX_APP_CNT_ANS = 0x09, + COMPLIANCE_BEACON_RX_STATUS_IND = 0x41, + COMPLIANCE_BEACON_CNT_ANS = 0x42, + COMPLIANCE_DUT_VERSION_ANS = 0x7F, +} ComplianceMoteCmd_t; + +typedef enum ComplianceSrvCmd_e +{ + COMPLIANCE_PKG_VERSION_REQ = 0x00, + COMPLIANCE_DUT_RESET_REQ = 0x01, + COMPLIANCE_DUT_JOIN_REQ = 0x02, + COMPLIANCE_SWITCH_CLASS_REQ = 0x03, + COMPLIANCE_ADR_BIT_CHANGE_REQ = 0x04, + COMPLIANCE_REGIONAL_DUTY_CYCLE_CTRL_REQ = 0x05, + COMPLIANCE_TX_PERIODICITY_CHANGE_REQ = 0x06, + COMPLIANCE_TX_FRAMES_CTRL_REQ = 0x07, + COMPLIANCE_ECHO_PAYLOAD_REQ = 0x08, + COMPLIANCE_RX_APP_CNT_REQ = 0x09, + COMPLIANCE_RX_APP_CNT_RESET_REQ = 0x0A, + COMPLIANCE_LINK_CHECK_REQ = 0x20, + COMPLIANCE_DEVICE_TIME_REQ = 0x21, + COMPLIANCE_PING_SLOT_INFO_REQ = 0x22, + COMPLIANCE_BEACON_RX_STATUS_IND_CTRL = 0x40, + COMPLIANCE_BEACON_CNT_REQ = 0x42, + COMPLIANCE_BEACON_CNT_RESET_REQ = 0x43, + COMPLIANCE_TX_CW_REQ = 0x7D, + COMPLIANCE_DUT_FPORT_224_DISABLE_REQ = 0x7E, + COMPLIANCE_DUT_VERSION_REQ = 0x7F, +} ComplianceSrvCmd_t; + +/*! + * Holds the compliance test current context + */ +static ComplianceTestState_t ComplianceTestState = { + .Initialized = false, + .IsTxPending = false, + .TxPendingTimestamp = 0, + .IsTxConfirmed = LORAMAC_HANDLER_UNCONFIRMED_MSG, + .DataBufferMaxSize = 0, + .DataBufferSize = 0, + .DataBuffer = NULL, + .RxAppCnt = 0, + .ClassBStatus = { 0 }, + .IsBeaconRxStatusIndOn = false, + .IsResetCmdPending = false, + .IsClassReqCmdPending = false, + .NewClass = CLASS_A, +}; + +/*! + * LoRaWAN compliance tests protocol handler parameters + */ +static LmhpComplianceParams_t* ComplianceParams; + +/*! + * Reset Beacon status structure + */ +static inline void ClassBStatusReset( void ) +{ + memset1( ( uint8_t* ) &ComplianceTestState.ClassBStatus, 0, sizeof( ClassBStatus_t ) / sizeof( uint8_t ) ); +} + +/*! + * Initializes the compliance tests with provided parameters + * + * \param [IN] params Structure containing the initial compliance + * tests parameters. + * \param [IN] dataBuffer Pointer to main application buffer + * \param [IN] dataBufferMaxSize Application buffer maximum size + */ +static void LmhpComplianceInit( void* params, uint8_t* dataBuffer, uint8_t dataBufferMaxSize ); + +/*! + * Returns the current compliance certification protocol initialization status. + * + * \retval status Compliance certification protocol initialization status + * [true: Initialized, false: Not initialized] + */ +static bool LmhpComplianceIsInitialized( void ); + +/*! + * Returns if a package transmission is pending or not. + * + * \retval status Package transmission status + * [true: pending, false: Not pending] + */ +static bool LmhpComplianceIsTxPending( void ); + +/*! + * Processes the LoRaMac Compliance events. + */ +static void LmhpComplianceProcess( void ); + +/*! + * Processes the MCPS Indication + * + * \param [IN] mcpsIndication MCPS indication primitive data + */ +static void LmhpComplianceOnMcpsIndication( McpsIndication_t* mcpsIndication ); + +/*! + * Processes the MLME Confirm + * + * \param [IN] mlmeConfirm MLME confirmation primitive data + */ +static void LmhpComplianceOnMlmeConfirm( MlmeConfirm_t *mlmeConfirm ); + +/*! + * Processes the MLME Indication + * + * \param [IN] mlmeIndication MLME indication primitive data + */ +static void LmhpComplianceOnMlmeIndication( MlmeIndication_t* mlmeIndication ); + +/*! + * Helper function to send the BeaconRxStatusInd message + * + * \param [IN] isBeaconRxStatusIndOn Indicates if the beacon status info is sent or not + */ +static void SendBeaconRxStatusInd( bool isBeaconRxStatusIndOn ); + +LmhPackage_t CompliancePackage = { + .Port = COMPLIANCE_PORT, + .Init = LmhpComplianceInit, + .IsInitialized = LmhpComplianceIsInitialized, + .IsTxPending = LmhpComplianceIsTxPending, + .Process = LmhpComplianceProcess, + .OnMcpsConfirmProcess = NULL, // Not used in this package + .OnMcpsIndicationProcess = LmhpComplianceOnMcpsIndication, + .OnMlmeConfirmProcess = LmhpComplianceOnMlmeConfirm, + .OnMlmeIndicationProcess = LmhpComplianceOnMlmeIndication, + .OnMacMcpsRequest = NULL, // To be initialized by LmHandler + .OnMacMlmeRequest = NULL, // To be initialized by LmHandler + .OnJoinRequest = NULL, // To be initialized by LmHandler + .OnDeviceTimeRequest = NULL, // To be initialized by LmHandler + .OnSysTimeUpdate = NULL, // To be initialized by LmHandler +}; + +LmhPackage_t* LmphCompliancePackageFactory( void ) +{ + return &CompliancePackage; +} + +static void LmhpComplianceInit( void* params, uint8_t* dataBuffer, uint8_t dataBufferMaxSize ) +{ + if( ( params != NULL ) && ( dataBuffer != NULL ) ) + { + ComplianceParams = ( LmhpComplianceParams_t* ) params; + ComplianceTestState.DataBuffer = dataBuffer; + ComplianceTestState.DataBufferMaxSize = dataBufferMaxSize; + ComplianceTestState.Initialized = true; + } + else + { + ComplianceParams = NULL; + ComplianceTestState.Initialized = false; + } + ComplianceTestState.RxAppCnt = 0; + ClassBStatusReset( ); + ComplianceTestState.IsTxPending = false; + ComplianceTestState.IsBeaconRxStatusIndOn = false; + ComplianceTestState.IsResetCmdPending = false; + ComplianceTestState.IsClassReqCmdPending = false; +} + +static bool LmhpComplianceIsInitialized( void ) +{ + return ComplianceTestState.Initialized; +} + +static bool LmhpComplianceIsTxPending( void ) +{ + return ComplianceTestState.IsTxPending; +} + +static void LmhpComplianceProcess( void ) +{ + if( ComplianceTestState.IsTxPending == true ) + { + TimerTime_t now = TimerGetCurrentTime( ); + if( now > ( ComplianceTestState.TxPendingTimestamp + LmHandlerGetDutyCycleWaitTime( ) ) ) + { + if( ComplianceTestState.DataBufferSize != 0 ) + { + // Answer commands + LmHandlerAppData_t appData = { + .Buffer = ComplianceTestState.DataBuffer, + .BufferSize = ComplianceTestState.DataBufferSize, + .Port = COMPLIANCE_PORT, + }; + + if( LmHandlerSend( &appData, ComplianceTestState.IsTxConfirmed ) != LORAMAC_HANDLER_SUCCESS ) + { + // try to send the message again + ComplianceTestState.IsTxPending = true; + } + else + { + ComplianceTestState.IsTxPending = false; + } + ComplianceTestState.TxPendingTimestamp = now; + } + } + } + else + { // If no Tx is pending process other commands + if( ComplianceTestState.IsClassReqCmdPending == true ) + { + ComplianceTestState.IsClassReqCmdPending = false; + LmHandlerRequestClass( ComplianceTestState.NewClass ); + } + } + + if( ComplianceTestState.IsResetCmdPending == true ) + { + ComplianceTestState.IsResetCmdPending = false; + + // Call platform MCU reset API + BoardResetMcu( ); + } +} + +static void LmhpComplianceOnMcpsIndication( McpsIndication_t* mcpsIndication ) +{ + uint8_t cmdIndex = 0; + MibRequestConfirm_t mibReq; + + if( ComplianceTestState.Initialized == false ) + { + return; + } + + // Increment the compliance certification protocol downlink counter + // Not counting downlinks on FPort 0 + if( ( mcpsIndication->Port > 0 ) || ( mcpsIndication->AckReceived == true ) ) + { + ComplianceTestState.RxAppCnt++; + } + + if( mcpsIndication->RxData == false ) + { + return; + } + + if( mcpsIndication->Port != COMPLIANCE_PORT ) + { + return; + } + + ComplianceTestState.DataBufferSize = 0; + + switch( mcpsIndication->Buffer[cmdIndex++] ) + { + case COMPLIANCE_PKG_VERSION_REQ: + { + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = COMPLIANCE_PKG_VERSION_ANS; + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = COMPLIANCE_ID; + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = COMPLIANCE_VERSION; + break; + } + case COMPLIANCE_DUT_RESET_REQ: + { + ComplianceTestState.IsResetCmdPending = true; + break; + } + case COMPLIANCE_DUT_JOIN_REQ: + { + CompliancePackage.OnJoinRequest( true ); + break; + } + case COMPLIANCE_SWITCH_CLASS_REQ: + { + // CLASS_A = 0, CLASS_B = 1, CLASS_C = 2 + ComplianceTestState.NewClass = ( DeviceClass_t ) mcpsIndication->Buffer[cmdIndex++]; + ComplianceTestState.IsClassReqCmdPending = true; + break; + } + case COMPLIANCE_ADR_BIT_CHANGE_REQ: + { + MibRequestConfirm_t mibReq; + mibReq.Type = MIB_ADR; + mibReq.Param.AdrEnable = mcpsIndication->Buffer[cmdIndex++]; + + LoRaMacMibSetRequestConfirm( &mibReq ); + break; + } + case COMPLIANCE_REGIONAL_DUTY_CYCLE_CTRL_REQ: + { + LoRaMacTestSetDutyCycleOn( mcpsIndication->Buffer[cmdIndex++] ); + break; + } + case COMPLIANCE_TX_PERIODICITY_CHANGE_REQ: + { + // Periodicity in milli-seconds + uint32_t periodicity[] = { 0, 5000, 10000, 20000, 30000, 40000, 50000, 60000, 120000, 240000, 480000 }; + uint8_t index = mcpsIndication->Buffer[cmdIndex++]; + + if( index < ( sizeof( periodicity ) / sizeof( uint32_t ) ) ) + { + if( ComplianceParams->OnTxPeriodicityChanged != NULL ) + { + ComplianceParams->OnTxPeriodicityChanged( periodicity[index] ); + } + } + break; + } + case COMPLIANCE_TX_FRAMES_CTRL_REQ: + { + uint8_t frameType = mcpsIndication->Buffer[cmdIndex++]; + + if( ( frameType == 1 ) || ( frameType == 2 ) ) + { + ComplianceTestState.IsTxConfirmed = ( frameType != 1 ) ? LORAMAC_HANDLER_CONFIRMED_MSG : LORAMAC_HANDLER_UNCONFIRMED_MSG; + + ComplianceParams->OnTxFrameCtrlChanged( ComplianceTestState.IsTxConfirmed ); + } + break; + } + case COMPLIANCE_ECHO_PAYLOAD_REQ: + { + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = COMPLIANCE_ECHO_PAYLOAD_ANS; + + for( uint8_t i = 1; i < MIN( mcpsIndication->BufferSize, ComplianceTestState.DataBufferMaxSize ); + i++ ) + { + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = mcpsIndication->Buffer[cmdIndex++] + 1; + } + break; + } + case COMPLIANCE_RX_APP_CNT_REQ: + { + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = COMPLIANCE_RX_APP_CNT_ANS; + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceTestState.RxAppCnt; + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceTestState.RxAppCnt >> 8; + break; + } + case COMPLIANCE_RX_APP_CNT_RESET_REQ: + { + ComplianceTestState.RxAppCnt = 0; + break; + } + case COMPLIANCE_LINK_CHECK_REQ: + { + MlmeReq_t mlmeReq; + mlmeReq.Type = MLME_LINK_CHECK; + + LoRaMacStatus_t status = LoRaMacMlmeRequest( &mlmeReq ); + if( CompliancePackage.OnMacMlmeRequest != NULL ) + { + CompliancePackage.OnMacMlmeRequest( status, &mlmeReq, mlmeReq.ReqReturn.DutyCycleWaitTime ); + } + break; + } + case COMPLIANCE_DEVICE_TIME_REQ: + { + CompliancePackage.OnDeviceTimeRequest( ); + break; + } + case COMPLIANCE_PING_SLOT_INFO_REQ: + { + ComplianceTestState.ClassBStatus.PingSlotPeriodicity = mcpsIndication->Buffer[cmdIndex++]; + ComplianceParams->OnPingSlotPeriodicityChanged( ComplianceTestState.ClassBStatus.PingSlotPeriodicity ); + break; + } + case COMPLIANCE_BEACON_RX_STATUS_IND_CTRL: + { + ComplianceTestState.IsBeaconRxStatusIndOn = ( bool ) mcpsIndication->Buffer[cmdIndex++]; + break; + } + case COMPLIANCE_BEACON_CNT_REQ: + { + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = COMPLIANCE_BEACON_CNT_ANS; + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceTestState.ClassBStatus.BeaconCnt; + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceTestState.ClassBStatus.BeaconCnt >> 8; + break; + } + case COMPLIANCE_BEACON_CNT_RESET_REQ: + { + ComplianceTestState.ClassBStatus.BeaconCnt = 0; + break; + } + case COMPLIANCE_TX_CW_REQ: + { + MlmeReq_t mlmeReq; + if( mcpsIndication->BufferSize == 7 ) + { + mlmeReq.Type = MLME_TXCW; + mlmeReq.Req.TxCw.Timeout = + ( uint16_t )( mcpsIndication->Buffer[cmdIndex] | ( mcpsIndication->Buffer[cmdIndex + 1] << 8 ) ); + cmdIndex += 2; + mlmeReq.Req.TxCw.Frequency = + ( uint32_t )( mcpsIndication->Buffer[cmdIndex] | ( mcpsIndication->Buffer[cmdIndex + 1] << 8 ) | + ( mcpsIndication->Buffer[cmdIndex + 2] << 16 ) ) * + 100; + cmdIndex += 3; + mlmeReq.Req.TxCw.Power = mcpsIndication->Buffer[cmdIndex++]; + + CompliancePackage.OnMacMlmeRequest( LoRaMacMlmeRequest( &mlmeReq ), &mlmeReq, + mlmeReq.ReqReturn.DutyCycleWaitTime ); + } + break; + } + case COMPLIANCE_DUT_FPORT_224_DISABLE_REQ: + { + mibReq.Type = MIB_IS_CERT_FPORT_ON; + mibReq.Param.IsCertPortOn = false; + LoRaMacMibSetRequestConfirm( &mibReq ); + + ComplianceTestState.IsResetCmdPending = true; + break; + } + case COMPLIANCE_DUT_VERSION_REQ: + { + Version_t lrwanVersion; + Version_t lrwanRpVersion; + MibRequestConfirm_t mibReq; + + mibReq.Type = MIB_LORAWAN_VERSION; + + LoRaMacMibGetRequestConfirm( &mibReq ); + lrwanVersion = mibReq.Param.LrWanVersion.LoRaWan; + lrwanRpVersion = mibReq.Param.LrWanVersion.LoRaWanRegion; + + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = COMPLIANCE_DUT_VERSION_ANS; + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceParams->FwVersion.Fields.Major; + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceParams->FwVersion.Fields.Minor; + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceParams->FwVersion.Fields.Patch; + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceParams->FwVersion.Fields.Revision; + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = lrwanVersion.Fields.Major; + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = lrwanVersion.Fields.Minor; + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = lrwanVersion.Fields.Patch; + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = lrwanVersion.Fields.Revision; + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = lrwanRpVersion.Fields.Major; + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = lrwanRpVersion.Fields.Minor; + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = lrwanRpVersion.Fields.Patch; + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = lrwanRpVersion.Fields.Revision; + break; + } + default: + { + break; + } + } + + if( ComplianceTestState.DataBufferSize != 0 ) + { + ComplianceTestState.IsTxPending = true; + } + else + { + // Abort any pending Tx as a new command has been processed + ComplianceTestState.IsTxPending = false; + } +} + +static void LmhpComplianceOnMlmeConfirm( MlmeConfirm_t *mlmeConfirm ) +{ + switch( mlmeConfirm->MlmeRequest ) + { + case MLME_BEACON_ACQUISITION: + { + if( mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK ) + { + ClassBStatusReset( ); + ComplianceTestState.ClassBStatus.IsBeaconRxOn = true; + } + else + { + ComplianceTestState.ClassBStatus.IsBeaconRxOn = false; + } + break; + } + default: + break; + } +} + +static void LmhpComplianceOnMlmeIndication( MlmeIndication_t* mlmeIndication ) +{ + if( ComplianceTestState.Initialized == false ) + { + return; + } + + switch( mlmeIndication->MlmeIndication ) + { + case MLME_BEACON_LOST: + { + ClassBStatusReset( ); + SendBeaconRxStatusInd( ComplianceTestState.IsBeaconRxStatusIndOn ); + break; + } + case MLME_BEACON: + { + if( mlmeIndication->Status == LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED ) + { + // As we received a beacon ensure that IsBeaconRxOn is set to true + if( ComplianceTestState.ClassBStatus.IsBeaconRxOn == false ) + { + ComplianceTestState.ClassBStatus.IsBeaconRxOn = true; + } + ComplianceTestState.ClassBStatus.BeaconCnt++; + } + ComplianceTestState.ClassBStatus.Info = mlmeIndication->BeaconInfo; + SendBeaconRxStatusInd( ComplianceTestState.IsBeaconRxStatusIndOn ); + break; + } + default: + break; + } +} + +static void SendBeaconRxStatusInd( bool isBeaconRxStatusIndOn ) +{ + if( isBeaconRxStatusIndOn == false ) + { + return; + } + uint32_t frequency = ComplianceTestState.ClassBStatus.Info.Frequency / 100; + + ComplianceTestState.DataBufferSize = 0; + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = COMPLIANCE_BEACON_RX_STATUS_IND; + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( ComplianceTestState.ClassBStatus.IsBeaconRxOn == true ) ? 1 : 0; + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.BeaconCnt ); + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.BeaconCnt >> 8 ); + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( frequency ); + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( frequency >> 8 ); + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( frequency >> 16 ); + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ComplianceTestState.ClassBStatus.Info.Datarate; + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.Rssi ); + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.Rssi >> 8 ); + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.Snr ); + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.Param ); + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.Time.Seconds ); + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.Time.Seconds >> 8 ); + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.Time.Seconds >> 16 ); + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.Time.Seconds >> 24 ); + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.GwSpecific.InfoDesc ); + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.GwSpecific.Info[0] ); + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.GwSpecific.Info[1] ); + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.GwSpecific.Info[2] ); + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.GwSpecific.Info[3] ); + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.GwSpecific.Info[4] ); + ComplianceTestState.DataBuffer[ComplianceTestState.DataBufferSize++] = ( uint8_t )( ComplianceTestState.ClassBStatus.Info.GwSpecific.Info[5] ); + + ComplianceTestState.IsTxPending = true; +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/packages/LmhpCompliance.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/packages/LmhpCompliance.h new file mode 100644 index 0000000000..714dbdb91c --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/packages/LmhpCompliance.h @@ -0,0 +1,68 @@ +/*! + * \file LmhpCompliance.h + * + * \brief Implements the LoRa-Alliance certification protocol handling + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2018 Semtech + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + */ +#ifndef __LMHP_COMPLIANCE__ +#define __LMHP_COMPLIANCE__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "LoRaMac.h" +#include "LmHandlerTypes.h" +#include "LmhPackage.h" + +/*! + * Compliance package identifier. + * + * \remark This value must be unique amongst the packages + */ +#define PACKAGE_ID_COMPLIANCE 0 + +/*! + * Compliance test protocol handler parameters + */ +typedef struct LmhpComplianceParams_s +{ + /*! + * Current firmware version + */ + Version_t FwVersion; + /*! + * + */ + void ( *OnTxPeriodicityChanged )( uint32_t periodicity ); + /*! + * + */ + void ( *OnTxFrameCtrlChanged )( LmHandlerMsgTypes_t isTxConfirmed ); + /*! + * + */ + void ( *OnPingSlotPeriodicityChanged )( uint8_t pingSlotPeriodicity ); +}LmhpComplianceParams_t; + +LmhPackage_t *LmphCompliancePackageFactory( void ); + +#ifdef __cplusplus +} +#endif + +#endif // __LMHP_COMPLIANCE__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/packages/LmhpFragmentation.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/packages/LmhpFragmentation.c new file mode 100644 index 0000000000..15e06460b4 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/packages/LmhpFragmentation.c @@ -0,0 +1,526 @@ +/*! + * \file LmhpFragmentation.c + * + * \brief Implements the LoRa-Alliance fragmented data block transport package + * Specification: https://lora-alliance.org/sites/default/files/2018-09/fragmented_data_block_transport_v1.0.0.pdf + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2018 Semtech + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + */ +#include "LmHandler.h" +#include "LmhpFragmentation.h" +#include "FragDecoder.h" + +/*! + * LoRaWAN Application Layer Fragmented Data Block Transport Specification + */ +#define FRAGMENTATION_PORT 201 + +#define FRAGMENTATION_ID 3 +#define FRAGMENTATION_VERSION 1 + +#define FRAGMENTATION_MAX_SESSIONS 4 + +// Fragmentation Tx delay state +typedef enum LmhpFragmentationTxDelayStates_e +{ + // Tx delay in idle state. + FRAGMENTATION_TX_DELAY_STATE_IDLE, + // Tx delay to be started. + FRAGMENTATION_TX_DELAY_STATE_START, + // Tx delay to be stopped. + FRAGMENTATION_TX_DELAY_STATE_STOP, +}LmhpFragmentationTxDelayStates_t; + +/*! + * Package current context + */ +typedef struct LmhpFragmentationState_s +{ + bool Initialized; + bool IsTxPending; + LmhpFragmentationTxDelayStates_t TxDelayState; + uint8_t DataBufferMaxSize; + uint8_t *DataBuffer; + uint8_t *file; +}LmhpFragmentationState_t; + +typedef enum LmhpFragmentationMoteCmd_e +{ + FRAGMENTATION_PKG_VERSION_ANS = 0x00, + FRAGMENTATION_FRAG_STATUS_ANS = 0x01, + FRAGMENTATION_FRAG_SESSION_SETUP_ANS = 0x02, + FRAGMENTATION_FRAG_SESSION_DELETE_ANS = 0x03, +}LmhpFragmentationMoteCmd_t; + +typedef enum LmhpFragmentationSrvCmd_e +{ + FRAGMENTATION_PKG_VERSION_REQ = 0x00, + FRAGMENTATION_FRAG_STATUS_REQ = 0x01, + FRAGMENTATION_FRAG_SESSION_SETUP_REQ = 0x02, + FRAGMENTATION_FRAG_SESSION_DELETE_REQ = 0x03, + FRAGMENTATION_DATA_FRAGMENT = 0x08, +}LmhpFragmentationSrvCmd_t; + +/*! + * LoRaWAN fragmented data block transport handler parameters + */ +static LmhpFragmentationParams_t* LmhpFragmentationParams; + +/*! + * Initializes the package with provided parameters + * + * \param [IN] params Pointer to the package parameters + * \param [IN] dataBuffer Pointer to main application buffer + * \param [IN] dataBufferMaxSize Main application buffer maximum size + */ +static void LmhpFragmentationInit( void *params, uint8_t *dataBuffer, uint8_t dataBufferMaxSize ); + +/*! + * Returns the current package initialization status. + * + * \retval status Package initialization status + * [true: Initialized, false: Not initialized] + */ +static bool LmhpFragmentationIsInitialized( void ); + +/*! + * Returns if a package transmission is pending or not. + * + * \retval status Package transmission status + * [true: pending, false: Not pending] + */ +static bool LmhpFragmentationIsTxPending( void ); + +/*! + * Processes the internal package events. + */ +static void LmhpFragmentationProcess( void ); + +/*! + * Processes the MCPS Indication + * + * \param [IN] mcpsIndication MCPS indication primitive data + */ +static void LmhpFragmentationOnMcpsIndication( McpsIndication_t *mcpsIndication ); + +static LmhpFragmentationState_t LmhpFragmentationState = +{ + .Initialized = false, + .IsTxPending = false, + .TxDelayState = FRAGMENTATION_TX_DELAY_STATE_IDLE, +}; + +typedef struct FragGroupData_s +{ + bool IsActive; + union + { + uint8_t Value; + struct + { + uint8_t McGroupBitMask: 4; + uint8_t FragIndex: 2; + uint8_t RFU: 2; + }Fields; + }FragSession; + uint16_t FragNb; + uint8_t FragSize; + union + { + uint8_t Value; + struct + { + uint8_t BlockAckDelay: 3; + uint8_t FragAlgo: 3; + uint8_t RFU: 2; + }Fields; + }Control; + uint8_t Padding; + uint32_t Descriptor; +}FragGroupData_t; + +typedef struct FragSessionData_s +{ + FragGroupData_t FragGroupData; + FragDecoderStatus_t FragDecoderStatus; + int32_t FragDecoderProcessStatus; +}FragSessionData_t; + +FragSessionData_t FragSessionData[FRAGMENTATION_MAX_SESSIONS]; + +// Answer struct for the commands. +LmHandlerAppData_t DelayedReplyAppData; + +static LmhPackage_t LmhpFragmentationPackage = +{ + .Port = FRAGMENTATION_PORT, + .Init = LmhpFragmentationInit, + .IsInitialized = LmhpFragmentationIsInitialized, + .IsTxPending = LmhpFragmentationIsTxPending, + .Process = LmhpFragmentationProcess, + .OnMcpsConfirmProcess = NULL, // Not used in this package + .OnMcpsIndicationProcess = LmhpFragmentationOnMcpsIndication, + .OnMlmeConfirmProcess = NULL, // Not used in this package + .OnMlmeIndicationProcess = NULL, // Not used in this package + .OnMacMcpsRequest = NULL, // To be initialized by LmHandler + .OnMacMlmeRequest = NULL, // To be initialized by LmHandler + .OnJoinRequest = NULL, // To be initialized by LmHandler + .OnDeviceTimeRequest = NULL, // To be initialized by LmHandler + .OnSysTimeUpdate = NULL, // To be initialized by LmHandler +}; + +// Delay value. +static uint32_t TxDelayTime; + +// Fragment Delay Timer struct +static TimerEvent_t FragmentTxDelayTimer; + +/*! + * \brief Callback function for Fragment delay timer. + */ +static void OnFragmentTxDelay( void* context ) +{ + // Stop the timer. + TimerStop( &FragmentTxDelayTimer ); + // Set the state. + LmhpFragmentationState.TxDelayState = FRAGMENTATION_TX_DELAY_STATE_STOP; +} + +LmhPackage_t *LmhpFragmentationPackageFactory( void ) +{ + return &LmhpFragmentationPackage; +} + +static void LmhpFragmentationInit( void *params, uint8_t *dataBuffer, uint8_t dataBufferMaxSize ) +{ + if( ( params != NULL ) && ( dataBuffer != NULL ) ) + { + LmhpFragmentationParams = ( LmhpFragmentationParams_t* )params; + LmhpFragmentationState.DataBuffer = dataBuffer; + LmhpFragmentationState.DataBufferMaxSize = dataBufferMaxSize; + LmhpFragmentationState.Initialized = true; + // Initialize Fragmentation delay time. + TxDelayTime = 0; + // Initialize Fragmentation delay timer. + TimerInit( &FragmentTxDelayTimer, OnFragmentTxDelay ); + } + else + { + LmhpFragmentationParams = NULL; + LmhpFragmentationState.Initialized = false; + } + LmhpFragmentationState.IsTxPending = false; +} + +static bool LmhpFragmentationIsInitialized( void ) +{ + return LmhpFragmentationState.Initialized; +} + +static bool LmhpFragmentationIsTxPending( void ) +{ + return LmhpFragmentationState.IsTxPending; +} + +static void LmhpFragmentationProcess( void ) +{ + LmhpFragmentationTxDelayStates_t delayTimerState; + + CRITICAL_SECTION_BEGIN( ); + delayTimerState = LmhpFragmentationState.TxDelayState; + // Set the state to idle so that the other states are executed only when they are set + // in the appropriate functions. + LmhpFragmentationState.TxDelayState = FRAGMENTATION_TX_DELAY_STATE_IDLE; + CRITICAL_SECTION_END( ); + + switch( delayTimerState ) + { + case FRAGMENTATION_TX_DELAY_STATE_START: + // Set the timer with the initially calculated Delay value. + TimerSetValue( &FragmentTxDelayTimer, TxDelayTime ); + // Start the timer. + TimerStart( &FragmentTxDelayTimer ); + break; + case FRAGMENTATION_TX_DELAY_STATE_STOP: + // Send the reply. + LmHandlerSend( &DelayedReplyAppData, LORAMAC_HANDLER_UNCONFIRMED_MSG ); + break; + case FRAGMENTATION_TX_DELAY_STATE_IDLE: + // Intentional fall through + default: + // Nothing to do. + break; + } +} + +static void LmhpFragmentationOnMcpsIndication( McpsIndication_t *mcpsIndication ) +{ + uint8_t cmdIndex = 0; + uint8_t dataBufferIndex = 0; + bool isAnswerDelayed = false; + // Answer struct for the commands. + LmHandlerAppData_t cmdReplyAppData; + // Co-efficient used to calculate delay. + uint8_t blockAckDelay = 0; + + if( mcpsIndication->Port != FRAGMENTATION_PORT ) + { + return; + } + + while( cmdIndex < mcpsIndication->BufferSize ) + { + switch( mcpsIndication->Buffer[cmdIndex++] ) + { + case FRAGMENTATION_PKG_VERSION_REQ: + { + if( mcpsIndication->Multicast == 1 ) + { + // Multicast channel. Don't process command. + break; + } + LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FRAGMENTATION_PKG_VERSION_ANS; + LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FRAGMENTATION_ID; + LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FRAGMENTATION_VERSION; + break; + } + case FRAGMENTATION_FRAG_STATUS_REQ: + { + uint8_t fragIndex = mcpsIndication->Buffer[cmdIndex++]; + uint8_t participants = fragIndex & 0x01; + + fragIndex >>= 1; + FragSessionData[fragIndex].FragDecoderStatus = FragDecoderGetStatus( ); + + if( ( participants == 1 ) || + ( ( participants == 0 ) && ( FragSessionData[fragIndex].FragDecoderStatus.FragNbLost > 0 ) ) ) + { + LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FRAGMENTATION_FRAG_STATUS_ANS; + LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FragSessionData[fragIndex].FragDecoderStatus.FragNbRx & 0xFF; + LmhpFragmentationState.DataBuffer[dataBufferIndex++] = ( fragIndex << 6 ) | + ( ( FragSessionData[fragIndex].FragDecoderStatus.FragNbRx >> 8 ) & 0x3F ); + LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FragSessionData[fragIndex].FragDecoderStatus.FragNbLost; + LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FragSessionData[fragIndex].FragDecoderStatus.MatrixError & 0x01; + + // Fetch the co-efficient value required to calculate delay of that respective session. + blockAckDelay = FragSessionData[fragIndex].FragGroupData.Control.Fields.BlockAckDelay; + isAnswerDelayed = true; + } + break; + } + case FRAGMENTATION_FRAG_SESSION_SETUP_REQ: + { + if( mcpsIndication->Multicast == 1 ) + { + // Multicast channel. Don't process command. + break; + } + FragSessionData_t fragSessionData; + uint8_t status = 0x00; + + fragSessionData.FragGroupData.FragSession.Value = mcpsIndication->Buffer[cmdIndex++]; + + fragSessionData.FragGroupData.FragNb = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x00FF; + fragSessionData.FragGroupData.FragNb |= ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0xFF00; + + fragSessionData.FragGroupData.FragSize = mcpsIndication->Buffer[cmdIndex++]; + + fragSessionData.FragGroupData.Control.Value = mcpsIndication->Buffer[cmdIndex++]; + + fragSessionData.FragGroupData.Padding = mcpsIndication->Buffer[cmdIndex++]; + + fragSessionData.FragGroupData.Descriptor = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF; + fragSessionData.FragGroupData.Descriptor += ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00; + fragSessionData.FragGroupData.Descriptor += ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000; + fragSessionData.FragGroupData.Descriptor += ( mcpsIndication->Buffer[cmdIndex++] << 24 ) & 0xFF000000; + + if( fragSessionData.FragGroupData.Control.Fields.FragAlgo > 0 ) + { + status |= 0x01; // Encoding unsupported + } + +#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) + if( ( fragSessionData.FragGroupData.FragNb > FRAG_MAX_NB ) || + ( fragSessionData.FragGroupData.FragSize > FRAG_MAX_SIZE ) || + ( ( fragSessionData.FragGroupData.FragNb * fragSessionData.FragGroupData.FragSize ) > FragDecoderGetMaxFileSize( ) ) ) + { + status |= 0x02; // Not enough Memory + } +#else + if( ( fragSessionData.FragGroupData.FragNb > FRAG_MAX_NB ) || + ( fragSessionData.FragGroupData.FragSize > FRAG_MAX_SIZE ) || + ( ( fragSessionData.FragGroupData.FragNb * fragSessionData.FragGroupData.FragSize ) > LmhpFragmentationParams->BufferSize ) ) + { + status |= 0x02; // Not enough Memory + } +#endif + status |= ( fragSessionData.FragGroupData.FragSession.Fields.FragIndex << 6 ) & 0xC0; + if( fragSessionData.FragGroupData.FragSession.Fields.FragIndex >= FRAGMENTATION_MAX_SESSIONS ) + { + status |= 0x04; // FragSession index not supported + } + + // Descriptor is not really defined in the specification + // Not clear how to handle this. + // Currently the descriptor is always correct + if( fragSessionData.FragGroupData.Descriptor != 0x01020304 ) + { + //status |= 0x08; // Wrong Descriptor + } + + if( ( status & 0x0F ) == 0 ) + { + // The FragSessionSetup is accepted + fragSessionData.FragGroupData.IsActive = true; + fragSessionData.FragDecoderProcessStatus = FRAG_SESSION_ONGOING; + FragSessionData[fragSessionData.FragGroupData.FragSession.Fields.FragIndex] = fragSessionData; +#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) + FragDecoderInit( fragSessionData.FragGroupData.FragNb, + fragSessionData.FragGroupData.FragSize, + &LmhpFragmentationParams->DecoderCallbacks ); +#else + FragDecoderInit( fragSessionData.FragGroupData.FragNb, + fragSessionData.FragGroupData.FragSize, + LmhpFragmentationParams->Buffer, + LmhpFragmentationParams->BufferSize ); +#endif + } + LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FRAGMENTATION_FRAG_SESSION_SETUP_ANS; + LmhpFragmentationState.DataBuffer[dataBufferIndex++] = status; + isAnswerDelayed = false; + break; + } + case FRAGMENTATION_FRAG_SESSION_DELETE_REQ: + { + if( mcpsIndication->Multicast == 1 ) + { + // Multicast channel. Don't process command. + break; + } + uint8_t status = 0x00; + uint8_t id = mcpsIndication->Buffer[cmdIndex++] & 0x03; + + status |= id; + if( ( id >= FRAGMENTATION_MAX_SESSIONS ) || ( FragSessionData[id].FragGroupData.IsActive == false ) ) + { + status |= 0x04; // Session does not exist + } + else + { + // Delete session + FragSessionData[id].FragGroupData.IsActive = false; + } + LmhpFragmentationState.DataBuffer[dataBufferIndex++] = FRAGMENTATION_FRAG_SESSION_DELETE_ANS; + LmhpFragmentationState.DataBuffer[dataBufferIndex++] = status; + isAnswerDelayed = false; + break; + } + case FRAGMENTATION_DATA_FRAGMENT: + { + uint8_t fragIndex = 0; + uint16_t fragCounter = 0; + + fragCounter = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x00FF; + fragCounter |= ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0xFF00; + + fragIndex = ( fragCounter >> 14 ) & 0x03; + fragCounter &= 0x3FFF; + + if( mcpsIndication->Multicast == 1 ) + { + // Message received on a multicast address + // + // TODO: Not working yet + // + // Check McGroupBitMask + //uint8_t groupId = LoRaMacMcChannelGetGroupId( mcpsIndication->DevAddress ); + //if( ( groupId == 0xFF ) || + // ( ( FragSessionData[fragIndex].FragGroupData.FragSession.Fields.McGroupBitMask & ( 1 << groupId ) ) == 0 ) ) + //{ + // // Ignore message + // break; + //} + } + + if( FragSessionData[fragIndex].FragDecoderProcessStatus == FRAG_SESSION_ONGOING ) + { + FragSessionData[fragIndex].FragDecoderProcessStatus = FragDecoderProcess( fragCounter, &mcpsIndication->Buffer[cmdIndex] ); + FragSessionData[fragIndex].FragDecoderStatus = FragDecoderGetStatus( ); + if( LmhpFragmentationParams->OnProgress != NULL ) + { + LmhpFragmentationParams->OnProgress( FragSessionData[fragIndex].FragDecoderStatus.FragNbRx, + FragSessionData[fragIndex].FragGroupData.FragNb, + FragSessionData[fragIndex].FragGroupData.FragSize, + FragSessionData[fragIndex].FragDecoderStatus.FragNbLost ); + } + } + if( FragSessionData[fragIndex].FragDecoderProcessStatus >= 0 ) + { + // Fragmentation successfully done + FragSessionData[fragIndex].FragDecoderProcessStatus = FRAG_SESSION_NOT_STARTED; + if( LmhpFragmentationParams->OnDone != NULL ) + { +#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) + LmhpFragmentationParams->OnDone( FragSessionData[fragIndex].FragDecoderProcessStatus, + ( FragSessionData[fragIndex].FragGroupData.FragNb * FragSessionData[fragIndex].FragGroupData.FragSize ) - FragSessionData[fragIndex].FragGroupData.Padding ); +#else + LmhpFragmentationParams->OnDone( FragSessionData[fragIndex].FragDecoderProcessStatus, + LmhpFragmentationParams->Buffer, + ( FragSessionData[fragIndex].FragGroupData.FragNb * FragSessionData[fragIndex].FragGroupData.FragSize ) - FragSessionData[fragIndex].FragGroupData.Padding ); +#endif + } + } + cmdIndex += FragSessionData[fragIndex].FragGroupData.FragSize; + break; + } + default: + { + break; + } + } + } + + // After processing the commands, if the end-node has to reply back then a flag is checked if the + // reply is to be sent immediately or with a delay. + // In some scenarios it is not desired that multiple end-notes send uplinks at the same time to + // the same server. (Example: Fragment status during a multicast FUOTA) + if( dataBufferIndex != 0 ) + { + // Prepare Answer that is to be transmitted + cmdReplyAppData.Buffer = LmhpFragmentationState.DataBuffer; + cmdReplyAppData.BufferSize = dataBufferIndex; + cmdReplyAppData.Port = FRAGMENTATION_PORT; + + if( isAnswerDelayed == true ) + { + // Delay value is calculated using BlockAckDelay which is communicated by server during the FragSessionSetupReq + // Pseudo Random Delay = rand(0:1) * 2^(blockAckDelay + 4) Seconds. + // Delay = Pseudo Random Delay * 1000 milli seconds. + // Eg: blockAckDelay = 7 + // Pseudo Random Delay = rand(0:1) * 2^11 + // rand(0:1) seconds = rand(0:1000) milliseconds + // Delay = rand(0:1000) * 2048 => 2048000ms = 34 minutes + TxDelayTime = randr( 0, 1000 ) * ( 1 << ( blockAckDelay + 4 ) ); + DelayedReplyAppData = cmdReplyAppData; + LmhpFragmentationState.TxDelayState = FRAGMENTATION_TX_DELAY_STATE_START; + } + else + { + // Send the prepared answer + LmHandlerSend( &cmdReplyAppData, LORAMAC_HANDLER_UNCONFIRMED_MSG ); + } + } +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/packages/LmhpFragmentation.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/packages/LmhpFragmentation.h new file mode 100644 index 0000000000..3033f36871 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/packages/LmhpFragmentation.h @@ -0,0 +1,100 @@ +/*! + * \file LmhpFragmentation.h + * + * \brief Implements the LoRa-Alliance fragmented data block transport package + * Specification: https://lora-alliance.org/sites/default/files/2018-09/fragmented_data_block_transport_v1.0.0.pdf + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2018 Semtech + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + */ +#ifndef __LMHP_FRAGMENTATION_H__ +#define __LMHP_FRAGMENTATION_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "LoRaMac.h" +#include "LmHandlerTypes.h" +#include "LmhPackage.h" +#include "FragDecoder.h" + +/*! + * Fragmentation data block transport package identifier. + * + * \remark This value must be unique amongst the packages + */ +#define PACKAGE_ID_FRAGMENTATION 3 + +/*! + * Fragmentation package parameters + */ +typedef struct LmhpFragmentationParams_s +{ +#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) + /*! + * FragDecoder Write/Read function callbacks + */ + FragDecoderCallbacks_t DecoderCallbacks; +#else + /*! + * Pointer to the un-fragmented received buffer. + */ + uint8_t *Buffer; + /*! + * Size of the un-fragmented received buffer. + */ + uint32_t BufferSize; +#endif + /*! + * Notifies the progress of the current fragmentation session + * + * \param [IN] fragCounter Fragment counter + * \param [IN] fragNb Number of fragments + * \param [IN] fragSize Size of fragments + * \param [IN] fragNbLost Number of lost fragments + */ + void ( *OnProgress )( uint16_t fragCounter, uint16_t fragNb, uint8_t fragSize, uint16_t fragNbLost ); +#if( FRAG_DECODER_FILE_HANDLING_NEW_API == 1 ) + /*! + * Notifies that the fragmentation session is finished + * + * \param [IN] status Fragmentation session status [FRAG_SESSION_ONGOING, + * FRAG_SESSION_FINISHED or + * FragDecoder.Status.FragNbLost] + * \param [IN] size Received file size + */ + void ( *OnDone )( int32_t status, uint32_t size ); +#else + /*! + * Notifies that the fragmentation session is finished + * + * \param [IN] status Fragmentation session status [FRAG_SESSION_ONGOING, + * FRAG_SESSION_FINISHED or + * FragDecoder.Status.FragNbLost] + * \param [IN] file Pointer to the reception file buffer + * \param [IN] size Received file size + */ + void ( *OnDone )( int32_t status, uint8_t *file, uint32_t size ); +#endif +}LmhpFragmentationParams_t; + +LmhPackage_t *LmhpFragmentationPackageFactory( void ); + +#ifdef __cplusplus +} +#endif + +#endif // __LMHP_FRAGMENTATION_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/packages/LmhpRemoteMcastSetup.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/packages/LmhpRemoteMcastSetup.c new file mode 100644 index 0000000000..a621cc0b54 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/packages/LmhpRemoteMcastSetup.c @@ -0,0 +1,555 @@ +/*! + * \file LmhpRemoteMcastSetup.c + * + * \brief Implements the LoRa-Alliance remote multicast setup package + * Specification: https://lora-alliance.org/sites/default/files/2018-09/remote_multicast_setup_v1.0.0.pdf + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2018 Semtech + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + */ +#include "LmHandler.h" +#include "LmhpRemoteMcastSetup.h" + +#define DBG_TRACE 1 + +#if DBG_TRACE == 1 + #include + /*! + * Works in the same way as the printf function does. + */ + #define DBG( ... ) \ + do \ + { \ + printf( __VA_ARGS__ ); \ + }while( 0 ) + + #define DBG_SESSION( id, isRxParamsSetup ) \ + do \ + { \ + DBG( "ID : %d\n", McSessionData[id].McGroupData.IdHeader.Fields.McGroupId ); \ + DBG( "McAddr : %08X\n", McSessionData[id].McGroupData.McAddr ); \ + DBG( "McKey : %02X", McSessionData[id].McGroupData.McKeyEncrypted[0] ); \ + for( int i = 1; i < 16; i++ ) \ + { \ + DBG( "-%02X", McSessionData[id].McGroupData.McKeyEncrypted[i] ); \ + } \ + DBG( "\n" ); \ + DBG( "McFCountMin : %u\n", McSessionData[id].McGroupData.McFCountMin ); \ + DBG( "McFCountMax : %u\n", McSessionData[id].McGroupData.McFCountMax ); \ + if( isRxParamsSetup == true ) \ + { \ + DBG( "SessionTime : %u\n", McSessionData[id].SessionTime ); \ + DBG( "SessionTimeT: %d\n", McSessionData[id].SessionTimeout ); \ + if( McSessionData[id].RxParams.Class == CLASS_B ) \ + { \ + DBG( "Rx Freq : %u\n", McSessionData[id].RxParams.Params.ClassB.Frequency ); \ + DBG( "Rx DR : DR_%d\n", McSessionData[id].RxParams.Params.ClassB.Datarate ); \ + DBG( "Periodicity : %u\n", McSessionData[id].RxParams.Params.ClassB.Periodicity ); \ + } \ + else \ + { \ + DBG( "Rx Freq : %u\n", McSessionData[id].RxParams.Params.ClassC.Frequency ); \ + DBG( "Rx DR : DR_%d\n", McSessionData[id].RxParams.Params.ClassC.Datarate ); \ + } \ + } \ + } while ( 0 ) +#else + #define DBG( ... ) + #define DBG_SESSION( id, isRxParamsSetup ) +#endif + +/*! + * LoRaWAN Application Layer Remote multicast setup Specification + */ +#define REMOTE_MCAST_SETUP_PORT 200 + +#define REMOTE_MCAST_SETUP_ID 2 +#define REMOTE_MCAST_SETUP_VERSION 1 + +typedef enum LmhpRemoteMcastSetupSessionStates_e +{ + REMOTE_MCAST_SETUP_SESSION_STATE_IDLE, + REMOTE_MCAST_SETUP_SESSION_STATE_START, + REMOTE_MCAST_SETUP_SESSION_STATE_STOP, +}LmhpRemoteMcastSetupSessionStates_t; + +/*! + * Package current context + */ +typedef struct LmhpRemoteMcastSetupState_s +{ + bool Initialized; + bool IsTxPending; + LmhpRemoteMcastSetupSessionStates_t SessionState; + uint8_t DataBufferMaxSize; + uint8_t *DataBuffer; +}LmhpRemoteMcastSetupState_t; + +typedef enum LmhpRemoteMcastSetupMoteCmd_e +{ + REMOTE_MCAST_SETUP_PKG_VERSION_ANS = 0x00, + REMOTE_MCAST_SETUP_MC_GROUP_STATUS_ANS = 0x01, + REMOTE_MCAST_SETUP_MC_GROUP_SETUP_ANS = 0x02, + REMOTE_MCAST_SETUP_MC_GROUP_DELETE_ANS = 0x03, + REMOTE_MCAST_SETUP_MC_GROUP_CLASS_C_SESSION_ANS = 0x04, + REMOTE_MCAST_SETUP_MC_GROUP_CLASS_B_SESSION_ANS = 0x05, +}LmhpRemoteMcastSetupMoteCmd_t; + +typedef enum LmhpRemoteMcastSetupSrvCmd_e +{ + REMOTE_MCAST_SETUP_PKG_VERSION_REQ = 0x00, + REMOTE_MCAST_SETUP_MC_GROUP_STATUS_REQ = 0x01, + REMOTE_MCAST_SETUP_MC_GROUP_SETUP_REQ = 0x02, + REMOTE_MCAST_SETUP_MC_GROUP_DELETE_REQ = 0x03, + REMOTE_MCAST_SETUP_MC_GROUP_CLASS_C_SESSION_REQ = 0x04, + REMOTE_MCAST_SETUP_MC_GROUP_CLASS_B_SESSION_REQ = 0x05, +}LmhpRemoteMcastSetupSrvCmd_t; + +/*! + * Initializes the package with provided parameters + * + * \param [IN] params Pointer to the package parameters + * \param [IN] dataBuffer Pointer to main application buffer + * \param [IN] dataBufferMaxSize Main application buffer maximum size + */ +static void LmhpRemoteMcastSetupInit( void *params, uint8_t *dataBuffer, uint8_t dataBufferMaxSize ); + +/*! + * Returns the current package initialization status. + * + * \retval status Package initialization status + * [true: Initialized, false: Not initialized] + */ +static bool LmhpRemoteMcastSetupIsInitialized( void ); + +/*! + * Returns if a package transmission is pending or not. + * + * \retval status Package transmission status + * [true: pending, false: Not pending] + */ +static bool LmhpRemoteMcastSetupIsTxPending( void ); + +/*! + * Processes the internal package events. + */ +static void LmhpRemoteMcastSetupProcess( void ); + +/*! + * Processes the MCPS Indication + * + * \param [IN] mcpsIndication MCPS indication primitive data + */ +static void LmhpRemoteMcastSetupOnMcpsIndication( McpsIndication_t *mcpsIndication ); + +static void OnSessionStartTimer( void *context ); + +static void OnSessionStopTimer( void *context ); + +static LmhpRemoteMcastSetupState_t LmhpRemoteMcastSetupState = +{ + .Initialized = false, + .IsTxPending = false, + .SessionState = REMOTE_MCAST_SETUP_SESSION_STATE_IDLE, +}; + +typedef struct McGroupData_s +{ + union + { + uint8_t Value; + struct + { + uint8_t McGroupId: 2; + uint8_t RFU: 6; + }Fields; + }IdHeader; + uint32_t McAddr; + uint8_t McKeyEncrypted[16]; + uint32_t McFCountMin; + uint32_t McFCountMax; +}McGroupData_t; + +typedef enum eSessionState +{ + SESSION_STOPED, + SESSION_STARTED +}SessionState_t; + +typedef struct McSessionData_s +{ + McGroupData_t McGroupData; + SessionState_t SessionState; + uint32_t SessionTime; + uint8_t SessionTimeout; + McRxParams_t RxParams; +}McSessionData_t; + +McSessionData_t McSessionData[LORAMAC_MAX_MC_CTX]; + +/*! + * Session start timer + */ +static TimerEvent_t SessionStartTimer; + +/*! + * Session start timer + */ +static TimerEvent_t SessionStopTimer; + +static LmhPackage_t LmhpRemoteMcastSetupPackage = +{ + .Port = REMOTE_MCAST_SETUP_PORT, + .Init = LmhpRemoteMcastSetupInit, + .IsInitialized = LmhpRemoteMcastSetupIsInitialized, + .IsTxPending = LmhpRemoteMcastSetupIsTxPending, + .Process = LmhpRemoteMcastSetupProcess, + .OnMcpsConfirmProcess = NULL, // Not used in this package + .OnMcpsIndicationProcess = LmhpRemoteMcastSetupOnMcpsIndication, + .OnMlmeConfirmProcess = NULL, // Not used in this package + .OnMlmeIndicationProcess = NULL, // Not used in this package + .OnMacMcpsRequest = NULL, // To be initialized by LmHandler + .OnMacMlmeRequest = NULL, // To be initialized by LmHandler + .OnJoinRequest = NULL, // To be initialized by LmHandler + .OnDeviceTimeRequest = NULL, // To be initialized by LmHandler + .OnSysTimeUpdate = NULL, // To be initialized by LmHandler +}; + +LmhPackage_t *LmhpRemoteMcastSetupPackageFactory( void ) +{ + return &LmhpRemoteMcastSetupPackage; +} + +static void LmhpRemoteMcastSetupInit( void * params, uint8_t *dataBuffer, uint8_t dataBufferMaxSize ) +{ + if( dataBuffer != NULL ) + { + LmhpRemoteMcastSetupState.DataBuffer = dataBuffer; + LmhpRemoteMcastSetupState.DataBufferMaxSize = dataBufferMaxSize; + LmhpRemoteMcastSetupState.Initialized = true; + TimerInit( &SessionStartTimer, OnSessionStartTimer ); + TimerInit( &SessionStopTimer, OnSessionStopTimer ); + } + else + { + LmhpRemoteMcastSetupState.Initialized = false; + } + LmhpRemoteMcastSetupState.IsTxPending = false; +} + +static bool LmhpRemoteMcastSetupIsInitialized( void ) +{ + return LmhpRemoteMcastSetupState.Initialized; +} + +static bool LmhpRemoteMcastSetupIsTxPending( void ) +{ + return LmhpRemoteMcastSetupState.IsTxPending; +} + +static void LmhpRemoteMcastSetupProcess( void ) +{ + LmhpRemoteMcastSetupSessionStates_t state; + + CRITICAL_SECTION_BEGIN( ); + state = LmhpRemoteMcastSetupState.SessionState; + LmhpRemoteMcastSetupState.SessionState = REMOTE_MCAST_SETUP_SESSION_STATE_IDLE; + CRITICAL_SECTION_END( ); + + switch( state ) + { + case REMOTE_MCAST_SETUP_SESSION_STATE_START: + // Switch to Class C + LmHandlerRequestClass( CLASS_C ); + + TimerSetValue( &SessionStopTimer, ( 1 << McSessionData[0].SessionTimeout ) * 1000 ); + TimerStart( &SessionStopTimer ); + break; + case REMOTE_MCAST_SETUP_SESSION_STATE_STOP: + // Switch back to Class A + LmHandlerRequestClass( CLASS_A ); + break; + case REMOTE_MCAST_SETUP_SESSION_STATE_IDLE: + // Intentional fall through + default: + // Nothing to do. + break; + } +} + +static void LmhpRemoteMcastSetupOnMcpsIndication( McpsIndication_t *mcpsIndication ) +{ + uint8_t cmdIndex = 0; + uint8_t dataBufferIndex = 0; + + if( mcpsIndication->Port != REMOTE_MCAST_SETUP_PORT ) + { + return; + } + + while( cmdIndex < mcpsIndication->BufferSize ) + { + switch( mcpsIndication->Buffer[cmdIndex++] ) + { + case REMOTE_MCAST_SETUP_PKG_VERSION_REQ: + { + LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = REMOTE_MCAST_SETUP_PKG_VERSION_ANS; + LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = REMOTE_MCAST_SETUP_ID; + LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = REMOTE_MCAST_SETUP_VERSION; + break; + } + case REMOTE_MCAST_SETUP_MC_GROUP_STATUS_REQ: + { + // TODO implement command processing and handling + break; + } + case REMOTE_MCAST_SETUP_MC_GROUP_SETUP_REQ: + { + uint8_t idError = 0x01; // One bit value + uint8_t id = mcpsIndication->Buffer[cmdIndex++]; + + McSessionData[id].McGroupData.IdHeader.Value = id; + + if( id < LORAMAC_MAX_MC_CTX ) + { + McSessionData[id].McGroupData.McAddr = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF; + McSessionData[id].McGroupData.McAddr += ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00; + McSessionData[id].McGroupData.McAddr += ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000; + McSessionData[id].McGroupData.McAddr += ( mcpsIndication->Buffer[cmdIndex++] << 24 ) & 0xFF000000; + + for( int8_t i = 0; i < 16; i++ ) + { + McSessionData[id].McGroupData.McKeyEncrypted[i] = mcpsIndication->Buffer[cmdIndex++]; + } + + McSessionData[id].McGroupData.McFCountMin = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF; + McSessionData[id].McGroupData.McFCountMin += ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00; + McSessionData[id].McGroupData.McFCountMin += ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000; + McSessionData[id].McGroupData.McFCountMin += ( mcpsIndication->Buffer[cmdIndex++] << 24 ) & 0xFF000000; + + McSessionData[id].McGroupData.McFCountMax = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF; + McSessionData[id].McGroupData.McFCountMax += ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00; + McSessionData[id].McGroupData.McFCountMax += ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000; + McSessionData[id].McGroupData.McFCountMax += ( mcpsIndication->Buffer[cmdIndex++] << 24 ) & 0xFF000000; + + McChannelParams_t channel = + { + .IsRemotelySetup = true, + .IsEnabled = true, + .GroupID = ( AddressIdentifier_t )McSessionData[id].McGroupData.IdHeader.Fields.McGroupId, + .Address = McSessionData[id].McGroupData.McAddr, + .McKeys.McKeyE = McSessionData[id].McGroupData.McKeyEncrypted, + .FCountMin = McSessionData[id].McGroupData.McFCountMin, + .FCountMax = McSessionData[id].McGroupData.McFCountMax, + .RxParams.Params.ClassC = // Field not used for multicast channel setup. Must be initialized to something + { + .Frequency = 0, + .Datarate = 0 + } + }; + + if( LoRaMacMcChannelSetup( &channel ) == LORAMAC_STATUS_OK ) + { + idError = 0x00; + + } + } + LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = REMOTE_MCAST_SETUP_MC_GROUP_SETUP_ANS; + LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = ( idError << 2 ) | McSessionData[id].McGroupData.IdHeader.Fields.McGroupId; + DBG_SESSION( id, false ); + break; + } + case REMOTE_MCAST_SETUP_MC_GROUP_DELETE_REQ: + { + uint8_t status = 0x00; + uint8_t id = mcpsIndication->Buffer[cmdIndex++] & 0x03; + + status = id; + + LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = REMOTE_MCAST_SETUP_MC_GROUP_DELETE_ANS; + + if( LoRaMacMcChannelDelete( ( AddressIdentifier_t )id ) != LORAMAC_STATUS_OK ) + { + status |= 0x04; // McGroupUndefined bit set + } + LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = status; + break; + } + case REMOTE_MCAST_SETUP_MC_GROUP_CLASS_C_SESSION_REQ: + { + bool isTimerSet = false; + int32_t timeToSessionStart = 0; + uint8_t status = 0x00; + uint8_t id = mcpsIndication->Buffer[cmdIndex++] & 0x03; + + if( id < LORAMAC_MAX_MC_CTX ) + { + McSessionData[id].RxParams.Class = CLASS_C; + + McSessionData[id].SessionTime = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF; + McSessionData[id].SessionTime += ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00; + McSessionData[id].SessionTime += ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000; + McSessionData[id].SessionTime += ( mcpsIndication->Buffer[cmdIndex++] << 24 ) & 0xFF000000; + + // Add Unix to Gps epoch offset. The system time is based on Unix time. + McSessionData[id].SessionTime += UNIX_GPS_EPOCH_OFFSET; + + McSessionData[id].SessionTimeout = mcpsIndication->Buffer[cmdIndex++] & 0x0F; + + McSessionData[id].RxParams.Params.ClassC.Frequency = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF; + McSessionData[id].RxParams.Params.ClassC.Frequency |= ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00; + McSessionData[id].RxParams.Params.ClassC.Frequency |= ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000; + McSessionData[id].RxParams.Params.ClassC.Frequency *= 100; + McSessionData[id].RxParams.Params.ClassC.Datarate = mcpsIndication->Buffer[cmdIndex++]; + + if( LoRaMacMcChannelSetupRxParams( ( AddressIdentifier_t )id, &McSessionData[id].RxParams, &status ) == LORAMAC_STATUS_OK ) + { + SysTime_t curTime = { .Seconds = 0, .SubSeconds = 0 }; + curTime = SysTimeGet( ); + + timeToSessionStart = McSessionData[id].SessionTime - curTime.Seconds; + if( timeToSessionStart > 0 ) + { + // Start session start timer + TimerSetValue( &SessionStartTimer, timeToSessionStart * 1000 ); + TimerStart( &SessionStartTimer ); + + isTimerSet = true; + + DBG( "Time2SessionStart: %d ms\n", timeToSessionStart * 1000 ); + } + else + { + // Session start time before current device time + status |= 0x10; // McGroupUndefined bit set + } + } + } + else + { + status |= 0x10; // McGroupUndefined bit set + } + + LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = REMOTE_MCAST_SETUP_MC_GROUP_CLASS_C_SESSION_ANS; + LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = status; + if( isTimerSet == true ) + { + LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = ( timeToSessionStart >> 0 ) & 0xFF; + LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = ( timeToSessionStart >> 8 ) & 0xFF; + LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = ( timeToSessionStart >> 16 ) & 0xFF; + } + DBG_SESSION( id, true ); + break; + } + case REMOTE_MCAST_SETUP_MC_GROUP_CLASS_B_SESSION_REQ: + { + bool isTimerSet = false; + int32_t timeToSessionStart = 0; + uint8_t status = 0x00; + uint8_t id = mcpsIndication->Buffer[cmdIndex++] & 0x03; + + if( id < LORAMAC_MAX_MC_CTX ) + { + McSessionData[id].RxParams.Class = CLASS_B; + + McSessionData[id].SessionTime = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF; + McSessionData[id].SessionTime += ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00; + McSessionData[id].SessionTime += ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000; + McSessionData[id].SessionTime += ( mcpsIndication->Buffer[cmdIndex++] << 24 ) & 0xFF000000; + + // Add Unix to Gps epoch offset. The system time is based on Unix time. + McSessionData[id].SessionTime += UNIX_GPS_EPOCH_OFFSET; + + McSessionData[id].RxParams.Params.ClassB.Periodicity = ( mcpsIndication->Buffer[cmdIndex] >> 4 ) & 0x07; + McSessionData[id].SessionTimeout = mcpsIndication->Buffer[cmdIndex++] & 0x0F; + + McSessionData[id].RxParams.Params.ClassB.Frequency = ( mcpsIndication->Buffer[cmdIndex++] << 0 ) & 0x000000FF; + McSessionData[id].RxParams.Params.ClassB.Frequency |= ( mcpsIndication->Buffer[cmdIndex++] << 8 ) & 0x0000FF00; + McSessionData[id].RxParams.Params.ClassB.Frequency |= ( mcpsIndication->Buffer[cmdIndex++] << 16 ) & 0x00FF0000; + McSessionData[id].RxParams.Params.ClassB.Frequency *= 100; + McSessionData[id].RxParams.Params.ClassB.Datarate = mcpsIndication->Buffer[cmdIndex++]; + + if( LoRaMacMcChannelSetupRxParams( ( AddressIdentifier_t )id, &McSessionData[id].RxParams, &status ) == LORAMAC_STATUS_OK ) + { + SysTime_t curTime = { .Seconds = 0, .SubSeconds = 0 }; + curTime = SysTimeGet( ); + + timeToSessionStart = McSessionData[id].SessionTime - curTime.Seconds; + if( timeToSessionStart > 0 ) + { + // Start session start timer + TimerSetValue( &SessionStartTimer, timeToSessionStart * 1000 ); + TimerStart( &SessionStartTimer ); + + isTimerSet = true; + + DBG( "Time2SessionStart: %d ms\n", timeToSessionStart * 1000 ); + } + else + { + // Session start time before current device time + status |= 0x10; // McGroupUndefined bit set + } + } + } + else + { + status |= 0x10; // McGroupUndefined bit set + } + + LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = REMOTE_MCAST_SETUP_MC_GROUP_CLASS_B_SESSION_ANS; + LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = status; + if( isTimerSet == true ) + { + LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = ( timeToSessionStart >> 0 ) & 0xFF; + LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = ( timeToSessionStart >> 8 ) & 0xFF; + LmhpRemoteMcastSetupState.DataBuffer[dataBufferIndex++] = ( timeToSessionStart >> 16 ) & 0xFF; + } + DBG_SESSION( id, true ); + break; + } + default: + { + break; + } + } + } + + if( dataBufferIndex != 0 ) + { + // Answer commands + LmHandlerAppData_t appData = + { + .Buffer = LmhpRemoteMcastSetupState.DataBuffer, + .BufferSize = dataBufferIndex, + .Port = REMOTE_MCAST_SETUP_PORT + }; + LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG ); + } +} + +static void OnSessionStartTimer( void *context ) +{ + TimerStop( &SessionStartTimer ); + + LmhpRemoteMcastSetupState.SessionState = REMOTE_MCAST_SETUP_SESSION_STATE_START; +} + +static void OnSessionStopTimer( void *context ) +{ + TimerStop( &SessionStopTimer ); + + LmhpRemoteMcastSetupState.SessionState = REMOTE_MCAST_SETUP_SESSION_STATE_STOP; +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/packages/LmhpRemoteMcastSetup.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/packages/LmhpRemoteMcastSetup.h new file mode 100644 index 0000000000..c1ecac0b6c --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandler/packages/LmhpRemoteMcastSetup.h @@ -0,0 +1,55 @@ +/*! + * \file LmhpRemoteMcastSetup.h + * + * \brief Implements the LoRa-Alliance remote multicast setup package + * Specification: https://lora-alliance.org/sites/default/files/2018-09/remote_multicast_setup_v1.0.0.pdf + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2018 Semtech + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + */ +#ifndef __LMHP_REMOTE_MCAST_SETUP_H__ +#define __LMHP_REMOTE_MCAST_SETUP_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "LoRaMac.h" +#include "LmHandlerTypes.h" +#include "LmhPackage.h" + +/*! + * Remote multicast setup package identifier. + * + * \remark This value must be unique amongst the packages + */ +#define PACKAGE_ID_REMOTE_MCAST_SETUP 2 + +/*! + * Remote multicast setup package parameters + * + * This package doesn't require parameters + */ +//typedef struct LmhpRemoteMcastSetupParams_s +//{ +//}LmhpRemoteMcastSetupParams_t; + +LmhPackage_t *LmhpRemoteMcastSetupPackageFactory( void ); + +#ifdef __cplusplus +} +#endif + +#endif // __LMHP_REMOTE_MCAST_SETUP_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandlerMsgDisplay.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandlerMsgDisplay.c new file mode 100644 index 0000000000..2e28b76a23 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandlerMsgDisplay.c @@ -0,0 +1,428 @@ +/*! + * \file LmHandlerMsgDisplay.h + * + * \brief Common set of functions to display default messages from + * LoRaMacHandler. + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2019 Semtech + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + */ +#include +#include +#include +#include +#include "utilities.h" +#include "timer.h" + +#include "LmHandlerMsgDisplay.h" + +/*! + * MAC status strings + */ +const char* MacStatusStrings[] = +{ + "OK", // LORAMAC_STATUS_OK + "Busy", // LORAMAC_STATUS_BUSY + "Service unknown", // LORAMAC_STATUS_SERVICE_UNKNOWN + "Parameter invalid", // LORAMAC_STATUS_PARAMETER_INVALID + "Frequency invalid", // LORAMAC_STATUS_FREQUENCY_INVALID + "Datarate invalid", // LORAMAC_STATUS_DATARATE_INVALID + "Frequency or datarate invalid", // LORAMAC_STATUS_FREQ_AND_DR_INVALID + "No network joined", // LORAMAC_STATUS_NO_NETWORK_JOINED + "Length error", // LORAMAC_STATUS_LENGTH_ERROR + "Region not supported", // LORAMAC_STATUS_REGION_NOT_SUPPORTED + "Skipped APP data", // LORAMAC_STATUS_SKIPPED_APP_DATA + "Duty-cycle restricted", // LORAMAC_STATUS_DUTYCYCLE_RESTRICTED + "No channel found", // LORAMAC_STATUS_NO_CHANNEL_FOUND + "No free channel found", // LORAMAC_STATUS_NO_FREE_CHANNEL_FOUND + "Busy beacon reserved time", // LORAMAC_STATUS_BUSY_BEACON_RESERVED_TIME + "Busy ping-slot window time", // LORAMAC_STATUS_BUSY_PING_SLOT_WINDOW_TIME + "Busy uplink collision", // LORAMAC_STATUS_BUSY_UPLINK_COLLISION + "Crypto error", // LORAMAC_STATUS_CRYPTO_ERROR + "FCnt handler error", // LORAMAC_STATUS_FCNT_HANDLER_ERROR + "MAC command error", // LORAMAC_STATUS_MAC_COMMAD_ERROR + "ClassB error", // LORAMAC_STATUS_CLASS_B_ERROR + "Confirm queue error", // LORAMAC_STATUS_CONFIRM_QUEUE_ERROR + "Multicast group undefined", // LORAMAC_STATUS_MC_GROUP_UNDEFINED + "Unknown error", // LORAMAC_STATUS_ERROR +}; + +/*! + * MAC event info status strings. + */ +const char* EventInfoStatusStrings[] = +{ + "OK", // LORAMAC_EVENT_INFO_STATUS_OK + "Error", // LORAMAC_EVENT_INFO_STATUS_ERROR + "Tx timeout", // LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT + "Rx 1 timeout", // LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT + "Rx 2 timeout", // LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT + "Rx1 error", // LORAMAC_EVENT_INFO_STATUS_RX1_ERROR + "Rx2 error", // LORAMAC_EVENT_INFO_STATUS_RX2_ERROR + "Join failed", // LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL + "Downlink repeated", // LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED + "Tx DR payload size error", // LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR + "Address fail", // LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL + "MIC fail", // LORAMAC_EVENT_INFO_STATUS_MIC_FAIL + "Multicast fail", // LORAMAC_EVENT_INFO_STATUS_MULTICAST_FAIL + "Beacon locked", // LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED + "Beacon lost", // LORAMAC_EVENT_INFO_STATUS_BEACON_LOST + "Beacon not found" // LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND +}; + +/*! + * Prints the provided buffer in HEX + * + * \param buffer Buffer to be printed + * \param size Buffer size to be printed + */ +void PrintHexBuffer( uint8_t *buffer, uint8_t size ) +{ + uint8_t newline = 0; + + for( uint8_t i = 0; i < size; i++ ) + { + if( newline != 0 ) + { + printf( "\n" ); + newline = 0; + } + + printf( "%02X ", buffer[i] ); + + if( ( ( i + 1 ) % 16 ) == 0 ) + { + newline = 1; + } + } + printf( "\n" ); +} + +void DisplayNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ) +{ + if( state == LORAMAC_HANDLER_NVM_STORE ) + { + printf( "\n###### ============ CTXS STORED ============ ######\n" ); + + } + else + { + printf( "\n###### =========== CTXS RESTORED =========== ######\n" ); + } + printf( "Size : %i\n\n", size ); +} + +void DisplayNetworkParametersUpdate( CommissioningParams_t *commissioningParams ) +{ + printf( "DevEui : %02X", commissioningParams->DevEui[0] ); + for( int i = 1; i < 8; i++ ) + { + printf( "-%02X", commissioningParams->DevEui[i] ); + } + printf( "\n" ); + printf( "JoinEui : %02X", commissioningParams->JoinEui[0] ); + for( int i = 1; i < 8; i++ ) + { + printf( "-%02X", commissioningParams->JoinEui[i] ); + } + printf( "\n" ); + printf( "Pin : %02X", commissioningParams->SePin[0] ); + for( int i = 1; i < 4; i++ ) + { + printf( "-%02X", commissioningParams->SePin[i] ); + } + printf( "\n\n" ); +} + +void DisplayMacMcpsRequestUpdate( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ) +{ + switch( mcpsReq->Type ) + { + case MCPS_CONFIRMED: + { + printf( "\n###### =========== MCPS-Request ============ ######\n" ); + printf( "###### MCPS_CONFIRMED ######\n"); + printf( "###### ===================================== ######\n"); + break; + } + case MCPS_UNCONFIRMED: + { + printf( "\n###### =========== MCPS-Request ============ ######\n" ); + printf( "###### MCPS_UNCONFIRMED ######\n"); + printf( "###### ===================================== ######\n"); + break; + } + case MCPS_PROPRIETARY: + { + printf( "\n###### =========== MCPS-Request ============ ######\n" ); + printf( "###### MCPS_PROPRIETARY ######\n"); + printf( "###### ===================================== ######\n"); + break; + } + default: + { + printf( "\n###### =========== MCPS-Request ============ ######\n" ); + printf( "###### MCPS_ERROR ######\n"); + printf( "###### ===================================== ######\n"); + break; + } + } + printf( "STATUS : %s\n", MacStatusStrings[status] ); + if( status == LORAMAC_STATUS_DUTYCYCLE_RESTRICTED ) + { + printf( "Next Tx in : %u [ms]\n", nextTxIn ); + } +} + +void DisplayMacMlmeRequestUpdate( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ) +{ + switch( mlmeReq->Type ) + { + case MLME_JOIN: + { + printf( "\n###### =========== MLME-Request ============ ######\n" ); + printf( "###### MLME_JOIN ######\n"); + printf( "###### ===================================== ######\n"); + break; + } + case MLME_LINK_CHECK: + { + printf( "\n###### =========== MLME-Request ============ ######\n" ); + printf( "###### MLME_LINK_CHECK ######\n"); + printf( "###### ===================================== ######\n"); + break; + } + case MLME_DEVICE_TIME: + { + printf( "\n###### =========== MLME-Request ============ ######\n" ); + printf( "###### MLME_DEVICE_TIME ######\n"); + printf( "###### ===================================== ######\n"); + break; + } + case MLME_TXCW: + { + printf( "\n###### =========== MLME-Request ============ ######\n" ); + printf( "###### MLME_TXCW ######\n"); + printf( "###### ===================================== ######\n"); + break; + } + default: + { + printf( "\n###### =========== MLME-Request ============ ######\n" ); + printf( "###### MLME_UNKNOWN ######\n"); + printf( "###### ===================================== ######\n"); + break; + } + } + printf( "STATUS : %s\n", MacStatusStrings[status] ); + if( status == LORAMAC_STATUS_DUTYCYCLE_RESTRICTED ) + { + printf( "Next Tx in : %u [ms]\n", nextTxIn ); + } +} + +void DisplayJoinRequestUpdate( LmHandlerJoinParams_t *params ) +{ + if( params->CommissioningParams->IsOtaaActivation == true ) + { + if( params->Status == LORAMAC_HANDLER_SUCCESS ) + { + printf( "###### =========== JOINED ============ ######\n" ); + printf( "\nOTAA\n\n" ); + printf( "DevAddr : %08X\n", params->CommissioningParams->DevAddr ); + printf( "\n\n" ); + printf( "DATA RATE : DR_%d\n\n", params->Datarate ); + } + } +#if ( OVER_THE_AIR_ACTIVATION == 0 ) + else + { + printf( "###### =========== JOINED ============ ######\n" ); + printf( "\nABP\n\n" ); + printf( "DevAddr : %08X\n", params->CommissioningParams->DevAddr ); + printf( "\n\n" ); + } +#endif +} + +void DisplayTxUpdate( LmHandlerTxParams_t *params ) +{ + MibRequestConfirm_t mibGet; + + if( params->IsMcpsConfirm == 0 ) + { + printf( "\n###### =========== MLME-Confirm ============ ######\n" ); + printf( "STATUS : %s\n", EventInfoStatusStrings[params->Status] ); + return; + } + + printf( "\n###### =========== MCPS-Confirm ============ ######\n" ); + printf( "STATUS : %s\n", EventInfoStatusStrings[params->Status] ); + + printf( "\n###### ===== UPLINK FRAME %8u ===== ######\n", params->UplinkCounter ); + printf( "\n" ); + + printf( "CLASS : %c\n", "ABC"[LmHandlerGetCurrentClass( )] ); + printf( "\n" ); + printf( "TX PORT : %d\n", params->AppData.Port ); + + if( params->AppData.BufferSize != 0 ) + { + printf( "TX DATA : " ); + if( params->MsgType == LORAMAC_HANDLER_CONFIRMED_MSG ) + { + printf( "CONFIRMED - %s\n", ( params->AckReceived != 0 ) ? "ACK" : "NACK" ); + } + else + { + printf( "UNCONFIRMED\n" ); + } + PrintHexBuffer( params->AppData.Buffer, params->AppData.BufferSize ); + } + + printf( "\n" ); + printf( "DATA RATE : DR_%d\n", params->Datarate ); + + mibGet.Type = MIB_CHANNELS; + if( LoRaMacMibGetRequestConfirm( &mibGet ) == LORAMAC_STATUS_OK ) + { + printf( "U/L FREQ : %u\n", mibGet.Param.ChannelList[params->Channel].Frequency ); + } + + printf( "TX POWER : %d\n", params->TxPower ); + + mibGet.Type = MIB_CHANNELS_MASK; + if( LoRaMacMibGetRequestConfirm( &mibGet ) == LORAMAC_STATUS_OK ) + { + printf("CHANNEL MASK: "); + switch( LmHandlerGetActiveRegion( ) ) + { + case LORAMAC_REGION_AS923: + case LORAMAC_REGION_CN779: + case LORAMAC_REGION_EU868: + case LORAMAC_REGION_IN865: + case LORAMAC_REGION_KR920: + case LORAMAC_REGION_EU433: + case LORAMAC_REGION_RU864: + { + printf( "%04X ", mibGet.Param.ChannelsMask[0] ); + break; + } + case LORAMAC_REGION_AU915: + case LORAMAC_REGION_CN470: + case LORAMAC_REGION_US915: + { + for( uint8_t i = 0; i < 5; i++) + { + printf( "%04X ", mibGet.Param.ChannelsMask[i] ); + } + break; + } + default: + { + printf( "\n###### ========= Unknown Region ============ ######" ); + break; + } + } + printf("\n"); + } + + printf( "\n" ); +} + +void DisplayRxUpdate( LmHandlerAppData_t *appData, LmHandlerRxParams_t *params ) +{ + const char *slotStrings[] = { "1", "2", "C", "C Multicast", "B Ping-Slot", "B Multicast Ping-Slot" }; + + if( params->IsMcpsIndication == 0 ) + { + printf( "\n###### ========== MLME-Indication ========== ######\n" ); + printf( "STATUS : %s\n", EventInfoStatusStrings[params->Status] ); + return; + } + + printf( "\n###### ========== MCPS-Indication ========== ######\n" ); + printf( "STATUS : %s\n", EventInfoStatusStrings[params->Status] ); + + printf( "\n###### ===== DOWNLINK FRAME %8u ===== ######\n", params->DownlinkCounter ); + + printf( "RX WINDOW : %s\n", slotStrings[params->RxSlot] ); + + printf( "RX PORT : %d\n", appData->Port ); + + if( appData->BufferSize != 0 ) + { + printf( "RX DATA : \n" ); + PrintHexBuffer( appData->Buffer, appData->BufferSize ); + } + + printf( "\n" ); + printf( "DATA RATE : DR_%d\n", params->Datarate ); + printf( "RX RSSI : %d\n", params->Rssi ); + printf( "RX SNR : %d\n", params->Snr ); + + printf( "\n" ); +} + +void DisplayBeaconUpdate( LoRaMacHandlerBeaconParams_t *params ) +{ + switch( params->State ) + { + default: + case LORAMAC_HANDLER_BEACON_ACQUIRING: + { + printf( "\n###### ========= BEACON ACQUIRING ========== ######\n" ); + break; + } + case LORAMAC_HANDLER_BEACON_LOST: + { + printf( "\n###### ============ BEACON LOST ============ ######\n" ); + break; + } + case LORAMAC_HANDLER_BEACON_RX: + { + printf( "\n###### ===== BEACON %8u ==== ######\n", params->Info.Time.Seconds ); + printf( "GW DESC : %d\n", params->Info.GwSpecific.InfoDesc ); + printf( "GW INFO : " ); + PrintHexBuffer( params->Info.GwSpecific.Info, 6 ); + printf( "\n" ); + printf( "FREQ : %u\n", params->Info.Frequency ); + printf( "DATA RATE : DR_%d\n", params->Info.Datarate ); + printf( "RX RSSI : %d\n", params->Info.Rssi ); + printf( "RX SNR : %d\n", params->Info.Snr ); + printf( "\n" ); + break; + } + case LORAMAC_HANDLER_BEACON_NRX: + { + printf( "\n###### ======== BEACON NOT RECEIVED ======== ######\n" ); + break; + } + } +} + +void DisplayClassUpdate( DeviceClass_t deviceClass ) +{ + printf( "\n\n###### ===== Switch to Class %c done. ===== ######\n\n", "ABC"[deviceClass] ); +} + +void DisplayAppInfo( const char* appName, const Version_t* appVersion, const Version_t* gitHubVersion ) +{ + printf( "\n###### ===================================== ######\n\n" ); + printf( "Application name : %s\n", appName ); + printf( "Application version: %d.%d.%d\n", appVersion->Fields.Major, appVersion->Fields.Minor, appVersion->Fields.Patch ); + printf( "GitHub base version: %d.%d.%d\n", gitHubVersion->Fields.Major, gitHubVersion->Fields.Minor, gitHubVersion->Fields.Patch ); + printf( "\n###### ===================================== ######\n\n" ); +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandlerMsgDisplay.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandlerMsgDisplay.h new file mode 100644 index 0000000000..ba3d2491b5 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/LmHandlerMsgDisplay.h @@ -0,0 +1,112 @@ +/*! + * \file LmHandlerMsgDisplay.h + * + * \brief Common set of functions to display default messages from + * LoRaMacHandler. + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2019 Semtech + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + */ +#ifndef __LMHANDLER_MSG_DISPLAY_H__ +#define __LMHANDLER_MSG_DISPLAY_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "utilities.h" +#include "LmHandler.h" + +/*! + * \brief Displays NVM context operation state + * + * \param [IN] state Indicates if we are storing (true) or + * restoring (false) the NVM context + * + * \param [IN] size Number of data bytes which were stored or restored. + */ +void DisplayNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ); + +/*! + * \brief Displays updated network parameters + * + * \param [IN] commissioningParams Commissioning provided parameters + */ +void DisplayNetworkParametersUpdate( CommissioningParams_t* commissioningParams ); + +/*! + * \brief Displays updated McpsRequest + * + * \param [IN] status McpsRequest execution status + * \param [IN] mcpsReq McpsRequest command executed + * \param [IN] nextTxIn Time to wait for the next uplink transmission + */ +void DisplayMacMcpsRequestUpdate( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ); + +/*! + * \brief Displays updated MlmeRequest + * + * \param [IN] status MlmeRequest execution status + * \param [IN] mlmeReq MlmeRequest command executed + * \param [IN] nextTxIn Time to wait for the next uplink transmission + */ +void DisplayMacMlmeRequestUpdate( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ); + +/*! + * \brief Displays updated JoinRequest + * + * \param [IN] params Executed JoinRequest parameters + */ +void DisplayJoinRequestUpdate( LmHandlerJoinParams_t* params ); + +/*! + * \brief Displays Tx params + * + * \param [IN] params Tx parameters + */ +void DisplayTxUpdate( LmHandlerTxParams_t* params ); + +/*! + * \brief Displays Rx params + * + * \param [IN] appData Receive data payload and port number + * \param [IN] params Rx parameters + */ +void DisplayRxUpdate( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ); + +/*! + * \brief Displays beacon status update + * + * \param [IN] params Beacon parameters + */ +void DisplayBeaconUpdate( LoRaMacHandlerBeaconParams_t* params ); + +/*! + * \brief Displays end-device class update + * + * \param [IN] deviceClass Current end-device class + */ +void DisplayClassUpdate( DeviceClass_t deviceClass ); + +/*! + * \brief Displays application information + */ +void DisplayAppInfo( const char* appName, const Version_t* appVersion, const Version_t* gitHubVersion ); + +#ifdef __cplusplus +} +#endif + +#endif // __LMHANDLER_MSG_DISPLAY_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/common/githubVersion.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/githubVersion.h new file mode 100644 index 0000000000..c995d555ba --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/common/githubVersion.h @@ -0,0 +1,33 @@ +/*! + * \file githubVersion.h + * + * \brief GitHub version definition + * + * \copyright Revised BSD License, see file LICENSE.txt + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2019-2020 Semtech + * + * \endcode + */ + +#ifndef __GITHUB_VERSION_H__ +#define __GITHUB_VERSION_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#define GITHUB_VERSION 0x04070000 // 4.7.0.0 + +#ifdef __cplusplus +} +#endif + +#endif // __GITHUB_VERSION_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/delay.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/delay.c new file mode 100644 index 0000000000..7a3edd7c7c --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/delay.c @@ -0,0 +1,58 @@ +/*! + * \file delay.c + * + * \brief Delay implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * \endcode + * \author Alistair Jordan + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + */ +#include +#include "delay.h" +#include + +/* msleep(): Sleep for the requested number of milliseconds. */ +int msleep(long msec) +{ + struct timespec ts; + int res; + + if (msec < 0) + { + errno = EINVAL; + return -1; + } + + ts.tv_sec = msec / 1000; + ts.tv_nsec = (msec % 1000) * 1000000; + + do { + res = nanosleep(&ts, &ts); + } while (res && errno == EINTR); + + return res; +} + +void Delay( float s ) +{ + DelayMs( s * 1000.0f ); +} + +void DelayMs( uint32_t ms ) +{ + msleep( ms ); +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/delay.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/delay.h new file mode 100644 index 0000000000..c5d8ec6441 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/delay.h @@ -0,0 +1,48 @@ +/*! + * \file delay.h + * + * \brief Delay implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + */ +#ifndef __DELAY_H__ +#define __DELAY_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +/*! + * Blocking delay of "s" seconds + */ +void Delay( float s ); + +/*! + * Blocking delay of "ms" milliseconds + */ +void DelayMs( uint32_t ms ); + +#ifdef __cplusplus +} +#endif + +#endif // __DELAY_H__ + diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/gitHubVersion.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/gitHubVersion.h new file mode 100644 index 0000000000..a9c72659e1 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/gitHubVersion.h @@ -0,0 +1,38 @@ +/*! + * \file githubVersion.h + * + * \brief GitHub version definition + * + * \copyright Revised BSD License, see file LICENSE.txt + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2019-2020 Semtech + * + * \endcode + */ + +#ifndef __GITHUB_VERSION_H__ +#define __GITHUB_VERSION_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#define GITHUB_VERSION 0x04070000 // 4.7.0.0 + +#ifndef __FIRMWARE_VERSION_H__ +#define __FIRMWARE_VERSION_H__ +#define FIRMWARE_VERSION 0x01030000 // 1.3.0.0 +#endif + +#ifdef __cplusplus +} +#endif + +#endif // __GITHUB_VERSION_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/main.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/main.c new file mode 100644 index 0000000000..aa11acfe41 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/main.c @@ -0,0 +1,532 @@ +/*! + * \file main.c + * + * \brief Performs a periodic uplink + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2018 Semtech + * + * \endcode + * + * \author Alistair Jordan + * + * \author Miguel Luis ( Semtech ) + */ + +/*! \file periodic-uplink/B-L072Z-LRWAN1/main.c */ + +#include +#include "utilities.h" +#include "board.h" +#include "RegionCommon.h" + +#include "Commissioning.h" +#include "LmHandler.h" +#include "LmhpCompliance.h" +#include "CayenneLpp.h" +#include "LmHandlerMsgDisplay.h" + +#include "gitHubVersion.h" + +#ifndef ACTIVE_REGION + +#warning "No active region defined, LORAMAC_REGION_EU868 will be used as default." + +#define ACTIVE_REGION LORAMAC_REGION_EU868 + +#endif + +/*! + * LoRaWAN default end-device class + */ +#ifndef LORAWAN_DEFAULT_CLASS +#define LORAWAN_DEFAULT_CLASS CLASS_A +#endif + +/*! + * Defines the application data transmission duty cycle. 5s, value in [ms]. + */ +#define APP_TX_DUTYCYCLE 5000 + +/*! + * Defines a random delay for application data transmission duty cycle. 1s, + * value in [ms]. + */ +#define APP_TX_DUTYCYCLE_RND 1000 + +/*! + * LoRaWAN Adaptive Data Rate + * + * \remark Please note that when ADR is enabled the end-device should be static + */ +#define LORAWAN_ADR_STATE LORAMAC_HANDLER_ADR_ON + +/*! + * Default datarate + * + * \remark Please note that LORAWAN_DEFAULT_DATARATE is used only when ADR is disabled + */ +#define LORAWAN_DEFAULT_DATARATE DR_0 + +/*! + * LoRaWAN confirmed messages + */ +#define LORAWAN_DEFAULT_CONFIRMED_MSG_STATE LORAMAC_HANDLER_UNCONFIRMED_MSG + +/*! + * User application data buffer size + */ +#define LORAWAN_APP_DATA_BUFFER_MAX_SIZE 242 + +/*! + * LoRaWAN ETSI duty cycle control enable/disable + * + * \remark Please note that ETSI mandates duty cycled transmissions. Use only for test purposes + */ +#define LORAWAN_DUTYCYCLE_ON true + +/*! + * LoRaWAN application port + * @remark The allowed port range is from 1 up to 223. Other values are reserved. + */ +#define LORAWAN_APP_PORT 2 + +/*! + * + */ +typedef enum +{ + LORAMAC_HANDLER_TX_ON_TIMER, + LORAMAC_HANDLER_TX_ON_EVENT, +}LmHandlerTxEvents_t; + +/*! + * User application data + */ +static uint8_t AppDataBuffer[LORAWAN_APP_DATA_BUFFER_MAX_SIZE]; + +/*! + * User application data structure + */ +static LmHandlerAppData_t AppData = +{ + .Buffer = AppDataBuffer, + .BufferSize = 0, + .Port = 0, +}; + +/*! + * Specifies the state of the application LED + */ +static bool AppLedStateOn = false; + +/*! + * Timer to handle the application data transmission duty cycle + */ +static TimerEvent_t TxTimer; + +/*! + * Timer to handle the state of LED1 + */ +static TimerEvent_t Led1Timer; + +/*! + * Timer to handle the state of LED2 + */ +static TimerEvent_t Led2Timer; + +/*! + * Timer to handle the state of LED3 + */ +static TimerEvent_t Led3Timer; + +/*! + * Timer to handle the state of LED beacon indicator + */ +static TimerEvent_t LedBeaconTimer; + +static void OnMacProcessNotify( void ); +static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ); +static void OnNetworkParametersChange( CommissioningParams_t* params ); +static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ); +static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ); +static void OnJoinRequest( LmHandlerJoinParams_t* params ); +static void OnTxData( LmHandlerTxParams_t* params ); +static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ); +static void OnClassChange( DeviceClass_t deviceClass ); +static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ); +#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) +static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ); +#else +static void OnSysTimeUpdate( void ); +#endif +static void PrepareTxFrame( void ); +static void StartTxProcess( LmHandlerTxEvents_t txEvent ); +static void UplinkProcess( void ); + +static void OnTxPeriodicityChanged( uint32_t periodicity ); +static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ); +static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ); + +/*! + * Function executed on TxTimer event + */ +static void OnTxTimerEvent( void* context ); + +/*! + * Function executed on Led 1 Timeout event + */ +static void OnLed1TimerEvent( void* context ); + +/*! + * Function executed on Led 2 Timeout event + */ +static void OnLed2TimerEvent( void* context ); + +/*! + * \brief Function executed on Led 3 Timeout event + */ +static void OnLed3TimerEvent( void* context ); + +/*! + * \brief Function executed on Beacon timer Timeout event + */ +static void OnLedBeaconTimerEvent( void* context ); + +static LmHandlerCallbacks_t LmHandlerCallbacks = +{ + .GetBatteryLevel = BoardGetBatteryLevel, + .GetTemperature = NULL, + .GetRandomSeed = BoardGetRandomSeed, + .OnMacProcess = OnMacProcessNotify, + .OnNvmDataChange = OnNvmDataChange, + .OnNetworkParametersChange = OnNetworkParametersChange, + .OnMacMcpsRequest = OnMacMcpsRequest, + .OnMacMlmeRequest = OnMacMlmeRequest, + .OnJoinRequest = OnJoinRequest, + .OnTxData = OnTxData, + .OnRxData = OnRxData, + .OnClassChange= OnClassChange, + .OnBeaconStatusChange = OnBeaconStatusChange, + .OnSysTimeUpdate = OnSysTimeUpdate, +}; + +static LmHandlerParams_t LmHandlerParams = +{ + .Region = ACTIVE_REGION, + .AdrEnable = LORAWAN_ADR_STATE, + .IsTxConfirmed = LORAWAN_DEFAULT_CONFIRMED_MSG_STATE, + .TxDatarate = LORAWAN_DEFAULT_DATARATE, + .PublicNetworkEnable = LORAWAN_PUBLIC_NETWORK, + .DutyCycleEnabled = LORAWAN_DUTYCYCLE_ON, + .DataBufferMaxSize = LORAWAN_APP_DATA_BUFFER_MAX_SIZE, + .DataBuffer = AppDataBuffer, + .PingSlotPeriodicity = REGION_COMMON_DEFAULT_PING_SLOT_PERIODICITY, +}; + +static LmhpComplianceParams_t LmhpComplianceParams = +{ + .FwVersion.Value = FIRMWARE_VERSION, + .OnTxPeriodicityChanged = OnTxPeriodicityChanged, + .OnTxFrameCtrlChanged = OnTxFrameCtrlChanged, + .OnPingSlotPeriodicityChanged = OnPingSlotPeriodicityChanged, +}; + +/*! + * Indicates if LoRaMacProcess call is pending. + * + * \warning If variable is equal to 0 then the MCU can be set in low power mode + */ +static volatile uint8_t IsMacProcessPending = 0; + +static volatile uint8_t IsTxFramePending = 0; + +static volatile uint32_t TxPeriodicity = 0; + +/*! + * Main application entry point. + */ +int main( void ) +{ + BoardInitMcu( ); + BoardInitPeriph( ); + + // Initialize transmission periodicity variable + TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); + + const Version_t appVersion = { .Value = FIRMWARE_VERSION }; + const Version_t gitHubVersion = { .Value = GITHUB_VERSION }; + DisplayAppInfo( "periodic-uplink-lpp", + &appVersion, + &gitHubVersion ); + + if ( LmHandlerInit( &LmHandlerCallbacks, &LmHandlerParams ) != LORAMAC_HANDLER_SUCCESS ) + { + printf( "LoRaMac wasn't properly initialized\n" ); + // Fatal error, endless loop. + while ( 1 ) + { + } + } + + // Set system maximum tolerated rx error in milliseconds + LmHandlerSetSystemMaxRxError( 20 ); + + // The LoRa-Alliance Compliance protocol package should always be + // initialized and activated. + LmHandlerPackageRegister( PACKAGE_ID_COMPLIANCE, &LmhpComplianceParams ); + + LmHandlerJoin( ); + + StartTxProcess( LORAMAC_HANDLER_TX_ON_TIMER ); + + // while( 1 ) + // { + // // Process characters sent over the command line interface + // CliProcess( &Uart2 ); + + // Processes the LoRaMac events + LmHandlerProcess( ); + + // Process application uplinks management + UplinkProcess( ); + + // CRITICAL_SECTION_BEGIN( ); + // if( IsMacProcessPending == 1 ) + // { + // // Clear flag and prevent MCU to go into low power modes. + // IsMacProcessPending = 0; + // } + // else + // { + // // The MCU wakes up through events + // BoardLowPowerHandler( ); + // } + // CRITICAL_SECTION_END( ); + //} +} + +static void OnMacProcessNotify( void ) +{ + IsMacProcessPending = 1; +} + +static void OnNvmDataChange( LmHandlerNvmContextStates_t state, uint16_t size ) +{ + DisplayNvmDataChange( state, size ); +} + +static void OnNetworkParametersChange( CommissioningParams_t* params ) +{ + DisplayNetworkParametersUpdate( params ); +} + +static void OnMacMcpsRequest( LoRaMacStatus_t status, McpsReq_t *mcpsReq, TimerTime_t nextTxIn ) +{ + DisplayMacMcpsRequestUpdate( status, mcpsReq, nextTxIn ); +} + +static void OnMacMlmeRequest( LoRaMacStatus_t status, MlmeReq_t *mlmeReq, TimerTime_t nextTxIn ) +{ + DisplayMacMlmeRequestUpdate( status, mlmeReq, nextTxIn ); +} + +static void OnJoinRequest( LmHandlerJoinParams_t* params ) +{ + DisplayJoinRequestUpdate( params ); + if( params->Status == LORAMAC_HANDLER_ERROR ) + { + LmHandlerJoin( ); + } + else + { + LmHandlerRequestClass( LORAWAN_DEFAULT_CLASS ); + } +} + +static void OnTxData( LmHandlerTxParams_t* params ) +{ + DisplayTxUpdate( params ); +} + +static void OnRxData( LmHandlerAppData_t* appData, LmHandlerRxParams_t* params ) +{ + DisplayRxUpdate( appData, params ); + + // switch( appData->Port ) + // { + // case 1: // The application LED can be controlled on port 1 or 2 + // case LORAWAN_APP_PORT: + // { + // AppLedStateOn = appData->Buffer[0] & 0x01; + // GpioWrite( &Led4, ( ( AppLedStateOn & 0x01 ) != 0 ) ? 1 : 0 ); + // } + // break; + // default: + // break; + // } + + // // Switch LED 2 ON for each received downlink + // GpioWrite( &Led3, 1 ); + // TimerStart( &Led3Timer ); +} + +static void OnClassChange( DeviceClass_t deviceClass ) +{ + DisplayClassUpdate( deviceClass ); + + // Inform the server as soon as possible that the end-device has switched to ClassB + LmHandlerAppData_t appData = + { + .Buffer = NULL, + .BufferSize = 0, + .Port = 0, + }; + LmHandlerSend( &appData, LORAMAC_HANDLER_UNCONFIRMED_MSG ); +} + +static void OnBeaconStatusChange( LoRaMacHandlerBeaconParams_t* params ) +{ + switch( params->State ) + { + case LORAMAC_HANDLER_BEACON_RX: + { + TimerStart( &LedBeaconTimer ); + break; + } + case LORAMAC_HANDLER_BEACON_LOST: + case LORAMAC_HANDLER_BEACON_NRX: + { + TimerStop( &LedBeaconTimer ); + break; + } + default: + { + break; + } + } + + DisplayBeaconUpdate( params ); +} + +#if( LMH_SYS_TIME_UPDATE_NEW_API == 1 ) +static void OnSysTimeUpdate( bool isSynchronized, int32_t timeCorrection ) +{ + +} +#else +static void OnSysTimeUpdate( void ) +{ + +} +#endif + +/*! + * Prepares the payload of the frame and transmits it. + */ +static void PrepareTxFrame( void ) +{ + if( LmHandlerIsBusy( ) == true ) + { + return; + } + + uint8_t channel = 0; + + AppData.Port = LORAWAN_APP_PORT; + + CayenneLppReset( ); + CayenneLppAddDigitalInput( channel++, AppLedStateOn ); + CayenneLppAddAnalogInput( channel++, BoardGetBatteryLevel( ) * 100 / 254 ); + + CayenneLppCopy( AppData.Buffer ); + AppData.BufferSize = CayenneLppGetSize( ); + + if( LmHandlerSend( &AppData, LmHandlerParams.IsTxConfirmed ) == LORAMAC_HANDLER_SUCCESS ) + { + // Switch LED 1 ON +// GpioWrite( &Led1, 1 ); +// TimerStart( &Led1Timer ); + } +} + +static void StartTxProcess( LmHandlerTxEvents_t txEvent ) +{ + switch( txEvent ) + { + default: + // Intentional fall through + case LORAMAC_HANDLER_TX_ON_TIMER: + { + // Schedule 1st packet transmission + TimerInit( &TxTimer, OnTxTimerEvent ); + TimerSetValue( &TxTimer, TxPeriodicity ); + OnTxTimerEvent( NULL ); + } + break; + case LORAMAC_HANDLER_TX_ON_EVENT: + { + } + break; + } +} + +static void UplinkProcess( void ) +{ + uint8_t isPending = 0; + CRITICAL_SECTION_BEGIN( ); + isPending = IsTxFramePending; + IsTxFramePending = 0; + CRITICAL_SECTION_END( ); + if( isPending == 1 ) + { + PrepareTxFrame( ); + } +} + +static void OnTxPeriodicityChanged( uint32_t periodicity ) +{ + TxPeriodicity = periodicity; + + if( TxPeriodicity == 0 ) + { // Revert to application default periodicity + TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); + } + + // Update timer periodicity + TimerStop( &TxTimer ); + TimerSetValue( &TxTimer, TxPeriodicity ); + TimerStart( &TxTimer ); +} + +static void OnTxFrameCtrlChanged( LmHandlerMsgTypes_t isTxConfirmed ) +{ + LmHandlerParams.IsTxConfirmed = isTxConfirmed; +} + +static void OnPingSlotPeriodicityChanged( uint8_t pingSlotPeriodicity ) +{ + LmHandlerParams.PingSlotPeriodicity = pingSlotPeriodicity; +} + +/*! + * Function executed on TxTimer event + */ +static void OnTxTimerEvent( void* context ) +{ + TimerStop( &TxTimer ); + + IsTxFramePending = 1; + + // Schedule next transmission + TimerSetValue( &TxTimer, TxPeriodicity ); + TimerStart( &TxTimer ); +} \ No newline at end of file diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/radio.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/radio.h new file mode 100644 index 0000000000..91d6eed09f --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/radio.h @@ -0,0 +1,419 @@ +/*! + * \file radio.h + * + * \brief Radio driver API definition + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * \endcode + * + * \author Alistair Jordan + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + */ +#ifndef __RADIO_H__ +#define __RADIO_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include + +/*! + * Radio driver supported modems + */ +typedef enum +{ + MODEM_FSK = 0, + MODEM_LORA, +}RadioModems_t; + +/*! + * Radio driver internal state machine states definition + */ +typedef enum +{ + RF_IDLE = 0, //!< The radio is idle + RF_RX_RUNNING, //!< The radio is in reception state + RF_TX_RUNNING, //!< The radio is in transmission state + RF_CAD, //!< The radio is doing channel activity detection +}RadioState_t; + +/*! + * \brief Radio driver callback functions + */ +typedef struct +{ + /*! + * \brief Tx Done callback prototype. + */ + void ( *TxDone )( void ); + /*! + * \brief Tx Timeout callback prototype. + */ + void ( *TxTimeout )( void ); + /*! + * \brief Rx Done callback prototype. + * + * \param [IN] payload Received buffer pointer + * \param [IN] size Received buffer size + * \param [IN] rssi RSSI value computed while receiving the frame [dBm] + * \param [IN] snr SNR value computed while receiving the frame [dB] + * FSK : N/A ( set to 0 ) + * LoRa: SNR value in dB + */ + void ( *RxDone )( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ); + /*! + * \brief Rx Timeout callback prototype. + */ + void ( *RxTimeout )( void ); + /*! + * \brief Rx Error callback prototype. + */ + void ( *RxError )( void ); + /*! + * \brief FHSS Change Channel callback prototype. + * + * \param [IN] currentChannel Index number of the current channel + */ + void ( *FhssChangeChannel )( uint8_t currentChannel ); + + /*! + * \brief CAD Done callback prototype. + * + * \param [IN] channelDetected Channel Activity detected during the CAD + */ + void ( *CadDone ) ( bool channelActivityDetected ); + + /*! + * \brief Gnss Done Done callback prototype. + */ + void ( *GnssDone )( void ); + + /*! + * \brief Gnss Done Done callback prototype. + */ + void ( *WifiDone )( void ); +}RadioEvents_t; + +/*! + * \brief Radio driver definition + */ +struct Radio_s +{ + /*! + * \brief Initializes the radio + * + * \param [IN] events Structure containing the driver callback functions + */ + void ( *Init )( RadioEvents_t *events ); + /*! + * Return current radio status + * + * \param status Radio status.[RF_IDLE, RF_RX_RUNNING, RF_TX_RUNNING] + */ + RadioState_t ( *GetStatus )( void ); + /*! + * \brief Configures the radio with the given modem + * + * \param [IN] modem Modem to be used [0: FSK, 1: LoRa] + */ + void ( *SetModem )( RadioModems_t modem ); + /*! + * \brief Sets the channel frequency + * + * \param [IN] freq Channel RF frequency + */ + void ( *SetChannel )( uint32_t freq ); + /*! + * \brief Checks if the channel is free for the given time + * + * \remark The FSK modem is always used for this task as we can select the Rx bandwidth at will. + * + * \param [IN] freq Channel RF frequency in Hertz + * \param [IN] rxBandwidth Rx bandwidth in Hertz + * \param [IN] rssiThresh RSSI threshold in dBm + * \param [IN] maxCarrierSenseTime Max time in milliseconds while the RSSI is measured + * + * \retval isFree [true: Channel is free, false: Channel is not free] + */ + bool ( *IsChannelFree )( uint32_t freq, uint32_t rxBandwidth, int16_t rssiThresh, uint32_t maxCarrierSenseTime ); + /*! + * \brief Generates a 32 bits random value based on the RSSI readings + * + * \remark This function sets the radio in LoRa modem mode and disables + * all interrupts. + * After calling this function either Radio.SetRxConfig or + * Radio.SetTxConfig functions must be called. + * + * \retval randomValue 32 bits random value + */ + uint32_t ( *Random )( void ); + /*! + * \brief Sets the reception parameters + * + * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * \param [IN] bandwidth Sets the bandwidth + * FSK : >= 2600 and <= 250000 Hz + * LoRa: [0: 125 kHz, 1: 250 kHz, + * 2: 500 kHz, 3: Reserved] + * \param [IN] datarate Sets the Datarate + * FSK : 600..300000 bits/s + * LoRa: [6: 64, 7: 128, 8: 256, 9: 512, + * 10: 1024, 11: 2048, 12: 4096 chips] + * \param [IN] coderate Sets the coding rate (LoRa only) + * FSK : N/A ( set to 0 ) + * LoRa: [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] + * \param [IN] bandwidthAfc Sets the AFC Bandwidth (FSK only) + * FSK : >= 2600 and <= 250000 Hz + * LoRa: N/A ( set to 0 ) + * \param [IN] preambleLen Sets the Preamble length + * FSK : Number of bytes + * LoRa: Length in symbols (the hardware adds 4 more symbols) + * \param [IN] symbTimeout Sets the RxSingle timeout value + * FSK : timeout in number of bytes + * LoRa: timeout in symbols + * \param [IN] fixLen Fixed length packets [0: variable, 1: fixed] + * \param [IN] payloadLen Sets payload length when fixed length is used + * \param [IN] crcOn Enables/Disables the CRC [0: OFF, 1: ON] + * \param [IN] freqHopOn Enables disables the intra-packet frequency hopping + * FSK : N/A ( set to 0 ) + * LoRa: [0: OFF, 1: ON] + * \param [IN] hopPeriod Number of symbols between each hop + * FSK : N/A ( set to 0 ) + * LoRa: Number of symbols + * \param [IN] iqInverted Inverts IQ signals (LoRa only) + * FSK : N/A ( set to 0 ) + * LoRa: [0: not inverted, 1: inverted] + * \param [IN] rxContinuous Sets the reception in continuous mode + * [false: single mode, true: continuous mode] + */ + void ( *SetRxConfig )( RadioModems_t modem, uint32_t bandwidth, + uint32_t datarate, uint8_t coderate, + uint32_t bandwidthAfc, uint16_t preambleLen, + uint16_t symbTimeout, bool fixLen, + uint8_t payloadLen, + bool crcOn, bool freqHopOn, uint8_t hopPeriod, + bool iqInverted, bool rxContinuous ); + /*! + * \brief Sets the transmission parameters + * + * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * \param [IN] power Sets the output power [dBm] + * \param [IN] fdev Sets the frequency deviation (FSK only) + * FSK : [Hz] + * LoRa: 0 + * \param [IN] bandwidth Sets the bandwidth (LoRa only) + * FSK : 0 + * LoRa: [0: 125 kHz, 1: 250 kHz, + * 2: 500 kHz, 3: Reserved] + * \param [IN] datarate Sets the Datarate + * FSK : 600..300000 bits/s + * LoRa: [6: 64, 7: 128, 8: 256, 9: 512, + * 10: 1024, 11: 2048, 12: 4096 chips] + * \param [IN] coderate Sets the coding rate (LoRa only) + * FSK : N/A ( set to 0 ) + * LoRa: [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] + * \param [IN] preambleLen Sets the preamble length + * FSK : Number of bytes + * LoRa: Length in symbols (the hardware adds 4 more symbols) + * \param [IN] fixLen Fixed length packets [0: variable, 1: fixed] + * \param [IN] crcOn Enables disables the CRC [0: OFF, 1: ON] + * \param [IN] freqHopOn Enables disables the intra-packet frequency hopping + * FSK : N/A ( set to 0 ) + * LoRa: [0: OFF, 1: ON] + * \param [IN] hopPeriod Number of symbols between each hop + * FSK : N/A ( set to 0 ) + * LoRa: Number of symbols + * \param [IN] iqInverted Inverts IQ signals (LoRa only) + * FSK : N/A ( set to 0 ) + * LoRa: [0: not inverted, 1: inverted] + * \param [IN] timeout Transmission timeout [ms] + */ + void ( *SetTxConfig )( RadioModems_t modem, int8_t power, uint32_t fdev, + uint32_t bandwidth, uint32_t datarate, + uint8_t coderate, uint16_t preambleLen, + bool fixLen, bool crcOn, bool freqHopOn, + uint8_t hopPeriod, bool iqInverted, uint32_t timeout ); + /*! + * \brief Checks if the given RF frequency is supported by the hardware + * + * \param [IN] frequency RF frequency to be checked + * \retval isSupported [true: supported, false: unsupported] + */ + bool ( *CheckRfFrequency )( uint32_t frequency ); + /*! + * \brief Computes the packet time on air in ms for the given payload + * + * \Remark Can only be called once SetRxConfig or SetTxConfig have been called + * + * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * \param [IN] bandwidth Sets the bandwidth + * FSK : >= 2600 and <= 250000 Hz + * LoRa: [0: 125 kHz, 1: 250 kHz, + * 2: 500 kHz, 3: Reserved] + * \param [IN] datarate Sets the Datarate + * FSK : 600..300000 bits/s + * LoRa: [6: 64, 7: 128, 8: 256, 9: 512, + * 10: 1024, 11: 2048, 12: 4096 chips] + * \param [IN] coderate Sets the coding rate (LoRa only) + * FSK : N/A ( set to 0 ) + * LoRa: [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] + * \param [IN] preambleLen Sets the Preamble length + * FSK : Number of bytes + * LoRa: Length in symbols (the hardware adds 4 more symbols) + * \param [IN] fixLen Fixed length packets [0: variable, 1: fixed] + * \param [IN] payloadLen Sets payload length when fixed length is used + * \param [IN] crcOn Enables/Disables the CRC [0: OFF, 1: ON] + * + * \retval airTime Computed airTime (ms) for the given packet payload length + */ + uint32_t ( *TimeOnAir )( RadioModems_t modem, uint32_t bandwidth, + uint32_t datarate, uint8_t coderate, + uint16_t preambleLen, bool fixLen, uint8_t payloadLen, + bool crcOn ); + /*! + * \brief Sends the buffer of size. Prepares the packet to be sent and sets + * the radio in transmission + * + * \param [IN]: buffer Buffer pointer + * \param [IN]: size Buffer size + */ + void ( *Send )( uint8_t *buffer, uint8_t size ); + /*! + * \brief Sets the radio in sleep mode + */ + void ( *Sleep )( void ); + /*! + * \brief Sets the radio in standby mode + */ + void ( *Standby )( void ); + /*! + * \brief Sets the radio in reception mode for the given time + * \param [IN] timeout Reception timeout [ms] + * [0: continuous, others timeout] + */ + void ( *Rx )( uint32_t timeout ); + /*! + * \brief Start a Channel Activity Detection + */ + void ( *StartCad )( void ); + /*! + * \brief Sets the radio in continuous wave transmission mode + * + * \param [IN]: freq Channel RF frequency + * \param [IN]: power Sets the output power [dBm] + * \param [IN]: time Transmission mode timeout [s] + */ + void ( *SetTxContinuousWave )( uint32_t freq, int8_t power, uint16_t time ); + /*! + * \brief Reads the current RSSI value + * + * \retval rssiValue Current RSSI value in [dBm] + */ + int16_t ( *Rssi )( RadioModems_t modem ); + /*! + * \brief Writes the radio register at the specified address + * + * \param [IN]: addr Register address + * \param [IN]: data New register value + */ + void ( *Write )( uint32_t addr, uint8_t data ); + /*! + * \brief Reads the radio register at the specified address + * + * \param [IN]: addr Register address + * \retval data Register value + */ + uint8_t ( *Read )( uint32_t addr ); + /*! + * \brief Writes multiple radio registers starting at address + * + * \param [IN] addr First Radio register address + * \param [IN] buffer Buffer containing the new register's values + * \param [IN] size Number of registers to be written + */ + void ( *WriteBuffer )( uint32_t addr, uint8_t *buffer, uint8_t size ); + /*! + * \brief Reads multiple radio registers starting at address + * + * \param [IN] addr First Radio register address + * \param [OUT] buffer Buffer where to copy the registers data + * \param [IN] size Number of registers to be read + */ + void ( *ReadBuffer )( uint32_t addr, uint8_t *buffer, uint8_t size ); + /*! + * \brief Sets the maximum payload length. + * + * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * \param [IN] max Maximum payload length in bytes + */ + void ( *SetMaxPayloadLength )( RadioModems_t modem, uint8_t max ); + /*! + * \brief Sets the network to public or private. Updates the sync byte. + * + * \remark Applies to LoRa modem only + * + * \param [IN] enable if true, it enables a public network + */ + void ( *SetPublicNetwork )( bool enable ); + /*! + * \brief Gets the time required for the board plus radio to get out of sleep.[ms] + * + * \retval time Radio plus board wakeup time in ms. + */ + uint32_t ( *GetWakeupTime )( void ); + /*! + * \brief Process radio irq + */ + void ( *IrqProcess )( void ); + /* + * The next functions are available only on SX126x radios. + */ + /*! + * \brief Sets the radio in reception mode with Max LNA gain for the given time + * + * \remark Available on SX126x radios only. + * + * \param [IN] timeout Reception timeout [ms] + * [0: continuous, others timeout] + */ + void ( *RxBoosted )( uint32_t timeout ); + /*! + * \brief Sets the Rx duty cycle management parameters + * + * \remark Available on SX126x radios only. + * + * \param [in] rxTime Structure describing reception timeout value + * \param [in] sleepTime Structure describing sleep timeout value + */ + void ( *SetRxDutyCycle ) ( uint32_t rxTime, uint32_t sleepTime ); +}; + +/*! + * \brief Radio driver + * + * \remark This variable is defined and initialized in the specific radio + * board implementation + */ +extern const struct Radio_s Radio; + +#ifdef __cplusplus +} +#endif + +#endif // __RADIO_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/Region.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/Region.c new file mode 100644 index 0000000000..83c380e0dc --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/Region.c @@ -0,0 +1,975 @@ +/*! + * \file Region.c + * + * \brief Region implementation. + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + */ +#include "LoRaMac.h" +#include "Region.h" + +// Setup regions +#ifdef REGION_AS923 +#include "RegionAS923.h" +#define AS923_CASE case LORAMAC_REGION_AS923: +#define AS923_IS_ACTIVE( ) AS923_CASE { return true; } +#define AS923_GET_PHY_PARAM( ) AS923_CASE { return RegionAS923GetPhyParam( getPhy ); } +#define AS923_SET_BAND_TX_DONE( ) AS923_CASE { RegionAS923SetBandTxDone( txDone ); break; } +#define AS923_INIT_DEFAULTS( ) AS923_CASE { RegionAS923InitDefaults( params ); break; } +#define AS923_VERIFY( ) AS923_CASE { return RegionAS923Verify( verify, phyAttribute ); } +#define AS923_APPLY_CF_LIST( ) AS923_CASE { RegionAS923ApplyCFList( applyCFList ); break; } +#define AS923_CHAN_MASK_SET( ) AS923_CASE { return RegionAS923ChanMaskSet( chanMaskSet ); } +#define AS923_COMPUTE_RX_WINDOW_PARAMETERS( ) AS923_CASE { RegionAS923ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; } +#define AS923_RX_CONFIG( ) AS923_CASE { return RegionAS923RxConfig( rxConfig, datarate ); } +#define AS923_TX_CONFIG( ) AS923_CASE { return RegionAS923TxConfig( txConfig, txPower, txTimeOnAir ); } +#define AS923_LINK_ADR_REQ( ) AS923_CASE { return RegionAS923LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); } +#define AS923_RX_PARAM_SETUP_REQ( ) AS923_CASE { return RegionAS923RxParamSetupReq( rxParamSetupReq ); } +#define AS923_NEW_CHANNEL_REQ( ) AS923_CASE { return RegionAS923NewChannelReq( newChannelReq ); } +#define AS923_TX_PARAM_SETUP_REQ( ) AS923_CASE { return RegionAS923TxParamSetupReq( txParamSetupReq ); } +#define AS923_DL_CHANNEL_REQ( ) AS923_CASE { return RegionAS923DlChannelReq( dlChannelReq ); } +#define AS923_ALTERNATE_DR( ) AS923_CASE { return RegionAS923AlternateDr( currentDr, type ); } +#define AS923_NEXT_CHANNEL( ) AS923_CASE { return RegionAS923NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); } +#define AS923_CHANNEL_ADD( ) AS923_CASE { return RegionAS923ChannelAdd( channelAdd ); } +#define AS923_CHANNEL_REMOVE( ) AS923_CASE { return RegionAS923ChannelsRemove( channelRemove ); } +#define AS923_APPLY_DR_OFFSET( ) AS923_CASE { return RegionAS923ApplyDrOffset( downlinkDwellTime, dr, drOffset ); } +#define AS923_RX_BEACON_SETUP( ) AS923_CASE { RegionAS923RxBeaconSetup( rxBeaconSetup, outDr ); break; } +#else +#define AS923_IS_ACTIVE( ) +#define AS923_GET_PHY_PARAM( ) +#define AS923_SET_BAND_TX_DONE( ) +#define AS923_INIT_DEFAULTS( ) +#define AS923_GET_NVM_CTX( ) +#define AS923_VERIFY( ) +#define AS923_APPLY_CF_LIST( ) +#define AS923_CHAN_MASK_SET( ) +#define AS923_COMPUTE_RX_WINDOW_PARAMETERS( ) +#define AS923_RX_CONFIG( ) +#define AS923_TX_CONFIG( ) +#define AS923_LINK_ADR_REQ( ) +#define AS923_RX_PARAM_SETUP_REQ( ) +#define AS923_NEW_CHANNEL_REQ( ) +#define AS923_TX_PARAM_SETUP_REQ( ) +#define AS923_DL_CHANNEL_REQ( ) +#define AS923_ALTERNATE_DR( ) +#define AS923_NEXT_CHANNEL( ) +#define AS923_CHANNEL_ADD( ) +#define AS923_CHANNEL_REMOVE( ) +#define AS923_APPLY_DR_OFFSET( ) +#define AS923_RX_BEACON_SETUP( ) +#endif + +#ifdef REGION_AU915 +#include "RegionAU915.h" +#define AU915_CASE case LORAMAC_REGION_AU915: +#define AU915_IS_ACTIVE( ) AU915_CASE { return true; } +#define AU915_GET_PHY_PARAM( ) AU915_CASE { return RegionAU915GetPhyParam( getPhy ); } +#define AU915_SET_BAND_TX_DONE( ) AU915_CASE { RegionAU915SetBandTxDone( txDone ); break; } +#define AU915_INIT_DEFAULTS( ) AU915_CASE { RegionAU915InitDefaults( params ); break; } +#define AU915_VERIFY( ) AU915_CASE { return RegionAU915Verify( verify, phyAttribute ); } +#define AU915_APPLY_CF_LIST( ) AU915_CASE { RegionAU915ApplyCFList( applyCFList ); break; } +#define AU915_CHAN_MASK_SET( ) AU915_CASE { return RegionAU915ChanMaskSet( chanMaskSet ); } +#define AU915_COMPUTE_RX_WINDOW_PARAMETERS( ) AU915_CASE { RegionAU915ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; } +#define AU915_RX_CONFIG( ) AU915_CASE { return RegionAU915RxConfig( rxConfig, datarate ); } +#define AU915_TX_CONFIG( ) AU915_CASE { return RegionAU915TxConfig( txConfig, txPower, txTimeOnAir ); } +#define AU915_LINK_ADR_REQ( ) AU915_CASE { return RegionAU915LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); } +#define AU915_RX_PARAM_SETUP_REQ( ) AU915_CASE { return RegionAU915RxParamSetupReq( rxParamSetupReq ); } +#define AU915_NEW_CHANNEL_REQ( ) AU915_CASE { return RegionAU915NewChannelReq( newChannelReq ); } +#define AU915_TX_PARAM_SETUP_REQ( ) AU915_CASE { return RegionAU915TxParamSetupReq( txParamSetupReq ); } +#define AU915_DL_CHANNEL_REQ( ) AU915_CASE { return RegionAU915DlChannelReq( dlChannelReq ); } +#define AU915_ALTERNATE_DR( ) AU915_CASE { return RegionAU915AlternateDr( currentDr, type ); } +#define AU915_NEXT_CHANNEL( ) AU915_CASE { return RegionAU915NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); } +#define AU915_CHANNEL_ADD( ) AU915_CASE { return RegionAU915ChannelAdd( channelAdd ); } +#define AU915_CHANNEL_REMOVE( ) AU915_CASE { return RegionAU915ChannelsRemove( channelRemove ); } +#define AU915_APPLY_DR_OFFSET( ) AU915_CASE { return RegionAU915ApplyDrOffset( downlinkDwellTime, dr, drOffset ); } +#define AU915_RX_BEACON_SETUP( ) AU915_CASE { RegionAU915RxBeaconSetup( rxBeaconSetup, outDr ); break; } +#else +#define AU915_IS_ACTIVE( ) +#define AU915_GET_PHY_PARAM( ) +#define AU915_SET_BAND_TX_DONE( ) +#define AU915_INIT_DEFAULTS( ) +#define AU915_GET_NVM_CTX( ) +#define AU915_VERIFY( ) +#define AU915_APPLY_CF_LIST( ) +#define AU915_CHAN_MASK_SET( ) +#define AU915_COMPUTE_RX_WINDOW_PARAMETERS( ) +#define AU915_RX_CONFIG( ) +#define AU915_TX_CONFIG( ) +#define AU915_LINK_ADR_REQ( ) +#define AU915_RX_PARAM_SETUP_REQ( ) +#define AU915_NEW_CHANNEL_REQ( ) +#define AU915_TX_PARAM_SETUP_REQ( ) +#define AU915_DL_CHANNEL_REQ( ) +#define AU915_ALTERNATE_DR( ) +#define AU915_NEXT_CHANNEL( ) +#define AU915_CHANNEL_ADD( ) +#define AU915_CHANNEL_REMOVE( ) +#define AU915_APPLY_DR_OFFSET( ) +#define AU915_RX_BEACON_SETUP( ) +#endif + +#ifdef REGION_CN470 +#include "RegionCN470.h" +#define CN470_CASE case LORAMAC_REGION_CN470: +#define CN470_IS_ACTIVE( ) CN470_CASE { return true; } +#define CN470_GET_PHY_PARAM( ) CN470_CASE { return RegionCN470GetPhyParam( getPhy ); } +#define CN470_SET_BAND_TX_DONE( ) CN470_CASE { RegionCN470SetBandTxDone( txDone ); break; } +#define CN470_INIT_DEFAULTS( ) CN470_CASE { RegionCN470InitDefaults( params ); break; } +#define CN470_VERIFY( ) CN470_CASE { return RegionCN470Verify( verify, phyAttribute ); } +#define CN470_APPLY_CF_LIST( ) CN470_CASE { RegionCN470ApplyCFList( applyCFList ); break; } +#define CN470_CHAN_MASK_SET( ) CN470_CASE { return RegionCN470ChanMaskSet( chanMaskSet ); } +#define CN470_COMPUTE_RX_WINDOW_PARAMETERS( ) CN470_CASE { RegionCN470ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; } +#define CN470_RX_CONFIG( ) CN470_CASE { return RegionCN470RxConfig( rxConfig, datarate ); } +#define CN470_TX_CONFIG( ) CN470_CASE { return RegionCN470TxConfig( txConfig, txPower, txTimeOnAir ); } +#define CN470_LINK_ADR_REQ( ) CN470_CASE { return RegionCN470LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); } +#define CN470_RX_PARAM_SETUP_REQ( ) CN470_CASE { return RegionCN470RxParamSetupReq( rxParamSetupReq ); } +#define CN470_NEW_CHANNEL_REQ( ) CN470_CASE { return RegionCN470NewChannelReq( newChannelReq ); } +#define CN470_TX_PARAM_SETUP_REQ( ) CN470_CASE { return RegionCN470TxParamSetupReq( txParamSetupReq ); } +#define CN470_DL_CHANNEL_REQ( ) CN470_CASE { return RegionCN470DlChannelReq( dlChannelReq ); } +#define CN470_ALTERNATE_DR( ) CN470_CASE { return RegionCN470AlternateDr( currentDr, type ); } +#define CN470_NEXT_CHANNEL( ) CN470_CASE { return RegionCN470NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); } +#define CN470_CHANNEL_ADD( ) CN470_CASE { return RegionCN470ChannelAdd( channelAdd ); } +#define CN470_CHANNEL_REMOVE( ) CN470_CASE { return RegionCN470ChannelsRemove( channelRemove ); } +#define CN470_APPLY_DR_OFFSET( ) CN470_CASE { return RegionCN470ApplyDrOffset( downlinkDwellTime, dr, drOffset ); } +#define CN470_RX_BEACON_SETUP( ) CN470_CASE { RegionCN470RxBeaconSetup( rxBeaconSetup, outDr ); break; } +#else +#define CN470_IS_ACTIVE( ) +#define CN470_GET_PHY_PARAM( ) +#define CN470_SET_BAND_TX_DONE( ) +#define CN470_INIT_DEFAULTS( ) +#define CN470_GET_NVM_CTX( ) +#define CN470_VERIFY( ) +#define CN470_APPLY_CF_LIST( ) +#define CN470_CHAN_MASK_SET( ) +#define CN470_COMPUTE_RX_WINDOW_PARAMETERS( ) +#define CN470_RX_CONFIG( ) +#define CN470_TX_CONFIG( ) +#define CN470_LINK_ADR_REQ( ) +#define CN470_RX_PARAM_SETUP_REQ( ) +#define CN470_NEW_CHANNEL_REQ( ) +#define CN470_TX_PARAM_SETUP_REQ( ) +#define CN470_DL_CHANNEL_REQ( ) +#define CN470_ALTERNATE_DR( ) +#define CN470_NEXT_CHANNEL( ) +#define CN470_CHANNEL_ADD( ) +#define CN470_CHANNEL_REMOVE( ) +#define CN470_APPLY_DR_OFFSET( ) +#define CN470_RX_BEACON_SETUP( ) +#endif + +#ifdef REGION_CN779 +#include "RegionCN779.h" +#define CN779_CASE case LORAMAC_REGION_CN779: +#define CN779_IS_ACTIVE( ) CN779_CASE { return true; } +#define CN779_GET_PHY_PARAM( ) CN779_CASE { return RegionCN779GetPhyParam( getPhy ); } +#define CN779_SET_BAND_TX_DONE( ) CN779_CASE { RegionCN779SetBandTxDone( txDone ); break; } +#define CN779_INIT_DEFAULTS( ) CN779_CASE { RegionCN779InitDefaults( params ); break; } +#define CN779_VERIFY( ) CN779_CASE { return RegionCN779Verify( verify, phyAttribute ); } +#define CN779_APPLY_CF_LIST( ) CN779_CASE { RegionCN779ApplyCFList( applyCFList ); break; } +#define CN779_CHAN_MASK_SET( ) CN779_CASE { return RegionCN779ChanMaskSet( chanMaskSet ); } +#define CN779_COMPUTE_RX_WINDOW_PARAMETERS( ) CN779_CASE { RegionCN779ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; } +#define CN779_RX_CONFIG( ) CN779_CASE { return RegionCN779RxConfig( rxConfig, datarate ); } +#define CN779_TX_CONFIG( ) CN779_CASE { return RegionCN779TxConfig( txConfig, txPower, txTimeOnAir ); } +#define CN779_LINK_ADR_REQ( ) CN779_CASE { return RegionCN779LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); } +#define CN779_RX_PARAM_SETUP_REQ( ) CN779_CASE { return RegionCN779RxParamSetupReq( rxParamSetupReq ); } +#define CN779_NEW_CHANNEL_REQ( ) CN779_CASE { return RegionCN779NewChannelReq( newChannelReq ); } +#define CN779_TX_PARAM_SETUP_REQ( ) CN779_CASE { return RegionCN779TxParamSetupReq( txParamSetupReq ); } +#define CN779_DL_CHANNEL_REQ( ) CN779_CASE { return RegionCN779DlChannelReq( dlChannelReq ); } +#define CN779_ALTERNATE_DR( ) CN779_CASE { return RegionCN779AlternateDr( currentDr, type ); } +#define CN779_NEXT_CHANNEL( ) CN779_CASE { return RegionCN779NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); } +#define CN779_CHANNEL_ADD( ) CN779_CASE { return RegionCN779ChannelAdd( channelAdd ); } +#define CN779_CHANNEL_REMOVE( ) CN779_CASE { return RegionCN779ChannelsRemove( channelRemove ); } +#define CN779_APPLY_DR_OFFSET( ) CN779_CASE { return RegionCN779ApplyDrOffset( downlinkDwellTime, dr, drOffset ); } +#define CN779_RX_BEACON_SETUP( ) CN779_CASE { RegionCN779RxBeaconSetup( rxBeaconSetup, outDr ); break; } +#else +#define CN779_IS_ACTIVE( ) +#define CN779_GET_PHY_PARAM( ) +#define CN779_SET_BAND_TX_DONE( ) +#define CN779_INIT_DEFAULTS( ) +#define CN779_GET_NVM_CTX( ) +#define CN779_VERIFY( ) +#define CN779_APPLY_CF_LIST( ) +#define CN779_CHAN_MASK_SET( ) +#define CN779_COMPUTE_RX_WINDOW_PARAMETERS( ) +#define CN779_RX_CONFIG( ) +#define CN779_TX_CONFIG( ) +#define CN779_LINK_ADR_REQ( ) +#define CN779_RX_PARAM_SETUP_REQ( ) +#define CN779_NEW_CHANNEL_REQ( ) +#define CN779_TX_PARAM_SETUP_REQ( ) +#define CN779_DL_CHANNEL_REQ( ) +#define CN779_ALTERNATE_DR( ) +#define CN779_NEXT_CHANNEL( ) +#define CN779_CHANNEL_ADD( ) +#define CN779_CHANNEL_REMOVE( ) +#define CN779_APPLY_DR_OFFSET( ) +#define CN779_RX_BEACON_SETUP( ) +#endif + +#ifdef REGION_EU433 +#include "RegionEU433.h" +#define EU433_CASE case LORAMAC_REGION_EU433: +#define EU433_IS_ACTIVE( ) EU433_CASE { return true; } +#define EU433_GET_PHY_PARAM( ) EU433_CASE { return RegionEU433GetPhyParam( getPhy ); } +#define EU433_SET_BAND_TX_DONE( ) EU433_CASE { RegionEU433SetBandTxDone( txDone ); break; } +#define EU433_INIT_DEFAULTS( ) EU433_CASE { RegionEU433InitDefaults( params ); break; } +#define EU433_VERIFY( ) EU433_CASE { return RegionEU433Verify( verify, phyAttribute ); } +#define EU433_APPLY_CF_LIST( ) EU433_CASE { RegionEU433ApplyCFList( applyCFList ); break; } +#define EU433_CHAN_MASK_SET( ) EU433_CASE { return RegionEU433ChanMaskSet( chanMaskSet ); } +#define EU433_COMPUTE_RX_WINDOW_PARAMETERS( ) EU433_CASE { RegionEU433ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; } +#define EU433_RX_CONFIG( ) EU433_CASE { return RegionEU433RxConfig( rxConfig, datarate ); } +#define EU433_TX_CONFIG( ) EU433_CASE { return RegionEU433TxConfig( txConfig, txPower, txTimeOnAir ); } +#define EU433_LINK_ADR_REQ( ) EU433_CASE { return RegionEU433LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); } +#define EU433_RX_PARAM_SETUP_REQ( ) EU433_CASE { return RegionEU433RxParamSetupReq( rxParamSetupReq ); } +#define EU433_NEW_CHANNEL_REQ( ) EU433_CASE { return RegionEU433NewChannelReq( newChannelReq ); } +#define EU433_TX_PARAM_SETUP_REQ( ) EU433_CASE { return RegionEU433TxParamSetupReq( txParamSetupReq ); } +#define EU433_DL_CHANNEL_REQ( ) EU433_CASE { return RegionEU433DlChannelReq( dlChannelReq ); } +#define EU433_ALTERNATE_DR( ) EU433_CASE { return RegionEU433AlternateDr( currentDr, type ); } +#define EU433_NEXT_CHANNEL( ) EU433_CASE { return RegionEU433NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); } +#define EU433_CHANNEL_ADD( ) EU433_CASE { return RegionEU433ChannelAdd( channelAdd ); } +#define EU433_CHANNEL_REMOVE( ) EU433_CASE { return RegionEU433ChannelsRemove( channelRemove ); } +#define EU433_APPLY_DR_OFFSET( ) EU433_CASE { return RegionEU433ApplyDrOffset( downlinkDwellTime, dr, drOffset ); } +#define EU433_RX_BEACON_SETUP( ) EU433_CASE { RegionEU433RxBeaconSetup( rxBeaconSetup, outDr ); break; } +#else +#define EU433_IS_ACTIVE( ) +#define EU433_GET_PHY_PARAM( ) +#define EU433_SET_BAND_TX_DONE( ) +#define EU433_INIT_DEFAULTS( ) +#define EU433_GET_NVM_CTX( ) +#define EU433_VERIFY( ) +#define EU433_APPLY_CF_LIST( ) +#define EU433_CHAN_MASK_SET( ) +#define EU433_COMPUTE_RX_WINDOW_PARAMETERS( ) +#define EU433_RX_CONFIG( ) +#define EU433_TX_CONFIG( ) +#define EU433_LINK_ADR_REQ( ) +#define EU433_RX_PARAM_SETUP_REQ( ) +#define EU433_NEW_CHANNEL_REQ( ) +#define EU433_TX_PARAM_SETUP_REQ( ) +#define EU433_DL_CHANNEL_REQ( ) +#define EU433_ALTERNATE_DR( ) +#define EU433_NEXT_CHANNEL( ) +#define EU433_CHANNEL_ADD( ) +#define EU433_CHANNEL_REMOVE( ) +#define EU433_APPLY_DR_OFFSET( ) +#define EU433_RX_BEACON_SETUP( ) +#endif + +#ifdef REGION_EU868 +#include "RegionEU868.h" +#define EU868_CASE case LORAMAC_REGION_EU868: +#define EU868_IS_ACTIVE( ) EU868_CASE { return true; } +#define EU868_GET_PHY_PARAM( ) EU868_CASE { return RegionEU868GetPhyParam( getPhy ); } +#define EU868_SET_BAND_TX_DONE( ) EU868_CASE { RegionEU868SetBandTxDone( txDone ); break; } +#define EU868_INIT_DEFAULTS( ) EU868_CASE { RegionEU868InitDefaults( params ); break; } +#define EU868_VERIFY( ) EU868_CASE { return RegionEU868Verify( verify, phyAttribute ); } +#define EU868_APPLY_CF_LIST( ) EU868_CASE { RegionEU868ApplyCFList( applyCFList ); break; } +#define EU868_CHAN_MASK_SET( ) EU868_CASE { return RegionEU868ChanMaskSet( chanMaskSet ); } +#define EU868_COMPUTE_RX_WINDOW_PARAMETERS( ) EU868_CASE { RegionEU868ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; } +#define EU868_RX_CONFIG( ) EU868_CASE { return RegionEU868RxConfig( rxConfig, datarate ); } +#define EU868_TX_CONFIG( ) EU868_CASE { return RegionEU868TxConfig( txConfig, txPower, txTimeOnAir ); } +#define EU868_LINK_ADR_REQ( ) EU868_CASE { return RegionEU868LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); } +#define EU868_RX_PARAM_SETUP_REQ( ) EU868_CASE { return RegionEU868RxParamSetupReq( rxParamSetupReq ); } +#define EU868_NEW_CHANNEL_REQ( ) EU868_CASE { return RegionEU868NewChannelReq( newChannelReq ); } +#define EU868_TX_PARAM_SETUP_REQ( ) EU868_CASE { return RegionEU868TxParamSetupReq( txParamSetupReq ); } +#define EU868_DL_CHANNEL_REQ( ) EU868_CASE { return RegionEU868DlChannelReq( dlChannelReq ); } +#define EU868_ALTERNATE_DR( ) EU868_CASE { return RegionEU868AlternateDr( currentDr, type ); } +#define EU868_NEXT_CHANNEL( ) EU868_CASE { return RegionEU868NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); } +#define EU868_CHANNEL_ADD( ) EU868_CASE { return RegionEU868ChannelAdd( channelAdd ); } +#define EU868_CHANNEL_REMOVE( ) EU868_CASE { return RegionEU868ChannelsRemove( channelRemove ); } +#define EU868_APPLY_DR_OFFSET( ) EU868_CASE { return RegionEU868ApplyDrOffset( downlinkDwellTime, dr, drOffset ); } +#define EU868_RX_BEACON_SETUP( ) EU868_CASE { RegionEU868RxBeaconSetup( rxBeaconSetup, outDr ); break; } +#else +#define EU868_IS_ACTIVE( ) +#define EU868_GET_PHY_PARAM( ) +#define EU868_SET_BAND_TX_DONE( ) +#define EU868_INIT_DEFAULTS( ) +#define EU868_GET_NVM_CTX( ) +#define EU868_VERIFY( ) +#define EU868_APPLY_CF_LIST( ) +#define EU868_CHAN_MASK_SET( ) +#define EU868_COMPUTE_RX_WINDOW_PARAMETERS( ) +#define EU868_RX_CONFIG( ) +#define EU868_TX_CONFIG( ) +#define EU868_LINK_ADR_REQ( ) +#define EU868_RX_PARAM_SETUP_REQ( ) +#define EU868_NEW_CHANNEL_REQ( ) +#define EU868_TX_PARAM_SETUP_REQ( ) +#define EU868_DL_CHANNEL_REQ( ) +#define EU868_ALTERNATE_DR( ) +#define EU868_NEXT_CHANNEL( ) +#define EU868_CHANNEL_ADD( ) +#define EU868_CHANNEL_REMOVE( ) +#define EU868_APPLY_DR_OFFSET( ) +#define EU868_RX_BEACON_SETUP( ) +#endif + +#ifdef REGION_KR920 +#include "RegionKR920.h" +#define KR920_CASE case LORAMAC_REGION_KR920: +#define KR920_IS_ACTIVE( ) KR920_CASE { return true; } +#define KR920_GET_PHY_PARAM( ) KR920_CASE { return RegionKR920GetPhyParam( getPhy ); } +#define KR920_SET_BAND_TX_DONE( ) KR920_CASE { RegionKR920SetBandTxDone( txDone ); break; } +#define KR920_INIT_DEFAULTS( ) KR920_CASE { RegionKR920InitDefaults( params ); break; } +#define KR920_VERIFY( ) KR920_CASE { return RegionKR920Verify( verify, phyAttribute ); } +#define KR920_APPLY_CF_LIST( ) KR920_CASE { RegionKR920ApplyCFList( applyCFList ); break; } +#define KR920_CHAN_MASK_SET( ) KR920_CASE { return RegionKR920ChanMaskSet( chanMaskSet ); } +#define KR920_COMPUTE_RX_WINDOW_PARAMETERS( ) KR920_CASE { RegionKR920ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; } +#define KR920_RX_CONFIG( ) KR920_CASE { return RegionKR920RxConfig( rxConfig, datarate ); } +#define KR920_TX_CONFIG( ) KR920_CASE { return RegionKR920TxConfig( txConfig, txPower, txTimeOnAir ); } +#define KR920_LINK_ADR_REQ( ) KR920_CASE { return RegionKR920LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); } +#define KR920_RX_PARAM_SETUP_REQ( ) KR920_CASE { return RegionKR920RxParamSetupReq( rxParamSetupReq ); } +#define KR920_NEW_CHANNEL_REQ( ) KR920_CASE { return RegionKR920NewChannelReq( newChannelReq ); } +#define KR920_TX_PARAM_SETUP_REQ( ) KR920_CASE { return RegionKR920TxParamSetupReq( txParamSetupReq ); } +#define KR920_DL_CHANNEL_REQ( ) KR920_CASE { return RegionKR920DlChannelReq( dlChannelReq ); } +#define KR920_ALTERNATE_DR( ) KR920_CASE { return RegionKR920AlternateDr( currentDr, type ); } +#define KR920_NEXT_CHANNEL( ) KR920_CASE { return RegionKR920NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); } +#define KR920_CHANNEL_ADD( ) KR920_CASE { return RegionKR920ChannelAdd( channelAdd ); } +#define KR920_CHANNEL_REMOVE( ) KR920_CASE { return RegionKR920ChannelsRemove( channelRemove ); } +#define KR920_APPLY_DR_OFFSET( ) KR920_CASE { return RegionKR920ApplyDrOffset( downlinkDwellTime, dr, drOffset ); } +#define KR920_RX_BEACON_SETUP( ) KR920_CASE { RegionKR920RxBeaconSetup( rxBeaconSetup, outDr ); break; } +#else +#define KR920_IS_ACTIVE( ) +#define KR920_GET_PHY_PARAM( ) +#define KR920_SET_BAND_TX_DONE( ) +#define KR920_INIT_DEFAULTS( ) +#define KR920_GET_NVM_CTX( ) +#define KR920_VERIFY( ) +#define KR920_APPLY_CF_LIST( ) +#define KR920_CHAN_MASK_SET( ) +#define KR920_COMPUTE_RX_WINDOW_PARAMETERS( ) +#define KR920_RX_CONFIG( ) +#define KR920_TX_CONFIG( ) +#define KR920_LINK_ADR_REQ( ) +#define KR920_RX_PARAM_SETUP_REQ( ) +#define KR920_NEW_CHANNEL_REQ( ) +#define KR920_TX_PARAM_SETUP_REQ( ) +#define KR920_DL_CHANNEL_REQ( ) +#define KR920_ALTERNATE_DR( ) +#define KR920_NEXT_CHANNEL( ) +#define KR920_CHANNEL_ADD( ) +#define KR920_CHANNEL_REMOVE( ) +#define KR920_APPLY_DR_OFFSET( ) +#define KR920_RX_BEACON_SETUP( ) +#endif + +#ifdef REGION_IN865 +#include "RegionIN865.h" +#define IN865_CASE case LORAMAC_REGION_IN865: +#define IN865_IS_ACTIVE( ) IN865_CASE { return true; } +#define IN865_GET_PHY_PARAM( ) IN865_CASE { return RegionIN865GetPhyParam( getPhy ); } +#define IN865_SET_BAND_TX_DONE( ) IN865_CASE { RegionIN865SetBandTxDone( txDone ); break; } +#define IN865_INIT_DEFAULTS( ) IN865_CASE { RegionIN865InitDefaults( params ); break; } +#define IN865_VERIFY( ) IN865_CASE { return RegionIN865Verify( verify, phyAttribute ); } +#define IN865_APPLY_CF_LIST( ) IN865_CASE { RegionIN865ApplyCFList( applyCFList ); break; } +#define IN865_CHAN_MASK_SET( ) IN865_CASE { return RegionIN865ChanMaskSet( chanMaskSet ); } +#define IN865_COMPUTE_RX_WINDOW_PARAMETERS( ) IN865_CASE { RegionIN865ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; } +#define IN865_RX_CONFIG( ) IN865_CASE { return RegionIN865RxConfig( rxConfig, datarate ); } +#define IN865_TX_CONFIG( ) IN865_CASE { return RegionIN865TxConfig( txConfig, txPower, txTimeOnAir ); } +#define IN865_LINK_ADR_REQ( ) IN865_CASE { return RegionIN865LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); } +#define IN865_RX_PARAM_SETUP_REQ( ) IN865_CASE { return RegionIN865RxParamSetupReq( rxParamSetupReq ); } +#define IN865_NEW_CHANNEL_REQ( ) IN865_CASE { return RegionIN865NewChannelReq( newChannelReq ); } +#define IN865_TX_PARAM_SETUP_REQ( ) IN865_CASE { return RegionIN865TxParamSetupReq( txParamSetupReq ); } +#define IN865_DL_CHANNEL_REQ( ) IN865_CASE { return RegionIN865DlChannelReq( dlChannelReq ); } +#define IN865_ALTERNATE_DR( ) IN865_CASE { return RegionIN865AlternateDr( currentDr, type ); } +#define IN865_NEXT_CHANNEL( ) IN865_CASE { return RegionIN865NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); } +#define IN865_CHANNEL_ADD( ) IN865_CASE { return RegionIN865ChannelAdd( channelAdd ); } +#define IN865_CHANNEL_REMOVE( ) IN865_CASE { return RegionIN865ChannelsRemove( channelRemove ); } +#define IN865_APPLY_DR_OFFSET( ) IN865_CASE { return RegionIN865ApplyDrOffset( downlinkDwellTime, dr, drOffset ); } +#define IN865_RX_BEACON_SETUP( ) IN865_CASE { RegionIN865RxBeaconSetup( rxBeaconSetup, outDr ); break; } +#else +#define IN865_IS_ACTIVE( ) +#define IN865_GET_PHY_PARAM( ) +#define IN865_SET_BAND_TX_DONE( ) +#define IN865_INIT_DEFAULTS( ) +#define IN865_GET_NVM_CTX( ) +#define IN865_VERIFY( ) +#define IN865_APPLY_CF_LIST( ) +#define IN865_CHAN_MASK_SET( ) +#define IN865_COMPUTE_RX_WINDOW_PARAMETERS( ) +#define IN865_RX_CONFIG( ) +#define IN865_TX_CONFIG( ) +#define IN865_LINK_ADR_REQ( ) +#define IN865_RX_PARAM_SETUP_REQ( ) +#define IN865_NEW_CHANNEL_REQ( ) +#define IN865_TX_PARAM_SETUP_REQ( ) +#define IN865_DL_CHANNEL_REQ( ) +#define IN865_ALTERNATE_DR( ) +#define IN865_NEXT_CHANNEL( ) +#define IN865_CHANNEL_ADD( ) +#define IN865_CHANNEL_REMOVE( ) +#define IN865_APPLY_DR_OFFSET( ) +#define IN865_RX_BEACON_SETUP( ) +#endif + +#ifdef REGION_US915 +#include "RegionUS915.h" +#define US915_CASE case LORAMAC_REGION_US915: +#define US915_IS_ACTIVE( ) US915_CASE { return true; } +#define US915_GET_PHY_PARAM( ) US915_CASE { return RegionUS915GetPhyParam( getPhy ); } +#define US915_SET_BAND_TX_DONE( ) US915_CASE { RegionUS915SetBandTxDone( txDone ); break; } +#define US915_INIT_DEFAULTS( ) US915_CASE { RegionUS915InitDefaults( params ); break; } +#define US915_VERIFY( ) US915_CASE { return RegionUS915Verify( verify, phyAttribute ); } +#define US915_APPLY_CF_LIST( ) US915_CASE { RegionUS915ApplyCFList( applyCFList ); break; } +#define US915_CHAN_MASK_SET( ) US915_CASE { return RegionUS915ChanMaskSet( chanMaskSet ); } +#define US915_COMPUTE_RX_WINDOW_PARAMETERS( ) US915_CASE { RegionUS915ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; } +#define US915_RX_CONFIG( ) US915_CASE { return RegionUS915RxConfig( rxConfig, datarate ); } +#define US915_TX_CONFIG( ) US915_CASE { return RegionUS915TxConfig( txConfig, txPower, txTimeOnAir ); } +#define US915_LINK_ADR_REQ( ) US915_CASE { return RegionUS915LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); } +#define US915_RX_PARAM_SETUP_REQ( ) US915_CASE { return RegionUS915RxParamSetupReq( rxParamSetupReq ); } +#define US915_NEW_CHANNEL_REQ( ) US915_CASE { return RegionUS915NewChannelReq( newChannelReq ); } +#define US915_TX_PARAM_SETUP_REQ( ) US915_CASE { return RegionUS915TxParamSetupReq( txParamSetupReq ); } +#define US915_DL_CHANNEL_REQ( ) US915_CASE { return RegionUS915DlChannelReq( dlChannelReq ); } +#define US915_ALTERNATE_DR( ) US915_CASE { return RegionUS915AlternateDr( currentDr, type ); } +#define US915_NEXT_CHANNEL( ) US915_CASE { return RegionUS915NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); } +#define US915_CHANNEL_ADD( ) US915_CASE { return RegionUS915ChannelAdd( channelAdd ); } +#define US915_CHANNEL_REMOVE( ) US915_CASE { return RegionUS915ChannelsRemove( channelRemove ); } +#define US915_APPLY_DR_OFFSET( ) US915_CASE { return RegionUS915ApplyDrOffset( downlinkDwellTime, dr, drOffset ); } +#define US915_RX_BEACON_SETUP( ) US915_CASE { RegionUS915RxBeaconSetup( rxBeaconSetup, outDr ); break; } +#else +#define US915_IS_ACTIVE( ) +#define US915_GET_PHY_PARAM( ) +#define US915_SET_BAND_TX_DONE( ) +#define US915_INIT_DEFAULTS( ) +#define US915_GET_NVM_CTX( ) +#define US915_VERIFY( ) +#define US915_APPLY_CF_LIST( ) +#define US915_CHAN_MASK_SET( ) +#define US915_COMPUTE_RX_WINDOW_PARAMETERS( ) +#define US915_RX_CONFIG( ) +#define US915_TX_CONFIG( ) +#define US915_LINK_ADR_REQ( ) +#define US915_RX_PARAM_SETUP_REQ( ) +#define US915_NEW_CHANNEL_REQ( ) +#define US915_TX_PARAM_SETUP_REQ( ) +#define US915_DL_CHANNEL_REQ( ) +#define US915_ALTERNATE_DR( ) +#define US915_NEXT_CHANNEL( ) +#define US915_CHANNEL_ADD( ) +#define US915_CHANNEL_REMOVE( ) +#define US915_APPLY_DR_OFFSET( ) +#define US915_RX_BEACON_SETUP( ) +#endif + +#ifdef REGION_RU864 +#include "RegionRU864.h" +#define RU864_CASE case LORAMAC_REGION_RU864: +#define RU864_IS_ACTIVE( ) RU864_CASE { return true; } +#define RU864_GET_PHY_PARAM( ) RU864_CASE { return RegionRU864GetPhyParam( getPhy ); } +#define RU864_SET_BAND_TX_DONE( ) RU864_CASE { RegionRU864SetBandTxDone( txDone ); break; } +#define RU864_INIT_DEFAULTS( ) RU864_CASE { RegionRU864InitDefaults( params ); break; } +#define RU864_VERIFY( ) RU864_CASE { return RegionRU864Verify( verify, phyAttribute ); } +#define RU864_APPLY_CF_LIST( ) RU864_CASE { RegionRU864ApplyCFList( applyCFList ); break; } +#define RU864_CHAN_MASK_SET( ) RU864_CASE { return RegionRU864ChanMaskSet( chanMaskSet ); } +#define RU864_COMPUTE_RX_WINDOW_PARAMETERS( ) RU864_CASE { RegionRU864ComputeRxWindowParameters( datarate, minRxSymbols, rxError, rxConfigParams ); break; } +#define RU864_RX_CONFIG( ) RU864_CASE { return RegionRU864RxConfig( rxConfig, datarate ); } +#define RU864_TX_CONFIG( ) RU864_CASE { return RegionRU864TxConfig( txConfig, txPower, txTimeOnAir ); } +#define RU864_LINK_ADR_REQ( ) RU864_CASE { return RegionRU864LinkAdrReq( linkAdrReq, drOut, txPowOut, nbRepOut, nbBytesParsed ); } +#define RU864_RX_PARAM_SETUP_REQ( ) RU864_CASE { return RegionRU864RxParamSetupReq( rxParamSetupReq ); } +#define RU864_NEW_CHANNEL_REQ( ) RU864_CASE { return RegionRU864NewChannelReq( newChannelReq ); } +#define RU864_TX_PARAM_SETUP_REQ( ) RU864_CASE { return RegionRU864TxParamSetupReq( txParamSetupReq ); } +#define RU864_DL_CHANNEL_REQ( ) RU864_CASE { return RegionRU864DlChannelReq( dlChannelReq ); } +#define RU864_ALTERNATE_DR( ) RU864_CASE { return RegionRU864AlternateDr( currentDr, type ); } +#define RU864_NEXT_CHANNEL( ) RU864_CASE { return RegionRU864NextChannel( nextChanParams, channel, time, aggregatedTimeOff ); } +#define RU864_CHANNEL_ADD( ) RU864_CASE { return RegionRU864ChannelAdd( channelAdd ); } +#define RU864_CHANNEL_REMOVE( ) RU864_CASE { return RegionRU864ChannelsRemove( channelRemove ); } +#define RU864_APPLY_DR_OFFSET( ) RU864_CASE { return RegionRU864ApplyDrOffset( downlinkDwellTime, dr, drOffset ); } +#define RU864_RX_BEACON_SETUP( ) RU864_CASE { RegionRU864RxBeaconSetup( rxBeaconSetup, outDr ); break; } +#else +#define RU864_IS_ACTIVE( ) +#define RU864_GET_PHY_PARAM( ) +#define RU864_SET_BAND_TX_DONE( ) +#define RU864_INIT_DEFAULTS( ) +#define RU864_GET_NVM_CTX( ) +#define RU864_VERIFY( ) +#define RU864_APPLY_CF_LIST( ) +#define RU864_CHAN_MASK_SET( ) +#define RU864_COMPUTE_RX_WINDOW_PARAMETERS( ) +#define RU864_RX_CONFIG( ) +#define RU864_TX_CONFIG( ) +#define RU864_LINK_ADR_REQ( ) +#define RU864_RX_PARAM_SETUP_REQ( ) +#define RU864_NEW_CHANNEL_REQ( ) +#define RU864_TX_PARAM_SETUP_REQ( ) +#define RU864_DL_CHANNEL_REQ( ) +#define RU864_ALTERNATE_DR( ) +#define RU864_NEXT_CHANNEL( ) +#define RU864_CHANNEL_ADD( ) +#define RU864_CHANNEL_REMOVE( ) +#define RU864_APPLY_DR_OFFSET( ) +#define RU864_RX_BEACON_SETUP( ) +#endif + +bool RegionIsActive( LoRaMacRegion_t region ) +{ + switch( region ) + { + AS923_IS_ACTIVE( ); + AU915_IS_ACTIVE( ); + CN470_IS_ACTIVE( ); + CN779_IS_ACTIVE( ); + EU433_IS_ACTIVE( ); + EU868_IS_ACTIVE( ); + KR920_IS_ACTIVE( ); + IN865_IS_ACTIVE( ); + US915_IS_ACTIVE( ); + RU864_IS_ACTIVE( ); + default: + { + return false; + } + } +} + +PhyParam_t RegionGetPhyParam( LoRaMacRegion_t region, GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + switch( region ) + { + AS923_GET_PHY_PARAM( ); + AU915_GET_PHY_PARAM( ); + CN470_GET_PHY_PARAM( ); + CN779_GET_PHY_PARAM( ); + EU433_GET_PHY_PARAM( ); + EU868_GET_PHY_PARAM( ); + KR920_GET_PHY_PARAM( ); + IN865_GET_PHY_PARAM( ); + US915_GET_PHY_PARAM( ); + RU864_GET_PHY_PARAM( ); + default: + { + return phyParam; + } + } +} + +void RegionSetBandTxDone( LoRaMacRegion_t region, SetBandTxDoneParams_t* txDone ) +{ + switch( region ) + { + AS923_SET_BAND_TX_DONE( ); + AU915_SET_BAND_TX_DONE( ); + CN470_SET_BAND_TX_DONE( ); + CN779_SET_BAND_TX_DONE( ); + EU433_SET_BAND_TX_DONE( ); + EU868_SET_BAND_TX_DONE( ); + KR920_SET_BAND_TX_DONE( ); + IN865_SET_BAND_TX_DONE( ); + US915_SET_BAND_TX_DONE( ); + RU864_SET_BAND_TX_DONE( ); + default: + { + return; + } + } +} + +void RegionInitDefaults( LoRaMacRegion_t region, InitDefaultsParams_t* params ) +{ + switch( region ) + { + AS923_INIT_DEFAULTS( ); + AU915_INIT_DEFAULTS( ); + CN470_INIT_DEFAULTS( ); + CN779_INIT_DEFAULTS( ); + EU433_INIT_DEFAULTS( ); + EU868_INIT_DEFAULTS( ); + KR920_INIT_DEFAULTS( ); + IN865_INIT_DEFAULTS( ); + US915_INIT_DEFAULTS( ); + RU864_INIT_DEFAULTS( ); + default: + { + break; + } + } +} + +bool RegionVerify( LoRaMacRegion_t region, VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( region ) + { + AS923_VERIFY( ); + AU915_VERIFY( ); + CN470_VERIFY( ); + CN779_VERIFY( ); + EU433_VERIFY( ); + EU868_VERIFY( ); + KR920_VERIFY( ); + IN865_VERIFY( ); + US915_VERIFY( ); + RU864_VERIFY( ); + default: + { + return false; + } + } +} + +void RegionApplyCFList( LoRaMacRegion_t region, ApplyCFListParams_t* applyCFList ) +{ + switch( region ) + { + AS923_APPLY_CF_LIST( ); + AU915_APPLY_CF_LIST( ); + CN470_APPLY_CF_LIST( ); + CN779_APPLY_CF_LIST( ); + EU433_APPLY_CF_LIST( ); + EU868_APPLY_CF_LIST( ); + KR920_APPLY_CF_LIST( ); + IN865_APPLY_CF_LIST( ); + US915_APPLY_CF_LIST( ); + RU864_APPLY_CF_LIST( ); + default: + { + break; + } + } +} + +bool RegionChanMaskSet( LoRaMacRegion_t region, ChanMaskSetParams_t* chanMaskSet ) +{ + switch( region ) + { + AS923_CHAN_MASK_SET( ); + AU915_CHAN_MASK_SET( ); + CN470_CHAN_MASK_SET( ); + CN779_CHAN_MASK_SET( ); + EU433_CHAN_MASK_SET( ); + EU868_CHAN_MASK_SET( ); + KR920_CHAN_MASK_SET( ); + IN865_CHAN_MASK_SET( ); + US915_CHAN_MASK_SET( ); + RU864_CHAN_MASK_SET( ); + default: + { + return false; + } + } +} + +void RegionComputeRxWindowParameters( LoRaMacRegion_t region, int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + switch( region ) + { + AS923_COMPUTE_RX_WINDOW_PARAMETERS( ); + AU915_COMPUTE_RX_WINDOW_PARAMETERS( ); + CN470_COMPUTE_RX_WINDOW_PARAMETERS( ); + CN779_COMPUTE_RX_WINDOW_PARAMETERS( ); + EU433_COMPUTE_RX_WINDOW_PARAMETERS( ); + EU868_COMPUTE_RX_WINDOW_PARAMETERS( ); + KR920_COMPUTE_RX_WINDOW_PARAMETERS( ); + IN865_COMPUTE_RX_WINDOW_PARAMETERS( ); + US915_COMPUTE_RX_WINDOW_PARAMETERS( ); + RU864_COMPUTE_RX_WINDOW_PARAMETERS( ); + default: + { + break; + } + } +} + +bool RegionRxConfig( LoRaMacRegion_t region, RxConfigParams_t* rxConfig, int8_t* datarate ) +{ + switch( region ) + { + AS923_RX_CONFIG( ); + AU915_RX_CONFIG( ); + CN470_RX_CONFIG( ); + CN779_RX_CONFIG( ); + EU433_RX_CONFIG( ); + EU868_RX_CONFIG( ); + KR920_RX_CONFIG( ); + IN865_RX_CONFIG( ); + US915_RX_CONFIG( ); + RU864_RX_CONFIG( ); + default: + { + return false; + } + } +} + +bool RegionTxConfig( LoRaMacRegion_t region, TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + switch( region ) + { + AS923_TX_CONFIG( ); + AU915_TX_CONFIG( ); + CN470_TX_CONFIG( ); + CN779_TX_CONFIG( ); + EU433_TX_CONFIG( ); + EU868_TX_CONFIG( ); + KR920_TX_CONFIG( ); + IN865_TX_CONFIG( ); + US915_TX_CONFIG( ); + RU864_TX_CONFIG( ); + default: + { + return false; + } + } +} + +uint8_t RegionLinkAdrReq( LoRaMacRegion_t region, LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + switch( region ) + { + AS923_LINK_ADR_REQ( ); + AU915_LINK_ADR_REQ( ); + CN470_LINK_ADR_REQ( ); + CN779_LINK_ADR_REQ( ); + EU433_LINK_ADR_REQ( ); + EU868_LINK_ADR_REQ( ); + KR920_LINK_ADR_REQ( ); + IN865_LINK_ADR_REQ( ); + US915_LINK_ADR_REQ( ); + RU864_LINK_ADR_REQ( ); + default: + { + return 0; + } + } +} + +uint8_t RegionRxParamSetupReq( LoRaMacRegion_t region, RxParamSetupReqParams_t* rxParamSetupReq ) +{ + switch( region ) + { + AS923_RX_PARAM_SETUP_REQ( ); + AU915_RX_PARAM_SETUP_REQ( ); + CN470_RX_PARAM_SETUP_REQ( ); + CN779_RX_PARAM_SETUP_REQ( ); + EU433_RX_PARAM_SETUP_REQ( ); + EU868_RX_PARAM_SETUP_REQ( ); + KR920_RX_PARAM_SETUP_REQ( ); + IN865_RX_PARAM_SETUP_REQ( ); + US915_RX_PARAM_SETUP_REQ( ); + RU864_RX_PARAM_SETUP_REQ( ); + default: + { + return 0; + } + } +} + +int8_t RegionNewChannelReq( LoRaMacRegion_t region, NewChannelReqParams_t* newChannelReq ) +{ + switch( region ) + { + AS923_NEW_CHANNEL_REQ( ); + AU915_NEW_CHANNEL_REQ( ); + CN470_NEW_CHANNEL_REQ( ); + CN779_NEW_CHANNEL_REQ( ); + EU433_NEW_CHANNEL_REQ( ); + EU868_NEW_CHANNEL_REQ( ); + KR920_NEW_CHANNEL_REQ( ); + IN865_NEW_CHANNEL_REQ( ); + US915_NEW_CHANNEL_REQ( ); + RU864_NEW_CHANNEL_REQ( ); + default: + { + return 0; + } + } +} + +int8_t RegionTxParamSetupReq( LoRaMacRegion_t region, TxParamSetupReqParams_t* txParamSetupReq ) +{ + switch( region ) + { + AS923_TX_PARAM_SETUP_REQ( ); + AU915_TX_PARAM_SETUP_REQ( ); + CN470_TX_PARAM_SETUP_REQ( ); + CN779_TX_PARAM_SETUP_REQ( ); + EU433_TX_PARAM_SETUP_REQ( ); + EU868_TX_PARAM_SETUP_REQ( ); + KR920_TX_PARAM_SETUP_REQ( ); + IN865_TX_PARAM_SETUP_REQ( ); + US915_TX_PARAM_SETUP_REQ( ); + RU864_TX_PARAM_SETUP_REQ( ); + default: + { + return 0; + } + } +} + +int8_t RegionDlChannelReq( LoRaMacRegion_t region, DlChannelReqParams_t* dlChannelReq ) +{ + switch( region ) + { + AS923_DL_CHANNEL_REQ( ); + AU915_DL_CHANNEL_REQ( ); + CN470_DL_CHANNEL_REQ( ); + CN779_DL_CHANNEL_REQ( ); + EU433_DL_CHANNEL_REQ( ); + EU868_DL_CHANNEL_REQ( ); + KR920_DL_CHANNEL_REQ( ); + IN865_DL_CHANNEL_REQ( ); + US915_DL_CHANNEL_REQ( ); + RU864_DL_CHANNEL_REQ( ); + default: + { + return 0; + } + } +} + +int8_t RegionAlternateDr( LoRaMacRegion_t region, int8_t currentDr, AlternateDrType_t type ) +{ + switch( region ) + { + AS923_ALTERNATE_DR( ); + AU915_ALTERNATE_DR( ); + CN470_ALTERNATE_DR( ); + CN779_ALTERNATE_DR( ); + EU433_ALTERNATE_DR( ); + EU868_ALTERNATE_DR( ); + KR920_ALTERNATE_DR( ); + IN865_ALTERNATE_DR( ); + US915_ALTERNATE_DR( ); + RU864_ALTERNATE_DR( ); + default: + { + return 0; + } + } +} + +LoRaMacStatus_t RegionNextChannel( LoRaMacRegion_t region, NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + switch( region ) + { + AS923_NEXT_CHANNEL( ); + AU915_NEXT_CHANNEL( ); + CN470_NEXT_CHANNEL( ); + CN779_NEXT_CHANNEL( ); + EU433_NEXT_CHANNEL( ); + EU868_NEXT_CHANNEL( ); + KR920_NEXT_CHANNEL( ); + IN865_NEXT_CHANNEL( ); + US915_NEXT_CHANNEL( ); + RU864_NEXT_CHANNEL( ); + default: + { + return LORAMAC_STATUS_REGION_NOT_SUPPORTED; + } + } +} + +LoRaMacStatus_t RegionChannelAdd( LoRaMacRegion_t region, ChannelAddParams_t* channelAdd ) +{ + switch( region ) + { + AS923_CHANNEL_ADD( ); + AU915_CHANNEL_ADD( ); + CN470_CHANNEL_ADD( ); + CN779_CHANNEL_ADD( ); + EU433_CHANNEL_ADD( ); + EU868_CHANNEL_ADD( ); + KR920_CHANNEL_ADD( ); + IN865_CHANNEL_ADD( ); + US915_CHANNEL_ADD( ); + RU864_CHANNEL_ADD( ); + default: + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + } +} + +bool RegionChannelsRemove( LoRaMacRegion_t region, ChannelRemoveParams_t* channelRemove ) +{ + switch( region ) + { + AS923_CHANNEL_REMOVE( ); + AU915_CHANNEL_REMOVE( ); + CN470_CHANNEL_REMOVE( ); + CN779_CHANNEL_REMOVE( ); + EU433_CHANNEL_REMOVE( ); + EU868_CHANNEL_REMOVE( ); + KR920_CHANNEL_REMOVE( ); + IN865_CHANNEL_REMOVE( ); + US915_CHANNEL_REMOVE( ); + RU864_CHANNEL_REMOVE( ); + default: + { + return false; + } + } +} + +uint8_t RegionApplyDrOffset( LoRaMacRegion_t region, uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + switch( region ) + { + AS923_APPLY_DR_OFFSET( ); + AU915_APPLY_DR_OFFSET( ); + CN470_APPLY_DR_OFFSET( ); + CN779_APPLY_DR_OFFSET( ); + EU433_APPLY_DR_OFFSET( ); + EU868_APPLY_DR_OFFSET( ); + KR920_APPLY_DR_OFFSET( ); + IN865_APPLY_DR_OFFSET( ); + US915_APPLY_DR_OFFSET( ); + RU864_APPLY_DR_OFFSET( ); + default: + { + return dr; + } + } +} + +void RegionRxBeaconSetup( LoRaMacRegion_t region, RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ) +{ + switch( region ) + { + AS923_RX_BEACON_SETUP( ); + AU915_RX_BEACON_SETUP( ); + CN470_RX_BEACON_SETUP( ); + CN779_RX_BEACON_SETUP( ); + EU433_RX_BEACON_SETUP( ); + EU868_RX_BEACON_SETUP( ); + KR920_RX_BEACON_SETUP( ); + IN865_RX_BEACON_SETUP( ); + US915_RX_BEACON_SETUP( ); + RU864_RX_BEACON_SETUP( ); + default: + { + break; + } + } +} + +Version_t RegionGetVersion( void ) +{ + Version_t version; + + version.Value = REGION_VERSION; + + return version; +} + diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/Region.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/Region.h new file mode 100644 index 0000000000..10a54230ce --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/Region.h @@ -0,0 +1,1161 @@ +/*! + * \file Region.h + * + * \brief Region implementation. + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \defgroup REGION Region implementation + * This is the common API to access the specific + * regional implementations. + * + * Preprocessor options: + * - LoRaWAN regions can be activated by defining the related preprocessor + * definition. It is possible to define more than one region. + * The following regions are supported: + * - #define REGION_AS923 + * - #define REGION_AU915 + * - #define REGION_CN470 + * - #define REGION_CN779 + * - #define REGION_EU433 + * - #define REGION_EU868 + * - #define REGION_KR920 + * - #define REGION_IN865 + * - #define REGION_US915 + * - #define REGION_RU864 + * + * \{ + */ +#ifndef __REGION_H__ +#define __REGION_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include +#include "utilities.h" +#include "LoRaMac.h" +//#include "timer.h" +#include "RegionCommon.h" + +/*! + * Macro to compute bit of a channel index. + */ +#define LC( channelIndex ) ( uint16_t )( 1 << ( channelIndex - 1 ) ) + +#ifndef REGION_VERSION +/*! + * Regional parameters version definition. + * RP002-1.0.3 + */ +#define REGION_VERSION 0x02010003 +#endif + + + +/*! + * Enumeration of phy attributes. + */ +typedef enum ePhyAttribute +{ + /*! + * Frequency. It is available + * to perform a verification with RegionVerify(). + */ + PHY_FREQUENCY, + /*! + * Minimum RX datarate. + */ + PHY_MIN_RX_DR, + /*! + * Minimum TX datarate. + */ + PHY_MIN_TX_DR, + /*! + * Maximum RX datarate. + */ + PHY_MAX_RX_DR, + /*! + * Maximum TX datarate. + */ + PHY_MAX_TX_DR, + /*! + * TX datarate. + * This is a parameter which can't be queried. It is available + * to perform a verification with RegionVerify(). + */ + PHY_TX_DR, + /*! + * Default TX datarate. + */ + PHY_DEF_TX_DR, + /*! + * RX datarate. It is available + * to perform a verification with RegionVerify(). + */ + PHY_RX_DR, + /*! + * Maximum TX power. + */ + PHY_MAX_TX_POWER, + /*! + * TX power. It is available + * to perform a verification with RegionVerify(). + */ + PHY_TX_POWER, + /*! + * Default TX power. + */ + PHY_DEF_TX_POWER, + /*! + * Default ADR_ACK_LIMIT value. + */ + PHY_DEF_ADR_ACK_LIMIT, + /*! + * Default ADR_ACK_DELAY value. + */ + PHY_DEF_ADR_ACK_DELAY, + /*! + * Maximum payload possible. + */ + PHY_MAX_PAYLOAD, + /*! + * Duty cycle. + */ + PHY_DUTY_CYCLE, + /*! + * Maximum receive window duration. + */ + PHY_MAX_RX_WINDOW, + /*! + * Receive delay for window 1. + */ + PHY_RECEIVE_DELAY1, + /*! + * Receive delay for window 2. + */ + PHY_RECEIVE_DELAY2, + /*! + * Join accept delay for window 1. + */ + PHY_JOIN_ACCEPT_DELAY1, + /*! + * Join accept delay for window 2. + */ + PHY_JOIN_ACCEPT_DELAY2, + /*! + * Acknowledgement time out. + */ + PHY_RETRANSMIT_TIMEOUT, + /*! + * Default datarate offset for window 1. + */ + PHY_DEF_DR1_OFFSET, + /*! + * Default receive window 2 frequency. + */ + PHY_DEF_RX2_FREQUENCY, + /*! + * Default receive window 2 datarate. + */ + PHY_DEF_RX2_DR, + /*! + * Channels mask. + */ + PHY_CHANNELS_MASK, + /*! + * Channels default mask. + */ + PHY_CHANNELS_DEFAULT_MASK, + /*! + * Maximum number of supported channels + */ + PHY_MAX_NB_CHANNELS, + /*! + * Channels. + */ + PHY_CHANNELS, + /*! + * Default value of the uplink dwell time. + */ + PHY_DEF_UPLINK_DWELL_TIME, + /*! + * Default value of the downlink dwell time. + */ + PHY_DEF_DOWNLINK_DWELL_TIME, + /*! + * Default value of the MaxEIRP. + */ + PHY_DEF_MAX_EIRP, + /*! + * Default value of the antenna gain. + */ + PHY_DEF_ANTENNA_GAIN, + /*! + * Next lower datarate. + */ + PHY_NEXT_LOWER_TX_DR, + /*! + * Beacon interval in ms. + */ + PHY_BEACON_INTERVAL, + /*! + * Beacon reserved time in ms. + */ + PHY_BEACON_RESERVED, + /*! + * Beacon guard time in ms. + */ + PHY_BEACON_GUARD, + /*! + * Beacon window time in ms. + */ + PHY_BEACON_WINDOW, + /*! + * Beacon window time in numer of slots. + */ + PHY_BEACON_WINDOW_SLOTS, + /*! + * Ping slot length time in ms. + */ + PHY_PING_SLOT_WINDOW, + /*! + * Default symbol timeout for beacons and ping slot windows. + */ + PHY_BEACON_SYMBOL_TO_DEFAULT, + /*! + * Maximum symbol timeout for beacons. + */ + PHY_BEACON_SYMBOL_TO_EXPANSION_MAX, + /*! + * Maximum symbol timeout for ping slots. + */ + PHY_PING_SLOT_SYMBOL_TO_EXPANSION_MAX, + /*! + * Symbol expansion value for beacon windows in case of beacon + * loss in symbols. + */ + PHY_BEACON_SYMBOL_TO_EXPANSION_FACTOR, + /*! + * Symbol expansion value for ping slot windows in case of beacon + * loss in symbols. + */ + PHY_PING_SLOT_SYMBOL_TO_EXPANSION_FACTOR, + /*! + * Maximum allowed beacon less time in ms. + */ + PHY_MAX_BEACON_LESS_PERIOD, + /*! + * Delay time for the BeaconTimingAns in ms. + */ + PHY_BEACON_DELAY_BEACON_TIMING_ANS, + /*! + * Beacon channel frequency. + */ + PHY_BEACON_CHANNEL_FREQ, + /*! + * The format of the beacon. + */ + PHY_BEACON_FORMAT, + /*! + * The beacon channel datarate. + */ + PHY_BEACON_CHANNEL_DR, + /*! + * The number of channels for the beacon reception. + */ + PHY_BEACON_NB_CHANNELS, + /*! + * The static offset for the downlink channel calculation. + */ + PHY_BEACON_CHANNEL_OFFSET, + /*! + * Ping slot channel frequency. + */ + PHY_PING_SLOT_CHANNEL_FREQ, + /*! + * The datarate of a ping slot channel. + */ + PHY_PING_SLOT_CHANNEL_DR, + /*! + * The number of channels for the ping slot reception. + */ + PHY_PING_SLOT_NB_CHANNELS, + /*! + * The equivalent spreading factor value from datarate + */ + PHY_SF_FROM_DR, + /*! + * The equivalent bandwith index from datarate + */ + PHY_BW_FROM_DR, +}PhyAttribute_t; + +/*! + * Enumeration of initialization types. + */ +typedef enum eInitType +{ + /*! + * Initializes the regional default settings for the band, + * channel and default channels mask. Some regions also initiate + * other default configurations. In general, this type is intended + * to be called once during the initialization. + */ + INIT_TYPE_DEFAULTS, + /*! + * Resets the channels mask to the default channels. Deactivates + * all other channels. + */ + INIT_TYPE_RESET_TO_DEFAULT_CHANNELS, + /*! + * Activates the default channels. Leaves all other active channels + * active. + */ + INIT_TYPE_ACTIVATE_DEFAULT_CHANNELS +}InitType_t; + +typedef enum eChannelsMask +{ + /*! + * The channels mask. + */ + CHANNELS_MASK, + /*! + * The channels default mask. + */ + CHANNELS_DEFAULT_MASK +}ChannelsMask_t; + +/*! + * Structure containing the beacon format + */ +typedef struct sBeaconFormat +{ + /*! + * Size of the beacon + */ + uint8_t BeaconSize; + /*! + * Size of the RFU 1 data field + */ + uint8_t Rfu1Size; + /*! + * Size of the RFU 2 data field + */ + uint8_t Rfu2Size; +}BeaconFormat_t; + +/*! + * Union for the structure uGetPhyParams + */ +typedef union uPhyParam +{ + /*! + * A parameter value. + */ + uint32_t Value; + /*! + * A floating point value. + */ + float fValue; + /*! + * Pointer to the channels mask. + */ + uint16_t* ChannelsMask; + /*! + * Pointer to the channels. + */ + ChannelParams_t* Channels; + /*! + * Beacon format + */ + BeaconFormat_t BeaconFormat; + /*! + * Duty Cycle Period + */ + TimerTime_t DutyCycleTimePeriod; +}PhyParam_t; + +/*! + * Parameter structure for the function RegionGetPhyParam. + */ +typedef struct sGetPhyParams +{ + /*! + * Setup the parameter to get. + */ + PhyAttribute_t Attribute; + /*! + * Datarate. + * The parameter is needed for the following queries: + * PHY_MAX_PAYLOAD, PHY_NEXT_LOWER_TX_DR, PHY_SF_FROM_DR, PHY_BW_FROM_DR. + */ + int8_t Datarate; + /*! + * Uplink dwell time. This parameter must be set to query: + * PHY_MAX_PAYLOAD, PHY_MIN_TX_DR. + * The parameter is needed for the following queries: + * PHY_MIN_TX_DR, PHY_MAX_PAYLOAD, PHY_NEXT_LOWER_TX_DR. + */ + uint8_t UplinkDwellTime; + /*! + * Downlink dwell time. This parameter must be set to query: + * PHY_MAX_PAYLOAD, PHY_MIN_RX_DR. + * The parameter is needed for the following queries: + * PHY_MIN_RX_DR, PHY_MAX_PAYLOAD. + */ + uint8_t DownlinkDwellTime; + /*! + * Specification of the downlink channel. Used in Class B only. + * The parameter is needed for the following queries: + * PHY_BEACON_CHANNEL_FREQ, PHY_PING_SLOT_CHANNEL_FREQ + */ + uint8_t Channel; +}GetPhyParams_t; + +/*! + * Parameter structure for the function RegionSetBandTxDone. + */ +typedef struct sSetBandTxDoneParams +{ + /*! + * Channel to update. + */ + uint8_t Channel; + /*! + * Joined Set to true, if the node has joined the network + */ + bool Joined; + /*! + * Last TX done time. + */ + TimerTime_t LastTxDoneTime; + /*! + * Time-on-air of the last transmission. + */ + TimerTime_t LastTxAirTime; + /*! + * Elapsed time since initialization. + */ + SysTime_t ElapsedTimeSinceStartUp; +}SetBandTxDoneParams_t; + +/*! + * Parameter structure for the function RegionInitDefaults. + */ +typedef struct sInitDefaultsParams +{ + /*! + * Pointer to region NVM group1. + */ + void* NvmGroup1; + /*! + * Pointer to region NVM group2. + */ + void* NvmGroup2; + /*! + * Pointer to common region band storage. + */ + void* Bands; + /*! + * Sets the initialization type. + */ + InitType_t Type; +}InitDefaultsParams_t; + +/*! + * Parameter structure for the function RegionVerify. + */ +typedef union uVerifyParams +{ + /*! + * Channel frequency to verify + */ + uint32_t Frequency; + /*! + * TX power to verify. + */ + int8_t TxPower; + /*! + * Set to true, if the duty cycle is enabled, otherwise false. + */ + bool DutyCycle; + /*! + * Datarate to verify. + */ + struct sDatarateParams + { + /*! + * Datarate to verify. + */ + int8_t Datarate; + /*! + * The downlink dwell time. + */ + uint8_t DownlinkDwellTime; + /*! + * The up link dwell time. + */ + uint8_t UplinkDwellTime; + }DatarateParams; +}VerifyParams_t; + +/*! + * Parameter structure for the function RegionApplyCFList. + */ +typedef struct sApplyCFListParams +{ + uint8_t JoinChannel; + /*! + * Payload which contains the CF list. + */ + uint8_t* Payload; + /*! + * Size of the payload. + */ + uint8_t Size; +}ApplyCFListParams_t; + +/*! + * Parameter structure for the function RegionChanMaskSet. + */ +typedef struct sChanMaskSetParams +{ + /*! + * Pointer to the channels mask which should be set. + */ + uint16_t* ChannelsMaskIn; + /*! + * Pointer to the channels mask which should be set. + */ + ChannelsMask_t ChannelsMaskType; +}ChanMaskSetParams_t; + +/*! + * Parameter structure for the function RegionRxConfig. + */ +typedef struct sRxConfigParams +{ + /*! + * The RX channel. + */ + uint8_t Channel; + /*! + * RX datarate. + */ + int8_t Datarate; + /*! + * RX bandwidth. + */ + uint8_t Bandwidth; + /*! + * RX datarate offset. + */ + int8_t DrOffset; + /*! + * RX frequency. + */ + uint32_t Frequency; + /*! + * RX window timeout + */ + uint32_t WindowTimeout; + /*! + * RX window offset + */ + int32_t WindowOffset; + /*! + * Downlink dwell time. + */ + uint8_t DownlinkDwellTime; + /*! + * Set to true, if RX should be continuous. + */ + bool RxContinuous; + /*! + * Sets the RX window. + */ + LoRaMacRxSlot_t RxSlot; + /*! + * LoRaWAN Network End-Device Activation ( ACTIVATION_TYPE_NONE, ACTIVATION_TYPE_ABP + * or ACTIVATION_TYPE_OTTA ) + * + * Related MIB type: \ref MIB_NETWORK_ACTIVATION + */ + ActivationType_t NetworkActivation; +}RxConfigParams_t; + +/*! + * Parameter structure for the function RegionTxConfig. + */ +typedef struct sTxConfigParams +{ + /*! + * The TX channel. + */ + uint8_t Channel; + /*! + * The TX datarate. + */ + int8_t Datarate; + /*! + * The TX power. + */ + int8_t TxPower; + /*! + * The Max EIRP, if applicable. + */ + float MaxEirp; + /*! + * The antenna gain, if applicable. + */ + float AntennaGain; + /*! + * Frame length to setup. + */ + uint16_t PktLen; +}TxConfigParams_t; + +/*! + * Parameter structure for the function RegionLinkAdrReq. + */ +typedef struct sLinkAdrReqParams +{ + /*! + * Current LoRaWAN Version + */ + Version_t Version; + /*! + * Pointer to the payload which contains the MAC commands. + */ + uint8_t* Payload; + /*! + * Size of the payload. + */ + uint8_t PayloadSize; + /*! + * Uplink dwell time. + */ + uint8_t UplinkDwellTime; + /*! + * Set to true, if ADR is enabled. + */ + bool AdrEnabled; + /*! + * The current datarate. + */ + int8_t CurrentDatarate; + /*! + * The current TX power. + */ + int8_t CurrentTxPower; + /*! + * The current number of repetitions. + */ + uint8_t CurrentNbRep; +}LinkAdrReqParams_t; + +/*! + * Parameter structure for the function RegionRxParamSetupReq. + */ +typedef struct sRxParamSetupReqParams +{ + /*! + * The datarate to setup. + */ + int8_t Datarate; + /*! + * Datarate offset. + */ + int8_t DrOffset; + /*! + * The frequency to setup. + */ + uint32_t Frequency; +}RxParamSetupReqParams_t; + +/*! + * Parameter structure for the function RegionNewChannelReq. + */ +typedef struct sNewChannelReqParams +{ + /*! + * Pointer to the new channels. + */ + ChannelParams_t* NewChannel; + /*! + * Channel id. + */ + int8_t ChannelId; +}NewChannelReqParams_t; + +/*! + * Parameter structure for the function RegionTxParamSetupReq. + */ +typedef struct sTxParamSetupReqParams +{ + /*! + * Uplink dwell time. + */ + uint8_t UplinkDwellTime; + /*! + * Downlink dwell time. + */ + uint8_t DownlinkDwellTime; + /*! + * Max EIRP. + */ + uint8_t MaxEirp; +}TxParamSetupReqParams_t; + +/*! + * Parameter structure for the function RegionDlChannelReq. + */ +typedef struct sDlChannelReqParams +{ + /*! + * Channel Id to add the frequency. + */ + uint8_t ChannelId; + /*! + * Alternative frequency for the Rx1 window. + */ + uint32_t Rx1Frequency; +}DlChannelReqParams_t; + +/*! + * Enumeration of alternation type + */ +typedef enum eAlternateDrType +{ + /*! + * Type to use for an alternation + */ + ALTERNATE_DR, + /*! + * Type to use to restore one alternation + */ + ALTERNATE_DR_RESTORE +}AlternateDrType_t; + +/*! + * Parameter structure for the function RegionNextChannel. + */ +typedef struct sNextChanParams +{ + /*! + * Aggregated time-off time. + */ + TimerTime_t AggrTimeOff; + /*! + * Time of the last aggregated TX. + */ + TimerTime_t LastAggrTx; + /*! + * Current datarate. + */ + int8_t Datarate; + /*! + * Set to true, if the node has already joined a network, otherwise false. + */ + bool Joined; + /*! + * Set to true, if the duty cycle is enabled, otherwise false. + */ + bool DutyCycleEnabled; + /*! + * Elapsed time since the start of the node. + */ + SysTime_t ElapsedTimeSinceStartUp; + /*! + * Joined Set to true, if the last uplink was a join request + */ + bool LastTxIsJoinRequest; + /*! + * Payload length of the next frame + */ + uint16_t PktLen; +}NextChanParams_t; + +/*! + * Parameter structure for the function RegionChannelsAdd. + */ +typedef struct sChannelAddParams +{ + /*! + * Pointer to the new channel to add. + */ + ChannelParams_t* NewChannel; + /*! + * Channel id to add. + */ + uint8_t ChannelId; +}ChannelAddParams_t; + +/*! + * Parameter structure for the function RegionChannelsRemove. + */ +typedef struct sChannelRemoveParams +{ + /*! + * Channel id to remove. + */ + uint8_t ChannelId; +}ChannelRemoveParams_t; + +/*! + * Parameter structure for the function RegionRxBeaconSetup + */ +typedef struct sRxBeaconSetupParams +{ + /*! + * Symbol timeout. + */ + uint16_t SymbolTimeout; + /*! + * Receive time. + */ + uint32_t RxTime; + /*! + * The frequency to setup. + */ + uint32_t Frequency; +}RxBeaconSetup_t; + + + +/*! + * \brief The function verifies if a region is active or not. If a region + * is not active, it cannot be used. + * + * \param [IN] region LoRaWAN region. + * + * \retval Return true, if the region is supported. + */ +bool RegionIsActive( LoRaMacRegion_t region ); + +/*! + * \brief The function gets a value of a specific phy attribute. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] getPhy Pointer to the function parameters. + * + * \retval Returns a structure containing the PHY parameter. + */ +PhyParam_t RegionGetPhyParam( LoRaMacRegion_t region, GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionSetBandTxDone( LoRaMacRegion_t region, SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] params Pointer to the function parameters. + */ +void RegionInitDefaults( LoRaMacRegion_t region, InitDefaultsParams_t* params ); + +/*! + * \brief Verifies a parameter. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] verify Pointer to the function parameters. + * + * \param [IN] type Sets the initialization type. + * + * \retval Returns true, if the parameter is valid. + */ +bool RegionVerify( LoRaMacRegion_t region, VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + +/*! + * \brief The function parses the input buffer and sets up the channels of the + * CF list. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] applyCFList Pointer to the function parameters. + */ +void RegionApplyCFList( LoRaMacRegion_t region, ApplyCFListParams_t* applyCFList ); + +/*! + * \brief Sets a channels mask. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] chanMaskSet Pointer to the function parameters. + * + * \retval Returns true, if the channels mask could be set. + */ +bool RegionChanMaskSet( LoRaMacRegion_t region, ChanMaskSetParams_t* chanMaskSet ); + +/*! + * \brief Configuration of the RX windows. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] rxConfig Pointer to the function parameters. + * + * \param [OUT] datarate The datarate index which was set. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionRxConfig( LoRaMacRegion_t region, RxConfigParams_t* rxConfig, int8_t* datarate ); + +/* + * Rx window precise timing + * + * For more details please consult the following document, chapter 3.1.2. + * https://www.semtech.com/uploads/documents/SX1272_settings_for_LoRaWAN_v2.0.pdf + * or + * https://www.semtech.com/uploads/documents/SX1276_settings_for_LoRaWAN_v2.0.pdf + * + * Downlink start: T = Tx + 1s (+/- 20 us) + * | + * TRxEarly | TRxLate + * | | | + * | | +---+---+---+---+---+---+---+---+ + * | | | Latest Rx window | + * | | +---+---+---+---+---+---+---+---+ + * | | | + * +---+---+---+---+---+---+---+---+ + * | Earliest Rx window | + * +---+---+---+---+---+---+---+---+ + * | + * +---+---+---+---+---+---+---+---+ + *Downlink preamble 8 symbols | | | | | | | | | + * +---+---+---+---+---+---+---+---+ + * + * Worst case Rx window timings + * + * TRxLate = DEFAULT_MIN_RX_SYMBOLS * tSymbol - RADIO_WAKEUP_TIME + * TRxEarly = 8 - DEFAULT_MIN_RX_SYMBOLS * tSymbol - RxWindowTimeout - RADIO_WAKEUP_TIME + * + * TRxLate - TRxEarly = 2 * DEFAULT_SYSTEM_MAX_RX_ERROR + * + * RxOffset = ( TRxLate + TRxEarly ) / 2 + * + * RxWindowTimeout = ( 2 * DEFAULT_MIN_RX_SYMBOLS - 8 ) * tSymbol + 2 * DEFAULT_SYSTEM_MAX_RX_ERROR + * RxOffset = 4 * tSymbol - RxWindowTimeout / 2 - RADIO_WAKE_UP_TIME + * + * Minimal value of RxWindowTimeout must be 5 symbols which implies that the system always tolerates at least an error of 1.5 * tSymbol + */ +/*! + * Computes the Rx window timeout and offset. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] datarate Rx window datarate index to be used + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms + * interval around RxOffset + * + * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields. + */ +void RegionComputeRxWindowParameters( LoRaMacRegion_t region, int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ); + +/*! + * \brief TX configuration. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] txConfig Pointer to the function parameters. + * + * \param [OUT] txPower The tx power index which was set. + * + * \param [OUT] txTimeOnAir The time-on-air of the frame. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionTxConfig( LoRaMacRegion_t region, TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ); + +/*! + * \brief The function processes a Link ADR Request. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] linkAdrReq Pointer to the function parameters. + * + * \param [OUT] drOut The datarate which was applied. + * + * \param [OUT] txPowOut The TX power which was applied. + * + * \param [OUT] nbRepOut The number of repetitions to apply. + * + * \param [OUT] nbBytesParsed The number bytes which were parsed. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionLinkAdrReq( LoRaMacRegion_t region, LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ); + +/*! + * \brief The function processes a RX Parameter Setup Request. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] rxParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionRxParamSetupReq( LoRaMacRegion_t region, RxParamSetupReqParams_t* rxParamSetupReq ); + +/*! + * \brief The function processes a New Channel Request. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] newChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +int8_t RegionNewChannelReq( LoRaMacRegion_t region, NewChannelReqParams_t* newChannelReq ); + +/*! + * \brief The function processes a TX ParamSetup Request. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] txParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + * Returns -1, if the functionality is not implemented. In this case, the end node + * shall ignore the command. + */ +int8_t RegionTxParamSetupReq( LoRaMacRegion_t region, TxParamSetupReqParams_t* txParamSetupReq ); + +/*! + * \brief The function processes a DlChannel Request. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] dlChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +int8_t RegionDlChannelReq( LoRaMacRegion_t region, DlChannelReqParams_t* dlChannelReq ); + +/*! + * \brief Alternates the datarate of the channel for the join request. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] currentDr Current datarate. + * + * \param [IN] type Alternation type. + * + * \retval Datarate to apply. + */ +int8_t RegionAlternateDr( LoRaMacRegion_t region, int8_t currentDr, AlternateDrType_t type ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [IN] region LoRaWAN region. + * + * \param [OUT] channel Next channel to use for TX. + * + * \param [OUT] time Time to wait for the next transmission according to the duty + * cycle. + * + * \param [OUT] aggregatedTimeOff Updates the aggregated time off. + * + * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate]. + */ +LoRaMacStatus_t RegionNextChannel( LoRaMacRegion_t region, NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ); + +/*! + * \brief Adds a channel. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] channelAdd Pointer to the function parameters. + * + * \retval Status of the operation. + */ +LoRaMacStatus_t RegionChannelAdd( LoRaMacRegion_t region, ChannelAddParams_t* channelAdd ); + +/*! + * \brief Removes a channel. + * + * \param [IN] region LoRaWAN region. + * + * \param [IN] channelRemove Pointer to the function parameters. + * + * \retval Returns true, if the channel was removed successfully. + */ +bool RegionChannelsRemove( LoRaMacRegion_t region, ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms + * + * \param [IN] dr Current datarate + * + * \param [IN] drOffset Offset to be applied + * + * \retval newDr Computed datarate. + */ +uint8_t RegionApplyDrOffset( LoRaMacRegion_t region, uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! + * \brief Sets the radio into beacon reception mode + * + * \param [IN] rxBeaconSetup Pointer to the function parameters + * + * \param [out] outDr Datarate used to receive the beacon + */ +void RegionRxBeaconSetup( LoRaMacRegion_t region, RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ); + +/*! + * \brief Gets the version of the regional parameters implementation. + * + * \retval Version of the regional parameters. + */ +Version_t RegionGetVersion( void ); + +/*! \} defgroup REGION */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGION_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionAS923.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionAS923.c new file mode 100644 index 0000000000..2acd0ee2d2 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionAS923.c @@ -0,0 +1,1171 @@ +/*! + * \file RegionAS923.c + * + * \brief Region implementation for AS923 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) +*/ +#include "radio.h" +#include "RegionCommon.h" +#include "RegionAS923.h" + +// Definitions +#define CHANNELS_MASK_SIZE 1 + +/*! + * RSSI threshold for a free channel [dBm] + */ +#define AS923_RSSI_FREE_TH -80 + +/*! + * Specifies the time the node performs a carrier sense + */ +#define AS923_CARRIER_SENSE_TIME 5 + +/*! + * Specifies the reception bandwidth to be used while executing the LBT + * Max channel bandwidth is 200 kHz + */ +#define AS923_LBT_RX_BANDWIDTH 200000 + +#ifndef REGION_AS923_DEFAULT_CHANNEL_PLAN +#define REGION_AS923_DEFAULT_CHANNEL_PLAN CHANNEL_PLAN_GROUP_AS923_1 +#endif + +#if( REGION_AS923_DEFAULT_CHANNEL_PLAN == CHANNEL_PLAN_GROUP_AS923_1 ) + +// Channel plan CHANNEL_PLAN_GROUP_AS923_1 + +#define REGION_AS923_FREQ_OFFSET 0 + +#define AS923_MIN_RF_FREQUENCY 915000000 +#define AS923_MAX_RF_FREQUENCY 928000000 + +#elif ( REGION_AS923_DEFAULT_CHANNEL_PLAN == CHANNEL_PLAN_GROUP_AS923_2 ) + +// Channel plan CHANNEL_PLAN_GROUP_AS923_2 +// -1.8MHz +#define REGION_AS923_FREQ_OFFSET ( ( ~( 0xFFFFB9B0 ) + 1 ) * 100 ) + +#define AS923_MIN_RF_FREQUENCY 920000000 +#define AS923_MAX_RF_FREQUENCY 923000000 + +#elif ( REGION_AS923_DEFAULT_CHANNEL_PLAN == CHANNEL_PLAN_GROUP_AS923_3 ) + +// Channel plan CHANNEL_PLAN_GROUP_AS923_3 +// -6.6MHz +#define REGION_AS923_FREQ_OFFSET ( ( ~( 0xFFFEFE30 ) + 1 ) * 100 ) + +#define AS923_MIN_RF_FREQUENCY 915000000 +#define AS923_MAX_RF_FREQUENCY 921000000 + +#elif ( REGION_AS923_DEFAULT_CHANNEL_PLAN == CHANNEL_PLAN_GROUP_AS923_4 ) + +// Channel plan CHANNEL_PLAN_GROUP_AS923_4 +// -5.90MHz +#define REGION_AS923_FREQ_OFFSET ( ( ~( 0xFFFF1988 ) + 1 ) * 100 ) + +#define AS923_MIN_RF_FREQUENCY 917000000 +#define AS923_MAX_RF_FREQUENCY 920000000 + +#elif ( REGION_AS923_DEFAULT_CHANNEL_PLAN == CHANNEL_PLAN_GROUP_AS923_1_JP_CH24_CH38_LBT ) + +// Channel plan CHANNEL_PLAN_GROUP_AS923_1_JP_CH24_CH38_LBT + +#define REGION_AS923_FREQ_OFFSET 0 + +/*! + * Restrict AS923 frequencies to channels 24 to 38 + * Center frequencies 920.6 MHz to 923.4 MHz @ 200 kHz max bandwidth + */ +#define AS923_MIN_RF_FREQUENCY 920600000 +#define AS923_MAX_RF_FREQUENCY 923400000 + +#undef AS923_TX_MAX_DATARATE +#define AS923_TX_MAX_DATARATE DR_5 + +#undef AS923_RX_MAX_DATARATE +#define AS923_RX_MAX_DATARATE DR_5 + +#undef AS923_DEFAULT_MAX_EIRP +#define AS923_DEFAULT_MAX_EIRP 13.0f + +/*! + * STD-T108 Ver1.4 does not require dwell-time enforcement when using LBT on channels 28 to 38 + */ +#undef AS923_DEFAULT_UPLINK_DWELL_TIME +#define AS923_DEFAULT_UPLINK_DWELL_TIME 0 + +#elif ( REGION_AS923_DEFAULT_CHANNEL_PLAN == CHANNEL_PLAN_GROUP_AS923_1_JP_CH24_CH38_DC ) + +/* + * STD-T108 Ver1.4 allows the use of channels 24 to 38 without LBT. + * However a duty cycle enforcement must be in place + */ + +// Channel plan CHANNEL_PLAN_GROUP_AS923_1_JP_CH24_CH38_DC + +#define REGION_AS923_FREQ_OFFSET 0 + +/*! + * Restrict AS923 frequencies to channels 24 to 38 + * Center frequencies 920.6 MHz to 923.4 MHz @ 200 kHz max bandwidth + */ +#define AS923_MIN_RF_FREQUENCY 920600000 +#define AS923_MAX_RF_FREQUENCY 923400000 + +#undef AS923_TX_MAX_DATARATE +#define AS923_TX_MAX_DATARATE DR_5 + +#undef AS923_RX_MAX_DATARATE +#define AS923_RX_MAX_DATARATE DR_5 + +#undef AS923_DEFAULT_MAX_EIRP +#define AS923_DEFAULT_MAX_EIRP 13.0f + +/*! + * STD-T108 Ver1.4 does not require dwell-time enforcement when using DC on channels 28 to 38 + */ +#undef AS923_DEFAULT_UPLINK_DWELL_TIME +#define AS923_DEFAULT_UPLINK_DWELL_TIME 0 + +/*! + * Enable duty cycle enforcement + */ +#undef AS923_DUTY_CYCLE_ENABLED +#define AS923_DUTY_CYCLE_ENABLED 1 + +#elif ( REGION_AS923_DEFAULT_CHANNEL_PLAN == CHANNEL_PLAN_GROUP_AS923_1_JP_CH37_CH61_LBT_DC ) + +/* + * STD-T108 Ver1.4 allows the use of channels 37 to 61 with LBT and DC. + * However dwell time enforcement must be enabled + */ + +// Channel plan CHANNEL_PLAN_GROUP_AS923_1_JP_CH37_CH61_LBT_DC + +#define REGION_AS923_FREQ_OFFSET 0 + +/*! + * Restrict AS923 frequencies to channels 37 to 61 + * Center frequencies 922.4 MHz to 928.0 MHz @ 200 kHz max bandwidth + */ +#define AS923_MIN_RF_FREQUENCY 922400000 +#define AS923_MAX_RF_FREQUENCY 928000000 + +#undef AS923_TX_MAX_DATARATE +#define AS923_TX_MAX_DATARATE DR_5 + +#undef AS923_RX_MAX_DATARATE +#define AS923_RX_MAX_DATARATE DR_5 + +#undef AS923_DEFAULT_MAX_EIRP +#define AS923_DEFAULT_MAX_EIRP 13.0f + +/*! + * Enable duty cycle enforcement + */ +#undef AS923_DUTY_CYCLE_ENABLED +#define AS923_DUTY_CYCLE_ENABLED 1 + +/*! + * STD-T108 Ver1.4 requires a carrier sense time of at least 128 us on channels 37 to 61 + */ +#undef AS923_CARRIER_SENSE_TIME +#define AS923_CARRIER_SENSE_TIME 1 + +#else +#error "Wrong default channel plan selected. Please review compiler options." +#endif + +/* + * Non-volatile module context. + */ +static RegionNvmDataGroup1_t* RegionNvmGroup1; +static RegionNvmDataGroup2_t* RegionNvmGroup2; +static Band_t* RegionBands; + +// Static functions +static bool VerifyRfFreq( uint32_t freq ) +{ + // Check radio driver support + if( Radio.CheckRfFrequency( freq ) == false ) + { + return false; + } + + if( ( freq < AS923_MIN_RF_FREQUENCY ) || ( freq > AS923_MAX_RF_FREQUENCY ) ) + { + return false; + } + return true; +} + +static TimerTime_t GetTimeOnAir( int8_t datarate, uint16_t pktLen ) +{ + int8_t phyDr = DataratesAS923[datarate]; + uint32_t bandwidth = RegionCommonGetBandwidth( datarate, BandwidthsAS923 ); + TimerTime_t timeOnAir = 0; + + if( datarate == DR_7 ) + { // High Speed FSK channel + timeOnAir = Radio.TimeOnAir( MODEM_FSK, bandwidth, phyDr * 1000, 0, 5, false, pktLen, true ); + } + else + { + timeOnAir = Radio.TimeOnAir( MODEM_LORA, bandwidth, phyDr, 1, 8, false, pktLen, true ); + } + return timeOnAir; +} + +PhyParam_t RegionAS923GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + + switch( getPhy->Attribute ) + { + case PHY_MIN_RX_DR: + { + if( getPhy->DownlinkDwellTime == 0 ) + { + phyParam.Value = AS923_RX_MIN_DATARATE; + } + else + { + phyParam.Value = AS923_DWELL_LIMIT_DATARATE; + } + break; + } + case PHY_MIN_TX_DR: + { + if( getPhy->UplinkDwellTime == 0 ) + { + phyParam.Value = AS923_TX_MIN_DATARATE; + } + else + { + phyParam.Value = AS923_DWELL_LIMIT_DATARATE; + } + break; + } + case PHY_DEF_TX_DR: + { + phyParam.Value = AS923_DEFAULT_DATARATE; + break; + } + case PHY_NEXT_LOWER_TX_DR: + { + RegionCommonGetNextLowerTxDrParams_t nextLowerTxDrParams = + { + .CurrentDr = getPhy->Datarate, + .MaxDr = ( int8_t )AS923_TX_MAX_DATARATE, + .MinDr = ( int8_t )( ( getPhy->UplinkDwellTime == 0 ) ? AS923_TX_MIN_DATARATE : AS923_DWELL_LIMIT_DATARATE ), + .NbChannels = AS923_MAX_NB_CHANNELS, + .ChannelsMask = RegionNvmGroup2->ChannelsMask, + .Channels = RegionNvmGroup2->Channels, + }; + phyParam.Value = RegionCommonGetNextLowerTxDr( &nextLowerTxDrParams ); + break; + } + case PHY_MAX_TX_POWER: + { + phyParam.Value = AS923_MAX_TX_POWER; + break; + } + case PHY_DEF_TX_POWER: + { + phyParam.Value = AS923_DEFAULT_TX_POWER; + break; + } + case PHY_DEF_ADR_ACK_LIMIT: + { + phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_LIMIT; + break; + } + case PHY_DEF_ADR_ACK_DELAY: + { + phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_DELAY; + break; + } + case PHY_MAX_PAYLOAD: + { + if( getPhy->UplinkDwellTime == 0 ) + { + phyParam.Value = MaxPayloadOfDatarateDwell0AS923[getPhy->Datarate]; + } + else + { + phyParam.Value = MaxPayloadOfDatarateDwell1AS923[getPhy->Datarate]; + } + break; + } + case PHY_DUTY_CYCLE: + { + phyParam.Value = AS923_DUTY_CYCLE_ENABLED; + break; + } + case PHY_MAX_RX_WINDOW: + { + phyParam.Value = AS923_MAX_RX_WINDOW; + break; + } + case PHY_RECEIVE_DELAY1: + { + phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY2; + break; + } + case PHY_RETRANSMIT_TIMEOUT: + { + phyParam.Value = ( REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT + randr( -REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND, REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND ) ); + break; + } + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = REGION_COMMON_DEFAULT_RX1_DR_OFFSET; + break; + } + case PHY_DEF_RX2_FREQUENCY: + { + phyParam.Value = AS923_RX_WND_2_FREQ - REGION_AS923_FREQ_OFFSET; + break; + } + case PHY_DEF_RX2_DR: + { + phyParam.Value = AS923_RX_WND_2_DR; + break; + } + case PHY_CHANNELS_MASK: + { + phyParam.ChannelsMask = RegionNvmGroup2->ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = RegionNvmGroup2->ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = AS923_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = RegionNvmGroup2->Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + { + phyParam.Value = AS923_DEFAULT_UPLINK_DWELL_TIME; + break; + } + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = REGION_COMMON_DEFAULT_DOWNLINK_DWELL_TIME; + break; + } + case PHY_DEF_MAX_EIRP: + { + phyParam.fValue = AS923_DEFAULT_MAX_EIRP; + break; + } + case PHY_DEF_ANTENNA_GAIN: + { + phyParam.fValue = AS923_DEFAULT_ANTENNA_GAIN; + break; + } + case PHY_BEACON_CHANNEL_FREQ: + { + phyParam.Value = AS923_BEACON_CHANNEL_FREQ - REGION_AS923_FREQ_OFFSET; + break; + } + case PHY_BEACON_FORMAT: + { + phyParam.BeaconFormat.BeaconSize = AS923_BEACON_SIZE; + phyParam.BeaconFormat.Rfu1Size = AS923_RFU1_SIZE; + phyParam.BeaconFormat.Rfu2Size = AS923_RFU2_SIZE; + break; + } + case PHY_BEACON_CHANNEL_DR: + { + phyParam.Value = AS923_BEACON_CHANNEL_DR; + break; + } + case PHY_PING_SLOT_CHANNEL_FREQ: + { + phyParam.Value = AS923_PING_SLOT_CHANNEL_FREQ - REGION_AS923_FREQ_OFFSET; + break; + } + case PHY_PING_SLOT_CHANNEL_DR: + { + phyParam.Value = AS923_PING_SLOT_CHANNEL_DR; + break; + } + case PHY_SF_FROM_DR: + { + phyParam.Value = DataratesAS923[getPhy->Datarate]; + break; + } + case PHY_BW_FROM_DR: + { + phyParam.Value = RegionCommonGetBandwidth( getPhy->Datarate, BandwidthsAS923 ); + break; + } + default: + { + break; + } + } + + return phyParam; +} + +void RegionAS923SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ + RegionCommonSetBandTxDone( &RegionBands[RegionNvmGroup2->Channels[txDone->Channel].Band], + txDone->LastTxAirTime, txDone->Joined, txDone->ElapsedTimeSinceStartUp ); +} + +void RegionAS923InitDefaults( InitDefaultsParams_t* params ) +{ + Band_t bands[AS923_MAX_NB_BANDS] = + { + AS923_BAND0 + }; + + switch( params->Type ) + { + case INIT_TYPE_DEFAULTS: + { + if( ( params->NvmGroup1 == NULL ) || ( params->NvmGroup2 == NULL ) ) + { + return; + } + + RegionNvmGroup1 = (RegionNvmDataGroup1_t*) params->NvmGroup1; + RegionNvmGroup2 = (RegionNvmDataGroup2_t*) params->NvmGroup2; + RegionBands = (Band_t*) params->Bands; + + // Default bands + memcpy1( ( uint8_t* )RegionBands, ( uint8_t* )bands, sizeof( Band_t ) * AS923_MAX_NB_BANDS ); + + // Default channels + RegionNvmGroup2->Channels[0] = ( ChannelParams_t ) AS923_LC1; + RegionNvmGroup2->Channels[1] = ( ChannelParams_t ) AS923_LC2; + + // Apply frequency offset + RegionNvmGroup2->Channels[0].Frequency -= REGION_AS923_FREQ_OFFSET; + RegionNvmGroup2->Channels[1].Frequency -= REGION_AS923_FREQ_OFFSET; + + // Default ChannelsMask + RegionNvmGroup2->ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ); + + // Update the channels mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + +#if ( ( REGION_AS923_DEFAULT_CHANNEL_PLAN == CHANNEL_PLAN_GROUP_AS923_1_JP_CH24_CH38_LBT ) || \ + ( REGION_AS923_DEFAULT_CHANNEL_PLAN == CHANNEL_PLAN_GROUP_AS923_1_JP_CH37_CH61_LBT_DC ) ) + RegionNvmGroup2->RssiFreeThreshold = AS923_RSSI_FREE_TH; + RegionNvmGroup2->CarrierSenseTime = AS923_CARRIER_SENSE_TIME; +#endif + break; + } + case INIT_TYPE_RESET_TO_DEFAULT_CHANNELS: + { + // Reset Channels Rx1Frequency to default 0 + RegionNvmGroup2->Channels[0].Rx1Frequency = 0; + RegionNvmGroup2->Channels[1].Rx1Frequency = 0; + // Update the channels mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + break; + } + case INIT_TYPE_ACTIVATE_DEFAULT_CHANNELS: + { + // Activate channels default mask + RegionNvmGroup2->ChannelsMask[0] |= RegionNvmGroup2->ChannelsDefaultMask[0]; + break; + } + default: + { + break; + } + } +} + +bool RegionAS923Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( phyAttribute ) + { + case PHY_FREQUENCY: + { + return VerifyRfFreq( verify->Frequency ); + } + case PHY_TX_DR: + { + if( verify->DatarateParams.UplinkDwellTime == 0 ) + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, AS923_TX_MIN_DATARATE, AS923_TX_MAX_DATARATE ); + } + else + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, AS923_DWELL_LIMIT_DATARATE, AS923_TX_MAX_DATARATE ); + } + } + case PHY_DEF_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 ); + } + case PHY_RX_DR: + { + if( verify->DatarateParams.DownlinkDwellTime == 0 ) + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, AS923_RX_MIN_DATARATE, AS923_RX_MAX_DATARATE ); + } + else + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, AS923_DWELL_LIMIT_DATARATE, AS923_RX_MAX_DATARATE ); + } + } + case PHY_DEF_TX_POWER: + case PHY_TX_POWER: + { + // Remark: switched min and max! + return RegionCommonValueInRange( verify->TxPower, AS923_MAX_TX_POWER, AS923_MIN_TX_POWER ); + } + case PHY_DUTY_CYCLE: + { + return AS923_DUTY_CYCLE_ENABLED; + } + default: + return false; + } +} + +void RegionAS923ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ + ChannelParams_t newChannel; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + // Setup default datarate range + newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0; + + // Size of the optional CF list + if( applyCFList->Size != 16 ) + { + return; + } + + // Last byte CFListType must be 0 to indicate the CFList contains a list of frequencies + if( applyCFList->Payload[15] != 0 ) + { + return; + } + + // Last byte is RFU, don't take it into account + for( uint8_t i = 0, chanIdx = AS923_NUMB_DEFAULT_CHANNELS; chanIdx < AS923_MAX_NB_CHANNELS; i+=3, chanIdx++ ) + { + if( chanIdx < ( AS923_NUMB_CHANNELS_CF_LIST + AS923_NUMB_DEFAULT_CHANNELS ) ) + { + // Channel frequency + newChannel.Frequency = (uint32_t) applyCFList->Payload[i]; + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 ); + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 ); + newChannel.Frequency *= 100; + + // Initialize alternative frequency to 0 + newChannel.Rx1Frequency = 0; + } + else + { + newChannel.Frequency = 0; + newChannel.DrRange.Value = 0; + newChannel.Rx1Frequency = 0; + } + + if( newChannel.Frequency != 0 ) + { + channelAdd.NewChannel = &newChannel; + channelAdd.ChannelId = chanIdx; + + // Try to add all channels + RegionAS923ChannelAdd( &channelAdd ); + } + else + { + channelRemove.ChannelId = chanIdx; + + RegionAS923ChannelsRemove( &channelRemove ); + } + } +} + +bool RegionAS923ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + default: + return false; + } + return true; +} + +void RegionAS923ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + uint32_t tSymbolInUs = 0; + + // Get the datarate, perform a boundary check + rxConfigParams->Datarate = MIN( datarate, AS923_RX_MAX_DATARATE ); + rxConfigParams->Bandwidth = RegionCommonGetBandwidth( rxConfigParams->Datarate, BandwidthsAS923 ); + + if( rxConfigParams->Datarate == DR_7 ) + { // FSK + tSymbolInUs = RegionCommonComputeSymbolTimeFsk( DataratesAS923[rxConfigParams->Datarate] ); + } + else + { // LoRa + tSymbolInUs = RegionCommonComputeSymbolTimeLoRa( DataratesAS923[rxConfigParams->Datarate], BandwidthsAS923[rxConfigParams->Datarate] ); + } + + RegionCommonComputeRxWindowParameters( tSymbolInUs, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +} + +bool RegionAS923RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ + RadioModems_t modem; + int8_t dr = rxConfig->Datarate; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->RxSlot == RX_SLOT_WIN_1 ) + { + // Apply window 1 frequency + frequency = RegionNvmGroup2->Channels[rxConfig->Channel].Frequency; + // Apply the alternative RX 1 window frequency, if it is available + if( RegionNvmGroup2->Channels[rxConfig->Channel].Rx1Frequency != 0 ) + { + frequency = RegionNvmGroup2->Channels[rxConfig->Channel].Rx1Frequency; + } + } + + // Read the physical datarate from the datarates table + phyDr = DataratesAS923[dr]; + + Radio.SetChannel( frequency ); + + // Radio configuration + if( dr == DR_7 ) + { + modem = MODEM_FSK; + Radio.SetRxConfig( modem, 50000, phyDr * 1000, 0, 83333, 5, rxConfig->WindowTimeout, false, 0, true, 0, 0, false, rxConfig->RxContinuous ); + } + else + { + modem = MODEM_LORA; + Radio.SetRxConfig( modem, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous ); + } + + Radio.SetMaxPayloadLength( modem, MaxPayloadOfDatarateDwell0AS923[dr] + LORAMAC_FRAME_PAYLOAD_OVERHEAD_SIZE ); + + *datarate = (uint8_t) dr; + return true; +} + +bool RegionAS923TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + RadioModems_t modem; + int8_t phyDr = DataratesAS923[txConfig->Datarate]; + int8_t txPowerLimited = RegionCommonLimitTxPower( txConfig->TxPower, RegionBands[RegionNvmGroup2->Channels[txConfig->Channel].Band].TxMaxPower ); + uint32_t bandwidth = RegionCommonGetBandwidth( txConfig->Datarate, BandwidthsAS923 ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( RegionNvmGroup2->Channels[txConfig->Channel].Frequency ); + + if( txConfig->Datarate == DR_7 ) + { // High Speed FSK channel + modem = MODEM_FSK; + Radio.SetTxConfig( modem, phyTxPower, 25000, bandwidth, phyDr * 1000, 0, 5, false, true, 0, 0, false, 4000 ); + } + else + { + modem = MODEM_LORA; + Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 4000 ); + } + + // Update time-on-air + *txTimeOnAir = GetTimeOnAir( txConfig->Datarate, txConfig->PktLen ); + + // Setup maximum payload lenght of the radio driver + Radio.SetMaxPayloadLength( modem, txConfig->PktLen ); + + *txPower = txPowerLimited; + return true; +} + +uint8_t RegionAS923LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; + RegionCommonLinkAdrParams_t linkAdrParams = { 0 }; + uint8_t nextIndex = 0; + uint8_t bytesProcessed = 0; + uint16_t chMask = 0; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams; + + while( bytesProcessed < linkAdrReq->PayloadSize ) + { + // Get ADR request parameters + nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); + + if( nextIndex == 0 ) + break; // break loop, since no more request has been found + + // Update bytes processed + bytesProcessed += nextIndex; + + // Revert status, as we only check the last ADR request for the channel mask KO + status = 0x07; + + // Setup temporary channels mask + chMask = linkAdrParams.ChMask; + + // Verify channels mask + if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) ) + { + status &= 0xFE; // Channel mask KO + } + else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) || + ( linkAdrParams.ChMaskCtrl >= 7 ) ) + { + // RFU + status &= 0xFE; // Channel mask KO + } + else + { + for( uint8_t i = 0; i < AS923_MAX_NB_CHANNELS; i++ ) + { + if( linkAdrParams.ChMaskCtrl == 6 ) + { + if( RegionNvmGroup2->Channels[i].Frequency != 0 ) + { + chMask |= 1 << i; + } + } + else + { + if( ( ( chMask & ( 1 << i ) ) != 0 ) && + ( RegionNvmGroup2->Channels[i].Frequency == 0 ) ) + {// Trying to enable an undefined channel + status &= 0xFE; // Channel mask KO + } + } + } + } + } + + // Get the minimum possible datarate + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; + phyParam = RegionAS923GetPhyParam( &getPhy ); + + linkAdrVerifyParams.Status = status; + linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; + linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; + linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; + linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; + linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; + linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; + linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; + linkAdrVerifyParams.NbChannels = AS923_MAX_NB_CHANNELS; + linkAdrVerifyParams.ChannelsMask = &chMask; + linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value; + linkAdrVerifyParams.MaxDatarate = AS923_TX_MAX_DATARATE; + linkAdrVerifyParams.Channels = RegionNvmGroup2->Channels; + linkAdrVerifyParams.MinTxPower = AS923_MIN_TX_POWER; + linkAdrVerifyParams.MaxTxPower = AS923_MAX_TX_POWER; + linkAdrVerifyParams.Version = linkAdrReq->Version; + + // Verify the parameters and update, if necessary + status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep ); + + // Update channelsMask if everything is correct + if( status == 0x07 ) + { + // Set the channels mask to a default value + memset1( ( uint8_t* ) RegionNvmGroup2->ChannelsMask, 0, sizeof( RegionNvmGroup2->ChannelsMask ) ); + // Update the channels mask + RegionNvmGroup2->ChannelsMask[0] = chMask; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + + return status; +} + +uint8_t RegionAS923RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; + + // Verify radio frequency + if( VerifyRfFreq( rxParamSetupReq->Frequency ) == false ) + { + status &= 0xFE; // Channel frequency KO + } + + // Verify datarate + if( RegionCommonValueInRange( rxParamSetupReq->Datarate, AS923_RX_MIN_DATARATE, AS923_RX_MAX_DATARATE ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify datarate offset + if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, AS923_MIN_RX1_DR_OFFSET, AS923_MAX_RX1_DR_OFFSET ) == false ) + { + status &= 0xFB; // Rx1DrOffset range KO + } + + return status; +} + +int8_t RegionAS923NewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + uint8_t status = 0x03; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + if( newChannelReq->NewChannel->Frequency == 0 ) + { + channelRemove.ChannelId = newChannelReq->ChannelId; + + // Remove + if( RegionAS923ChannelsRemove( &channelRemove ) == false ) + { + status &= 0xFC; + } + } + else + { + channelAdd.NewChannel = newChannelReq->NewChannel; + channelAdd.ChannelId = newChannelReq->ChannelId; + + switch( RegionAS923ChannelAdd( &channelAdd ) ) + { + case LORAMAC_STATUS_OK: + { + break; + } + case LORAMAC_STATUS_FREQUENCY_INVALID: + { + status &= 0xFE; + break; + } + case LORAMAC_STATUS_DATARATE_INVALID: + { + status &= 0xFD; + break; + } + case LORAMAC_STATUS_FREQ_AND_DR_INVALID: + { + status &= 0xFC; + break; + } + default: + { + status &= 0xFC; + break; + } + } + } + + return status; +} + +int8_t RegionAS923TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + // Accept the request + return 0; +} + +int8_t RegionAS923DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + uint8_t status = 0x03; + + if( dlChannelReq->ChannelId >= ( CHANNELS_MASK_SIZE * 16 ) ) + { + return 0; + } + + // Verify if the frequency is supported + if( VerifyRfFreq( dlChannelReq->Rx1Frequency ) == false ) + { + status &= 0xFE; + } + + // Verify if an uplink frequency exists + if( RegionNvmGroup2->Channels[dlChannelReq->ChannelId].Frequency == 0 ) + { + status &= 0xFD; + } + + // Apply Rx1 frequency, if the status is OK + if( status == 0x03 ) + { + RegionNvmGroup2->Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency; + } + + return status; +} + +int8_t RegionAS923AlternateDr( int8_t currentDr, AlternateDrType_t type ) +{ + // Only AS923_DWELL_LIMIT_DATARATE is supported + return AS923_DWELL_LIMIT_DATARATE; +} + +LoRaMacStatus_t RegionAS923NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t nbRestrictedChannels = 0; + uint8_t enabledChannels[AS923_MAX_NB_CHANNELS] = { 0 }; + RegionCommonIdentifyChannelsParam_t identifyChannelsParam; + RegionCommonCountNbOfEnabledChannelsParams_t countChannelsParams; + LoRaMacStatus_t status = LORAMAC_STATUS_NO_CHANNEL_FOUND; + uint16_t joinChannels = AS923_JOIN_CHANNELS; + + if( RegionCommonCountChannels( RegionNvmGroup2->ChannelsMask, 0, 1 ) == 0 ) + { // Reactivate default channels + RegionNvmGroup2->ChannelsMask[0] |= LC( 1 ) + LC( 2 ); + } + + // Search how many channels are enabled + countChannelsParams.Joined = nextChanParams->Joined; + countChannelsParams.Datarate = nextChanParams->Datarate; + countChannelsParams.ChannelsMask = RegionNvmGroup2->ChannelsMask; + countChannelsParams.Channels = RegionNvmGroup2->Channels; + countChannelsParams.Bands = RegionBands; + countChannelsParams.MaxNbChannels = AS923_MAX_NB_CHANNELS; + countChannelsParams.JoinChannels = &joinChannels; + + identifyChannelsParam.AggrTimeOff = nextChanParams->AggrTimeOff; + identifyChannelsParam.LastAggrTx = nextChanParams->LastAggrTx; + identifyChannelsParam.DutyCycleEnabled = nextChanParams->DutyCycleEnabled; + identifyChannelsParam.MaxBands = AS923_MAX_NB_BANDS; + + identifyChannelsParam.ElapsedTimeSinceStartUp = nextChanParams->ElapsedTimeSinceStartUp; + identifyChannelsParam.LastTxIsJoinRequest = nextChanParams->LastTxIsJoinRequest; + identifyChannelsParam.ExpectedTimeOnAir = GetTimeOnAir( nextChanParams->Datarate, nextChanParams->PktLen ); + + identifyChannelsParam.CountNbOfEnabledChannelsParam = &countChannelsParams; + + status = RegionCommonIdentifyChannels( &identifyChannelsParam, aggregatedTimeOff, enabledChannels, + &nbEnabledChannels, &nbRestrictedChannels, time ); + + if( status == LORAMAC_STATUS_OK ) + { +#if ( ( REGION_AS923_DEFAULT_CHANNEL_PLAN == CHANNEL_PLAN_GROUP_AS923_1_JP_CH24_CH38_LBT ) || \ + ( REGION_AS923_DEFAULT_CHANNEL_PLAN == CHANNEL_PLAN_GROUP_AS923_1_JP_CH37_CH61_LBT_DC ) ) + // Executes the LBT algorithm when operating in Japan + uint8_t channelNext = 0; + + for( uint8_t i = 0, j = randr( 0, nbEnabledChannels - 1 ); i < AS923_MAX_NB_CHANNELS; i++ ) + { + channelNext = enabledChannels[j]; + j = ( j + 1 ) % nbEnabledChannels; + + // Perform carrier sense for AS923_CARRIER_SENSE_TIME + // If the channel is free, we can stop the LBT mechanism + if( Radio.IsChannelFree( RegionNvmGroup2->Channels[channelNext].Frequency, AS923_LBT_RX_BANDWIDTH, RegionNvmGroup2->RssiFreeThreshold, RegionNvmGroup2->CarrierSenseTime ) == true ) + { + // Free channel found + *channel = channelNext; + return LORAMAC_STATUS_OK; + } + } + // Even if one or more channels are available according to the channel plan, no free channel + // was found during the LBT procedure. + status = LORAMAC_STATUS_NO_FREE_CHANNEL_FOUND; +#else + // We found a valid channel + *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; +#endif + } + else if( status == LORAMAC_STATUS_NO_CHANNEL_FOUND ) + { + // Datarate not supported by any channel, restore defaults + RegionNvmGroup2->ChannelsMask[0] |= LC( 1 ) + LC( 2 ); + } + return status; +} + +LoRaMacStatus_t RegionAS923ChannelAdd( ChannelAddParams_t* channelAdd ) +{ + bool drInvalid = false; + bool freqInvalid = false; + uint8_t id = channelAdd->ChannelId; + + if( id < AS923_NUMB_DEFAULT_CHANNELS ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + + if( id >= AS923_MAX_NB_CHANNELS ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + // Validate the datarate range + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, AS923_TX_MIN_DATARATE, AS923_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, AS923_TX_MIN_DATARATE, AS923_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max ) + { + drInvalid = true; + } + + // Check frequency + if( freqInvalid == false ) + { + if( VerifyRfFreq( channelAdd->NewChannel->Frequency ) == false ) + { + freqInvalid = true; + } + } + + // Check status + if( ( drInvalid == true ) && ( freqInvalid == true ) ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + if( drInvalid == true ) + { + return LORAMAC_STATUS_DATARATE_INVALID; + } + if( freqInvalid == true ) + { + return LORAMAC_STATUS_FREQUENCY_INVALID; + } + + memcpy1( ( uint8_t* ) &(RegionNvmGroup2->Channels[id]), ( uint8_t* ) channelAdd->NewChannel, sizeof( RegionNvmGroup2->Channels[id] ) ); + RegionNvmGroup2->Channels[id].Band = 0; + RegionNvmGroup2->ChannelsMask[0] |= ( 1 << id ); + return LORAMAC_STATUS_OK; +} + +bool RegionAS923ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + uint8_t id = channelRemove->ChannelId; + + if( id < AS923_NUMB_DEFAULT_CHANNELS ) + { + return false; + } + + // Remove the channel from the list of channels + RegionNvmGroup2->Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 }; + + return RegionCommonChanDisable( RegionNvmGroup2->ChannelsMask, id, AS923_MAX_NB_CHANNELS ); +} + +uint8_t RegionAS923ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + // Initialize minDr + int8_t minDr; + + if( downlinkDwellTime == 0 ) + { + // Update the minDR for a downlink dwell time configuration of 0 + minDr = EffectiveRx1DrOffsetDownlinkDwell0AS923[dr][drOffset]; + } + else + { + // Update the minDR for a downlink dwell time configuration of 1 + minDr = EffectiveRx1DrOffsetDownlinkDwell1AS923[dr][drOffset]; + } + + return minDr; +} + +void RegionAS923RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ) +{ + RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup; + + regionCommonRxBeaconSetup.Datarates = DataratesAS923; + regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency; + regionCommonRxBeaconSetup.BeaconSize = AS923_BEACON_SIZE; + regionCommonRxBeaconSetup.BeaconDatarate = AS923_BEACON_CHANNEL_DR; + regionCommonRxBeaconSetup.BeaconChannelBW = AS923_BEACON_CHANNEL_BW; + regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime; + regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout; + + RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup ); + + // Store downlink datarate + *outDr = AS923_BEACON_CHANNEL_DR; +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionAS923.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionAS923.h new file mode 100644 index 0000000000..d43d8ec876 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionAS923.h @@ -0,0 +1,527 @@ +/*! + * \file RegionAS923.h + * + * \brief Region definition for AS923 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * \defgroup REGIONAS923 Region AS923 + * Implementation according to LoRaWAN Specification v1.0.2. + * \{ + */ +#ifndef __REGION_AS923_H__ +#define __REGION_AS923_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "region/Region.h" + + +/*! + * Channel plan group AS923-1 + * AS923_FREQ_OFFSET = 0 + */ +#define CHANNEL_PLAN_GROUP_AS923_1 1 + +/*! + * Channel plan group AS923-2 + * AS923_FREQ_OFFSET = -1.8MHz + */ +#define CHANNEL_PLAN_GROUP_AS923_2 2 + +/*! + * Channel plan group AS923-3 + * AS923_FREQ_OFFSET = -6.6MHz + */ +#define CHANNEL_PLAN_GROUP_AS923_3 3 + +/*! + * Channel plan group AS923-4 + * AS923_FREQ_OFFSET = -5.90MHz + */ +#define CHANNEL_PLAN_GROUP_AS923_4 4 + +/*! + * Channel plan group AS923-1 for Japan - channels 24 to 38 Listen Before Talk + * AS923_FREQ_OFFSET = 0 + */ +#define CHANNEL_PLAN_GROUP_AS923_1_JP_CH24_CH38_LBT 5 + +/*! + * Channel plan group AS923-1 for Japan - channels 24 to 38 Duty Cycle + * AS923_FREQ_OFFSET = 0 + */ +#define CHANNEL_PLAN_GROUP_AS923_1_JP_CH24_CH38_DC 6 + +/*! + * Channel plan group AS923-1 for Japan - channels 37 to 61 Listen Before Talk + Duty Cycle + * AS923_FREQ_OFFSET = 0 + */ +#define CHANNEL_PLAN_GROUP_AS923_1_JP_CH37_CH61_LBT_DC 7 + +/*! + * LoRaMac maximum number of channels + */ +#define AS923_MAX_NB_CHANNELS 16 + +/*! + * Number of default channels + */ +#define AS923_NUMB_DEFAULT_CHANNELS 2 + +/*! + * Number of channels to apply for the CF list + */ +#define AS923_NUMB_CHANNELS_CF_LIST 5 + +/*! + * Minimal datarate that can be used by the node + */ +#define AS923_TX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define AS923_TX_MAX_DATARATE DR_7 + +/*! + * Minimal datarate that can be used by the node + */ +#define AS923_RX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define AS923_RX_MAX_DATARATE DR_7 + +/*! + * Default datarate used by the node + */ +#define AS923_DEFAULT_DATARATE DR_2 + +/*! + * The minimum datarate which is used when the + * dwell time is limited. + */ +#define AS923_DWELL_LIMIT_DATARATE DR_2 + +/*! + * Minimal Rx1 receive datarate offset + */ +#define AS923_MIN_RX1_DR_OFFSET 0 + +/*! + * Maximal Rx1 receive datarate offset + */ +#define AS923_MAX_RX1_DR_OFFSET 7 + +/*! + * Minimal Tx output power that can be used by the node + */ +#define AS923_MIN_TX_POWER TX_POWER_7 + +/*! + * Maximal Tx output power that can be used by the node + */ +#define AS923_MAX_TX_POWER TX_POWER_0 + +/*! + * Default Tx output power used by the node + */ +#define AS923_DEFAULT_TX_POWER TX_POWER_0 + +/*! + * Default uplink dwell time configuration + */ +#define AS923_DEFAULT_UPLINK_DWELL_TIME 1 + +/*! + * Default Max EIRP + */ +#define AS923_DEFAULT_MAX_EIRP 16.0f + +/*! + * Default antenna gain + */ +#define AS923_DEFAULT_ANTENNA_GAIN 2.15f + +/*! + * Enabled or disabled the duty cycle + */ +#define AS923_DUTY_CYCLE_ENABLED 0 + +/*! + * Maximum RX window duration + */ +#define AS923_MAX_RX_WINDOW 3000 + +#if ( AS923_DEFAULT_DATARATE > DR_5 ) +#error "A default DR higher than DR_5 may lead to connectivity loss." +#endif + +/*! + * Second reception window channel frequency definition. + */ +#define AS923_RX_WND_2_FREQ 923200000 + +/*! + * Second reception window channel datarate definition. + */ +#define AS923_RX_WND_2_DR DR_2 + +/* + * CLASS B + */ +/*! + * Beacon frequency + */ +#define AS923_BEACON_CHANNEL_FREQ 923400000 + +/*! + * Ping slot channel frequency + */ +#define AS923_PING_SLOT_CHANNEL_FREQ 923400000 + +/*! + * Payload size of a beacon frame + */ +#define AS923_BEACON_SIZE 17 + +/*! + * Size of RFU 1 field + */ +#define AS923_RFU1_SIZE 1 + +/*! + * Size of RFU 2 field + */ +#define AS923_RFU2_SIZE 0 + +/*! + * Datarate of the beacon channel + */ +#define AS923_BEACON_CHANNEL_DR DR_3 + +/*! + * Bandwith of the beacon channel + */ +#define AS923_BEACON_CHANNEL_BW 0 + +/*! + * Ping slot channel datarate + */ +#define AS923_PING_SLOT_CHANNEL_DR DR_3 + +/*! + * Maximum number of bands + */ +#define AS923_MAX_NB_BANDS 1 + +/*! + * Band 0 definition + * Band = { DutyCycle, TxMaxPower, LastBandUpdateTime, LastMaxCreditAssignTime, TimeCredits, MaxTimeCredits, ReadyForTransmission } + */ +#define AS923_BAND0 { 100, AS923_MAX_TX_POWER, 0, 0, 0, 0, 0 } // 1.0 % + +/*! + * LoRaMac default channel 1 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define AS923_LC1 { 923200000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac default channel 2 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define AS923_LC2 { 923400000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac channels which are allowed for the join procedure + */ +#define AS923_JOIN_CHANNELS ( uint16_t )( LC( 1 ) | LC( 2 ) ) + +/*! + * Data rates table definition + */ +static const uint8_t DataratesAS923[] = { 12, 11, 10, 9, 8, 7, 7, 50 }; + +/*! + * Bandwidths table definition in Hz + */ +static const uint32_t BandwidthsAS923[] = { 125000, 125000, 125000, 125000, 125000, 125000, 250000, 0 }; + +/*! + * Maximum payload with respect to the datarate index. + * The table is valid for the dwell time configuration of 0 for uplinks and downlinks. + */ +static const uint8_t MaxPayloadOfDatarateDwell0AS923[] = { 51, 51, 115, 115, 242, 242, 242, 242 }; + +/*! + * Maximum payload with respect to the datarate index. + * The table is only valid for uplinks. + */ +static const uint8_t MaxPayloadOfDatarateDwell1AS923[] = { 0, 0, 11, 53, 125, 242, 242, 242 }; + +/*! + * Effective datarate offsets for receive window 1 when downlink dwell time is zero. + */ +static const int8_t EffectiveRx1DrOffsetDownlinkDwell0AS923[8][8] = + { + { DR_0 , DR_0 , DR_0 , DR_0 , DR_0 , DR_0 , DR_1 , DR_2 }, // DR_0 + { DR_1 , DR_0 , DR_0 , DR_0 , DR_0 , DR_0 , DR_2 , DR_3 }, // DR_1 + { DR_2 , DR_1 , DR_0 , DR_0 , DR_0 , DR_0 , DR_3 , DR_4 }, // DR_2 + { DR_3 , DR_2 , DR_1 , DR_0 , DR_0 , DR_0 , DR_4 , DR_5 }, // DR_3 + { DR_4 , DR_3 , DR_2 , DR_1 , DR_0 , DR_0 , DR_5 , DR_6 }, // DR_4 + { DR_5 , DR_4 , DR_3 , DR_2 , DR_1 , DR_0 , DR_6 , DR_7 }, // DR_5 + { DR_6 , DR_5 , DR_4 , DR_3 , DR_2 , DR_1 , DR_7 , DR_7 }, // DR_6 + { DR_7 , DR_6 , DR_5 , DR_4 , DR_3 , DR_2 , DR_7 , DR_7 }, // DR_7 + }; + +/*! + * Effective datarate offsets for receive window 1 when downlink dwell time is one. + */ +static const int8_t EffectiveRx1DrOffsetDownlinkDwell1AS923[8][8] = + { + { DR_2 , DR_2 , DR_2 , DR_2 , DR_2 , DR_2 , DR_2 , DR_2 }, // DR_0 + { DR_2 , DR_2 , DR_2 , DR_2 , DR_2 , DR_2 , DR_2 , DR_3 }, // DR_1 + { DR_2 , DR_2 , DR_2 , DR_2 , DR_2 , DR_2 , DR_3 , DR_4 }, // DR_2 + { DR_3 , DR_2 , DR_2 , DR_2 , DR_2 , DR_2 , DR_4 , DR_5 }, // DR_3 + { DR_4 , DR_3 , DR_2 , DR_2 , DR_2 , DR_2 , DR_5 , DR_6 }, // DR_4 + { DR_5 , DR_4 , DR_3 , DR_2 , DR_2 , DR_2 , DR_6 , DR_7 }, // DR_5 + { DR_6 , DR_5 , DR_4 , DR_3 , DR_2 , DR_2 , DR_7 , DR_7 }, // DR_6 + { DR_7 , DR_6 , DR_5 , DR_4 , DR_3 , DR_2 , DR_7 , DR_7 }, // DR_7 + }; + +/*! + * \brief The function gets a value of a specific phy attribute. + * + * \param [IN] getPhy Pointer to the function parameters. + * + * \retval Returns a structure containing the PHY parameter. + */ +PhyParam_t RegionAS923GetPhyParam( GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionAS923SetBandTxDone( SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] type Sets the initialization type. + */ +void RegionAS923InitDefaults( InitDefaultsParams_t* params ); + +/*! + * \brief Verifies a parameter. + * + * \param [IN] verify Pointer to the function parameters. + * + * \param [IN] type Sets the initialization type. + * + * \retval Returns true, if the parameter is valid. + */ +bool RegionAS923Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + +/*! + * \brief The function parses the input buffer and sets up the channels of the + * CF list. + * + * \param [IN] applyCFList Pointer to the function parameters. + */ +void RegionAS923ApplyCFList( ApplyCFListParams_t* applyCFList ); + +/*! + * \brief Sets a channels mask. + * + * \param [IN] chanMaskSet Pointer to the function parameters. + * + * \retval Returns true, if the channels mask could be set. + */ +bool RegionAS923ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ); + +/*! + * Computes the Rx window timeout and offset. + * + * \param [IN] datarate Rx window datarate index to be used + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms + * interval around RxOffset + * + * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields. + */ +void RegionAS923ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ); + +/*! + * \brief Configuration of the RX windows. + * + * \param [IN] rxConfig Pointer to the function parameters. + * + * \param [OUT] datarate The datarate index which was set. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionAS923RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ); + +/*! + * \brief TX configuration. + * + * \param [IN] txConfig Pointer to the function parameters. + * + * \param [OUT] txPower The tx power index which was set. + * + * \param [OUT] txTimeOnAir The time-on-air of the frame. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionAS923TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ); + +/*! + * \brief The function processes a Link ADR Request. + * + * \param [IN] linkAdrReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionAS923LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ); + +/*! + * \brief The function processes a RX Parameter Setup Request. + * + * \param [IN] rxParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionAS923RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ); + +/*! + * \brief The function processes a Channel Request. + * + * \param [IN] newChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +int8_t RegionAS923NewChannelReq( NewChannelReqParams_t* newChannelReq ); + +/*! + * \brief The function processes a TX ParamSetup Request. + * + * \param [IN] txParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + * Returns -1, if the functionality is not implemented. In this case, the end node + * shall not process the command. + */ +int8_t RegionAS923TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ); + +/*! + * \brief The function processes a DlChannel Request. + * + * \param [IN] dlChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +int8_t RegionAS923DlChannelReq( DlChannelReqParams_t* dlChannelReq ); + +/*! + * \brief Alternates the datarate of the channel for the join request. + * + * \param [IN] currentDr Current datarate. + * + * \retval Datarate to apply. + */ +int8_t RegionAS923AlternateDr( int8_t currentDr, AlternateDrType_t type ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [OUT] channel Next channel to use for TX. + * + * \param [OUT] time Time to wait for the next transmission according to the duty + * cycle. + * + * \param [OUT] aggregatedTimeOff Updates the aggregated time off. + * + * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate] + */ +LoRaMacStatus_t RegionAS923NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ); + +/*! + * \brief Adds a channel. + * + * \param [IN] channelAdd Pointer to the function parameters. + * + * \retval Status of the operation. + */ +LoRaMacStatus_t RegionAS923ChannelAdd( ChannelAddParams_t* channelAdd ); + +/*! + * \brief Removes a channel. + * + * \param [IN] channelRemove Pointer to the function parameters. + * + * \retval Returns true, if the channel was removed successfully. + */ +bool RegionAS923ChannelsRemove( ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms + * + * \param [IN] dr Current datarate + * + * \param [IN] drOffset Offset to be applied + * + * \retval newDr Computed datarate. + */ +uint8_t RegionAS923ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! + * \brief Sets the radio into beacon reception mode + * + * \param [IN] rxBeaconSetup Pointer to the function parameters + */ + void RegionAS923RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ); + +/*! \} defgroup REGIONAS923 */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGION_AS923_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionAU915.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionAU915.c new file mode 100644 index 0000000000..15718b2c1b --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionAU915.c @@ -0,0 +1,939 @@ +/*! + * \file RegionAU915.c + * + * \brief Region implementation for AU915 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) +*/ +#include "radio.h" +#include "RegionCommon.h" +#include "RegionAU915.h" +#include "RegionBaseUS.h" + +// Definitions +#define CHANNELS_MASK_SIZE 6 + +// A mask to select only valid 500KHz channels +#define CHANNELS_MASK_500KHZ_MASK 0x00FF + +/* + * Non-volatile module context. + */ +static RegionNvmDataGroup1_t* RegionNvmGroup1; +static RegionNvmDataGroup2_t* RegionNvmGroup2; +static Band_t* RegionBands; + +static bool VerifyRfFreq( uint32_t freq ) +{ + // Check radio driver support + if( Radio.CheckRfFrequency( freq ) == false ) + { + return false; + } + + // Rx frequencies + if( ( freq < AU915_FIRST_RX1_CHANNEL ) || + ( freq > AU915_LAST_RX1_CHANNEL ) || + ( ( ( freq - ( uint32_t ) AU915_FIRST_RX1_CHANNEL ) % ( uint32_t ) AU915_STEPWIDTH_RX1_CHANNEL ) != 0 ) ) + { + return false; + } + + // Tx frequencies for 125kHz + // Also includes the range for 500kHz channels + if( ( freq < 915200000 ) || ( freq > 927800000 ) ) + { + return false; + } + return true; +} + +static TimerTime_t GetTimeOnAir( int8_t datarate, uint16_t pktLen ) +{ + int8_t phyDr = DataratesAU915[datarate]; + uint32_t bandwidth = RegionCommonGetBandwidth( datarate, BandwidthsAU915 ); + + return Radio.TimeOnAir( MODEM_LORA, bandwidth, phyDr, 1, 8, false, pktLen, true ); +} + +PhyParam_t RegionAU915GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + + switch( getPhy->Attribute ) + { + case PHY_MIN_RX_DR: + { + if( getPhy->DownlinkDwellTime == 0) + { + phyParam.Value = AU915_RX_MIN_DATARATE; + } + else + { + phyParam.Value = AU915_DWELL_LIMIT_DATARATE; + } + break; + } + case PHY_MIN_TX_DR: + { + if( getPhy->UplinkDwellTime == 0) + { + phyParam.Value = AU915_TX_MIN_DATARATE; + } + else + { + phyParam.Value = AU915_DWELL_LIMIT_DATARATE; + } + break; + } + case PHY_DEF_TX_DR: + { + phyParam.Value = AU915_DEFAULT_DATARATE; + break; + } + case PHY_NEXT_LOWER_TX_DR: + { + RegionCommonGetNextLowerTxDrParams_t nextLowerTxDrParams = + { + .CurrentDr = getPhy->Datarate, + .MaxDr = ( int8_t )AU915_TX_MAX_DATARATE, + .MinDr = ( int8_t )( ( getPhy->UplinkDwellTime == 0 ) ? AU915_TX_MIN_DATARATE : AU915_DWELL_LIMIT_DATARATE ), + .NbChannels = AU915_MAX_NB_CHANNELS, + .ChannelsMask = RegionNvmGroup2->ChannelsMask, + .Channels = RegionNvmGroup2->Channels, + }; + phyParam.Value = RegionCommonGetNextLowerTxDr( &nextLowerTxDrParams ); + break; + } + case PHY_MAX_TX_POWER: + { + phyParam.Value = AU915_MAX_TX_POWER; + break; + } + case PHY_DEF_TX_POWER: + { + phyParam.Value = AU915_DEFAULT_TX_POWER; + break; + } + case PHY_DEF_ADR_ACK_LIMIT: + { + phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_LIMIT; + break; + } + case PHY_DEF_ADR_ACK_DELAY: + { + phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_DELAY; + break; + } + case PHY_MAX_PAYLOAD: + { + if( getPhy->UplinkDwellTime == 0 ) + { + phyParam.Value = MaxPayloadOfDatarateDwell0AU915[getPhy->Datarate]; + } + else + { + phyParam.Value = MaxPayloadOfDatarateDwell1AU915[getPhy->Datarate]; + } + break; + } + case PHY_DUTY_CYCLE: + { + phyParam.Value = AU915_DUTY_CYCLE_ENABLED; + break; + } + case PHY_MAX_RX_WINDOW: + { + phyParam.Value = AU915_MAX_RX_WINDOW; + break; + } + case PHY_RECEIVE_DELAY1: + { + phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY2; + break; + } + case PHY_RETRANSMIT_TIMEOUT: + { + phyParam.Value = ( REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT + randr( -REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND, REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND ) ); + break; + } + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = REGION_COMMON_DEFAULT_RX1_DR_OFFSET; + break; + } + case PHY_DEF_RX2_FREQUENCY: + { + phyParam.Value = AU915_RX_WND_2_FREQ; + break; + } + case PHY_DEF_RX2_DR: + { + phyParam.Value = AU915_RX_WND_2_DR; + break; + } + case PHY_CHANNELS_MASK: + { + phyParam.ChannelsMask = RegionNvmGroup2->ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = RegionNvmGroup2->ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = AU915_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = RegionNvmGroup2->Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + { + phyParam.Value = AU915_DEFAULT_UPLINK_DWELL_TIME; + break; + } + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = REGION_COMMON_DEFAULT_DOWNLINK_DWELL_TIME; + break; + } + case PHY_DEF_MAX_EIRP: + { + phyParam.fValue = AU915_DEFAULT_MAX_EIRP; + break; + } + case PHY_DEF_ANTENNA_GAIN: + { + phyParam.fValue = AU915_DEFAULT_ANTENNA_GAIN; + break; + } + case PHY_BEACON_CHANNEL_FREQ: + { + phyParam.Value = RegionBaseUSCalcDownlinkFrequency( getPhy->Channel, + AU915_BEACON_CHANNEL_FREQ, + AU915_BEACON_CHANNEL_STEPWIDTH ); + break; + } + case PHY_BEACON_FORMAT: + { + phyParam.BeaconFormat.BeaconSize = AU915_BEACON_SIZE; + phyParam.BeaconFormat.Rfu1Size = AU915_RFU1_SIZE; + phyParam.BeaconFormat.Rfu2Size = AU915_RFU2_SIZE; + break; + } + case PHY_BEACON_CHANNEL_DR: + { + phyParam.Value = AU915_BEACON_CHANNEL_DR; + break; + } + case PHY_BEACON_NB_CHANNELS: + { + phyParam.Value = AU915_BEACON_NB_CHANNELS; + break; + } + case PHY_PING_SLOT_CHANNEL_FREQ: + { + phyParam.Value = RegionBaseUSCalcDownlinkFrequency( getPhy->Channel, + AU915_PING_SLOT_CHANNEL_FREQ, + AU915_BEACON_CHANNEL_STEPWIDTH ); + break; + } + case PHY_PING_SLOT_CHANNEL_DR: + { + phyParam.Value = AU915_PING_SLOT_CHANNEL_DR; + break; + } + case PHY_PING_SLOT_NB_CHANNELS: + { + phyParam.Value = AU915_BEACON_NB_CHANNELS; + break; + } + case PHY_SF_FROM_DR: + { + phyParam.Value = DataratesAU915[getPhy->Datarate]; + break; + } + case PHY_BW_FROM_DR: + { + phyParam.Value = RegionCommonGetBandwidth( getPhy->Datarate, BandwidthsAU915 ); + break; + } + default: + { + break; + } + } + + return phyParam; +} + +void RegionAU915SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ + RegionCommonSetBandTxDone( &RegionBands[RegionNvmGroup2->Channels[txDone->Channel].Band], + txDone->LastTxAirTime, txDone->Joined, txDone->ElapsedTimeSinceStartUp ); +} + +void RegionAU915InitDefaults( InitDefaultsParams_t* params ) +{ + Band_t bands[AU915_MAX_NB_BANDS] = + { + AU915_BAND0 + }; + + switch( params->Type ) + { + case INIT_TYPE_DEFAULTS: + { + if( ( params->NvmGroup1 == NULL ) || ( params->NvmGroup2 == NULL ) ) + { + return; + } + + RegionNvmGroup1 = (RegionNvmDataGroup1_t*) params->NvmGroup1; + RegionNvmGroup2 = (RegionNvmDataGroup2_t*) params->NvmGroup2; + RegionBands = (Band_t*) params->Bands; + + // Initialize 8 bit channel groups index + RegionNvmGroup1->JoinChannelGroupsCurrentIndex = 0; + + // Initialize the join trials counter + RegionNvmGroup1->JoinTrialsCounter = 0; + + // Default bands + memcpy1( ( uint8_t* )RegionBands, ( uint8_t* )bands, sizeof( Band_t ) * AU915_MAX_NB_BANDS ); + + // Channels + for( uint8_t i = 0; i < AU915_MAX_NB_CHANNELS - 8; i++ ) + { + // 125 kHz channels + RegionNvmGroup2->Channels[i].Frequency = 915200000 + i * 200000; + RegionNvmGroup2->Channels[i].DrRange.Value = ( DR_5 << 4 ) | DR_0; + RegionNvmGroup2->Channels[i].Band = 0; + } + for( uint8_t i = AU915_MAX_NB_CHANNELS - 8; i < AU915_MAX_NB_CHANNELS; i++ ) + { + // 500 kHz channels + RegionNvmGroup2->Channels[i].Frequency = 915900000 + ( i - ( AU915_MAX_NB_CHANNELS - 8 ) ) * 1600000; + RegionNvmGroup2->Channels[i].DrRange.Value = ( DR_6 << 4 ) | DR_6; + RegionNvmGroup2->Channels[i].Band = 0; + } + + // Initialize channels default mask + RegionNvmGroup2->ChannelsDefaultMask[0] = 0xFFFF; + RegionNvmGroup2->ChannelsDefaultMask[1] = 0xFFFF; + RegionNvmGroup2->ChannelsDefaultMask[2] = 0xFFFF; + RegionNvmGroup2->ChannelsDefaultMask[3] = 0xFFFF; + RegionNvmGroup2->ChannelsDefaultMask[4] = 0x00FF; + RegionNvmGroup2->ChannelsDefaultMask[5] = 0x0000; + + // Copy channels default mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + + // Copy into channels mask remaining + RegionCommonChanMaskCopy( RegionNvmGroup1->ChannelsMaskRemaining, RegionNvmGroup2->ChannelsMask, CHANNELS_MASK_SIZE ); + break; + } + case INIT_TYPE_RESET_TO_DEFAULT_CHANNELS: + { + // Intentional fallthrough + } + case INIT_TYPE_ACTIVATE_DEFAULT_CHANNELS: + { + // Copy channels default mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + + for( uint8_t i = 0; i < CHANNELS_MASK_SIZE; i++ ) + { // Copy-And the channels mask + RegionNvmGroup1->ChannelsMaskRemaining[i] &= RegionNvmGroup2->ChannelsMask[i]; + } + break; + } + default: + { + break; + } + } +} + +bool RegionAU915Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( phyAttribute ) + { + case PHY_FREQUENCY: + { + return VerifyRfFreq( verify->Frequency ); + } + case PHY_TX_DR: + case PHY_DEF_TX_DR: + { + if( verify->DatarateParams.UplinkDwellTime == 0 ) + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, AU915_TX_MIN_DATARATE, AU915_TX_MAX_DATARATE ); + } + else + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, AU915_DWELL_LIMIT_DATARATE, AU915_TX_MAX_DATARATE ); + } + } + case PHY_RX_DR: + { + if( verify->DatarateParams.UplinkDwellTime == 0 ) + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, AU915_RX_MIN_DATARATE, AU915_RX_MAX_DATARATE ); + } + else + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, AU915_DWELL_LIMIT_DATARATE, AU915_RX_MAX_DATARATE ); + } + } + case PHY_DEF_TX_POWER: + case PHY_TX_POWER: + { + // Remark: switched min and max! + return RegionCommonValueInRange( verify->TxPower, AU915_MAX_TX_POWER, AU915_MIN_TX_POWER ); + } + case PHY_DUTY_CYCLE: + { + return AU915_DUTY_CYCLE_ENABLED; + } + default: + return false; + } +} + +void RegionAU915ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ + // Size of the optional CF list must be 16 byte + if( applyCFList->Size != 16 ) + { + return; + } + + // Last byte CFListType must be 0x01 to indicate the CFList contains a series of ChMask fields + if( applyCFList->Payload[15] != 0x01 ) + { + return; + } + + // ChMask0 - ChMask4 must be set (every ChMask has 16 bit) + for( uint8_t chMaskItr = 0, cntPayload = 0; chMaskItr <= 4; chMaskItr++, cntPayload+=2 ) + { + RegionNvmGroup2->ChannelsMask[chMaskItr] = (uint16_t) (0x00FF & applyCFList->Payload[cntPayload]); + RegionNvmGroup2->ChannelsMask[chMaskItr] |= (uint16_t) (applyCFList->Payload[cntPayload+1] << 8); + if( chMaskItr == 4 ) + { + RegionNvmGroup2->ChannelsMask[chMaskItr] = RegionNvmGroup2->ChannelsMask[chMaskItr] & CHANNELS_MASK_500KHZ_MASK; + } + // Set the channel mask to the remaining + RegionNvmGroup1->ChannelsMaskRemaining[chMaskItr] &= RegionNvmGroup2->ChannelsMask[chMaskItr]; + } +} + +bool RegionAU915ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, chanMaskSet->ChannelsMaskIn, CHANNELS_MASK_SIZE ); + + RegionNvmGroup2->ChannelsDefaultMask[4] = RegionNvmGroup2->ChannelsDefaultMask[4] & CHANNELS_MASK_500KHZ_MASK; + RegionNvmGroup2->ChannelsDefaultMask[5] = 0x0000; + + for( uint8_t i = 0; i < 6; i++ ) + { // Copy-And the channels mask + RegionNvmGroup1->ChannelsMaskRemaining[i] &= RegionNvmGroup2->ChannelsMask[i]; + } + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, CHANNELS_MASK_SIZE ); + break; + } + default: + return false; + } + return true; +} + +void RegionAU915ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + uint32_t tSymbolInUs = 0; + + // Get the datarate, perform a boundary check + rxConfigParams->Datarate = MIN( datarate, AU915_RX_MAX_DATARATE ); + rxConfigParams->Bandwidth = RegionCommonGetBandwidth( rxConfigParams->Datarate, BandwidthsAU915 ); + + tSymbolInUs = RegionCommonComputeSymbolTimeLoRa( DataratesAU915[rxConfigParams->Datarate], BandwidthsAU915[rxConfigParams->Datarate] ); + + RegionCommonComputeRxWindowParameters( tSymbolInUs, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +} + +bool RegionAU915RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ + int8_t dr = rxConfig->Datarate; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->RxSlot == RX_SLOT_WIN_1 ) + { + // Apply window 1 frequency + frequency = AU915_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 8 ) * AU915_STEPWIDTH_RX1_CHANNEL; + } + + // Read the physical datarate from the datarates table + phyDr = DataratesAU915[dr]; + + Radio.SetChannel( frequency ); + + // Radio configuration + Radio.SetRxConfig( MODEM_LORA, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous ); + + Radio.SetMaxPayloadLength( MODEM_LORA, MaxPayloadOfDatarateDwell0AU915[dr] + LORAMAC_FRAME_PAYLOAD_OVERHEAD_SIZE ); + + *datarate = (uint8_t) dr; + return true; +} + +bool RegionAU915TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + int8_t phyDr = DataratesAU915[txConfig->Datarate]; + int8_t txPowerLimited = RegionCommonLimitTxPower( txConfig->TxPower, RegionBands[RegionNvmGroup2->Channels[txConfig->Channel].Band].TxMaxPower ); + uint32_t bandwidth = RegionCommonGetBandwidth( txConfig->Datarate, BandwidthsAU915 ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( RegionNvmGroup2->Channels[txConfig->Channel].Frequency ); + + Radio.SetTxConfig( MODEM_LORA, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 4000 ); + + // Setup maximum payload lenght of the radio driver + Radio.SetMaxPayloadLength( MODEM_LORA, txConfig->PktLen ); + // Update time-on-air + *txTimeOnAir = GetTimeOnAir( txConfig->Datarate, txConfig->PktLen ); + + *txPower = txPowerLimited; + return true; +} + +uint8_t RegionAU915LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; + RegionCommonLinkAdrParams_t linkAdrParams = { 0 }; + uint8_t nextIndex = 0; + uint8_t bytesProcessed = 0; + uint16_t channelsMask[6] = { 0, 0, 0, 0, 0, 0 }; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams; + + // Initialize local copy of channels mask + RegionCommonChanMaskCopy( channelsMask, RegionNvmGroup2->ChannelsMask, 6 ); + + while( bytesProcessed < linkAdrReq->PayloadSize ) + { + nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); + + if( nextIndex == 0 ) + break; // break loop, since no more request has been found + + // Update bytes processed + bytesProcessed += nextIndex; + + // Revert status, as we only check the last ADR request for the channel mask KO + status = 0x07; + + if( linkAdrParams.ChMaskCtrl == 6 ) + { + // Enable all 125 kHz channels + channelsMask[0] = 0xFFFF; + channelsMask[1] = 0xFFFF; + channelsMask[2] = 0xFFFF; + channelsMask[3] = 0xFFFF; + // Apply chMask to channels 64 to 71 + channelsMask[4] = linkAdrParams.ChMask & CHANNELS_MASK_500KHZ_MASK; + } + else if( linkAdrParams.ChMaskCtrl == 7 ) + { + // Disable all 125 kHz channels + channelsMask[0] = 0x0000; + channelsMask[1] = 0x0000; + channelsMask[2] = 0x0000; + channelsMask[3] = 0x0000; + // Apply chMask to channels 64 to 71 + channelsMask[4] = linkAdrParams.ChMask & CHANNELS_MASK_500KHZ_MASK; + } + else if( linkAdrParams.ChMaskCtrl == 5 ) + { + // Start value for comparision + uint8_t bitMask = 1; + + // cntChannelMask for channelsMask[0] until channelsMask[3] + uint8_t cntChannelMask = 0; + + // i will be 1, 2, 3, ..., 7 + for( uint8_t i = 0; i <= 7; i++ ) + { + // 8 MSBs of ChMask are RFU + // Checking if the ChMask is set, then true + if( ( ( linkAdrParams.ChMask & 0x00FF ) & ( bitMask << i ) ) != 0 ) + { + if( ( i % 2 ) == 0 ) + { + // Enable a bank of 8 125kHz channels, 8 LSBs + channelsMask[cntChannelMask] |= 0x00FF; + // Enable the corresponding 500kHz channel + channelsMask[4] |= ( bitMask << i ); + } + else + { + // Enable a bank of 8 125kHz channels, 8 MSBs + channelsMask[cntChannelMask] |= 0xFF00; + // Enable the corresponding 500kHz channel + channelsMask[4] |= ( bitMask << i ); + // cntChannelMask increment for uneven i + cntChannelMask++; + } + } + // ChMask is not set + else + { + if( ( i % 2 ) == 0 ) + { + // Disable a bank of 8 125kHz channels, 8 LSBs + channelsMask[cntChannelMask] &= 0xFF00; + // Disable the corresponding 500kHz channel + channelsMask[4] &= ~( bitMask << i ); + } + else + { + // Enable a bank of 8 125kHz channels, 8 MSBs + channelsMask[cntChannelMask] &= 0x00FF; + // Disable the corresponding 500kHz channel + channelsMask[4] &= ~( bitMask << i ); + // cntChannelMask increment for uneven i + cntChannelMask++; + } + } + } + } + else + { + channelsMask[linkAdrParams.ChMaskCtrl] = linkAdrParams.ChMask; + } + } + + // FCC 15.247 paragraph F mandates to hop on at least 2 125 kHz channels + if( ( linkAdrParams.Datarate < DR_6 ) && ( RegionCommonCountChannels( channelsMask, 0, 4 ) < 2 ) ) + { + status &= 0xFE; // Channel mask KO + } + + // Get the minimum possible datarate + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; + phyParam = RegionAU915GetPhyParam( &getPhy ); + + linkAdrVerifyParams.Status = status; + linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; + linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; + linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; + linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; + linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; + linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; + linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; + linkAdrVerifyParams.NbChannels = AU915_MAX_NB_CHANNELS; + linkAdrVerifyParams.ChannelsMask = channelsMask; + linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value; + linkAdrVerifyParams.MaxDatarate = AU915_TX_MAX_DATARATE; + linkAdrVerifyParams.Channels = RegionNvmGroup2->Channels; + linkAdrVerifyParams.MinTxPower = AU915_MIN_TX_POWER; + linkAdrVerifyParams.MaxTxPower = AU915_MAX_TX_POWER; + linkAdrVerifyParams.Version = linkAdrReq->Version; + + // Verify the parameters and update, if necessary + status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep ); + + // Update channelsMask if everything is correct + if( status == 0x07 ) + { + // Copy Mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, channelsMask, 6 ); + + RegionNvmGroup1->ChannelsMaskRemaining[0] &= RegionNvmGroup2->ChannelsMask[0]; + RegionNvmGroup1->ChannelsMaskRemaining[1] &= RegionNvmGroup2->ChannelsMask[1]; + RegionNvmGroup1->ChannelsMaskRemaining[2] &= RegionNvmGroup2->ChannelsMask[2]; + RegionNvmGroup1->ChannelsMaskRemaining[3] &= RegionNvmGroup2->ChannelsMask[3]; + RegionNvmGroup1->ChannelsMaskRemaining[4] = RegionNvmGroup2->ChannelsMask[4]; + RegionNvmGroup1->ChannelsMaskRemaining[5] = RegionNvmGroup2->ChannelsMask[5]; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + + return status; +} + +uint8_t RegionAU915RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; + + // Verify radio frequency + if( VerifyRfFreq( rxParamSetupReq->Frequency ) == false ) + { + status &= 0xFE; // Channel frequency KO + } + + // Verify datarate + if( RegionCommonValueInRange( rxParamSetupReq->Datarate, AU915_RX_MIN_DATARATE, AU915_RX_MAX_DATARATE ) == false ) + { + status &= 0xFD; // Datarate KO + } + if( ( rxParamSetupReq->Datarate == DR_7 ) || + ( rxParamSetupReq->Datarate > DR_13 ) ) + { + status &= 0xFD; // Datarate KO + } + + // Verify datarate offset + if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, AU915_MIN_RX1_DR_OFFSET, AU915_MAX_RX1_DR_OFFSET ) == false ) + { + status &= 0xFB; // Rx1DrOffset range KO + } + + return status; +} + +int8_t RegionAU915NewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + // Do not accept the request + return -1; +} + +int8_t RegionAU915TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + // Accept the request + return 0; +} + +int8_t RegionAU915DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + // Do not accept the request + return -1; +} + +int8_t RegionAU915AlternateDr( int8_t currentDr, AlternateDrType_t type ) +{ + // Alternates the data rate according to the channel sequence: + // Eight times a 125kHz DR_2 and then one 500kHz DR_6 channel + if( type == ALTERNATE_DR ) + { + RegionNvmGroup1->JoinTrialsCounter++; + } + else + { + RegionNvmGroup1->JoinTrialsCounter--; + } + + if( RegionNvmGroup1->JoinTrialsCounter % 9 == 0 ) + { + // Use DR_6 every 9th times. + currentDr = DR_6; + } + else + { + currentDr = DR_2; + } + return currentDr; +} + +LoRaMacStatus_t RegionAU915NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t nbRestrictedChannels = 0; + uint8_t enabledChannels[AU915_MAX_NB_CHANNELS] = { 0 }; + RegionCommonIdentifyChannelsParam_t identifyChannelsParam; + RegionCommonCountNbOfEnabledChannelsParams_t countChannelsParams; + LoRaMacStatus_t status = LORAMAC_STATUS_NO_CHANNEL_FOUND; + + // Count 125kHz channels + if( RegionCommonCountChannels( RegionNvmGroup1->ChannelsMaskRemaining, 0, 4 ) == 0 ) + { // Reactivate default channels + RegionCommonChanMaskCopy( RegionNvmGroup1->ChannelsMaskRemaining, RegionNvmGroup2->ChannelsMask, 4 ); + + RegionNvmGroup1->JoinChannelGroupsCurrentIndex = 0; + } + // Check other channels + if( nextChanParams->Datarate >= DR_6 ) + { + if( ( RegionNvmGroup1->ChannelsMaskRemaining[4] & CHANNELS_MASK_500KHZ_MASK ) == 0 ) + { + RegionNvmGroup1->ChannelsMaskRemaining[4] = RegionNvmGroup2->ChannelsMask[4]; + } + } + + // Search how many channels are enabled + countChannelsParams.Joined = nextChanParams->Joined; + countChannelsParams.Datarate = nextChanParams->Datarate; + countChannelsParams.ChannelsMask = RegionNvmGroup1->ChannelsMaskRemaining; + countChannelsParams.Channels = RegionNvmGroup2->Channels; + countChannelsParams.Bands = RegionBands; + countChannelsParams.MaxNbChannels = AU915_MAX_NB_CHANNELS; + countChannelsParams.JoinChannels = NULL; + + identifyChannelsParam.AggrTimeOff = nextChanParams->AggrTimeOff; + identifyChannelsParam.LastAggrTx = nextChanParams->LastAggrTx; + identifyChannelsParam.DutyCycleEnabled = nextChanParams->DutyCycleEnabled; + identifyChannelsParam.MaxBands = AU915_MAX_NB_BANDS; + + identifyChannelsParam.ElapsedTimeSinceStartUp = nextChanParams->ElapsedTimeSinceStartUp; + identifyChannelsParam.LastTxIsJoinRequest = nextChanParams->LastTxIsJoinRequest; + identifyChannelsParam.ExpectedTimeOnAir = GetTimeOnAir( nextChanParams->Datarate, nextChanParams->PktLen ); + + identifyChannelsParam.CountNbOfEnabledChannelsParam = &countChannelsParams; + + status = RegionCommonIdentifyChannels( &identifyChannelsParam, aggregatedTimeOff, enabledChannels, + &nbEnabledChannels, &nbRestrictedChannels, time ); + + if( status == LORAMAC_STATUS_OK ) + { + if( nextChanParams->Joined == true ) + { + // Choose randomly on of the remaining channels + *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; + } + else + { + // For rapid network acquisition in mixed gateway channel plan environments, the device + // follow a random channel selection sequence. It probes alternating one out of a + // group of eight 125 kHz channels followed by probing one 500 kHz channel each pass. + // Each time a 125 kHz channel will be selected from another group. + + // 125kHz Channels (0 - 63) DR2 + if( nextChanParams->Datarate == DR_2 ) + { + if( RegionBaseUSComputeNext125kHzJoinChannel( ( uint16_t* ) RegionNvmGroup1->ChannelsMaskRemaining, + &RegionNvmGroup1->JoinChannelGroupsCurrentIndex, channel ) == LORAMAC_STATUS_PARAMETER_INVALID ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + } + // 500kHz Channels (64 - 71) DR6 + else + { + // Choose the next available channel + uint8_t i = 0; + while( ( ( RegionNvmGroup1->ChannelsMaskRemaining[4] & CHANNELS_MASK_500KHZ_MASK ) & ( 1 << i ) ) == 0 ) + { + i++; + } + *channel = 64 + i; + } + } + + // Disable the channel in the mask + RegionCommonChanDisable( RegionNvmGroup1->ChannelsMaskRemaining, *channel, AU915_MAX_NB_CHANNELS ); + } + return status; +} + +LoRaMacStatus_t RegionAU915ChannelAdd( ChannelAddParams_t* channelAdd ) +{ + return LORAMAC_STATUS_PARAMETER_INVALID; +} + +bool RegionAU915ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + return LORAMAC_STATUS_PARAMETER_INVALID; +} + +uint8_t RegionAU915ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + int8_t datarate = DatarateOffsetsAU915[dr][drOffset]; + + if( datarate < 0 ) + { + if( downlinkDwellTime == 0 ) + { + datarate = AU915_TX_MIN_DATARATE; + } + else + { + datarate = AU915_DWELL_LIMIT_DATARATE; + } + } + return datarate; +} + +void RegionAU915RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ) +{ + RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup; + + regionCommonRxBeaconSetup.Datarates = DataratesAU915; + regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency; + regionCommonRxBeaconSetup.BeaconSize = AU915_BEACON_SIZE; + regionCommonRxBeaconSetup.BeaconDatarate = AU915_BEACON_CHANNEL_DR; + regionCommonRxBeaconSetup.BeaconChannelBW = AU915_BEACON_CHANNEL_BW; + regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime; + regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout; + + RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup ); + + // Store downlink datarate + *outDr = AU915_BEACON_CHANNEL_DR; +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionAU915.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionAU915.h new file mode 100644 index 0000000000..00ac4fb96e --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionAU915.h @@ -0,0 +1,462 @@ +/*! + * \file RegionAU915.h + * + * \brief Region definition for AU915 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * \defgroup REGIONAU915 Region AU915 + * Implementation according to LoRaWAN Specification v1.0.2. + * \{ + */ +#ifndef __REGION_AU915_H__ +#define __REGION_AU915_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "region/Region.h" + +/*! + * LoRaMac maximum number of channels + */ +#define AU915_MAX_NB_CHANNELS 72 + +/*! + * Minimal datarate that can be used by the node + */ +#define AU915_TX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define AU915_TX_MAX_DATARATE DR_13 + +/*! + * Minimal datarate that can be used by the node + */ +#define AU915_RX_MIN_DATARATE DR_8 + +/*! + * Maximal datarate that can be used by the node + */ +#define AU915_RX_MAX_DATARATE DR_13 + +/*! + * Default datarate used by the node + */ +#define AU915_DEFAULT_DATARATE DR_2 + +/*! + * The minimum datarate which is used when the + * dwell time is limited. + */ +#define AU915_DWELL_LIMIT_DATARATE DR_2 + +/*! + * Minimal Rx1 receive datarate offset + */ +#define AU915_MIN_RX1_DR_OFFSET 0 + +/*! + * Maximal Rx1 receive datarate offset + */ +#define AU915_MAX_RX1_DR_OFFSET 5 + +/*! + * Minimal Tx output power that can be used by the node + */ +#define AU915_MIN_TX_POWER TX_POWER_14 + +/*! + * Maximal Tx output power that can be used by the node + */ +#define AU915_MAX_TX_POWER TX_POWER_0 + +/*! + * Default Tx output power used by the node + */ +#define AU915_DEFAULT_TX_POWER TX_POWER_0 + +/*! + * Default uplink dwell time configuration + */ +#define AU915_DEFAULT_UPLINK_DWELL_TIME 1 + +/*! + * Default Max EIRP + */ +#define AU915_DEFAULT_MAX_EIRP 30.0f + +/*! + * Default antenna gain + */ +#define AU915_DEFAULT_ANTENNA_GAIN 2.15f + +/*! + * Enabled or disabled the duty cycle + */ +#define AU915_DUTY_CYCLE_ENABLED 0 + +/*! + * Maximum RX window duration + */ +#define AU915_MAX_RX_WINDOW 3000 + +/*! + * Second reception window channel frequency definition. + */ +#define AU915_RX_WND_2_FREQ 923300000 + +/*! + * Second reception window channel datarate definition. + */ +#define AU915_RX_WND_2_DR DR_8 + +/* + * CLASS B + */ +/*! + * Beacon frequency + */ +#define AU915_BEACON_CHANNEL_FREQ 923300000 + +/*! + * Beacon frequency channel stepwidth + */ +#define AU915_BEACON_CHANNEL_STEPWIDTH 600000 + +/*! + * Ping slot channel frequency + */ +#define AU915_PING_SLOT_CHANNEL_FREQ 923300000 + +/*! + * Number of possible beacon channels + */ +#define AU915_BEACON_NB_CHANNELS 8 + +/*! + * Payload size of a beacon frame + */ +#define AU915_BEACON_SIZE 23 + +/*! + * Size of RFU 1 field + */ +#define AU915_RFU1_SIZE 4 + +/*! + * Size of RFU 2 field + */ +#define AU915_RFU2_SIZE 3 + +/*! + * Datarate of the beacon channel + */ +#define AU915_BEACON_CHANNEL_DR DR_8 + +/*! + * Bandwith of the beacon channel + */ +#define AU915_BEACON_CHANNEL_BW 2 + +/*! + * Ping slot channel datarate + */ +#define AU915_PING_SLOT_CHANNEL_DR DR_8 + +/*! + * LoRaMac maximum number of bands + */ +#define AU915_MAX_NB_BANDS 1 + +/*! + * Band 0 definition + * Band = { DutyCycle, TxMaxPower, LastBandUpdateTime, LastMaxCreditAssignTime, TimeCredits, MaxTimeCredits, ReadyForTransmission } + */ +#define AU915_BAND0 { 1, AU915_MAX_TX_POWER, 0, 0, 0, 0, 0 } // 100.0 % + +/*! + * Defines the first channel for RX window 1 for US band + */ +#define AU915_FIRST_RX1_CHANNEL ( (uint32_t) 923300000 ) + +/*! + * Defines the last channel for RX window 1 for US band + */ +#define AU915_LAST_RX1_CHANNEL ( (uint32_t) 927500000 ) + +/*! + * Defines the step width of the channels for RX window 1 + */ +#define AU915_STEPWIDTH_RX1_CHANNEL ( (uint32_t) 600000 ) + +/*! + * Data rates table definition + */ +static const uint8_t DataratesAU915[] = { 12, 11, 10, 9, 8, 7, 8, 0, 12, 11, 10, 9, 8, 7, 0, 0 }; + +/*! + * Bandwidths table definition in Hz + */ +static const uint32_t BandwidthsAU915[] = { 125000, 125000, 125000, 125000, 125000, 125000, 500000, 0, 500000, 500000, 500000, 500000, 500000, 500000, 0, 0 }; + +/*! + * Up/Down link data rates offset definition + */ +static const int8_t DatarateOffsetsAU915[7][6] = +{ + { DR_8 , DR_8 , DR_8 , DR_8 , DR_8 , DR_8 }, // DR_0 + { DR_9 , DR_8 , DR_8 , DR_8 , DR_8 , DR_8 }, // DR_1 + { DR_10, DR_9 , DR_8 , DR_8 , DR_8 , DR_8 }, // DR_2 + { DR_11, DR_10, DR_9 , DR_8 , DR_8 , DR_8 }, // DR_3 + { DR_12, DR_11, DR_10, DR_9 , DR_8 , DR_8 }, // DR_4 + { DR_13, DR_12, DR_11, DR_10, DR_9 , DR_8 }, // DR_5 + { DR_13, DR_13, DR_12, DR_11, DR_10, DR_9 }, // DR_6 +}; + +/*! + * Maximum payload with respect to the datarate index. + * The table is valid for the dwell time configuration of 0 for uplinks. + */ +static const uint8_t MaxPayloadOfDatarateDwell0AU915[] = { 51, 51, 51, 115, 242, 242, 242, 0, 53, 129, 242, 242, 242, 242 }; + +/*! + * Maximum payload with respect to the datarate index. + * The table is valid for the dwell time configuration of 1 for uplinks. + */ +static const uint8_t MaxPayloadOfDatarateDwell1AU915[] = { 0, 0, 11, 53, 125, 242, 242, 0, 53, 129, 242, 242, 242, 242 }; + +/*! + * \brief The function gets a value of a specific phy attribute. + * + * \param [IN] getPhy Pointer to the function parameters. + * + * \retval Returns a structure containing the PHY parameter. + */ +PhyParam_t RegionAU915GetPhyParam( GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionAU915SetBandTxDone( SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] type Sets the initialization type. + */ +void RegionAU915InitDefaults( InitDefaultsParams_t* params ); + +/*! + * \brief Verifies a parameter. + * + * \param [IN] verify Pointer to the function parameters. + * + * \param [IN] type Sets the initialization type. + * + * \retval Returns true, if the parameter is valid. + */ +bool RegionAU915Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + +/*! + * \brief The function parses the input buffer and sets up the channels of the + * CF list. + * + * \param [IN] applyCFList Pointer to the function parameters. + */ +void RegionAU915ApplyCFList( ApplyCFListParams_t* applyCFList ); + +/*! + * \brief Sets a channels mask. + * + * \param [IN] chanMaskSet Pointer to the function parameters. + * + * \retval Returns true, if the channels mask could be set. + */ +bool RegionAU915ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ); + +/*! + * Computes the Rx window timeout and offset. + * + * \param [IN] datarate Rx window datarate index to be used + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms + * interval around RxOffset + * + * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields. + */ +void RegionAU915ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ); + +/*! + * \brief Configuration of the RX windows. + * + * \param [IN] rxConfig Pointer to the function parameters. + * + * \param [OUT] datarate The datarate index which was set. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionAU915RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ); + +/*! + * \brief TX configuration. + * + * \param [IN] txConfig Pointer to the function parameters. + * + * \param [OUT] txPower The tx power index which was set. + * + * \param [OUT] txTimeOnAir The time-on-air of the frame. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionAU915TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ); + +/*! + * \brief The function processes a Link ADR Request. + * + * \param [IN] linkAdrReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionAU915LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ); + +/*! + * \brief The function processes a RX Parameter Setup Request. + * + * \param [IN] rxParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionAU915RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ); + +/*! + * \brief The function processes a Channel Request. + * + * \param [IN] newChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +int8_t RegionAU915NewChannelReq( NewChannelReqParams_t* newChannelReq ); + +/*! + * \brief The function processes a TX ParamSetup Request. + * + * \param [IN] txParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + * Returns -1, if the functionality is not implemented. In this case, the end node + * shall not process the command. + */ +int8_t RegionAU915TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ); + +/*! + * \brief The function processes a DlChannel Request. + * + * \param [IN] dlChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +int8_t RegionAU915DlChannelReq( DlChannelReqParams_t* dlChannelReq ); + +/*! + * \brief Alternates the datarate of the channel for the join request. + * + * \param [IN] currentDr Current datarate. + * + * \retval Datarate to apply. + */ +int8_t RegionAU915AlternateDr( int8_t currentDr, AlternateDrType_t type ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [OUT] channel Next channel to use for TX. + * + * \param [OUT] time Time to wait for the next transmission according to the duty + * cycle. + * + * \param [OUT] aggregatedTimeOff Updates the aggregated time off. + * + * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate] + */ +LoRaMacStatus_t RegionAU915NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ); + +/*! + * \brief Adds a channel. + * + * \param [IN] channelAdd Pointer to the function parameters. + * + * \retval Status of the operation. + */ +LoRaMacStatus_t RegionAU915ChannelAdd( ChannelAddParams_t* channelAdd ); + +/*! + * \brief Removes a channel. + * + * \param [IN] channelRemove Pointer to the function parameters. + * + * \retval Returns true, if the channel was removed successfully. + */ +bool RegionAU915ChannelsRemove( ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms + * + * \param [IN] dr Current datarate + * + * \param [IN] drOffset Offset to be applied + * + * \retval newDr Computed datarate. + */ +uint8_t RegionAU915ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! + * \brief Sets the radio into beacon reception mode + * + * \param [IN] rxBeaconSetup Pointer to the function parameters + */ + void RegionAU915RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ); + +/*! \} defgroup REGIONAU915 */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGION_AU915_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionBaseUS.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionBaseUS.c new file mode 100644 index 0000000000..2eacb69844 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionBaseUS.c @@ -0,0 +1,153 @@ +/*! + * \file RegionBaseUS.c + * + * \brief Implementations common with US region. + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Phanindra Kumar Yellapu ( STACKFORCE ) + */ +#include "LoRaMacTypes.h" +#include "region/Region.h" +#include "RegionBaseUS.h" + + +/*! + * \brief Searches for available 125 kHz channels in the given channel mask. + * + * \param [IN] currentChannelMaskLeft The remaining channel mask. + * + * \param [OUT] findAvailableChannelsIndex List containing the indexes of all available 125 kHz channels. + * + * \param [OUT] availableChannels Number of available 125 kHz channels. + * + * \retval Status + */ +static LoRaMacStatus_t FindAvailable125kHzChannels( uint16_t currentChannelMaskLeft, + uint8_t* findAvailableChannelsIndex, uint8_t* availableChannels ) +{ + // Nullpointer check + if( findAvailableChannelsIndex == NULL || availableChannels == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + // Initialize counter + *availableChannels = 0; + for( uint8_t i = 0; i < 8; i++ ) + { + // Find available channels + if( ( currentChannelMaskLeft & ( 1 << i ) ) != 0 ) + { + // Save available channel index + findAvailableChannelsIndex[*availableChannels] = i; + // Increment counter of available channels if the current channel is available + ( *availableChannels )++; + } + } + + return LORAMAC_STATUS_OK; +} + +LoRaMacStatus_t RegionBaseUSComputeNext125kHzJoinChannel( uint16_t* channelsMaskRemaining, + uint8_t* groupsCurrentIndex, uint8_t* newChannelIndex ) +{ + uint8_t currentChannelMaskLeftIndex; + uint16_t currentChannelMaskLeft; + uint8_t findAvailableChannelsIndex[8] = { 0 }; + uint8_t availableChannels = 0; + uint8_t startIndex; + + // Null pointer check + if( channelsMaskRemaining == NULL || groupsCurrentIndex == NULL || newChannelIndex == NULL ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + // copy the current index. + startIndex = *groupsCurrentIndex; + + do + { + // Current ChannelMaskRemaining, two groups per channel mask. For example Group 0 and 1 (8 bit) are ChannelMaskRemaining 0 (16 bit), etc. + currentChannelMaskLeftIndex = (uint8_t) startIndex / 2; + + // For even numbers we need the 8 LSBs and for uneven the 8 MSBs + if( ( startIndex % 2 ) == 0 ) + { + currentChannelMaskLeft = ( channelsMaskRemaining[currentChannelMaskLeftIndex] & 0x00FF ); + } + else + { + currentChannelMaskLeft = ( ( channelsMaskRemaining[currentChannelMaskLeftIndex] >> 8 ) & 0x00FF ); + } + + + if( FindAvailable125kHzChannels( currentChannelMaskLeft, findAvailableChannelsIndex, &availableChannels ) == LORAMAC_STATUS_PARAMETER_INVALID ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + if ( availableChannels > 0 ) + { + // Choose randomly a free channel 125kHz + *newChannelIndex = ( startIndex * 8 ) + findAvailableChannelsIndex[randr( 0, ( availableChannels - 1 ) )]; + } + + // Increment start index + startIndex++; + if ( startIndex > 7 ) + { + startIndex = 0; + } + } while( ( availableChannels == 0 ) && ( startIndex != *groupsCurrentIndex ) ); + + if ( availableChannels > 0 ) + { + *groupsCurrentIndex = startIndex; + return LORAMAC_STATUS_OK; + } + + return LORAMAC_STATUS_PARAMETER_INVALID; +} + +bool RegionBaseUSVerifyFrequencyGroup( uint32_t freq, uint32_t minFreq, uint32_t maxFreq, uint32_t stepwidth ) +{ + if( ( freq < minFreq ) || + ( freq > maxFreq ) || + ( ( ( freq - ( uint32_t ) minFreq ) % ( uint32_t ) stepwidth ) != 0 ) ) + { + return false; + } + return true; +} + +uint32_t RegionBaseUSCalcDownlinkFrequency( uint8_t channel, uint32_t frequency, + uint32_t stepwidth ) +{ + // Calculate the frequency + return frequency + ( channel * stepwidth ); +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionBaseUS.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionBaseUS.h new file mode 100644 index 0000000000..72edfb5b02 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionBaseUS.h @@ -0,0 +1,98 @@ +/*! + * \file RegionBaseUS.h + * + * \brief Implementations common with US region. + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Phanindra Kumar Yellapu ( STACKFORCE ) + * + * \defgroup REGIONBASEUS US region common implementations. + * \{ + */ +#ifndef __REGIONBASEUS_H__ +#define __REGIONBASEUS_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "LoRaMac.h" + +/*! + * \brief Computes the next 125kHz channel used for join requests. + * And it returns all the parameters updated. + * + * \param [IN] channelsMaskRemaining pointer to remaining channels. + * + * \param [IN] groupsCurrentIndex Index of current channel. + * + * \param [OUT] newChannelIndex Index of next available channel. + * + * \retval Status + */ +LoRaMacStatus_t RegionBaseUSComputeNext125kHzJoinChannel( uint16_t* channelsMaskRemaining, + uint8_t* groupsCurrentIndex, uint8_t* newChannelIndex ); + +/*! + * \brief Verifies if the frequency is in the correct range with a + * specific stepwidth. + * + * \param [IN] freq Frequency to verify. + * + * \param [IN] minFreq Minimum frequency. + * + * \param [IN] maxFreq Maximum frequency. + * + * \param [IN] stepwidth Frequency stepwidth. + * + * \retval True, if the frequency is valid, false if not. + */ +bool RegionBaseUSVerifyFrequencyGroup( uint32_t freq, uint32_t minFreq, uint32_t maxFreq, uint32_t stepwidth ); + +/*! + * \brief Calculates the downlink frequency for a given channel. This + * function is used in class B only. + * + * \param [IN] channel The channel according to the channel plan. + * + * \param [IN] frequency The base frequency. + * + * \param [IN] stepwidth The frequency stepwidth. + * + * \retval The downlink frequency. + */ +uint32_t RegionBaseUSCalcDownlinkFrequency( uint8_t channel, uint32_t frequency, + uint32_t stepwidth ); + +/*! \} defgroup REGIONBASEUS */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGIONBASEUS_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN470.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN470.c new file mode 100644 index 0000000000..9e18e5107f --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN470.c @@ -0,0 +1,1037 @@ +/*! + * \file RegionCN470.c + * + * \brief Region implementation for CN470 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) +*/ +#include "radio.h" +#include "RegionCommon.h" +#include "RegionCN470.h" +#include "RegionCN470A20.h" +#include "RegionCN470B20.h" +#include "RegionCN470A26.h" +#include "RegionCN470B26.h" + +// Definitions +#define CHANNELS_MASK_SIZE 6 + + +#ifndef REGION_CN470_DEFAULT_CHANNEL_PLAN +#define REGION_CN470_DEFAULT_CHANNEL_PLAN CHANNEL_PLAN_20MHZ_TYPE_A +#endif + +#ifndef REGION_CN470_DEFAULT_RX_WND_2_FREQ +#define REGION_CN470_DEFAULT_RX_WND_2_FREQ CN470_A20_RX_WND_2_FREQ_ABP +#endif + + +ChannelParams_t CommonJoinChannels[] = CN470_COMMON_JOIN_CHANNELS; + +/*! + * Definition of the regional channel plan. + */ +typedef struct sRegionCN470ChannelPlanCtx +{ + /*! + * Size of the channels mask. Must be smaller + * or equal than CHANNELS_MASK_SIZE. + */ + uint8_t ChannelsMaskSize; + /*! + * Number of elements in the join accept list. + */ + uint8_t JoinAcceptListSize; + /*! + * Number of available channels for beaconing. + */ + uint8_t NbBeaconChannels; + /*! + * Number of available channels for ping slots. + */ + uint8_t NbPingSlotChannels; + /*! + * \brief Calculation of the beacon frequency. + * + * \param [IN] channel The Beacon channel number. + * + * \param [IN] joinChannelIndex The join channel index. + * + * \param [IN] isPingSlot Set to true, if its a ping slot. + * + * \retval Returns the beacon frequency. + */ + uint32_t ( *GetDownlinkFrequency )( uint8_t channel, uint8_t joinChannelIndex, bool isPingSlot ); + /*! + * \brief Performs the update of the channelsMask based on the input parameters. + * + * \param [IN] joinChannelIndex The join channel index. + * + * \retval Returns the offset for the given join channel. + */ + uint8_t ( *GetBeaconChannelOffset )( uint8_t joinChannelIndex ); + /*! + * \brief Performs the update of the channelsMask based on the input parameters. + * + * \param [IN] channelsMask A pointer to the channels mask. + * + * \param [IN] chMaskCntl The value of the chMaskCntl field of the LinkAdrReq. + * + * \param [IN] chanMask The value of the chanMask field of the LinkAdrReq. + * + * \param [IN] channels A pointer to the available channels. + * + * \retval Status of the operation. Return 0x07 if the channels mask is valid. + */ + uint8_t ( *LinkAdrChMaskUpdate )( uint16_t* channelsMask, uint8_t chMaskCntl, + uint16_t chanMask, ChannelParams_t* channels ); + /*! + * \brief Verifies if the frequency provided is valid. + * + * \param [IN] frequency The frequency to verify. + * + * \retval Returns true, if the frequency is valid. + */ + bool ( *VerifyRfFreq )( uint32_t frequency ); + /*! + * \brief Initializes all channels, datarates, frequencies and bands. + * + * \param [IN] channels A pointer to the available channels. + */ + void ( *InitializeChannels )( ChannelParams_t* channels ); + /*! + * \brief Initializes the channels mask and the channels default mask. + * + * \param [IN] channelsDefaultMask A pointer to the channels default mask. + */ + void ( *InitializeChannelsMask )( uint16_t* channelsDefaultMask ); + /*! + * \brief Computes the frequency for the RX1 window. + * + * \param [IN] channel The channel utilized currently. + * + * \retval Returns the frequency which shall be used. + */ + uint32_t ( *GetRx1Frequency )( uint8_t channel ); + /*! + * \brief Computes the frequency for the RX2 window. + * + * \param [IN] joinChannelIndex The join channel index. + * + * \param [IN] isOtaaDevice Set to true, if the device is an OTAA device. + * + * \retval Returns the frequency which shall be used. + */ + uint32_t ( *GetRx2Frequency )( uint8_t joinChannelIndex, bool isOtaaDevice ); +}RegionCN470ChannelPlanCtx_t; + +/* + * Non-volatile module context. + */ +static RegionNvmDataGroup1_t* RegionNvmGroup1; +static RegionNvmDataGroup2_t* RegionNvmGroup2; +static Band_t* RegionBands; + +/* + * Context for the current channel plan. + */ +static RegionCN470ChannelPlanCtx_t ChannelPlanCtx; + +// Static functions +static void ApplyChannelPlanConfig( RegionCN470ChannelPlan_t channelPlan, RegionCN470ChannelPlanCtx_t* ctx ) +{ + switch( channelPlan ) + { + case CHANNEL_PLAN_20MHZ_TYPE_A: + { + ctx->ChannelsMaskSize = CN470_A20_CHANNELS_MASK_SIZE; + ctx->JoinAcceptListSize = CN470_A20_JOIN_ACCEPT_LIST_SIZE; + ctx->NbBeaconChannels = CN470_A20_BEACON_NB_CHANNELS; + ctx->NbPingSlotChannels = CN470_A20_PING_SLOT_NB_CHANNELS; + ctx->GetDownlinkFrequency = RegionCN470A20GetDownlinkFrequency; + ctx->GetBeaconChannelOffset = RegionCN470A20GetBeaconChannelOffset; + ctx->LinkAdrChMaskUpdate = RegionCN470A20LinkAdrChMaskUpdate; + ctx->VerifyRfFreq = RegionCN470A20VerifyRfFreq; + ctx->InitializeChannels = RegionCN470A20InitializeChannels; + ctx->InitializeChannelsMask = RegionCN470A20InitializeChannelsMask; + ctx->GetRx1Frequency = RegionCN470A20GetRx1Frequency; + ctx->GetRx2Frequency = RegionCN470A20GetRx2Frequency; + break; + } + case CHANNEL_PLAN_20MHZ_TYPE_B: + { + ctx->ChannelsMaskSize = CN470_B20_CHANNELS_MASK_SIZE; + ctx->JoinAcceptListSize = CN470_B20_JOIN_ACCEPT_LIST_SIZE; + ctx->NbBeaconChannels = CN470_B20_BEACON_NB_CHANNELS; + ctx->NbPingSlotChannels = CN470_B20_PING_SLOT_NB_CHANNELS; + ctx->GetDownlinkFrequency = RegionCN470B20GetDownlinkFrequency; + ctx->GetBeaconChannelOffset = RegionCN470B20GetBeaconChannelOffset; + ctx->LinkAdrChMaskUpdate = RegionCN470B20LinkAdrChMaskUpdate; + ctx->VerifyRfFreq = RegionCN470B20VerifyRfFreq; + ctx->InitializeChannels = RegionCN470B20InitializeChannels; + ctx->InitializeChannelsMask = RegionCN470B20InitializeChannelsMask; + ctx->GetRx1Frequency = RegionCN470B20GetRx1Frequency; + ctx->GetRx2Frequency = RegionCN470B20GetRx2Frequency; + break; + } + case CHANNEL_PLAN_26MHZ_TYPE_A: + { + ctx->ChannelsMaskSize = CN470_A26_CHANNELS_MASK_SIZE; + ctx->JoinAcceptListSize = CN470_A26_JOIN_ACCEPT_LIST_SIZE; + ctx->NbBeaconChannels = CN470_A26_BEACON_NB_CHANNELS; + ctx->NbPingSlotChannels = CN470_A26_PING_SLOT_NB_CHANNELS; + ctx->GetDownlinkFrequency = RegionCN470A26GetDownlinkFrequency; + ctx->GetBeaconChannelOffset = RegionCN470A26GetBeaconChannelOffset; + ctx->LinkAdrChMaskUpdate = RegionCN470A26LinkAdrChMaskUpdate; + ctx->VerifyRfFreq = RegionCN470A26VerifyRfFreq; + ctx->InitializeChannels = RegionCN470A26InitializeChannels; + ctx->InitializeChannelsMask = RegionCN470A26InitializeChannelsMask; + ctx->GetRx1Frequency = RegionCN470A26GetRx1Frequency; + ctx->GetRx2Frequency = RegionCN470A26GetRx2Frequency; + break; + } + case CHANNEL_PLAN_26MHZ_TYPE_B: + { + ctx->ChannelsMaskSize = CN470_B26_CHANNELS_MASK_SIZE; + ctx->JoinAcceptListSize = CN470_B26_JOIN_ACCEPT_LIST_SIZE; + ctx->NbBeaconChannels = CN470_B26_BEACON_NB_CHANNELS; + ctx->NbPingSlotChannels = CN470_B26_PING_SLOT_NB_CHANNELS; + ctx->GetDownlinkFrequency = RegionCN470B26GetDownlinkFrequency; + ctx->GetBeaconChannelOffset = RegionCN470B26GetBeaconChannelOffset; + ctx->LinkAdrChMaskUpdate = RegionCN470B26LinkAdrChMaskUpdate; + ctx->VerifyRfFreq = RegionCN470B26VerifyRfFreq; + ctx->InitializeChannels = RegionCN470B26InitializeChannels; + ctx->InitializeChannelsMask = RegionCN470B26InitializeChannelsMask; + ctx->GetRx1Frequency = RegionCN470B26GetRx1Frequency; + ctx->GetRx2Frequency = RegionCN470B26GetRx2Frequency; + break; + } + default: + { + // Apply CHANNEL_PLAN_20MHZ_TYPE_A + ctx->ChannelsMaskSize = CN470_A20_CHANNELS_MASK_SIZE; + ctx->JoinAcceptListSize = CN470_A20_JOIN_ACCEPT_LIST_SIZE; + ctx->NbBeaconChannels = CN470_A20_BEACON_NB_CHANNELS; + ctx->NbPingSlotChannels = CN470_A20_PING_SLOT_NB_CHANNELS; + ctx->GetDownlinkFrequency = RegionCN470A20GetDownlinkFrequency; + ctx->GetBeaconChannelOffset = RegionCN470A20GetBeaconChannelOffset; + ctx->LinkAdrChMaskUpdate = RegionCN470A20LinkAdrChMaskUpdate; + ctx->VerifyRfFreq = RegionCN470A20VerifyRfFreq; + ctx->InitializeChannels = RegionCN470A20InitializeChannels; + ctx->InitializeChannelsMask = RegionCN470A20InitializeChannelsMask; + ctx->GetRx1Frequency = RegionCN470A20GetRx1Frequency; + ctx->GetRx2Frequency = RegionCN470A20GetRx2Frequency; + break; + } + } +} + +static RegionCN470ChannelPlan_t IdentifyChannelPlan( uint8_t joinChannel ) +{ + RegionCN470ChannelPlan_t channelPlan = CHANNEL_PLAN_UNKNOWN; + + if( joinChannel <= 7 ) + { + channelPlan = CHANNEL_PLAN_20MHZ_TYPE_A; + } + else if ( ( joinChannel <= 9 ) && ( joinChannel >= 8 ) ) + { + channelPlan = CHANNEL_PLAN_20MHZ_TYPE_B; + } + else if ( ( joinChannel <= 14 ) && ( joinChannel >= 10 ) ) + { + channelPlan = CHANNEL_PLAN_26MHZ_TYPE_A; + } + else if( ( joinChannel <= 19 ) && ( joinChannel >= 15 ) ) + { + channelPlan = CHANNEL_PLAN_26MHZ_TYPE_B; + } + return channelPlan; +} + +static bool VerifyRfFreq( uint32_t frequency ) +{ + // Check radio driver support + if( Radio.CheckRfFrequency( frequency ) == false ) + { + return false; + } + + return ChannelPlanCtx.VerifyRfFreq( frequency ); +} + +static TimerTime_t GetTimeOnAir( int8_t datarate, uint16_t pktLen ) +{ + int8_t phyDr = DataratesCN470[datarate]; + uint32_t bandwidth = RegionCommonGetBandwidth( datarate, BandwidthsCN470 ); + + return Radio.TimeOnAir( MODEM_LORA, bandwidth, phyDr, 1, 8, false, pktLen, true ); +} + +PhyParam_t RegionCN470GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + + switch( getPhy->Attribute ) + { + case PHY_MIN_RX_DR: + { + phyParam.Value = CN470_RX_MIN_DATARATE; + break; + } + case PHY_MIN_TX_DR: + { + phyParam.Value = CN470_TX_MIN_DATARATE; + break; + } + case PHY_DEF_TX_DR: + { + phyParam.Value = CN470_DEFAULT_DATARATE; + break; + } + case PHY_NEXT_LOWER_TX_DR: + { + RegionCommonGetNextLowerTxDrParams_t nextLowerTxDrParams = + { + .CurrentDr = getPhy->Datarate, + .MaxDr = ( int8_t )CN470_TX_MAX_DATARATE, + .MinDr = ( int8_t )CN470_TX_MIN_DATARATE, + .NbChannels = CN470_MAX_NB_CHANNELS, + .ChannelsMask = RegionNvmGroup2->ChannelsMask, + .Channels = RegionNvmGroup2->Channels, + }; + phyParam.Value = RegionCommonGetNextLowerTxDr( &nextLowerTxDrParams ); + break; + } + case PHY_MAX_TX_POWER: + { + phyParam.Value = CN470_MAX_TX_POWER; + break; + } + case PHY_DEF_TX_POWER: + { + phyParam.Value = CN470_DEFAULT_TX_POWER; + break; + } + case PHY_DEF_ADR_ACK_LIMIT: + { + phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_LIMIT; + break; + } + case PHY_DEF_ADR_ACK_DELAY: + { + phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_DELAY; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateCN470[getPhy->Datarate]; + break; + } + case PHY_DUTY_CYCLE: + { + phyParam.Value = CN470_DUTY_CYCLE_ENABLED; + break; + } + case PHY_MAX_RX_WINDOW: + { + phyParam.Value = CN470_MAX_RX_WINDOW; + break; + } + case PHY_RECEIVE_DELAY1: + { + phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY2; + break; + } + case PHY_RETRANSMIT_TIMEOUT: + { + phyParam.Value = ( REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT + randr( -REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND, REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND ) ); + break; + } + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = REGION_COMMON_DEFAULT_RX1_DR_OFFSET; + break; + } + case PHY_DEF_RX2_FREQUENCY: + { + phyParam.Value = REGION_CN470_DEFAULT_RX_WND_2_FREQ; + + if( RegionNvmGroup2->ChannelPlan != CHANNEL_PLAN_UNKNOWN ) + { + phyParam.Value = ChannelPlanCtx.GetRx2Frequency( RegionNvmGroup2->CommonJoinChannelIndex, RegionNvmGroup2->IsOtaaDevice ); + } + break; + } + case PHY_DEF_RX2_DR: + { + phyParam.Value = CN470_RX_WND_2_DR; + break; + } + case PHY_CHANNELS_MASK: + { + phyParam.ChannelsMask = RegionNvmGroup2->ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = RegionNvmGroup2->ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = CN470_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = RegionNvmGroup2->Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + { + phyParam.Value = CN470_DEFAULT_UPLINK_DWELL_TIME; + break; + } + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = REGION_COMMON_DEFAULT_DOWNLINK_DWELL_TIME; + break; + } + case PHY_DEF_MAX_EIRP: + { + phyParam.fValue = CN470_DEFAULT_MAX_EIRP; + break; + } + case PHY_DEF_ANTENNA_GAIN: + { + phyParam.fValue = CN470_DEFAULT_ANTENNA_GAIN; + break; + } + case PHY_BEACON_CHANNEL_FREQ: + { + phyParam.Value = REGION_CN470_DEFAULT_RX_WND_2_FREQ; + + // Implementation depending on the join channel + if( RegionNvmGroup2->ChannelPlan != CHANNEL_PLAN_UNKNOWN ) + { + phyParam.Value = ChannelPlanCtx.GetDownlinkFrequency( getPhy->Channel, + RegionNvmGroup2->CommonJoinChannelIndex, + false ); + } + break; + } + case PHY_BEACON_FORMAT: + { + phyParam.BeaconFormat.BeaconSize = CN470_BEACON_SIZE; + phyParam.BeaconFormat.Rfu1Size = CN470_RFU1_SIZE; + phyParam.BeaconFormat.Rfu2Size = CN470_RFU2_SIZE; + break; + } + case PHY_BEACON_CHANNEL_DR: + { + phyParam.Value = CN470_BEACON_CHANNEL_DR; + break; + } + case PHY_BEACON_NB_CHANNELS: + { + // Implementation depending on the join channel + if( RegionNvmGroup2->ChannelPlan != CHANNEL_PLAN_UNKNOWN ) + { + phyParam.Value = ChannelPlanCtx.NbBeaconChannels; + } + break; + } + case PHY_BEACON_CHANNEL_OFFSET: + { + // Implementation depending on the join channel + if( RegionNvmGroup2->ChannelPlan != CHANNEL_PLAN_UNKNOWN ) + { + phyParam.Value = ChannelPlanCtx.GetBeaconChannelOffset( RegionNvmGroup2->CommonJoinChannelIndex ); + } + break; + } + case PHY_PING_SLOT_CHANNEL_FREQ: + { + phyParam.Value = REGION_CN470_DEFAULT_RX_WND_2_FREQ; + + // Implementation depending on the join channel + if( RegionNvmGroup2->ChannelPlan != CHANNEL_PLAN_UNKNOWN ) + { + phyParam.Value = ChannelPlanCtx.GetDownlinkFrequency( getPhy->Channel, + RegionNvmGroup2->CommonJoinChannelIndex, + true ); + } + break; + } + case PHY_PING_SLOT_CHANNEL_DR: + { + phyParam.Value = CN470_PING_SLOT_CHANNEL_DR; + break; + } + case PHY_PING_SLOT_NB_CHANNELS: + { + // Implementation depending on the join channel + if( RegionNvmGroup2->ChannelPlan != CHANNEL_PLAN_UNKNOWN ) + { + phyParam.Value = ChannelPlanCtx.NbPingSlotChannels; + } + break; + } + case PHY_SF_FROM_DR: + { + phyParam.Value = DataratesCN470[getPhy->Datarate]; + break; + } + case PHY_BW_FROM_DR: + { + phyParam.Value = RegionCommonGetBandwidth( getPhy->Datarate, BandwidthsCN470 ); + break; + } + default: + { + break; + } + } + + return phyParam; +} + +void RegionCN470SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ + RegionCommonSetBandTxDone( &RegionBands[RegionNvmGroup2->Channels[txDone->Channel].Band], + txDone->LastTxAirTime, txDone->Joined, txDone->ElapsedTimeSinceStartUp ); +} + +void RegionCN470InitDefaults( InitDefaultsParams_t* params ) +{ + Band_t bands[CN470_MAX_NB_BANDS] = + { + CN470_BAND0 + }; + + switch( params->Type ) + { + case INIT_TYPE_DEFAULTS: + { + if( ( params->NvmGroup1 == NULL ) || ( params->NvmGroup2 == NULL ) ) + { + return; + } + + RegionNvmGroup1 = (RegionNvmDataGroup1_t*) params->NvmGroup1; + RegionNvmGroup2 = (RegionNvmDataGroup2_t*) params->NvmGroup2; + RegionBands = (Band_t*) params->Bands; + + // Default bands + memcpy1( ( uint8_t* )RegionBands, ( uint8_t* )bands, sizeof( Band_t ) * CN470_MAX_NB_BANDS ); + + // 125 kHz channels + RegionNvmGroup2->ChannelPlan = REGION_CN470_DEFAULT_CHANNEL_PLAN; + RegionNvmGroup2->CommonJoinChannelIndex = 0; + RegionNvmGroup2->IsOtaaDevice = false; + + // Apply the channel plan configuration + ApplyChannelPlanConfig( RegionNvmGroup2->ChannelPlan, &ChannelPlanCtx ); + + // Default channels + ChannelPlanCtx.InitializeChannels( RegionNvmGroup2->Channels ); + + // Default ChannelsMask + ChannelPlanCtx.InitializeChannelsMask( RegionNvmGroup2->ChannelsDefaultMask ); + + // Copy channels default mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + + // Copy into channels mask remaining + RegionCommonChanMaskCopy( RegionNvmGroup1->ChannelsMaskRemaining, RegionNvmGroup2->ChannelsMask, CHANNELS_MASK_SIZE ); + break; + } + case INIT_TYPE_RESET_TO_DEFAULT_CHANNELS: + { + // Intentional fallthrough + } + case INIT_TYPE_ACTIVATE_DEFAULT_CHANNELS: + { + // Restore channels default mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + + for( uint8_t i = 0; i < CHANNELS_MASK_SIZE; i++ ) + { // Copy-And the channels mask + RegionNvmGroup1->ChannelsMaskRemaining[i] &= RegionNvmGroup2->ChannelsMask[i]; + } + break; + } + default: + { + break; + } + } +} + +bool RegionCN470Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( phyAttribute ) + { + case PHY_FREQUENCY: + { + return VerifyRfFreq( verify->Frequency ); + } + case PHY_TX_DR: + case PHY_DEF_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, CN470_TX_MIN_DATARATE, CN470_TX_MAX_DATARATE ); + } + case PHY_RX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, CN470_RX_MIN_DATARATE, CN470_RX_MAX_DATARATE ); + } + case PHY_DEF_TX_POWER: + case PHY_TX_POWER: + { + // Remark: switched min and max! + return RegionCommonValueInRange( verify->TxPower, CN470_MAX_TX_POWER, CN470_MIN_TX_POWER ); + } + case PHY_DUTY_CYCLE: + { + return CN470_DUTY_CYCLE_ENABLED; + } + default: + return false; + } +} + +void RegionCN470ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ + // Setup the channel plan based on the join channel + RegionNvmGroup2->CommonJoinChannelIndex = applyCFList->JoinChannel; + RegionNvmGroup2->IsOtaaDevice = true; + RegionNvmGroup2->ChannelPlan = IdentifyChannelPlan( RegionNvmGroup2->CommonJoinChannelIndex ); + + if( RegionNvmGroup2->ChannelPlan == CHANNEL_PLAN_UNKNOWN ) + { + // Invalid channel plan, fallback to default + RegionNvmGroup2->ChannelPlan = REGION_CN470_DEFAULT_CHANNEL_PLAN; + } + // Apply the configuration for the channel plan + ApplyChannelPlanConfig( RegionNvmGroup2->ChannelPlan, &ChannelPlanCtx ); + + // Size of the optional CF list must be 16 byte + if( applyCFList->Size != 16 ) + { + return; + } + + // Last byte CFListType must be 0x01 to indicate the CFList contains a series of ChMask fields + if( applyCFList->Payload[15] != 0x01 ) + { + return; + } + + // ChMask0 - ChMask5 must be set (every ChMask has 16 bit) + for( uint8_t chMaskItr = 0, cntPayload = 0; chMaskItr < ChannelPlanCtx.JoinAcceptListSize; chMaskItr++, cntPayload+=2 ) + { + RegionNvmGroup2->ChannelsMask[chMaskItr] = (uint16_t) (0x00FF & applyCFList->Payload[cntPayload]); + RegionNvmGroup2->ChannelsMask[chMaskItr] |= (uint16_t) (applyCFList->Payload[cntPayload+1] << 8); + + // Set the channel mask to the remaining + RegionNvmGroup1->ChannelsMaskRemaining[chMaskItr] &= RegionNvmGroup2->ChannelsMask[chMaskItr]; + } +} + +bool RegionCN470ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, chanMaskSet->ChannelsMaskIn, CHANNELS_MASK_SIZE ); + + for( uint8_t i = 0; i < CHANNELS_MASK_SIZE; i++ ) + { // Copy-And the channels mask + RegionNvmGroup1->ChannelsMaskRemaining[i] &= RegionNvmGroup2->ChannelsMask[i]; + } + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, CHANNELS_MASK_SIZE ); + break; + } + default: + return false; + } + return true; +} + +void RegionCN470ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + uint32_t tSymbolInUs = 0; + + // Get the datarate, perform a boundary check + rxConfigParams->Datarate = MIN( datarate, CN470_RX_MAX_DATARATE ); + rxConfigParams->Bandwidth = RegionCommonGetBandwidth( rxConfigParams->Datarate, BandwidthsCN470 ); + + tSymbolInUs = RegionCommonComputeSymbolTimeLoRa( DataratesCN470[rxConfigParams->Datarate], BandwidthsCN470[rxConfigParams->Datarate] ); + + RegionCommonComputeRxWindowParameters( tSymbolInUs, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +} + +bool RegionCN470RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ + int8_t dr = rxConfig->Datarate; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + // The RX configuration depends on whether the device has joined or not. + if( rxConfig->NetworkActivation != ACTIVATION_TYPE_NONE ) + { + // Update the downlink frequency in case of RX_SLOT_WIN_1 or RX_SLOT_WIN_2. + // Keep the frequency for all other cases. + if( rxConfig->RxSlot == RX_SLOT_WIN_1 ) + { + // Apply window 1 frequency + frequency = ChannelPlanCtx.GetRx1Frequency( rxConfig->Channel ); + } + else if( rxConfig->RxSlot == RX_SLOT_WIN_2 ) + { + // Apply window 2 frequency + frequency = ChannelPlanCtx.GetRx2Frequency( RegionNvmGroup2->CommonJoinChannelIndex, RegionNvmGroup2->IsOtaaDevice ); + } + } + else + { + // In this case, only RX_SLOT_WIN_1 and RX_SLOT_WIN_2 is possible. There is + // no need to verify it. The end device is not joined and is an OTAA device. + frequency = CommonJoinChannels[rxConfig->Channel].Rx1Frequency; + } + + // Read the physical datarate from the datarates table + phyDr = DataratesCN470[dr]; + + Radio.SetChannel( frequency ); + + // Radio configuration + Radio.SetRxConfig( MODEM_LORA, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous ); + + Radio.SetMaxPayloadLength( MODEM_LORA, MaxPayloadOfDatarateCN470[dr] + LORAMAC_FRAME_PAYLOAD_OVERHEAD_SIZE ); + + *datarate = (uint8_t) dr; + return true; +} + +bool RegionCN470TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + RadioModems_t modem; + int8_t phyDr = DataratesCN470[txConfig->Datarate]; + int8_t txPowerLimited = RegionCommonLimitTxPower( txConfig->TxPower, RegionBands[RegionNvmGroup2->Channels[txConfig->Channel].Band].TxMaxPower ); + uint32_t bandwidth = RegionCommonGetBandwidth( txConfig->Datarate, BandwidthsCN470 ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( RegionNvmGroup2->Channels[txConfig->Channel].Frequency ); + + if( txConfig->Datarate == DR_7 ) + { // High Speed FSK channel + modem = MODEM_FSK; + Radio.SetTxConfig( modem, phyTxPower, 25000, bandwidth, phyDr * 1000, 0, 5, false, true, 0, 0, false, 4000 ); + } + else + { + modem = MODEM_LORA; + Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 4000 ); + } + + // Setup maximum payload length of the radio driver + Radio.SetMaxPayloadLength( modem, txConfig->PktLen ); + // Update time-on-air + *txTimeOnAir = GetTimeOnAir( txConfig->Datarate, txConfig->PktLen ); + + *txPower = txPowerLimited; + + return true; +} + +uint8_t RegionCN470LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; + RegionCommonLinkAdrParams_t linkAdrParams = { 0 }; + uint8_t nextIndex = 0; + uint8_t bytesProcessed = 0; + uint16_t channelsMask[CHANNELS_MASK_SIZE] = { 0, 0, 0, 0, 0, 0 }; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams; + + // Initialize local copy of channels mask + RegionCommonChanMaskCopy( channelsMask, RegionNvmGroup2->ChannelsMask, CHANNELS_MASK_SIZE ); + + while( bytesProcessed < linkAdrReq->PayloadSize ) + { + // Get ADR request parameters + nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); + + if( nextIndex == 0 ) + break; // break loop, since no more request has been found + + // Update bytes processed + bytesProcessed += nextIndex; + + // Update the channel plan + status = ChannelPlanCtx.LinkAdrChMaskUpdate( channelsMask, linkAdrParams.ChMaskCtrl, + linkAdrParams.ChMask, RegionNvmGroup2->Channels ); + } + + // Make sure at least one channel is active + if( RegionCommonCountChannels( channelsMask, 0, ChannelPlanCtx.ChannelsMaskSize ) == 0 ) + { + status &= 0xFE; // Channel mask KO + } + + // Get the minimum possible datarate + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; + phyParam = RegionCN470GetPhyParam( &getPhy ); + + linkAdrVerifyParams.Status = status; + linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; + linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; + linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; + linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; + linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; + linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; + linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; + linkAdrVerifyParams.NbChannels = CN470_MAX_NB_CHANNELS; + linkAdrVerifyParams.ChannelsMask = channelsMask; + linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value; + linkAdrVerifyParams.MaxDatarate = CN470_TX_MAX_DATARATE; + linkAdrVerifyParams.Channels = RegionNvmGroup2->Channels; + linkAdrVerifyParams.MinTxPower = CN470_MIN_TX_POWER; + linkAdrVerifyParams.MaxTxPower = CN470_MAX_TX_POWER; + linkAdrVerifyParams.Version = linkAdrReq->Version; + + // Verify the parameters and update, if necessary + status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep ); + + // Update channelsMask if everything is correct + if( status == 0x07 ) + { + // Copy Mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, channelsMask, CHANNELS_MASK_SIZE ); + + RegionNvmGroup1->ChannelsMaskRemaining[0] &= RegionNvmGroup2->ChannelsMask[0]; + RegionNvmGroup1->ChannelsMaskRemaining[1] &= RegionNvmGroup2->ChannelsMask[1]; + RegionNvmGroup1->ChannelsMaskRemaining[2] &= RegionNvmGroup2->ChannelsMask[2]; + RegionNvmGroup1->ChannelsMaskRemaining[3] &= RegionNvmGroup2->ChannelsMask[3]; + RegionNvmGroup1->ChannelsMaskRemaining[4] = RegionNvmGroup2->ChannelsMask[4]; + RegionNvmGroup1->ChannelsMaskRemaining[5] = RegionNvmGroup2->ChannelsMask[5]; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + + return status; +} + +uint8_t RegionCN470RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; + + // Verify radio frequency + if( VerifyRfFreq( rxParamSetupReq->Frequency ) == false ) + { + status &= 0xFE; // Channel frequency KO + } + + // Verify datarate + if( RegionCommonValueInRange( rxParamSetupReq->Datarate, CN470_RX_MIN_DATARATE, CN470_RX_MAX_DATARATE ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify datarate offset + if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, CN470_MIN_RX1_DR_OFFSET, CN470_MAX_RX1_DR_OFFSET ) == false ) + { + status &= 0xFB; // Rx1DrOffset range KO + } + + return status; +} + +int8_t RegionCN470NewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + // Do not accept the request + return -1; +} + +int8_t RegionCN470TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + // Do not accept the request + return -1; +} + +int8_t RegionCN470DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + // Do not accept the request + return -1; +} + +int8_t RegionCN470AlternateDr( int8_t currentDr, AlternateDrType_t type ) +{ + return currentDr; +} + +LoRaMacStatus_t RegionCN470NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t nbRestrictedChannels = 0; + uint8_t enabledChannels[CN470_MAX_NB_CHANNELS] = { 0 }; + uint16_t joinChannelsMask[2] = CN470_JOIN_CHANNELS; + RegionCommonIdentifyChannelsParam_t identifyChannelsParam; + RegionCommonCountNbOfEnabledChannelsParams_t countChannelsParams; + LoRaMacStatus_t status = LORAMAC_STATUS_NO_CHANNEL_FOUND; + + // Count 125kHz channels + if( RegionCommonCountChannels( RegionNvmGroup1->ChannelsMaskRemaining, 0, ChannelPlanCtx.ChannelsMaskSize ) == 0 ) + { // Reactivate default channels + RegionNvmGroup2->ChannelsMask[0] = 0xFFFF; + RegionNvmGroup2->ChannelsMask[1] = 0xFFFF; + RegionNvmGroup2->ChannelsMask[2] = 0xFFFF; + RegionNvmGroup2->ChannelsMask[3] = 0xFFFF; + RegionNvmGroup2->ChannelsMask[4] = 0xFFFF; + RegionNvmGroup2->ChannelsMask[5] = 0xFFFF; + RegionCommonChanMaskCopy( RegionNvmGroup1->ChannelsMaskRemaining, RegionNvmGroup2->ChannelsMask, ChannelPlanCtx.ChannelsMaskSize ); + } + + // Search how many channels are enabled + countChannelsParams.Joined = nextChanParams->Joined; + countChannelsParams.Datarate = nextChanParams->Datarate; + countChannelsParams.ChannelsMask = RegionNvmGroup1->ChannelsMaskRemaining; + countChannelsParams.Channels = RegionNvmGroup2->Channels; + countChannelsParams.Bands = RegionBands; + countChannelsParams.MaxNbChannels = CN470_MAX_NB_CHANNELS; + countChannelsParams.JoinChannels = NULL; + + // Apply a different channel selection if the device is not joined yet + // In this case the device shall not follow the individual channel plans for the + // different type, but instead shall follow the common join channel plan. + if( countChannelsParams.Joined == false ) + { + countChannelsParams.ChannelsMask = joinChannelsMask; + countChannelsParams.Channels = CommonJoinChannels; + countChannelsParams.MaxNbChannels = CN470_COMMON_JOIN_CHANNELS_SIZE; + countChannelsParams.JoinChannels = joinChannelsMask; + } + + identifyChannelsParam.AggrTimeOff = nextChanParams->AggrTimeOff; + identifyChannelsParam.LastAggrTx = nextChanParams->LastAggrTx; + identifyChannelsParam.DutyCycleEnabled = nextChanParams->DutyCycleEnabled; + identifyChannelsParam.MaxBands = CN470_MAX_NB_BANDS; + + identifyChannelsParam.ElapsedTimeSinceStartUp = nextChanParams->ElapsedTimeSinceStartUp; + identifyChannelsParam.LastTxIsJoinRequest = nextChanParams->LastTxIsJoinRequest; + identifyChannelsParam.ExpectedTimeOnAir = GetTimeOnAir( nextChanParams->Datarate, nextChanParams->PktLen ); + + identifyChannelsParam.CountNbOfEnabledChannelsParam = &countChannelsParams; + + status = RegionCommonIdentifyChannels( &identifyChannelsParam, aggregatedTimeOff, enabledChannels, + &nbEnabledChannels, &nbRestrictedChannels, time ); + + if( status == LORAMAC_STATUS_OK ) + { + // We found a valid channel. Selection is random. + *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; + + // Disable the channel in the mask + RegionCommonChanDisable( RegionNvmGroup1->ChannelsMaskRemaining, *channel, ChannelPlanCtx.ChannelsMaskSize ); + } + return status; +} + +LoRaMacStatus_t RegionCN470ChannelAdd( ChannelAddParams_t* channelAdd ) +{ + return LORAMAC_STATUS_PARAMETER_INVALID; +} + +bool RegionCN470ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + return LORAMAC_STATUS_PARAMETER_INVALID; +} + +uint8_t RegionCN470ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + int8_t datarate = DatarateOffsetsCN470[dr][drOffset]; + + if( datarate < 0 ) + { + datarate = DR_0; + } + return datarate; +} + +void RegionCN470RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ) +{ + RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup; + + regionCommonRxBeaconSetup.Datarates = DataratesCN470; + regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency; + regionCommonRxBeaconSetup.BeaconSize = CN470_BEACON_SIZE; + regionCommonRxBeaconSetup.BeaconDatarate = CN470_BEACON_CHANNEL_DR; + regionCommonRxBeaconSetup.BeaconChannelBW = CN470_BEACON_CHANNEL_BW; + regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime; + regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout; + + RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup ); + + // Store downlink datarate + *outDr = CN470_BEACON_CHANNEL_DR; +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN470.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN470.h new file mode 100644 index 0000000000..b45c063f98 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN470.h @@ -0,0 +1,459 @@ +/*! + * \file RegionCN470.h + * + * \brief Region definition for CN470 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * \defgroup REGIONCN470 Region CN470 + * Implementation according to LoRaWAN Specification v1.0.2. + * \{ + */ +#ifndef __REGION_CN470_H__ +#define __REGION_CN470_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "region/Region.h" + +/*! + * LoRaMac maximum number of channels + */ +#define CN470_MAX_NB_CHANNELS 96 + +/*! + * Minimal datarate that can be used by the node + */ +#define CN470_TX_MIN_DATARATE DR_1 + +/*! + * Maximal datarate that can be used by the node + */ +#define CN470_TX_MAX_DATARATE DR_5 + +/*! + * Minimal datarate that can be used by the node + */ +#define CN470_RX_MIN_DATARATE DR_1 + +/*! + * Maximal datarate that can be used by the node + */ +#define CN470_RX_MAX_DATARATE DR_5 + +/*! + * Default datarate used by the node + */ +#define CN470_DEFAULT_DATARATE DR_1 + +/*! + * Minimal Rx1 receive datarate offset + */ +#define CN470_MIN_RX1_DR_OFFSET 0 + +/*! + * Maximal Rx1 receive datarate offset + */ +#define CN470_MAX_RX1_DR_OFFSET 5 + +/*! + * Minimal Tx output power that can be used by the node + */ +#define CN470_MIN_TX_POWER TX_POWER_7 + +/*! + * Maximal Tx output power that can be used by the node + */ +#define CN470_MAX_TX_POWER TX_POWER_0 + +/*! + * Default Tx output power used by the node + */ +#define CN470_DEFAULT_TX_POWER TX_POWER_0 + +/*! + * Default Max EIRP + */ +#define CN470_DEFAULT_MAX_EIRP 19.15f + +/*! + * Default antenna gain + */ +#define CN470_DEFAULT_ANTENNA_GAIN 2.15f + +/*! + * Enabled or disabled the duty cycle + */ +#define CN470_DUTY_CYCLE_ENABLED 0 + +/*! + * Maximum RX window duration + */ +#define CN470_MAX_RX_WINDOW 3000 + +/*! + * Second reception window channel datarate definition. + */ +#define CN470_RX_WND_2_DR DR_1 + +/*! + * Default uplink dwell time configuration + */ +#define CN470_DEFAULT_UPLINK_DWELL_TIME 0 + +/* + * CLASS B + */ + +/*! + * Payload size of a beacon frame + */ +#define CN470_BEACON_SIZE 19 + +/*! + * Size of RFU 1 field + */ +#define CN470_RFU1_SIZE 2 + +/*! + * Size of RFU 2 field + */ +#define CN470_RFU2_SIZE 1 + +/*! + * Datarate of the beacon channel + */ +#define CN470_BEACON_CHANNEL_DR DR_2 + +/*! + * Bandwith of the beacon channel + */ +#define CN470_BEACON_CHANNEL_BW 0 + +/*! + * Ping slot channel datarate + */ +#define CN470_PING_SLOT_CHANNEL_DR DR_2 + +/*! + * LoRaMac maximum number of bands + */ +#define CN470_MAX_NB_BANDS 1 + +/*! + * Band 0 definition + * Band = { DutyCycle, TxMaxPower, LastBandUpdateTime, LastMaxCreditAssignTime, TimeCredits, MaxTimeCredits, ReadyForTransmission } + */ +#define CN470_BAND0 { 1, CN470_MAX_TX_POWER, 0, 0, 0, 0, 0 } // 100.0 % + +/*! + * Defines the first channel for RX window 1 for CN470 band + */ +#define CN470_FIRST_RX1_CHANNEL ( (uint32_t) 500300000 ) + +/*! + * Defines the last channel for RX window 1 for CN470 band + */ +#define CN470_LAST_RX1_CHANNEL ( (uint32_t) 509700000 ) + +/*! + * Defines the step width of the channels for RX window 1 + */ +#define CN470_STEPWIDTH_RX1_CHANNEL ( (uint32_t) 200000 ) + +#define CN470_DEFAULT_DR_RANGE { .Value = ( CN470_TX_MAX_DATARATE << 4 ) | CN470_TX_MIN_DATARATE } + +#define CN470_COMMON_JOIN_CHANNELS \ +{ \ + { .Frequency = 470900000, .Rx1Frequency = 484500000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ + { .Frequency = 472500000, .Rx1Frequency = 486100000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ + { .Frequency = 474100000, .Rx1Frequency = 487700000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ + { .Frequency = 475700000, .Rx1Frequency = 489300000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ + { .Frequency = 504100000, .Rx1Frequency = 490900000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ + { .Frequency = 505700000, .Rx1Frequency = 492500000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ + { .Frequency = 507300000, .Rx1Frequency = 494100000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ + { .Frequency = 508900000, .Rx1Frequency = 495700000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ +\ + { .Frequency = 479900000, .Rx1Frequency = 479900000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ + { .Frequency = 499900000, .Rx1Frequency = 499900000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ +\ + { .Frequency = 470300000, .Rx1Frequency = 492500000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ + { .Frequency = 472300000, .Rx1Frequency = 492500000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ + { .Frequency = 474300000, .Rx1Frequency = 492500000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ + { .Frequency = 476300000, .Rx1Frequency = 492500000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ + { .Frequency = 478300000, .Rx1Frequency = 492500000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ +\ + { .Frequency = 480300000, .Rx1Frequency = 502500000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ + { .Frequency = 482300000, .Rx1Frequency = 502500000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ + { .Frequency = 484300000, .Rx1Frequency = 502500000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ + { .Frequency = 486300000, .Rx1Frequency = 502500000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ + { .Frequency = 488300000, .Rx1Frequency = 502500000, CN470_DEFAULT_DR_RANGE, .Band = 0 }, \ +} + +#define CN470_COMMON_JOIN_CHANNELS_SIZE 20 + +#define CN470_JOIN_CHANNELS { 0xFFFF, 0x000F } + +/*! + * Data rates table definition + */ +static const uint8_t DataratesCN470[] = { 12, 11, 10, 9, 8, 7, 7, 50 }; + +/*! + * Bandwidths table definition in Hz + */ +static const uint32_t BandwidthsCN470[] = { 125000, 125000, 125000, 125000, 125000, 125000, 500000, 0 }; + +/*! + * Up/Down link data rates offset definition + */ +static const int8_t DatarateOffsetsCN470[8][6] = +{ + { DR_0, DR_0, DR_0, DR_0, DR_0, DR_0 }, // DR_0 + { DR_1, DR_1, DR_1, DR_1, DR_1, DR_1 }, // DR_1 + { DR_2, DR_1, DR_1, DR_1, DR_1, DR_1 }, // DR_2 + { DR_3, DR_2, DR_1, DR_1, DR_1, DR_1 }, // DR_3 + { DR_4, DR_3, DR_2, DR_1, DR_1, DR_1 }, // DR_4 + { DR_5, DR_4, DR_3, DR_2, DR_1, DR_1 }, // DR_5 + { DR_6, DR_5, DR_4, DR_3, DR_2, DR_1 }, // DR_6 + { DR_7, DR_6, DR_5, DR_4, DR_3, DR_2 }, // DR_7 +}; + +/*! + * Maximum payload with respect to the datarate index. Cannot operate with repeater. + */ +static const uint8_t MaxPayloadOfDatarateCN470[] = { 0, 23, 86, 184, 242, 242, 242, 242 }; + +/*! + * \brief The function gets a value of a specific phy attribute. + * + * \param [IN] getPhy Pointer to the function parameters. + * + * \retval Returns a structure containing the PHY parameter. + */ +PhyParam_t RegionCN470GetPhyParam( GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionCN470SetBandTxDone( SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] type Sets the initialization type. + */ +void RegionCN470InitDefaults( InitDefaultsParams_t* params ); + +/*! + * \brief Verifies a parameter. + * + * \param [IN] verify Pointer to the function parameters. + * + * \param [IN] type Sets the initialization type. + * + * \retval Returns true, if the parameter is valid. + */ +bool RegionCN470Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + +/*! + * \brief The function parses the input buffer and sets up the channels of the + * CF list. + * + * \param [IN] applyCFList Pointer to the function parameters. + */ +void RegionCN470ApplyCFList( ApplyCFListParams_t* applyCFList ); + +/*! + * \brief Sets a channels mask. + * + * \param [IN] chanMaskSet Pointer to the function parameters. + * + * \retval Returns true, if the channels mask could be set. + */ +bool RegionCN470ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ); + +/*! + * Computes the Rx window timeout and offset. + * + * \param [IN] datarate Rx window datarate index to be used + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms + * interval around RxOffset + * + * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields. + */ +void RegionCN470ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ); + +/*! + * \brief Configuration of the RX windows. + * + * \param [IN] rxConfig Pointer to the function parameters. + * + * \param [OUT] datarate The datarate index which was set. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionCN470RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ); + +/*! + * \brief TX configuration. + * + * \param [IN] txConfig Pointer to the function parameters. + * + * \param [OUT] txPower The tx power index which was set. + * + * \param [OUT] txTimeOnAir The time-on-air of the frame. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionCN470TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ); + +/*! + * \brief The function processes a Link ADR Request. + * + * \param [IN] linkAdrReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionCN470LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ); + +/*! + * \brief The function processes a RX Parameter Setup Request. + * + * \param [IN] rxParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionCN470RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ); + +/*! + * \brief The function processes a Channel Request. + * + * \param [IN] newChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +int8_t RegionCN470NewChannelReq( NewChannelReqParams_t* newChannelReq ); + +/*! + * \brief The function processes a TX ParamSetup Request. + * + * \param [IN] txParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + * Returns -1, if the functionality is not implemented. In this case, the end node + * shall not process the command. + */ +int8_t RegionCN470TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ); + +/*! + * \brief The function processes a DlChannel Request. + * + * \param [IN] dlChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +int8_t RegionCN470DlChannelReq( DlChannelReqParams_t* dlChannelReq ); + +/*! + * \brief Alternates the datarate of the channel for the join request. + * + * \param [IN] currentDr Current datarate. + * + * \retval Datarate to apply. + */ +int8_t RegionCN470AlternateDr( int8_t currentDr, AlternateDrType_t type ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [OUT] channel Next channel to use for TX. + * + * \param [OUT] time Time to wait for the next transmission according to the duty + * cycle. + * + * \param [OUT] aggregatedTimeOff Updates the aggregated time off. + * + * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate] + */ +LoRaMacStatus_t RegionCN470NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ); + +/*! + * \brief Adds a channel. + * + * \param [IN] channelAdd Pointer to the function parameters. + * + * \retval Status of the operation. + */ +LoRaMacStatus_t RegionCN470ChannelAdd( ChannelAddParams_t* channelAdd ); + +/*! + * \brief Removes a channel. + * + * \param [IN] channelRemove Pointer to the function parameters. + * + * \retval Returns true, if the channel was removed successfully. + */ +bool RegionCN470ChannelsRemove( ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms + * + * \param [IN] dr Current datarate + * + * \param [IN] drOffset Offset to be applied + * + * \retval newDr Computed datarate. + */ +uint8_t RegionCN470ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! + * \brief Sets the radio into beacon reception mode + * + * \param [IN] rxBeaconSetup Pointer to the function parameters + */ + void RegionCN470RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ); + +/*! \} defgroup REGIONCN470 */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGION_CN470_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN470A20.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN470A20.c new file mode 100644 index 0000000000..690c4c6868 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN470A20.c @@ -0,0 +1,158 @@ +/*! + * \file RegionCN470A20.c + * + * \brief + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \defgroup REGIONCN470 + */ +#include "RegionCN470.h" +#include "RegionBaseUS.h" +#include "RegionCN470A20.h" + +uint32_t RegionCN470A20GetDownlinkFrequency( uint8_t channel, uint8_t joinChannelIndex, bool isPingSlot ) +{ + return RegionCN470A20GetRx1Frequency( channel ); +} + +uint8_t RegionCN470A20GetBeaconChannelOffset( uint8_t joinChannelIndex ) +{ + return ( joinChannelIndex * 8 ); +} + +uint8_t RegionCN470A20LinkAdrChMaskUpdate( uint16_t* channelsMask, uint8_t chMaskCntl, + uint16_t chanMask, ChannelParams_t* channels ) +{ + uint8_t status = 0x07; + + if( ( chMaskCntl == 4 ) || ( chMaskCntl == 5 ) ) + { + // RFU + status &= 0xFE; // Channel mask KO + } + else if( chMaskCntl == 6 ) + { + // Enable all channels + channelsMask[0] = 0xFFFF; + channelsMask[1] = 0xFFFF; + channelsMask[2] = 0xFFFF; + channelsMask[3] = 0xFFFF; + channelsMask[4] = 0x0000; + channelsMask[5] = 0x0000; + } + else if( chMaskCntl == 7 ) + { + // Disable all channels + channelsMask[0] = 0x0000; + channelsMask[1] = 0x0000; + channelsMask[2] = 0x0000; + channelsMask[3] = 0x0000; + channelsMask[4] = 0x0000; + channelsMask[5] = 0x0000; + } + else + { + // chMaskCntl 0 to 3 + for( uint8_t i = 0; i < 16; i++ ) + { + if( ( ( chanMask & ( 1 << i ) ) != 0 ) && + ( channels[chMaskCntl * 16 + i].Frequency == 0 ) ) + {// Trying to enable an undefined channel + status &= 0xFE; // Channel mask KO + } + } + channelsMask[chMaskCntl] = chanMask; + } + return status; +} + +bool RegionCN470A20VerifyRfFreq( uint32_t freq ) +{ + // Downstream group 1 and 2 + if( RegionBaseUSVerifyFrequencyGroup( freq, CN470_A20_FIRST_RX_CHANNEL, + CN470_A20_LAST_RX_CHANNEL, + CN470_A20_STEPWIDTH_RX_CHANNEL ) == false ) + { + return false; + } + return true; +} + +void RegionCN470A20InitializeChannels( ChannelParams_t* channels ) +{ + // Upstream group 1 + for( uint8_t i = 0; i < 32; i++ ) + { + channels[i].Frequency = CN470_A20_FIRST_TX1_CHANNEL + i * CN470_A20_STEPWIDTH_RX_CHANNEL; + channels[i].DrRange.Value = ( CN470_TX_MAX_DATARATE << 4 ) | CN470_TX_MIN_DATARATE; + channels[i].Band = 0; + } + // Upstream group 2 + for( uint8_t i = 32; i < 64; i++ ) + { + channels[i].Frequency = CN470_A20_FIRST_TX2_CHANNEL + ( i - 32 ) * CN470_A20_STEPWIDTH_RX_CHANNEL; + channels[i].DrRange.Value = ( CN470_TX_MAX_DATARATE << 4 ) | CN470_TX_MIN_DATARATE; + channels[i].Band = 0; + } +} + +void RegionCN470A20InitializeChannelsMask( uint16_t* channelsDefaultMask ) +{ + // Enable all possible channels + channelsDefaultMask[0] = 0xFFFF; + channelsDefaultMask[1] = 0xFFFF; + channelsDefaultMask[2] = 0xFFFF; + channelsDefaultMask[3] = 0xFFFF; + channelsDefaultMask[4] = 0x0000; + channelsDefaultMask[5] = 0x0000; +} + +uint32_t RegionCN470A20GetRx1Frequency( uint8_t channel ) +{ + // Base frequency for downstream group 1 + uint32_t baseFrequency = CN470_A20_FIRST_RX_CHANNEL; + uint8_t offset = 0; + + if( channel >= 32 ) + { + // Base frequency for downstream group 2 + baseFrequency = 490300000; + offset = 32; + } + return ( baseFrequency + ( ( channel - offset ) * CN470_A20_STEPWIDTH_RX_CHANNEL ) ); +} + +uint32_t RegionCN470A20GetRx2Frequency( uint8_t joinChannelIndex, bool isOtaaDevice ) +{ + uint32_t otaaFrequencies[] = CN470_A20_RX_WND_2_FREQ_OTAA; + + if( isOtaaDevice == true ) + { + return otaaFrequencies[joinChannelIndex]; + } + // ABP device + return CN470_A20_RX_WND_2_FREQ_ABP; +} + diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN470A20.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN470A20.h new file mode 100644 index 0000000000..f9592778cd --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN470A20.h @@ -0,0 +1,229 @@ +/*! + * \file RegionCN470A20.h + * + * \brief Specific implementations of channel plan type A, 20MHz. + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \defgroup REGIONCN470 + * + * \{ + */ +#ifndef __REGION_CN470_A20_H__ +#define __REGION_CN470_A20_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "region/Region.h" + +/*! + * The maximum number of channels. + * Channel plan type A, 20MHz. + */ +#define CN470_A20_CHANNELS_MASK_SIZE 4 + +/*! + * The number of entries in the join accept list. + * Channel plan type A, 20MHz. + */ +#define CN470_A20_JOIN_ACCEPT_LIST_SIZE 4 + +/*! + * This is a number which is used to calculate the + * beacon channel in case of frequency hopping. + * Channel plan type A, 20MHz. + */ +#define CN470_A20_BEACON_NB_CHANNELS 8 + +/*! + * This is a number which is used to calculate the + * ping slot channel in case of frequency hopping. + * Channel plan type A, 20MHz. + */ +#define CN470_A20_PING_SLOT_NB_CHANNELS 8 + +/*! + * The first RX channel, downstream group 1 and 2. + * Channel plan type A, 20MHz. + */ +#define CN470_A20_FIRST_RX_CHANNEL 483900000 + +/*! + * The last RX channel, downstream group 1 and 2. + * Channel plan type A, 20MHz. + */ +#define CN470_A20_LAST_RX_CHANNEL 496500000 + +/*! + * The frequency stepwidth between RX channels, + * downstream group 1 and 2. + * Channel plan type A, 20MHz. + */ +#define CN470_A20_STEPWIDTH_RX_CHANNEL 200000 + +/*! + * The first TX channel, upstream group 1. + * Channel plan type A, 20MHz. + */ +#define CN470_A20_FIRST_TX1_CHANNEL 470300000 + +/*! + * The frequency stepwidth between RX channels, + * upstream group 1. + * Channel plan type A, 20MHz. + */ +#define CN470_A20_STEPWIDTH_TX1_CHANNEL 200000 + +/*! + * The first TX channel, upstream group 2. + * Channel plan type A, 20MHz. + */ +#define CN470_A20_FIRST_TX2_CHANNEL 503500000 + +/*! + * The last TX channel, upstream group 2. + * Channel plan type A, 20MHz. + */ +#define CN470_A20_LAST_TX2_CHANNEL 509700000 + +/*! + * The frequency stepwidth between RX channels, + * upstream group 2. + * Channel plan type A, 20MHz. + */ +#define CN470_A20_STEPWIDTH_TX2_CHANNEL 200000 + +/*! + * The default frequency for RX window 2, when its + * an ABP device. + * Channel plan type A, 20MHz. + */ +#define CN470_A20_RX_WND_2_FREQ_ABP 486900000 + +/*! + * The channel plan frequencies for RX window 2, + * when its an OTAA device. + * Channel plan type A, 20MHz. + */ +#define CN470_A20_RX_WND_2_FREQ_OTAA { 485300000, 486900000, 488500000, 490100000, \ + 491700000, 493300000, 494000000, 496500000 } + +/*! + * \brief Calculation of the beacon frequency. + * + * \param [IN] channel The Beacon channel number. + * + * \param [IN] joinChannelIndex The join channel index. + * + * \param [IN] isPingSlot Set to true, if its a ping slot. + * + * \retval Returns the beacon frequency. + */ +uint32_t RegionCN470A20GetDownlinkFrequency( uint8_t channel, uint8_t joinChannelIndex, bool isPingSlot ); + +/*! + * \brief Performs the update of the channelsMask based on the input parameters + * for the channel plan type A, 20MHz. + * + * \param [IN] joinChannelIndex The join channel index. + * + * \retval Returns the offset for the given join channel. + */ +uint8_t RegionCN470A20GetBeaconChannelOffset( uint8_t joinChannelIndex ); + +/*! + * \brief Performs the update of the channelsMask based on the input parameters + * for the channel plan type A, 20MHz. + * + * \param [IN] channelsMask A pointer to the channels mask. + * + * \param [IN] chMaskCntl The value of the chMaskCntl field of the LinkAdrReq. + * + * \param [IN] chanMask The value of the chanMask field of the LinkAdrReq. + * + * \param [IN] channels A pointer to the available channels. + * + * \retval Status of the operation. Return 0x07 if the channels mask is valid. + */ +uint8_t RegionCN470A20LinkAdrChMaskUpdate( uint16_t* channelsMask, uint8_t chMaskCntl, + uint16_t chanMask, ChannelParams_t* channels ); + +/*! + * \brief Verifies if the frequency provided is valid + * for the channel plan type A, 20MHz. + * + * \param [IN] frequency The frequency to verify. + * + * \retval Returns true, if the frequency is valid. + */ +bool RegionCN470A20VerifyRfFreq( uint32_t frequency ); + +/*! + * \brief Initializes all channels, datarates, frequencies and bands + * for the channel plan type A, 20MHz. + * + * \param [IN] channels A pointer to the available channels. + */ +void RegionCN470A20InitializeChannels( ChannelParams_t* channels ); + +/*! + * \brief Initializes the channels default mask + * for the channel plan type A, 20MHz. + * + * \param [IN] channelsDefaultMask A pointer to the channels default mask. + */ +void RegionCN470A20InitializeChannelsMask( uint16_t* channelsDefaultMask ); + +/*! + * \brief Computes the frequency for the RX1 window + * for the channel plan type A, 20MHz. + * + * \param [IN] channel The channel utilized currently. + * + * \retval Returns the frequency which shall be used. + */ +uint32_t RegionCN470A20GetRx1Frequency( uint8_t channel ); + +/*! + * \brief Computes the frequency for the RX2 window + * for the channel plan type A, 20MHz. + * + * \param [IN] joinChannelIndex The join channel index. + * + * \param [IN] isOtaaDevice Set to true, if the device is an OTAA device. + * + * \retval Returns the frequency which shall be used. + */ +uint32_t RegionCN470A20GetRx2Frequency( uint8_t joinChannelIndex, bool isOtaaDevice ); + +/*! \} defgroup REGIONCN470 */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGION_CN470_A20_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN470A26.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN470A26.c new file mode 100644 index 0000000000..0625f4ca0e --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN470A26.c @@ -0,0 +1,133 @@ +/*! + * \file RegionCN470A26.c + * + * \brief + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \defgroup REGIONCN470 + */ +#include "RegionCN470.h" +#include "RegionBaseUS.h" +#include "RegionCN470A26.h" + +uint32_t RegionCN470A26GetDownlinkFrequency( uint8_t channel, uint8_t joinChannelIndex, bool isPingSlot ) +{ + return CN470_A26_BEACON_FREQ; +} + +uint8_t RegionCN470A26GetBeaconChannelOffset( uint8_t joinChannelIndex ) +{ + return 0; +} + +uint8_t RegionCN470A26LinkAdrChMaskUpdate( uint16_t* channelsMask, uint8_t chMaskCntl, + uint16_t chanMask, ChannelParams_t* channels ) +{ + uint8_t status = 0x07; + + if( ( chMaskCntl == 5 ) || ( chMaskCntl == 6 ) || ( chMaskCntl == 7 ) ) + { + // RFU + status &= 0xFE; // Channel mask KO + } + else if( chMaskCntl == 3 ) + { + // Enable all channels + channelsMask[0] = 0xFFFF; + channelsMask[1] = 0xFFFF; + channelsMask[2] = 0xFFFF; + channelsMask[3] = 0xFFFF; + channelsMask[4] = 0x0000; + channelsMask[5] = 0x0000; + } + else if( chMaskCntl == 4 ) + { + // Disable all channels + channelsMask[0] = 0x0000; + channelsMask[1] = 0x0000; + channelsMask[2] = 0x0000; + channelsMask[3] = 0x0000; + channelsMask[4] = 0x0000; + channelsMask[5] = 0x0000; + } + else + { + // chMaskCntl 0 to 2 + for( uint8_t i = 0; i < 16; i++ ) + { + if( ( ( chanMask & ( 1 << i ) ) != 0 ) && + ( channels[chMaskCntl * 16 + i].Frequency == 0 ) ) + {// Trying to enable an undefined channel + status &= 0xFE; // Channel mask KO + } + } + channelsMask[chMaskCntl] = chanMask; + } + return status; +} + +bool RegionCN470A26VerifyRfFreq( uint32_t freq ) +{ + // Downstream group 1 + if( RegionBaseUSVerifyFrequencyGroup( freq, CN470_A26_FIRST_RX_CHANNEL, + CN470_A26_LAST_RX_CHANNEL, + CN470_A26_STEPWIDTH_RX_CHANNEL ) == false ) + { + return false; + } + return true; +} + +void RegionCN470A26InitializeChannels( ChannelParams_t* channels ) +{ + // Upstream group 1 + for( uint8_t i = 0; i < 48; i++ ) + { + channels[i].Frequency = CN470_A26_FIRST_TX_CHANNEL + i * CN470_A26_STEPWIDTH_RX_CHANNEL; + channels[i].DrRange.Value = ( CN470_TX_MAX_DATARATE << 4 ) | CN470_TX_MIN_DATARATE; + channels[i].Band = 0; + } +} + +void RegionCN470A26InitializeChannelsMask( uint16_t* channelsDefaultMask ) +{ + // Enable all possible channels + channelsDefaultMask[0] = 0xFFFF; + channelsDefaultMask[1] = 0xFFFF; + channelsDefaultMask[2] = 0xFFFF; + channelsDefaultMask[3] = 0x0000; + channelsDefaultMask[4] = 0x0000; + channelsDefaultMask[5] = 0x0000; +} + +uint32_t RegionCN470A26GetRx1Frequency( uint8_t channel ) +{ + return ( CN470_A26_FIRST_RX_CHANNEL + ( ( channel % 24 ) * CN470_A26_STEPWIDTH_RX_CHANNEL ) ); +} + +uint32_t RegionCN470A26GetRx2Frequency( uint8_t joinChannelIndex, bool isOtaaDevice ) +{ + return CN470_A26_RX_WND_2_FREQ; +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN470A26.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN470A26.h new file mode 100644 index 0000000000..a698726315 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN470A26.h @@ -0,0 +1,211 @@ +/*! + * \file RegionCN470A26.h + * + * \brief Specific implementations of Channel plan type A, 26MHz. + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \defgroup REGIONCN470 + * + * \{ + */ +#ifndef __REGION_CN470_A26_H__ +#define __REGION_CN470_A26_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "region/Region.h" + +/*! + * The maximum number of channels. + * Channel plan type A, 26MHz. + */ +#define CN470_A26_CHANNELS_MASK_SIZE 3 + +/*! + * The number of entries in the join accept list. + * Channel plan type A, 26MHz. + */ +#define CN470_A26_JOIN_ACCEPT_LIST_SIZE 3 + +/*! + * The number of channels available for the beacon. + * Channel plan type A, 26MHz. + */ +#define CN470_A26_BEACON_NB_CHANNELS 1 + +/*! + * The number of channels available for the ping slots. + * Channel plan type A, 26MHz. + */ +#define CN470_A26_PING_SLOT_NB_CHANNELS 1 + +/*! + * The first RX channel, downstream group 1. + * Channel plan type A, 26MHz. + */ +#define CN470_A26_FIRST_RX_CHANNEL 490100000 + +/*! + * The last RX channel, downstream group 1. + * Channel plan type A, 26MHz. + */ +#define CN470_A26_LAST_RX_CHANNEL 494700000 + +/*! + * The frequency stepwidth between RX channels, + * downstream group 1. + * Channel plan type A, 26MHz. + */ +#define CN470_A26_STEPWIDTH_RX_CHANNEL 200000 + +/*! + * The first TX channel, upstream group 1. + * Channel plan type A, 26MHz. + */ +#define CN470_A26_FIRST_TX_CHANNEL 470300000 + +/*! + * The last TX channel, upstream group 1. + * Channel plan type A, 26MHz. + */ +#define CN470_A26_LAST_TX_CHANNEL 479700000 + +/*! + * The frequency stepwidth between RX channels, + * upstream group 1. + * Channel plan type A, 26MHz. + */ +#define CN470_A26_STEPWIDTH_TX_CHANNEL 200000 + +/*! + * The default frequency for RX window 2 + * Channel plan type A, 26MHz. + */ +#define CN470_A26_RX_WND_2_FREQ 492500000 + +/*! + * The default frequency for beacon. + * Channel plan type A, 26MHz. + */ +#define CN470_A26_BEACON_FREQ 494900000 + +/*! + * \brief Calculation of the beacon frequency. + * + * \param [IN] channel The Beacon channel number. + * + * \param [IN] joinChannelIndex The join channel index. + * + * \param [IN] isPingSlot Set to true, if its a ping slot. + * + * \retval Returns the beacon frequency. + */ +uint32_t RegionCN470A26GetDownlinkFrequency( uint8_t channel, uint8_t joinChannelIndex, bool isPingSlot ); + +/*! + * \brief Performs the update of the channelsMask based on the input parameters + * for the Channel plan type A, 26MHz. + * + * \param [IN] joinChannelIndex The join channel index. + * + * \retval Returns the offset for the given join channel. + */ +uint8_t RegionCN470A26GetBeaconChannelOffset( uint8_t joinChannelIndex ); + +/*! + * \brief Performs the update of the channelsMask based on the input parameters + * for the Channel plan type A, 26MHz. + * + * \param [IN] channelsMask A pointer to the channels mask. + * + * \param [IN] chMaskCntl The value of the chMaskCntl field of the LinkAdrReq. + * + * \param [IN] chanMask The value of the chanMask field of the LinkAdrReq. + * + * \param [IN] channels A pointer to the available channels. + * + * \retval Status of the operation. Return 0x07 if the channels mask is valid. + */ +uint8_t RegionCN470A26LinkAdrChMaskUpdate( uint16_t* channelsMask, uint8_t chMaskCntl, + uint16_t chanMask, ChannelParams_t* channels ); + +/*! + * \brief Verifies if the frequency provided is valid + * for the Channel plan type A, 26MHz. + * + * \param [IN] frequency The frequency to verify. + * + * \retval Returns true, if the frequency is valid. + */ +bool RegionCN470A26VerifyRfFreq( uint32_t frequency ); + +/*! + * \brief Initializes all channels, datarates, frequencies and bands + * for the Channel plan type A, 26MHz. + * + * \param [IN] channels A pointer to the available channels. + */ +void RegionCN470A26InitializeChannels( ChannelParams_t* channels ); + +/*! + * \brief Initializes the channels default mask + * for the Channel plan type A, 26MHz. + * + * \param [IN] channelsDefaultMask A pointer to the channels default mask. + */ +void RegionCN470A26InitializeChannelsMask( uint16_t* channelsDefaultMask ); + +/*! + * \brief Computes the frequency for the RX1 window + * for the Channel plan type A, 26MHz. + * + * \param [IN] channel The channel utilized currently. + * + * \retval Returns the frequency which shall be used. + */ +uint32_t RegionCN470A26GetRx1Frequency( uint8_t channel ); + +/*! + * \brief Computes the frequency for the RX2 window + * for the Channel plan type A, 26MHz. + * + * \param [IN] joinChannelIndex The join channel index. + * + * \param [IN] isOtaaDevice Set to true, if the device is an OTAA device. + * + * \retval Returns the frequency which shall be used. + */ +uint32_t RegionCN470A26GetRx2Frequency( uint8_t joinChannelIndex, bool isOtaaDevice ); + +/*! \} defgroup REGIONCN470 */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGION_CN470_A26_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN470B20.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN470B20.c new file mode 100644 index 0000000000..8769080566 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN470B20.c @@ -0,0 +1,139 @@ +/*! + * \file RegionCN470B20.c + * + * \brief + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \defgroup REGIONCN470 + */ +#include "RegionCN470.h" +#include "RegionBaseUS.h" +#include "RegionCN470B20.h" +#include "RegionCN470A20.h" + +uint32_t RegionCN470B20GetDownlinkFrequency( uint8_t channel, uint8_t joinChannelIndex, bool isPingSlot ) +{ + if( isPingSlot == true) + { + return RegionCN470B20GetRx1Frequency( channel ); + } + else + { + if( joinChannelIndex == 8 ) + { + return RegionCN470B20GetRx1Frequency( 23 ); + } + else + { + return RegionCN470B20GetRx1Frequency( 55 ); + } + } +} + +uint8_t RegionCN470B20GetBeaconChannelOffset( uint8_t joinChannelIndex ) +{ + return ( joinChannelIndex - 8 ) * 32; +} + +uint8_t RegionCN470B20LinkAdrChMaskUpdate( uint16_t* channelsMask, uint8_t chMaskCntl, + uint16_t chanMask, ChannelParams_t* channels ) +{ + // It follows the same implementation as type A + return RegionCN470A20LinkAdrChMaskUpdate( channelsMask, chMaskCntl, + chanMask, channels ); +} + +bool RegionCN470B20VerifyRfFreq( uint32_t freq ) +{ + bool group1Status = false; + bool group2Status = false; + + // Downstream group 1 + group1Status = RegionBaseUSVerifyFrequencyGroup( freq, CN470_B20_FIRST_RX1_CHANNEL, + CN470_B20_LAST_RX1_CHANNEL, + CN470_B20_STEPWIDTH_RX1_CHANNEL ); + // Downstream group 2 + group2Status = RegionBaseUSVerifyFrequencyGroup( freq, CN470_B20_FIRST_RX2_CHANNEL, + CN470_B20_LAST_RX2_CHANNEL, + CN470_B20_STEPWIDTH_RX2_CHANNEL ); + + // The frequency must be available in one of the groups + if( ( group1Status == false ) && ( group2Status == false ) ) + { + return false; + } + return true; +} + +void RegionCN470B20InitializeChannels( ChannelParams_t* channels ) +{ + // Upstream group 1 + for( uint8_t i = 0; i < 32; i++ ) + { + channels[i].Frequency = CN470_B20_FIRST_TX1_CHANNEL + i * CN470_B20_STEPWIDTH_TX1_CHANNEL; + channels[i].DrRange.Value = ( CN470_TX_MAX_DATARATE << 4 ) | CN470_TX_MIN_DATARATE; + channels[i].Band = 0; + } + // Upstream group 2 + for( uint8_t i = 32; i < 64; i++ ) + { + channels[i].Frequency = CN470_B20_FIRST_TX2_CHANNEL + ( i - 32 ) * CN470_B20_STEPWIDTH_TX2_CHANNEL; + channels[i].DrRange.Value = ( CN470_TX_MAX_DATARATE << 4 ) | CN470_TX_MIN_DATARATE; + channels[i].Band = 0; + } +} + +void RegionCN470B20InitializeChannelsMask( uint16_t* channelsDefaultMask ) +{ + RegionCN470A20InitializeChannelsMask( channelsDefaultMask ); +} + +uint32_t RegionCN470B20GetRx1Frequency( uint8_t channel ) +{ + // Base frequency for downstream group 1 + uint32_t baseFrequency = CN470_B20_FIRST_RX1_CHANNEL; + uint8_t offset = 0; + + if( channel >= 32 ) + { + // Base frequency for downstream group 2 + baseFrequency = CN470_B20_FIRST_RX2_CHANNEL; + offset = 32; + } + return ( baseFrequency + ( ( channel - offset ) * CN470_B20_STEPWIDTH_RX1_CHANNEL ) ); +} + +uint32_t RegionCN470B20GetRx2Frequency( uint8_t joinChannelIndex, bool isOtaaDevice ) +{ + uint32_t otaaFrequencies[] = CN470_B20_RX_WND_2_FREQ_OTAA; + + if( isOtaaDevice == true ) + { + return otaaFrequencies[joinChannelIndex - 8]; + } + // ABP device + return CN470_B20_RX_WND_2_FREQ_ABP; +} + diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN470B20.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN470B20.h new file mode 100644 index 0000000000..25b2f60dd0 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN470B20.h @@ -0,0 +1,253 @@ +/*! + * \file RegionCN470B20.h + * + * \brief Specific implementations of Channel plan type B, 20MHz. + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \defgroup REGIONCN470 + * + * \{ + */ +#ifndef __REGION_CN470_B20_H__ +#define __REGION_CN470_B20_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "region/Region.h" + +/*! + * The maximum number of channels. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_CHANNELS_MASK_SIZE 4 + +/*! + * The number of entries in the join accept list. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_JOIN_ACCEPT_LIST_SIZE 4 + +/*! + * This is a number which is used to calculate the + * beacon channel in case of frequency hopping. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_BEACON_NB_CHANNELS 1 + +/*! + * This is a number which is used to calculate the + * ping slot channel in case of frequency hopping. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_PING_SLOT_NB_CHANNELS 32 + +/*! + * The first RX channel, downstream group 1. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_FIRST_RX1_CHANNEL 476900000 + +/*! + * The last RX channel, downstream group 1. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_LAST_RX1_CHANNEL 483100000 + +/*! + * The frequency stepwidth between RX channels, + * downstream group 1. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_STEPWIDTH_RX1_CHANNEL 200000 + +/*! + * The first RX channel, downstream group 2. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_FIRST_RX2_CHANNEL 496900000 + +/*! + * The last RX channel, downstream group 2. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_LAST_RX2_CHANNEL 503100000 + +/*! + * The frequency stepwidth between RX channels, + * downstream group 2. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_STEPWIDTH_RX2_CHANNEL 200000 + +/*! + * The first TX channel, upstream group 1. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_FIRST_TX1_CHANNEL CN470_B20_FIRST_RX1_CHANNEL + +/*! + * The last TX channel, upstream group 1. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_LAST_TX1_CHANNEL CN470_B20_LAST_RX1_CHANNEL + +/*! + * The frequency stepwidth between RX channels, + * upstream group 1. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_STEPWIDTH_TX1_CHANNEL CN470_B20_STEPWIDTH_RX1_CHANNEL + +/*! + * The first TX channel, upstream group 2. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_FIRST_TX2_CHANNEL CN470_B20_FIRST_RX2_CHANNEL + +/*! + * The last TX channel, upstream group 2. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_LAST_TX2_CHANNEL CN470_B20_LAST_RX2_CHANNEL + +/*! + * The frequency stepwidth between RX channels, + * upstream group 2. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_STEPWIDTH_TX2_CHANNEL CN470_B20_STEPWIDTH_RX2_CHANNEL + +/*! + * The default frequency for RX window 2, when its + * an ABP device. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_RX_WND_2_FREQ_ABP 498300000 + +/*! + * The channel plan frequencies for RX window 2, + * when its an OTAA device. + * Channel plan type B, 20MHz. + */ +#define CN470_B20_RX_WND_2_FREQ_OTAA { 478300000, 498300000 } + +/*! + * \brief Calculation of the beacon frequency. + * + * \param [IN] channel The Beacon channel number. + * + * \param [IN] joinChannelIndex The join channel index. + * + * \param [IN] isPingSlot Set to true, if its a ping slot. + * + * \retval Returns the beacon frequency. + */ +uint32_t RegionCN470B20GetDownlinkFrequency( uint8_t channel, uint8_t joinChannelIndex, bool isPingSlot ); + +/*! + * \brief Performs the update of the channelsMask based on the input parameters + * for the Channel plan type B, 20MHz. + * + * \param [IN] joinChannelIndex The join channel index. + * + * \retval Returns the offset for the given join channel. + */ +uint8_t RegionCN470B20GetBeaconChannelOffset( uint8_t joinChannelIndex ); + +/*! + * \brief Performs the update of the channelsMask based on the input parameters + * for the Channel plan type B, 20MHz. + * + * \param [IN] channelsMask A pointer to the channels mask. + * + * \param [IN] chMaskCntl The value of the chMaskCntl field of the LinkAdrReq. + * + * \param [IN] chanMask The value of the chanMask field of the LinkAdrReq. + * + * \param [IN] channels A pointer to the available channels. + * + * \retval Status of the operation. Return 0x07 if the channels mask is valid. + */ +uint8_t RegionCN470B20LinkAdrChMaskUpdate( uint16_t* channelsMask, uint8_t chMaskCntl, + uint16_t chanMask, ChannelParams_t* channels ); + +/*! + * \brief Verifies if the frequency provided is valid + * for the Channel plan type B, 20MHz. + * + * \param [IN] frequency The frequency to verify. + * + * \retval Returns true, if the frequency is valid. + */ +bool RegionCN470B20VerifyRfFreq( uint32_t frequency ); + +/*! + * \brief Initializes all channels, datarates, frequencies and bands + * for the Channel plan type B, 20MHz. + * + * \param [IN] channels A pointer to the available channels. + */ +void RegionCN470B20InitializeChannels( ChannelParams_t* channels ); + +/*! + * \brief Initializes the channels default mask + * for the Channel plan type B, 20MHz. + * + * \param [IN] channelsDefaultMask A pointer to the channels default mask. + */ +void RegionCN470B20InitializeChannelsMask( uint16_t* channelsDefaultMask ); + +/*! + * \brief Computes the frequency for the RX1 window + * for the Channel plan type B, 20MHz. + * + * \param [IN] channel The channel utilized currently. + * + * \retval Returns the frequency which shall be used. + */ +uint32_t RegionCN470B20GetRx1Frequency( uint8_t channel ); + +/*! + * \brief Computes the frequency for the RX2 window + * for the Channel plan type B, 20MHz. + * + * \param [IN] joinChannelIndex The join channel index. + * + * \param [IN] isOtaaDevice Set to true, if the device is an OTAA device. + * + * \retval Returns the frequency which shall be used. + */ +uint32_t RegionCN470B20GetRx2Frequency( uint8_t joinChannelIndex, bool isOtaaDevice ); + +/*! \} defgroup REGIONCN470 */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGION_CN470_B20_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN470B26.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN470B26.c new file mode 100644 index 0000000000..2b9d370e14 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN470B26.c @@ -0,0 +1,90 @@ +/*! + * \file RegionCN470B26.c + * + * \brief + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \defgroup REGIONCN470 + */ +#include "RegionCN470.h" +#include "RegionBaseUS.h" +#include "RegionCN470A26.h" +#include "RegionCN470B26.h" + +uint32_t RegionCN470B26GetDownlinkFrequency( uint8_t channel, uint8_t joinChannelIndex, bool isPingSlot ) +{ + return CN470_B26_BEACON_FREQ; +} + +uint8_t RegionCN470B26GetBeaconChannelOffset( uint8_t joinChannelIndex ) +{ + return 0; +} + +uint8_t RegionCN470B26LinkAdrChMaskUpdate( uint16_t* channelsMask, uint8_t chMaskCntl, + uint16_t chanMask, ChannelParams_t* channels ) +{ + return RegionCN470A26LinkAdrChMaskUpdate( channelsMask, chMaskCntl, + chanMask, channels ); +} + +bool RegionCN470B26VerifyRfFreq( uint32_t freq ) +{ + // Downstream group 1 + if( RegionBaseUSVerifyFrequencyGroup( freq, CN470_B26_FIRST_RX_CHANNEL, + CN470_B26_LAST_RX_CHANNEL, + CN470_B26_STEPWIDTH_RX_CHANNEL ) == false ) + { + return false; + } + return true; +} + +void RegionCN470B26InitializeChannels( ChannelParams_t* channels ) +{ + // Upstream group 1 + for( uint8_t i = 0; i < 48; i++ ) + { + channels[i].Frequency = CN470_B26_FIRST_TX_CHANNEL + i * CN470_B26_STEPWIDTH_RX_CHANNEL; + channels[i].DrRange.Value = ( CN470_TX_MAX_DATARATE << 4 ) | CN470_TX_MIN_DATARATE; + channels[i].Band = 0; + } +} + +void RegionCN470B26InitializeChannelsMask( uint16_t* channelsDefaultMask ) +{ + RegionCN470A26InitializeChannelsMask( channelsDefaultMask ); +} + +uint32_t RegionCN470B26GetRx1Frequency( uint8_t channel ) +{ + return ( CN470_B26_FIRST_RX_CHANNEL + ( ( channel % 24 ) * CN470_B26_STEPWIDTH_RX_CHANNEL ) ); +} + +uint32_t RegionCN470B26GetRx2Frequency( uint8_t joinChannelIndex, bool isOtaaDevice ) +{ + return CN470_B26_RX_WND_2_FREQ; +} + diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN470B26.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN470B26.h new file mode 100644 index 0000000000..7fd2ba47a5 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN470B26.h @@ -0,0 +1,211 @@ +/*! + * \file RegionCN470B26.h + * + * \brief Specific implementations of Channel plan type B, 26MHz. + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \defgroup REGIONCN470 + * + * \{ + */ +#ifndef __REGION_CN470_B26_H__ +#define __REGION_CN470_B26_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "region/Region.h" + +/*! + * The maximum number of channels. + * Channel plan type B, 26MHz. + */ +#define CN470_B26_CHANNELS_MASK_SIZE 3 + +/*! + * The number of entries in the join accept list. + * Channel plan type B, 26MHz. + */ +#define CN470_B26_JOIN_ACCEPT_LIST_SIZE 3 + +/*! + * The number of channels available for the beacon. + * Channel plan type B, 26MHz. + */ +#define CN470_B26_BEACON_NB_CHANNELS 1 + +/*! + * The number of channels available for the ping slots. + * Channel plan type B, 26MHz. + */ +#define CN470_B26_PING_SLOT_NB_CHANNELS 1 + +/*! + * The first RX channel, downstream group 1. + * Channel plan type B, 26MHz. + */ +#define CN470_B26_FIRST_RX_CHANNEL 500100000 + +/*! + * The last RX channel, downstream group 1. + * Channel plan type B, 26MHz. + */ +#define CN470_B26_LAST_RX_CHANNEL 504700000 + +/*! + * The frequency stepwidth between RX channels, + * downstream group 1 and 2. + * Channel plan type B, 26MHz. + */ +#define CN470_B26_STEPWIDTH_RX_CHANNEL 200000 + +/*! + * The first TX channel, upstream group 1. + * Channel plan type B, 26MHz. + */ +#define CN470_B26_FIRST_TX_CHANNEL 480300000 + +/*! + * The last TX channel, upstream group 1. + * Channel plan type B, 26MHz. + */ +#define CN470_B26_LAST_TX_CHANNEL 489700000 + +/*! + * The frequency stepwidth between RX channels, + * upstream group 1. + * Channel plan type B, 26MHz. + */ +#define CN470_B26_STEPWIDTH_TX_CHANNEL 200000 + +/*! + * The default frequency for RX window 2, + * Channel plan type B, 26MHz. + */ +#define CN470_B26_RX_WND_2_FREQ 502500000 + +/*! + * The default frequency for beacon, + * Channel plan type B, 26MHz. + */ +#define CN470_B26_BEACON_FREQ 504900000 + +/*! + * \brief Calculation of the beacon frequency. + * + * \param [IN] channel The Beacon channel number. + * + * \param [IN] joinChannelIndex The join channel index. + * + * \param [IN] isPingSlot Set to true, if its a ping slot. + * + * \retval Returns the beacon frequency. + */ +uint32_t RegionCN470B26GetDownlinkFrequency( uint8_t channel, uint8_t joinChannelIndex, bool isPingSlot ); + +/*! + * \brief Performs the update of the channelsMask based on the input parameters + * for the Channel plan type B, 26MHz. + * + * \param [IN] joinChannelIndex The join channel index. + * + * \retval Returns the offset for the given join channel. + */ +uint8_t RegionCN470B26GetBeaconChannelOffset( uint8_t joinChannelIndex ); + +/*! + * \brief Performs the update of the channelsMask based on the input parameters + * for the Channel plan type B, 26MHz. + * + * \param [IN] channelsMask A pointer to the channels mask. + * + * \param [IN] chMaskCntl The value of the chMaskCntl field of the LinkAdrReq. + * + * \param [IN] chanMask The value of the chanMask field of the LinkAdrReq. + * + * \param [IN] channels A pointer to the available channels. + * + * \retval Status of the operation. Return 0x07 if the channels mask is valid. + */ +uint8_t RegionCN470B26LinkAdrChMaskUpdate( uint16_t* channelsMask, uint8_t chMaskCntl, + uint16_t chanMask, ChannelParams_t* channels ); + +/*! + * \brief Verifies if the frequency provided is valid + * for the Channel plan type B, 26MHz. + * + * \param [IN] frequency The frequency to verify. + * + * \retval Returns true, if the frequency is valid. + */ +bool RegionCN470B26VerifyRfFreq( uint32_t frequency ); + +/*! + * \brief Initializes all channels, datarates, frequencies and bands + * for the Channel plan type B, 26MHz. + * + * \param [IN] channels A pointer to the available channels. + */ +void RegionCN470B26InitializeChannels( ChannelParams_t* channels ); + +/*! + * \brief Initializes the channels mask and the channels default mask + * for the Channel plan type B, 26MHz. + * + * \param [IN] channelsDefaultMask A pointer to the channels default mask. + */ +void RegionCN470B26InitializeChannelsMask( uint16_t* channelsDefaultMask ); + +/*! + * \brief Computes the frequency for the RX1 window + * for the Channel plan type B, 26MHz. + * + * \param [IN] channel The channel utilized currently. + * + * \retval Returns the frequency which shall be used. + */ +uint32_t RegionCN470B26GetRx1Frequency( uint8_t channel ); + +/*! + * \brief Computes the frequency for the RX2 window + * for the Channel plan type B, 26MHz. + * + * \param [IN] joinChannelIndex The join channel index. + * + * \param [IN] isOtaaDevice Set to true, if the device is an OTAA device. + * + * \retval Returns the frequency which shall be used. + */ +uint32_t RegionCN470B26GetRx2Frequency( uint8_t joinChannelIndex, bool isOtaaDevice ); + +/*! \} defgroup REGIONCN470 */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGION_CN470_B26_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN779.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN779.c new file mode 100644 index 0000000000..7d86dbd56e --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN779.c @@ -0,0 +1,929 @@ +/*! + * \file RegionCN779.c + * + * \brief Region implementation for CN779 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) +*/ +#include "radio.h" +#include "RegionCommon.h" +#include "RegionCN779.h" + +// Definitions +#define CHANNELS_MASK_SIZE 1 + +/* + * Non-volatile module context. + */ +static RegionNvmDataGroup1_t* RegionNvmGroup1; +static RegionNvmDataGroup2_t* RegionNvmGroup2; +static Band_t* RegionBands; + +// Static functions +static bool VerifyRfFreq( uint32_t freq ) +{ + // Check radio driver support + if( Radio.CheckRfFrequency( freq ) == false ) + { + return false; + } + + if( ( freq < 779500000 ) || ( freq > 786500000 ) ) + { + return false; + } + return true; +} + +static TimerTime_t GetTimeOnAir( int8_t datarate, uint16_t pktLen ) +{ + int8_t phyDr = DataratesCN779[datarate]; + uint32_t bandwidth = RegionCommonGetBandwidth( datarate, BandwidthsCN779 ); + TimerTime_t timeOnAir = 0; + + if( datarate == DR_7 ) + { // High Speed FSK channel + timeOnAir = Radio.TimeOnAir( MODEM_FSK, bandwidth, phyDr * 1000, 0, 5, false, pktLen, true ); + } + else + { + timeOnAir = Radio.TimeOnAir( MODEM_LORA, bandwidth, phyDr, 1, 8, false, pktLen, true ); + } + return timeOnAir; +} + +PhyParam_t RegionCN779GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + + switch( getPhy->Attribute ) + { + case PHY_MIN_RX_DR: + { + phyParam.Value = CN779_RX_MIN_DATARATE; + break; + } + case PHY_MIN_TX_DR: + { + phyParam.Value = CN779_TX_MIN_DATARATE; + break; + } + case PHY_DEF_TX_DR: + { + phyParam.Value = CN779_DEFAULT_DATARATE; + break; + } + case PHY_NEXT_LOWER_TX_DR: + { + RegionCommonGetNextLowerTxDrParams_t nextLowerTxDrParams = + { + .CurrentDr = getPhy->Datarate, + .MaxDr = ( int8_t )CN779_TX_MAX_DATARATE, + .MinDr = ( int8_t )CN779_TX_MIN_DATARATE, + .NbChannels = CN779_MAX_NB_CHANNELS, + .ChannelsMask = RegionNvmGroup2->ChannelsMask, + .Channels = RegionNvmGroup2->Channels, + }; + phyParam.Value = RegionCommonGetNextLowerTxDr( &nextLowerTxDrParams ); + break; + } + case PHY_MAX_TX_POWER: + { + phyParam.Value = CN779_MAX_TX_POWER; + break; + } + case PHY_DEF_TX_POWER: + { + phyParam.Value = CN779_DEFAULT_TX_POWER; + break; + } + case PHY_DEF_ADR_ACK_LIMIT: + { + phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_LIMIT; + break; + } + case PHY_DEF_ADR_ACK_DELAY: + { + phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_DELAY; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateCN779[getPhy->Datarate]; + break; + } + case PHY_DUTY_CYCLE: + { + phyParam.Value = CN779_DUTY_CYCLE_ENABLED; + break; + } + case PHY_MAX_RX_WINDOW: + { + phyParam.Value = CN779_MAX_RX_WINDOW; + break; + } + case PHY_RECEIVE_DELAY1: + { + phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY2; + break; + } + case PHY_RETRANSMIT_TIMEOUT: + { + phyParam.Value = ( REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT + randr( -REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND, REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND ) ); + break; + } + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = REGION_COMMON_DEFAULT_RX1_DR_OFFSET; + break; + } + case PHY_DEF_RX2_FREQUENCY: + { + phyParam.Value = CN779_RX_WND_2_FREQ; + break; + } + case PHY_DEF_RX2_DR: + { + phyParam.Value = CN779_RX_WND_2_DR; + break; + } + case PHY_CHANNELS_MASK: + { + phyParam.ChannelsMask = RegionNvmGroup2->ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = RegionNvmGroup2->ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = CN779_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = RegionNvmGroup2->Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + { + phyParam.Value = CN779_DEFAULT_UPLINK_DWELL_TIME; + break; + } + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = REGION_COMMON_DEFAULT_DOWNLINK_DWELL_TIME; + break; + } + case PHY_DEF_MAX_EIRP: + { + phyParam.fValue = CN779_DEFAULT_MAX_EIRP; + break; + } + case PHY_DEF_ANTENNA_GAIN: + { + phyParam.fValue = CN779_DEFAULT_ANTENNA_GAIN; + break; + } + case PHY_BEACON_CHANNEL_FREQ: + { + phyParam.Value = CN779_BEACON_CHANNEL_FREQ; + break; + } + case PHY_BEACON_FORMAT: + { + phyParam.BeaconFormat.BeaconSize = CN779_BEACON_SIZE; + phyParam.BeaconFormat.Rfu1Size = CN779_RFU1_SIZE; + phyParam.BeaconFormat.Rfu2Size = CN779_RFU2_SIZE; + break; + } + case PHY_BEACON_CHANNEL_DR: + { + phyParam.Value = CN779_BEACON_CHANNEL_DR; + break; + } + case PHY_PING_SLOT_CHANNEL_FREQ: + { + phyParam.Value = CN779_PING_SLOT_CHANNEL_FREQ; + break; + } + case PHY_PING_SLOT_CHANNEL_DR: + { + phyParam.Value = CN779_PING_SLOT_CHANNEL_DR; + break; + } + case PHY_SF_FROM_DR: + { + phyParam.Value = DataratesCN779[getPhy->Datarate]; + break; + } + case PHY_BW_FROM_DR: + { + phyParam.Value = RegionCommonGetBandwidth( getPhy->Datarate, BandwidthsCN779 ); + break; + } + default: + { + break; + } + } + + return phyParam; +} + +void RegionCN779SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ + RegionCommonSetBandTxDone( &RegionBands[RegionNvmGroup2->Channels[txDone->Channel].Band], + txDone->LastTxAirTime, txDone->Joined, txDone->ElapsedTimeSinceStartUp ); +} + +void RegionCN779InitDefaults( InitDefaultsParams_t* params ) +{ + Band_t bands[CN779_MAX_NB_BANDS] = + { + CN779_BAND0 + }; + + switch( params->Type ) + { + case INIT_TYPE_DEFAULTS: + { + if( ( params->NvmGroup1 == NULL ) || ( params->NvmGroup2 == NULL ) ) + { + return; + } + + RegionNvmGroup1 = (RegionNvmDataGroup1_t*) params->NvmGroup1; + RegionNvmGroup2 = (RegionNvmDataGroup2_t*) params->NvmGroup2; + RegionBands = (Band_t*) params->Bands; + + // Default bands + memcpy1( ( uint8_t* )RegionBands, ( uint8_t* )bands, sizeof( Band_t ) * CN779_MAX_NB_BANDS ); + + // Default channels + RegionNvmGroup2->Channels[0] = ( ChannelParams_t ) CN779_LC1; + RegionNvmGroup2->Channels[1] = ( ChannelParams_t ) CN779_LC2; + RegionNvmGroup2->Channels[2] = ( ChannelParams_t ) CN779_LC3; + + // Default ChannelsMask + RegionNvmGroup2->ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 ); + + // Update the channels mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + break; + } + case INIT_TYPE_RESET_TO_DEFAULT_CHANNELS: + { + // Reset Channels Rx1Frequency to default 0 + RegionNvmGroup2->Channels[0].Rx1Frequency = 0; + RegionNvmGroup2->Channels[1].Rx1Frequency = 0; + RegionNvmGroup2->Channels[2].Rx1Frequency = 0; + // Update the channels mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + break; + } + case INIT_TYPE_ACTIVATE_DEFAULT_CHANNELS: + { + // Restore channels default mask + RegionNvmGroup2->ChannelsMask[0] |= RegionNvmGroup2->ChannelsDefaultMask[0]; + break; + } + default: + { + break; + } + } +} + +bool RegionCN779Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( phyAttribute ) + { + case PHY_FREQUENCY: + { + return VerifyRfFreq( verify->Frequency ); + } + case PHY_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, CN779_TX_MIN_DATARATE, CN779_TX_MAX_DATARATE ); + } + case PHY_DEF_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 ); + } + case PHY_RX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, CN779_RX_MIN_DATARATE, CN779_RX_MAX_DATARATE ); + } + case PHY_DEF_TX_POWER: + case PHY_TX_POWER: + { + // Remark: switched min and max! + return RegionCommonValueInRange( verify->TxPower, CN779_MAX_TX_POWER, CN779_MIN_TX_POWER ); + } + case PHY_DUTY_CYCLE: + { + return CN779_DUTY_CYCLE_ENABLED; + } + default: + return false; + } +} + +void RegionCN779ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ + ChannelParams_t newChannel; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + // Setup default datarate range + newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0; + + // Size of the optional CF list + if( applyCFList->Size != 16 ) + { + return; + } + + // Last byte CFListType must be 0 to indicate the CFList contains a list of frequencies + if( applyCFList->Payload[15] != 0 ) + { + return; + } + + // Last byte is RFU, don't take it into account + for( uint8_t i = 0, chanIdx = CN779_NUMB_DEFAULT_CHANNELS; chanIdx < CN779_MAX_NB_CHANNELS; i+=3, chanIdx++ ) + { + if( chanIdx < ( CN779_NUMB_CHANNELS_CF_LIST + CN779_NUMB_DEFAULT_CHANNELS ) ) + { + // Channel frequency + newChannel.Frequency = (uint32_t) applyCFList->Payload[i]; + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 ); + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 ); + newChannel.Frequency *= 100; + + // Initialize alternative frequency to 0 + newChannel.Rx1Frequency = 0; + } + else + { + newChannel.Frequency = 0; + newChannel.DrRange.Value = 0; + newChannel.Rx1Frequency = 0; + } + + if( newChannel.Frequency != 0 ) + { + channelAdd.NewChannel = &newChannel; + channelAdd.ChannelId = chanIdx; + + // Try to add all channels + RegionCN779ChannelAdd( &channelAdd ); + } + else + { + channelRemove.ChannelId = chanIdx; + + RegionCN779ChannelsRemove( &channelRemove ); + } + } +} + +bool RegionCN779ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + default: + return false; + } + return true; +} + +void RegionCN779ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + uint32_t tSymbolInUs = 0; + + // Get the datarate, perform a boundary check + rxConfigParams->Datarate = MIN( datarate, CN779_RX_MAX_DATARATE ); + rxConfigParams->Bandwidth = RegionCommonGetBandwidth( rxConfigParams->Datarate, BandwidthsCN779 ); + + if( rxConfigParams->Datarate == DR_7 ) + { // FSK + tSymbolInUs = RegionCommonComputeSymbolTimeFsk( DataratesCN779[rxConfigParams->Datarate] ); + } + else + { // LoRa + tSymbolInUs = RegionCommonComputeSymbolTimeLoRa( DataratesCN779[rxConfigParams->Datarate], BandwidthsCN779[rxConfigParams->Datarate] ); + } + + RegionCommonComputeRxWindowParameters( tSymbolInUs, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +} + +bool RegionCN779RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ + RadioModems_t modem; + int8_t dr = rxConfig->Datarate; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->RxSlot == RX_SLOT_WIN_1 ) + { + // Apply window 1 frequency + frequency = RegionNvmGroup2->Channels[rxConfig->Channel].Frequency; + // Apply the alternative RX 1 window frequency, if it is available + if( RegionNvmGroup2->Channels[rxConfig->Channel].Rx1Frequency != 0 ) + { + frequency = RegionNvmGroup2->Channels[rxConfig->Channel].Rx1Frequency; + } + } + + // Read the physical datarate from the datarates table + phyDr = DataratesCN779[dr]; + + Radio.SetChannel( frequency ); + + // Radio configuration + if( dr == DR_7 ) + { + modem = MODEM_FSK; + Radio.SetRxConfig( modem, 50000, phyDr * 1000, 0, 83333, 5, rxConfig->WindowTimeout, false, 0, true, 0, 0, false, rxConfig->RxContinuous ); + } + else + { + modem = MODEM_LORA; + Radio.SetRxConfig( modem, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous ); + } + + Radio.SetMaxPayloadLength( modem, MaxPayloadOfDatarateCN779[dr] + LORAMAC_FRAME_PAYLOAD_OVERHEAD_SIZE ); + + *datarate = (uint8_t) dr; + return true; +} + +bool RegionCN779TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + RadioModems_t modem; + int8_t phyDr = DataratesCN779[txConfig->Datarate]; + int8_t txPowerLimited = RegionCommonLimitTxPower( txConfig->TxPower, RegionBands[RegionNvmGroup2->Channels[txConfig->Channel].Band].TxMaxPower ); + uint32_t bandwidth = RegionCommonGetBandwidth( txConfig->Datarate, BandwidthsCN779 ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( RegionNvmGroup2->Channels[txConfig->Channel].Frequency ); + + if( txConfig->Datarate == DR_7 ) + { // High Speed FSK channel + modem = MODEM_FSK; + Radio.SetTxConfig( modem, phyTxPower, 25000, bandwidth, phyDr * 1000, 0, 5, false, true, 0, 0, false, 4000 ); + } + else + { + modem = MODEM_LORA; + Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 4000 ); + } + + // Update time-on-air + *txTimeOnAir = GetTimeOnAir( txConfig->Datarate, txConfig->PktLen ); + + // Setup maximum payload lenght of the radio driver + Radio.SetMaxPayloadLength( modem, txConfig->PktLen ); + + *txPower = txPowerLimited; + return true; +} + +uint8_t RegionCN779LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; + RegionCommonLinkAdrParams_t linkAdrParams = { 0 }; + uint8_t nextIndex = 0; + uint8_t bytesProcessed = 0; + uint16_t chMask = 0; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams; + + while( bytesProcessed < linkAdrReq->PayloadSize ) + { + // Get ADR request parameters + nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); + + if( nextIndex == 0 ) + break; // break loop, since no more request has been found + + // Update bytes processed + bytesProcessed += nextIndex; + + // Revert status, as we only check the last ADR request for the channel mask KO + status = 0x07; + + // Setup temporary channels mask + chMask = linkAdrParams.ChMask; + + // Verify channels mask + if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) ) + { + status &= 0xFE; // Channel mask KO + } + else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) || + ( linkAdrParams.ChMaskCtrl >= 7 ) ) + { + // RFU + status &= 0xFE; // Channel mask KO + } + else + { + for( uint8_t i = 0; i < CN779_MAX_NB_CHANNELS; i++ ) + { + if( linkAdrParams.ChMaskCtrl == 6 ) + { + if( RegionNvmGroup2->Channels[i].Frequency != 0 ) + { + chMask |= 1 << i; + } + } + else + { + if( ( ( chMask & ( 1 << i ) ) != 0 ) && + ( RegionNvmGroup2->Channels[i].Frequency == 0 ) ) + {// Trying to enable an undefined channel + status &= 0xFE; // Channel mask KO + } + } + } + } + } + + // Get the minimum possible datarate + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; + phyParam = RegionCN779GetPhyParam( &getPhy ); + + linkAdrVerifyParams.Status = status; + linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; + linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; + linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; + linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; + linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; + linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; + linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; + linkAdrVerifyParams.NbChannels = CN779_MAX_NB_CHANNELS; + linkAdrVerifyParams.ChannelsMask = &chMask; + linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value; + linkAdrVerifyParams.MaxDatarate = CN779_TX_MAX_DATARATE; + linkAdrVerifyParams.Channels = RegionNvmGroup2->Channels; + linkAdrVerifyParams.MinTxPower = CN779_MIN_TX_POWER; + linkAdrVerifyParams.MaxTxPower = CN779_MAX_TX_POWER; + linkAdrVerifyParams.Version = linkAdrReq->Version; + + // Verify the parameters and update, if necessary + status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep ); + + // Update channelsMask if everything is correct + if( status == 0x07 ) + { + // Set the channels mask to a default value + memset1( ( uint8_t* ) RegionNvmGroup2->ChannelsMask, 0, sizeof( RegionNvmGroup2->ChannelsMask ) ); + // Update the channels mask + RegionNvmGroup2->ChannelsMask[0] = chMask; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + + return status; +} + +uint8_t RegionCN779RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; + + // Verify radio frequency + if( VerifyRfFreq( rxParamSetupReq->Frequency ) == false ) + { + status &= 0xFE; // Channel frequency KO + } + + // Verify datarate + if( RegionCommonValueInRange( rxParamSetupReq->Datarate, CN779_RX_MIN_DATARATE, CN779_RX_MAX_DATARATE ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify datarate offset + if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, CN779_MIN_RX1_DR_OFFSET, CN779_MAX_RX1_DR_OFFSET ) == false ) + { + status &= 0xFB; // Rx1DrOffset range KO + } + + return status; +} + +int8_t RegionCN779NewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + uint8_t status = 0x03; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + if( newChannelReq->NewChannel->Frequency == 0 ) + { + channelRemove.ChannelId = newChannelReq->ChannelId; + + // Remove + if( RegionCN779ChannelsRemove( &channelRemove ) == false ) + { + status &= 0xFC; + } + } + else + { + channelAdd.NewChannel = newChannelReq->NewChannel; + channelAdd.ChannelId = newChannelReq->ChannelId; + + switch( RegionCN779ChannelAdd( &channelAdd ) ) + { + case LORAMAC_STATUS_OK: + { + break; + } + case LORAMAC_STATUS_FREQUENCY_INVALID: + { + status &= 0xFE; + break; + } + case LORAMAC_STATUS_DATARATE_INVALID: + { + status &= 0xFD; + break; + } + case LORAMAC_STATUS_FREQ_AND_DR_INVALID: + { + status &= 0xFC; + break; + } + default: + { + status &= 0xFC; + break; + } + } + } + + return status; +} + +int8_t RegionCN779TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + // Do not accept the request + return -1; +} + +int8_t RegionCN779DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + uint8_t status = 0x03; + + if( dlChannelReq->ChannelId >= ( CHANNELS_MASK_SIZE * 16 ) ) + { + return 0; + } + + // Verify if the frequency is supported + if( VerifyRfFreq( dlChannelReq->Rx1Frequency ) == false ) + { + status &= 0xFE; + } + + // Verify if an uplink frequency exists + if( RegionNvmGroup2->Channels[dlChannelReq->ChannelId].Frequency == 0 ) + { + status &= 0xFD; + } + + // Apply Rx1 frequency, if the status is OK + if( status == 0x03 ) + { + RegionNvmGroup2->Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency; + } + + return status; +} + +int8_t RegionCN779AlternateDr( int8_t currentDr, AlternateDrType_t type ) +{ + return currentDr; +} + +LoRaMacStatus_t RegionCN779NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t nbRestrictedChannels = 0; + uint8_t enabledChannels[CN779_MAX_NB_CHANNELS] = { 0 }; + RegionCommonIdentifyChannelsParam_t identifyChannelsParam; + RegionCommonCountNbOfEnabledChannelsParams_t countChannelsParams; + LoRaMacStatus_t status = LORAMAC_STATUS_NO_CHANNEL_FOUND; + uint16_t joinChannels = CN779_JOIN_CHANNELS; + + if( RegionCommonCountChannels( RegionNvmGroup2->ChannelsMask, 0, 1 ) == 0 ) + { // Reactivate default channels + RegionNvmGroup2->ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + + // Search how many channels are enabled + countChannelsParams.Joined = nextChanParams->Joined; + countChannelsParams.Datarate = nextChanParams->Datarate; + countChannelsParams.ChannelsMask = RegionNvmGroup2->ChannelsMask; + countChannelsParams.Channels = RegionNvmGroup2->Channels; + countChannelsParams.Bands = RegionBands; + countChannelsParams.MaxNbChannels = CN779_MAX_NB_CHANNELS; + countChannelsParams.JoinChannels = &joinChannels; + + identifyChannelsParam.AggrTimeOff = nextChanParams->AggrTimeOff; + identifyChannelsParam.LastAggrTx = nextChanParams->LastAggrTx; + identifyChannelsParam.DutyCycleEnabled = nextChanParams->DutyCycleEnabled; + identifyChannelsParam.MaxBands = CN779_MAX_NB_BANDS; + + identifyChannelsParam.ElapsedTimeSinceStartUp = nextChanParams->ElapsedTimeSinceStartUp; + identifyChannelsParam.LastTxIsJoinRequest = nextChanParams->LastTxIsJoinRequest; + identifyChannelsParam.ExpectedTimeOnAir = GetTimeOnAir( nextChanParams->Datarate, nextChanParams->PktLen ); + + identifyChannelsParam.CountNbOfEnabledChannelsParam = &countChannelsParams; + + status = RegionCommonIdentifyChannels( &identifyChannelsParam, aggregatedTimeOff, enabledChannels, + &nbEnabledChannels, &nbRestrictedChannels, time ); + + if( status == LORAMAC_STATUS_OK ) + { + // We found a valid channel + *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; + } + else if( status == LORAMAC_STATUS_NO_CHANNEL_FOUND ) + { + // Datarate not supported by any channel, restore defaults + RegionNvmGroup2->ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + return status; +} + +LoRaMacStatus_t RegionCN779ChannelAdd( ChannelAddParams_t* channelAdd ) +{ + bool drInvalid = false; + bool freqInvalid = false; + uint8_t id = channelAdd->ChannelId; + + if( id < CN779_NUMB_DEFAULT_CHANNELS ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + + if( id >= CN779_MAX_NB_CHANNELS ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + // Validate the datarate range + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, CN779_TX_MIN_DATARATE, CN779_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, CN779_TX_MIN_DATARATE, CN779_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max ) + { + drInvalid = true; + } + + // Check frequency + if( freqInvalid == false ) + { + if( VerifyRfFreq( channelAdd->NewChannel->Frequency ) == false ) + { + freqInvalid = true; + } + } + + // Check status + if( ( drInvalid == true ) && ( freqInvalid == true ) ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + if( drInvalid == true ) + { + return LORAMAC_STATUS_DATARATE_INVALID; + } + if( freqInvalid == true ) + { + return LORAMAC_STATUS_FREQUENCY_INVALID; + } + + memcpy1( ( uint8_t* ) &(RegionNvmGroup2->Channels[id]), ( uint8_t* ) channelAdd->NewChannel, sizeof( RegionNvmGroup2->Channels[id] ) ); + RegionNvmGroup2->Channels[id].Band = 0; + RegionNvmGroup2->ChannelsMask[0] |= ( 1 << id ); + return LORAMAC_STATUS_OK; +} + +bool RegionCN779ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + uint8_t id = channelRemove->ChannelId; + + if( id < CN779_NUMB_DEFAULT_CHANNELS ) + { + return false; + } + + // Remove the channel from the list of channels + RegionNvmGroup2->Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 }; + + return RegionCommonChanDisable( RegionNvmGroup2->ChannelsMask, id, CN779_MAX_NB_CHANNELS ); +} + +uint8_t RegionCN779ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + int8_t datarate = dr - drOffset; + + if( datarate < 0 ) + { + datarate = DR_0; + } + return datarate; +} + +void RegionCN779RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ) +{ + RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup; + + regionCommonRxBeaconSetup.Datarates = DataratesCN779; + regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency; + regionCommonRxBeaconSetup.BeaconSize = CN779_BEACON_SIZE; + regionCommonRxBeaconSetup.BeaconDatarate = CN779_BEACON_CHANNEL_DR; + regionCommonRxBeaconSetup.BeaconChannelBW = CN779_BEACON_CHANNEL_BW; + regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime; + regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout; + + RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup ); + + // Store downlink datarate + *outDr = CN779_BEACON_CHANNEL_DR; +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN779.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN779.h new file mode 100644 index 0000000000..555db5afc1 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCN779.h @@ -0,0 +1,449 @@ +/*! + * \file RegionCN779.h + * + * \brief Region definition for CN779 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * \defgroup REGIONCN779 Region CN779 + * Implementation according to LoRaWAN Specification v1.0.2. + * \{ + */ +#ifndef __REGION_CN779_H__ +#define __REGION_CN779_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "region/Region.h" + +/*! + * LoRaMac maximum number of channels + */ +#define CN779_MAX_NB_CHANNELS 16 + +/*! + * Number of default channels + */ +#define CN779_NUMB_DEFAULT_CHANNELS 3 + +/*! + * Number of channels to apply for the CF list + */ +#define CN779_NUMB_CHANNELS_CF_LIST 5 + +/*! + * Minimal datarate that can be used by the node + */ +#define CN779_TX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define CN779_TX_MAX_DATARATE DR_7 + +/*! + * Minimal datarate that can be used by the node + */ +#define CN779_RX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define CN779_RX_MAX_DATARATE DR_7 + +/*! + * Default datarate used by the node + */ +#define CN779_DEFAULT_DATARATE DR_0 + +/*! + * Minimal Rx1 receive datarate offset + */ +#define CN779_MIN_RX1_DR_OFFSET 0 + +/*! + * Maximal Rx1 receive datarate offset + */ +#define CN779_MAX_RX1_DR_OFFSET 5 + +/*! + * Minimal Tx output power that can be used by the node + */ +#define CN779_MIN_TX_POWER TX_POWER_5 + +/*! + * Maximal Tx output power that can be used by the node + */ +#define CN779_MAX_TX_POWER TX_POWER_0 + +/*! + * Default Tx output power used by the node + */ +#define CN779_DEFAULT_TX_POWER TX_POWER_0 + +/*! + * Default Max EIRP + */ +#define CN779_DEFAULT_MAX_EIRP 12.15f + +/*! + * Default antenna gain + */ +#define CN779_DEFAULT_ANTENNA_GAIN 2.15f + +/*! + * Enabled or disabled the duty cycle + */ +#define CN779_DUTY_CYCLE_ENABLED 1 + +/*! + * Maximum RX window duration + */ +#define CN779_MAX_RX_WINDOW 3000 + +/*! + * Verification of default datarate + */ +#if ( CN779_DEFAULT_DATARATE > DR_5 ) +#error "A default DR higher than DR_5 may lead to connectivity loss." +#endif + +/*! + * Second reception window channel frequency definition. + */ +#define CN779_RX_WND_2_FREQ 786000000 + +/*! + * Second reception window channel datarate definition. + */ +#define CN779_RX_WND_2_DR DR_0 + +/*! + * Default uplink dwell time configuration + */ +#define CN779_DEFAULT_UPLINK_DWELL_TIME 0 + +/* + * CLASS B + */ +/*! + * Beacon frequency + */ +#define CN779_BEACON_CHANNEL_FREQ 785000000 + +/*! + * Ping slot channel frequency + */ +#define CN779_PING_SLOT_CHANNEL_FREQ 785000000 + +/*! + * Payload size of a beacon frame + */ +#define CN779_BEACON_SIZE 17 + +/*! + * Size of RFU 1 field + */ +#define CN779_RFU1_SIZE 1 + +/*! + * Size of RFU 2 field + */ +#define CN779_RFU2_SIZE 0 + +/*! + * Datarate of the beacon channel + */ +#define CN779_BEACON_CHANNEL_DR DR_3 + +/*! + * Bandwith of the beacon channel + */ +#define CN779_BEACON_CHANNEL_BW 0 + +/*! + * Ping slot channel datarate + */ +#define CN779_PING_SLOT_CHANNEL_DR DR_3 + +/*! + * LoRaMac maximum number of bands + */ +#define CN779_MAX_NB_BANDS 1 + +/*! + * Band 0 definition + * Band = { DutyCycle, TxMaxPower, LastBandUpdateTime, LastMaxCreditAssignTime, TimeCredits, MaxTimeCredits, ReadyForTransmission } + */ +#define CN779_BAND0 { 100, CN779_MAX_TX_POWER, 0, 0, 0, 0, 0 } // 1.0 % + +/*! + * LoRaMac default channel 1 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define CN779_LC1 { 779500000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } +/*! + * LoRaMac default channel 2 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define CN779_LC2 { 779700000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac default channel 3 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define CN779_LC3 { 779900000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac channels which are allowed for the join procedure + */ +#define CN779_JOIN_CHANNELS ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) ) + +/*! + * Data rates table definition + */ +static const uint8_t DataratesCN779[] = { 12, 11, 10, 9, 8, 7, 7, 50 }; + +/*! + * Bandwidths table definition in Hz + */ +static const uint32_t BandwidthsCN779[] = { 125000, 125000, 125000, 125000, 125000, 125000, 250000, 0 }; + +/*! + * Maximum payload with respect to the datarate index. + */ +static const uint8_t MaxPayloadOfDatarateCN779[] = { 51, 51, 51, 115, 242, 242, 242, 242 }; + +/*! + * \brief The function gets a value of a specific phy attribute. + * + * \param [IN] getPhy Pointer to the function parameters. + * + * \retval Returns a structure containing the PHY parameter. + */ +PhyParam_t RegionCN779GetPhyParam( GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionCN779SetBandTxDone( SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] type Sets the initialization type. + */ +void RegionCN779InitDefaults( InitDefaultsParams_t* params ); + +/*! + * \brief Verifies a parameter. + * + * \param [IN] verify Pointer to the function parameters. + * + * \param [IN] type Sets the initialization type. + * + * \retval Returns true, if the parameter is valid. + */ +bool RegionCN779Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + +/*! + * \brief The function parses the input buffer and sets up the channels of the + * CF list. + * + * \param [IN] applyCFList Pointer to the function parameters. + */ +void RegionCN779ApplyCFList( ApplyCFListParams_t* applyCFList ); + +/*! + * \brief Sets a channels mask. + * + * \param [IN] chanMaskSet Pointer to the function parameters. + * + * \retval Returns true, if the channels mask could be set. + */ +bool RegionCN779ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ); + +/*! + * Computes the Rx window timeout and offset. + * + * \param [IN] datarate Rx window datarate index to be used + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms + * interval around RxOffset + * + * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields. + */ +void RegionCN779ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ); + +/*! + * \brief Configuration of the RX windows. + * + * \param [IN] rxConfig Pointer to the function parameters. + * + * \param [OUT] datarate The datarate index which was set. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionCN779RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ); + +/*! + * \brief TX configuration. + * + * \param [IN] txConfig Pointer to the function parameters. + * + * \param [OUT] txPower The tx power index which was set. + * + * \param [OUT] txTimeOnAir The time-on-air of the frame. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionCN779TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ); + +/*! + * \brief The function processes a Link ADR Request. + * + * \param [IN] linkAdrReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionCN779LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ); + +/*! + * \brief The function processes a RX Parameter Setup Request. + * + * \param [IN] rxParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionCN779RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ); + +/*! + * \brief The function processes a Channel Request. + * + * \param [IN] newChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +int8_t RegionCN779NewChannelReq( NewChannelReqParams_t* newChannelReq ); + +/*! + * \brief The function processes a TX ParamSetup Request. + * + * \param [IN] txParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + * Returns -1, if the functionality is not implemented. In this case, the end node + * shall not process the command. + */ +int8_t RegionCN779TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ); + +/*! + * \brief The function processes a DlChannel Request. + * + * \param [IN] dlChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +int8_t RegionCN779DlChannelReq( DlChannelReqParams_t* dlChannelReq ); + +/*! + * \brief Alternates the datarate of the channel for the join request. + * + * \param [IN] currentDr Current datarate. + * + * \retval Datarate to apply. + */ +int8_t RegionCN779AlternateDr( int8_t currentDr, AlternateDrType_t type ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [OUT] channel Next channel to use for TX. + * + * \param [OUT] time Time to wait for the next transmission according to the duty + * cycle. + * + * \param [OUT] aggregatedTimeOff Updates the aggregated time off. + * + * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate] + */ +LoRaMacStatus_t RegionCN779NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ); + +/*! + * \brief Adds a channel. + * + * \param [IN] channelAdd Pointer to the function parameters. + * + * \retval Status of the operation. + */ +LoRaMacStatus_t RegionCN779ChannelAdd( ChannelAddParams_t* channelAdd ); + +/*! + * \brief Removes a channel. + * + * \param [IN] channelRemove Pointer to the function parameters. + * + * \retval Returns true, if the channel was removed successfully. + */ +bool RegionCN779ChannelsRemove( ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms + * + * \param [IN] dr Current datarate + * + * \param [IN] drOffset Offset to be applied + * + * \retval newDr Computed datarate. + */ +uint8_t RegionCN779ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! + * \brief Sets the radio into beacon reception mode + * + * \param [IN] rxBeaconSetup Pointer to the function parameters + */ + void RegionCN779RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ); + +/*! \} defgroup REGIONCN779 */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGION_CN779_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCommon.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCommon.c new file mode 100644 index 0000000000..78b3507f8a --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCommon.c @@ -0,0 +1,624 @@ +/*! + * \file RegionCommon.c + * + * \brief LoRa MAC common region implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + */ +#include +#include "radio.h" +#include "utilities.h" +#include "RegionCommon.h" +#include "systime.h" + +#define BACKOFF_DC_1_HOUR 100 + +#define BACKOFF_DUTY_CYCLE_1_HOUR_IN_S 3600 +#define BACKOFF_DUTY_CYCLE_10_HOURS_IN_S ( BACKOFF_DUTY_CYCLE_1_HOUR_IN_S + ( BACKOFF_DUTY_CYCLE_1_HOUR_IN_S * 10 ) ) +#define BACKOFF_DUTY_CYCLE_24_HOURS_IN_S ( BACKOFF_DUTY_CYCLE_10_HOURS_IN_S + ( BACKOFF_DUTY_CYCLE_1_HOUR_IN_S * 24 ) ) +#define BACKOFF_24_HOURS_IN_S ( BACKOFF_DUTY_CYCLE_1_HOUR_IN_S * 24 ) + +#ifndef DUTY_CYCLE_TIME_PERIOD +/*! + * Default duty cycle observation time period is 1 hour (3600000 ms) according to ETSI. + */ +#define DUTY_CYCLE_TIME_PERIOD 3600000 +#endif + +#ifndef DUTY_CYCLE_TIME_PERIOD_JOIN_BACKOFF_24H +/*! + * Time credits for the join backoff algorithm for the 24H period. + */ +#define DUTY_CYCLE_TIME_PERIOD_JOIN_BACKOFF_24H 870000 +#endif + +/*! + * \brief Returns `N / D` rounded to the smallest integer value greater than or equal to `N / D` + * + * \warning when `D == 0`, the result is undefined + * + * \remark `N` and `D` can be signed or unsigned + * + * \param [IN] N the numerator, which can have any sign + * \param [IN] D the denominator, which can have any sign + * \retval N / D with any fractional part rounded to the smallest integer value greater than or equal to `N / D` + */ +#define DIV_CEIL( N, D ) \ + ( \ + ( N > 0 ) ? \ + ( ( ( N ) + ( D ) - 1 ) / ( D ) ) : \ + ( ( N ) / ( D ) ) \ + ) + +static uint16_t GetDutyCycle( Band_t* band, bool joined, SysTime_t elapsedTimeSinceStartup ) +{ + uint16_t dutyCycle = band->DCycle; + + if( joined == false ) + { + uint16_t joinDutyCycle = BACKOFF_DC_1_HOUR; + + // Take the most restrictive duty cycle + dutyCycle = MAX( dutyCycle, joinDutyCycle ); + } + + // Prevent value of 0 + if( dutyCycle == 0 ) + { + dutyCycle = 1; + } + + return dutyCycle; +} + +static uint16_t SetMaxTimeCredits( Band_t* band, bool joined, SysTime_t elapsedTimeSinceStartup, + bool dutyCycleEnabled, bool lastTxIsJoinRequest ) +{ + uint16_t dutyCycle = band->DCycle; + TimerTime_t maxCredits = DUTY_CYCLE_TIME_PERIOD; + + // Get the band duty cycle. If not joined, the function either returns the join duty cycle + // or the band duty cycle, whichever is more restrictive. + dutyCycle = GetDutyCycle( band, joined, elapsedTimeSinceStartup ); + + if( joined == false ) + { + if( elapsedTimeSinceStartup.Seconds < BACKOFF_DUTY_CYCLE_1_HOUR_IN_S ) + { + maxCredits = DUTY_CYCLE_TIME_PERIOD; + } + else if( elapsedTimeSinceStartup.Seconds < BACKOFF_DUTY_CYCLE_10_HOURS_IN_S ) + { + maxCredits = DUTY_CYCLE_TIME_PERIOD; + } + else + { + maxCredits = DUTY_CYCLE_TIME_PERIOD_JOIN_BACKOFF_24H; + } + } + else + { + if( dutyCycleEnabled == false ) + { + // Assign max credits when the duty cycle is disabled. + band->TimeCredits = maxCredits; + } + } + + // Setup the maximum allowed credits. We can assign them + // safely all the time. + band->MaxTimeCredits = maxCredits; + + return dutyCycle; +} + +static uint16_t UpdateTimeCredits( Band_t* band, bool joined, bool dutyCycleEnabled, + bool lastTxIsJoinRequest, SysTime_t elapsedTimeSinceStartup, + TimerTime_t currentTime, TimerTime_t lastBandUpdateTime ) +{ + uint16_t dutyCycle = SetMaxTimeCredits( band, joined, elapsedTimeSinceStartup, + dutyCycleEnabled, lastTxIsJoinRequest ); + TimerTime_t observation = DUTY_CYCLE_TIME_PERIOD; + + if( joined == false ) + { + if( elapsedTimeSinceStartup.Seconds < BACKOFF_DUTY_CYCLE_1_HOUR_IN_S ) + { + observation = BACKOFF_DUTY_CYCLE_1_HOUR_IN_S * 1000; + } + else if( elapsedTimeSinceStartup.Seconds < BACKOFF_DUTY_CYCLE_10_HOURS_IN_S ) + { + observation = ( BACKOFF_DUTY_CYCLE_10_HOURS_IN_S * 1000 ); + } + else + { + observation = ( BACKOFF_DUTY_CYCLE_24_HOURS_IN_S * 1000 ); + } + } + + // Apply new credits only if the observation period has been elapsed. + if( ( observation <= lastBandUpdateTime ) || + ( band->LastMaxCreditAssignTime != observation ) || + ( band->LastBandUpdateTime == 0 ) ) + { + band->TimeCredits = band->MaxTimeCredits; + band->LastBandUpdateTime = currentTime; + band->LastMaxCreditAssignTime = observation; + } + return dutyCycle; +} + +static uint8_t CountChannels( uint16_t mask, uint8_t nbBits ) +{ + uint8_t nbActiveBits = 0; + + for( uint8_t j = 0; j < nbBits; j++ ) + { + if( ( mask & ( 1 << j ) ) == ( 1 << j ) ) + { + nbActiveBits++; + } + } + return nbActiveBits; +} + +bool RegionCommonChanVerifyDr( uint8_t nbChannels, uint16_t* channelsMask, int8_t dr, int8_t minDr, int8_t maxDr, ChannelParams_t* channels ) +{ + if( RegionCommonValueInRange( dr, minDr, maxDr ) == 0 ) + { + return false; + } + + for( uint8_t i = 0, k = 0; i < nbChannels; i += 16, k++ ) + { + for( uint8_t j = 0; j < 16; j++ ) + { + if( ( ( channelsMask[k] & ( 1 << j ) ) != 0 ) ) + {// Check datarate validity for enabled channels + if( RegionCommonValueInRange( dr, ( channels[i + j].DrRange.Fields.Min & 0x0F ), + ( channels[i + j].DrRange.Fields.Max & 0x0F ) ) == 1 ) + { + // At least 1 channel has been found we can return OK. + return true; + } + } + } + } + return false; +} + +uint8_t RegionCommonValueInRange( int8_t value, int8_t min, int8_t max ) +{ + if( ( value >= min ) && ( value <= max ) ) + { + return 1; + } + return 0; +} + +bool RegionCommonChanDisable( uint16_t* channelsMask, uint8_t id, uint8_t maxChannels ) +{ + uint8_t index = id / 16; + + if( ( index > ( maxChannels / 16 ) ) || ( id >= maxChannels ) ) + { + return false; + } + + // Deactivate channel + channelsMask[index] &= ~( 1 << ( id % 16 ) ); + + return true; +} + +uint8_t RegionCommonCountChannels( uint16_t* channelsMask, uint8_t startIdx, uint8_t stopIdx ) +{ + uint8_t nbChannels = 0; + + if( channelsMask == NULL ) + { + return 0; + } + + for( uint8_t i = startIdx; i < stopIdx; i++ ) + { + nbChannels += CountChannels( channelsMask[i], 16 ); + } + + return nbChannels; +} + +void RegionCommonChanMaskCopy( uint16_t* channelsMaskDest, uint16_t* channelsMaskSrc, uint8_t len ) +{ + if( ( channelsMaskDest != NULL ) && ( channelsMaskSrc != NULL ) ) + { + for( uint8_t i = 0; i < len; i++ ) + { + channelsMaskDest[i] = channelsMaskSrc[i]; + } + } +} + +void RegionCommonSetBandTxDone( Band_t* band, TimerTime_t lastTxAirTime, bool joined, SysTime_t elapsedTimeSinceStartup ) +{ + // Get the band duty cycle. If not joined, the function either returns the join duty cycle + // or the band duty cycle, whichever is more restrictive. + uint16_t dutyCycle = GetDutyCycle( band, joined, elapsedTimeSinceStartup ); + + // Reduce with transmission time + if( band->TimeCredits > ( lastTxAirTime * dutyCycle ) ) + { + // Reduce time credits by the time of air + band->TimeCredits -= ( lastTxAirTime * dutyCycle ); + } + else + { + band->TimeCredits = 0; + } +} + +TimerTime_t RegionCommonUpdateBandTimeOff( bool joined, Band_t* bands, + uint8_t nbBands, bool dutyCycleEnabled, + bool lastTxIsJoinRequest, SysTime_t elapsedTimeSinceStartup, + TimerTime_t expectedTimeOnAir ) +{ + TimerTime_t minTimeToWait = TIMERTIME_T_MAX; + TimerTime_t currentTime = TimerGetCurrentTime( ); + TimerTime_t creditCosts = 0; + uint16_t dutyCycle = 1; + uint8_t validBands = 0; + + for( uint8_t i = 0; i < nbBands; i++ ) + { + TimerTime_t elapsedTime = TimerGetElapsedTime( bands[i].LastBandUpdateTime ); + + // Synchronization of bands and credits + dutyCycle = UpdateTimeCredits( &bands[i], joined, dutyCycleEnabled, + lastTxIsJoinRequest, elapsedTimeSinceStartup, + currentTime, elapsedTime ); + + // Calculate the credit costs for the next transmission + // with the duty cycle and the expected time on air + creditCosts = expectedTimeOnAir * dutyCycle; + + // Check if the band is ready for transmission. Its ready, + // when the duty cycle is off, or the TimeCredits of the band + // is higher than the credit costs for the transmission. + if( ( bands[i].TimeCredits > creditCosts ) || + ( ( dutyCycleEnabled == false ) && ( joined == true ) ) ) + { + bands[i].ReadyForTransmission = true; + // This band is a potential candidate for an + // upcoming transmission, so increase the counter. + validBands++; + } + else + { + // In this case, the band has not enough credits + // for the next transmission. + bands[i].ReadyForTransmission = false; + + if( bands[i].MaxTimeCredits > creditCosts ) + { + // The band can only be taken into account, if the maximum credits + // of the band are higher than the credit costs. + // We calculate the minTimeToWait among the bands which are not + // ready for transmission and which are potentially available + // for a transmission in the future. + TimerTime_t observationTimeDiff = 0; + if( bands[i].LastMaxCreditAssignTime >= elapsedTime ) + { + observationTimeDiff = bands[i].LastMaxCreditAssignTime - elapsedTime; + } + minTimeToWait = MIN( minTimeToWait, observationTimeDiff ); + // This band is a potential candidate for an + // upcoming transmission (even if its time credits are not enough + // at the moment), so increase the counter. + validBands++; + } + } + } + + + if( validBands == 0 ) + { + // There is no valid band available to handle a transmission + // in the given DUTY_CYCLE_TIME_PERIOD. + return TIMERTIME_T_MAX; + } + return minTimeToWait; +} + +uint8_t RegionCommonParseLinkAdrReq( uint8_t* payload, RegionCommonLinkAdrParams_t* linkAdrParams ) +{ + uint8_t retIndex = 0; + + if( payload[0] == SRV_MAC_LINK_ADR_REQ ) + { + // Parse datarate and tx power + linkAdrParams->Datarate = payload[1]; + linkAdrParams->TxPower = linkAdrParams->Datarate & 0x0F; + linkAdrParams->Datarate = ( linkAdrParams->Datarate >> 4 ) & 0x0F; + // Parse ChMask + linkAdrParams->ChMask = ( uint16_t )payload[2]; + linkAdrParams->ChMask |= ( uint16_t )payload[3] << 8; + // Parse ChMaskCtrl and nbRep + linkAdrParams->NbRep = payload[4]; + linkAdrParams->ChMaskCtrl = ( linkAdrParams->NbRep >> 4 ) & 0x07; + linkAdrParams->NbRep &= 0x0F; + + // LinkAdrReq has 4 bytes length + 1 byte CMD + retIndex = 5; + } + return retIndex; +} + +uint8_t RegionCommonLinkAdrReqVerifyParams( RegionCommonLinkAdrReqVerifyParams_t* verifyParams, int8_t* dr, int8_t* txPow, uint8_t* nbRep ) +{ + uint8_t status = verifyParams->Status; + int8_t datarate = verifyParams->Datarate; + int8_t txPower = verifyParams->TxPower; + int8_t nbRepetitions = verifyParams->NbRep; + + // Handle the case when ADR is off. + if( verifyParams->AdrEnabled == false ) + { + // When ADR is off, we are allowed to change the channels mask + nbRepetitions = verifyParams->CurrentNbRep; + datarate = verifyParams->CurrentDatarate; + txPower = verifyParams->CurrentTxPower; + } + + if( status != 0 ) + { + // Verify datarate. The variable phyParam. Value contains the minimum allowed datarate. + if( datarate == 0x0F ) + { // 0xF means that the device MUST ignore that field, and keep the current parameter value. + datarate = verifyParams->CurrentDatarate; + } + else if( RegionCommonChanVerifyDr( verifyParams->NbChannels, verifyParams->ChannelsMask, datarate, + verifyParams->MinDatarate, verifyParams->MaxDatarate, verifyParams->Channels ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify tx power + if( txPower == 0x0F ) + { // 0xF means that the device MUST ignore that field, and keep the current parameter value. + txPower = verifyParams->CurrentTxPower; + } + else if( RegionCommonValueInRange( txPower, verifyParams->MaxTxPower, verifyParams->MinTxPower ) == 0 ) + { + // Verify if the maximum TX power is exceeded + if( verifyParams->MaxTxPower > txPower ) + { // Apply maximum TX power. Accept TX power. + txPower = verifyParams->MaxTxPower; + } + else + { + status &= 0xFB; // TxPower KO + } + } + } + + // If the status is ok, verify the NbRep + if( status == 0x07 ) + { + if( nbRepetitions == 0 ) + { // Set nbRep to the default value of 1. + nbRepetitions = 1; + } + } + + // Apply changes + *dr = datarate; + *txPow = txPower; + *nbRep = nbRepetitions; + + return status; +} + +uint32_t RegionCommonComputeSymbolTimeLoRa( uint8_t phyDr, uint32_t bandwidthInHz ) +{ + return ( 1 << phyDr ) * 1000000 / bandwidthInHz; +} + +uint32_t RegionCommonComputeSymbolTimeFsk( uint8_t phyDrInKbps ) +{ + return 8000 / ( uint32_t )phyDrInKbps; // 1 symbol equals 1 byte +} + +void RegionCommonComputeRxWindowParameters( uint32_t tSymbolInUs, uint8_t minRxSymbols, uint32_t rxErrorInMs, uint32_t wakeUpTimeInMs, uint32_t* windowTimeoutInSymbols, int32_t* windowOffsetInMs ) +{ + *windowTimeoutInSymbols = MAX( DIV_CEIL( ( ( 2 * minRxSymbols - 8 ) * tSymbolInUs + 2 * ( rxErrorInMs * 1000 ) ), tSymbolInUs ), minRxSymbols ); // Computed number of symbols + *windowOffsetInMs = ( int32_t )DIV_CEIL( ( int32_t )( 4 * tSymbolInUs ) - + ( int32_t )DIV_CEIL( ( *windowTimeoutInSymbols * tSymbolInUs ), 2 ) - + ( int32_t )( wakeUpTimeInMs * 1000 ), 1000 ); +} + +int8_t RegionCommonComputeTxPower( int8_t txPowerIndex, float maxEirp, float antennaGain ) +{ + int8_t phyTxPower = 0; + + phyTxPower = ( int8_t )floor( ( maxEirp - ( txPowerIndex * 2U ) ) - antennaGain ); + + return phyTxPower; +} + +void RegionCommonRxBeaconSetup( RegionCommonRxBeaconSetupParams_t* rxBeaconSetupParams ) +{ + bool rxContinuous = true; + uint8_t datarate; + + // Set the radio into sleep mode + Radio.Sleep( ); + + // Setup frequency and payload length + Radio.SetChannel( rxBeaconSetupParams->Frequency ); + Radio.SetMaxPayloadLength( MODEM_LORA, rxBeaconSetupParams->BeaconSize ); + + // Check the RX continuous mode + if( rxBeaconSetupParams->RxTime != 0 ) + { + rxContinuous = false; + } + + // Get region specific datarate + datarate = rxBeaconSetupParams->Datarates[rxBeaconSetupParams->BeaconDatarate]; + + // Setup radio + Radio.SetRxConfig( MODEM_LORA, rxBeaconSetupParams->BeaconChannelBW, datarate, + 1, 0, 10, rxBeaconSetupParams->SymbolTimeout, true, rxBeaconSetupParams->BeaconSize, false, 0, 0, false, rxContinuous ); + + Radio.Rx( rxBeaconSetupParams->RxTime ); +} + +void RegionCommonCountNbOfEnabledChannels( RegionCommonCountNbOfEnabledChannelsParams_t* countNbOfEnabledChannelsParams, + uint8_t* enabledChannels, uint8_t* nbEnabledChannels, uint8_t* nbRestrictedChannels ) +{ + uint8_t nbChannelCount = 0; + uint8_t nbRestrictedChannelsCount = 0; + + for( uint8_t i = 0, k = 0; i < countNbOfEnabledChannelsParams->MaxNbChannels; i += 16, k++ ) + { + for( uint8_t j = 0; j < 16; j++ ) + { + if( ( countNbOfEnabledChannelsParams->ChannelsMask[k] & ( 1 << j ) ) != 0 ) + { + if( countNbOfEnabledChannelsParams->Channels[i + j].Frequency == 0 ) + { // Check if the channel is enabled + continue; + } + if( ( countNbOfEnabledChannelsParams->Joined == false ) && + ( countNbOfEnabledChannelsParams->JoinChannels != NULL ) ) + { + if( ( countNbOfEnabledChannelsParams->JoinChannels[k] & ( 1 << j ) ) == 0 ) + { + continue; + } + } + if( RegionCommonValueInRange( countNbOfEnabledChannelsParams->Datarate, + countNbOfEnabledChannelsParams->Channels[i + j].DrRange.Fields.Min, + countNbOfEnabledChannelsParams->Channels[i + j].DrRange.Fields.Max ) == false ) + { // Check if the current channel selection supports the given datarate + continue; + } + if( countNbOfEnabledChannelsParams->Bands[countNbOfEnabledChannelsParams->Channels[i + j].Band].ReadyForTransmission == false ) + { // Check if the band is available for transmission + nbRestrictedChannelsCount++; + continue; + } + enabledChannels[nbChannelCount++] = i + j; + } + } + } + *nbEnabledChannels = nbChannelCount; + *nbRestrictedChannels = nbRestrictedChannelsCount; +} + +LoRaMacStatus_t RegionCommonIdentifyChannels( RegionCommonIdentifyChannelsParam_t* identifyChannelsParam, + TimerTime_t* aggregatedTimeOff, uint8_t* enabledChannels, + uint8_t* nbEnabledChannels, uint8_t* nbRestrictedChannels, + TimerTime_t* nextTxDelay ) +{ + TimerTime_t elapsed = TimerGetElapsedTime( identifyChannelsParam->LastAggrTx ); + *nextTxDelay = identifyChannelsParam->AggrTimeOff - elapsed; + *nbRestrictedChannels = 1; + *nbEnabledChannels = 0; + + if( ( identifyChannelsParam->LastAggrTx == 0 ) || + ( identifyChannelsParam->AggrTimeOff <= elapsed ) ) + { + // Reset Aggregated time off + *aggregatedTimeOff = 0; + + // Update bands Time OFF + *nextTxDelay = RegionCommonUpdateBandTimeOff( identifyChannelsParam->CountNbOfEnabledChannelsParam->Joined, + identifyChannelsParam->CountNbOfEnabledChannelsParam->Bands, + identifyChannelsParam->MaxBands, + identifyChannelsParam->DutyCycleEnabled, + identifyChannelsParam->LastTxIsJoinRequest, + identifyChannelsParam->ElapsedTimeSinceStartUp, + identifyChannelsParam->ExpectedTimeOnAir ); + + RegionCommonCountNbOfEnabledChannels( identifyChannelsParam->CountNbOfEnabledChannelsParam, enabledChannels, + nbEnabledChannels, nbRestrictedChannels ); + } + + if( *nbEnabledChannels > 0 ) + { + *nextTxDelay = 0; + return LORAMAC_STATUS_OK; + } + else if( *nbRestrictedChannels > 0 ) + { + return LORAMAC_STATUS_DUTYCYCLE_RESTRICTED; + } + else + { + return LORAMAC_STATUS_NO_CHANNEL_FOUND; + } +} + +int8_t RegionCommonGetNextLowerTxDr( RegionCommonGetNextLowerTxDrParams_t *params ) +{ + int8_t drLocal = params->CurrentDr; + + if( params->CurrentDr == params->MinDr ) + { + return params->MinDr; + } + else + { + do + { + drLocal = ( drLocal - 1 ); + } while( ( drLocal != params->MinDr ) && + ( RegionCommonChanVerifyDr( params->NbChannels, params->ChannelsMask, drLocal, params->MinDr, params->MaxDr, params->Channels ) == false ) ); + + return drLocal; + } +} + +int8_t RegionCommonLimitTxPower( int8_t txPower, int8_t maxBandTxPower ) +{ + // Limit tx power to the band max + return MAX( txPower, maxBandTxPower ); +} + +uint32_t RegionCommonGetBandwidth( uint32_t drIndex, const uint32_t* bandwidths ) +{ + switch( bandwidths[drIndex] ) + { + default: + case 125000: + return 0; + case 250000: + return 1; + case 500000: + return 2; + } +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCommon.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCommon.h new file mode 100644 index 0000000000..ffb663be77 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionCommon.h @@ -0,0 +1,625 @@ +/*! + * \file RegionCommon.h + * + * \brief Region independent implementations which are common to all regions. + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * \defgroup REGIONCOMMON Common region implementation + * Region independent implementations which are common to all regions. + * \{ + */ +#ifndef __REGIONCOMMON_H__ +#define __REGIONCOMMON_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "LoRaMacTypes.h" +#include "LoRaMacHeaderTypes.h" +#include "region/Region.h" + +// Constants that are common to all the regions. + +/*! + * Receive delay of 1 second. + */ +#define REGION_COMMON_DEFAULT_RECEIVE_DELAY1 1000 + +/*! + * Receive delay of 2 seconds. + */ +#define REGION_COMMON_DEFAULT_RECEIVE_DELAY2 ( REGION_COMMON_DEFAULT_RECEIVE_DELAY1 + 1000 ) + +/*! + * Join accept delay of 5 seconds. + */ +#define REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY1 5000 + +/*! + * Join accept delay of 6 seconds. + */ +#define REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY2 ( REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY1 + 1000 ) + +/*! + * ADR ack limit. + */ +#define REGION_COMMON_DEFAULT_ADR_ACK_LIMIT 64 + +/*! + * ADR ack delay. + */ +#define REGION_COMMON_DEFAULT_ADR_ACK_DELAY 32 + +/*! + * Retransmission timeout for ACK in milliseconds. + */ +#define REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT 2000 + +/*! + * Rounding limit for generating random retransmission timeout for ACK. + * In milliseconds. + */ +#define REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND 1000 + +/*! + * Default Rx1 receive datarate offset + */ +#define REGION_COMMON_DEFAULT_RX1_DR_OFFSET 0 + +/*! + * Default downlink dwell time configuration + */ +#define REGION_COMMON_DEFAULT_DOWNLINK_DWELL_TIME 0 + +/*! + * Default ping slots periodicity + * + * Periodicity is equal to 2^REGION_COMMON_DEFAULT_PING_SLOT_PERIODICITY seconds. + * Example: 2^7 = 128 seconds. The end-device will open an Rx slot every 128 seconds. + */ +#define REGION_COMMON_DEFAULT_PING_SLOT_PERIODICITY 7 + +/*! + * Default reponse timeout for class b and class c confirmed + * downlink frames in milli seconds. + * + * The value shall not be smaller than RETRANSMIT_TIMEOUT plus + * the maximum time on air. + */ +#define REGION_COMMON_CLASS_B_C_RESP_TIMEOUT 8000 + + +typedef struct sRegionCommonLinkAdrParams +{ + /*! + * Number of repetitions. + */ + uint8_t NbRep; + /*! + * Datarate. + */ + int8_t Datarate; + /*! + * Tx power. + */ + int8_t TxPower; + /*! + * Channels mask control field. + */ + uint8_t ChMaskCtrl; + /*! + * Channels mask field. + */ + uint16_t ChMask; +}RegionCommonLinkAdrParams_t; + +typedef struct sRegionCommonLinkAdrReqVerifyParams +{ + /*! + * LoRaWAN specification Version + */ + Version_t Version; + /*! + * The current status of the AdrLinkRequest. + */ + uint8_t Status; + /*! + * Set to true, if ADR is enabled. + */ + bool AdrEnabled; + /*! + * The datarate the AdrLinkRequest wants to set. + */ + int8_t Datarate; + /*! + * The TX power the AdrLinkRequest wants to set. + */ + int8_t TxPower; + /*! + * The number of repetitions the AdrLinkRequest wants to set. + */ + uint8_t NbRep; + /*! + * The current datarate the node is using. + */ + int8_t CurrentDatarate; + /*! + * The current TX power the node is using. + */ + int8_t CurrentTxPower; + /*! + * The current number of repetitions the node is using. + */ + int8_t CurrentNbRep; + /*! + * The number of channels. + */ + uint8_t NbChannels; + /*! + * Pointer to the first element of the channels mask. + */ + uint16_t* ChannelsMask; + /*! + * The minimum possible datarate. + */ + int8_t MinDatarate; + /*! + * The maximum possible datarate. + */ + int8_t MaxDatarate; + /*! + * Pointer to the channels. + */ + ChannelParams_t* Channels; + /*! + * The minimum possible TX power. + */ + int8_t MinTxPower; + /*! + * The maximum possible TX power. + */ + int8_t MaxTxPower; +}RegionCommonLinkAdrReqVerifyParams_t; + +typedef struct sRegionCommonRxBeaconSetupParams +{ + /*! + * A pointer to the available datarates. + */ + const uint8_t* Datarates; + /*! + * Frequency + */ + uint32_t Frequency; + /*! + * The size of the beacon frame. + */ + uint8_t BeaconSize; + /*! + * The datarate of the beacon. + */ + uint8_t BeaconDatarate; + /*! + * The channel bandwidth of the beacon. + */ + uint8_t BeaconChannelBW; + /*! + * The RX time. + */ + uint32_t RxTime; + /*! + * The symbol timeout of the RX procedure. + */ + uint16_t SymbolTimeout; +}RegionCommonRxBeaconSetupParams_t; + +typedef struct sRegionCommonCountNbOfEnabledChannelsParams +{ + /*! + * Set to true, if the device is joined. + */ + bool Joined; + /*! + * The datarate to count the available channels. + */ + uint8_t Datarate; + /*! + * A pointer to the channels mask to verify. + */ + uint16_t* ChannelsMask; + /*! + * A pointer to the channels. + */ + ChannelParams_t* Channels; + /*! + * A pointer to the bands. + */ + Band_t* Bands; + /*! + * The number of available channels. + */ + uint16_t MaxNbChannels; + /*! + * A pointer to the bitmask containing the + * join channels. Shall have the same dimension as the + * ChannelsMask with a number of MaxNbChannels channels. + */ + uint16_t* JoinChannels; +}RegionCommonCountNbOfEnabledChannelsParams_t; + +typedef struct sRegionCommonIdentifyChannelsParam +{ + /*! + * Aggregated time-off time. + */ + TimerTime_t AggrTimeOff; + /*! + * Time of the last aggregated TX. + */ + TimerTime_t LastAggrTx; + /*! + * Set to true, if the duty cycle is enabled, otherwise false. + */ + bool DutyCycleEnabled; + /*! + * Maximum number of bands. + */ + uint8_t MaxBands; + /*! + * Elapsed time since the start of the node. + */ + SysTime_t ElapsedTimeSinceStartUp; + /*! + * Joined Set to true, if the last uplink was a join request + */ + bool LastTxIsJoinRequest; + /*! + * Expected time-on-air + */ + TimerTime_t ExpectedTimeOnAir; + /*! + * Pointer to a structure of RegionCommonCountNbOfEnabledChannelsParams_t. + */ + RegionCommonCountNbOfEnabledChannelsParams_t* CountNbOfEnabledChannelsParam; +}RegionCommonIdentifyChannelsParam_t; + +typedef struct sRegionCommonSetDutyCycleParams +{ + /*! + * Duty cycle period. + */ + TimerTime_t DutyCycleTimePeriod; + /*! + * Number of bands available. + */ + uint8_t MaxBands; + /*! + * A pointer to the bands. + */ + Band_t* Bands; +}RegionCommonSetDutyCycleParams_t; + +typedef struct sRegionCommonGetNextLowerTxDrParams +{ + int8_t CurrentDr; + int8_t MaxDr; + int8_t MinDr; + uint8_t NbChannels; + uint16_t* ChannelsMask; + ChannelParams_t* Channels; +}RegionCommonGetNextLowerTxDrParams_t; + +/*! + * \brief Verifies, if a value is in a given range. + * This is a generic function and valid for all regions. + * + * \param [IN] value Value to verify, if it is in range. + * + * \param [IN] min Minimum possible value. + * + * \param [IN] max Maximum possible value. + * + * \retval Returns 1 if the value is in range, otherwise 0. + */ +uint8_t RegionCommonValueInRange( int8_t value, int8_t min, int8_t max ); + +/*! + * \brief Verifies, if a datarate is available on an active channel. + * This is a generic function and valid for all regions. + * + * \param [IN] nbChannels Number of channels. + * + * \param [IN] channelsMask The channels mask of the region. + * + * \param [IN] dr The datarate to verify. + * + * \param [IN] minDr Minimum datarate. + * + * \param [IN] maxDr Maximum datarate. + * + * \param [IN] channels The channels of the region. + * + * \retval Returns true if the datarate is supported, false if not. + */ +bool RegionCommonChanVerifyDr( uint8_t nbChannels, uint16_t* channelsMask, int8_t dr, + int8_t minDr, int8_t maxDr, ChannelParams_t* channels ); + +/*! + * \brief Disables a channel in a given channels mask. + * This is a generic function and valid for all regions. + * + * \param [IN] channelsMask The channels mask of the region. + * + * \param [IN] id The id of the channels mask to disable. + * + * \param [IN] maxChannels Maximum number of channels. + * + * \retval Returns true if the channel could be disabled, false if not. + */ +bool RegionCommonChanDisable( uint16_t* channelsMask, uint8_t id, uint8_t maxChannels ); + +/*! + * \brief Counts the number of active channels in a given channels mask. + * This is a generic function and valid for all regions. + * + * \param [IN] channelsMask The channels mask of the region. + * + * \param [IN] startIdx Start index. + * + * \param [IN] stopIdx Stop index ( the channels of this index will not be counted ). + * + * \retval Returns the number of active channels. + */ +uint8_t RegionCommonCountChannels( uint16_t* channelsMask, uint8_t startIdx, uint8_t stopIdx ); + +/*! + * \brief Copy a channels mask. + * This is a generic function and valid for all regions. + * + * \param [IN] channelsMaskDest The destination channels mask. + * + * \param [IN] channelsMaskSrc The source channels mask. + * + * \param [IN] len The index length to copy. + */ +void RegionCommonChanMaskCopy( uint16_t* channelsMaskDest, uint16_t* channelsMaskSrc, uint8_t len ); + +/*! + * \brief Sets the last tx done property. + * This is a generic function and valid for all regions. + * + * \param [IN] band The band to be updated. + * + * \param [IN] lastTxAirTime The time on air of the last TX frame. + * + * \param [IN] joined Set to true if the device has joined. + * + * \param [IN] elapsedTimeSinceStartup Elapsed time since initialization. + */ +void RegionCommonSetBandTxDone( Band_t* band, TimerTime_t lastTxAirTime, bool joined, SysTime_t elapsedTimeSinceStartup ); + +/*! + * \brief Updates the time-offs of the bands. + * This is a generic function and valid for all regions. + * + * \param [IN] joined Set to true, if the node has joined the network + * + * \param [IN] bands A pointer to the bands. + * + * \param [IN] nbBands The number of bands available. + * + * \param [IN] dutyCycleEnabled Set to true, if the duty cycle is enabled. + * + * \param [IN] lastTxIsJoinRequest Set to true, if the last TX is a join request. + * + * \param [IN] elapsedTimeSinceStartup Elapsed time since start up. + * + * \param [IN] expectedTimeOnAir Expected time on air for the next transmission. + * + * \retval Returns the time which must be waited to perform the next uplink. + */ +TimerTime_t RegionCommonUpdateBandTimeOff( bool joined, Band_t* bands, + uint8_t nbBands, bool dutyCycleEnabled, + bool lastTxIsJoinRequest, SysTime_t elapsedTimeSinceStartup, + TimerTime_t expectedTimeOnAir ); + +/*! + * \brief Parses the parameter of an LinkAdrRequest. + * This is a generic function and valid for all regions. + * + * \param [IN] payload Pointer to the payload containing the MAC commands. The payload + * must contain the CMD identifier, following by the parameters. + * + * \param [OUT] parseLinkAdr The function fills the structure with the ADR parameters. + * + * \retval Returns the length of the ADR request, if a request was found. Otherwise, the + * function returns 0. + */ +uint8_t RegionCommonParseLinkAdrReq( uint8_t* payload, RegionCommonLinkAdrParams_t* parseLinkAdr ); + +/*! + * \brief Verifies and updates the datarate, the TX power and the number of repetitions + * of a LinkAdrRequest. This depends on the configuration of ADR also. + * + * \param [IN] verifyParams Pointer to a structure containing input parameters. + * + * \param [OUT] dr The updated datarate. + * + * \param [OUT] txPow The updated TX power. + * + * \param [OUT] nbRep The updated number of repetitions. + * + * \retval Returns the status according to the LinkAdrRequest definition. + */ +uint8_t RegionCommonLinkAdrReqVerifyParams( RegionCommonLinkAdrReqVerifyParams_t* verifyParams, int8_t* dr, int8_t* txPow, uint8_t* nbRep ); + +/*! + * \brief Computes the symbol time for LoRa modulation. + * + * \param [IN] phyDr Physical datarate to use. + * + * \param [IN] bandwidth Bandwidth to use. + * + * \retval Returns the symbol time in microseconds. + */ +uint32_t RegionCommonComputeSymbolTimeLoRa( uint8_t phyDr, uint32_t bandwidthInHz ); + +/*! + * \brief Computes the symbol time for FSK modulation. + * + * \param [IN] phyDr Physical datarate to use. + * + * \retval Returns the symbol time in microseconds. + */ +uint32_t RegionCommonComputeSymbolTimeFsk( uint8_t phyDrInKbps ); + +/*! + * \brief Computes the RX window timeout and the RX window offset. + * + * \param [IN] tSymbolInUs Symbol timeout. + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxErrorInMs System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxErrorInMs : +rxErrorInMs] ms interval around RxOffset. + * + * \param [IN] wakeUpTimeInMs Wakeup time of the system. + * + * \param [OUT] windowTimeoutInSymbols RX window timeout. + * + * \param [OUT] windowOffsetInMs RX window time offset to be applied to the RX delay. + */ +void RegionCommonComputeRxWindowParameters( uint32_t tSymbolInUs, uint8_t minRxSymbols, uint32_t rxErrorInMs, uint32_t wakeUpTimeInMs, uint32_t* windowTimeoutInSymbols, int32_t* windowOffsetInMs ); + +/*! + * \brief Computes the txPower, based on the max EIRP and the antenna gain. + * + * \remark US915 region uses a conducted power as input value for maxEirp. + * Thus, the antennaGain parameter must be set to 0. + * + * \param [IN] txPower TX power index. + * + * \param [IN] maxEirp Maximum EIRP. + * + * \param [IN] antennaGain Antenna gain. Referenced to the isotropic antenna. + * Value is in dBi. ( antennaGain[dBi] = measuredAntennaGain[dBd] + 2.15 ) + * + * \retval Returns the physical TX power. + */ +int8_t RegionCommonComputeTxPower( int8_t txPowerIndex, float maxEirp, float antennaGain ); + +/*! + * \brief Sets up the radio into RX beacon mode. + * + * \param [IN] rxBeaconSetupParams A pointer to the input parameters. + */ +void RegionCommonRxBeaconSetup( RegionCommonRxBeaconSetupParams_t* rxBeaconSetupParams ); + +/*! + * \brief Counts the number of enabled channels. + * + * \param [IN] countNbOfEnabledChannelsParams A pointer to the input parameters. + * + * \param [OUT] enabledChannels A pointer to an array of size XX_MAX_NB_CHANNELS. The function + * stores the available channels into this array. + * + * \param [OUT] nbEnabledChannels The number of available channels found. + * + * \param [OUT] nbRestrictedChannels It contains the number of channel + * which are available, but restricted due to duty cycle. + */ +void RegionCommonCountNbOfEnabledChannels( RegionCommonCountNbOfEnabledChannelsParams_t* countNbOfEnabledChannelsParams, + uint8_t* enabledChannels, uint8_t* nbEnabledChannels, uint8_t* nbRestrictedChannels ); + +/*! + * \brief Identifies all channels which are available currently. + * + * \param [IN] identifyChannelsParam A pointer to the input parameters. + * + * \param [OUT] aggregatedTimeOff The new value of the aggregatedTimeOff. The function + * may resets it to 0. + * + * \param [OUT] enabledChannels A pointer to an array of size XX_MAX_NB_CHANNELS. The function + * stores the available channels into this array. + * + * \param [OUT] nbEnabledChannels The number of available channels found. + * + * \param [OUT] nbRestrictedChannels It contains the number of channel + * which are available, but restricted due to duty cycle. + * + * \param [OUT] nextTxDelay Holds the time which has to be waited for the next possible + * uplink transmission. + * + *\retval Status of the operation. + */ +LoRaMacStatus_t RegionCommonIdentifyChannels( RegionCommonIdentifyChannelsParam_t* identifyChannelsParam, + TimerTime_t* aggregatedTimeOff, uint8_t* enabledChannels, + uint8_t* nbEnabledChannels, uint8_t* nbRestrictedChannels, + TimerTime_t* nextTxDelay ); + +/*! + * \brief Selects the next lower datarate. + * + * \param [IN] params Data structure providing parameters based on \ref RegionCommonGetNextLowerTxDrParams_t + * + * \retval The next lower datarate. + */ +int8_t RegionCommonGetNextLowerTxDr( RegionCommonGetNextLowerTxDrParams_t *params ); + +/*! + * \brief Limits the TX power. + * + * \param [IN] txPower Current TX power. + * + * \param [IN] maxBandTxPower Maximum possible TX power. + * + * \retval Limited TX power. + */ +int8_t RegionCommonLimitTxPower( int8_t txPower, int8_t maxBandTxPower ); + +/*! + * \brief Gets the bandwidth. + * + * \param [IN] drIndex Datarate index. + * + * \param [IN] bandwidths A pointer to the bandwidth table. + * + * \retval Bandwidth. + */ +uint32_t RegionCommonGetBandwidth( uint32_t drIndex, const uint32_t* bandwidths ); + +/*! \} defgroup REGIONCOMMON */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGIONCOMMON_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionEU433.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionEU433.c new file mode 100644 index 0000000000..a4a9df0ea2 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionEU433.c @@ -0,0 +1,929 @@ +/*! + * \file RegionEU433.c + * + * \brief Region implementation for EU433 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) +*/ +#include "radio.h" +#include "RegionCommon.h" +#include "RegionEU433.h" + +// Definitions +#define CHANNELS_MASK_SIZE 1 + +/* + * Non-volatile module context. + */ +static RegionNvmDataGroup1_t* RegionNvmGroup1; +static RegionNvmDataGroup2_t* RegionNvmGroup2; +static Band_t* RegionBands; + +// Static functions +static bool VerifyRfFreq( uint32_t freq ) +{ + // Check radio driver support + if( Radio.CheckRfFrequency( freq ) == false ) + { + return false; + } + + if( ( freq < 433175000 ) || ( freq > 434665000 ) ) + { + return false; + } + return true; +} + +static TimerTime_t GetTimeOnAir( int8_t datarate, uint16_t pktLen ) +{ + int8_t phyDr = DataratesEU433[datarate]; + uint32_t bandwidth = RegionCommonGetBandwidth( datarate, BandwidthsEU433 ); + TimerTime_t timeOnAir = 0; + + if( datarate == DR_7 ) + { // High Speed FSK channel + timeOnAir = Radio.TimeOnAir( MODEM_FSK, bandwidth, phyDr * 1000, 0, 5, false, pktLen, true ); + } + else + { + timeOnAir = Radio.TimeOnAir( MODEM_LORA, bandwidth, phyDr, 1, 8, false, pktLen, true ); + } + return timeOnAir; +} + +PhyParam_t RegionEU433GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + + switch( getPhy->Attribute ) + { + case PHY_MIN_RX_DR: + { + phyParam.Value = EU433_RX_MIN_DATARATE; + break; + } + case PHY_MIN_TX_DR: + { + phyParam.Value = EU433_TX_MIN_DATARATE; + break; + } + case PHY_DEF_TX_DR: + { + phyParam.Value = EU433_DEFAULT_DATARATE; + break; + } + case PHY_NEXT_LOWER_TX_DR: + { + RegionCommonGetNextLowerTxDrParams_t nextLowerTxDrParams = + { + .CurrentDr = getPhy->Datarate, + .MaxDr = ( int8_t )EU433_TX_MAX_DATARATE, + .MinDr = ( int8_t )EU433_TX_MIN_DATARATE, + .NbChannels = EU433_MAX_NB_CHANNELS, + .ChannelsMask = RegionNvmGroup2->ChannelsMask, + .Channels = RegionNvmGroup2->Channels, + }; + phyParam.Value = RegionCommonGetNextLowerTxDr( &nextLowerTxDrParams ); + break; + } + case PHY_MAX_TX_POWER: + { + phyParam.Value = EU433_MAX_TX_POWER; + break; + } + case PHY_DEF_TX_POWER: + { + phyParam.Value = EU433_DEFAULT_TX_POWER; + break; + } + case PHY_DEF_ADR_ACK_LIMIT: + { + phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_LIMIT; + break; + } + case PHY_DEF_ADR_ACK_DELAY: + { + phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_DELAY; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateEU433[getPhy->Datarate]; + break; + } + case PHY_DUTY_CYCLE: + { + phyParam.Value = EU433_DUTY_CYCLE_ENABLED; + break; + } + case PHY_MAX_RX_WINDOW: + { + phyParam.Value = EU433_MAX_RX_WINDOW; + break; + } + case PHY_RECEIVE_DELAY1: + { + phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY2; + break; + } + case PHY_RETRANSMIT_TIMEOUT: + { + phyParam.Value = ( REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT + randr( -REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND, REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND ) ); + break; + } + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = REGION_COMMON_DEFAULT_RX1_DR_OFFSET; + break; + } + case PHY_DEF_RX2_FREQUENCY: + { + phyParam.Value = EU433_RX_WND_2_FREQ; + break; + } + case PHY_DEF_RX2_DR: + { + phyParam.Value = EU433_RX_WND_2_DR; + break; + } + case PHY_CHANNELS_MASK: + { + phyParam.ChannelsMask = RegionNvmGroup2->ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = RegionNvmGroup2->ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = EU433_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = RegionNvmGroup2->Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + { + phyParam.Value = EU433_DEFAULT_UPLINK_DWELL_TIME; + break; + } + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = REGION_COMMON_DEFAULT_DOWNLINK_DWELL_TIME; + break; + } + case PHY_DEF_MAX_EIRP: + { + phyParam.fValue = EU433_DEFAULT_MAX_EIRP; + break; + } + case PHY_DEF_ANTENNA_GAIN: + { + phyParam.fValue = EU433_DEFAULT_ANTENNA_GAIN; + break; + } + case PHY_BEACON_CHANNEL_FREQ: + { + phyParam.Value = EU433_BEACON_CHANNEL_FREQ; + break; + } + case PHY_BEACON_FORMAT: + { + phyParam.BeaconFormat.BeaconSize = EU433_BEACON_SIZE; + phyParam.BeaconFormat.Rfu1Size = EU433_RFU1_SIZE; + phyParam.BeaconFormat.Rfu2Size = EU433_RFU2_SIZE; + break; + } + case PHY_BEACON_CHANNEL_DR: + { + phyParam.Value = EU433_BEACON_CHANNEL_DR; + break; + } + case PHY_PING_SLOT_CHANNEL_FREQ: + { + phyParam.Value = EU433_PING_SLOT_CHANNEL_FREQ; + break; + } + case PHY_PING_SLOT_CHANNEL_DR: + { + phyParam.Value = EU433_PING_SLOT_CHANNEL_DR; + break; + } + case PHY_SF_FROM_DR: + { + phyParam.Value = DataratesEU433[getPhy->Datarate]; + break; + } + case PHY_BW_FROM_DR: + { + phyParam.Value = RegionCommonGetBandwidth( getPhy->Datarate, BandwidthsEU433 ); + break; + } + default: + { + break; + } + } + + return phyParam; +} + +void RegionEU433SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ + RegionCommonSetBandTxDone( &RegionBands[RegionNvmGroup2->Channels[txDone->Channel].Band], + txDone->LastTxAirTime, txDone->Joined, txDone->ElapsedTimeSinceStartUp ); +} + +void RegionEU433InitDefaults( InitDefaultsParams_t* params ) +{ + Band_t bands[EU433_MAX_NB_BANDS] = + { + EU433_BAND0 + }; + + switch( params->Type ) + { + case INIT_TYPE_DEFAULTS: + { + if( ( params->NvmGroup1 == NULL ) || ( params->NvmGroup2 == NULL ) ) + { + return; + } + + RegionNvmGroup1 = (RegionNvmDataGroup1_t*) params->NvmGroup1; + RegionNvmGroup2 = (RegionNvmDataGroup2_t*) params->NvmGroup2; + RegionBands = (Band_t*) params->Bands; + + // Default bands + memcpy1( ( uint8_t* )RegionBands, ( uint8_t* )bands, sizeof( Band_t ) * EU433_MAX_NB_BANDS ); + + // Default channels + RegionNvmGroup2->Channels[0] = ( ChannelParams_t ) EU433_LC1; + RegionNvmGroup2->Channels[1] = ( ChannelParams_t ) EU433_LC2; + RegionNvmGroup2->Channels[2] = ( ChannelParams_t ) EU433_LC3; + + // Default ChannelsMask + RegionNvmGroup2->ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 ); + + // Update the channels mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + break; + } + case INIT_TYPE_RESET_TO_DEFAULT_CHANNELS: + { + // Reset Channels Rx1Frequency to default 0 + RegionNvmGroup2->Channels[0].Rx1Frequency = 0; + RegionNvmGroup2->Channels[1].Rx1Frequency = 0; + RegionNvmGroup2->Channels[2].Rx1Frequency = 0; + // Update the channels mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + break; + } + case INIT_TYPE_ACTIVATE_DEFAULT_CHANNELS: + { + // Restore channels default mask + RegionNvmGroup2->ChannelsMask[0] |= RegionNvmGroup2->ChannelsDefaultMask[0]; + break; + } + default: + { + break; + } + } +} + +bool RegionEU433Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( phyAttribute ) + { + case PHY_FREQUENCY: + { + return VerifyRfFreq( verify->Frequency ); + } + case PHY_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, EU433_TX_MIN_DATARATE, EU433_TX_MAX_DATARATE ); + } + case PHY_DEF_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 ); + } + case PHY_RX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, EU433_RX_MIN_DATARATE, EU433_RX_MAX_DATARATE ); + } + case PHY_DEF_TX_POWER: + case PHY_TX_POWER: + { + // Remark: switched min and max! + return RegionCommonValueInRange( verify->TxPower, EU433_MAX_TX_POWER, EU433_MIN_TX_POWER ); + } + case PHY_DUTY_CYCLE: + { + return EU433_DUTY_CYCLE_ENABLED; + } + default: + return false; + } +} + +void RegionEU433ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ + ChannelParams_t newChannel; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + // Setup default datarate range + newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0; + + // Size of the optional CF list + if( applyCFList->Size != 16 ) + { + return; + } + + // Last byte CFListType must be 0 to indicate the CFList contains a list of frequencies + if( applyCFList->Payload[15] != 0 ) + { + return; + } + + // Last byte is RFU, don't take it into account + for( uint8_t i = 0, chanIdx = EU433_NUMB_DEFAULT_CHANNELS; chanIdx < EU433_MAX_NB_CHANNELS; i+=3, chanIdx++ ) + { + if( chanIdx < ( EU433_NUMB_CHANNELS_CF_LIST + EU433_NUMB_DEFAULT_CHANNELS ) ) + { + // Channel frequency + newChannel.Frequency = (uint32_t) applyCFList->Payload[i]; + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 ); + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 ); + newChannel.Frequency *= 100; + + // Initialize alternative frequency to 0 + newChannel.Rx1Frequency = 0; + } + else + { + newChannel.Frequency = 0; + newChannel.DrRange.Value = 0; + newChannel.Rx1Frequency = 0; + } + + if( newChannel.Frequency != 0 ) + { + channelAdd.NewChannel = &newChannel; + channelAdd.ChannelId = chanIdx; + + // Try to add all channels + RegionEU433ChannelAdd( &channelAdd ); + } + else + { + channelRemove.ChannelId = chanIdx; + + RegionEU433ChannelsRemove( &channelRemove ); + } + } +} + +bool RegionEU433ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + default: + return false; + } + return true; +} + +void RegionEU433ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + uint32_t tSymbolInUs = 0; + + // Get the datarate, perform a boundary check + rxConfigParams->Datarate = MIN( datarate, EU433_RX_MAX_DATARATE ); + rxConfigParams->Bandwidth = RegionCommonGetBandwidth( rxConfigParams->Datarate, BandwidthsEU433 ); + + if( rxConfigParams->Datarate == DR_7 ) + { // FSK + tSymbolInUs = RegionCommonComputeSymbolTimeFsk( DataratesEU433[rxConfigParams->Datarate] ); + } + else + { // LoRa + tSymbolInUs = RegionCommonComputeSymbolTimeLoRa( DataratesEU433[rxConfigParams->Datarate], BandwidthsEU433[rxConfigParams->Datarate] ); + } + + RegionCommonComputeRxWindowParameters( tSymbolInUs, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +} + +bool RegionEU433RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ + RadioModems_t modem; + int8_t dr = rxConfig->Datarate; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->RxSlot == RX_SLOT_WIN_1 ) + { + // Apply window 1 frequency + frequency = RegionNvmGroup2->Channels[rxConfig->Channel].Frequency; + // Apply the alternative RX 1 window frequency, if it is available + if( RegionNvmGroup2->Channels[rxConfig->Channel].Rx1Frequency != 0 ) + { + frequency = RegionNvmGroup2->Channels[rxConfig->Channel].Rx1Frequency; + } + } + + // Read the physical datarate from the datarates table + phyDr = DataratesEU433[dr]; + + Radio.SetChannel( frequency ); + + // Radio configuration + if( dr == DR_7 ) + { + modem = MODEM_FSK; + Radio.SetRxConfig( modem, 50000, phyDr * 1000, 0, 83333, 5, rxConfig->WindowTimeout, false, 0, true, 0, 0, false, rxConfig->RxContinuous ); + } + else + { + modem = MODEM_LORA; + Radio.SetRxConfig( modem, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous ); + } + + Radio.SetMaxPayloadLength( modem, MaxPayloadOfDatarateEU433[dr] + LORAMAC_FRAME_PAYLOAD_OVERHEAD_SIZE ); + + *datarate = (uint8_t) dr; + return true; +} + +bool RegionEU433TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + RadioModems_t modem; + int8_t phyDr = DataratesEU433[txConfig->Datarate]; + int8_t txPowerLimited = RegionCommonLimitTxPower( txConfig->TxPower, RegionBands[RegionNvmGroup2->Channels[txConfig->Channel].Band].TxMaxPower ); + uint32_t bandwidth = RegionCommonGetBandwidth( txConfig->Datarate, BandwidthsEU433 ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( RegionNvmGroup2->Channels[txConfig->Channel].Frequency ); + + if( txConfig->Datarate == DR_7 ) + { // High Speed FSK channel + modem = MODEM_FSK; + Radio.SetTxConfig( modem, phyTxPower, 25000, bandwidth, phyDr * 1000, 0, 5, false, true, 0, 0, false, 4000 ); + } + else + { + modem = MODEM_LORA; + Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 4000 ); + } + + // Update time-on-air + *txTimeOnAir = GetTimeOnAir( txConfig->Datarate, txConfig->PktLen ); + + // Setup maximum payload lenght of the radio driver + Radio.SetMaxPayloadLength( modem, txConfig->PktLen ); + + *txPower = txPowerLimited; + return true; +} + +uint8_t RegionEU433LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; + RegionCommonLinkAdrParams_t linkAdrParams = { 0 }; + uint8_t nextIndex = 0; + uint8_t bytesProcessed = 0; + uint16_t chMask = 0; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams; + + while( bytesProcessed < linkAdrReq->PayloadSize ) + { + // Get ADR request parameters + nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); + + if( nextIndex == 0 ) + break; // break loop, since no more request has been found + + // Update bytes processed + bytesProcessed += nextIndex; + + // Revert status, as we only check the last ADR request for the channel mask KO + status = 0x07; + + // Setup temporary channels mask + chMask = linkAdrParams.ChMask; + + // Verify channels mask + if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) ) + { + status &= 0xFE; // Channel mask KO + } + else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) || + ( linkAdrParams.ChMaskCtrl >= 7 ) ) + { + // RFU + status &= 0xFE; // Channel mask KO + } + else + { + for( uint8_t i = 0; i < EU433_MAX_NB_CHANNELS; i++ ) + { + if( linkAdrParams.ChMaskCtrl == 6 ) + { + if( RegionNvmGroup2->Channels[i].Frequency != 0 ) + { + chMask |= 1 << i; + } + } + else + { + if( ( ( chMask & ( 1 << i ) ) != 0 ) && + ( RegionNvmGroup2->Channels[i].Frequency == 0 ) ) + {// Trying to enable an undefined channel + status &= 0xFE; // Channel mask KO + } + } + } + } + } + + // Get the minimum possible datarate + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; + phyParam = RegionEU433GetPhyParam( &getPhy ); + + linkAdrVerifyParams.Status = status; + linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; + linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; + linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; + linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; + linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; + linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; + linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; + linkAdrVerifyParams.NbChannels = EU433_MAX_NB_CHANNELS; + linkAdrVerifyParams.ChannelsMask = &chMask; + linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value; + linkAdrVerifyParams.MaxDatarate = EU433_TX_MAX_DATARATE; + linkAdrVerifyParams.Channels = RegionNvmGroup2->Channels; + linkAdrVerifyParams.MinTxPower = EU433_MIN_TX_POWER; + linkAdrVerifyParams.MaxTxPower = EU433_MAX_TX_POWER; + linkAdrVerifyParams.Version = linkAdrReq->Version; + + // Verify the parameters and update, if necessary + status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep ); + + // Update channelsMask if everything is correct + if( status == 0x07 ) + { + // Set the channels mask to a default value + memset1( ( uint8_t* ) RegionNvmGroup2->ChannelsMask, 0, sizeof( RegionNvmGroup2->ChannelsMask ) ); + // Update the channels mask + RegionNvmGroup2->ChannelsMask[0] = chMask; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + + return status; +} + +uint8_t RegionEU433RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; + + // Verify radio frequency + if( VerifyRfFreq( rxParamSetupReq->Frequency ) == false ) + { + status &= 0xFE; // Channel frequency KO + } + + // Verify datarate + if( RegionCommonValueInRange( rxParamSetupReq->Datarate, EU433_RX_MIN_DATARATE, EU433_RX_MAX_DATARATE ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify datarate offset + if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, EU433_MIN_RX1_DR_OFFSET, EU433_MAX_RX1_DR_OFFSET ) == false ) + { + status &= 0xFB; // Rx1DrOffset range KO + } + + return status; +} + +int8_t RegionEU433NewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + uint8_t status = 0x03; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + if( newChannelReq->NewChannel->Frequency == 0 ) + { + channelRemove.ChannelId = newChannelReq->ChannelId; + + // Remove + if( RegionEU433ChannelsRemove( &channelRemove ) == false ) + { + status &= 0xFC; + } + } + else + { + channelAdd.NewChannel = newChannelReq->NewChannel; + channelAdd.ChannelId = newChannelReq->ChannelId; + + switch( RegionEU433ChannelAdd( &channelAdd ) ) + { + case LORAMAC_STATUS_OK: + { + break; + } + case LORAMAC_STATUS_FREQUENCY_INVALID: + { + status &= 0xFE; + break; + } + case LORAMAC_STATUS_DATARATE_INVALID: + { + status &= 0xFD; + break; + } + case LORAMAC_STATUS_FREQ_AND_DR_INVALID: + { + status &= 0xFC; + break; + } + default: + { + status &= 0xFC; + break; + } + } + } + + return status; +} + +int8_t RegionEU433TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + // Do not accept the request + return -1; +} + +int8_t RegionEU433DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + uint8_t status = 0x03; + + if( dlChannelReq->ChannelId >= ( CHANNELS_MASK_SIZE * 16 ) ) + { + return 0; + } + + // Verify if the frequency is supported + if( VerifyRfFreq( dlChannelReq->Rx1Frequency ) == false ) + { + status &= 0xFE; + } + + // Verify if an uplink frequency exists + if( RegionNvmGroup2->Channels[dlChannelReq->ChannelId].Frequency == 0 ) + { + status &= 0xFD; + } + + // Apply Rx1 frequency, if the status is OK + if( status == 0x03 ) + { + RegionNvmGroup2->Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency; + } + + return status; +} + +int8_t RegionEU433AlternateDr( int8_t currentDr, AlternateDrType_t type ) +{ + return currentDr; +} + +LoRaMacStatus_t RegionEU433NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t nbRestrictedChannels = 0; + uint8_t enabledChannels[EU433_MAX_NB_CHANNELS] = { 0 }; + RegionCommonIdentifyChannelsParam_t identifyChannelsParam; + RegionCommonCountNbOfEnabledChannelsParams_t countChannelsParams; + LoRaMacStatus_t status = LORAMAC_STATUS_NO_CHANNEL_FOUND; + uint16_t joinChannels = EU433_JOIN_CHANNELS; + + if( RegionCommonCountChannels( RegionNvmGroup2->ChannelsMask, 0, 1 ) == 0 ) + { // Reactivate default channels + RegionNvmGroup2->ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + + // Search how many channels are enabled + countChannelsParams.Joined = nextChanParams->Joined; + countChannelsParams.Datarate = nextChanParams->Datarate; + countChannelsParams.ChannelsMask = RegionNvmGroup2->ChannelsMask; + countChannelsParams.Channels = RegionNvmGroup2->Channels; + countChannelsParams.Bands = RegionBands; + countChannelsParams.MaxNbChannels = EU433_MAX_NB_CHANNELS; + countChannelsParams.JoinChannels = &joinChannels; + + identifyChannelsParam.AggrTimeOff = nextChanParams->AggrTimeOff; + identifyChannelsParam.LastAggrTx = nextChanParams->LastAggrTx; + identifyChannelsParam.DutyCycleEnabled = nextChanParams->DutyCycleEnabled; + identifyChannelsParam.MaxBands = EU433_MAX_NB_BANDS; + + identifyChannelsParam.ElapsedTimeSinceStartUp = nextChanParams->ElapsedTimeSinceStartUp; + identifyChannelsParam.LastTxIsJoinRequest = nextChanParams->LastTxIsJoinRequest; + identifyChannelsParam.ExpectedTimeOnAir = GetTimeOnAir( nextChanParams->Datarate, nextChanParams->PktLen ); + + identifyChannelsParam.CountNbOfEnabledChannelsParam = &countChannelsParams; + + status = RegionCommonIdentifyChannels( &identifyChannelsParam, aggregatedTimeOff, enabledChannels, + &nbEnabledChannels, &nbRestrictedChannels, time ); + + if( status == LORAMAC_STATUS_OK ) + { + // We found a valid channel + *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; + } + else if( status == LORAMAC_STATUS_NO_CHANNEL_FOUND ) + { + // Datarate not supported by any channel, restore defaults + RegionNvmGroup2->ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + return status; +} + +LoRaMacStatus_t RegionEU433ChannelAdd( ChannelAddParams_t* channelAdd ) +{ + bool drInvalid = false; + bool freqInvalid = false; + uint8_t id = channelAdd->ChannelId; + + if( id < EU433_NUMB_DEFAULT_CHANNELS ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + + if( id >= EU433_MAX_NB_CHANNELS ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + // Validate the datarate range + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, EU433_TX_MIN_DATARATE, EU433_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, EU433_TX_MIN_DATARATE, EU433_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max ) + { + drInvalid = true; + } + + // Check frequency + if( freqInvalid == false ) + { + if( VerifyRfFreq( channelAdd->NewChannel->Frequency ) == false ) + { + freqInvalid = true; + } + } + + // Check status + if( ( drInvalid == true ) && ( freqInvalid == true ) ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + if( drInvalid == true ) + { + return LORAMAC_STATUS_DATARATE_INVALID; + } + if( freqInvalid == true ) + { + return LORAMAC_STATUS_FREQUENCY_INVALID; + } + + memcpy1( ( uint8_t* ) &(RegionNvmGroup2->Channels[id]), ( uint8_t* ) channelAdd->NewChannel, sizeof( RegionNvmGroup2->Channels[id] ) ); + RegionNvmGroup2->Channels[id].Band = 0; + RegionNvmGroup2->ChannelsMask[0] |= ( 1 << id ); + return LORAMAC_STATUS_OK; +} + +bool RegionEU433ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + uint8_t id = channelRemove->ChannelId; + + if( id < EU433_NUMB_DEFAULT_CHANNELS ) + { + return false; + } + + // Remove the channel from the list of channels + RegionNvmGroup2->Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 }; + + return RegionCommonChanDisable( RegionNvmGroup2->ChannelsMask, id, EU433_MAX_NB_CHANNELS ); +} + +uint8_t RegionEU433ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + int8_t datarate = dr - drOffset; + + if( datarate < 0 ) + { + datarate = DR_0; + } + return datarate; +} + +void RegionEU433RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ) +{ + RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup; + + regionCommonRxBeaconSetup.Datarates = DataratesEU433; + regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency; + regionCommonRxBeaconSetup.BeaconSize = EU433_BEACON_SIZE; + regionCommonRxBeaconSetup.BeaconDatarate = EU433_BEACON_CHANNEL_DR; + regionCommonRxBeaconSetup.BeaconChannelBW = EU433_BEACON_CHANNEL_BW; + regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime; + regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout; + + RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup ); + + // Store downlink datarate + *outDr = EU433_BEACON_CHANNEL_DR; +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionEU433.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionEU433.h new file mode 100644 index 0000000000..d7e6ac869e --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionEU433.h @@ -0,0 +1,450 @@ +/*! + * \file RegionEU433.h + * + * \brief Region definition for EU433 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * \defgroup REGIONEU433 Region EU433 + * Implementation according to LoRaWAN Specification v1.0.2. + * \{ + */ +#ifndef __REGION_EU433_H__ +#define __REGION_EU433_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "region/Region.h" + +/*! + * LoRaMac maximum number of channels + */ +#define EU433_MAX_NB_CHANNELS 16 + +/*! + * Number of default channels + */ +#define EU433_NUMB_DEFAULT_CHANNELS 3 + +/*! + * Number of channels to apply for the CF list + */ +#define EU433_NUMB_CHANNELS_CF_LIST 5 + +/*! + * Minimal datarate that can be used by the node + */ +#define EU433_TX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define EU433_TX_MAX_DATARATE DR_7 + +/*! + * Minimal datarate that can be used by the node + */ +#define EU433_RX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define EU433_RX_MAX_DATARATE DR_7 + +/*! + * Default datarate used by the node + */ +#define EU433_DEFAULT_DATARATE DR_0 + +/*! + * Minimal Rx1 receive datarate offset + */ +#define EU433_MIN_RX1_DR_OFFSET 0 + +/*! + * Maximal Rx1 receive datarate offset + */ +#define EU433_MAX_RX1_DR_OFFSET 5 + +/*! + * Minimal Tx output power that can be used by the node + */ +#define EU433_MIN_TX_POWER TX_POWER_5 + +/*! + * Maximal Tx output power that can be used by the node + */ +#define EU433_MAX_TX_POWER TX_POWER_0 + +/*! + * Default Tx output power used by the node + */ +#define EU433_DEFAULT_TX_POWER TX_POWER_0 + +/*! + * Default Max EIRP + */ +#define EU433_DEFAULT_MAX_EIRP 12.15f + +/*! + * Default antenna gain + */ +#define EU433_DEFAULT_ANTENNA_GAIN 2.15f + +/*! + * Enabled or disabled the duty cycle + */ +#define EU433_DUTY_CYCLE_ENABLED 1 + +/*! + * Maximum RX window duration + */ +#define EU433_MAX_RX_WINDOW 3000 + +/*! + * Verification of default datarate + */ +#if ( EU433_DEFAULT_DATARATE > DR_5 ) +#error "A default DR higher than DR_5 may lead to connectivity loss." +#endif + +/*! + * Second reception window channel frequency definition. + */ +#define EU433_RX_WND_2_FREQ 434665000 + +/*! + * Second reception window channel datarate definition. + */ +#define EU433_RX_WND_2_DR DR_0 + +/*! + * LoRaMac maximum number of bands + */ +#define EU433_MAX_NB_BANDS 1 + +/*! + * Default uplink dwell time configuration + */ +#define EU433_DEFAULT_UPLINK_DWELL_TIME 0 + +/* + * CLASS B + */ +/*! + * Beacon frequency + */ +#define EU433_BEACON_CHANNEL_FREQ 434665000 + +/*! + * Ping slot channel frequency + */ +#define EU433_PING_SLOT_CHANNEL_FREQ 434665000 + +/*! + * Payload size of a beacon frame + */ +#define EU433_BEACON_SIZE 17 + +/*! + * Size of RFU 1 field + */ +#define EU433_RFU1_SIZE 1 + +/*! + * Size of RFU 2 field + */ +#define EU433_RFU2_SIZE 0 + +/*! + * Datarate of the beacon channel + */ +#define EU433_BEACON_CHANNEL_DR DR_3 + +/*! + * Bandwith of the beacon channel + */ +#define EU433_BEACON_CHANNEL_BW 0 + +/*! + * Ping slot channel datarate + */ +#define EU433_PING_SLOT_CHANNEL_DR DR_3 + +/*! + * Band 0 definition + * Band = { DutyCycle, TxMaxPower, LastBandUpdateTime, LastMaxCreditAssignTime, TimeCredits, MaxTimeCredits, ReadyForTransmission } + */ +#define EU433_BAND0 { 100, EU433_MAX_TX_POWER, 0, 0, 0, 0, 0 } // 1.0 % + +/*! + * LoRaMac default channel 1 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define EU433_LC1 { 433175000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac default channel 2 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define EU433_LC2 { 433375000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac default channel 3 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define EU433_LC3 { 433575000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac channels which are allowed for the join procedure + */ +#define EU433_JOIN_CHANNELS ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) ) + +/*! + * Data rates table definition + */ +static const uint8_t DataratesEU433[] = { 12, 11, 10, 9, 8, 7, 7, 50 }; + +/*! + * Bandwidths table definition in Hz + */ +static const uint32_t BandwidthsEU433[] = { 125000, 125000, 125000, 125000, 125000, 125000, 250000, 0 }; + +/*! + * Maximum payload with respect to the datarate index. + */ +static const uint8_t MaxPayloadOfDatarateEU433[] = { 51, 51, 51, 115, 242, 242, 242, 242 }; + +/*! + * \brief The function gets a value of a specific phy attribute. + * + * \param [IN] getPhy Pointer to the function parameters. + * + * \retval Returns a structure containing the PHY parameter. + */ +PhyParam_t RegionEU433GetPhyParam( GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionEU433SetBandTxDone( SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] type Sets the initialization type. + */ +void RegionEU433InitDefaults( InitDefaultsParams_t* params ); + +/*! + * \brief Verifies a parameter. + * + * \param [IN] verify Pointer to the function parameters. + * + * \param [IN] type Sets the initialization type. + * + * \retval Returns true, if the parameter is valid. + */ +bool RegionEU433Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + +/*! + * \brief The function parses the input buffer and sets up the channels of the + * CF list. + * + * \param [IN] applyCFList Pointer to the function parameters. + */ +void RegionEU433ApplyCFList( ApplyCFListParams_t* applyCFList ); + +/*! + * \brief Sets a channels mask. + * + * \param [IN] chanMaskSet Pointer to the function parameters. + * + * \retval Returns true, if the channels mask could be set. + */ +bool RegionEU433ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ); + +/*! + * Computes the Rx window timeout and offset. + * + * \param [IN] datarate Rx window datarate index to be used + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms + * interval around RxOffset + * + * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields. + */ +void RegionEU433ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ); + +/*! + * \brief Configuration of the RX windows. + * + * \param [IN] rxConfig Pointer to the function parameters. + * + * \param [OUT] datarate The datarate index which was set. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionEU433RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ); + +/*! + * \brief TX configuration. + * + * \param [IN] txConfig Pointer to the function parameters. + * + * \param [OUT] txPower The tx power index which was set. + * + * \param [OUT] txTimeOnAir The time-on-air of the frame. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionEU433TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ); + +/*! + * \brief The function processes a Link ADR Request. + * + * \param [IN] linkAdrReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionEU433LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ); + +/*! + * \brief The function processes a RX Parameter Setup Request. + * + * \param [IN] rxParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionEU433RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ); + +/*! + * \brief The function processes a Channel Request. + * + * \param [IN] newChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +int8_t RegionEU433NewChannelReq( NewChannelReqParams_t* newChannelReq ); + +/*! + * \brief The function processes a TX ParamSetup Request. + * + * \param [IN] txParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + * Returns -1, if the functionality is not implemented. In this case, the end node + * shall not process the command. + */ +int8_t RegionEU433TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ); + +/*! + * \brief The function processes a DlChannel Request. + * + * \param [IN] dlChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +int8_t RegionEU433DlChannelReq( DlChannelReqParams_t* dlChannelReq ); + +/*! + * \brief Alternates the datarate of the channel for the join request. + * + * \param [IN] currentDr Current datarate. + * + * \retval Datarate to apply. + */ +int8_t RegionEU433AlternateDr( int8_t currentDr, AlternateDrType_t type ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [OUT] channel Next channel to use for TX. + * + * \param [OUT] time Time to wait for the next transmission according to the duty + * cycle. + * + * \param [OUT] aggregatedTimeOff Updates the aggregated time off. + * + * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate] + */ +LoRaMacStatus_t RegionEU433NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ); + +/*! + * \brief Adds a channel. + * + * \param [IN] channelAdd Pointer to the function parameters. + * + * \retval Status of the operation. + */ +LoRaMacStatus_t RegionEU433ChannelAdd( ChannelAddParams_t* channelAdd ); + +/*! + * \brief Removes a channel. + * + * \param [IN] channelRemove Pointer to the function parameters. + * + * \retval Returns true, if the channel was removed successfully. + */ +bool RegionEU433ChannelsRemove( ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms + * + * \param [IN] dr Current datarate + * + * \param [IN] drOffset Offset to be applied + * + * \retval newDr Computed datarate. + */ +uint8_t RegionEU433ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! + * \brief Sets the radio into beacon reception mode + * + * \param [IN] rxBeaconSetup Pointer to the function parameters + */ +void RegionEU433RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ); + +/*! \} defgroup REGIONEU433 */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGION_EU433_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionEU868.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionEU868.c new file mode 100644 index 0000000000..d0b5dc691f --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionEU868.c @@ -0,0 +1,963 @@ +/*! + * \file RegionEU868.c + * + * \brief Region implementation for EU868 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) +*/ +#include "radio.h" +#include "RegionCommon.h" +#include "RegionEU868.h" + +// Definitions +#define CHANNELS_MASK_SIZE 1 + +/* + * Non-volatile module context. + */ +static RegionNvmDataGroup1_t* RegionNvmGroup1; +static RegionNvmDataGroup2_t* RegionNvmGroup2; +static Band_t* RegionBands; + +// Static functions +static bool VerifyRfFreq( uint32_t freq, uint8_t *band ) +{ + // Check radio driver support + if( Radio.CheckRfFrequency( freq ) == false ) + { + return false; + } + + // Check frequency bands + if( ( freq >= 863000000 ) && ( freq < 865000000 ) ) + { + *band = 2; + } + else if( ( freq >= 865000000 ) && ( freq <= 868000000 ) ) + { + *band = 0; + } + else if( ( freq > 868000000 ) && ( freq <= 868600000 ) ) + { + *band = 1; + } + else if( ( freq >= 868700000 ) && ( freq <= 869200000 ) ) + { + *band = 5; + } + else if( ( freq >= 869400000 ) && ( freq <= 869650000 ) ) + { + *band = 3; + } + else if( ( freq >= 869700000 ) && ( freq <= 870000000 ) ) + { + *band = 4; + } + else + { + return false; + } + return true; +} + +static TimerTime_t GetTimeOnAir( int8_t datarate, uint16_t pktLen ) +{ + int8_t phyDr = DataratesEU868[datarate]; + uint32_t bandwidth = RegionCommonGetBandwidth( datarate, BandwidthsEU868 ); + TimerTime_t timeOnAir = 0; + + if( datarate == DR_7 ) + { // High Speed FSK channel + timeOnAir = Radio.TimeOnAir( MODEM_FSK, bandwidth, phyDr * 1000, 0, 5, false, pktLen, true ); + } + else + { + timeOnAir = Radio.TimeOnAir( MODEM_LORA, bandwidth, phyDr, 1, 8, false, pktLen, true ); + } + return timeOnAir; +} + +PhyParam_t RegionEU868GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + + switch( getPhy->Attribute ) + { + case PHY_MIN_RX_DR: + { + phyParam.Value = EU868_RX_MIN_DATARATE; + break; + } + case PHY_MIN_TX_DR: + { + phyParam.Value = EU868_TX_MIN_DATARATE; + break; + } + case PHY_DEF_TX_DR: + { + phyParam.Value = EU868_DEFAULT_DATARATE; + break; + } + case PHY_NEXT_LOWER_TX_DR: + { + RegionCommonGetNextLowerTxDrParams_t nextLowerTxDrParams = + { + .CurrentDr = getPhy->Datarate, + .MaxDr = ( int8_t )EU868_TX_MAX_DATARATE, + .MinDr = ( int8_t )EU868_TX_MIN_DATARATE, + .NbChannels = EU868_MAX_NB_CHANNELS, + .ChannelsMask = RegionNvmGroup2->ChannelsMask, + .Channels = RegionNvmGroup2->Channels, + }; + phyParam.Value = RegionCommonGetNextLowerTxDr( &nextLowerTxDrParams ); + break; + } + case PHY_MAX_TX_POWER: + { + phyParam.Value = EU868_MAX_TX_POWER; + break; + } + case PHY_DEF_TX_POWER: + { + phyParam.Value = EU868_DEFAULT_TX_POWER; + break; + } + case PHY_DEF_ADR_ACK_LIMIT: + { + phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_LIMIT; + break; + } + case PHY_DEF_ADR_ACK_DELAY: + { + phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_DELAY; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateEU868[getPhy->Datarate]; + break; + } + case PHY_DUTY_CYCLE: + { + phyParam.Value = EU868_DUTY_CYCLE_ENABLED; + break; + } + case PHY_MAX_RX_WINDOW: + { + phyParam.Value = EU868_MAX_RX_WINDOW; + break; + } + case PHY_RECEIVE_DELAY1: + { + phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY2; + break; + } + case PHY_RETRANSMIT_TIMEOUT: + { + phyParam.Value = ( REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT + randr( -REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND, REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND ) ); + break; + } + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = REGION_COMMON_DEFAULT_RX1_DR_OFFSET; + break; + } + case PHY_DEF_RX2_FREQUENCY: + { + phyParam.Value = EU868_RX_WND_2_FREQ; + break; + } + case PHY_DEF_RX2_DR: + { + phyParam.Value = EU868_RX_WND_2_DR; + break; + } + case PHY_CHANNELS_MASK: + { + phyParam.ChannelsMask = RegionNvmGroup2->ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = RegionNvmGroup2->ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = EU868_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = RegionNvmGroup2->Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + { + phyParam.Value = EU868_DEFAULT_UPLINK_DWELL_TIME; + break; + } + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = REGION_COMMON_DEFAULT_DOWNLINK_DWELL_TIME; + break; + } + case PHY_DEF_MAX_EIRP: + { + phyParam.fValue = EU868_DEFAULT_MAX_EIRP; + break; + } + case PHY_DEF_ANTENNA_GAIN: + { + phyParam.fValue = EU868_DEFAULT_ANTENNA_GAIN; + break; + } + case PHY_BEACON_CHANNEL_FREQ: + { + phyParam.Value = EU868_BEACON_CHANNEL_FREQ; + break; + } + case PHY_BEACON_FORMAT: + { + phyParam.BeaconFormat.BeaconSize = EU868_BEACON_SIZE; + phyParam.BeaconFormat.Rfu1Size = EU868_RFU1_SIZE; + phyParam.BeaconFormat.Rfu2Size = EU868_RFU2_SIZE; + break; + } + case PHY_BEACON_CHANNEL_DR: + { + phyParam.Value = EU868_BEACON_CHANNEL_DR; + break; + } + case PHY_PING_SLOT_CHANNEL_FREQ: + { + phyParam.Value = EU868_PING_SLOT_CHANNEL_FREQ; + break; + } + case PHY_PING_SLOT_CHANNEL_DR: + { + phyParam.Value = EU868_PING_SLOT_CHANNEL_DR; + break; + } + case PHY_SF_FROM_DR: + { + phyParam.Value = DataratesEU868[getPhy->Datarate]; + break; + } + case PHY_BW_FROM_DR: + { + phyParam.Value = RegionCommonGetBandwidth( getPhy->Datarate, BandwidthsEU868 ); + break; + } + default: + { + break; + } + } + + return phyParam; +} + +void RegionEU868SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ + RegionCommonSetBandTxDone( &RegionBands[RegionNvmGroup2->Channels[txDone->Channel].Band], + txDone->LastTxAirTime, txDone->Joined, txDone->ElapsedTimeSinceStartUp ); +} + +void RegionEU868InitDefaults( InitDefaultsParams_t* params ) +{ + Band_t bands[EU868_MAX_NB_BANDS] = + { + EU868_BAND0, + EU868_BAND1, + EU868_BAND2, + EU868_BAND3, + EU868_BAND4, + EU868_BAND5, + }; + + switch( params->Type ) + { + case INIT_TYPE_DEFAULTS: + { + if( ( params->NvmGroup1 == NULL ) || ( params->NvmGroup2 == NULL ) ) + { + return; + } + + RegionNvmGroup1 = (RegionNvmDataGroup1_t*) params->NvmGroup1; + RegionNvmGroup2 = (RegionNvmDataGroup2_t*) params->NvmGroup2; + RegionBands = (Band_t*) params->Bands; + + // Default bands + memcpy1( ( uint8_t* )RegionBands, ( uint8_t* )bands, sizeof( Band_t ) * EU868_MAX_NB_BANDS ); + + // Default channels + RegionNvmGroup2->Channels[0] = ( ChannelParams_t ) EU868_LC1; + RegionNvmGroup2->Channels[1] = ( ChannelParams_t ) EU868_LC2; + RegionNvmGroup2->Channels[2] = ( ChannelParams_t ) EU868_LC3; + + // Default ChannelsMask + RegionNvmGroup2->ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 ); + + // Update the channels mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + break; + } + case INIT_TYPE_RESET_TO_DEFAULT_CHANNELS: + { + // Reset Channels Rx1Frequency to default 0 + RegionNvmGroup2->Channels[0].Rx1Frequency = 0; + RegionNvmGroup2->Channels[1].Rx1Frequency = 0; + RegionNvmGroup2->Channels[2].Rx1Frequency = 0; + // Update the channels mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + break; + } + case INIT_TYPE_ACTIVATE_DEFAULT_CHANNELS: + { + // Restore channels default mask + RegionNvmGroup2->ChannelsMask[0] |= RegionNvmGroup2->ChannelsDefaultMask[0]; + break; + } + default: + { + break; + } + } +} + +bool RegionEU868Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( phyAttribute ) + { + case PHY_FREQUENCY: + { + uint8_t band = 0; + return VerifyRfFreq( verify->Frequency, &band ); + } + case PHY_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, EU868_TX_MIN_DATARATE, EU868_TX_MAX_DATARATE ); + } + case PHY_DEF_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 ); + } + case PHY_RX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, EU868_RX_MIN_DATARATE, EU868_RX_MAX_DATARATE ); + } + case PHY_DEF_TX_POWER: + case PHY_TX_POWER: + { + // Remark: switched min and max! + return RegionCommonValueInRange( verify->TxPower, EU868_MAX_TX_POWER, EU868_MIN_TX_POWER ); + } + case PHY_DUTY_CYCLE: + { + return EU868_DUTY_CYCLE_ENABLED; + } + default: + return false; + } +} + +void RegionEU868ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ + ChannelParams_t newChannel; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + // Setup default datarate range + newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0; + + // Size of the optional CF list + if( applyCFList->Size != 16 ) + { + return; + } + + // Last byte CFListType must be 0 to indicate the CFList contains a list of frequencies + if( applyCFList->Payload[15] != 0 ) + { + return; + } + + // Last byte is RFU, don't take it into account + for( uint8_t i = 0, chanIdx = EU868_NUMB_DEFAULT_CHANNELS; chanIdx < EU868_MAX_NB_CHANNELS; i+=3, chanIdx++ ) + { + if( chanIdx < ( EU868_NUMB_CHANNELS_CF_LIST + EU868_NUMB_DEFAULT_CHANNELS ) ) + { + // Channel frequency + newChannel.Frequency = (uint32_t) applyCFList->Payload[i]; + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 ); + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 ); + newChannel.Frequency *= 100; + + // Initialize alternative frequency to 0 + newChannel.Rx1Frequency = 0; + } + else + { + newChannel.Frequency = 0; + newChannel.DrRange.Value = 0; + newChannel.Rx1Frequency = 0; + } + + if( newChannel.Frequency != 0 ) + { + channelAdd.NewChannel = &newChannel; + channelAdd.ChannelId = chanIdx; + + // Try to add all channels + RegionEU868ChannelAdd( &channelAdd ); + } + else + { + channelRemove.ChannelId = chanIdx; + + RegionEU868ChannelsRemove( &channelRemove ); + } + } +} + +bool RegionEU868ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, chanMaskSet->ChannelsMaskIn, CHANNELS_MASK_SIZE ); + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, CHANNELS_MASK_SIZE ); + break; + } + default: + return false; + } + return true; +} + +void RegionEU868ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + uint32_t tSymbolInUs = 0; + + // Get the datarate, perform a boundary check + rxConfigParams->Datarate = MIN( datarate, EU868_RX_MAX_DATARATE ); + rxConfigParams->Bandwidth = RegionCommonGetBandwidth( rxConfigParams->Datarate, BandwidthsEU868 ); + + if( rxConfigParams->Datarate == DR_7 ) + { // FSK + tSymbolInUs = RegionCommonComputeSymbolTimeFsk( DataratesEU868[rxConfigParams->Datarate] ); + } + else + { // LoRa + tSymbolInUs = RegionCommonComputeSymbolTimeLoRa( DataratesEU868[rxConfigParams->Datarate], BandwidthsEU868[rxConfigParams->Datarate] ); + } + + RegionCommonComputeRxWindowParameters( tSymbolInUs, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +} + +bool RegionEU868RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ + RadioModems_t modem; + int8_t dr = rxConfig->Datarate; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->RxSlot == RX_SLOT_WIN_1 ) + { + // Apply window 1 frequency + frequency = RegionNvmGroup2->Channels[rxConfig->Channel].Frequency; + // Apply the alternative RX 1 window frequency, if it is available + if( RegionNvmGroup2->Channels[rxConfig->Channel].Rx1Frequency != 0 ) + { + frequency = RegionNvmGroup2->Channels[rxConfig->Channel].Rx1Frequency; + } + } + + // Read the physical datarate from the datarates table + phyDr = DataratesEU868[dr]; + + Radio.SetChannel( frequency ); + + // Radio configuration + if( dr == DR_7 ) + { + modem = MODEM_FSK; + Radio.SetRxConfig( modem, 50000, phyDr * 1000, 0, 83333, 5, rxConfig->WindowTimeout, false, 0, true, 0, 0, false, rxConfig->RxContinuous ); + } + else + { + modem = MODEM_LORA; + Radio.SetRxConfig( modem, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous ); + } + + Radio.SetMaxPayloadLength( modem, MaxPayloadOfDatarateEU868[dr] + LORAMAC_FRAME_PAYLOAD_OVERHEAD_SIZE ); + + *datarate = (uint8_t) dr; + return true; +} + +bool RegionEU868TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + RadioModems_t modem; + int8_t phyDr = DataratesEU868[txConfig->Datarate]; + int8_t txPowerLimited = RegionCommonLimitTxPower( txConfig->TxPower, RegionBands[RegionNvmGroup2->Channels[txConfig->Channel].Band].TxMaxPower ); + uint32_t bandwidth = RegionCommonGetBandwidth( txConfig->Datarate, BandwidthsEU868 ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( RegionNvmGroup2->Channels[txConfig->Channel].Frequency ); + + if( txConfig->Datarate == DR_7 ) + { // High Speed FSK channel + modem = MODEM_FSK; + Radio.SetTxConfig( modem, phyTxPower, 25000, bandwidth, phyDr * 1000, 0, 5, false, true, 0, 0, false, 4000 ); + } + else + { + modem = MODEM_LORA; + Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 4000 ); + } + + // Update time-on-air + *txTimeOnAir = GetTimeOnAir( txConfig->Datarate, txConfig->PktLen ); + + // Setup maximum payload lenght of the radio driver + Radio.SetMaxPayloadLength( modem, txConfig->PktLen ); + + *txPower = txPowerLimited; + return true; +} + +uint8_t RegionEU868LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; + RegionCommonLinkAdrParams_t linkAdrParams = { 0 }; + uint8_t nextIndex = 0; + uint8_t bytesProcessed = 0; + uint16_t chMask = 0; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams; + + while( bytesProcessed < linkAdrReq->PayloadSize ) + { + // Get ADR request parameters + nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); + + if( nextIndex == 0 ) + break; // break loop, since no more request has been found + + // Update bytes processed + bytesProcessed += nextIndex; + + // Revert status, as we only check the last ADR request for the channel mask KO + status = 0x07; + + // Setup temporary channels mask + chMask = linkAdrParams.ChMask; + + // Verify channels mask + if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) ) + { + status &= 0xFE; // Channel mask KO + } + else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) || + ( linkAdrParams.ChMaskCtrl >= 7 ) ) + { + // RFU + status &= 0xFE; // Channel mask KO + } + else + { + for( uint8_t i = 0; i < EU868_MAX_NB_CHANNELS; i++ ) + { + if( linkAdrParams.ChMaskCtrl == 6 ) + { + if( RegionNvmGroup2->Channels[i].Frequency != 0 ) + { + chMask |= 1 << i; + } + } + else + { + if( ( ( chMask & ( 1 << i ) ) != 0 ) && + ( RegionNvmGroup2->Channels[i].Frequency == 0 ) ) + {// Trying to enable an undefined channel + status &= 0xFE; // Channel mask KO + } + } + } + } + } + + // Get the minimum possible datarate + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; + phyParam = RegionEU868GetPhyParam( &getPhy ); + + linkAdrVerifyParams.Status = status; + linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; + linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; + linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; + linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; + linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; + linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; + linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; + linkAdrVerifyParams.NbChannels = EU868_MAX_NB_CHANNELS; + linkAdrVerifyParams.ChannelsMask = &chMask; + linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value; + linkAdrVerifyParams.MaxDatarate = EU868_TX_MAX_DATARATE; + linkAdrVerifyParams.Channels = RegionNvmGroup2->Channels; + linkAdrVerifyParams.MinTxPower = EU868_MIN_TX_POWER; + linkAdrVerifyParams.MaxTxPower = EU868_MAX_TX_POWER; + linkAdrVerifyParams.Version = linkAdrReq->Version; + + // Verify the parameters and update, if necessary + status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep ); + + // Update channelsMask if everything is correct + if( status == 0x07 ) + { + // Set the channels mask to a default value + memset1( ( uint8_t* ) RegionNvmGroup2->ChannelsMask, 0, sizeof( RegionNvmGroup2->ChannelsMask ) ); + // Update the channels mask + RegionNvmGroup2->ChannelsMask[0] = chMask; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + + return status; +} + +uint8_t RegionEU868RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; + uint8_t band = 0; + + // Verify radio frequency + if( VerifyRfFreq( rxParamSetupReq->Frequency, &band ) == false ) + { + status &= 0xFE; // Channel frequency KO + } + + // Verify datarate + if( RegionCommonValueInRange( rxParamSetupReq->Datarate, EU868_RX_MIN_DATARATE, EU868_RX_MAX_DATARATE ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify datarate offset + if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, EU868_MIN_RX1_DR_OFFSET, EU868_MAX_RX1_DR_OFFSET ) == false ) + { + status &= 0xFB; // Rx1DrOffset range KO + } + + return status; +} + +int8_t RegionEU868NewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + uint8_t status = 0x03; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + if( newChannelReq->NewChannel->Frequency == 0 ) + { + channelRemove.ChannelId = newChannelReq->ChannelId; + + // Remove + if( RegionEU868ChannelsRemove( &channelRemove ) == false ) + { + status &= 0xFC; + } + } + else + { + channelAdd.NewChannel = newChannelReq->NewChannel; + channelAdd.ChannelId = newChannelReq->ChannelId; + + switch( RegionEU868ChannelAdd( &channelAdd ) ) + { + case LORAMAC_STATUS_OK: + { + break; + } + case LORAMAC_STATUS_FREQUENCY_INVALID: + { + status &= 0xFE; + break; + } + case LORAMAC_STATUS_DATARATE_INVALID: + { + status &= 0xFD; + break; + } + case LORAMAC_STATUS_FREQ_AND_DR_INVALID: + { + status &= 0xFC; + break; + } + default: + { + status &= 0xFC; + break; + } + } + } + + return status; +} + +int8_t RegionEU868TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + // Do not accept the request + return -1; +} + +int8_t RegionEU868DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + uint8_t status = 0x03; + uint8_t band = 0; + + if( dlChannelReq->ChannelId >= ( CHANNELS_MASK_SIZE * 16 ) ) + { + return 0; + } + + // Verify if the frequency is supported + if( VerifyRfFreq( dlChannelReq->Rx1Frequency, &band ) == false ) + { + status &= 0xFE; + } + + // Verify if an uplink frequency exists + if( RegionNvmGroup2->Channels[dlChannelReq->ChannelId].Frequency == 0 ) + { + status &= 0xFD; + } + + // Apply Rx1 frequency, if the status is OK + if( status == 0x03 ) + { + RegionNvmGroup2->Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency; + } + + return status; +} + +int8_t RegionEU868AlternateDr( int8_t currentDr, AlternateDrType_t type ) +{ + return currentDr; +} + +LoRaMacStatus_t RegionEU868NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t nbRestrictedChannels = 0; + uint8_t enabledChannels[EU868_MAX_NB_CHANNELS] = { 0 }; + RegionCommonIdentifyChannelsParam_t identifyChannelsParam; + RegionCommonCountNbOfEnabledChannelsParams_t countChannelsParams; + LoRaMacStatus_t status = LORAMAC_STATUS_NO_CHANNEL_FOUND; + uint16_t joinChannels = EU868_JOIN_CHANNELS; + + if( RegionCommonCountChannels( RegionNvmGroup2->ChannelsMask, 0, 1 ) == 0 ) + { // Reactivate default channels + RegionNvmGroup2->ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + + // Search how many channels are enabled + countChannelsParams.Joined = nextChanParams->Joined; + countChannelsParams.Datarate = nextChanParams->Datarate; + countChannelsParams.ChannelsMask = RegionNvmGroup2->ChannelsMask; + countChannelsParams.Channels = RegionNvmGroup2->Channels; + countChannelsParams.Bands = RegionBands; + countChannelsParams.MaxNbChannels = EU868_MAX_NB_CHANNELS; + countChannelsParams.JoinChannels = &joinChannels; + + identifyChannelsParam.AggrTimeOff = nextChanParams->AggrTimeOff; + identifyChannelsParam.LastAggrTx = nextChanParams->LastAggrTx; + identifyChannelsParam.DutyCycleEnabled = nextChanParams->DutyCycleEnabled; + identifyChannelsParam.MaxBands = EU868_MAX_NB_BANDS; + + identifyChannelsParam.ElapsedTimeSinceStartUp = nextChanParams->ElapsedTimeSinceStartUp; + identifyChannelsParam.LastTxIsJoinRequest = nextChanParams->LastTxIsJoinRequest; + identifyChannelsParam.ExpectedTimeOnAir = GetTimeOnAir( nextChanParams->Datarate, nextChanParams->PktLen ); + + identifyChannelsParam.CountNbOfEnabledChannelsParam = &countChannelsParams; + + status = RegionCommonIdentifyChannels( &identifyChannelsParam, aggregatedTimeOff, enabledChannels, + &nbEnabledChannels, &nbRestrictedChannels, time ); + + if( status == LORAMAC_STATUS_OK ) + { + // We found a valid channel + *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; + } + else if( status == LORAMAC_STATUS_NO_CHANNEL_FOUND ) + { + // Datarate not supported by any channel, restore defaults + RegionNvmGroup2->ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + return status; +} + +LoRaMacStatus_t RegionEU868ChannelAdd( ChannelAddParams_t* channelAdd ) +{ + uint8_t band = 0; + bool drInvalid = false; + bool freqInvalid = false; + uint8_t id = channelAdd->ChannelId; + + if( id < EU868_NUMB_DEFAULT_CHANNELS ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + + if( id >= EU868_MAX_NB_CHANNELS ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + // Validate the datarate range + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, EU868_TX_MIN_DATARATE, EU868_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, EU868_TX_MIN_DATARATE, EU868_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max ) + { + drInvalid = true; + } + + // Check frequency + if( freqInvalid == false ) + { + if( VerifyRfFreq( channelAdd->NewChannel->Frequency, &band ) == false ) + { + freqInvalid = true; + } + } + + // Check status + if( ( drInvalid == true ) && ( freqInvalid == true ) ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + if( drInvalid == true ) + { + return LORAMAC_STATUS_DATARATE_INVALID; + } + if( freqInvalid == true ) + { + return LORAMAC_STATUS_FREQUENCY_INVALID; + } + + memcpy1( ( uint8_t* ) &(RegionNvmGroup2->Channels[id]), ( uint8_t* ) channelAdd->NewChannel, sizeof( RegionNvmGroup2->Channels[id] ) ); + RegionNvmGroup2->Channels[id].Band = band; + RegionNvmGroup2->ChannelsMask[0] |= ( 1 << id ); + return LORAMAC_STATUS_OK; +} + +bool RegionEU868ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + uint8_t id = channelRemove->ChannelId; + + if( id < EU868_NUMB_DEFAULT_CHANNELS ) + { + return false; + } + + // Remove the channel from the list of channels + RegionNvmGroup2->Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 }; + + return RegionCommonChanDisable( RegionNvmGroup2->ChannelsMask, id, EU868_MAX_NB_CHANNELS ); +} + +uint8_t RegionEU868ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + int8_t datarate = dr - drOffset; + + if( datarate < 0 ) + { + datarate = DR_0; + } + return datarate; +} + +void RegionEU868RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ) +{ + RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup; + + regionCommonRxBeaconSetup.Datarates = DataratesEU868; + regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency; + regionCommonRxBeaconSetup.BeaconSize = EU868_BEACON_SIZE; + regionCommonRxBeaconSetup.BeaconDatarate = EU868_BEACON_CHANNEL_DR; + regionCommonRxBeaconSetup.BeaconChannelBW = EU868_BEACON_CHANNEL_BW; + regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime; + regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout; + + RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup ); + + // Store downlink datarate + *outDr = EU868_BEACON_CHANNEL_DR; +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionEU868.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionEU868.h new file mode 100644 index 0000000000..ebc2a0ee5c --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionEU868.h @@ -0,0 +1,478 @@ +/*! + * \file RegionEU868.h + * + * \brief Region definition for EU868 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * \defgroup REGIONEU868 Region EU868 + * Implementation according to LoRaWAN Specification v1.0.2. + * \{ + */ +#ifndef __REGION_EU868_H__ +#define __REGION_EU868_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "region/Region.h" + +/*! + * LoRaMac maximum number of channels + */ +#define EU868_MAX_NB_CHANNELS 16 + +/*! + * Number of default channels + */ +#define EU868_NUMB_DEFAULT_CHANNELS 3 + +/*! + * Number of channels to apply for the CF list + */ +#define EU868_NUMB_CHANNELS_CF_LIST 5 + +/*! + * Minimal datarate that can be used by the node + */ +#define EU868_TX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define EU868_TX_MAX_DATARATE DR_7 + +/*! + * Minimal datarate that can be used by the node + */ +#define EU868_RX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define EU868_RX_MAX_DATARATE DR_7 + +/*! + * Default datarate used by the node + */ +#define EU868_DEFAULT_DATARATE DR_0 + +/*! + * Minimal Rx1 receive datarate offset + */ +#define EU868_MIN_RX1_DR_OFFSET 0 + +/*! + * Maximal Rx1 receive datarate offset + */ +#define EU868_MAX_RX1_DR_OFFSET 5 + +/*! + * Minimal Tx output power that can be used by the node + */ +#define EU868_MIN_TX_POWER TX_POWER_7 + +/*! + * Maximal Tx output power that can be used by the node + */ +#define EU868_MAX_TX_POWER TX_POWER_0 + +/*! + * Default Tx output power used by the node + */ +#define EU868_DEFAULT_TX_POWER TX_POWER_0 + +/*! + * Default Max EIRP + */ +#define EU868_DEFAULT_MAX_EIRP 16.0f + +/*! + * Default antenna gain + */ +#define EU868_DEFAULT_ANTENNA_GAIN 2.15f + +/*! + * Enabled or disabled the duty cycle + */ +#define EU868_DUTY_CYCLE_ENABLED 1 + +/*! + * Maximum RX window duration + */ +#define EU868_MAX_RX_WINDOW 3000 + +#if ( EU868_DEFAULT_DATARATE > DR_5 ) +#error "A default DR higher than DR_5 may lead to connectivity loss." +#endif + +/*! + * Second reception window channel frequency definition. + */ +#define EU868_RX_WND_2_FREQ 869525000 + +/*! + * Second reception window channel datarate definition. + */ +#define EU868_RX_WND_2_DR DR_0 + +/*! + * Default uplink dwell time configuration + */ +#define EU868_DEFAULT_UPLINK_DWELL_TIME 0 + +/* + * CLASS B + */ +/*! + * Beacon frequency + */ +#define EU868_BEACON_CHANNEL_FREQ 869525000 + +/*! + * Ping slot channel frequency + */ +#define EU868_PING_SLOT_CHANNEL_FREQ 869525000 + +/*! + * Payload size of a beacon frame + */ +#define EU868_BEACON_SIZE 17 + +/*! + * Size of RFU 1 field + */ +#define EU868_RFU1_SIZE 1 + +/*! + * Size of RFU 2 field + */ +#define EU868_RFU2_SIZE 0 + +/*! + * Datarate of the beacon channel + */ +#define EU868_BEACON_CHANNEL_DR DR_3 + +/*! + * Bandwith of the beacon channel + */ +#define EU868_BEACON_CHANNEL_BW 0 + +/*! + * Ping slot channel datarate + */ +#define EU868_PING_SLOT_CHANNEL_DR DR_3 + +/*! + * Maximum number of bands + */ +#define EU868_MAX_NB_BANDS 6 + +/*! + * Band 0 definition + * Band = { DutyCycle, TxMaxPower, LastBandUpdateTime, LastMaxCreditAssignTime, TimeCredits, MaxTimeCredits, ReadyForTransmission } + */ +#define EU868_BAND0 { 100 , EU868_MAX_TX_POWER, 0, 0, 0, 0, 0 } // 1.0 % + +/*! + * Band 1 definition + * Band = { DutyCycle, TxMaxPower, LastBandUpdateTime, LastMaxCreditAssignTime, TimeCredits, MaxTimeCredits, ReadyForTransmission } + */ +#define EU868_BAND1 { 100 , EU868_MAX_TX_POWER, 0, 0, 0, 0, 0 } // 1.0 % + +/*! + * Band 2 definition + * Band = { DutyCycle, TxMaxPower, LastBandUpdateTime, LastMaxCreditAssignTime, TimeCredits, MaxTimeCredits, ReadyForTransmission } + */ +#define EU868_BAND2 { 1000, EU868_MAX_TX_POWER, 0, 0, 0, 0, 0 } // 0.1 % + +/*! + * Band 3 definition + * Band = { DutyCycle, TxMaxPower, LastBandUpdateTime, LastMaxCreditAssignTime, TimeCredits, MaxTimeCredits, ReadyForTransmission } + */ +#define EU868_BAND3 { 10 , EU868_MAX_TX_POWER, 0, 0, 0, 0, 0 } // 10.0 % + +/*! + * Band 4 definition + * Band = { DutyCycle, TxMaxPower, LastBandUpdateTime, LastMaxCreditAssignTime, TimeCredits, MaxTimeCredits, ReadyForTransmission } + */ +#define EU868_BAND4 { 100 , EU868_MAX_TX_POWER, 0, 0, 0, 0, 0 } // 1.0 % + +/*! + * Band 5 definition + * Band = { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff, + * DutyCycleTimePeriod, MaxAllowedTimeOnAir, AggregatedTimeOnAir, StartTimeOfPeriod } + */ +#define EU868_BAND5 { 1000, EU868_MAX_TX_POWER, 0, 0, 0, 0, 0 } // 0.1 % + +/*! + * LoRaMac default channel 1 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define EU868_LC1 { 868100000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 1 } + +/*! + * LoRaMac default channel 2 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define EU868_LC2 { 868300000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 1 } + +/*! + * LoRaMac default channel 3 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define EU868_LC3 { 868500000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 1 } + +/*! + * LoRaMac channels which are allowed for the join procedure + */ +#define EU868_JOIN_CHANNELS ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) ) + +/*! + * Data rates table definition + */ +static const uint8_t DataratesEU868[] = { 12, 11, 10, 9, 8, 7, 7, 50 }; + +/*! + * Bandwidths table definition in Hz + */ +static const uint32_t BandwidthsEU868[] = { 125000, 125000, 125000, 125000, 125000, 125000, 250000, 0 }; + +/*! + * Maximum payload with respect to the datarate index. + */ +static const uint8_t MaxPayloadOfDatarateEU868[] = { 51, 51, 51, 115, 242, 242, 242, 242 }; + +/*! + * \brief The function gets a value of a specific phy attribute. + * + * \param [IN] getPhy Pointer to the function parameters. + * + * \retval Returns a structure containing the PHY parameter. + */ +PhyParam_t RegionEU868GetPhyParam( GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionEU868SetBandTxDone( SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] type Sets the initialization type. + */ +void RegionEU868InitDefaults( InitDefaultsParams_t* params ); + +/*! + * \brief Verifies a parameter. + * + * \param [IN] verify Pointer to the function parameters. + * + * \param [IN] type Sets the initialization type. + * + * \retval Returns true, if the parameter is valid. + */ +bool RegionEU868Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + +/*! + * \brief The function parses the input buffer and sets up the channels of the + * CF list. + * + * \param [IN] applyCFList Pointer to the function parameters. + */ +void RegionEU868ApplyCFList( ApplyCFListParams_t* applyCFList ); + +/*! + * \brief Sets a channels mask. + * + * \param [IN] chanMaskSet Pointer to the function parameters. + * + * \retval Returns true, if the channels mask could be set. + */ +bool RegionEU868ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ); + +/*! + * Computes the Rx window timeout and offset. + * + * \param [IN] datarate Rx window datarate index to be used + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms + * interval around RxOffset + * + * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields. + */ +void RegionEU868ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ); + +/*! + * \brief Configuration of the RX windows. + * + * \param [IN] rxConfig Pointer to the function parameters. + * + * \param [OUT] datarate The datarate index which was set. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionEU868RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ); + +/*! + * \brief TX configuration. + * + * \param [IN] txConfig Pointer to the function parameters. + * + * \param [OUT] txPower The tx power index which was set. + * + * \param [OUT] txTimeOnAir The time-on-air of the frame. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionEU868TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ); + +/*! + * \brief The function processes a Link ADR Request. + * + * \param [IN] linkAdrReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionEU868LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ); + +/*! + * \brief The function processes a RX Parameter Setup Request. + * + * \param [IN] rxParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionEU868RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ); + +/*! + * \brief The function processes a Channel Request. + * + * \param [IN] newChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +int8_t RegionEU868NewChannelReq( NewChannelReqParams_t* newChannelReq ); + +/*! + * \brief The function processes a TX ParamSetup Request. + * + * \param [IN] txParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + * Returns -1, if the functionality is not implemented. In this case, the end node + * shall not process the command. + */ +int8_t RegionEU868TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ); + +/*! + * \brief The function processes a DlChannel Request. + * + * \param [IN] dlChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +int8_t RegionEU868DlChannelReq( DlChannelReqParams_t* dlChannelReq ); + +/*! + * \brief Alternates the datarate of the channel for the join request. + * + * \param [IN] currentDr Current datarate. + * + * \retval Datarate to apply. + */ +int8_t RegionEU868AlternateDr( int8_t currentDr, AlternateDrType_t type ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [OUT] channel Next channel to use for TX. + * + * \param [OUT] time Time to wait for the next transmission according to the duty + * cycle. + * + * \param [OUT] aggregatedTimeOff Updates the aggregated time off. + * + * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate] + */ +LoRaMacStatus_t RegionEU868NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ); + +/*! + * \brief Adds a channel. + * + * \param [IN] channelAdd Pointer to the function parameters. + * + * \retval Status of the operation. + */ +LoRaMacStatus_t RegionEU868ChannelAdd( ChannelAddParams_t* channelAdd ); + +/*! + * \brief Removes a channel. + * + * \param [IN] channelRemove Pointer to the function parameters. + * + * \retval Returns true, if the channel was removed successfully. + */ +bool RegionEU868ChannelsRemove( ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms + * + * \param [IN] dr Current datarate + * + * \param [IN] drOffset Offset to be applied + * + * \retval newDr Computed datarate. + */ +uint8_t RegionEU868ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! + * \brief Sets the radio into beacon reception mode + * + * \param [IN] rxBeaconSetup Pointer to the function parameters + */ +void RegionEU868RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ); + +/*! \} defgroup REGIONEU868 */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGION_EU868_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionIN865.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionIN865.c new file mode 100644 index 0000000000..f1ae30483e --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionIN865.c @@ -0,0 +1,952 @@ +/*! + * \file RegionIN865.c + * + * \brief Region implementation for IN865 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) +*/ +#include "radio.h" +#include "RegionCommon.h" +#include "RegionIN865.h" + +// Definitions +#define CHANNELS_MASK_SIZE 1 + +/* + * Non-volatile module context. + */ +static RegionNvmDataGroup1_t* RegionNvmGroup1; +static RegionNvmDataGroup2_t* RegionNvmGroup2; +static Band_t* RegionBands; + + +static bool VerifyRfFreq( uint32_t freq ) +{ + // Check radio driver support + if( Radio.CheckRfFrequency( freq ) == false ) + { + return false; + } + + if( ( freq < 865000000 ) || ( freq > 867000000 ) ) + { + return false; + } + return true; +} + +static TimerTime_t GetTimeOnAir( int8_t datarate, uint16_t pktLen ) +{ + int8_t phyDr = DataratesIN865[datarate]; + uint32_t bandwidth = RegionCommonGetBandwidth( datarate, BandwidthsIN865 ); + TimerTime_t timeOnAir = 0; + + if( datarate == DR_7 ) + { // High Speed FSK channel + timeOnAir = Radio.TimeOnAir( MODEM_FSK, bandwidth, phyDr * 1000, 0, 5, false, pktLen, true ); + } + else + { + timeOnAir = Radio.TimeOnAir( MODEM_LORA, bandwidth, phyDr, 1, 8, false, pktLen, true ); + } + return timeOnAir; +} + +PhyParam_t RegionIN865GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + + switch( getPhy->Attribute ) + { + case PHY_MIN_RX_DR: + { + phyParam.Value = IN865_RX_MIN_DATARATE; + break; + } + case PHY_MIN_TX_DR: + { + phyParam.Value = IN865_TX_MIN_DATARATE; + break; + } + case PHY_DEF_TX_DR: + { + phyParam.Value = IN865_DEFAULT_DATARATE; + break; + } + case PHY_NEXT_LOWER_TX_DR: + { + RegionCommonGetNextLowerTxDrParams_t nextLowerTxDrParams = + { + .CurrentDr = getPhy->Datarate, + .MaxDr = ( int8_t )IN865_TX_MAX_DATARATE, + .MinDr = ( int8_t )IN865_TX_MIN_DATARATE, + .NbChannels = IN865_MAX_NB_CHANNELS, + .ChannelsMask = RegionNvmGroup2->ChannelsMask, + .Channels = RegionNvmGroup2->Channels, + }; + phyParam.Value = RegionCommonGetNextLowerTxDr( &nextLowerTxDrParams ); + break; + } + case PHY_MAX_TX_POWER: + { + phyParam.Value = IN865_MAX_TX_POWER; + break; + } + case PHY_DEF_TX_POWER: + { + phyParam.Value = IN865_DEFAULT_TX_POWER; + break; + } + case PHY_DEF_ADR_ACK_LIMIT: + { + phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_LIMIT; + break; + } + case PHY_DEF_ADR_ACK_DELAY: + { + phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_DELAY; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateIN865[getPhy->Datarate]; + break; + } + case PHY_DUTY_CYCLE: + { + phyParam.Value = IN865_DUTY_CYCLE_ENABLED; + break; + } + case PHY_MAX_RX_WINDOW: + { + phyParam.Value = IN865_MAX_RX_WINDOW; + break; + } + case PHY_RECEIVE_DELAY1: + { + phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY2; + break; + } + case PHY_RETRANSMIT_TIMEOUT: + { + phyParam.Value = ( REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT + randr( -REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND, REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND ) ); + break; + } + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = REGION_COMMON_DEFAULT_RX1_DR_OFFSET; + break; + } + case PHY_DEF_RX2_FREQUENCY: + { + phyParam.Value = IN865_RX_WND_2_FREQ; + break; + } + case PHY_DEF_RX2_DR: + { + phyParam.Value = IN865_RX_WND_2_DR; + break; + } + case PHY_CHANNELS_MASK: + { + phyParam.ChannelsMask = RegionNvmGroup2->ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = RegionNvmGroup2->ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = IN865_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = RegionNvmGroup2->Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + { + phyParam.Value = IN865_DEFAULT_UPLINK_DWELL_TIME; + break; + } + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = REGION_COMMON_DEFAULT_DOWNLINK_DWELL_TIME; + break; + } + case PHY_DEF_MAX_EIRP: + { + phyParam.fValue = IN865_DEFAULT_MAX_EIRP; + break; + } + case PHY_DEF_ANTENNA_GAIN: + { + phyParam.fValue = IN865_DEFAULT_ANTENNA_GAIN; + break; + } + case PHY_BEACON_CHANNEL_FREQ: + { + phyParam.Value = IN865_BEACON_CHANNEL_FREQ; + break; + } + case PHY_BEACON_FORMAT: + { + phyParam.BeaconFormat.BeaconSize = IN865_BEACON_SIZE; + phyParam.BeaconFormat.Rfu1Size = IN865_RFU1_SIZE; + phyParam.BeaconFormat.Rfu2Size = IN865_RFU2_SIZE; + break; + } + case PHY_BEACON_CHANNEL_DR: + { + phyParam.Value = IN865_BEACON_CHANNEL_DR; + break; + } + case PHY_PING_SLOT_CHANNEL_FREQ: + { + phyParam.Value = IN865_PING_SLOT_CHANNEL_FREQ; + break; + } + case PHY_PING_SLOT_CHANNEL_DR: + { + phyParam.Value = IN865_PING_SLOT_CHANNEL_DR; + break; + } + case PHY_SF_FROM_DR: + { + phyParam.Value = DataratesIN865[getPhy->Datarate]; + break; + } + case PHY_BW_FROM_DR: + { + phyParam.Value = RegionCommonGetBandwidth( getPhy->Datarate, BandwidthsIN865 ); + break; + } + default: + { + break; + } + } + + return phyParam; +} + +void RegionIN865SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ + RegionCommonSetBandTxDone( &RegionBands[RegionNvmGroup2->Channels[txDone->Channel].Band], + txDone->LastTxAirTime, txDone->Joined, txDone->ElapsedTimeSinceStartUp ); +} + +void RegionIN865InitDefaults( InitDefaultsParams_t* params ) +{ + Band_t bands[IN865_MAX_NB_BANDS] = + { + IN865_BAND0 + }; + + switch( params->Type ) + { + case INIT_TYPE_DEFAULTS: + { + if( ( params->NvmGroup1 == NULL ) || ( params->NvmGroup2 == NULL ) ) + { + return; + } + + RegionNvmGroup1 = (RegionNvmDataGroup1_t*) params->NvmGroup1; + RegionNvmGroup2 = (RegionNvmDataGroup2_t*) params->NvmGroup2; + RegionBands = (Band_t*) params->Bands; + + // Initialize bands + memcpy1( ( uint8_t* )RegionBands, ( uint8_t* )bands, sizeof( Band_t ) * IN865_MAX_NB_BANDS ); + + // Default channels + RegionNvmGroup2->Channels[0] = ( ChannelParams_t ) IN865_LC1; + RegionNvmGroup2->Channels[1] = ( ChannelParams_t ) IN865_LC2; + RegionNvmGroup2->Channels[2] = ( ChannelParams_t ) IN865_LC3; + + // Initialize the channels default mask + RegionNvmGroup2->ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 ); + + // Default ChannelsMask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + break; + } + case INIT_TYPE_RESET_TO_DEFAULT_CHANNELS: + { + // Reset Channels Rx1Frequency to default 0 + RegionNvmGroup2->Channels[0].Rx1Frequency = 0; + RegionNvmGroup2->Channels[1].Rx1Frequency = 0; + RegionNvmGroup2->Channels[2].Rx1Frequency = 0; + // Default ChannelsMask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + break; + } + case INIT_TYPE_ACTIVATE_DEFAULT_CHANNELS: + { + // Restore channels default mask + RegionNvmGroup2->ChannelsMask[0] |= RegionNvmGroup2->ChannelsDefaultMask[0]; + break; + } + default: + { + break; + } + } +} + +bool RegionIN865Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( phyAttribute ) + { + case PHY_FREQUENCY: + { + return VerifyRfFreq( verify->Frequency ); + } + case PHY_TX_DR: + { + if( verify->DatarateParams.Datarate == DR_6 ) + {// DR_6 is not supported by this region + return false; + } + else + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, IN865_TX_MIN_DATARATE, IN865_TX_MAX_DATARATE ); + } + } + case PHY_DEF_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 ); + } + case PHY_RX_DR: + { + if( verify->DatarateParams.Datarate == DR_6 ) + {// DR_6 is not supported by this region + return false; + } + else + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, IN865_RX_MIN_DATARATE, IN865_RX_MAX_DATARATE ); + } + } + case PHY_DEF_TX_POWER: + case PHY_TX_POWER: + { + // Remark: switched min and max! + return RegionCommonValueInRange( verify->TxPower, IN865_MAX_TX_POWER, IN865_MIN_TX_POWER ); + } + case PHY_DUTY_CYCLE: + { + return IN865_DUTY_CYCLE_ENABLED; + } + default: + return false; + } +} + +void RegionIN865ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ + ChannelParams_t newChannel; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + // Setup default datarate range + newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0; + + // Size of the optional CF list + if( applyCFList->Size != 16 ) + { + return; + } + + // Last byte CFListType must be 0 to indicate the CFList contains a list of frequencies + if( applyCFList->Payload[15] != 0 ) + { + return; + } + + // Last byte is RFU, don't take it into account + for( uint8_t i = 0, chanIdx = IN865_NUMB_DEFAULT_CHANNELS; chanIdx < IN865_MAX_NB_CHANNELS; i+=3, chanIdx++ ) + { + if( chanIdx < ( IN865_NUMB_CHANNELS_CF_LIST + IN865_NUMB_DEFAULT_CHANNELS ) ) + { + // Channel frequency + newChannel.Frequency = (uint32_t) applyCFList->Payload[i]; + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 ); + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 ); + newChannel.Frequency *= 100; + + // Initialize alternative frequency to 0 + newChannel.Rx1Frequency = 0; + } + else + { + newChannel.Frequency = 0; + newChannel.DrRange.Value = 0; + newChannel.Rx1Frequency = 0; + } + + if( newChannel.Frequency != 0 ) + { + channelAdd.NewChannel = &newChannel; + channelAdd.ChannelId = chanIdx; + + // Try to add all channels + RegionIN865ChannelAdd( &channelAdd ); + } + else + { + channelRemove.ChannelId = chanIdx; + + RegionIN865ChannelsRemove( &channelRemove ); + } + } +} + +bool RegionIN865ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + default: + return false; + } + return true; +} + +void RegionIN865ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + uint32_t tSymbolInUs = 0; + + // Get the datarate, perform a boundary check + rxConfigParams->Datarate = MIN( datarate, IN865_RX_MAX_DATARATE ); + rxConfigParams->Bandwidth = RegionCommonGetBandwidth( rxConfigParams->Datarate, BandwidthsIN865 ); + + if( rxConfigParams->Datarate == DR_7 ) + { // FSK + tSymbolInUs = RegionCommonComputeSymbolTimeFsk( DataratesIN865[rxConfigParams->Datarate] ); + } + else + { // LoRa + tSymbolInUs = RegionCommonComputeSymbolTimeLoRa( DataratesIN865[rxConfigParams->Datarate], BandwidthsIN865[rxConfigParams->Datarate] ); + } + + RegionCommonComputeRxWindowParameters( tSymbolInUs, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +} + +bool RegionIN865RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ + RadioModems_t modem; + int8_t dr = rxConfig->Datarate; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->RxSlot == RX_SLOT_WIN_1 ) + { + // Apply window 1 frequency + frequency = RegionNvmGroup2->Channels[rxConfig->Channel].Frequency; + // Apply the alternative RX 1 window frequency, if it is available + if( RegionNvmGroup2->Channels[rxConfig->Channel].Rx1Frequency != 0 ) + { + frequency = RegionNvmGroup2->Channels[rxConfig->Channel].Rx1Frequency; + } + } + + // Read the physical datarate from the datarates table + phyDr = DataratesIN865[dr]; + + Radio.SetChannel( frequency ); + + // Radio configuration + if( dr == DR_7 ) + { + modem = MODEM_FSK; + Radio.SetRxConfig( modem, 50000, phyDr * 1000, 0, 83333, 5, rxConfig->WindowTimeout, false, 0, true, 0, 0, false, rxConfig->RxContinuous ); + } + else + { + modem = MODEM_LORA; + Radio.SetRxConfig( modem, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous ); + } + + Radio.SetMaxPayloadLength( modem, MaxPayloadOfDatarateIN865[dr] + LORAMAC_FRAME_PAYLOAD_OVERHEAD_SIZE ); + + *datarate = (uint8_t) dr; + return true; +} + +bool RegionIN865TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + RadioModems_t modem; + int8_t phyDr = DataratesIN865[txConfig->Datarate]; + int8_t txPowerLimited = RegionCommonLimitTxPower( txConfig->TxPower, RegionBands[RegionNvmGroup2->Channels[txConfig->Channel].Band].TxMaxPower ); + uint32_t bandwidth = RegionCommonGetBandwidth( txConfig->Datarate, BandwidthsIN865 ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( RegionNvmGroup2->Channels[txConfig->Channel].Frequency ); + + if( txConfig->Datarate == DR_7 ) + { // High Speed FSK channel + modem = MODEM_FSK; + Radio.SetTxConfig( modem, phyTxPower, 25000, bandwidth, phyDr * 1000, 0, 5, false, true, 0, 0, false, 4000 ); + } + else + { + modem = MODEM_LORA; + Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 4000 ); + } + + // Update time-on-air + *txTimeOnAir = GetTimeOnAir( txConfig->Datarate, txConfig->PktLen ); + + // Setup maximum payload lenght of the radio driver + Radio.SetMaxPayloadLength( modem, txConfig->PktLen ); + + *txPower = txPowerLimited; + return true; +} + +uint8_t RegionIN865LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; + RegionCommonLinkAdrParams_t linkAdrParams = { 0 }; + uint8_t nextIndex = 0; + uint8_t bytesProcessed = 0; + uint16_t chMask = 0; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams; + + while( bytesProcessed < linkAdrReq->PayloadSize ) + { + // Get ADR request parameters + nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); + + if( nextIndex == 0 ) + break; // break loop, since no more request has been found + + // Update bytes processed + bytesProcessed += nextIndex; + + // Revert status, as we only check the last ADR request for the channel mask KO + status = 0x07; + + // Setup temporary channels mask + chMask = linkAdrParams.ChMask; + + // Verify channels mask + if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) ) + { + status &= 0xFE; // Channel mask KO + } + else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) || + ( linkAdrParams.ChMaskCtrl >= 7 ) ) + { + // RFU + status &= 0xFE; // Channel mask KO + } + else + { + for( uint8_t i = 0; i < IN865_MAX_NB_CHANNELS; i++ ) + { + if( linkAdrParams.ChMaskCtrl == 6 ) + { + if( RegionNvmGroup2->Channels[i].Frequency != 0 ) + { + chMask |= 1 << i; + } + } + else + { + if( ( ( chMask & ( 1 << i ) ) != 0 ) && + ( RegionNvmGroup2->Channels[i].Frequency == 0 ) ) + {// Trying to enable an undefined channel + status &= 0xFE; // Channel mask KO + } + } + } + } + } + + if( linkAdrParams.Datarate != DR_6 ) + { + // Get the minimum possible datarate + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; + phyParam = RegionIN865GetPhyParam( &getPhy ); + + linkAdrVerifyParams.Status = status; + linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; + linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; + linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; + linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; + linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; + linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; + linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; + linkAdrVerifyParams.NbChannels = IN865_MAX_NB_CHANNELS; + linkAdrVerifyParams.ChannelsMask = &chMask; + linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value; + linkAdrVerifyParams.MaxDatarate = IN865_TX_MAX_DATARATE; + linkAdrVerifyParams.Channels = RegionNvmGroup2->Channels; + linkAdrVerifyParams.MinTxPower = IN865_MIN_TX_POWER; + linkAdrVerifyParams.MaxTxPower = IN865_MAX_TX_POWER; + linkAdrVerifyParams.Version = linkAdrReq->Version; + + // Verify the parameters and update, if necessary + status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep ); + } + else + {// DR_6 is not supported by this region + status &= 0xFD; // Datarate KO + } + + // Update channelsMask if everything is correct + if( status == 0x07 ) + { + // Set the channels mask to a default value + memset1( ( uint8_t* ) RegionNvmGroup2->ChannelsMask, 0, sizeof( RegionNvmGroup2->ChannelsMask ) ); + // Update the channels mask + RegionNvmGroup2->ChannelsMask[0] = chMask; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + + return status; +} + +uint8_t RegionIN865RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; + + // Verify radio frequency + if( VerifyRfFreq( rxParamSetupReq->Frequency ) == false ) + { + status &= 0xFE; // Channel frequency KO + } + + // Verify datarate + if( ( RegionCommonValueInRange( rxParamSetupReq->Datarate, IN865_RX_MIN_DATARATE, IN865_RX_MAX_DATARATE ) == false ) || + // DR_6 is not supported by this region + ( rxParamSetupReq->Datarate == DR_6 ) ) + { + status &= 0xFD; // Datarate KO + } + + // Verify datarate offset + if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, IN865_MIN_RX1_DR_OFFSET, IN865_MAX_RX1_DR_OFFSET ) == false ) + { + status &= 0xFB; // Rx1DrOffset range KO + } + + return status; +} + +int8_t RegionIN865NewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + uint8_t status = 0x03; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + if( newChannelReq->NewChannel->Frequency == 0 ) + { + channelRemove.ChannelId = newChannelReq->ChannelId; + + // Remove + if( RegionIN865ChannelsRemove( &channelRemove ) == false ) + { + status &= 0xFC; + } + } + else + { + channelAdd.NewChannel = newChannelReq->NewChannel; + channelAdd.ChannelId = newChannelReq->ChannelId; + + switch( RegionIN865ChannelAdd( &channelAdd ) ) + { + case LORAMAC_STATUS_OK: + { + break; + } + case LORAMAC_STATUS_FREQUENCY_INVALID: + { + status &= 0xFE; + break; + } + case LORAMAC_STATUS_DATARATE_INVALID: + { + status &= 0xFD; + break; + } + case LORAMAC_STATUS_FREQ_AND_DR_INVALID: + { + status &= 0xFC; + break; + } + default: + { + status &= 0xFC; + break; + } + } + } + + return status; +} + +int8_t RegionIN865TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + // Do not accept the request + return -1; +} + +int8_t RegionIN865DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + uint8_t status = 0x03; + + if( dlChannelReq->ChannelId >= ( CHANNELS_MASK_SIZE * 16 ) ) + { + return 0; + } + + // Verify if the frequency is supported + if( VerifyRfFreq( dlChannelReq->Rx1Frequency ) == false ) + { + status &= 0xFE; + } + + // Verify if an uplink frequency exists + if( RegionNvmGroup2->Channels[dlChannelReq->ChannelId].Frequency == 0 ) + { + status &= 0xFD; + } + + // Apply Rx1 frequency, if the status is OK + if( status == 0x03 ) + { + RegionNvmGroup2->Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency; + } + + return status; +} + +int8_t RegionIN865AlternateDr( int8_t currentDr, AlternateDrType_t type ) +{ + return currentDr; +} + +LoRaMacStatus_t RegionIN865NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t nbRestrictedChannels = 0; + uint8_t enabledChannels[IN865_MAX_NB_CHANNELS] = { 0 }; + RegionCommonIdentifyChannelsParam_t identifyChannelsParam; + RegionCommonCountNbOfEnabledChannelsParams_t countChannelsParams; + LoRaMacStatus_t status = LORAMAC_STATUS_NO_CHANNEL_FOUND; + uint16_t joinChannels = IN865_JOIN_CHANNELS; + + if( RegionCommonCountChannels( RegionNvmGroup2->ChannelsMask, 0, 1 ) == 0 ) + { // Reactivate default channels + RegionNvmGroup2->ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + + // Search how many channels are enabled + countChannelsParams.Joined = nextChanParams->Joined; + countChannelsParams.Datarate = nextChanParams->Datarate; + countChannelsParams.ChannelsMask = RegionNvmGroup2->ChannelsMask; + countChannelsParams.Channels = RegionNvmGroup2->Channels; + countChannelsParams.Bands = RegionBands; + countChannelsParams.MaxNbChannels = IN865_MAX_NB_CHANNELS; + countChannelsParams.JoinChannels = &joinChannels; + + identifyChannelsParam.AggrTimeOff = nextChanParams->AggrTimeOff; + identifyChannelsParam.LastAggrTx = nextChanParams->LastAggrTx; + identifyChannelsParam.DutyCycleEnabled = nextChanParams->DutyCycleEnabled; + identifyChannelsParam.MaxBands = IN865_MAX_NB_BANDS; + + identifyChannelsParam.ElapsedTimeSinceStartUp = nextChanParams->ElapsedTimeSinceStartUp; + identifyChannelsParam.LastTxIsJoinRequest = nextChanParams->LastTxIsJoinRequest; + identifyChannelsParam.ExpectedTimeOnAir = GetTimeOnAir( nextChanParams->Datarate, nextChanParams->PktLen ); + + identifyChannelsParam.CountNbOfEnabledChannelsParam = &countChannelsParams; + + status = RegionCommonIdentifyChannels( &identifyChannelsParam, aggregatedTimeOff, enabledChannels, + &nbEnabledChannels, &nbRestrictedChannels, time ); + + if( status == LORAMAC_STATUS_OK ) + { + // We found a valid channel + *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; + } + else if( status == LORAMAC_STATUS_NO_CHANNEL_FOUND ) + { + // Datarate not supported by any channel, restore defaults + RegionNvmGroup2->ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + return status; +} + +LoRaMacStatus_t RegionIN865ChannelAdd( ChannelAddParams_t* channelAdd ) +{ + bool drInvalid = false; + bool freqInvalid = false; + uint8_t id = channelAdd->ChannelId; + + if( id < IN865_NUMB_DEFAULT_CHANNELS ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + + if( id >= IN865_MAX_NB_CHANNELS ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + // Validate the datarate range + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, IN865_TX_MIN_DATARATE, IN865_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, IN865_TX_MIN_DATARATE, IN865_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max ) + { + drInvalid = true; + } + + // Check frequency + if( freqInvalid == false ) + { + if( VerifyRfFreq( channelAdd->NewChannel->Frequency ) == false ) + { + freqInvalid = true; + } + } + + // Check status + if( ( drInvalid == true ) && ( freqInvalid == true ) ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + if( drInvalid == true ) + { + return LORAMAC_STATUS_DATARATE_INVALID; + } + if( freqInvalid == true ) + { + return LORAMAC_STATUS_FREQUENCY_INVALID; + } + + memcpy1( ( uint8_t* ) &(RegionNvmGroup2->Channels[id]), ( uint8_t* ) channelAdd->NewChannel, sizeof( RegionNvmGroup2->Channels[id] ) ); + RegionNvmGroup2->Channels[id].Band = 0; + RegionNvmGroup2->ChannelsMask[0] |= ( 1 << id ); + return LORAMAC_STATUS_OK; +} + +bool RegionIN865ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + uint8_t id = channelRemove->ChannelId; + + if( id < IN865_NUMB_DEFAULT_CHANNELS ) + { + return false; + } + + // Remove the channel from the list of channels + RegionNvmGroup2->Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 }; + + return RegionCommonChanDisable( RegionNvmGroup2->ChannelsMask, id, IN865_MAX_NB_CHANNELS ); +} + +uint8_t RegionIN865ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + int8_t datarate = EffectiveRx1DrOffsetIN865[dr][drOffset]; + + if( ( datarate < 0 ) || ( dr == DR_6 ) ) + { + datarate = DR_0; + } + return datarate; +} + +void RegionIN865RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ) +{ + RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup; + + regionCommonRxBeaconSetup.Datarates = DataratesIN865; + regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency; + regionCommonRxBeaconSetup.BeaconSize = IN865_BEACON_SIZE; + regionCommonRxBeaconSetup.BeaconDatarate = IN865_BEACON_CHANNEL_DR; + regionCommonRxBeaconSetup.BeaconChannelBW = IN865_BEACON_CHANNEL_BW; + regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime; + regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout; + + RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup ); + + // Store downlink datarate + *outDr = IN865_BEACON_CHANNEL_DR; +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionIN865.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionIN865.h new file mode 100644 index 0000000000..826e19ffe9 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionIN865.h @@ -0,0 +1,467 @@ +/*! + * \file RegionIN865.h + * + * \brief Region definition for IN865 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * \defgroup REGIONIN865 Region IN865 + * Implementation according to LoRaWAN Specification v1.0.2. + * \{ + */ +#ifndef __REGION_IN865_H__ +#define __REGION_IN865_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "region/Region.h" + +/*! + * LoRaMac maximum number of channels + */ +#define IN865_MAX_NB_CHANNELS 16 + +/*! + * Number of default channels + */ +#define IN865_NUMB_DEFAULT_CHANNELS 3 + +/*! + * Number of channels to apply for the CF list + */ +#define IN865_NUMB_CHANNELS_CF_LIST 5 + +/*! + * Minimal datarate that can be used by the node + */ +#define IN865_TX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define IN865_TX_MAX_DATARATE DR_7 + +/*! + * Minimal datarate that can be used by the node + */ +#define IN865_RX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define IN865_RX_MAX_DATARATE DR_7 + +/*! + * Default datarate used by the node + */ +#define IN865_DEFAULT_DATARATE DR_0 + +/*! + * Minimal Rx1 receive datarate offset + */ +#define IN865_MIN_RX1_DR_OFFSET 0 + +/*! + * Maximal Rx1 receive datarate offset + */ +#define IN865_MAX_RX1_DR_OFFSET 7 + +/*! + * Minimal Tx output power that can be used by the node + */ +#define IN865_MIN_TX_POWER TX_POWER_10 + +/*! + * Maximal Tx output power that can be used by the node + */ +#define IN865_MAX_TX_POWER TX_POWER_0 + +/*! + * Default Tx output power used by the node + */ +#define IN865_DEFAULT_TX_POWER TX_POWER_0 + +/*! + * Default Max EIRP + */ +#define IN865_DEFAULT_MAX_EIRP 30.0f + +/*! + * Default antenna gain + */ +#define IN865_DEFAULT_ANTENNA_GAIN 2.15f + +/*! + * Enabled or disabled the duty cycle + */ +#define IN865_DUTY_CYCLE_ENABLED 0 + +/*! + * Maximum RX window duration + */ +#define IN865_MAX_RX_WINDOW 3000 + +#if ( IN865_DEFAULT_DATARATE > DR_5 ) +#error "A default DR higher than DR_5 may lead to connectivity loss." +#endif + +/*! + * Second reception window channel frequency definition. + */ +#define IN865_RX_WND_2_FREQ 866550000 + +/*! + * Second reception window channel datarate definition. + */ +#define IN865_RX_WND_2_DR DR_2 + +/*! + * Default uplink dwell time configuration + */ +#define IN865_DEFAULT_UPLINK_DWELL_TIME 0 + +/* + * CLASS B + */ +/*! + * Beacon frequency + */ +#define IN865_BEACON_CHANNEL_FREQ 866550000 + +/*! + * Ping slot channel frequency + */ +#define IN865_PING_SLOT_CHANNEL_FREQ 866550000 + +/*! + * Payload size of a beacon frame + */ +#define IN865_BEACON_SIZE 19 + +/*! + * Size of RFU 1 field + */ +#define IN865_RFU1_SIZE 0 + +/*! + * Size of RFU 2 field + */ +#define IN865_RFU2_SIZE 3 + +/*! + * Datarate of the beacon channel + */ +#define IN865_BEACON_CHANNEL_DR DR_4 + +/*! + * Bandwith of the beacon channel + */ +#define IN865_BEACON_CHANNEL_BW 0 + +/*! + * Ping slot channel datarate + */ +#define IN865_PING_SLOT_CHANNEL_DR DR_4 + +/*! + * Maximum number of bands + */ +#define IN865_MAX_NB_BANDS 1 + +/*! + * Band 0 definition + * Band = { DutyCycle, TxMaxPower, LastBandUpdateTime, LastMaxCreditAssignTime, TimeCredits, MaxTimeCredits, ReadyForTransmission } + */ +#define IN865_BAND0 { 1 , IN865_MAX_TX_POWER, 0, 0, 0, 0, 0 } // 100.0 % + +/*! + * LoRaMac default channel 1 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define IN865_LC1 { 865062500, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac default channel 2 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define IN865_LC2 { 865402500, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac default channel 3 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define IN865_LC3 { 865985000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac channels which are allowed for the join procedure + */ +#define IN865_JOIN_CHANNELS ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) ) + +/*! + * RFU value + */ +#define IN865_DR_RFU_VALUE { 0, 0, 0, 0, 0, 0, 0, 0 } + +/*! + * Data rates table definition + */ +static const uint8_t DataratesIN865[] = { 12, 11, 10, 9, 8, 7, 7, 50 }; + +/*! + * Bandwidths table definition in Hz + */ +static const uint32_t BandwidthsIN865[] = { 125000, 125000, 125000, 125000, 125000, 125000, 250000, 0 }; + +/*! + * Maximum payload with respect to the datarate index. + */ +static const uint8_t MaxPayloadOfDatarateIN865[] = { 51, 51, 51, 115, 242, 242, 242, 242 }; + +/*! + * Effective datarate offsets for receive window 1. + */ +static const int8_t EffectiveRx1DrOffsetIN865[8][8] = +{ + { DR_0 , DR_0 , DR_0 , DR_0 , DR_0 , DR_0 , DR_1 , DR_2 }, // DR_0 + { DR_1 , DR_0 , DR_0 , DR_0 , DR_0 , DR_0 , DR_2 , DR_3 }, // DR_1 + { DR_2 , DR_1 , DR_0 , DR_0 , DR_0 , DR_0 , DR_3 , DR_4 }, // DR_2 + { DR_3 , DR_2 , DR_1 , DR_0 , DR_0 , DR_0 , DR_4 , DR_5 }, // DR_3 + { DR_4 , DR_3 , DR_2 , DR_1 , DR_0 , DR_0 , DR_5 , DR_5 }, // DR_4 + { DR_5 , DR_4 , DR_3 , DR_2 , DR_1 , DR_0 , DR_5 , DR_7 }, // DR_5 + IN865_DR_RFU_VALUE , // DR_6 + { DR_7 , DR_5 , DR_5 , DR_4 , DR_3 , DR_2 , DR_7 , DR_7 }, // DR_7 +}; + +/*! + * \brief The function gets a value of a specific phy attribute. + * + * \param [IN] getPhy Pointer to the function parameters. + * + * \retval Returns a structure containing the PHY parameter. + */ +PhyParam_t RegionIN865GetPhyParam( GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionIN865SetBandTxDone( SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] type Sets the initialization type. + */ +void RegionIN865InitDefaults( InitDefaultsParams_t* params ); + +/*! + * \brief Verifies a parameter. + * + * \param [IN] verify Pointer to the function parameters. + * + * \param [IN] type Sets the initialization type. + * + * \retval Returns true, if the parameter is valid. + */ +bool RegionIN865Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + +/*! + * \brief The function parses the input buffer and sets up the channels of the + * CF list. + * + * \param [IN] applyCFList Pointer to the function parameters. + */ +void RegionIN865ApplyCFList( ApplyCFListParams_t* applyCFList ); + +/*! + * \brief Sets a channels mask. + * + * \param [IN] chanMaskSet Pointer to the function parameters. + * + * \retval Returns true, if the channels mask could be set. + */ +bool RegionIN865ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ); + +/*! + * Computes the Rx window timeout and offset. + * + * \param [IN] datarate Rx window datarate index to be used + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms + * interval around RxOffset + * + * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields. + */ +void RegionIN865ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ); + +/*! + * \brief Configuration of the RX windows. + * + * \param [IN] rxConfig Pointer to the function parameters. + * + * \param [OUT] datarate The datarate index which was set. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionIN865RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ); + +/*! + * \brief TX configuration. + * + * \param [IN] txConfig Pointer to the function parameters. + * + * \param [OUT] txPower The tx power index which was set. + * + * \param [OUT] txTimeOnAir The time-on-air of the frame. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionIN865TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ); + +/*! + * \brief The function processes a Link ADR Request. + * + * \param [IN] linkAdrReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionIN865LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ); + +/*! + * \brief The function processes a RX Parameter Setup Request. + * + * \param [IN] rxParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionIN865RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ); + +/*! + * \brief The function processes a Channel Request. + * + * \param [IN] newChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +int8_t RegionIN865NewChannelReq( NewChannelReqParams_t* newChannelReq ); + +/*! + * \brief The function processes a TX ParamSetup Request. + * + * \param [IN] txParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + * Returns -1, if the functionality is not implemented. In this case, the end node + * shall not process the command. + */ +int8_t RegionIN865TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ); + +/*! + * \brief The function processes a DlChannel Request. + * + * \param [IN] dlChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +int8_t RegionIN865DlChannelReq( DlChannelReqParams_t* dlChannelReq ); + +/*! + * \brief Alternates the datarate of the channel for the join request. + * + * \param [IN] currentDr Current datarate. + * + * \retval Datarate to apply. + */ +int8_t RegionIN865AlternateDr( int8_t currentDr, AlternateDrType_t type ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [OUT] channel Next channel to use for TX. + * + * \param [OUT] time Time to wait for the next transmission according to the duty + * cycle. + * + * \param [OUT] aggregatedTimeOff Updates the aggregated time off. + * + * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate] + */ +LoRaMacStatus_t RegionIN865NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ); + +/*! + * \brief Adds a channel. + * + * \param [IN] channelAdd Pointer to the function parameters. + * + * \retval Status of the operation. + */ +LoRaMacStatus_t RegionIN865ChannelAdd( ChannelAddParams_t* channelAdd ); + +/*! + * \brief Removes a channel. + * + * \param [IN] channelRemove Pointer to the function parameters. + * + * \retval Returns true, if the channel was removed successfully. + */ +bool RegionIN865ChannelsRemove( ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms + * + * \param [IN] dr Current datarate + * + * \param [IN] drOffset Offset to be applied + * + * \retval newDr Computed datarate. + */ +uint8_t RegionIN865ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! + * \brief Sets the radio into beacon reception mode + * + * \param [IN] rxBeaconSetup Pointer to the function parameters + */ + void RegionIN865RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ); + +/*! \} defgroup REGIONIN865 */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGION_IN865_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionKR920.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionKR920.c new file mode 100644 index 0000000000..8b26e53e7f --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionKR920.c @@ -0,0 +1,954 @@ +/*! + * \file RegionKR920.c + * + * \brief Region implementation for KR920 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) +*/ +#include "radio.h" +#include "RegionCommon.h" +#include "RegionKR920.h" + +// Definitions +#define CHANNELS_MASK_SIZE 1 + +/*! + * Specifies the reception bandwidth to be used while executing the LBT + * Max channel bandwidth is 200 kHz + */ +#define KR920_LBT_RX_BANDWIDTH 200000 + +/*! + * RSSI threshold for a free channel [dBm] + */ +#define KR920_RSSI_FREE_TH -65 + +/*! + * Specifies the time the node performs a carrier sense + */ +#define KR920_CARRIER_SENSE_TIME 6 + +/* + * Non-volatile module context. + */ +static RegionNvmDataGroup1_t* RegionNvmGroup1; +static RegionNvmDataGroup2_t* RegionNvmGroup2; +static Band_t* RegionBands; + +// Static functions +static int8_t GetMaxEIRP( uint32_t freq ) +{ + if( freq >= 922100000 ) + {// Limit to 14dBm + return KR920_DEFAULT_MAX_EIRP_HIGH; + } + // Limit to 10dBm + return KR920_DEFAULT_MAX_EIRP_LOW; +} + +static bool VerifyRfFreq( uint32_t freq ) +{ + uint32_t tmpFreq = freq; + + // Check radio driver support + if( Radio.CheckRfFrequency( tmpFreq ) == false ) + { + return false; + } + + // Verify if the frequency is valid. The frequency must be in a specified + // range and can be set to specific values. + if( ( tmpFreq >= 920900000 ) && ( tmpFreq <=923300000 ) ) + { + // Range ok, check for specific value + tmpFreq -= 920900000; + if( ( tmpFreq % 200000 ) == 0 ) + { + return true; + } + } + return false; +} + +static TimerTime_t GetTimeOnAir( int8_t datarate, uint16_t pktLen ) +{ + int8_t phyDr = DataratesKR920[datarate]; + uint32_t bandwidth = RegionCommonGetBandwidth( datarate, BandwidthsKR920 ); + + return Radio.TimeOnAir( MODEM_LORA, bandwidth, phyDr, 1, 8, false, pktLen, true ); +} + +PhyParam_t RegionKR920GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + + switch( getPhy->Attribute ) + { + case PHY_MIN_RX_DR: + { + phyParam.Value = KR920_RX_MIN_DATARATE; + break; + } + case PHY_MIN_TX_DR: + { + phyParam.Value = KR920_TX_MIN_DATARATE; + break; + } + case PHY_DEF_TX_DR: + { + phyParam.Value = KR920_DEFAULT_DATARATE; + break; + } + case PHY_NEXT_LOWER_TX_DR: + { + RegionCommonGetNextLowerTxDrParams_t nextLowerTxDrParams = + { + .CurrentDr = getPhy->Datarate, + .MaxDr = ( int8_t )KR920_TX_MAX_DATARATE, + .MinDr = ( int8_t )KR920_TX_MIN_DATARATE, + .NbChannels = KR920_MAX_NB_CHANNELS, + .ChannelsMask = RegionNvmGroup2->ChannelsMask, + .Channels = RegionNvmGroup2->Channels, + }; + phyParam.Value = RegionCommonGetNextLowerTxDr( &nextLowerTxDrParams ); + break; + } + case PHY_MAX_TX_POWER: + { + phyParam.Value = KR920_MAX_TX_POWER; + break; + } + case PHY_DEF_TX_POWER: + { + phyParam.Value = KR920_DEFAULT_TX_POWER; + break; + } + case PHY_DEF_ADR_ACK_LIMIT: + { + phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_LIMIT; + break; + } + case PHY_DEF_ADR_ACK_DELAY: + { + phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_DELAY; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateKR920[getPhy->Datarate]; + break; + } + case PHY_DUTY_CYCLE: + { + phyParam.Value = KR920_DUTY_CYCLE_ENABLED; + break; + } + case PHY_MAX_RX_WINDOW: + { + phyParam.Value = KR920_MAX_RX_WINDOW; + break; + } + case PHY_RECEIVE_DELAY1: + { + phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY2; + break; + } + case PHY_RETRANSMIT_TIMEOUT: + { + phyParam.Value = ( REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT + randr( -REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND, REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND ) ); + break; + } + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = REGION_COMMON_DEFAULT_RX1_DR_OFFSET; + break; + } + case PHY_DEF_RX2_FREQUENCY: + { + phyParam.Value = KR920_RX_WND_2_FREQ; + break; + } + case PHY_DEF_RX2_DR: + { + phyParam.Value = KR920_RX_WND_2_DR; + break; + } + case PHY_CHANNELS_MASK: + { + phyParam.ChannelsMask = RegionNvmGroup2->ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = RegionNvmGroup2->ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = KR920_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = RegionNvmGroup2->Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + { + phyParam.Value = KR920_DEFAULT_UPLINK_DWELL_TIME; + break; + } + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = REGION_COMMON_DEFAULT_DOWNLINK_DWELL_TIME; + break; + } + case PHY_DEF_MAX_EIRP: + { + // We set the higher maximum EIRP as default value. + // The reason for this is, that the frequency may + // change during a channel selection for the next uplink. + // The value has to be recalculated in the TX configuration. + phyParam.fValue = KR920_DEFAULT_MAX_EIRP_HIGH; + break; + } + case PHY_DEF_ANTENNA_GAIN: + { + phyParam.fValue = KR920_DEFAULT_ANTENNA_GAIN; + break; + } + case PHY_BEACON_CHANNEL_FREQ: + { + phyParam.Value = KR920_BEACON_CHANNEL_FREQ; + break; + } + case PHY_BEACON_FORMAT: + { + phyParam.BeaconFormat.BeaconSize = KR920_BEACON_SIZE; + phyParam.BeaconFormat.Rfu1Size = KR920_RFU1_SIZE; + phyParam.BeaconFormat.Rfu2Size = KR920_RFU2_SIZE; + break; + } + case PHY_BEACON_CHANNEL_DR: + { + phyParam.Value = KR920_BEACON_CHANNEL_DR; + break; + } + case PHY_PING_SLOT_CHANNEL_FREQ: + { + phyParam.Value = KR920_PING_SLOT_CHANNEL_FREQ; + break; + } + case PHY_PING_SLOT_CHANNEL_DR: + { + phyParam.Value = KR920_PING_SLOT_CHANNEL_DR; + break; + } + case PHY_SF_FROM_DR: + { + phyParam.Value = DataratesKR920[getPhy->Datarate]; + break; + } + case PHY_BW_FROM_DR: + { + phyParam.Value = RegionCommonGetBandwidth( getPhy->Datarate, BandwidthsKR920 ); + break; + } + default: + { + break; + } + } + + return phyParam; +} + +void RegionKR920SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ + RegionCommonSetBandTxDone( &RegionBands[RegionNvmGroup2->Channels[txDone->Channel].Band], + txDone->LastTxAirTime, txDone->Joined, txDone->ElapsedTimeSinceStartUp ); +} + +void RegionKR920InitDefaults( InitDefaultsParams_t* params ) +{ + Band_t bands[KR920_MAX_NB_BANDS] = + { + KR920_BAND0 + }; + + switch( params->Type ) + { + case INIT_TYPE_DEFAULTS: + { + if( ( params->NvmGroup1 == NULL ) || ( params->NvmGroup2 == NULL ) ) + { + return; + } + + RegionNvmGroup1 = (RegionNvmDataGroup1_t*) params->NvmGroup1; + RegionNvmGroup2 = (RegionNvmDataGroup2_t*) params->NvmGroup2; + RegionBands = (Band_t*) params->Bands; + + // Initialize bands + memcpy1( ( uint8_t* )RegionBands, ( uint8_t* )bands, sizeof( Band_t ) * KR920_MAX_NB_BANDS ); + + // Default channels + RegionNvmGroup2->Channels[0] = ( ChannelParams_t ) KR920_LC1; + RegionNvmGroup2->Channels[1] = ( ChannelParams_t ) KR920_LC2; + RegionNvmGroup2->Channels[2] = ( ChannelParams_t ) KR920_LC3; + + // Default ChannelsMask + RegionNvmGroup2->ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 ); + + // Update the channels mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + + RegionNvmGroup2->RssiFreeThreshold = KR920_RSSI_FREE_TH; + RegionNvmGroup2->CarrierSenseTime = KR920_CARRIER_SENSE_TIME; + break; + } + case INIT_TYPE_RESET_TO_DEFAULT_CHANNELS: + { + // Reset Channels Rx1Frequency to default 0 + RegionNvmGroup2->Channels[0].Rx1Frequency = 0; + RegionNvmGroup2->Channels[1].Rx1Frequency = 0; + RegionNvmGroup2->Channels[2].Rx1Frequency = 0; + // Update the channels mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + break; + } + case INIT_TYPE_ACTIVATE_DEFAULT_CHANNELS: + { + // Restore channels default mask + RegionNvmGroup2->ChannelsMask[0] |= RegionNvmGroup2->ChannelsDefaultMask[0]; + break; + } + default: + { + break; + } + } +} + +bool RegionKR920Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( phyAttribute ) + { + case PHY_FREQUENCY: + { + return VerifyRfFreq( verify->Frequency ); + } + case PHY_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, KR920_TX_MIN_DATARATE, KR920_TX_MAX_DATARATE ); + } + case PHY_DEF_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 ); + } + case PHY_RX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, KR920_RX_MIN_DATARATE, KR920_RX_MAX_DATARATE ); + } + case PHY_DEF_TX_POWER: + case PHY_TX_POWER: + { + // Remark: switched min and max! + return RegionCommonValueInRange( verify->TxPower, KR920_MAX_TX_POWER, KR920_MIN_TX_POWER ); + } + case PHY_DUTY_CYCLE: + { + return KR920_DUTY_CYCLE_ENABLED; + } + default: + return false; + } +} + +void RegionKR920ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ + ChannelParams_t newChannel; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + // Setup default datarate range + newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0; + + // Size of the optional CF list + if( applyCFList->Size != 16 ) + { + return; + } + + // Last byte CFListType must be 0 to indicate the CFList contains a list of frequencies + if( applyCFList->Payload[15] != 0 ) + { + return; + } + + // Last byte is RFU, don't take it into account + for( uint8_t i = 0, chanIdx = KR920_NUMB_DEFAULT_CHANNELS; chanIdx < KR920_MAX_NB_CHANNELS; i+=3, chanIdx++ ) + { + if( chanIdx < ( KR920_NUMB_CHANNELS_CF_LIST + KR920_NUMB_DEFAULT_CHANNELS ) ) + { + // Channel frequency + newChannel.Frequency = (uint32_t) applyCFList->Payload[i]; + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 ); + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 ); + newChannel.Frequency *= 100; + + // Initialize alternative frequency to 0 + newChannel.Rx1Frequency = 0; + } + else + { + newChannel.Frequency = 0; + newChannel.DrRange.Value = 0; + newChannel.Rx1Frequency = 0; + } + + if( newChannel.Frequency != 0 ) + { + channelAdd.NewChannel = &newChannel; + channelAdd.ChannelId = chanIdx; + + // Try to add all channels + RegionKR920ChannelAdd( &channelAdd ); + } + else + { + channelRemove.ChannelId = chanIdx; + + RegionKR920ChannelsRemove( &channelRemove ); + } + } +} + +bool RegionKR920ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + default: + return false; + } + return true; +} + +void RegionKR920ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + uint32_t tSymbolInUs = 0; + + // Get the datarate, perform a boundary check + rxConfigParams->Datarate = MIN( datarate, KR920_RX_MAX_DATARATE ); + rxConfigParams->Bandwidth = RegionCommonGetBandwidth( rxConfigParams->Datarate, BandwidthsKR920 ); + + tSymbolInUs = RegionCommonComputeSymbolTimeLoRa( DataratesKR920[rxConfigParams->Datarate], BandwidthsKR920[rxConfigParams->Datarate] ); + + RegionCommonComputeRxWindowParameters( tSymbolInUs, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +} + +bool RegionKR920RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ + int8_t dr = rxConfig->Datarate; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->RxSlot == RX_SLOT_WIN_1 ) + { + // Apply window 1 frequency + frequency = RegionNvmGroup2->Channels[rxConfig->Channel].Frequency; + // Apply the alternative RX 1 window frequency, if it is available + if( RegionNvmGroup2->Channels[rxConfig->Channel].Rx1Frequency != 0 ) + { + frequency = RegionNvmGroup2->Channels[rxConfig->Channel].Rx1Frequency; + } + } + + // Read the physical datarate from the datarates table + phyDr = DataratesKR920[dr]; + + Radio.SetChannel( frequency ); + + // Radio configuration + Radio.SetRxConfig( MODEM_LORA, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous ); + Radio.SetMaxPayloadLength( MODEM_LORA, MaxPayloadOfDatarateKR920[dr] + LORAMAC_FRAME_PAYLOAD_OVERHEAD_SIZE ); + + *datarate = (uint8_t) dr; + return true; +} + +bool RegionKR920TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + int8_t phyDr = DataratesKR920[txConfig->Datarate]; + int8_t txPowerLimited = RegionCommonLimitTxPower( txConfig->TxPower, RegionBands[RegionNvmGroup2->Channels[txConfig->Channel].Band].TxMaxPower ); + uint32_t bandwidth = RegionCommonGetBandwidth( txConfig->Datarate, BandwidthsKR920 ); + float maxEIRP = GetMaxEIRP( RegionNvmGroup2->Channels[txConfig->Channel].Frequency ); + int8_t phyTxPower = 0; + + // Take the minimum between the maxEIRP and txConfig->MaxEirp. + // The value of txConfig->MaxEirp could have changed during runtime, e.g. due to a MAC command. + maxEIRP = MIN( txConfig->MaxEirp, maxEIRP ); + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, maxEIRP, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( RegionNvmGroup2->Channels[txConfig->Channel].Frequency ); + + Radio.SetTxConfig( MODEM_LORA, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 4000 ); + + // Setup maximum payload lenght of the radio driver + Radio.SetMaxPayloadLength( MODEM_LORA, txConfig->PktLen ); + // Update time-on-air + *txTimeOnAir = GetTimeOnAir( txConfig->Datarate, txConfig->PktLen ); + + *txPower = txPowerLimited; + return true; +} + +uint8_t RegionKR920LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; + RegionCommonLinkAdrParams_t linkAdrParams = { 0 }; + uint8_t nextIndex = 0; + uint8_t bytesProcessed = 0; + uint16_t chMask = 0; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams; + + while( bytesProcessed < linkAdrReq->PayloadSize ) + { + // Get ADR request parameters + nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); + + if( nextIndex == 0 ) + break; // break loop, since no more request has been found + + // Update bytes processed + bytesProcessed += nextIndex; + + // Revert status, as we only check the last ADR request for the channel mask KO + status = 0x07; + + // Setup temporary channels mask + chMask = linkAdrParams.ChMask; + + // Verify channels mask + if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) ) + { + status &= 0xFE; // Channel mask KO + } + else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) || + ( linkAdrParams.ChMaskCtrl >= 7 ) ) + { + // RFU + status &= 0xFE; // Channel mask KO + } + else + { + for( uint8_t i = 0; i < KR920_MAX_NB_CHANNELS; i++ ) + { + if( linkAdrParams.ChMaskCtrl == 6 ) + { + if( RegionNvmGroup2->Channels[i].Frequency != 0 ) + { + chMask |= 1 << i; + } + } + else + { + if( ( ( chMask & ( 1 << i ) ) != 0 ) && + ( RegionNvmGroup2->Channels[i].Frequency == 0 ) ) + {// Trying to enable an undefined channel + status &= 0xFE; // Channel mask KO + } + } + } + } + } + + // Get the minimum possible datarate + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; + phyParam = RegionKR920GetPhyParam( &getPhy ); + + linkAdrVerifyParams.Status = status; + linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; + linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; + linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; + linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; + linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; + linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; + linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; + linkAdrVerifyParams.NbChannels = KR920_MAX_NB_CHANNELS; + linkAdrVerifyParams.ChannelsMask = &chMask; + linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value; + linkAdrVerifyParams.MaxDatarate = KR920_TX_MAX_DATARATE; + linkAdrVerifyParams.Channels = RegionNvmGroup2->Channels; + linkAdrVerifyParams.MinTxPower = KR920_MIN_TX_POWER; + linkAdrVerifyParams.MaxTxPower = KR920_MAX_TX_POWER; + linkAdrVerifyParams.Version = linkAdrReq->Version; + + // Verify the parameters and update, if necessary + status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep ); + + // Update channelsMask if everything is correct + if( status == 0x07 ) + { + // Set the channels mask to a default value + memset1( ( uint8_t* ) RegionNvmGroup2->ChannelsMask, 0, sizeof( RegionNvmGroup2->ChannelsMask ) ); + // Update the channels mask + RegionNvmGroup2->ChannelsMask[0] = chMask; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + + return status; +} + +uint8_t RegionKR920RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; + + // Verify radio frequency + if( VerifyRfFreq( rxParamSetupReq->Frequency ) == false ) + { + status &= 0xFE; // Channel frequency KO + } + + // Verify datarate + if( RegionCommonValueInRange( rxParamSetupReq->Datarate, KR920_RX_MIN_DATARATE, KR920_RX_MAX_DATARATE ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify datarate offset + if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, KR920_MIN_RX1_DR_OFFSET, KR920_MAX_RX1_DR_OFFSET ) == false ) + { + status &= 0xFB; // Rx1DrOffset range KO + } + + return status; +} + +int8_t RegionKR920NewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + uint8_t status = 0x03; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + if( newChannelReq->NewChannel->Frequency == 0 ) + { + channelRemove.ChannelId = newChannelReq->ChannelId; + + // Remove + if( RegionKR920ChannelsRemove( &channelRemove ) == false ) + { + status &= 0xFC; + } + } + else + { + channelAdd.NewChannel = newChannelReq->NewChannel; + channelAdd.ChannelId = newChannelReq->ChannelId; + + switch( RegionKR920ChannelAdd( &channelAdd ) ) + { + case LORAMAC_STATUS_OK: + { + break; + } + case LORAMAC_STATUS_FREQUENCY_INVALID: + { + status &= 0xFE; + break; + } + case LORAMAC_STATUS_DATARATE_INVALID: + { + status &= 0xFD; + break; + } + case LORAMAC_STATUS_FREQ_AND_DR_INVALID: + { + status &= 0xFC; + break; + } + default: + { + status &= 0xFC; + break; + } + } + } + + return status; +} + +int8_t RegionKR920TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + // Do not accept the request + return -1; +} + +int8_t RegionKR920DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + uint8_t status = 0x03; + + if( dlChannelReq->ChannelId >= ( CHANNELS_MASK_SIZE * 16 ) ) + { + return 0; + } + + // Verify if the frequency is supported + if( VerifyRfFreq( dlChannelReq->Rx1Frequency ) == false ) + { + status &= 0xFE; + } + + // Verify if an uplink frequency exists + if( RegionNvmGroup2->Channels[dlChannelReq->ChannelId].Frequency == 0 ) + { + status &= 0xFD; + } + + // Apply Rx1 frequency, if the status is OK + if( status == 0x03 ) + { + RegionNvmGroup2->Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency; + } + + return status; +} + +int8_t RegionKR920AlternateDr( int8_t currentDr, AlternateDrType_t type ) +{ + return currentDr; +} + +LoRaMacStatus_t RegionKR920NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + uint8_t channelNext = 0; + uint8_t nbEnabledChannels = 0; + uint8_t nbRestrictedChannels = 0; + uint8_t enabledChannels[KR920_MAX_NB_CHANNELS] = { 0 }; + RegionCommonIdentifyChannelsParam_t identifyChannelsParam; + RegionCommonCountNbOfEnabledChannelsParams_t countChannelsParams; + LoRaMacStatus_t status = LORAMAC_STATUS_NO_CHANNEL_FOUND; + uint16_t joinChannels = KR920_JOIN_CHANNELS; + + if( RegionCommonCountChannels( RegionNvmGroup2->ChannelsMask, 0, 1 ) == 0 ) + { // Reactivate default channels + RegionNvmGroup2->ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + + // Search how many channels are enabled + countChannelsParams.Joined = nextChanParams->Joined; + countChannelsParams.Datarate = nextChanParams->Datarate; + countChannelsParams.ChannelsMask = RegionNvmGroup2->ChannelsMask; + countChannelsParams.Channels = RegionNvmGroup2->Channels; + countChannelsParams.Bands = RegionBands; + countChannelsParams.MaxNbChannels = KR920_MAX_NB_CHANNELS; + countChannelsParams.JoinChannels = &joinChannels; + + identifyChannelsParam.AggrTimeOff = nextChanParams->AggrTimeOff; + identifyChannelsParam.LastAggrTx = nextChanParams->LastAggrTx; + identifyChannelsParam.DutyCycleEnabled = nextChanParams->DutyCycleEnabled; + identifyChannelsParam.MaxBands = KR920_MAX_NB_BANDS; + + identifyChannelsParam.ElapsedTimeSinceStartUp = nextChanParams->ElapsedTimeSinceStartUp; + identifyChannelsParam.LastTxIsJoinRequest = nextChanParams->LastTxIsJoinRequest; + identifyChannelsParam.ExpectedTimeOnAir = GetTimeOnAir( nextChanParams->Datarate, nextChanParams->PktLen ); + + identifyChannelsParam.CountNbOfEnabledChannelsParam = &countChannelsParams; + + status = RegionCommonIdentifyChannels( &identifyChannelsParam, aggregatedTimeOff, enabledChannels, + &nbEnabledChannels, &nbRestrictedChannels, time ); + + if( status == LORAMAC_STATUS_OK ) + { + for( uint8_t i = 0, j = randr( 0, nbEnabledChannels - 1 ); i < KR920_MAX_NB_CHANNELS; i++ ) + { + channelNext = enabledChannels[j]; + j = ( j + 1 ) % nbEnabledChannels; + + // Perform carrier sense for KR920_CARRIER_SENSE_TIME + // If the channel is free, we can stop the LBT mechanism + if( Radio.IsChannelFree( RegionNvmGroup2->Channels[channelNext].Frequency, KR920_LBT_RX_BANDWIDTH, RegionNvmGroup2->RssiFreeThreshold, RegionNvmGroup2->CarrierSenseTime ) == true ) + { + // Free channel found + *channel = channelNext; + return LORAMAC_STATUS_OK; + } + } + // Even if one or more channels are available according to the channel plan, no free channel + // was found during the LBT procedure. + status = LORAMAC_STATUS_NO_FREE_CHANNEL_FOUND; + } + else if( status == LORAMAC_STATUS_NO_CHANNEL_FOUND ) + { + // Datarate not supported by any channel, restore defaults + RegionNvmGroup2->ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + } + return status; +} + +LoRaMacStatus_t RegionKR920ChannelAdd( ChannelAddParams_t* channelAdd ) +{ + bool drInvalid = false; + bool freqInvalid = false; + uint8_t id = channelAdd->ChannelId; + + if( id < KR920_NUMB_DEFAULT_CHANNELS ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + + if( id >= KR920_MAX_NB_CHANNELS ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + // Validate the datarate range + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, KR920_TX_MIN_DATARATE, KR920_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, KR920_TX_MIN_DATARATE, KR920_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max ) + { + drInvalid = true; + } + + // Check frequency + if( freqInvalid == false ) + { + if( VerifyRfFreq( channelAdd->NewChannel->Frequency ) == false ) + { + freqInvalid = true; + } + } + + // Check status + if( ( drInvalid == true ) && ( freqInvalid == true ) ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + if( drInvalid == true ) + { + return LORAMAC_STATUS_DATARATE_INVALID; + } + if( freqInvalid == true ) + { + return LORAMAC_STATUS_FREQUENCY_INVALID; + } + + memcpy1( ( uint8_t* ) &(RegionNvmGroup2->Channels[id]), ( uint8_t* ) channelAdd->NewChannel, sizeof( RegionNvmGroup2->Channels[id] ) ); + RegionNvmGroup2->Channels[id].Band = 0; + RegionNvmGroup2->ChannelsMask[0] |= ( 1 << id ); + return LORAMAC_STATUS_OK; +} + +bool RegionKR920ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + uint8_t id = channelRemove->ChannelId; + + if( id < KR920_NUMB_DEFAULT_CHANNELS ) + { + return false; + } + + // Remove the channel from the list of channels + RegionNvmGroup2->Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 }; + + return RegionCommonChanDisable( RegionNvmGroup2->ChannelsMask, id, KR920_MAX_NB_CHANNELS ); +} + +uint8_t RegionKR920ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + int8_t datarate = dr - drOffset; + + if( datarate < 0 ) + { + datarate = DR_0; + } + return datarate; +} + +void RegionKR920RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ) +{ + RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup; + + regionCommonRxBeaconSetup.Datarates = DataratesKR920; + regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency; + regionCommonRxBeaconSetup.BeaconSize = KR920_BEACON_SIZE; + regionCommonRxBeaconSetup.BeaconDatarate = KR920_BEACON_CHANNEL_DR; + regionCommonRxBeaconSetup.BeaconChannelBW = KR920_BEACON_CHANNEL_BW; + regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime; + regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout; + + RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup ); + + // Store downlink datarate + *outDr = KR920_BEACON_CHANNEL_DR; +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionKR920.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionKR920.h new file mode 100644 index 0000000000..de195eb80c --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionKR920.h @@ -0,0 +1,452 @@ +/*! + * \file RegionKR920.h + * + * \brief Region definition for KR920 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * \defgroup REGIONKR920 Region KR920 + * Implementation according to LoRaWAN Specification v1.0.2. + * \{ + */ +#ifndef __REGION_KR920_H__ +#define __REGION_KR920_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "region/Region.h" + +/*! + * LoRaMac maximum number of channels + */ +#define KR920_MAX_NB_CHANNELS 16 + +/*! + * Number of default channels + */ +#define KR920_NUMB_DEFAULT_CHANNELS 3 + +/*! + * Number of channels to apply for the CF list + */ +#define KR920_NUMB_CHANNELS_CF_LIST 5 + +/*! + * Minimal datarate that can be used by the node + */ +#define KR920_TX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define KR920_TX_MAX_DATARATE DR_5 + +/*! + * Minimal datarate that can be used by the node + */ +#define KR920_RX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define KR920_RX_MAX_DATARATE DR_5 + +/*! + * Default datarate used by the node + */ +#define KR920_DEFAULT_DATARATE DR_0 + +/*! + * Minimal Rx1 receive datarate offset + */ +#define KR920_MIN_RX1_DR_OFFSET 0 + +/*! + * Maximal Rx1 receive datarate offset + */ +#define KR920_MAX_RX1_DR_OFFSET 5 + +/*! + * Minimal Tx output power that can be used by the node + */ +#define KR920_MIN_TX_POWER TX_POWER_7 + +/*! + * Maximal Tx output power that can be used by the node + */ +#define KR920_MAX_TX_POWER TX_POWER_0 + +/*! + * Default Tx output power used by the node + */ +#define KR920_DEFAULT_TX_POWER TX_POWER_0 + +/*! + * Default Max EIRP for frequency 920.9 MHz - 921.9 MHz + */ +#define KR920_DEFAULT_MAX_EIRP_LOW 10.0f + +/*! + * Default Max EIRP for frequency 922.1 MHz - 923.3 MHz + */ +#define KR920_DEFAULT_MAX_EIRP_HIGH 14.0f + +/*! + * Default antenna gain + */ +#define KR920_DEFAULT_ANTENNA_GAIN 2.15f + +/*! + * Enabled or disabled the duty cycle + */ +#define KR920_DUTY_CYCLE_ENABLED 0 + +/*! + * Maximum RX window duration + */ +#define KR920_MAX_RX_WINDOW 4000 + +#if ( KR920_DEFAULT_DATARATE > DR_5 ) +#error "A default DR higher than DR_5 may lead to connectivity loss." +#endif + +/*! + * Second reception window channel frequency definition. + */ +#define KR920_RX_WND_2_FREQ 921900000 + +/*! + * Second reception window channel datarate definition. + */ +#define KR920_RX_WND_2_DR DR_0 + +/*! + * Default uplink dwell time configuration + */ +#define KR920_DEFAULT_UPLINK_DWELL_TIME 0 + +/* + * CLASS B + */ +/*! + * Beacon frequency + */ +#define KR920_BEACON_CHANNEL_FREQ 923100000 + +/*! + * Ping slot channel frequency + */ +#define KR920_PING_SLOT_CHANNEL_FREQ 923100000 + +/*! + * Payload size of a beacon frame + */ +#define KR920_BEACON_SIZE 17 + +/*! + * Size of RFU 1 field + */ +#define KR920_RFU1_SIZE 1 + +/*! + * Size of RFU 2 field + */ +#define KR920_RFU2_SIZE 0 + +/*! + * Datarate of the beacon channel + */ +#define KR920_BEACON_CHANNEL_DR DR_3 + +/*! + * Bandwith of the beacon channel + */ +#define KR920_BEACON_CHANNEL_BW 0 + +/*! + * Ping slot channel datarate + */ +#define KR920_PING_SLOT_CHANNEL_DR DR_3 + +/*! + * Maximum number of bands + */ +#define KR920_MAX_NB_BANDS 1 + +/*! + * Band 0 definition + * Band = { DutyCycle, TxMaxPower, LastBandUpdateTime, LastMaxCreditAssignTime, TimeCredits, MaxTimeCredits, ReadyForTransmission } + */ +#define KR920_BAND0 { 1 , KR920_MAX_TX_POWER, 0, 0, 0, 0, 0 } // 100.0 % + +/*! + * LoRaMac default channel 1 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define KR920_LC1 { 922100000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac default channel 2 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define KR920_LC2 { 922300000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac default channel 3 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define KR920_LC3 { 922500000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac channels which are allowed for the join procedure + */ +#define KR920_JOIN_CHANNELS ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) ) + +/*! + * Data rates table definition + */ +static const uint8_t DataratesKR920[] = { 12, 11, 10, 9, 8, 7 }; + +/*! + * Bandwidths table definition in Hz + */ +static const uint32_t BandwidthsKR920[] = { 125000, 125000, 125000, 125000, 125000, 125000 }; + +/*! + * Maximum payload with respect to the datarate index. + */ +static const uint8_t MaxPayloadOfDatarateKR920[] = { 51, 51, 51, 115, 242, 242 }; + +/*! + * \brief The function gets a value of a specific phy attribute. + * + * \param [IN] getPhy Pointer to the function parameters. + * + * \retval Returns a structure containing the PHY parameter. + */ +PhyParam_t RegionKR920GetPhyParam( GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionKR920SetBandTxDone( SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] type Sets the initialization type. + */ +void RegionKR920InitDefaults( InitDefaultsParams_t* params ); + +/*! + * \brief Verifies a parameter. + * + * \param [IN] verify Pointer to the function parameters. + * + * \param [IN] type Sets the initialization type. + * + * \retval Returns true, if the parameter is valid. + */ +bool RegionKR920Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + +/*! + * \brief The function parses the input buffer and sets up the channels of the + * CF list. + * + * \param [IN] applyCFList Pointer to the function parameters. + */ +void RegionKR920ApplyCFList( ApplyCFListParams_t* applyCFList ); + +/*! + * \brief Sets a channels mask. + * + * \param [IN] chanMaskSet Pointer to the function parameters. + * + * \retval Returns true, if the channels mask could be set. + */ +bool RegionKR920ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ); + +/*! + * Computes the Rx window timeout and offset. + * + * \param [IN] datarate Rx window datarate index to be used + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms + * interval around RxOffset + * + * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields. + */ +void RegionKR920ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ); + +/*! + * \brief Configuration of the RX windows. + * + * \param [IN] rxConfig Pointer to the function parameters. + * + * \param [OUT] datarate The datarate index which was set. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionKR920RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ); + +/*! + * \brief TX configuration. + * + * \param [IN] txConfig Pointer to the function parameters. + * + * \param [OUT] txPower The tx power index which was set. + * + * \param [OUT] txTimeOnAir The time-on-air of the frame. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionKR920TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ); + +/*! + * \brief The function processes a Link ADR Request. + * + * \param [IN] linkAdrReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionKR920LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ); + +/*! + * \brief The function processes a RX Parameter Setup Request. + * + * \param [IN] rxParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionKR920RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ); + +/*! + * \brief The function processes a Channel Request. + * + * \param [IN] newChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +int8_t RegionKR920NewChannelReq( NewChannelReqParams_t* newChannelReq ); + +/*! + * \brief The function processes a TX ParamSetup Request. + * + * \param [IN] txParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + * Returns -1, if the functionality is not implemented. In this case, the end node + * shall not process the command. + */ +int8_t RegionKR920TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ); + +/*! + * \brief The function processes a DlChannel Request. + * + * \param [IN] dlChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +int8_t RegionKR920DlChannelReq( DlChannelReqParams_t* dlChannelReq ); + +/*! + * \brief Alternates the datarate of the channel for the join request. + * + * \param [IN] currentDr current datarate. + * + * \retval Datarate to apply. + */ +int8_t RegionKR920AlternateDr( int8_t currentDr, AlternateDrType_t type ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [OUT] channel Next channel to use for TX. + * + * \param [OUT] time Time to wait for the next transmission according to the duty + * cycle. + * + * \param [OUT] aggregatedTimeOff Updates the aggregated time off. + * + * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate] + */ +LoRaMacStatus_t RegionKR920NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ); + +/*! + * \brief Adds a channel. + * + * \param [IN] channelAdd Pointer to the function parameters. + * + * \retval Status of the operation. + */ +LoRaMacStatus_t RegionKR920ChannelAdd( ChannelAddParams_t* channelAdd ); + +/*! + * \brief Removes a channel. + * + * \param [IN] channelRemove Pointer to the function parameters. + * + * \retval Returns true, if the channel was removed successfully. + */ +bool RegionKR920ChannelsRemove( ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms + * + * \param [IN] dr Current datarate + * + * \param [IN] drOffset Offset to be applied + * + * \retval newDr Computed datarate. + */ +uint8_t RegionKR920ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! + * \brief Sets the radio into beacon reception mode + * + * \param [IN] rxBeaconSetup Pointer to the function parameters + */ + void RegionKR920RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ); + +/*! \} defgroup REGIONKR920 */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGION_KR920_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionNvm.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionNvm.h new file mode 100644 index 0000000000..225a7123e0 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionNvm.h @@ -0,0 +1,168 @@ +/*! + * \file RegionNvm.h + * + * \brief Region independent non-volatile data. + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \addtogroup REGIONCOMMON + * + * \{ + */ +#ifndef __REGIONNVM_H__ +#define __REGIONNVM_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "LoRaMacTypes.h" + +/*! + * Channel plan for region CN470 + */ +typedef enum eRegionCN470ChannelPlan +{ + CHANNEL_PLAN_UNKNOWN, + CHANNEL_PLAN_20MHZ_TYPE_A, + CHANNEL_PLAN_20MHZ_TYPE_B, + CHANNEL_PLAN_26MHZ_TYPE_A, + CHANNEL_PLAN_26MHZ_TYPE_B +}RegionCN470ChannelPlan_t; + +// Selection of REGION_NVM_MAX_NB_CHANNELS +#if defined( REGION_CN470 ) + #define REGION_NVM_MAX_NB_CHANNELS 96 +#elif defined( REGION_US915 ) || defined( REGION_AU915 ) + #define REGION_NVM_MAX_NB_CHANNELS 72 +#else + // All others + #define REGION_NVM_MAX_NB_CHANNELS 16 +#endif + +// Selection of REGION_NVM_MAX_NB_BANDS +#if defined( REGION_EU868 ) + #define REGION_NVM_MAX_NB_BANDS 6 +#else + // All others + #define REGION_NVM_MAX_NB_BANDS 1 +#endif + +// Selection of REGION_NVM_CHANNELS_MASK_SIZE +#if defined( REGION_CN470 ) || defined( REGION_US915 ) || \ + defined( REGION_AU915 ) + #define REGION_NVM_CHANNELS_MASK_SIZE 6 +#else + // All others + #define REGION_NVM_CHANNELS_MASK_SIZE 1 +#endif + +/*! + * Region specific data which must be stored in the NVM. + */ +typedef struct sRegionNvmDataGroup1 +{ +//#if defined( REGION_US915 ) || defined( REGION_AU915 ) || defined( REGION_CN470 ) + /*! + * LoRaMac channels remaining + */ + uint16_t ChannelsMaskRemaining[ REGION_NVM_CHANNELS_MASK_SIZE ]; +//#endif +//#if defined( REGION_US915 ) || defined( REGION_AU915 ) + /*! + * Index of current in use 8 bit group (0: bit 0 - 7, 1: bit 8 - 15, ..., + * 7: bit 56 - 63) + */ + uint8_t JoinChannelGroupsCurrentIndex; + /*! + * Counter of join trials needed to alternate between datarates. + */ + uint8_t JoinTrialsCounter; +//#endif + /*! + * CRC32 value of the Region data structure. + */ + uint32_t Crc32; +}RegionNvmDataGroup1_t; + +/*! + * Region specific data which must be stored in the NVM. + * Parameters which do not change very frequently. + */ +typedef struct sRegionNvmDataGroup2 +{ + /*! + * LoRaMAC channels + */ + ChannelParams_t Channels[ REGION_NVM_MAX_NB_CHANNELS ]; + /*! + * LoRaMac channels mask + */ + uint16_t ChannelsMask[ REGION_NVM_CHANNELS_MASK_SIZE ]; + /*! + * LoRaMac channels default mask + */ + uint16_t ChannelsDefaultMask[ REGION_NVM_CHANNELS_MASK_SIZE ]; +//#if defined( REGION_CN470 ) + /*! + * Holds the channel plan. + */ + RegionCN470ChannelPlan_t ChannelPlan; + /*! + * Holds the common join channel, if its an OTAA device, otherwise + * this value is 0. + */ + uint8_t CommonJoinChannelIndex; + /*! + * Identifier which specifies if the device is an OTAA device. Set + * to true, if its an OTAA device. + */ + bool IsOtaaDevice; +//#endif +//#if defined( REGION_KR920 ) || defined( REGION_AS923 ) + /*! + * RSSI threshold for a free channel [dBm] + */ + int16_t RssiFreeThreshold; + + /*! + * Specifies the time the node performs a carrier sense + */ + uint32_t CarrierSenseTime; +//#endif + + /*! + * CRC32 value of the Region data structure. + */ + uint32_t Crc32; +}RegionNvmDataGroup2_t; + +/*! \} addtogroup REGIONCOMMON */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGIONNVM_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionRU864.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionRU864.c new file mode 100644 index 0000000000..cfb1419e66 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionRU864.c @@ -0,0 +1,928 @@ +/*! + * \file RegionRU864.c + * + * \brief Region implementation for RU864 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) +*/ +#include "radio.h" +#include "RegionCommon.h" +#include "RegionRU864.h" + +// Definitions +#define CHANNELS_MASK_SIZE 1 + +/* + * Non-volatile module context. + */ +static RegionNvmDataGroup1_t* RegionNvmGroup1; +static RegionNvmDataGroup2_t* RegionNvmGroup2; +static Band_t* RegionBands; + +// Static functions +static bool VerifyRfFreq( uint32_t freq ) +{ + // Check radio driver support + if( Radio.CheckRfFrequency( freq ) == false ) + { + return false; + } + + // Check frequency bands + if( ( freq < 864000000 ) || ( freq > 870000000 ) ) + { + return false; + } + return true; +} + +static TimerTime_t GetTimeOnAir( int8_t datarate, uint16_t pktLen ) +{ + int8_t phyDr = DataratesRU864[datarate]; + uint32_t bandwidth = RegionCommonGetBandwidth( datarate, BandwidthsRU864 ); + TimerTime_t timeOnAir = 0; + + if( datarate == DR_7 ) + { // High Speed FSK channel + timeOnAir = Radio.TimeOnAir( MODEM_FSK, bandwidth, phyDr * 1000, 0, 5, false, pktLen, true ); + } + else + { + timeOnAir = Radio.TimeOnAir( MODEM_LORA, bandwidth, phyDr, 1, 8, false, pktLen, true ); + } + return timeOnAir; +} + +PhyParam_t RegionRU864GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + + switch( getPhy->Attribute ) + { + case PHY_MIN_RX_DR: + { + phyParam.Value = RU864_RX_MIN_DATARATE; + break; + } + case PHY_MIN_TX_DR: + { + phyParam.Value = RU864_TX_MIN_DATARATE; + break; + } + case PHY_DEF_TX_DR: + { + phyParam.Value = RU864_DEFAULT_DATARATE; + break; + } + case PHY_NEXT_LOWER_TX_DR: + { + RegionCommonGetNextLowerTxDrParams_t nextLowerTxDrParams = + { + .CurrentDr = getPhy->Datarate, + .MaxDr = ( int8_t )RU864_TX_MAX_DATARATE, + .MinDr = ( int8_t )RU864_TX_MIN_DATARATE, + .NbChannels = RU864_MAX_NB_CHANNELS, + .ChannelsMask = RegionNvmGroup2->ChannelsMask, + .Channels = RegionNvmGroup2->Channels, + }; + phyParam.Value = RegionCommonGetNextLowerTxDr( &nextLowerTxDrParams ); + break; + } + case PHY_MAX_TX_POWER: + { + phyParam.Value = RU864_MAX_TX_POWER; + break; + } + case PHY_DEF_TX_POWER: + { + phyParam.Value = RU864_DEFAULT_TX_POWER; + break; + } + case PHY_DEF_ADR_ACK_LIMIT: + { + phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_LIMIT; + break; + } + case PHY_DEF_ADR_ACK_DELAY: + { + phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_DELAY; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateRU864[getPhy->Datarate]; + break; + } + case PHY_DUTY_CYCLE: + { + phyParam.Value = RU864_DUTY_CYCLE_ENABLED; + break; + } + case PHY_MAX_RX_WINDOW: + { + phyParam.Value = RU864_MAX_RX_WINDOW; + break; + } + case PHY_RECEIVE_DELAY1: + { + phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY2; + break; + } + case PHY_RETRANSMIT_TIMEOUT: + { + phyParam.Value = ( REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT + randr( -REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND, REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND ) ); + break; + } + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = REGION_COMMON_DEFAULT_RX1_DR_OFFSET; + break; + } + case PHY_DEF_RX2_FREQUENCY: + { + phyParam.Value = RU864_RX_WND_2_FREQ; + break; + } + case PHY_DEF_RX2_DR: + { + phyParam.Value = RU864_RX_WND_2_DR; + break; + } + case PHY_CHANNELS_MASK: + { + phyParam.ChannelsMask = RegionNvmGroup2->ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = RegionNvmGroup2->ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = RU864_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = RegionNvmGroup2->Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + { + phyParam.Value = RU864_DEFAULT_UPLINK_DWELL_TIME; + break; + } + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = REGION_COMMON_DEFAULT_DOWNLINK_DWELL_TIME; + break; + } + case PHY_DEF_MAX_EIRP: + { + phyParam.fValue = RU864_DEFAULT_MAX_EIRP; + break; + } + case PHY_DEF_ANTENNA_GAIN: + { + phyParam.fValue = RU864_DEFAULT_ANTENNA_GAIN; + break; + } + case PHY_BEACON_CHANNEL_FREQ: + { + phyParam.Value = RU864_BEACON_CHANNEL_FREQ; + break; + } + case PHY_BEACON_FORMAT: + { + phyParam.BeaconFormat.BeaconSize = RU864_BEACON_SIZE; + phyParam.BeaconFormat.Rfu1Size = RU864_RFU1_SIZE; + phyParam.BeaconFormat.Rfu2Size = RU864_RFU2_SIZE; + break; + } + case PHY_BEACON_CHANNEL_DR: + { + phyParam.Value = RU864_BEACON_CHANNEL_DR; + break; + } + case PHY_PING_SLOT_CHANNEL_FREQ: + { + phyParam.Value = RU864_PING_SLOT_CHANNEL_FREQ; + break; + } + case PHY_PING_SLOT_CHANNEL_DR: + { + phyParam.Value = RU864_PING_SLOT_CHANNEL_DR; + break; + } + case PHY_SF_FROM_DR: + { + phyParam.Value = DataratesRU864[getPhy->Datarate]; + break; + } + case PHY_BW_FROM_DR: + { + phyParam.Value = RegionCommonGetBandwidth( getPhy->Datarate, BandwidthsRU864 ); + break; + } + default: + { + break; + } + } + + return phyParam; +} + +void RegionRU864SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ + RegionCommonSetBandTxDone( &RegionBands[RegionNvmGroup2->Channels[txDone->Channel].Band], + txDone->LastTxAirTime, txDone->Joined, txDone->ElapsedTimeSinceStartUp ); +} + +void RegionRU864InitDefaults( InitDefaultsParams_t* params ) +{ + Band_t bands[RU864_MAX_NB_BANDS] = + { + RU864_BAND0 + }; + + switch( params->Type ) + { + case INIT_TYPE_DEFAULTS: + { + if( ( params->NvmGroup1 == NULL ) || ( params->NvmGroup2 == NULL ) ) + { + return; + } + + RegionNvmGroup1 = (RegionNvmDataGroup1_t*) params->NvmGroup1; + RegionNvmGroup2 = (RegionNvmDataGroup2_t*) params->NvmGroup2; + RegionBands = (Band_t*) params->Bands; + + // Default bands + memcpy1( ( uint8_t* )RegionBands, ( uint8_t* )bands, sizeof( Band_t ) * RU864_MAX_NB_BANDS ); + + // Default channels + RegionNvmGroup2->Channels[0] = ( ChannelParams_t ) RU864_LC1; + RegionNvmGroup2->Channels[1] = ( ChannelParams_t ) RU864_LC2; + + // Default ChannelsMask + RegionNvmGroup2->ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ); + + // Update the channels mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + break; + } + case INIT_TYPE_RESET_TO_DEFAULT_CHANNELS: + { + // Reset Channels Rx1Frequency to default 0 + RegionNvmGroup2->Channels[0].Rx1Frequency = 0; + RegionNvmGroup2->Channels[1].Rx1Frequency = 0; + // Update the channels mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + break; + } + case INIT_TYPE_ACTIVATE_DEFAULT_CHANNELS: + { + // Restore channels default mask + RegionNvmGroup2->ChannelsMask[0] |= RegionNvmGroup2->ChannelsDefaultMask[0]; + break; + } + default: + { + break; + } + } +} + +bool RegionRU864Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( phyAttribute ) + { + case PHY_FREQUENCY: + { + return VerifyRfFreq( verify->Frequency ); + } + case PHY_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, RU864_TX_MIN_DATARATE, RU864_TX_MAX_DATARATE ); + } + case PHY_DEF_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 ); + } + case PHY_RX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, RU864_RX_MIN_DATARATE, RU864_RX_MAX_DATARATE ); + } + case PHY_DEF_TX_POWER: + case PHY_TX_POWER: + { + // Remark: switched min and max! + return RegionCommonValueInRange( verify->TxPower, RU864_MAX_TX_POWER, RU864_MIN_TX_POWER ); + } + case PHY_DUTY_CYCLE: + { + return RU864_DUTY_CYCLE_ENABLED; + } + default: + return false; + } +} + +void RegionRU864ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ + ChannelParams_t newChannel; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + // Setup default datarate range + newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0; + + // Size of the optional CF list + if( applyCFList->Size != 16 ) + { + return; + } + + // Last byte CFListType must be 0 to indicate the CFList contains a list of frequencies + if( applyCFList->Payload[15] != 0 ) + { + return; + } + + // Last byte is RFU, don't take it into account + for( uint8_t i = 0, chanIdx = RU864_NUMB_DEFAULT_CHANNELS; chanIdx < RU864_MAX_NB_CHANNELS; i+=3, chanIdx++ ) + { + if( chanIdx < ( RU864_NUMB_CHANNELS_CF_LIST + RU864_NUMB_DEFAULT_CHANNELS ) ) + { + // Channel frequency + newChannel.Frequency = (uint32_t) applyCFList->Payload[i]; + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 ); + newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 ); + newChannel.Frequency *= 100; + + // Initialize alternative frequency to 0 + newChannel.Rx1Frequency = 0; + } + else + { + newChannel.Frequency = 0; + newChannel.DrRange.Value = 0; + newChannel.Rx1Frequency = 0; + } + + if( newChannel.Frequency != 0 ) + { + channelAdd.NewChannel = &newChannel; + channelAdd.ChannelId = chanIdx; + + // Try to add all channels + RegionRU864ChannelAdd( &channelAdd ); + } + else + { + channelRemove.ChannelId = chanIdx; + + RegionRU864ChannelsRemove( &channelRemove ); + } + } +} + +bool RegionRU864ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 ); + break; + } + default: + return false; + } + return true; +} + +void RegionRU864ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + uint32_t tSymbolInUs = 0; + + // Get the datarate, perform a boundary check + rxConfigParams->Datarate = MIN( datarate, RU864_RX_MAX_DATARATE ); + rxConfigParams->Bandwidth = RegionCommonGetBandwidth( rxConfigParams->Datarate, BandwidthsRU864 ); + + if( rxConfigParams->Datarate == DR_7 ) + { // FSK + tSymbolInUs = RegionCommonComputeSymbolTimeFsk( DataratesRU864[rxConfigParams->Datarate] ); + } + else + { // LoRa + tSymbolInUs = RegionCommonComputeSymbolTimeLoRa( DataratesRU864[rxConfigParams->Datarate], BandwidthsRU864[rxConfigParams->Datarate] ); + } + + RegionCommonComputeRxWindowParameters( tSymbolInUs, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +} + +bool RegionRU864RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ + RadioModems_t modem; + int8_t dr = rxConfig->Datarate; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->RxSlot == RX_SLOT_WIN_1 ) + { + // Apply window 1 frequency + frequency = RegionNvmGroup2->Channels[rxConfig->Channel].Frequency; + // Apply the alternative RX 1 window frequency, if it is available + if( RegionNvmGroup2->Channels[rxConfig->Channel].Rx1Frequency != 0 ) + { + frequency = RegionNvmGroup2->Channels[rxConfig->Channel].Rx1Frequency; + } + } + + // Read the physical datarate from the datarates table + phyDr = DataratesRU864[dr]; + + Radio.SetChannel( frequency ); + + // Radio configuration + if( dr == DR_7 ) + { + modem = MODEM_FSK; + Radio.SetRxConfig( modem, 50000, phyDr * 1000, 0, 83333, 5, rxConfig->WindowTimeout, false, 0, true, 0, 0, false, rxConfig->RxContinuous ); + } + else + { + modem = MODEM_LORA; + Radio.SetRxConfig( modem, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous ); + } + + Radio.SetMaxPayloadLength( modem, MaxPayloadOfDatarateRU864[dr] + LORAMAC_FRAME_PAYLOAD_OVERHEAD_SIZE ); + + *datarate = (uint8_t) dr; + return true; +} + +bool RegionRU864TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + RadioModems_t modem; + int8_t phyDr = DataratesRU864[txConfig->Datarate]; + int8_t txPowerLimited = RegionCommonLimitTxPower( txConfig->TxPower, RegionBands[RegionNvmGroup2->Channels[txConfig->Channel].Band].TxMaxPower ); + uint32_t bandwidth = RegionCommonGetBandwidth( txConfig->Datarate, BandwidthsRU864 ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); + + // Setup the radio frequency + Radio.SetChannel( RegionNvmGroup2->Channels[txConfig->Channel].Frequency ); + + if( txConfig->Datarate == DR_7 ) + { // High Speed FSK channel + modem = MODEM_FSK; + Radio.SetTxConfig( modem, phyTxPower, 25000, bandwidth, phyDr * 1000, 0, 5, false, true, 0, 0, false, 4000 ); + } + else + { + modem = MODEM_LORA; + Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 4000 ); + } + + // Update time-on-air + *txTimeOnAir = GetTimeOnAir( txConfig->Datarate, txConfig->PktLen ); + + // Setup maximum payload lenght of the radio driver + Radio.SetMaxPayloadLength( modem, txConfig->PktLen ); + + *txPower = txPowerLimited; + return true; +} + +uint8_t RegionRU864LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; + RegionCommonLinkAdrParams_t linkAdrParams = { 0 }; + uint8_t nextIndex = 0; + uint8_t bytesProcessed = 0; + uint16_t chMask = 0; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams; + + while( bytesProcessed < linkAdrReq->PayloadSize ) + { + // Get ADR request parameters + nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); + + if( nextIndex == 0 ) + break; // break loop, since no more request has been found + + // Update bytes processed + bytesProcessed += nextIndex; + + // Revert status, as we only check the last ADR request for the channel mask KO + status = 0x07; + + // Setup temporary channels mask + chMask = linkAdrParams.ChMask; + + // Verify channels mask + if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) ) + { + status &= 0xFE; // Channel mask KO + } + else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) || + ( linkAdrParams.ChMaskCtrl >= 7 ) ) + { + // RFU + status &= 0xFE; // Channel mask KO + } + else + { + for( uint8_t i = 0; i < RU864_MAX_NB_CHANNELS; i++ ) + { + if( linkAdrParams.ChMaskCtrl == 6 ) + { + if( RegionNvmGroup2->Channels[i].Frequency != 0 ) + { + chMask |= 1 << i; + } + } + else + { + if( ( ( chMask & ( 1 << i ) ) != 0 ) && + ( RegionNvmGroup2->Channels[i].Frequency == 0 ) ) + {// Trying to enable an undefined channel + status &= 0xFE; // Channel mask KO + } + } + } + } + } + + // Get the minimum possible datarate + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; + phyParam = RegionRU864GetPhyParam( &getPhy ); + + linkAdrVerifyParams.Status = status; + linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; + linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; + linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; + linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; + linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; + linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; + linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; + linkAdrVerifyParams.NbChannels = RU864_MAX_NB_CHANNELS; + linkAdrVerifyParams.ChannelsMask = &chMask; + linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value; + linkAdrVerifyParams.MaxDatarate = RU864_TX_MAX_DATARATE; + linkAdrVerifyParams.Channels = RegionNvmGroup2->Channels; + linkAdrVerifyParams.MinTxPower = RU864_MIN_TX_POWER; + linkAdrVerifyParams.MaxTxPower = RU864_MAX_TX_POWER; + linkAdrVerifyParams.Version = linkAdrReq->Version; + + // Verify the parameters and update, if necessary + status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep ); + + // Update channelsMask if everything is correct + if( status == 0x07 ) + { + // Set the channels mask to a default value + memset1( ( uint8_t* ) RegionNvmGroup2->ChannelsMask, 0, sizeof( RegionNvmGroup2->ChannelsMask ) ); + // Update the channels mask + RegionNvmGroup2->ChannelsMask[0] = chMask; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + + return status; +} + +uint8_t RegionRU864RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; + + // Verify radio frequency + if( VerifyRfFreq( rxParamSetupReq->Frequency ) == false ) + { + status &= 0xFE; // Channel frequency KO + } + + // Verify datarate + if( RegionCommonValueInRange( rxParamSetupReq->Datarate, RU864_RX_MIN_DATARATE, RU864_RX_MAX_DATARATE ) == false ) + { + status &= 0xFD; // Datarate KO + } + + // Verify datarate offset + if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, RU864_MIN_RX1_DR_OFFSET, RU864_MAX_RX1_DR_OFFSET ) == false ) + { + status &= 0xFB; // Rx1DrOffset range KO + } + + return status; +} + +int8_t RegionRU864NewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + uint8_t status = 0x03; + ChannelAddParams_t channelAdd; + ChannelRemoveParams_t channelRemove; + + if( newChannelReq->NewChannel->Frequency == 0 ) + { + channelRemove.ChannelId = newChannelReq->ChannelId; + + // Remove + if( RegionRU864ChannelsRemove( &channelRemove ) == false ) + { + status &= 0xFC; + } + } + else + { + channelAdd.NewChannel = newChannelReq->NewChannel; + channelAdd.ChannelId = newChannelReq->ChannelId; + + switch( RegionRU864ChannelAdd( &channelAdd ) ) + { + case LORAMAC_STATUS_OK: + { + break; + } + case LORAMAC_STATUS_FREQUENCY_INVALID: + { + status &= 0xFE; + break; + } + case LORAMAC_STATUS_DATARATE_INVALID: + { + status &= 0xFD; + break; + } + case LORAMAC_STATUS_FREQ_AND_DR_INVALID: + { + status &= 0xFC; + break; + } + default: + { + status &= 0xFC; + break; + } + } + } + + return status; +} + +int8_t RegionRU864TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + // Do not accept the request + return -1; +} + +int8_t RegionRU864DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + uint8_t status = 0x03; + + if( dlChannelReq->ChannelId >= ( CHANNELS_MASK_SIZE * 16 ) ) + { + return 0; + } + + // Verify if the frequency is supported + if( VerifyRfFreq( dlChannelReq->Rx1Frequency ) == false ) + { + status &= 0xFE; + } + + // Verify if an uplink frequency exists + if( RegionNvmGroup2->Channels[dlChannelReq->ChannelId].Frequency == 0 ) + { + status &= 0xFD; + } + + // Apply Rx1 frequency, if the status is OK + if( status == 0x03 ) + { + RegionNvmGroup2->Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency; + } + + return status; +} + +int8_t RegionRU864AlternateDr( int8_t currentDr, AlternateDrType_t type ) +{ + return currentDr; +} + +LoRaMacStatus_t RegionRU864NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t nbRestrictedChannels = 0; + uint8_t enabledChannels[RU864_MAX_NB_CHANNELS] = { 0 }; + RegionCommonIdentifyChannelsParam_t identifyChannelsParam; + RegionCommonCountNbOfEnabledChannelsParams_t countChannelsParams; + LoRaMacStatus_t status = LORAMAC_STATUS_NO_CHANNEL_FOUND; + uint16_t joinChannels = RU864_JOIN_CHANNELS; + + if( RegionCommonCountChannels( RegionNvmGroup2->ChannelsMask, 0, 1 ) == 0 ) + { // Reactivate default channels + RegionNvmGroup2->ChannelsMask[0] |= LC( 1 ) + LC( 2 ); + } + + // Search how many channels are enabled + countChannelsParams.Joined = nextChanParams->Joined; + countChannelsParams.Datarate = nextChanParams->Datarate; + countChannelsParams.ChannelsMask = RegionNvmGroup2->ChannelsMask; + countChannelsParams.Channels = RegionNvmGroup2->Channels; + countChannelsParams.Bands = RegionBands; + countChannelsParams.MaxNbChannels = RU864_MAX_NB_CHANNELS; + countChannelsParams.JoinChannels = &joinChannels; + + identifyChannelsParam.AggrTimeOff = nextChanParams->AggrTimeOff; + identifyChannelsParam.LastAggrTx = nextChanParams->LastAggrTx; + identifyChannelsParam.DutyCycleEnabled = nextChanParams->DutyCycleEnabled; + identifyChannelsParam.MaxBands = RU864_MAX_NB_BANDS; + + identifyChannelsParam.ElapsedTimeSinceStartUp = nextChanParams->ElapsedTimeSinceStartUp; + identifyChannelsParam.LastTxIsJoinRequest = nextChanParams->LastTxIsJoinRequest; + identifyChannelsParam.ExpectedTimeOnAir = GetTimeOnAir( nextChanParams->Datarate, nextChanParams->PktLen ); + + identifyChannelsParam.CountNbOfEnabledChannelsParam = &countChannelsParams; + + status = RegionCommonIdentifyChannels( &identifyChannelsParam, aggregatedTimeOff, enabledChannels, + &nbEnabledChannels, &nbRestrictedChannels, time ); + + if( status == LORAMAC_STATUS_OK ) + { + // We found a valid channel + *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; + } + else if( status == LORAMAC_STATUS_NO_CHANNEL_FOUND ) + { + // Datarate not supported by any channel, restore defaults + RegionNvmGroup2->ChannelsMask[0] |= LC( 1 ) + LC( 2 ); + } + return status; +} + +LoRaMacStatus_t RegionRU864ChannelAdd( ChannelAddParams_t* channelAdd ) +{ + bool drInvalid = false; + bool freqInvalid = false; + uint8_t id = channelAdd->ChannelId; + + if( id < RU864_NUMB_DEFAULT_CHANNELS ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + + if( id >= RU864_MAX_NB_CHANNELS ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + + // Validate the datarate range + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, RU864_TX_MIN_DATARATE, RU864_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, RU864_TX_MIN_DATARATE, RU864_TX_MAX_DATARATE ) == false ) + { + drInvalid = true; + } + if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max ) + { + drInvalid = true; + } + + // Check frequency + if( freqInvalid == false ) + { + if( VerifyRfFreq( channelAdd->NewChannel->Frequency ) == false ) + { + freqInvalid = true; + } + } + + // Check status + if( ( drInvalid == true ) && ( freqInvalid == true ) ) + { + return LORAMAC_STATUS_FREQ_AND_DR_INVALID; + } + if( drInvalid == true ) + { + return LORAMAC_STATUS_DATARATE_INVALID; + } + if( freqInvalid == true ) + { + return LORAMAC_STATUS_FREQUENCY_INVALID; + } + + memcpy1( ( uint8_t* ) &(RegionNvmGroup2->Channels[id]), ( uint8_t* ) channelAdd->NewChannel, sizeof( RegionNvmGroup2->Channels[id] ) ); + RegionNvmGroup2->Channels[id].Band = 0; + RegionNvmGroup2->ChannelsMask[0] |= ( 1 << id ); + return LORAMAC_STATUS_OK; +} + +bool RegionRU864ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + uint8_t id = channelRemove->ChannelId; + + if( id < RU864_NUMB_DEFAULT_CHANNELS ) + { + return false; + } + + // Remove the channel from the list of channels + RegionNvmGroup2->Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 }; + + return RegionCommonChanDisable( RegionNvmGroup2->ChannelsMask, id, RU864_MAX_NB_CHANNELS ); +} + +uint8_t RegionRU864ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + int8_t datarate = dr - drOffset; + + if( datarate < 0 ) + { + datarate = DR_0; + } + return datarate; +} + +void RegionRU864RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ) +{ + RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup; + + regionCommonRxBeaconSetup.Datarates = DataratesRU864; + regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency; + regionCommonRxBeaconSetup.BeaconSize = RU864_BEACON_SIZE; + regionCommonRxBeaconSetup.BeaconDatarate = RU864_BEACON_CHANNEL_DR; + regionCommonRxBeaconSetup.BeaconChannelBW = RU864_BEACON_CHANNEL_BW; + regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime; + regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout; + + RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup ); + + // Store downlink datarate + *outDr = RU864_BEACON_CHANNEL_DR; +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionRU864.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionRU864.h new file mode 100644 index 0000000000..cf3cfd8b86 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionRU864.h @@ -0,0 +1,445 @@ +/*! + * \file RegionRU864.h + * + * \brief Region definition for RU864 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \defgroup REGIONRU864 Region RU864 + * Implementation according to LoRaWAN Specification v1.0.2. + * \{ + */ +#ifndef __REGION_RU864_H__ +#define __REGION_RU864_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "region/Region.h" + +/*! + * LoRaMac maximum number of channels + */ +#define RU864_MAX_NB_CHANNELS 16 + +/*! + * Number of default channels + */ +#define RU864_NUMB_DEFAULT_CHANNELS 2 + +/*! + * Number of channels to apply for the CF list + */ +#define RU864_NUMB_CHANNELS_CF_LIST 5 + +/*! + * Minimal datarate that can be used by the node + */ +#define RU864_TX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define RU864_TX_MAX_DATARATE DR_7 + +/*! + * Minimal datarate that can be used by the node + */ +#define RU864_RX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define RU864_RX_MAX_DATARATE DR_7 + +/*! + * Default datarate used by the node + */ +#define RU864_DEFAULT_DATARATE DR_0 + +/*! + * Minimal Rx1 receive datarate offset + */ +#define RU864_MIN_RX1_DR_OFFSET 0 + +/*! + * Maximal Rx1 receive datarate offset + */ +#define RU864_MAX_RX1_DR_OFFSET 5 + +/*! + * Minimal Tx output power that can be used by the node + */ +#define RU864_MIN_TX_POWER TX_POWER_7 + +/*! + * Maximal Tx output power that can be used by the node + */ +#define RU864_MAX_TX_POWER TX_POWER_0 + +/*! + * Default Tx output power used by the node + */ +#define RU864_DEFAULT_TX_POWER TX_POWER_0 + +/*! + * Default Max EIRP + */ +#define RU864_DEFAULT_MAX_EIRP 16.0f + +/*! + * Default antenna gain + */ +#define RU864_DEFAULT_ANTENNA_GAIN 2.15f + +/*! + * Enabled or disabled the duty cycle + */ +#define RU864_DUTY_CYCLE_ENABLED 1 + +/*! + * Maximum RX window duration + */ +#define RU864_MAX_RX_WINDOW 3000 //TODO + +#if ( RU864_DEFAULT_DATARATE > DR_5 ) +#error "A default DR higher than DR_5 may lead to connectivity loss." +#endif + +/*! + * Second reception window channel frequency definition. + */ +#define RU864_RX_WND_2_FREQ 869100000 + +/*! + * Second reception window channel datarate definition. + */ +#define RU864_RX_WND_2_DR DR_0 + +/*! + * Default uplink dwell time configuration + */ +#define RU864_DEFAULT_UPLINK_DWELL_TIME 0 + +/* + * CLASS B + */ +/*! + * Beacon frequency + */ +#define RU864_BEACON_CHANNEL_FREQ 869100000 + +/*! + * Ping slot channel frequency + */ +#define RU864_PING_SLOT_CHANNEL_FREQ 868900000 + +/*! + * Payload size of a beacon frame + */ +#define RU864_BEACON_SIZE 17 + +/*! + * Size of RFU 1 field + */ +#define RU864_RFU1_SIZE 1 + +/*! + * Size of RFU 2 field + */ +#define RU864_RFU2_SIZE 0 + +/*! + * Datarate of the beacon channel + */ +#define RU864_BEACON_CHANNEL_DR DR_3 + +/*! + * Bandwith of the beacon channel (Index of BandwidthsRU864[]) + */ +#define RU864_BEACON_CHANNEL_BW 0 + +/*! + * Datarate of the ping slot channel + */ +#define RU864_PING_SLOT_CHANNEL_DR DR_3 + +/*! + * Ping slot channel datarate + */ +#define RU864_PING_SLOT_CHANNEL_DR DR_3 + +/*! + * Maximum number of bands + */ +#define RU864_MAX_NB_BANDS 1 + +/*! + * Band 0 definition + * Band = { DutyCycle, TxMaxPower, LastBandUpdateTime, LastMaxCreditAssignTime, TimeCredits, MaxTimeCredits, ReadyForTransmission } + */ +#define RU864_BAND0 { 100 , RU864_MAX_TX_POWER, 0, 0, 0, 0, 0 } // 1.0 % + +/*! + * LoRaMac default channel 1 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define RU864_LC1 { 868900000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + +/*! + * LoRaMac default channel 2 + * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } + */ +#define RU864_LC2 { 869100000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } + + +/*! + * LoRaMac channels which are allowed for the join procedure + */ +#define RU864_JOIN_CHANNELS ( uint16_t )( LC( 1 ) | LC( 2 ) ) + +/*! + * Data rates table definition + */ +static const uint8_t DataratesRU864[] = { 12, 11, 10, 9, 8, 7, 7, 50 }; + +/*! + * Bandwidths table definition in Hz + */ +static const uint32_t BandwidthsRU864[] = { 125000, 125000, 125000, 125000, 125000, 125000, 250000, 0 }; + +/*! + * Maximum payload with respect to the datarate index. + */ +static const uint8_t MaxPayloadOfDatarateRU864[] = { 51, 51, 51, 115, 242, 242, 242, 242 }; + +/*! + * \brief The function gets a value of a specific phy attribute. + * + * \param [IN] getPhy Pointer to the function parameters. + * + * \retval Returns a structure containing the PHY parameter. + */ +PhyParam_t RegionRU864GetPhyParam( GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionRU864SetBandTxDone( SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] type Sets the initialization type. + */ +void RegionRU864InitDefaults( InitDefaultsParams_t* params ); + +/*! + * \brief Verifies a parameter. + * + * \param [IN] verify Pointer to the function parameters. + * + * \param [IN] type Sets the initialization type. + * + * \retval Returns true, if the parameter is valid. + */ +bool RegionRU864Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + +/*! + * \brief The function parses the input buffer and sets up the channels of the + * CF list. + * + * \param [IN] applyCFList Pointer to the function parameters. + */ +void RegionRU864ApplyCFList( ApplyCFListParams_t* applyCFList ); + +/*! + * \brief Sets a channels mask. + * + * \param [IN] chanMaskSet Pointer to the function parameters. + * + * \retval Returns true, if the channels mask could be set. + */ +bool RegionRU864ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ); + +/*! + * Computes the Rx window timeout and offset. + * + * \param [IN] datarate Rx window datarate index to be used + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms + * interval around RxOffset + * + * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields. + */ +void RegionRU864ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ); + +/*! + * \brief Configuration of the RX windows. + * + * \param [IN] rxConfig Pointer to the function parameters. + * + * \param [OUT] datarate The datarate index which was set. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionRU864RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ); + +/*! + * \brief TX configuration. + * + * \param [IN] txConfig Pointer to the function parameters. + * + * \param [OUT] txPower The tx power index which was set. + * + * \param [OUT] txTimeOnAir The time-on-air of the frame. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionRU864TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ); + +/*! + * \brief The function processes a Link ADR Request. + * + * \param [IN] linkAdrReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionRU864LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ); + +/*! + * \brief The function processes a RX Parameter Setup Request. + * + * \param [IN] rxParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionRU864RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ); + +/*! + * \brief The function processes a Channel Request. + * + * \param [IN] newChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +int8_t RegionRU864NewChannelReq( NewChannelReqParams_t* newChannelReq ); + +/*! + * \brief The function processes a TX ParamSetup Request. + * + * \param [IN] txParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + * Returns -1, if the functionality is not implemented. In this case, the end node + * shall not process the command. + */ +int8_t RegionRU864TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ); + +/*! + * \brief The function processes a DlChannel Request. + * + * \param [IN] dlChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +int8_t RegionRU864DlChannelReq( DlChannelReqParams_t* dlChannelReq ); + +/*! + * \brief Alternates the datarate of the channel for the join request. + * + * \param [IN] currentDr Current datarate. + * + * \retval Datarate to apply. + */ +int8_t RegionRU864AlternateDr( int8_t currentDr, AlternateDrType_t type ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [OUT] channel Next channel to use for TX. + * + * \param [OUT] time Time to wait for the next transmission according to the duty + * cycle. + * + * \param [OUT] aggregatedTimeOff Updates the aggregated time off. + * + * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate] + */ +LoRaMacStatus_t RegionRU864NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ); + +/*! + * \brief Adds a channel. + * + * \param [IN] channelAdd Pointer to the function parameters. + * + * \retval Status of the operation. + */ +LoRaMacStatus_t RegionRU864ChannelAdd( ChannelAddParams_t* channelAdd ); + +/*! + * \brief Removes a channel. + * + * \param [IN] channelRemove Pointer to the function parameters. + * + * \retval Returns true, if the channel was removed successfully. + */ +bool RegionRU864ChannelsRemove( ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms + * + * \param [IN] dr Current datarate + * + * \param [IN] drOffset Offset to be applied + * + * \retval newDr Computed datarate. + */ +uint8_t RegionRU864ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! + * \brief Sets the radio into beacon reception mode + * + * \param [IN] rxBeaconSetup Pointer to the function parameters + */ +void RegionRU864RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ); + +/*! \} defgroup REGIONRU864 */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGION_RU864_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionUS915.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionUS915.c new file mode 100644 index 0000000000..b6a12da484 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionUS915.c @@ -0,0 +1,930 @@ +/*! + * \file RegionUS915.c + * + * \brief Region implementation for US915 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) +*/ +#include "radio.h" +#include "RegionCommon.h" +#include "RegionUS915.h" +#include "RegionBaseUS.h" + +// Definitions +#define CHANNELS_MASK_SIZE 6 + +// A mask to select only valid 500KHz channels +#define CHANNELS_MASK_500KHZ_MASK 0x00FF + +/* + * Non-volatile module context. + */ +static RegionNvmDataGroup1_t* RegionNvmGroup1; +static RegionNvmDataGroup2_t* RegionNvmGroup2; +static Band_t* RegionBands; + +static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask ) +{ + int8_t txPowerResult = txPower; + + // Limit tx power to the band max + txPowerResult = RegionCommonLimitTxPower( txPower, maxBandTxPower ); + + if( datarate == DR_4 ) + {// Limit tx power to max 26dBm + txPowerResult = MAX( txPower, TX_POWER_2 ); + } + else + { + if( RegionCommonCountChannels( channelsMask, 0, 4 ) < 50 ) + {// Limit tx power to max 21dBm + txPowerResult = MAX( txPower, TX_POWER_5 ); + } + } + return txPowerResult; +} + +static bool VerifyRfFreq( uint32_t freq ) +{ + // Check radio driver support + if( Radio.CheckRfFrequency( freq ) == false ) + { + return false; + } + + // Rx frequencies + if( ( freq < US915_FIRST_RX1_CHANNEL ) || + ( freq > US915_LAST_RX1_CHANNEL ) || + ( ( ( freq - ( uint32_t ) US915_FIRST_RX1_CHANNEL ) % ( uint32_t ) US915_STEPWIDTH_RX1_CHANNEL ) != 0 ) ) + { + return false; + } + + // Test for frequency range - take RX and TX freqencies into account + if( ( freq < 902300000 ) || ( freq > 927500000 ) ) + { + return false; + } + return true; +} + +static TimerTime_t GetTimeOnAir( int8_t datarate, uint16_t pktLen ) +{ + int8_t phyDr = DataratesUS915[datarate]; + uint32_t bandwidth = RegionCommonGetBandwidth( datarate, BandwidthsUS915 ); + + return Radio.TimeOnAir( MODEM_LORA, bandwidth, phyDr, 1, 8, false, pktLen, true ); +} + +PhyParam_t RegionUS915GetPhyParam( GetPhyParams_t* getPhy ) +{ + PhyParam_t phyParam = { 0 }; + + switch( getPhy->Attribute ) + { + case PHY_MIN_RX_DR: + { + phyParam.Value = US915_RX_MIN_DATARATE; + break; + } + case PHY_MIN_TX_DR: + { + phyParam.Value = US915_TX_MIN_DATARATE; + break; + } + case PHY_DEF_TX_DR: + { + phyParam.Value = US915_DEFAULT_DATARATE; + break; + } + case PHY_NEXT_LOWER_TX_DR: + { + RegionCommonGetNextLowerTxDrParams_t nextLowerTxDrParams = + { + .CurrentDr = getPhy->Datarate, + .MaxDr = ( int8_t )US915_TX_MAX_DATARATE, + .MinDr = ( int8_t )US915_TX_MIN_DATARATE, + .NbChannels = US915_MAX_NB_CHANNELS, + .ChannelsMask = RegionNvmGroup2->ChannelsMask, + .Channels = RegionNvmGroup2->Channels, + }; + phyParam.Value = RegionCommonGetNextLowerTxDr( &nextLowerTxDrParams ); + break; + } + case PHY_MAX_TX_POWER: + { + phyParam.Value = US915_MAX_TX_POWER; + break; + } + case PHY_DEF_TX_POWER: + { + phyParam.Value = US915_DEFAULT_TX_POWER; + break; + } + case PHY_DEF_ADR_ACK_LIMIT: + { + phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_LIMIT; + break; + } + case PHY_DEF_ADR_ACK_DELAY: + { + phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_DELAY; + break; + } + case PHY_MAX_PAYLOAD: + { + phyParam.Value = MaxPayloadOfDatarateUS915[getPhy->Datarate]; + break; + } + case PHY_DUTY_CYCLE: + { + phyParam.Value = US915_DUTY_CYCLE_ENABLED; + break; + } + case PHY_MAX_RX_WINDOW: + { + phyParam.Value = US915_MAX_RX_WINDOW; + break; + } + case PHY_RECEIVE_DELAY1: + { + phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY1; + break; + } + case PHY_RECEIVE_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY2; + break; + } + case PHY_JOIN_ACCEPT_DELAY1: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY1; + break; + } + case PHY_JOIN_ACCEPT_DELAY2: + { + phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY2; + break; + } + case PHY_RETRANSMIT_TIMEOUT: + { + phyParam.Value = ( REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT + randr( -REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND, REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND ) ); + break; + } + case PHY_DEF_DR1_OFFSET: + { + phyParam.Value = REGION_COMMON_DEFAULT_RX1_DR_OFFSET; + break; + } + case PHY_DEF_RX2_FREQUENCY: + { + phyParam.Value = US915_RX_WND_2_FREQ; + break; + } + case PHY_DEF_RX2_DR: + { + phyParam.Value = US915_RX_WND_2_DR; + break; + } + case PHY_CHANNELS_MASK: + { + phyParam.ChannelsMask = RegionNvmGroup2->ChannelsMask; + break; + } + case PHY_CHANNELS_DEFAULT_MASK: + { + phyParam.ChannelsMask = RegionNvmGroup2->ChannelsDefaultMask; + break; + } + case PHY_MAX_NB_CHANNELS: + { + phyParam.Value = US915_MAX_NB_CHANNELS; + break; + } + case PHY_CHANNELS: + { + phyParam.Channels = RegionNvmGroup2->Channels; + break; + } + case PHY_DEF_UPLINK_DWELL_TIME: + { + phyParam.Value = US915_DEFAULT_UPLINK_DWELL_TIME; + break; + } + case PHY_DEF_DOWNLINK_DWELL_TIME: + { + phyParam.Value = REGION_COMMON_DEFAULT_DOWNLINK_DWELL_TIME; + break; + } + case PHY_DEF_MAX_EIRP: + { + phyParam.fValue = US915_DEFAULT_MAX_ERP + 2.15f; + break; + } + case PHY_DEF_ANTENNA_GAIN: + { + phyParam.fValue = 0; + break; + } + case PHY_BEACON_CHANNEL_FREQ: + { + phyParam.Value = RegionBaseUSCalcDownlinkFrequency( getPhy->Channel, + US915_BEACON_CHANNEL_FREQ, + US915_BEACON_CHANNEL_STEPWIDTH ); + break; + } + case PHY_BEACON_FORMAT: + { + phyParam.BeaconFormat.BeaconSize = US915_BEACON_SIZE; + phyParam.BeaconFormat.Rfu1Size = US915_RFU1_SIZE; + phyParam.BeaconFormat.Rfu2Size = US915_RFU2_SIZE; + break; + } + case PHY_BEACON_CHANNEL_DR: + { + phyParam.Value = US915_BEACON_CHANNEL_DR; + break; + } + case PHY_BEACON_NB_CHANNELS: + { + phyParam.Value = US915_BEACON_NB_CHANNELS; + break; + } + case PHY_PING_SLOT_CHANNEL_FREQ: + { + phyParam.Value = RegionBaseUSCalcDownlinkFrequency( getPhy->Channel, + US915_PING_SLOT_CHANNEL_FREQ, + US915_BEACON_CHANNEL_STEPWIDTH ); + break; + } + case PHY_PING_SLOT_CHANNEL_DR: + { + phyParam.Value = US915_PING_SLOT_CHANNEL_DR; + break; + } + case PHY_PING_SLOT_NB_CHANNELS: + { + phyParam.Value = US915_BEACON_NB_CHANNELS; + break; + } + case PHY_SF_FROM_DR: + { + phyParam.Value = DataratesUS915[getPhy->Datarate]; + break; + } + case PHY_BW_FROM_DR: + { + phyParam.Value = RegionCommonGetBandwidth( getPhy->Datarate, BandwidthsUS915 ); + break; + } + default: + { + break; + } + } + + return phyParam; +} + +void RegionUS915SetBandTxDone( SetBandTxDoneParams_t* txDone ) +{ + RegionCommonSetBandTxDone( &RegionBands[RegionNvmGroup2->Channels[txDone->Channel].Band], + txDone->LastTxAirTime, txDone->Joined, txDone->ElapsedTimeSinceStartUp ); +} + +void RegionUS915InitDefaults( InitDefaultsParams_t* params ) +{ + Band_t bands[US915_MAX_NB_BANDS] = + { + US915_BAND0 + }; + + switch( params->Type ) + { + case INIT_TYPE_DEFAULTS: + { + if( ( params->NvmGroup1 == NULL ) || ( params->NvmGroup2 == NULL ) ) + { + return; + } + + RegionNvmGroup1 = (RegionNvmDataGroup1_t*) params->NvmGroup1; + RegionNvmGroup2 = (RegionNvmDataGroup2_t*) params->NvmGroup2; + RegionBands = (Band_t*) params->Bands; + + // Initialize 8 bit channel groups index + RegionNvmGroup1->JoinChannelGroupsCurrentIndex = 0; + + // Initialize the join trials counter + RegionNvmGroup1->JoinTrialsCounter = 0; + + // Default bands + memcpy1( ( uint8_t* )RegionBands, ( uint8_t* )bands, sizeof( Band_t ) * US915_MAX_NB_BANDS ); + + // Default channels + for( uint8_t i = 0; i < US915_MAX_NB_CHANNELS - 8; i++ ) + { + // 125 kHz channels + RegionNvmGroup2->Channels[i].Frequency = 902300000 + i * 200000; + RegionNvmGroup2->Channels[i].DrRange.Value = ( DR_3 << 4 ) | DR_0; + RegionNvmGroup2->Channels[i].Band = 0; + } + for( uint8_t i = US915_MAX_NB_CHANNELS - 8; i < US915_MAX_NB_CHANNELS; i++ ) + { + // 500 kHz channels + RegionNvmGroup2->Channels[i].Frequency = 903000000 + ( i - ( US915_MAX_NB_CHANNELS - 8 ) ) * 1600000; + RegionNvmGroup2->Channels[i].DrRange.Value = ( DR_4 << 4 ) | DR_4; + RegionNvmGroup2->Channels[i].Band = 0; + } + + // Default ChannelsMask + RegionNvmGroup2->ChannelsDefaultMask[0] = 0xFFFF; + RegionNvmGroup2->ChannelsDefaultMask[1] = 0xFFFF; + RegionNvmGroup2->ChannelsDefaultMask[2] = 0xFFFF; + RegionNvmGroup2->ChannelsDefaultMask[3] = 0xFFFF; + RegionNvmGroup2->ChannelsDefaultMask[4] = 0x00FF; + RegionNvmGroup2->ChannelsDefaultMask[5] = 0x0000; + + // Copy channels default mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + + // Copy into channels mask remaining + RegionCommonChanMaskCopy( RegionNvmGroup1->ChannelsMaskRemaining, RegionNvmGroup2->ChannelsMask, CHANNELS_MASK_SIZE ); + break; + } + case INIT_TYPE_RESET_TO_DEFAULT_CHANNELS: + { + // Intentional fallthrough + } + case INIT_TYPE_ACTIVATE_DEFAULT_CHANNELS: + { + // Copy channels default mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE ); + + for( uint8_t i = 0; i < 6; i++ ) + { // Copy-And the channels mask + RegionNvmGroup1->ChannelsMaskRemaining[i] &= RegionNvmGroup2->ChannelsMask[i]; + } + break; + } + default: + { + break; + } + } +} + +bool RegionUS915Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) +{ + switch( phyAttribute ) + { + case PHY_FREQUENCY: + { + return VerifyRfFreq( verify->Frequency ); + } + case PHY_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, US915_TX_MIN_DATARATE, US915_TX_MAX_DATARATE ); + } + case PHY_DEF_TX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 ); + } + case PHY_RX_DR: + { + return RegionCommonValueInRange( verify->DatarateParams.Datarate, US915_RX_MIN_DATARATE, US915_RX_MAX_DATARATE ); + } + case PHY_DEF_TX_POWER: + case PHY_TX_POWER: + { + // Remark: switched min and max! + return RegionCommonValueInRange( verify->TxPower, US915_MAX_TX_POWER, US915_MIN_TX_POWER ); + } + case PHY_DUTY_CYCLE: + { + return US915_DUTY_CYCLE_ENABLED; + } + default: + return false; + } +} + +void RegionUS915ApplyCFList( ApplyCFListParams_t* applyCFList ) +{ + // Size of the optional CF list must be 16 byte + if( applyCFList->Size != 16 ) + { + return; + } + + // Last byte CFListType must be 0x01 to indicate the CFList contains a series of ChMask fields + if( applyCFList->Payload[15] != 0x01 ) + { + return; + } + + // ChMask0 - ChMask4 must be set (every ChMask has 16 bit) + for( uint8_t chMaskItr = 0, cntPayload = 0; chMaskItr <= 4; chMaskItr++, cntPayload+=2 ) + { + RegionNvmGroup2->ChannelsMask[chMaskItr] = (uint16_t) (0x00FF & applyCFList->Payload[cntPayload]); + RegionNvmGroup2->ChannelsMask[chMaskItr] |= (uint16_t) (applyCFList->Payload[cntPayload+1] << 8); + if( chMaskItr == 4 ) + { + RegionNvmGroup2->ChannelsMask[chMaskItr] = RegionNvmGroup2->ChannelsMask[chMaskItr] & CHANNELS_MASK_500KHZ_MASK; + } + // Set the channel mask to the remaining + RegionNvmGroup1->ChannelsMaskRemaining[chMaskItr] &= RegionNvmGroup2->ChannelsMask[chMaskItr]; + } +} + +bool RegionUS915ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ) +{ + uint8_t nbChannels = RegionCommonCountChannels( chanMaskSet->ChannelsMaskIn, 0, 4 ); + + // Check the number of active channels + if( ( nbChannels < 2 ) && + ( nbChannels > 0 ) ) + { + return false; + } + + switch( chanMaskSet->ChannelsMaskType ) + { + case CHANNELS_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, chanMaskSet->ChannelsMaskIn, CHANNELS_MASK_SIZE ); + + RegionNvmGroup2->ChannelsDefaultMask[4] = RegionNvmGroup2->ChannelsDefaultMask[4] & CHANNELS_MASK_500KHZ_MASK; + RegionNvmGroup2->ChannelsDefaultMask[5] = 0x0000; + + for( uint8_t i = 0; i < CHANNELS_MASK_SIZE; i++ ) + { // Copy-And the channels mask + RegionNvmGroup1->ChannelsMaskRemaining[i] &= RegionNvmGroup2->ChannelsMask[i]; + } + break; + } + case CHANNELS_DEFAULT_MASK: + { + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, CHANNELS_MASK_SIZE ); + break; + } + default: + return false; + } + return true; +} + +void RegionUS915ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ) +{ + uint32_t tSymbolInUs = 0; + + // Get the datarate, perform a boundary check + rxConfigParams->Datarate = MIN( datarate, US915_RX_MAX_DATARATE ); + rxConfigParams->Bandwidth = RegionCommonGetBandwidth( rxConfigParams->Datarate, BandwidthsUS915 ); + + tSymbolInUs = RegionCommonComputeSymbolTimeLoRa( DataratesUS915[rxConfigParams->Datarate], BandwidthsUS915[rxConfigParams->Datarate] ); + + RegionCommonComputeRxWindowParameters( tSymbolInUs, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); +} + +bool RegionUS915RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ) +{ + int8_t dr = rxConfig->Datarate; + int8_t phyDr = 0; + uint32_t frequency = rxConfig->Frequency; + + if( Radio.GetStatus( ) != RF_IDLE ) + { + return false; + } + + if( rxConfig->RxSlot == RX_SLOT_WIN_1 ) + { + // Apply window 1 frequency + frequency = US915_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 8 ) * US915_STEPWIDTH_RX1_CHANNEL; + } + + // Read the physical datarate from the datarates table + phyDr = DataratesUS915[dr]; + + Radio.SetChannel( frequency ); + + // Radio configuration + Radio.SetRxConfig( MODEM_LORA, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous ); + + Radio.SetMaxPayloadLength( MODEM_LORA, MaxPayloadOfDatarateUS915[dr] + LORAMAC_FRAME_PAYLOAD_OVERHEAD_SIZE ); + + *datarate = (uint8_t) dr; + return true; +} + +bool RegionUS915TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ) +{ + int8_t phyDr = DataratesUS915[txConfig->Datarate]; + int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, RegionBands[RegionNvmGroup2->Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, RegionNvmGroup2->ChannelsMask ); + uint32_t bandwidth = RegionCommonGetBandwidth( txConfig->Datarate, BandwidthsUS915 ); + int8_t phyTxPower = 0; + + // Calculate physical TX power + phyTxPower = RegionCommonComputeTxPower( txPowerLimited, US915_DEFAULT_MAX_ERP, 0 ); + + // Setup the radio frequency + Radio.SetChannel( RegionNvmGroup2->Channels[txConfig->Channel].Frequency ); + + Radio.SetTxConfig( MODEM_LORA, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 4000 ); + + // Setup maximum payload lenght of the radio driver + Radio.SetMaxPayloadLength( MODEM_LORA, txConfig->PktLen ); + + // Update time-on-air + *txTimeOnAir = GetTimeOnAir( txConfig->Datarate, txConfig->PktLen ); + + *txPower = txPowerLimited; + return true; +} + +uint8_t RegionUS915LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ) +{ + uint8_t status = 0x07; + RegionCommonLinkAdrParams_t linkAdrParams = { 0 }; + uint8_t nextIndex = 0; + uint8_t bytesProcessed = 0; + uint16_t channelsMask[6] = { 0, 0, 0, 0, 0, 0 }; + GetPhyParams_t getPhy; + PhyParam_t phyParam; + RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams; + + // Initialize local copy of channels mask + RegionCommonChanMaskCopy( channelsMask, RegionNvmGroup2->ChannelsMask, CHANNELS_MASK_SIZE ); + + while( bytesProcessed < linkAdrReq->PayloadSize ) + { + nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); + + if( nextIndex == 0 ) + break; // break loop, since no more request has been found + + // Update bytes processed + bytesProcessed += nextIndex; + + // Revert status, as we only check the last ADR request for the channel mask KO + status = 0x07; + + if( linkAdrParams.ChMaskCtrl == 6 ) + { + // Enable all 125 kHz channels + channelsMask[0] = 0xFFFF; + channelsMask[1] = 0xFFFF; + channelsMask[2] = 0xFFFF; + channelsMask[3] = 0xFFFF; + // Apply chMask to channels 64 to 71 + channelsMask[4] = linkAdrParams.ChMask & CHANNELS_MASK_500KHZ_MASK; + } + else if( linkAdrParams.ChMaskCtrl == 7 ) + { + // Disable all 125 kHz channels + channelsMask[0] = 0x0000; + channelsMask[1] = 0x0000; + channelsMask[2] = 0x0000; + channelsMask[3] = 0x0000; + // Apply chMask to channels 64 to 71 + channelsMask[4] = linkAdrParams.ChMask & CHANNELS_MASK_500KHZ_MASK; + } + else if( linkAdrParams.ChMaskCtrl == 5 ) + { + // Start value for comparision + uint8_t bitMask = 1; + + // cntChannelMask for channelsMask[0] until channelsMask[3] + uint8_t cntChannelMask = 0; + + // i will be 1, 2, 3, ..., 7 + for( uint8_t i = 0; i <= 7; i++ ) + { + // 8 MSBs of ChMask are RFU + // Checking if the ChMask is set, then true + if( ( ( linkAdrParams.ChMask & 0x00FF ) & ( bitMask << i ) ) != 0 ) + { + if( ( i % 2 ) == 0 ) + { + // Enable a bank of 8 125kHz channels, 8 LSBs + channelsMask[cntChannelMask] |= 0x00FF; + // Enable the corresponding 500kHz channel + channelsMask[4] |= ( bitMask << i ); + } + else + { + // Enable a bank of 8 125kHz channels, 8 MSBs + channelsMask[cntChannelMask] |= 0xFF00; + // Enable the corresponding 500kHz channel + channelsMask[4] |= ( bitMask << i ); + // cntChannelMask increment for uneven i + cntChannelMask++; + } + } + // ChMask is not set + else + { + if( ( i % 2 ) == 0 ) + { + // Disable a bank of 8 125kHz channels, 8 LSBs + channelsMask[cntChannelMask] &= 0xFF00; + // Disable the corresponding 500kHz channel + channelsMask[4] &= ~( bitMask << i ); + } + else + { + // Enable a bank of 8 125kHz channels, 8 MSBs + channelsMask[cntChannelMask] &= 0x00FF; + // Disable the corresponding 500kHz channel + channelsMask[4] &= ~( bitMask << i ); + // cntChannelMask increment for uneven i + cntChannelMask++; + } + } + } + } + else + { + channelsMask[linkAdrParams.ChMaskCtrl] = linkAdrParams.ChMask; + } + } + + // FCC 15.247 paragraph F mandates to hop on at least 2 125 kHz channels + if( ( linkAdrParams.Datarate < DR_4 ) && ( RegionCommonCountChannels( channelsMask, 0, 4 ) < 2 ) ) + { + status &= 0xFE; // Channel mask KO + } + + // Get the minimum possible datarate + getPhy.Attribute = PHY_MIN_TX_DR; + getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; + phyParam = RegionUS915GetPhyParam( &getPhy ); + + linkAdrVerifyParams.Status = status; + linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; + linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; + linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; + linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; + linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; + linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; + linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; + linkAdrVerifyParams.NbChannels = US915_MAX_NB_CHANNELS; + linkAdrVerifyParams.ChannelsMask = channelsMask; + linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value; + linkAdrVerifyParams.MaxDatarate = US915_TX_MAX_DATARATE; + linkAdrVerifyParams.Channels = RegionNvmGroup2->Channels; + linkAdrVerifyParams.MinTxPower = US915_MIN_TX_POWER; + linkAdrVerifyParams.MaxTxPower = US915_MAX_TX_POWER; + linkAdrVerifyParams.Version = linkAdrReq->Version; + + // Verify the parameters and update, if necessary + status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep ); + + // Update channelsMask if everything is correct + if( status == 0x07 ) + { + // Copy Mask + RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, channelsMask, 6 ); + + RegionNvmGroup1->ChannelsMaskRemaining[0] &= RegionNvmGroup2->ChannelsMask[0]; + RegionNvmGroup1->ChannelsMaskRemaining[1] &= RegionNvmGroup2->ChannelsMask[1]; + RegionNvmGroup1->ChannelsMaskRemaining[2] &= RegionNvmGroup2->ChannelsMask[2]; + RegionNvmGroup1->ChannelsMaskRemaining[3] &= RegionNvmGroup2->ChannelsMask[3]; + RegionNvmGroup1->ChannelsMaskRemaining[4] = RegionNvmGroup2->ChannelsMask[4]; + RegionNvmGroup1->ChannelsMaskRemaining[5] = RegionNvmGroup2->ChannelsMask[5]; + } + + // Update status variables + *drOut = linkAdrParams.Datarate; + *txPowOut = linkAdrParams.TxPower; + *nbRepOut = linkAdrParams.NbRep; + *nbBytesParsed = bytesProcessed; + + return status; +} + +uint8_t RegionUS915RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ) +{ + uint8_t status = 0x07; + + // Verify radio frequency + if( VerifyRfFreq( rxParamSetupReq->Frequency ) == false ) + { + status &= 0xFE; // Channel frequency KO + } + + // Verify datarate + if( RegionCommonValueInRange( rxParamSetupReq->Datarate, US915_RX_MIN_DATARATE, US915_RX_MAX_DATARATE ) == false ) + { + status &= 0xFD; // Datarate KO + } + if( ( RegionCommonValueInRange( rxParamSetupReq->Datarate, DR_5, DR_7 ) == true ) || + ( rxParamSetupReq->Datarate > DR_13 ) ) + { + status &= 0xFD; // Datarate KO + } + + // Verify datarate offset + if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, US915_MIN_RX1_DR_OFFSET, US915_MAX_RX1_DR_OFFSET ) == false ) + { + status &= 0xFB; // Rx1DrOffset range KO + } + + return status; +} + +int8_t RegionUS915NewChannelReq( NewChannelReqParams_t* newChannelReq ) +{ + // Do not accept the request + return -1; +} + +int8_t RegionUS915TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ) +{ + // Do not accept the request + return -1; +} + +int8_t RegionUS915DlChannelReq( DlChannelReqParams_t* dlChannelReq ) +{ + // Do not accept the request + return -1; +} + +int8_t RegionUS915AlternateDr( int8_t currentDr, AlternateDrType_t type ) +{ + // Alternates the data rate according to the channel sequence: + // Eight times a 125kHz DR_0 and then one 500kHz DR_4 channel + if( type == ALTERNATE_DR ) + { + RegionNvmGroup1->JoinTrialsCounter++; + } + else + { + RegionNvmGroup1->JoinTrialsCounter--; + } + + if( RegionNvmGroup1->JoinTrialsCounter % 9 == 0 ) + { + // Use DR_4 every 9th times. + currentDr = DR_4; + } + else + { + currentDr = DR_0; + } + return currentDr; +} + +LoRaMacStatus_t RegionUS915NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ) +{ + uint8_t nbEnabledChannels = 0; + uint8_t nbRestrictedChannels = 0; + uint8_t enabledChannels[US915_MAX_NB_CHANNELS] = { 0 }; + RegionCommonIdentifyChannelsParam_t identifyChannelsParam; + RegionCommonCountNbOfEnabledChannelsParams_t countChannelsParams; + LoRaMacStatus_t status = LORAMAC_STATUS_NO_CHANNEL_FOUND; + + // Count 125kHz channels + if( RegionCommonCountChannels( RegionNvmGroup1->ChannelsMaskRemaining, 0, 4 ) == 0 ) + { // Reactivate default channels + RegionCommonChanMaskCopy( RegionNvmGroup1->ChannelsMaskRemaining, RegionNvmGroup2->ChannelsMask, 4 ); + + RegionNvmGroup1->JoinChannelGroupsCurrentIndex = 0; + } + // Check other channels + if( nextChanParams->Datarate >= DR_4 ) + { + if( ( RegionNvmGroup1->ChannelsMaskRemaining[4] & CHANNELS_MASK_500KHZ_MASK ) == 0 ) + { + RegionNvmGroup1->ChannelsMaskRemaining[4] = RegionNvmGroup2->ChannelsMask[4]; + } + } + + // Search how many channels are enabled + countChannelsParams.Joined = nextChanParams->Joined; + countChannelsParams.Datarate = nextChanParams->Datarate; + countChannelsParams.ChannelsMask = RegionNvmGroup1->ChannelsMaskRemaining; + countChannelsParams.Channels = RegionNvmGroup2->Channels; + countChannelsParams.Bands = RegionBands; + countChannelsParams.MaxNbChannels = US915_MAX_NB_CHANNELS; + countChannelsParams.JoinChannels = NULL; + + identifyChannelsParam.AggrTimeOff = nextChanParams->AggrTimeOff; + identifyChannelsParam.LastAggrTx = nextChanParams->LastAggrTx; + identifyChannelsParam.DutyCycleEnabled = nextChanParams->DutyCycleEnabled; + identifyChannelsParam.MaxBands = US915_MAX_NB_BANDS; + + identifyChannelsParam.CountNbOfEnabledChannelsParam = &countChannelsParams; + + identifyChannelsParam.ElapsedTimeSinceStartUp = nextChanParams->ElapsedTimeSinceStartUp; + identifyChannelsParam.LastTxIsJoinRequest = nextChanParams->LastTxIsJoinRequest; + identifyChannelsParam.ExpectedTimeOnAir = GetTimeOnAir( nextChanParams->Datarate, nextChanParams->PktLen ); + + status = RegionCommonIdentifyChannels( &identifyChannelsParam, aggregatedTimeOff, enabledChannels, + &nbEnabledChannels, &nbRestrictedChannels, time ); + + if( status == LORAMAC_STATUS_OK ) + { + if( nextChanParams->Joined == true ) + { + // Choose randomly on of the remaining channels + *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )]; + } + else + { + // For rapid network acquisition in mixed gateway channel plan environments, the device + // follow a random channel selection sequence. It probes alternating one out of a + // group of eight 125 kHz channels followed by probing one 500 kHz channel each pass. + // Each time a 125 kHz channel will be selected from another group. + + // 125kHz Channels (0 - 63) DR0 + if( nextChanParams->Datarate == DR_0 ) + { + if( RegionBaseUSComputeNext125kHzJoinChannel( ( uint16_t* ) RegionNvmGroup1->ChannelsMaskRemaining, + &RegionNvmGroup1->JoinChannelGroupsCurrentIndex, channel ) == LORAMAC_STATUS_PARAMETER_INVALID ) + { + return LORAMAC_STATUS_PARAMETER_INVALID; + } + } + // 500kHz Channels (64 - 71) DR4 + else + { + // Choose the next available channel + uint8_t i = 0; + while( ( ( RegionNvmGroup1->ChannelsMaskRemaining[4] & CHANNELS_MASK_500KHZ_MASK ) & ( 1 << i ) ) == 0 ) + { + i++; + } + *channel = 64 + i; + } + } + + // Disable the channel in the mask + RegionCommonChanDisable( RegionNvmGroup1->ChannelsMaskRemaining, *channel, US915_MAX_NB_CHANNELS ); + } + return status; +} + +LoRaMacStatus_t RegionUS915ChannelAdd( ChannelAddParams_t* channelAdd ) +{ + return LORAMAC_STATUS_PARAMETER_INVALID; +} + +bool RegionUS915ChannelsRemove( ChannelRemoveParams_t* channelRemove ) +{ + return LORAMAC_STATUS_PARAMETER_INVALID; +} + +uint8_t RegionUS915ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) +{ + int8_t datarate = DatarateOffsetsUS915[dr][drOffset]; + + if( datarate < 0 ) + { + datarate = DR_0; + } + return datarate; +} + +void RegionUS915RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ) +{ + RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup; + + regionCommonRxBeaconSetup.Datarates = DataratesUS915; + regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency; + regionCommonRxBeaconSetup.BeaconSize = US915_BEACON_SIZE; + regionCommonRxBeaconSetup.BeaconDatarate = US915_BEACON_CHANNEL_DR; + regionCommonRxBeaconSetup.BeaconChannelBW = US915_BEACON_CHANNEL_BW; + regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime; + regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout; + + RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup ); + + // Store downlink datarate + *outDr = US915_BEACON_CHANNEL_DR; +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionUS915.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionUS915.h new file mode 100644 index 0000000000..33d2e97586 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/region/RegionUS915.h @@ -0,0 +1,444 @@ +/*! + * \file RegionUS915.h + * + * \brief Region definition for US915 + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * \defgroup REGIONUS915 Region US915 + * Implementation according to LoRaWAN Specification v1.0.2. + * \{ + */ +#ifndef __REGION_US915_H__ +#define __REGION_US915_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "region/Region.h" + +/*! + * LoRaMac maximum number of channels + */ +#define US915_MAX_NB_CHANNELS 72 + +/*! + * Minimal datarate that can be used by the node + */ +#define US915_TX_MIN_DATARATE DR_0 + +/*! + * Maximal datarate that can be used by the node + */ +#define US915_TX_MAX_DATARATE DR_4 + +/*! + * Minimal datarate that can be used by the node + */ +#define US915_RX_MIN_DATARATE DR_8 + +/*! + * Maximal datarate that can be used by the node + */ +#define US915_RX_MAX_DATARATE DR_13 + +/*! + * Default datarate used by the node + */ +#define US915_DEFAULT_DATARATE DR_0 + +/*! + * Minimal Rx1 receive datarate offset + */ +#define US915_MIN_RX1_DR_OFFSET 0 + +/*! + * Maximal Rx1 receive datarate offset + */ +#define US915_MAX_RX1_DR_OFFSET 3 + +/*! + * Minimal Tx output power that can be used by the node + */ +#define US915_MIN_TX_POWER TX_POWER_14 + +/*! + * Maximal Tx output power that can be used by the node + */ +#define US915_MAX_TX_POWER TX_POWER_0 + +/*! + * Default Tx output power used by the node + */ +#define US915_DEFAULT_TX_POWER TX_POWER_0 + +/*! + * Default Max ERP + */ +#define US915_DEFAULT_MAX_ERP 30.0f + +/*! + * Enabled or disabled the duty cycle + */ +#define US915_DUTY_CYCLE_ENABLED 0 + +/*! + * Maximum RX window duration + */ +#define US915_MAX_RX_WINDOW 3000 + +/*! + * Second reception window channel frequency definition. + */ +#define US915_RX_WND_2_FREQ 923300000 + +/*! + * Second reception window channel datarate definition. + */ +#define US915_RX_WND_2_DR DR_8 + +/*! + * Default uplink dwell time configuration + */ +#define US915_DEFAULT_UPLINK_DWELL_TIME 0 + +/* + * CLASS B + */ +/*! + * Beacon frequency + */ +#define US915_BEACON_CHANNEL_FREQ 923300000 + +/*! + * Beacon frequency channel stepwidth + */ +#define US915_BEACON_CHANNEL_STEPWIDTH 600000 + +/*! + * Ping slot channel frequency + */ +#define US915_PING_SLOT_CHANNEL_FREQ 923300000 + +/*! + * Number of possible beacon channels + */ +#define US915_BEACON_NB_CHANNELS 8 + +/*! + * Payload size of a beacon frame + */ +#define US915_BEACON_SIZE 23 + +/*! + * Size of RFU 1 field + */ +#define US915_RFU1_SIZE 4 + +/*! + * Size of RFU 2 field + */ +#define US915_RFU2_SIZE 3 + +/*! + * Datarate of the beacon channel + */ +#define US915_BEACON_CHANNEL_DR DR_8 + +/*! + * Bandwith of the beacon channel + */ +#define US915_BEACON_CHANNEL_BW 2 + +/*! + * Ping slot channel datarate + */ +#define US915_PING_SLOT_CHANNEL_DR DR_8 + +/*! + * LoRaMac maximum number of bands + */ +#define US915_MAX_NB_BANDS 1 + +/*! + * Band 0 definition + * Band = { DutyCycle, TxMaxPower, LastBandUpdateTime, LastMaxCreditAssignTime, TimeCredits, MaxTimeCredits, ReadyForTransmission } + */ +#define US915_BAND0 { 1, US915_MAX_TX_POWER, 0, 0, 0, 0, 0 } // 100.0 % + +/*! + * Defines the first channel for RX window 1 for US band + */ +#define US915_FIRST_RX1_CHANNEL ( (uint32_t) 923300000 ) + +/*! + * Defines the last channel for RX window 1 for US band + */ +#define US915_LAST_RX1_CHANNEL ( (uint32_t) 927500000 ) + +/*! + * Defines the step width of the channels for RX window 1 + */ +#define US915_STEPWIDTH_RX1_CHANNEL ( (uint32_t) 600000 ) + +/*! + * Data rates table definition + */ +static const uint8_t DataratesUS915[] = { 10, 9, 8, 7, 8, 0, 0, 0, 12, 11, 10, 9, 8, 7, 0, 0 }; + +/*! + * Bandwidths table definition in Hz + */ +static const uint32_t BandwidthsUS915[] = { 125000, 125000, 125000, 125000, 500000, 0, 0, 0, 500000, 500000, 500000, 500000, 500000, 500000, 0, 0 }; + +/*! + * Up/Down link data rates offset definition + */ +static const int8_t DatarateOffsetsUS915[5][4] = +{ + { DR_10, DR_9 , DR_8 , DR_8 }, // DR_0 + { DR_11, DR_10, DR_9 , DR_8 }, // DR_1 + { DR_12, DR_11, DR_10, DR_9 }, // DR_2 + { DR_13, DR_12, DR_11, DR_10 }, // DR_3 + { DR_13, DR_13, DR_12, DR_11 }, // DR_4 +}; + +/*! + * Maximum payload with respect to the datarate index. + */ +static const uint8_t MaxPayloadOfDatarateUS915[] = { 11, 53, 125, 242, 242, 0, 0, 0, 53, 129, 242, 242, 242, 242, 0, 0 }; + +/*! + * \brief The function gets a value of a specific phy attribute. + * + * \param [IN] getPhy Pointer to the function parameters. + * + * \retval Returns a structure containing the PHY parameter. + */ +PhyParam_t RegionUS915GetPhyParam( GetPhyParams_t* getPhy ); + +/*! + * \brief Updates the last TX done parameters of the current channel. + * + * \param [IN] txDone Pointer to the function parameters. + */ +void RegionUS915SetBandTxDone( SetBandTxDoneParams_t* txDone ); + +/*! + * \brief Initializes the channels masks and the channels. + * + * \param [IN] type Sets the initialization type. + */ +void RegionUS915InitDefaults( InitDefaultsParams_t* params ); + +/*! + * \brief Verifies a parameter. + * + * \param [IN] verify Pointer to the function parameters. + * + * \param [IN] type Sets the initialization type. + * + * \retval Returns true, if the parameter is valid. + */ +bool RegionUS915Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + +/*! + * \brief The function parses the input buffer and sets up the channels of the + * CF list. + * + * \param [IN] applyCFList Pointer to the function parameters. + */ +void RegionUS915ApplyCFList( ApplyCFListParams_t* applyCFList ); + +/*! + * \brief Sets a channels mask. + * + * \param [IN] chanMaskSet Pointer to the function parameters. + * + * \retval Returns true, if the channels mask could be set. + */ +bool RegionUS915ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ); + +/*! + * Computes the Rx window timeout and offset. + * + * \param [IN] datarate Rx window datarate index to be used + * + * \param [IN] minRxSymbols Minimum required number of symbols to detect an Rx frame. + * + * \param [IN] rxError System maximum timing error of the receiver. In milliseconds + * The receiver will turn on in a [-rxError : +rxError] ms + * interval around RxOffset + * + * \param [OUT]rxConfigParams Returns updated WindowTimeout and WindowOffset fields. + */ +void RegionUS915ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams ); + +/*! + * \brief Configuration of the RX windows. + * + * \param [IN] rxConfig Pointer to the function parameters. + * + * \param [OUT] datarate The datarate index which was set. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionUS915RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate ); + +/*! + * \brief TX configuration. + * + * \param [IN] txConfig Pointer to the function parameters. + * + * \param [OUT] txPower The tx power index which was set. + * + * \param [OUT] txTimeOnAir The time-on-air of the frame. + * + * \retval Returns true, if the configuration was applied successfully. + */ +bool RegionUS915TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir ); + +/*! + * \brief The function processes a Link ADR Request. + * + * \param [IN] linkAdrReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionUS915LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed ); + +/*! + * \brief The function processes a RX Parameter Setup Request. + * + * \param [IN] rxParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +uint8_t RegionUS915RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq ); + +/*! + * \brief The function processes a Channel Request. + * + * \param [IN] newChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +int8_t RegionUS915NewChannelReq( NewChannelReqParams_t* newChannelReq ); + +/*! + * \brief The function processes a TX ParamSetup Request. + * + * \param [IN] txParamSetupReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + * Returns -1, if the functionality is not implemented. In this case, the end node + * shall not process the command. + */ +int8_t RegionUS915TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq ); + +/*! + * \brief The function processes a DlChannel Request. + * + * \param [IN] dlChannelReq Pointer to the function parameters. + * + * \retval Returns the status of the operation, according to the LoRaMAC specification. + */ +int8_t RegionUS915DlChannelReq( DlChannelReqParams_t* dlChannelReq ); + +/*! + * \brief Alternates the datarate of the channel for the join request. + * + * \param [IN] currentDr Current datarate. + * + * \param [IN] type Alternation type. + * + * \retval Datarate to apply. + */ +int8_t RegionUS915AlternateDr( int8_t currentDr, AlternateDrType_t type ); + +/*! + * \brief Searches and set the next random available channel + * + * \param [OUT] channel Next channel to use for TX. + * + * \param [OUT] time Time to wait for the next transmission according to the duty + * cycle. + * + * \param [OUT] aggregatedTimeOff Updates the aggregated time off. + * + * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate] + */ +LoRaMacStatus_t RegionUS915NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff ); + +/*! + * \brief Adds a channel. + * + * \param [IN] channelAdd Pointer to the function parameters. + * + * \retval Status of the operation. + */ +LoRaMacStatus_t RegionUS915ChannelAdd( ChannelAddParams_t* channelAdd ); + +/*! + * \brief Removes a channel. + * + * \param [IN] channelRemove Pointer to the function parameters. + * + * \retval Returns true, if the channel was removed successfully. + */ +bool RegionUS915ChannelsRemove( ChannelRemoveParams_t* channelRemove ); + +/*! + * \brief Computes new datarate according to the given offset + * + * \param [IN] downlinkDwellTime Downlink dwell time configuration. 0: No limit, 1: 400ms + * + * \param [IN] dr Current datarate + * + * \param [IN] drOffset Offset to be applied + * + * \retval newDr Computed datarate. + */ +uint8_t RegionUS915ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + +/*! + * \brief Sets the radio into beacon reception mode + * + * \param [IN] rxBeaconSetup Pointer to the function parameters + */ +void RegionUS915RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr ); + +/*! \} defgroup REGIONUS915 */ + +#ifdef __cplusplus +} +#endif + +#endif // __REGION_US915_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/secure-element-nvm.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/secure-element-nvm.h new file mode 100644 index 0000000000..81ec66887a --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/secure-element-nvm.h @@ -0,0 +1,117 @@ +/*! + * \file secure-element-nvm.h + * + * \brief Secure Element non-volatile data. + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \addtogroup SECUREELEMENT + * + * \{ + * + */ +#ifndef __SECURE_ELEMENT_NVM_H__ +#define __SECURE_ELEMENT_NVM_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include "LoRaMacTypes.h" + +/*! + * Secure-element keys size in bytes + */ +#define SE_KEY_SIZE 16 + +/*! + * Secure-element EUI size in bytes + */ +#define SE_EUI_SIZE 8 + +/*! + * Secure-element pin size in bytes + */ +#define SE_PIN_SIZE 4 + +#ifdef SOFT_SE +/*! + * Number of supported crypto keys for the soft-se + */ +#define NUM_OF_KEYS 23 + +/*! + * Key structure definition for the soft-se + */ +typedef struct sKey +{ + /*! + * Key identifier + */ + KeyIdentifier_t KeyID; + /*! + * Key value + */ + uint8_t KeyValue[SE_KEY_SIZE]; +} Key_t; + +#endif + +typedef struct sSecureElementNvCtx +{ + /*! + * DevEUI storage + */ + uint8_t DevEui[SE_EUI_SIZE]; + /*! + * Join EUI storage + */ + uint8_t JoinEui[SE_EUI_SIZE]; + /*! + * Pin storage + */ + uint8_t Pin[SE_PIN_SIZE]; +#ifdef SOFT_SE + /*! + * The key list is required for the soft-se only. All other secure-elements + * handle the storage on their own. + */ + Key_t KeyList[NUM_OF_KEYS]; +#endif + /*! + * CRC32 value of the SecureElement data structure. + */ + uint32_t Crc32; +} SecureElementNvmData_t; + + +/*! \} addtogroup SECUREELEMENT */ + +#ifdef __cplusplus +} +#endif + +#endif // __SECURE_ELEMENT_NVM_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/secure-element.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/secure-element.h new file mode 100644 index 0000000000..c4446e9881 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/secure-element.h @@ -0,0 +1,218 @@ +/*! + * \file secure-element.h + * + * \brief Secure Element driver API + * + * \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 Jaeckle ( STACKFORCE ) + * + * \author Johannes Bruder ( STACKFORCE ) + * + * \defgroup SECUREELEMENT Secure Element API Definition + * + * \{ + * + */ +#ifndef __SECURE_ELEMENT_H__ +#define __SECURE_ELEMENT_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include "LoRaMacCrypto.h" +#include "secure-element-nvm.h" + +/*! + * Return values. + */ +typedef enum eSecureElementStatus +{ + /*! + * No error occurred + */ + SECURE_ELEMENT_SUCCESS = 0, + /*! + * CMAC does not match + */ + SECURE_ELEMENT_FAIL_CMAC, + /*! + * Null pointer exception + */ + SECURE_ELEMENT_ERROR_NPE, + /*! + * Invalid key identifier exception + */ + SECURE_ELEMENT_ERROR_INVALID_KEY_ID, + /*! + * Invalid LoRaWAN specification version + */ + SECURE_ELEMENT_ERROR_INVALID_LORAWAM_SPEC_VERSION, + /*! + * Incompatible buffer size + */ + SECURE_ELEMENT_ERROR_BUF_SIZE, + /*! + * Undefined Error occurred + */ + SECURE_ELEMENT_ERROR, + /*! + * Failed to encrypt + */ + SECURE_ELEMENT_FAIL_ENCRYPT, +}SecureElementStatus_t; + +/*! + * Initialization of Secure Element driver + * + * \param[IN] nvm - Pointer to the non-volatile memory data + * structure. + * \retval - Status of the operation + */ +SecureElementStatus_t SecureElementInit( SecureElementNvmData_t* nvm ); + +/*! + * Sets a key + * + * \param[IN] keyID - Key identifier + * \param[IN] key - Key value + * \retval - Status of the operation + */ +SecureElementStatus_t SecureElementSetKey( KeyIdentifier_t keyID, uint8_t* key ); + +/*! + * Computes a CMAC of a message using provided initial Bx block + * + * \param[IN] micBxBuffer - Buffer containing the initial Bx block + * \param[IN] buffer - Data buffer + * \param[IN] size - Data buffer size + * \param[IN] keyID - Key identifier to determine the AES key to be used + * \param[OUT] cmac - Computed cmac + * \retval - Status of the operation + */ +SecureElementStatus_t SecureElementComputeAesCmac( uint8_t* micBxBuffer, uint8_t* buffer, uint16_t size, KeyIdentifier_t keyID, uint32_t* cmac ); + +/*! + * Verifies a CMAC (computes and compare with expected cmac) + * + * \param[IN] buffer - Data buffer + * \param[IN] size - Data buffer size + * \param[in] expectedCmac - Expected cmac + * \param[IN] keyID - Key identifier to determine the AES key to be used + * \retval - Status of the operation + */ +SecureElementStatus_t SecureElementVerifyAesCmac( uint8_t* buffer, uint16_t size, uint32_t expectedCmac, KeyIdentifier_t keyID ); + +/*! + * Encrypt a buffer + * + * \param[IN] buffer - Data buffer + * \param[IN] size - Data buffer size + * \param[IN] keyID - Key identifier to determine the AES key to be used + * \param[OUT] encBuffer - Encrypted buffer + * \retval - Status of the operation + */ +SecureElementStatus_t SecureElementAesEncrypt( uint8_t* buffer, uint16_t size, KeyIdentifier_t keyID, uint8_t* encBuffer ); + +/*! + * Derives and store a key + * + * \param[IN] input - Input data from which the key is derived ( 16 byte ) + * \param[IN] rootKeyID - Key identifier of the root key to use to perform the derivation + * \param[IN] targetKeyID - Key identifier of the key which will be derived + * \retval - Status of the operation + */ +SecureElementStatus_t SecureElementDeriveAndStoreKey( uint8_t* input, KeyIdentifier_t rootKeyID, KeyIdentifier_t targetKeyID ); + +/*! + * Process JoinAccept message. + * + * \param[IN] encJoinAccept - Received encrypted JoinAccept message + * \param[IN] encJoinAcceptSize - Received encrypted JoinAccept message Size + * \param[OUT] decJoinAccept - Decrypted and validated JoinAccept message + * \param[OUT] versionMinor - Detected LoRaWAN specification version minor field. + * - 0 -> LoRaWAN 1.0.x + * - 1 -> LoRaWAN 1.1.x + * \retval - Status of the operation + */ +SecureElementStatus_t SecureElementProcessJoinAccept( JoinReqIdentifier_t joinReqType, uint8_t* joinEui, + uint16_t devNonce, uint8_t* encJoinAccept, + uint8_t encJoinAcceptSize, uint8_t* decJoinAccept, + uint8_t* versionMinor ); + +/*! + * Sets the DevEUI + * + * \param[IN] devEui - Pointer to the 8-byte devEUI + * \retval - Status of the operation + */ +SecureElementStatus_t SecureElementSetDevEui( uint8_t* devEui ); + +/*! + * Gets the DevEUI + * + * \retval - Pointer to the 8-byte devEUI + */ +uint8_t* SecureElementGetDevEui( void ); + +/*! + * Sets the JoinEUI + * + * \param[IN] joinEui - Pointer to the 8-byte joinEui + * \retval - Status of the operation + */ +SecureElementStatus_t SecureElementSetJoinEui( uint8_t* joinEui ); + +/*! + * Gets the DevEUI + * + * \retval - Pointer to the 8-byte joinEui + */ +uint8_t* SecureElementGetJoinEui( void ); + +/*! + * Sets the pin + * + * \param[IN] pin - Pointer to the 4-byte pin + * \retval - Status of the operation + */ +SecureElementStatus_t SecureElementSetPin( uint8_t* pin ); + +/*! + * Gets the Pin + * + * \retval - Pointer to the 4-byte pin + */ +uint8_t* SecureElementGetPin( void ); + +/*! \} defgroup SECUREELEMENT */ + +#ifdef __cplusplus +} +#endif + +#endif // __SECURE_ELEMENT_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/soft-se/aes.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/soft-se/aes.c new file mode 100644 index 0000000000..eb0551f9b5 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/soft-se/aes.c @@ -0,0 +1,936 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue 09/09/2006 + + This is an AES implementation that uses only 8-bit byte operations on the + cipher state (there are options to use 32-bit types if available). + + The combination of mix columns and byte substitution used here is based on + that developed by Karl Malbrain. His contribution is acknowledged. + */ + +/* define if you have a fast memcpy function on your system */ +#if 0 +# define HAVE_MEMCPY +# include +# if defined( _MSC_VER ) +# include +# pragma intrinsic( memcpy ) +# endif +#endif + + +#include +#include + +/* define if you have fast 32-bit types on your system */ +#if ( __CORTEX_M != 0 ) // if Cortex is different from M0/M0+ +# define HAVE_UINT_32T +#endif + +/* define if you don't want any tables */ +#if 1 +# define USE_TABLES +#endif + +/* On Intel Core 2 duo VERSION_1 is faster */ + +/* alternative versions (test for performance on your system) */ +#if 1 +# define VERSION_1 +#endif + +#include "aes.h" + +//#if defined( HAVE_UINT_32T ) +// typedef unsigned long uint32_t; +//#endif + +/* functions for finite field multiplication in the AES Galois field */ + +#define WPOLY 0x011b +#define BPOLY 0x1b +#define DPOLY 0x008d + +#define f1(x) (x) +#define f2(x) ((x << 1) ^ (((x >> 7) & 1) * WPOLY)) +#define f4(x) ((x << 2) ^ (((x >> 6) & 1) * WPOLY) ^ (((x >> 6) & 2) * WPOLY)) +#define f8(x) ((x << 3) ^ (((x >> 5) & 1) * WPOLY) ^ (((x >> 5) & 2) * WPOLY) \ + ^ (((x >> 5) & 4) * WPOLY)) +#define d2(x) (((x) >> 1) ^ ((x) & 1 ? DPOLY : 0)) + +#define f3(x) (f2(x) ^ x) +#define f9(x) (f8(x) ^ x) +#define fb(x) (f8(x) ^ f2(x) ^ x) +#define fd(x) (f8(x) ^ f4(x) ^ x) +#define fe(x) (f8(x) ^ f4(x) ^ f2(x)) + +#if defined( USE_TABLES ) + +#define sb_data(w) { /* S Box data values */ \ + w(0x63), w(0x7c), w(0x77), w(0x7b), w(0xf2), w(0x6b), w(0x6f), w(0xc5),\ + w(0x30), w(0x01), w(0x67), w(0x2b), w(0xfe), w(0xd7), w(0xab), w(0x76),\ + w(0xca), w(0x82), w(0xc9), w(0x7d), w(0xfa), w(0x59), w(0x47), w(0xf0),\ + w(0xad), w(0xd4), w(0xa2), w(0xaf), w(0x9c), w(0xa4), w(0x72), w(0xc0),\ + w(0xb7), w(0xfd), w(0x93), w(0x26), w(0x36), w(0x3f), w(0xf7), w(0xcc),\ + w(0x34), w(0xa5), w(0xe5), w(0xf1), w(0x71), w(0xd8), w(0x31), w(0x15),\ + w(0x04), w(0xc7), w(0x23), w(0xc3), w(0x18), w(0x96), w(0x05), w(0x9a),\ + w(0x07), w(0x12), w(0x80), w(0xe2), w(0xeb), w(0x27), w(0xb2), w(0x75),\ + w(0x09), w(0x83), w(0x2c), w(0x1a), w(0x1b), w(0x6e), w(0x5a), w(0xa0),\ + w(0x52), w(0x3b), w(0xd6), w(0xb3), w(0x29), w(0xe3), w(0x2f), w(0x84),\ + w(0x53), w(0xd1), w(0x00), w(0xed), w(0x20), w(0xfc), w(0xb1), w(0x5b),\ + w(0x6a), w(0xcb), w(0xbe), w(0x39), w(0x4a), w(0x4c), w(0x58), w(0xcf),\ + w(0xd0), w(0xef), w(0xaa), w(0xfb), w(0x43), w(0x4d), w(0x33), w(0x85),\ + w(0x45), w(0xf9), w(0x02), w(0x7f), w(0x50), w(0x3c), w(0x9f), w(0xa8),\ + w(0x51), w(0xa3), w(0x40), w(0x8f), w(0x92), w(0x9d), w(0x38), w(0xf5),\ + w(0xbc), w(0xb6), w(0xda), w(0x21), w(0x10), w(0xff), w(0xf3), w(0xd2),\ + w(0xcd), w(0x0c), w(0x13), w(0xec), w(0x5f), w(0x97), w(0x44), w(0x17),\ + w(0xc4), w(0xa7), w(0x7e), w(0x3d), w(0x64), w(0x5d), w(0x19), w(0x73),\ + w(0x60), w(0x81), w(0x4f), w(0xdc), w(0x22), w(0x2a), w(0x90), w(0x88),\ + w(0x46), w(0xee), w(0xb8), w(0x14), w(0xde), w(0x5e), w(0x0b), w(0xdb),\ + w(0xe0), w(0x32), w(0x3a), w(0x0a), w(0x49), w(0x06), w(0x24), w(0x5c),\ + w(0xc2), w(0xd3), w(0xac), w(0x62), w(0x91), w(0x95), w(0xe4), w(0x79),\ + w(0xe7), w(0xc8), w(0x37), w(0x6d), w(0x8d), w(0xd5), w(0x4e), w(0xa9),\ + w(0x6c), w(0x56), w(0xf4), w(0xea), w(0x65), w(0x7a), w(0xae), w(0x08),\ + w(0xba), w(0x78), w(0x25), w(0x2e), w(0x1c), w(0xa6), w(0xb4), w(0xc6),\ + w(0xe8), w(0xdd), w(0x74), w(0x1f), w(0x4b), w(0xbd), w(0x8b), w(0x8a),\ + w(0x70), w(0x3e), w(0xb5), w(0x66), w(0x48), w(0x03), w(0xf6), w(0x0e),\ + w(0x61), w(0x35), w(0x57), w(0xb9), w(0x86), w(0xc1), w(0x1d), w(0x9e),\ + w(0xe1), w(0xf8), w(0x98), w(0x11), w(0x69), w(0xd9), w(0x8e), w(0x94),\ + w(0x9b), w(0x1e), w(0x87), w(0xe9), w(0xce), w(0x55), w(0x28), w(0xdf),\ + w(0x8c), w(0xa1), w(0x89), w(0x0d), w(0xbf), w(0xe6), w(0x42), w(0x68),\ + w(0x41), w(0x99), w(0x2d), w(0x0f), w(0xb0), w(0x54), w(0xbb), w(0x16) } + +#define isb_data(w) { /* inverse S Box data values */ \ + w(0x52), w(0x09), w(0x6a), w(0xd5), w(0x30), w(0x36), w(0xa5), w(0x38),\ + w(0xbf), w(0x40), w(0xa3), w(0x9e), w(0x81), w(0xf3), w(0xd7), w(0xfb),\ + w(0x7c), w(0xe3), w(0x39), w(0x82), w(0x9b), w(0x2f), w(0xff), w(0x87),\ + w(0x34), w(0x8e), w(0x43), w(0x44), w(0xc4), w(0xde), w(0xe9), w(0xcb),\ + w(0x54), w(0x7b), w(0x94), w(0x32), w(0xa6), w(0xc2), w(0x23), w(0x3d),\ + w(0xee), w(0x4c), w(0x95), w(0x0b), w(0x42), w(0xfa), w(0xc3), w(0x4e),\ + w(0x08), w(0x2e), w(0xa1), w(0x66), w(0x28), w(0xd9), w(0x24), w(0xb2),\ + w(0x76), w(0x5b), w(0xa2), w(0x49), w(0x6d), w(0x8b), w(0xd1), w(0x25),\ + w(0x72), w(0xf8), w(0xf6), w(0x64), w(0x86), w(0x68), w(0x98), w(0x16),\ + w(0xd4), w(0xa4), w(0x5c), w(0xcc), w(0x5d), w(0x65), w(0xb6), w(0x92),\ + w(0x6c), w(0x70), w(0x48), w(0x50), w(0xfd), w(0xed), w(0xb9), w(0xda),\ + w(0x5e), w(0x15), w(0x46), w(0x57), w(0xa7), w(0x8d), w(0x9d), w(0x84),\ + w(0x90), w(0xd8), w(0xab), w(0x00), w(0x8c), w(0xbc), w(0xd3), w(0x0a),\ + w(0xf7), w(0xe4), w(0x58), w(0x05), w(0xb8), w(0xb3), w(0x45), w(0x06),\ + w(0xd0), w(0x2c), w(0x1e), w(0x8f), w(0xca), w(0x3f), w(0x0f), w(0x02),\ + w(0xc1), w(0xaf), w(0xbd), w(0x03), w(0x01), w(0x13), w(0x8a), w(0x6b),\ + w(0x3a), w(0x91), w(0x11), w(0x41), w(0x4f), w(0x67), w(0xdc), w(0xea),\ + w(0x97), w(0xf2), w(0xcf), w(0xce), w(0xf0), w(0xb4), w(0xe6), w(0x73),\ + w(0x96), w(0xac), w(0x74), w(0x22), w(0xe7), w(0xad), w(0x35), w(0x85),\ + w(0xe2), w(0xf9), w(0x37), w(0xe8), w(0x1c), w(0x75), w(0xdf), w(0x6e),\ + w(0x47), w(0xf1), w(0x1a), w(0x71), w(0x1d), w(0x29), w(0xc5), w(0x89),\ + w(0x6f), w(0xb7), w(0x62), w(0x0e), w(0xaa), w(0x18), w(0xbe), w(0x1b),\ + w(0xfc), w(0x56), w(0x3e), w(0x4b), w(0xc6), w(0xd2), w(0x79), w(0x20),\ + w(0x9a), w(0xdb), w(0xc0), w(0xfe), w(0x78), w(0xcd), w(0x5a), w(0xf4),\ + w(0x1f), w(0xdd), w(0xa8), w(0x33), w(0x88), w(0x07), w(0xc7), w(0x31),\ + w(0xb1), w(0x12), w(0x10), w(0x59), w(0x27), w(0x80), w(0xec), w(0x5f),\ + w(0x60), w(0x51), w(0x7f), w(0xa9), w(0x19), w(0xb5), w(0x4a), w(0x0d),\ + w(0x2d), w(0xe5), w(0x7a), w(0x9f), w(0x93), w(0xc9), w(0x9c), w(0xef),\ + w(0xa0), w(0xe0), w(0x3b), w(0x4d), w(0xae), w(0x2a), w(0xf5), w(0xb0),\ + w(0xc8), w(0xeb), w(0xbb), w(0x3c), w(0x83), w(0x53), w(0x99), w(0x61),\ + w(0x17), w(0x2b), w(0x04), w(0x7e), w(0xba), w(0x77), w(0xd6), w(0x26),\ + w(0xe1), w(0x69), w(0x14), w(0x63), w(0x55), w(0x21), w(0x0c), w(0x7d) } + +#define mm_data(w) { /* basic data for forming finite field tables */ \ + w(0x00), w(0x01), w(0x02), w(0x03), w(0x04), w(0x05), w(0x06), w(0x07),\ + w(0x08), w(0x09), w(0x0a), w(0x0b), w(0x0c), w(0x0d), w(0x0e), w(0x0f),\ + w(0x10), w(0x11), w(0x12), w(0x13), w(0x14), w(0x15), w(0x16), w(0x17),\ + w(0x18), w(0x19), w(0x1a), w(0x1b), w(0x1c), w(0x1d), w(0x1e), w(0x1f),\ + w(0x20), w(0x21), w(0x22), w(0x23), w(0x24), w(0x25), w(0x26), w(0x27),\ + w(0x28), w(0x29), w(0x2a), w(0x2b), w(0x2c), w(0x2d), w(0x2e), w(0x2f),\ + w(0x30), w(0x31), w(0x32), w(0x33), w(0x34), w(0x35), w(0x36), w(0x37),\ + w(0x38), w(0x39), w(0x3a), w(0x3b), w(0x3c), w(0x3d), w(0x3e), w(0x3f),\ + w(0x40), w(0x41), w(0x42), w(0x43), w(0x44), w(0x45), w(0x46), w(0x47),\ + w(0x48), w(0x49), w(0x4a), w(0x4b), w(0x4c), w(0x4d), w(0x4e), w(0x4f),\ + w(0x50), w(0x51), w(0x52), w(0x53), w(0x54), w(0x55), w(0x56), w(0x57),\ + w(0x58), w(0x59), w(0x5a), w(0x5b), w(0x5c), w(0x5d), w(0x5e), w(0x5f),\ + w(0x60), w(0x61), w(0x62), w(0x63), w(0x64), w(0x65), w(0x66), w(0x67),\ + w(0x68), w(0x69), w(0x6a), w(0x6b), w(0x6c), w(0x6d), w(0x6e), w(0x6f),\ + w(0x70), w(0x71), w(0x72), w(0x73), w(0x74), w(0x75), w(0x76), w(0x77),\ + w(0x78), w(0x79), w(0x7a), w(0x7b), w(0x7c), w(0x7d), w(0x7e), w(0x7f),\ + w(0x80), w(0x81), w(0x82), w(0x83), w(0x84), w(0x85), w(0x86), w(0x87),\ + w(0x88), w(0x89), w(0x8a), w(0x8b), w(0x8c), w(0x8d), w(0x8e), w(0x8f),\ + w(0x90), w(0x91), w(0x92), w(0x93), w(0x94), w(0x95), w(0x96), w(0x97),\ + w(0x98), w(0x99), w(0x9a), w(0x9b), w(0x9c), w(0x9d), w(0x9e), w(0x9f),\ + w(0xa0), w(0xa1), w(0xa2), w(0xa3), w(0xa4), w(0xa5), w(0xa6), w(0xa7),\ + w(0xa8), w(0xa9), w(0xaa), w(0xab), w(0xac), w(0xad), w(0xae), w(0xaf),\ + w(0xb0), w(0xb1), w(0xb2), w(0xb3), w(0xb4), w(0xb5), w(0xb6), w(0xb7),\ + w(0xb8), w(0xb9), w(0xba), w(0xbb), w(0xbc), w(0xbd), w(0xbe), w(0xbf),\ + w(0xc0), w(0xc1), w(0xc2), w(0xc3), w(0xc4), w(0xc5), w(0xc6), w(0xc7),\ + w(0xc8), w(0xc9), w(0xca), w(0xcb), w(0xcc), w(0xcd), w(0xce), w(0xcf),\ + w(0xd0), w(0xd1), w(0xd2), w(0xd3), w(0xd4), w(0xd5), w(0xd6), w(0xd7),\ + w(0xd8), w(0xd9), w(0xda), w(0xdb), w(0xdc), w(0xdd), w(0xde), w(0xdf),\ + w(0xe0), w(0xe1), w(0xe2), w(0xe3), w(0xe4), w(0xe5), w(0xe6), w(0xe7),\ + w(0xe8), w(0xe9), w(0xea), w(0xeb), w(0xec), w(0xed), w(0xee), w(0xef),\ + w(0xf0), w(0xf1), w(0xf2), w(0xf3), w(0xf4), w(0xf5), w(0xf6), w(0xf7),\ + w(0xf8), w(0xf9), w(0xfa), w(0xfb), w(0xfc), w(0xfd), w(0xfe), w(0xff) } + +static const uint8_t sbox[256] = sb_data(f1); + +#if defined( AES_DEC_PREKEYED ) +static const uint8_t isbox[256] = isb_data(f1); +#endif + +static const uint8_t gfm2_sbox[256] = sb_data(f2); +static const uint8_t gfm3_sbox[256] = sb_data(f3); + +#if defined( AES_DEC_PREKEYED ) +static const uint8_t gfmul_9[256] = mm_data(f9); +static const uint8_t gfmul_b[256] = mm_data(fb); +static const uint8_t gfmul_d[256] = mm_data(fd); +static const uint8_t gfmul_e[256] = mm_data(fe); +#endif + +#define s_box(x) sbox[(x)] +#if defined( AES_DEC_PREKEYED ) +#define is_box(x) isbox[(x)] +#endif +#define gfm2_sb(x) gfm2_sbox[(x)] +#define gfm3_sb(x) gfm3_sbox[(x)] +#if defined( AES_DEC_PREKEYED ) +#define gfm_9(x) gfmul_9[(x)] +#define gfm_b(x) gfmul_b[(x)] +#define gfm_d(x) gfmul_d[(x)] +#define gfm_e(x) gfmul_e[(x)] +#endif +#else + +/* this is the high bit of x right shifted by 1 */ +/* position. Since the starting polynomial has */ +/* 9 bits (0x11b), this right shift keeps the */ +/* values of all top bits within a byte */ + +static uint8_t hibit(const uint8_t x) +{ uint8_t r = (uint8_t)((x >> 1) | (x >> 2)); + + r |= (r >> 2); + r |= (r >> 4); + return (r + 1) >> 1; +} + +/* return the inverse of the finite field element x */ + +static uint8_t gf_inv(const uint8_t x) +{ uint8_t p1 = x, p2 = BPOLY, n1 = hibit(x), n2 = 0x80, v1 = 1, v2 = 0; + + if(x < 2) + return x; + + for( ; ; ) + { + if(n1) + while(n2 >= n1) /* divide polynomial p2 by p1 */ + { + n2 /= n1; /* shift smaller polynomial left */ + p2 ^= (p1 * n2) & 0xff; /* and remove from larger one */ + v2 ^= (v1 * n2); /* shift accumulated value and */ + n2 = hibit(p2); /* add into result */ + } + else + return v1; + + if(n2) /* repeat with values swapped */ + while(n1 >= n2) + { + n1 /= n2; + p1 ^= p2 * n1; + v1 ^= v2 * n1; + n1 = hibit(p1); + } + else + return v2; + } +} + +/* The forward and inverse affine transformations used in the S-box */ +uint8_t fwd_affine(const uint8_t x) +{ +#if defined( HAVE_UINT_32T ) + uint32_t w = x; + w ^= (w << 1) ^ (w << 2) ^ (w << 3) ^ (w << 4); + return 0x63 ^ ((w ^ (w >> 8)) & 0xff); +#else + return 0x63 ^ x ^ (x << 1) ^ (x << 2) ^ (x << 3) ^ (x << 4) + ^ (x >> 7) ^ (x >> 6) ^ (x >> 5) ^ (x >> 4); +#endif +} + +uint8_t inv_affine(const uint8_t x) +{ +#if defined( HAVE_UINT_32T ) + uint32_t w = x; + w = (w << 1) ^ (w << 3) ^ (w << 6); + return 0x05 ^ ((w ^ (w >> 8)) & 0xff); +#else + return 0x05 ^ (x << 1) ^ (x << 3) ^ (x << 6) + ^ (x >> 7) ^ (x >> 5) ^ (x >> 2); +#endif +} + +#define s_box(x) fwd_affine(gf_inv(x)) +#define is_box(x) gf_inv(inv_affine(x)) +#define gfm2_sb(x) f2(s_box(x)) +#define gfm3_sb(x) f3(s_box(x)) +#define gfm_9(x) f9(x) +#define gfm_b(x) fb(x) +#define gfm_d(x) fd(x) +#define gfm_e(x) fe(x) + +#endif + +#if defined( HAVE_MEMCPY ) +# define block_copy_nn(d, s, l) memcpy(d, s, l) +# define block_copy(d, s) memcpy(d, s, N_BLOCK) +#else +# define block_copy_nn(d, s, l) copy_block_nn(d, s, l) +# define block_copy(d, s) copy_block(d, s) +#endif + +static void copy_block( void *d, const void *s ) +{ +#if defined( HAVE_UINT_32T ) + ((uint32_t*)d)[ 0] = ((uint32_t*)s)[ 0]; + ((uint32_t*)d)[ 1] = ((uint32_t*)s)[ 1]; + ((uint32_t*)d)[ 2] = ((uint32_t*)s)[ 2]; + ((uint32_t*)d)[ 3] = ((uint32_t*)s)[ 3]; +#else + ((uint8_t*)d)[ 0] = ((uint8_t*)s)[ 0]; + ((uint8_t*)d)[ 1] = ((uint8_t*)s)[ 1]; + ((uint8_t*)d)[ 2] = ((uint8_t*)s)[ 2]; + ((uint8_t*)d)[ 3] = ((uint8_t*)s)[ 3]; + ((uint8_t*)d)[ 4] = ((uint8_t*)s)[ 4]; + ((uint8_t*)d)[ 5] = ((uint8_t*)s)[ 5]; + ((uint8_t*)d)[ 6] = ((uint8_t*)s)[ 6]; + ((uint8_t*)d)[ 7] = ((uint8_t*)s)[ 7]; + ((uint8_t*)d)[ 8] = ((uint8_t*)s)[ 8]; + ((uint8_t*)d)[ 9] = ((uint8_t*)s)[ 9]; + ((uint8_t*)d)[10] = ((uint8_t*)s)[10]; + ((uint8_t*)d)[11] = ((uint8_t*)s)[11]; + ((uint8_t*)d)[12] = ((uint8_t*)s)[12]; + ((uint8_t*)d)[13] = ((uint8_t*)s)[13]; + ((uint8_t*)d)[14] = ((uint8_t*)s)[14]; + ((uint8_t*)d)[15] = ((uint8_t*)s)[15]; +#endif +} + +static void copy_block_nn( uint8_t * d, const uint8_t *s, uint8_t nn ) +{ + while( nn-- ) + //*((uint8_t*)d)++ = *((uint8_t*)s)++; + *d++ = *s++; +} + +static void xor_block( void *d, const void *s ) +{ +#if defined( HAVE_UINT_32T ) + ((uint32_t*)d)[ 0] ^= ((uint32_t*)s)[ 0]; + ((uint32_t*)d)[ 1] ^= ((uint32_t*)s)[ 1]; + ((uint32_t*)d)[ 2] ^= ((uint32_t*)s)[ 2]; + ((uint32_t*)d)[ 3] ^= ((uint32_t*)s)[ 3]; +#else + ((uint8_t*)d)[ 0] ^= ((uint8_t*)s)[ 0]; + ((uint8_t*)d)[ 1] ^= ((uint8_t*)s)[ 1]; + ((uint8_t*)d)[ 2] ^= ((uint8_t*)s)[ 2]; + ((uint8_t*)d)[ 3] ^= ((uint8_t*)s)[ 3]; + ((uint8_t*)d)[ 4] ^= ((uint8_t*)s)[ 4]; + ((uint8_t*)d)[ 5] ^= ((uint8_t*)s)[ 5]; + ((uint8_t*)d)[ 6] ^= ((uint8_t*)s)[ 6]; + ((uint8_t*)d)[ 7] ^= ((uint8_t*)s)[ 7]; + ((uint8_t*)d)[ 8] ^= ((uint8_t*)s)[ 8]; + ((uint8_t*)d)[ 9] ^= ((uint8_t*)s)[ 9]; + ((uint8_t*)d)[10] ^= ((uint8_t*)s)[10]; + ((uint8_t*)d)[11] ^= ((uint8_t*)s)[11]; + ((uint8_t*)d)[12] ^= ((uint8_t*)s)[12]; + ((uint8_t*)d)[13] ^= ((uint8_t*)s)[13]; + ((uint8_t*)d)[14] ^= ((uint8_t*)s)[14]; + ((uint8_t*)d)[15] ^= ((uint8_t*)s)[15]; +#endif +} + +static void copy_and_key( void *d, const void *s, const void *k ) +{ +#if defined( HAVE_UINT_32T ) + ((uint32_t*)d)[ 0] = ((uint32_t*)s)[ 0] ^ ((uint32_t*)k)[ 0]; + ((uint32_t*)d)[ 1] = ((uint32_t*)s)[ 1] ^ ((uint32_t*)k)[ 1]; + ((uint32_t*)d)[ 2] = ((uint32_t*)s)[ 2] ^ ((uint32_t*)k)[ 2]; + ((uint32_t*)d)[ 3] = ((uint32_t*)s)[ 3] ^ ((uint32_t*)k)[ 3]; +#elif 1 + ((uint8_t*)d)[ 0] = ((uint8_t*)s)[ 0] ^ ((uint8_t*)k)[ 0]; + ((uint8_t*)d)[ 1] = ((uint8_t*)s)[ 1] ^ ((uint8_t*)k)[ 1]; + ((uint8_t*)d)[ 2] = ((uint8_t*)s)[ 2] ^ ((uint8_t*)k)[ 2]; + ((uint8_t*)d)[ 3] = ((uint8_t*)s)[ 3] ^ ((uint8_t*)k)[ 3]; + ((uint8_t*)d)[ 4] = ((uint8_t*)s)[ 4] ^ ((uint8_t*)k)[ 4]; + ((uint8_t*)d)[ 5] = ((uint8_t*)s)[ 5] ^ ((uint8_t*)k)[ 5]; + ((uint8_t*)d)[ 6] = ((uint8_t*)s)[ 6] ^ ((uint8_t*)k)[ 6]; + ((uint8_t*)d)[ 7] = ((uint8_t*)s)[ 7] ^ ((uint8_t*)k)[ 7]; + ((uint8_t*)d)[ 8] = ((uint8_t*)s)[ 8] ^ ((uint8_t*)k)[ 8]; + ((uint8_t*)d)[ 9] = ((uint8_t*)s)[ 9] ^ ((uint8_t*)k)[ 9]; + ((uint8_t*)d)[10] = ((uint8_t*)s)[10] ^ ((uint8_t*)k)[10]; + ((uint8_t*)d)[11] = ((uint8_t*)s)[11] ^ ((uint8_t*)k)[11]; + ((uint8_t*)d)[12] = ((uint8_t*)s)[12] ^ ((uint8_t*)k)[12]; + ((uint8_t*)d)[13] = ((uint8_t*)s)[13] ^ ((uint8_t*)k)[13]; + ((uint8_t*)d)[14] = ((uint8_t*)s)[14] ^ ((uint8_t*)k)[14]; + ((uint8_t*)d)[15] = ((uint8_t*)s)[15] ^ ((uint8_t*)k)[15]; +#else + block_copy(d, s); + xor_block(d, k); +#endif +} + +static void add_round_key( uint8_t d[N_BLOCK], const uint8_t k[N_BLOCK] ) +{ + xor_block(d, k); +} + +static void shift_sub_rows( uint8_t st[N_BLOCK] ) +{ uint8_t tt; + + st[ 0] = s_box(st[ 0]); st[ 4] = s_box(st[ 4]); + st[ 8] = s_box(st[ 8]); st[12] = s_box(st[12]); + + tt = st[1]; st[ 1] = s_box(st[ 5]); st[ 5] = s_box(st[ 9]); + st[ 9] = s_box(st[13]); st[13] = s_box( tt ); + + tt = st[2]; st[ 2] = s_box(st[10]); st[10] = s_box( tt ); + tt = st[6]; st[ 6] = s_box(st[14]); st[14] = s_box( tt ); + + tt = st[15]; st[15] = s_box(st[11]); st[11] = s_box(st[ 7]); + st[ 7] = s_box(st[ 3]); st[ 3] = s_box( tt ); +} + +#if defined( AES_DEC_PREKEYED ) + +static void inv_shift_sub_rows( uint8_t st[N_BLOCK] ) +{ uint8_t tt; + + st[ 0] = is_box(st[ 0]); st[ 4] = is_box(st[ 4]); + st[ 8] = is_box(st[ 8]); st[12] = is_box(st[12]); + + tt = st[13]; st[13] = is_box(st[9]); st[ 9] = is_box(st[5]); + st[ 5] = is_box(st[1]); st[ 1] = is_box( tt ); + + tt = st[2]; st[ 2] = is_box(st[10]); st[10] = is_box( tt ); + tt = st[6]; st[ 6] = is_box(st[14]); st[14] = is_box( tt ); + + tt = st[3]; st[ 3] = is_box(st[ 7]); st[ 7] = is_box(st[11]); + st[11] = is_box(st[15]); st[15] = is_box( tt ); +} + +#endif + +#if defined( VERSION_1 ) + static void mix_sub_columns( uint8_t dt[N_BLOCK] ) + { uint8_t st[N_BLOCK]; + block_copy(st, dt); +#else + static void mix_sub_columns( uint8_t dt[N_BLOCK], uint8_t st[N_BLOCK] ) + { +#endif + dt[ 0] = gfm2_sb(st[0]) ^ gfm3_sb(st[5]) ^ s_box(st[10]) ^ s_box(st[15]); + dt[ 1] = s_box(st[0]) ^ gfm2_sb(st[5]) ^ gfm3_sb(st[10]) ^ s_box(st[15]); + dt[ 2] = s_box(st[0]) ^ s_box(st[5]) ^ gfm2_sb(st[10]) ^ gfm3_sb(st[15]); + dt[ 3] = gfm3_sb(st[0]) ^ s_box(st[5]) ^ s_box(st[10]) ^ gfm2_sb(st[15]); + + dt[ 4] = gfm2_sb(st[4]) ^ gfm3_sb(st[9]) ^ s_box(st[14]) ^ s_box(st[3]); + dt[ 5] = s_box(st[4]) ^ gfm2_sb(st[9]) ^ gfm3_sb(st[14]) ^ s_box(st[3]); + dt[ 6] = s_box(st[4]) ^ s_box(st[9]) ^ gfm2_sb(st[14]) ^ gfm3_sb(st[3]); + dt[ 7] = gfm3_sb(st[4]) ^ s_box(st[9]) ^ s_box(st[14]) ^ gfm2_sb(st[3]); + + dt[ 8] = gfm2_sb(st[8]) ^ gfm3_sb(st[13]) ^ s_box(st[2]) ^ s_box(st[7]); + dt[ 9] = s_box(st[8]) ^ gfm2_sb(st[13]) ^ gfm3_sb(st[2]) ^ s_box(st[7]); + dt[10] = s_box(st[8]) ^ s_box(st[13]) ^ gfm2_sb(st[2]) ^ gfm3_sb(st[7]); + dt[11] = gfm3_sb(st[8]) ^ s_box(st[13]) ^ s_box(st[2]) ^ gfm2_sb(st[7]); + + dt[12] = gfm2_sb(st[12]) ^ gfm3_sb(st[1]) ^ s_box(st[6]) ^ s_box(st[11]); + dt[13] = s_box(st[12]) ^ gfm2_sb(st[1]) ^ gfm3_sb(st[6]) ^ s_box(st[11]); + dt[14] = s_box(st[12]) ^ s_box(st[1]) ^ gfm2_sb(st[6]) ^ gfm3_sb(st[11]); + dt[15] = gfm3_sb(st[12]) ^ s_box(st[1]) ^ s_box(st[6]) ^ gfm2_sb(st[11]); + } + +#if defined( AES_DEC_PREKEYED ) + +#if defined( VERSION_1 ) + static void inv_mix_sub_columns( uint8_t dt[N_BLOCK] ) + { uint8_t st[N_BLOCK]; + block_copy(st, dt); +#else + static void inv_mix_sub_columns( uint8_t dt[N_BLOCK], uint8_t st[N_BLOCK] ) + { +#endif + dt[ 0] = is_box(gfm_e(st[ 0]) ^ gfm_b(st[ 1]) ^ gfm_d(st[ 2]) ^ gfm_9(st[ 3])); + dt[ 5] = is_box(gfm_9(st[ 0]) ^ gfm_e(st[ 1]) ^ gfm_b(st[ 2]) ^ gfm_d(st[ 3])); + dt[10] = is_box(gfm_d(st[ 0]) ^ gfm_9(st[ 1]) ^ gfm_e(st[ 2]) ^ gfm_b(st[ 3])); + dt[15] = is_box(gfm_b(st[ 0]) ^ gfm_d(st[ 1]) ^ gfm_9(st[ 2]) ^ gfm_e(st[ 3])); + + dt[ 4] = is_box(gfm_e(st[ 4]) ^ gfm_b(st[ 5]) ^ gfm_d(st[ 6]) ^ gfm_9(st[ 7])); + dt[ 9] = is_box(gfm_9(st[ 4]) ^ gfm_e(st[ 5]) ^ gfm_b(st[ 6]) ^ gfm_d(st[ 7])); + dt[14] = is_box(gfm_d(st[ 4]) ^ gfm_9(st[ 5]) ^ gfm_e(st[ 6]) ^ gfm_b(st[ 7])); + dt[ 3] = is_box(gfm_b(st[ 4]) ^ gfm_d(st[ 5]) ^ gfm_9(st[ 6]) ^ gfm_e(st[ 7])); + + dt[ 8] = is_box(gfm_e(st[ 8]) ^ gfm_b(st[ 9]) ^ gfm_d(st[10]) ^ gfm_9(st[11])); + dt[13] = is_box(gfm_9(st[ 8]) ^ gfm_e(st[ 9]) ^ gfm_b(st[10]) ^ gfm_d(st[11])); + dt[ 2] = is_box(gfm_d(st[ 8]) ^ gfm_9(st[ 9]) ^ gfm_e(st[10]) ^ gfm_b(st[11])); + dt[ 7] = is_box(gfm_b(st[ 8]) ^ gfm_d(st[ 9]) ^ gfm_9(st[10]) ^ gfm_e(st[11])); + + dt[12] = is_box(gfm_e(st[12]) ^ gfm_b(st[13]) ^ gfm_d(st[14]) ^ gfm_9(st[15])); + dt[ 1] = is_box(gfm_9(st[12]) ^ gfm_e(st[13]) ^ gfm_b(st[14]) ^ gfm_d(st[15])); + dt[ 6] = is_box(gfm_d(st[12]) ^ gfm_9(st[13]) ^ gfm_e(st[14]) ^ gfm_b(st[15])); + dt[11] = is_box(gfm_b(st[12]) ^ gfm_d(st[13]) ^ gfm_9(st[14]) ^ gfm_e(st[15])); + } + +#endif + +#if defined( AES_ENC_PREKEYED ) || defined( AES_DEC_PREKEYED ) + +/* Set the cipher key for the pre-keyed version */ + +return_type aes_set_key( const uint8_t key[], length_type keylen, aes_context ctx[1] ) +{ + uint8_t cc, rc, hi; + + switch( keylen ) + { + case 16: + case 24: + case 32: + break; + default: + ctx->rnd = 0; + return ( uint8_t )-1; + } + block_copy_nn(ctx->ksch, key, keylen); + hi = (keylen + 28) << 2; + ctx->rnd = (hi >> 4) - 1; + for( cc = keylen, rc = 1; cc < hi; cc += 4 ) + { uint8_t tt, t0, t1, t2, t3; + + t0 = ctx->ksch[cc - 4]; + t1 = ctx->ksch[cc - 3]; + t2 = ctx->ksch[cc - 2]; + t3 = ctx->ksch[cc - 1]; + if( cc % keylen == 0 ) + { + tt = t0; + t0 = s_box(t1) ^ rc; + t1 = s_box(t2); + t2 = s_box(t3); + t3 = s_box(tt); + rc = f2(rc); + } + else if( keylen > 24 && cc % keylen == 16 ) + { + t0 = s_box(t0); + t1 = s_box(t1); + t2 = s_box(t2); + t3 = s_box(t3); + } + tt = cc - keylen; + ctx->ksch[cc + 0] = ctx->ksch[tt + 0] ^ t0; + ctx->ksch[cc + 1] = ctx->ksch[tt + 1] ^ t1; + ctx->ksch[cc + 2] = ctx->ksch[tt + 2] ^ t2; + ctx->ksch[cc + 3] = ctx->ksch[tt + 3] ^ t3; + } + return 0; +} + +#endif + +#if defined( AES_ENC_PREKEYED ) + +/* Encrypt a single block of 16 bytes */ + +return_type aes_encrypt( const uint8_t in[N_BLOCK], uint8_t out[N_BLOCK], const aes_context ctx[1] ) +{ + if( ctx->rnd ) + { + uint8_t s1[N_BLOCK], r; + copy_and_key( s1, in, ctx->ksch ); + + for( r = 1 ; r < ctx->rnd ; ++r ) +#if defined( VERSION_1 ) + { + mix_sub_columns( s1 ); + add_round_key( s1, ctx->ksch + r * N_BLOCK); + } +#else + { uint8_t s2[N_BLOCK]; + mix_sub_columns( s2, s1 ); + copy_and_key( s1, s2, ctx->ksch + r * N_BLOCK); + } +#endif + shift_sub_rows( s1 ); + copy_and_key( out, s1, ctx->ksch + r * N_BLOCK ); + } + else + return ( uint8_t )-1; + return 0; +} + +/* CBC encrypt a number of blocks (input and return an IV) */ + +return_type aes_cbc_encrypt( const uint8_t *in, uint8_t *out, + int32_t n_block, uint8_t iv[N_BLOCK], const aes_context ctx[1] ) +{ + + while(n_block--) + { + xor_block(iv, in); + if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) + return EXIT_FAILURE; + //memcpy(out, iv, N_BLOCK); + block_copy(out, iv); + in += N_BLOCK; + out += N_BLOCK; + } + return EXIT_SUCCESS; +} + +#endif + +#if defined( AES_DEC_PREKEYED ) + +/* Decrypt a single block of 16 bytes */ + +return_type aes_decrypt( const uint8_t in[N_BLOCK], uint8_t out[N_BLOCK], const aes_context ctx[1] ) +{ + if( ctx->rnd ) + { + uint8_t s1[N_BLOCK], r; + copy_and_key( s1, in, ctx->ksch + ctx->rnd * N_BLOCK ); + inv_shift_sub_rows( s1 ); + + for( r = ctx->rnd ; --r ; ) +#if defined( VERSION_1 ) + { + add_round_key( s1, ctx->ksch + r * N_BLOCK ); + inv_mix_sub_columns( s1 ); + } +#else + { uint8_t s2[N_BLOCK]; + copy_and_key( s2, s1, ctx->ksch + r * N_BLOCK ); + inv_mix_sub_columns( s1, s2 ); + } +#endif + copy_and_key( out, s1, ctx->ksch ); + } + else + return -1; + return 0; +} + +/* CBC decrypt a number of blocks (input and return an IV) */ + +return_type aes_cbc_decrypt( const uint8_t *in, uint8_t *out, + int32_t n_block, uint8_t iv[N_BLOCK], const aes_context ctx[1] ) +{ + while(n_block--) + { uint8_t tmp[N_BLOCK]; + + //memcpy(tmp, in, N_BLOCK); + block_copy(tmp, in); + if(aes_decrypt(in, out, ctx) != EXIT_SUCCESS) + return EXIT_FAILURE; + xor_block(out, iv); + //memcpy(iv, tmp, N_BLOCK); + block_copy(iv, tmp); + in += N_BLOCK; + out += N_BLOCK; + } + return EXIT_SUCCESS; +} + +#endif + +#if defined( AES_ENC_128_OTFK ) + +/* The 'on the fly' encryption key update for for 128 bit keys */ + +static void update_encrypt_key_128( uint8_t k[N_BLOCK], uint8_t *rc ) +{ uint8_t cc; + + k[0] ^= s_box(k[13]) ^ *rc; + k[1] ^= s_box(k[14]); + k[2] ^= s_box(k[15]); + k[3] ^= s_box(k[12]); + *rc = f2( *rc ); + + for(cc = 4; cc < 16; cc += 4 ) + { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } +} + +/* Encrypt a single block of 16 bytes with 'on the fly' 128 bit keying */ + +void aes_encrypt_128( const uint8_t in[N_BLOCK], uint8_t out[N_BLOCK], + const uint8_t key[N_BLOCK], uint8_t o_key[N_BLOCK] ) +{ uint8_t s1[N_BLOCK], r, rc = 1; + + if(o_key != key) + block_copy( o_key, key ); + copy_and_key( s1, in, o_key ); + + for( r = 1 ; r < 10 ; ++r ) +#if defined( VERSION_1 ) + { + mix_sub_columns( s1 ); + update_encrypt_key_128( o_key, &rc ); + add_round_key( s1, o_key ); + } +#else + { uint8_t s2[N_BLOCK]; + mix_sub_columns( s2, s1 ); + update_encrypt_key_128( o_key, &rc ); + copy_and_key( s1, s2, o_key ); + } +#endif + + shift_sub_rows( s1 ); + update_encrypt_key_128( o_key, &rc ); + copy_and_key( out, s1, o_key ); +} + +#endif + +#if defined( AES_DEC_128_OTFK ) + +/* The 'on the fly' decryption key update for for 128 bit keys */ + +static void update_decrypt_key_128( uint8_t k[N_BLOCK], uint8_t *rc ) +{ uint8_t cc; + + for( cc = 12; cc > 0; cc -= 4 ) + { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } + *rc = d2(*rc); + k[0] ^= s_box(k[13]) ^ *rc; + k[1] ^= s_box(k[14]); + k[2] ^= s_box(k[15]); + k[3] ^= s_box(k[12]); +} + +/* Decrypt a single block of 16 bytes with 'on the fly' 128 bit keying */ + +void aes_decrypt_128( const uint8_t in[N_BLOCK], uint8_t out[N_BLOCK], + const uint8_t key[N_BLOCK], uint8_t o_key[N_BLOCK] ) +{ + uint8_t s1[N_BLOCK], r, rc = 0x6c; + if(o_key != key) + block_copy( o_key, key ); + + copy_and_key( s1, in, o_key ); + inv_shift_sub_rows( s1 ); + + for( r = 10 ; --r ; ) +#if defined( VERSION_1 ) + { + update_decrypt_key_128( o_key, &rc ); + add_round_key( s1, o_key ); + inv_mix_sub_columns( s1 ); + } +#else + { uint8_t s2[N_BLOCK]; + update_decrypt_key_128( o_key, &rc ); + copy_and_key( s2, s1, o_key ); + inv_mix_sub_columns( s1, s2 ); + } +#endif + update_decrypt_key_128( o_key, &rc ); + copy_and_key( out, s1, o_key ); +} + +#endif + +#if defined( AES_ENC_256_OTFK ) + +/* The 'on the fly' encryption key update for for 256 bit keys */ + +static void update_encrypt_key_256( uint8_t k[2 * N_BLOCK], uint8_t *rc ) +{ uint8_t cc; + + k[0] ^= s_box(k[29]) ^ *rc; + k[1] ^= s_box(k[30]); + k[2] ^= s_box(k[31]); + k[3] ^= s_box(k[28]); + *rc = f2( *rc ); + + for(cc = 4; cc < 16; cc += 4) + { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } + + k[16] ^= s_box(k[12]); + k[17] ^= s_box(k[13]); + k[18] ^= s_box(k[14]); + k[19] ^= s_box(k[15]); + + for( cc = 20; cc < 32; cc += 4 ) + { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } +} + +/* Encrypt a single block of 16 bytes with 'on the fly' 256 bit keying */ + +void aes_encrypt_256( const uint8_t in[N_BLOCK], uint8_t out[N_BLOCK], + const uint8_t key[2 * N_BLOCK], uint8_t o_key[2 * N_BLOCK] ) +{ + uint8_t s1[N_BLOCK], r, rc = 1; + if(o_key != key) + { + block_copy( o_key, key ); + block_copy( o_key + 16, key + 16 ); + } + copy_and_key( s1, in, o_key ); + + for( r = 1 ; r < 14 ; ++r ) +#if defined( VERSION_1 ) + { + mix_sub_columns(s1); + if( r & 1 ) + add_round_key( s1, o_key + 16 ); + else + { + update_encrypt_key_256( o_key, &rc ); + add_round_key( s1, o_key ); + } + } +#else + { uint8_t s2[N_BLOCK]; + mix_sub_columns( s2, s1 ); + if( r & 1 ) + copy_and_key( s1, s2, o_key + 16 ); + else + { + update_encrypt_key_256( o_key, &rc ); + copy_and_key( s1, s2, o_key ); + } + } +#endif + + shift_sub_rows( s1 ); + update_encrypt_key_256( o_key, &rc ); + copy_and_key( out, s1, o_key ); +} + +#endif + +#if defined( AES_DEC_256_OTFK ) + +/* The 'on the fly' encryption key update for for 256 bit keys */ + +static void update_decrypt_key_256( uint8_t k[2 * N_BLOCK], uint8_t *rc ) +{ uint8_t cc; + + for(cc = 28; cc > 16; cc -= 4) + { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } + + k[16] ^= s_box(k[12]); + k[17] ^= s_box(k[13]); + k[18] ^= s_box(k[14]); + k[19] ^= s_box(k[15]); + + for(cc = 12; cc > 0; cc -= 4) + { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } + + *rc = d2(*rc); + k[0] ^= s_box(k[29]) ^ *rc; + k[1] ^= s_box(k[30]); + k[2] ^= s_box(k[31]); + k[3] ^= s_box(k[28]); +} + +/* Decrypt a single block of 16 bytes with 'on the fly' + 256 bit keying +*/ +void aes_decrypt_256( const uint8_t in[N_BLOCK], uint8_t out[N_BLOCK], + const uint8_t key[2 * N_BLOCK], uint8_t o_key[2 * N_BLOCK] ) +{ + uint8_t s1[N_BLOCK], r, rc = 0x80; + + if(o_key != key) + { + block_copy( o_key, key ); + block_copy( o_key + 16, key + 16 ); + } + + copy_and_key( s1, in, o_key ); + inv_shift_sub_rows( s1 ); + + for( r = 14 ; --r ; ) +#if defined( VERSION_1 ) + { + if( ( r & 1 ) ) + { + update_decrypt_key_256( o_key, &rc ); + add_round_key( s1, o_key + 16 ); + } + else + add_round_key( s1, o_key ); + inv_mix_sub_columns( s1 ); + } +#else + { uint8_t s2[N_BLOCK]; + if( ( r & 1 ) ) + { + update_decrypt_key_256( o_key, &rc ); + copy_and_key( s2, s1, o_key + 16 ); + } + else + copy_and_key( s2, s1, o_key ); + inv_mix_sub_columns( s1, s2 ); + } +#endif + copy_and_key( out, s1, o_key ); +} + +#endif diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/soft-se/aes.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/soft-se/aes.h new file mode 100644 index 0000000000..b4f9f40b38 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/soft-se/aes.h @@ -0,0 +1,170 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue 09/09/2006 + + This is an AES implementation that uses only 8-bit byte operations on the + cipher state. + */ + +#ifndef AES_H +#define AES_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#if 1 +# define AES_ENC_PREKEYED /* AES encryption with a precomputed key schedule */ +#endif +#if 0 +# define AES_DEC_PREKEYED /* AES decryption with a precomputed key schedule */ +#endif +#if 0 +# define AES_ENC_128_OTFK /* AES encryption with 'on the fly' 128 bit keying */ +#endif +#if 0 +# define AES_DEC_128_OTFK /* AES decryption with 'on the fly' 128 bit keying */ +#endif +#if 0 +# define AES_ENC_256_OTFK /* AES encryption with 'on the fly' 256 bit keying */ +#endif +#if 0 +# define AES_DEC_256_OTFK /* AES decryption with 'on the fly' 256 bit keying */ +#endif + +#define N_ROW 4 +#define N_COL 4 +#define N_BLOCK (N_ROW * N_COL) +#define N_MAX_ROUNDS 14 + +typedef uint8_t return_type; + +/* Warning: The key length for 256 bit keys overflows a byte + (see comment below) +*/ + +typedef uint8_t length_type; + +typedef struct +{ uint8_t ksch[(N_MAX_ROUNDS + 1) * N_BLOCK]; + uint8_t rnd; +} aes_context; + +/* The following calls are for a precomputed key schedule + + NOTE: If the length_type used for the key length is an + unsigned 8-bit character, a key length of 256 bits must + be entered as a length in bytes (valid inputs are hence + 128, 192, 16, 24 and 32). +*/ + +#if defined( AES_ENC_PREKEYED ) || defined( AES_DEC_PREKEYED ) + +return_type aes_set_key( const uint8_t key[], + length_type keylen, + aes_context ctx[1] ); +#endif + +#if defined( AES_ENC_PREKEYED ) + +return_type aes_encrypt( const uint8_t in[N_BLOCK], + uint8_t out[N_BLOCK], + const aes_context ctx[1] ); + +return_type aes_cbc_encrypt( const uint8_t *in, + uint8_t *out, + int32_t n_block, + uint8_t iv[N_BLOCK], + const aes_context ctx[1] ); +#endif + +#if defined( AES_DEC_PREKEYED ) + +return_type aes_decrypt( const uint8_t in[N_BLOCK], + uint8_t out[N_BLOCK], + const aes_context ctx[1] ); + +return_type aes_cbc_decrypt( const uint8_t *in, + uint8_t *out, + int32_t n_block, + uint8_t iv[N_BLOCK], + const aes_context ctx[1] ); +#endif + +/* The following calls are for 'on the fly' keying. In this case the + encryption and decryption keys are different. + + The encryption subroutines take a key in an array of bytes in + key[L] where L is 16, 24 or 32 bytes for key lengths of 128, + 192, and 256 bits respectively. They then encrypts the input + data, in[] with this key and put the reult in the output array + out[]. In addition, the second key array, o_key[L], is used + to output the key that is needed by the decryption subroutine + to reverse the encryption operation. The two key arrays can + be the same array but in this case the original key will be + overwritten. + + In the same way, the decryption subroutines output keys that + can be used to reverse their effect when used for encryption. + + Only 128 and 256 bit keys are supported in these 'on the fly' + modes. +*/ + +#if defined( AES_ENC_128_OTFK ) +void aes_encrypt_128( const uint8_t in[N_BLOCK], + uint8_t out[N_BLOCK], + const uint8_t key[N_BLOCK], + uint8_t o_key[N_BLOCK] ); +#endif + +#if defined( AES_DEC_128_OTFK ) +void aes_decrypt_128( const uint8_t in[N_BLOCK], + uint8_t out[N_BLOCK], + const uint8_t key[N_BLOCK], + uint8_t o_key[N_BLOCK] ); +#endif + +#if defined( AES_ENC_256_OTFK ) +void aes_encrypt_256( const uint8_t in[N_BLOCK], + uint8_t out[N_BLOCK], + const uint8_t key[2 * N_BLOCK], + uint8_t o_key[2 * N_BLOCK] ); +#endif + +#if defined( AES_DEC_256_OTFK ) +void aes_decrypt_256( const uint8_t in[N_BLOCK], + uint8_t out[N_BLOCK], + const uint8_t key[2 * N_BLOCK], + uint8_t o_key[2 * N_BLOCK] ); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/soft-se/cmac.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/soft-se/cmac.c new file mode 100644 index 0000000000..705a5470d5 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/soft-se/cmac.c @@ -0,0 +1,154 @@ +/************************************************************************** +Copyright (C) 2009 Lander Casado, Philippas Tsigas + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files +(the "Software"), to deal with the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimers. Redistributions in +binary form must reproduce the above copyright notice, this list of +conditions and the following disclaimers in the documentation and/or +other materials provided with the distribution. + +In no event shall the authors or copyright holders be liable for any special, +incidental, indirect or consequential damages of any kind, or any damages +whatsoever resulting from loss of use, data or profits, whether or not +advised of the possibility of damage, and on any theory of liability, +arising out of or in connection with the use or performance of this software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS WITH THE SOFTWARE + +*****************************************************************************/ +#include +#include "aes.h" +#include "cmac.h" +#include "utilities.h" + +#define LSHIFT( v, r ) \ + do \ + { \ + int32_t i; \ + for( i = 0; i < 15; i++ ) \ + ( r )[i] = ( v )[i] << 1 | ( v )[i + 1] >> 7; \ + ( r )[15] = ( v )[15] << 1; \ + } while( 0 ) + +#define XOR( v, r ) \ + do \ + { \ + int32_t i; \ + for( i = 0; i < 16; i++ ) \ + { \ + ( r )[i] = ( r )[i] ^ ( v )[i]; \ + } \ + } while( 0 ) + +void AES_CMAC_Init( AES_CMAC_CTX* ctx ) +{ + memset1( ctx->X, 0, sizeof ctx->X ); + ctx->M_n = 0; + memset1( ctx->rijndael.ksch, '\0', 240 ); +} + +void AES_CMAC_SetKey( AES_CMAC_CTX* ctx, const uint8_t key[AES_CMAC_KEY_LENGTH] ) +{ + aes_set_key( key, AES_CMAC_KEY_LENGTH, &ctx->rijndael ); +} + +void AES_CMAC_Update( AES_CMAC_CTX* ctx, const uint8_t* data, uint32_t len ) +{ + uint32_t mlen; + uint8_t in[16]; + + if( ctx->M_n > 0 ) + { + mlen = MIN( 16 - ctx->M_n, len ); + memcpy1( ctx->M_last + ctx->M_n, data, mlen ); + ctx->M_n += mlen; + if( ctx->M_n < 16 || len == mlen ) + return; + XOR( ctx->M_last, ctx->X ); + + memcpy1( in, &ctx->X[0], 16 ); // Otherwise it does not look good + aes_encrypt( in, in, &ctx->rijndael ); + memcpy1( &ctx->X[0], in, 16 ); + + data += mlen; + len -= mlen; + } + while( len > 16 ) + { /* not last block */ + + XOR( data, ctx->X ); + + memcpy1( in, &ctx->X[0], 16 ); // Otherwise it does not look good + aes_encrypt( in, in, &ctx->rijndael ); + memcpy1( &ctx->X[0], in, 16 ); + + data += 16; + len -= 16; + } + /* potential last block, save it */ + memcpy1( ctx->M_last, data, len ); + ctx->M_n = len; +} + +void AES_CMAC_Final( uint8_t digest[AES_CMAC_DIGEST_LENGTH], AES_CMAC_CTX* ctx ) +{ + uint8_t K[16]; + uint8_t in[16]; + /* generate subkey K1 */ + memset1( K, '\0', 16 ); + + aes_encrypt( K, K, &ctx->rijndael ); + + if( K[0] & 0x80 ) + { + LSHIFT( K, K ); + K[15] ^= 0x87; + } + else + LSHIFT( K, K ); + + if( ctx->M_n == 16 ) + { + /* last block was a complete block */ + XOR( K, ctx->M_last ); + } + else + { + /* generate subkey K2 */ + if( K[0] & 0x80 ) + { + LSHIFT( K, K ); + K[15] ^= 0x87; + } + else + LSHIFT( K, K ); + + /* padding(M_last) */ + ctx->M_last[ctx->M_n] = 0x80; + while( ++ctx->M_n < 16 ) + ctx->M_last[ctx->M_n] = 0; + + XOR( K, ctx->M_last ); + } + XOR( ctx->M_last, ctx->X ); + + memcpy1( in, &ctx->X[0], 16 ); // Otherwise it does not look good + aes_encrypt( in, digest, &ctx->rijndael ); + memset1( K, 0, sizeof K ); +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/soft-se/cmac.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/soft-se/cmac.h new file mode 100644 index 0000000000..7098cc411c --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/soft-se/cmac.h @@ -0,0 +1,71 @@ +/************************************************************************** +Copyright (C) 2009 Lander Casado, Philippas Tsigas + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files +(the "Software"), to deal with the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimers. Redistributions in +binary form must reproduce the above copyright notice, this list of +conditions and the following disclaimers in the documentation and/or +other materials provided with the distribution. + +In no event shall the authors or copyright holders be liable for any special, +incidental, indirect or consequential damages of any kind, or any damages +whatsoever resulting from loss of use, data or profits, whether or not +advised of the possibility of damage, and on any theory of liability, +arising out of or in connection with the use or performance of this software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS WITH THE SOFTWARE + +*****************************************************************************/ + +#ifndef _CMAC_H_ +#define _CMAC_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "aes.h" + +#define AES_CMAC_KEY_LENGTH 16 +#define AES_CMAC_DIGEST_LENGTH 16 + +typedef struct _AES_CMAC_CTX { + aes_context rijndael; + uint8_t X[16]; + uint8_t M_last[16]; + uint32_t M_n; + } AES_CMAC_CTX; + +//#include + +//__BEGIN_DECLS +void AES_CMAC_Init(AES_CMAC_CTX * ctx); +void AES_CMAC_SetKey(AES_CMAC_CTX * ctx, const uint8_t key[AES_CMAC_KEY_LENGTH]); +void AES_CMAC_Update(AES_CMAC_CTX * ctx, const uint8_t * data, uint32_t len); + // __attribute__((__bounded__(__string__,2,3))); +void AES_CMAC_Final(uint8_t digest[AES_CMAC_DIGEST_LENGTH], AES_CMAC_CTX * ctx); + // __attribute__((__bounded__(__minbytes__,1,AES_CMAC_DIGEST_LENGTH))); +//__END_DECLS + +#ifdef __cplusplus +} +#endif + +#endif /* _CMAC_H_ */ + diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/soft-se/se-identity.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/soft-se/se-identity.h new file mode 100644 index 0000000000..872167b441 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/soft-se/se-identity.h @@ -0,0 +1,297 @@ +/*! + * \file se-identity.h + * + * \brief Secure Element identity and keys + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2020 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + */ +#ifndef __SOFT_SE_IDENTITY_H__ +#define __SOFT_SE_IDENTITY_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + ****************************************************************************** + ********************************** WARNING *********************************** + ****************************************************************************** + The secure-element implementation supports both 1.0.x and 1.1.x LoRaWAN + versions of the specification. + Thus it has been decided to use the 1.1.x keys and EUI name definitions. + The below table shows the names equivalence between versions: + +---------------------+-------------------------+ + | 1.0.x | 1.1.x | + +=====================+=========================+ + | LORAWAN_DEVICE_EUI | LORAWAN_DEVICE_EUI | + +---------------------+-------------------------+ + | LORAWAN_APP_EUI | LORAWAN_JOIN_EUI | + +---------------------+-------------------------+ + | LORAWAN_GEN_APP_KEY | LORAWAN_APP_KEY | + +---------------------+-------------------------+ + | LORAWAN_APP_KEY | LORAWAN_NWK_KEY | + +---------------------+-------------------------+ + | LORAWAN_NWK_S_KEY | LORAWAN_F_NWK_S_INT_KEY | + +---------------------+-------------------------+ + | LORAWAN_NWK_S_KEY | LORAWAN_S_NWK_S_INT_KEY | + +---------------------+-------------------------+ + | LORAWAN_NWK_S_KEY | LORAWAN_NWK_S_ENC_KEY | + +---------------------+-------------------------+ + | LORAWAN_APP_S_KEY | LORAWAN_APP_S_KEY | + +---------------------+-------------------------+ + ****************************************************************************** + ****************************************************************************** + ****************************************************************************** + */ + +/*! + * When set to 1 DevEui is LORAWAN_DEVICE_EUI + * When set to 0 DevEui is automatically set with a value provided by MCU platform + */ +#define STATIC_DEVICE_EUI 0 + +/*! + * end-device IEEE EUI (big endian) + */ +#define LORAWAN_DEVICE_EUI { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } + +/*! + * App/Join server IEEE EUI (big endian) + */ +#define LORAWAN_JOIN_EUI { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } + +/*! + * Secure-element pin + */ +#define SECURE_ELEMENT_PIN { 0x00, 0x00, 0x00, 0x00 } + +/*! + * When set to 1 DevAddr is LORAWAN_DEVICE_ADDRESS + * When set to 0 DevAddr is automatically set with a value provided by a pseudo + * random generator seeded with a value provided by the MCU platform + */ +#define STATIC_DEVICE_ADDRESS 0 + +/*! + * Device address on the network (big endian) + */ +#define LORAWAN_DEVICE_ADDRESS ( uint32_t )0x00000000 + +#define SOFT_SE_KEY_LIST \ + { \ + { \ + /*! \ + * Application root key \ + * WARNING: FOR 1.0.x DEVICES IT IS THE \ref LORAWAN_GEN_APP_KEY \ + */ \ + .KeyID = APP_KEY, \ + .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + 0x3C }, \ + }, \ + { \ + /*! \ + * Network root key \ + * WARNING: FOR 1.0.x DEVICES IT IS THE \ref LORAWAN_APP_KEY \ + */ \ + .KeyID = NWK_KEY, \ + .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + 0x3C }, \ + }, \ + { \ + /*! \ + * Join session integrity key (Dynamically updated) \ + * WARNING: NOT USED FOR 1.0.x DEVICES \ + */ \ + .KeyID = J_S_INT_KEY, \ + .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00 }, \ + }, \ + { \ + /*! \ + * Join session encryption key (Dynamically updated) \ + * WARNING: NOT USED FOR 1.0.x DEVICES \ + */ \ + .KeyID = J_S_ENC_KEY, \ + .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00 }, \ + }, \ + { \ + /*! \ + * Forwarding Network session integrity key \ + * WARNING: NWK_S_KEY FOR 1.0.x DEVICES \ + */ \ + .KeyID = F_NWK_S_INT_KEY, \ + .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + 0x3C }, \ + }, \ + { \ + /*! \ + * Serving Network session integrity key \ + * WARNING: NOT USED FOR 1.0.x DEVICES. MUST BE THE SAME AS \ref LORAWAN_F_NWK_S_INT_KEY \ + */ \ + .KeyID = S_NWK_S_INT_KEY, \ + .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + 0x3C }, \ + }, \ + { \ + /*! \ + * Network session encryption key \ + * WARNING: NOT USED FOR 1.0.x DEVICES. MUST BE THE SAME AS \ref LORAWAN_F_NWK_S_INT_KEY \ + */ \ + .KeyID = NWK_S_ENC_KEY, \ + .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + 0x3C }, \ + }, \ + { \ + /*! \ + * Application session key \ + */ \ + .KeyID = APP_S_KEY, \ + .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + 0x3C }, \ + }, \ + { \ + /*! \ + * Multicast root key (Dynamically updated) \ + */ \ + .KeyID = MC_ROOT_KEY, \ + .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00 }, \ + }, \ + { \ + /*! \ + * Multicast key encryption key (Dynamically updated) \ + */ \ + .KeyID = MC_KE_KEY, \ + .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00 }, \ + }, \ + { \ + /*! \ + * Multicast group #0 root key (Dynamically updated) \ + */ \ + .KeyID = MC_KEY_0, \ + .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00 }, \ + }, \ + { \ + /*! \ + * Multicast group #0 application session key (Dynamically updated) \ + */ \ + .KeyID = MC_APP_S_KEY_0, \ + .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00 }, \ + }, \ + { \ + /*! \ + * Multicast group #0 network session key (Dynamically updated) \ + */ \ + .KeyID = MC_NWK_S_KEY_0, \ + .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00 }, \ + }, \ + { \ + /*! \ + * Multicast group #1 root key (Dynamically updated) \ + */ \ + .KeyID = MC_KEY_1, \ + .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00 }, \ + }, \ + { \ + /*! \ + * Multicast group #1 application session key (Dynamically updated) \ + */ \ + .KeyID = MC_APP_S_KEY_1, \ + .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00 }, \ + }, \ + { \ + /*! \ + * Multicast group #1 network session key (Dynamically updated) \ + */ \ + .KeyID = MC_NWK_S_KEY_1, \ + .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00 }, \ + }, \ + { \ + /*! \ + * Multicast group #2 root key (Dynamically updated) \ + */ \ + .KeyID = MC_KEY_2, \ + .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00 }, \ + }, \ + { \ + /*! \ + * Multicast group #2 application session key (Dynamically updated) \ + */ \ + .KeyID = MC_APP_S_KEY_2, \ + .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00 }, \ + }, \ + { \ + /*! \ + * Multicast group #2 network session key (Dynamically updated) \ + */ \ + .KeyID = MC_NWK_S_KEY_2, \ + .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00 }, \ + }, \ + { \ + /*! \ + * Multicast group #3 root key (Dynamically updated) \ + */ \ + .KeyID = MC_KEY_3, \ + .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00 }, \ + }, \ + { \ + /*! \ + * Multicast group #3 application session key (Dynamically updated) \ + */ \ + .KeyID = MC_APP_S_KEY_3, \ + .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00 }, \ + }, \ + { \ + /*! \ + * Multicast group #3 network session key (Dynamically updated) \ + */ \ + .KeyID = MC_NWK_S_KEY_3, \ + .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00 }, \ + }, \ + { \ + /*! \ + * All zeros key. (ClassB usage)(constant) \ + */ \ + .KeyID = SLOT_RAND_ZERO_KEY, \ + .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00 }, \ + }, \ + }, + +#ifdef __cplusplus +} +#endif + +#endif // __SOFT_SE_IDENTITY_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/soft-se/soft-se-hal.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/soft-se/soft-se-hal.c new file mode 100644 index 0000000000..2d33349963 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/soft-se/soft-se-hal.c @@ -0,0 +1,33 @@ +/*! + * \file soft-se-hal.h + * + * \brief Secure Element hardware abstraction layer implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2020 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + */ +#include "board.h" + +#include "soft-se-hal.h" + +void SoftSeHalGetUniqueId( uint8_t *id ) +{ + BoardGetUniqueId( id ); +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/soft-se/soft-se-hal.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/soft-se/soft-se-hal.h new file mode 100644 index 0000000000..f6c733c0e7 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/soft-se/soft-se-hal.h @@ -0,0 +1,47 @@ +/*! + * \file soft-se-hal.h + * + * \brief Secure Element hardware abstraction layer + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2020 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + */ +#ifndef __SOFT_SE_HAL_H__ +#define __SOFT_SE_HAL_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/*! + * \brief Get a 64 bits unique ID + * + * \param [IN] id Pointer to an array that will contain the Unique ID + */ +void SoftSeHalGetUniqueId( uint8_t *id ); + +#ifdef __cplusplus +} +#endif + +#endif // __SOFT_SE_HAL_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/soft-se/soft-se.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/soft-se/soft-se.c new file mode 100644 index 0000000000..671ec308b1 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/soft-se/soft-se.c @@ -0,0 +1,448 @@ +/*! + * \file soft-se.c + * + * \brief Secure Element software implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2020 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + */ +#include +#include + +#include "utilities.h" +#include "aes.h" +#include "cmac.h" + +#include "LoRaMacHeaderTypes.h" + +#include "secure-element.h" +#include "secure-element-nvm.h" +#include "se-identity.h" +#include "soft-se-hal.h" + +static SecureElementNvmData_t* SeNvm; + +/* + * Local functions + */ + +/* + * Gets key item from key list. + * + * \param[IN] keyID - Key identifier + * \param[OUT] keyItem - Key item reference + * \retval - Status of the operation + */ +static SecureElementStatus_t GetKeyByID( KeyIdentifier_t keyID, Key_t** keyItem ) +{ + for( uint8_t i = 0; i < NUM_OF_KEYS; i++ ) + { + if( SeNvm->KeyList[i].KeyID == keyID ) + { + *keyItem = &( SeNvm->KeyList[i] ); + return SECURE_ELEMENT_SUCCESS; + } + } + return SECURE_ELEMENT_ERROR_INVALID_KEY_ID; +} + +/* + * Computes a CMAC of a message using provided initial Bx block + * + * cmac = aes128_cmac(keyID, blocks[i].Buffer) + * + * \param[IN] micBxBuffer - Buffer containing the initial Bx block + * \param[IN] buffer - Data buffer + * \param[IN] size - Data buffer size + * \param[IN] keyID - Key identifier to determine the AES key to be used + * \param[OUT] cmac - Computed cmac + * \retval - Status of the operation + */ +static SecureElementStatus_t ComputeCmac( uint8_t* micBxBuffer, uint8_t* buffer, uint16_t size, KeyIdentifier_t keyID, + uint32_t* cmac ) +{ + if( ( buffer == NULL ) || ( cmac == NULL ) ) + { + return SECURE_ELEMENT_ERROR_NPE; + } + + uint8_t Cmac[16]; + AES_CMAC_CTX aesCmacCtx[1]; + + AES_CMAC_Init( aesCmacCtx ); + + Key_t* keyItem; + SecureElementStatus_t retval = GetKeyByID( keyID, &keyItem ); + + if( retval == SECURE_ELEMENT_SUCCESS ) + { + AES_CMAC_SetKey( aesCmacCtx, keyItem->KeyValue ); + + if( micBxBuffer != NULL ) + { + AES_CMAC_Update( aesCmacCtx, micBxBuffer, 16 ); + } + + AES_CMAC_Update( aesCmacCtx, buffer, size ); + + AES_CMAC_Final( Cmac, aesCmacCtx ); + + // Bring into the required format + *cmac = ( uint32_t )( ( uint32_t ) Cmac[3] << 24 | ( uint32_t ) Cmac[2] << 16 | ( uint32_t ) Cmac[1] << 8 | + ( uint32_t ) Cmac[0] ); + } + + return retval; +} + +/* + * API functions + */ + +SecureElementStatus_t SecureElementInit( SecureElementNvmData_t* nvm ) +{ + SecureElementNvmData_t seNvmInit = + { + /*! + * end-device IEEE EUI (big endian) + * + * \remark In this application the value is automatically generated by + * calling BoardGetUniqueId function + */ + .DevEui = LORAWAN_DEVICE_EUI, + /*! + * App/Join server IEEE EUI (big endian) + */ + .JoinEui = LORAWAN_JOIN_EUI, + /*! + * Secure-element pin (big endian) + */ + .Pin = SECURE_ELEMENT_PIN, + /*! + * LoRaWAN key list + */ + .KeyList = SOFT_SE_KEY_LIST + }; + + + if( nvm == NULL ) + { + return SECURE_ELEMENT_ERROR_NPE; + } + + // Initialize nvm pointer + SeNvm = nvm; + + // Initialize data + memcpy1( ( uint8_t* )SeNvm, ( uint8_t* )&seNvmInit, sizeof( seNvmInit ) ); + +#if !defined( SECURE_ELEMENT_PRE_PROVISIONED ) +#if( STATIC_DEVICE_EUI == 0 ) + // Get a DevEUI from MCU unique ID + SoftSeHalGetUniqueId( SeNvm->DevEui ); +#endif +#endif + return SECURE_ELEMENT_SUCCESS; +} + +SecureElementStatus_t SecureElementSetKey( KeyIdentifier_t keyID, uint8_t* key ) +{ + if( key == NULL ) + { + return SECURE_ELEMENT_ERROR_NPE; + } + + for( uint8_t i = 0; i < NUM_OF_KEYS; i++ ) + { + if( SeNvm->KeyList[i].KeyID == keyID ) + { + if( ( keyID == MC_KEY_0 ) || ( keyID == MC_KEY_1 ) || ( keyID == MC_KEY_2 ) || ( keyID == MC_KEY_3 ) ) + { // Decrypt the key if its a Mckey + SecureElementStatus_t retval = SECURE_ELEMENT_ERROR; + uint8_t decryptedKey[16] = { 0 }; + + retval = SecureElementAesEncrypt( key, 16, MC_KE_KEY, decryptedKey ); + + memcpy1( SeNvm->KeyList[i].KeyValue, decryptedKey, SE_KEY_SIZE ); + return retval; + } + else + { + memcpy1( SeNvm->KeyList[i].KeyValue, key, SE_KEY_SIZE ); + return SECURE_ELEMENT_SUCCESS; + } + } + } + + return SECURE_ELEMENT_ERROR_INVALID_KEY_ID; +} + +SecureElementStatus_t SecureElementComputeAesCmac( uint8_t* micBxBuffer, uint8_t* buffer, uint16_t size, + KeyIdentifier_t keyID, uint32_t* cmac ) +{ + if( keyID >= LORAMAC_CRYPTO_MULTICAST_KEYS ) + { + // Never accept multicast key identifier for cmac computation + return SECURE_ELEMENT_ERROR_INVALID_KEY_ID; + } + + return ComputeCmac( micBxBuffer, buffer, size, keyID, cmac ); +} + +SecureElementStatus_t SecureElementVerifyAesCmac( uint8_t* buffer, uint16_t size, uint32_t expectedCmac, + KeyIdentifier_t keyID ) +{ + if( buffer == NULL ) + { + return SECURE_ELEMENT_ERROR_NPE; + } + + SecureElementStatus_t retval = SECURE_ELEMENT_ERROR; + uint32_t compCmac = 0; + retval = ComputeCmac( NULL, buffer, size, keyID, &compCmac ); + if( retval != SECURE_ELEMENT_SUCCESS ) + { + return retval; + } + + if( expectedCmac != compCmac ) + { + retval = SECURE_ELEMENT_FAIL_CMAC; + } + + return retval; +} + +SecureElementStatus_t SecureElementAesEncrypt( uint8_t* buffer, uint16_t size, KeyIdentifier_t keyID, + uint8_t* encBuffer ) +{ + if( buffer == NULL || encBuffer == NULL ) + { + return SECURE_ELEMENT_ERROR_NPE; + } + + // Check if the size is divisible by 16, + if( ( size % 16 ) != 0 ) + { + return SECURE_ELEMENT_ERROR_BUF_SIZE; + } + + aes_context aesContext; + memset1( aesContext.ksch, '\0', 240 ); + + Key_t* pItem; + SecureElementStatus_t retval = GetKeyByID( keyID, &pItem ); + + if( retval == SECURE_ELEMENT_SUCCESS ) + { + aes_set_key( pItem->KeyValue, 16, &aesContext ); + + uint8_t block = 0; + + while( size != 0 ) + { + aes_encrypt( &buffer[block], &encBuffer[block], &aesContext ); + block = block + 16; + size = size - 16; + } + } + return retval; +} + +SecureElementStatus_t SecureElementDeriveAndStoreKey( uint8_t* input, KeyIdentifier_t rootKeyID, + KeyIdentifier_t targetKeyID ) +{ + if( input == NULL ) + { + return SECURE_ELEMENT_ERROR_NPE; + } + + SecureElementStatus_t retval = SECURE_ELEMENT_ERROR; + uint8_t key[16] = { 0 }; + + // In case of MC_KE_KEY, only McRootKey can be used as root key + if( targetKeyID == MC_KE_KEY ) + { + if( rootKeyID != MC_ROOT_KEY ) + { + return SECURE_ELEMENT_ERROR_INVALID_KEY_ID; + } + } + + // Derive key + retval = SecureElementAesEncrypt( input, 16, rootKeyID, key ); + if( retval != SECURE_ELEMENT_SUCCESS ) + { + return retval; + } + + // Store key + retval = SecureElementSetKey( targetKeyID, key ); + if( retval != SECURE_ELEMENT_SUCCESS ) + { + return retval; + } + + return SECURE_ELEMENT_SUCCESS; +} + +SecureElementStatus_t SecureElementProcessJoinAccept( JoinReqIdentifier_t joinReqType, uint8_t* joinEui, + uint16_t devNonce, uint8_t* encJoinAccept, + uint8_t encJoinAcceptSize, uint8_t* decJoinAccept, + uint8_t* versionMinor ) +{ + if( ( encJoinAccept == NULL ) || ( decJoinAccept == NULL ) || ( versionMinor == NULL ) ) + { + return SECURE_ELEMENT_ERROR_NPE; + } + + // Check that frame size isn't bigger than a JoinAccept with CFList size + if( encJoinAcceptSize > LORAMAC_JOIN_ACCEPT_FRAME_MAX_SIZE ) + { + return SECURE_ELEMENT_ERROR_BUF_SIZE; + } + + // Determine decryption key + KeyIdentifier_t encKeyID = NWK_KEY; + + if( joinReqType != JOIN_REQ ) + { + encKeyID = J_S_ENC_KEY; + } + + memcpy1( decJoinAccept, encJoinAccept, encJoinAcceptSize ); + + // Decrypt JoinAccept, skip MHDR + if( SecureElementAesEncrypt( encJoinAccept + LORAMAC_MHDR_FIELD_SIZE, encJoinAcceptSize - LORAMAC_MHDR_FIELD_SIZE, + encKeyID, decJoinAccept + LORAMAC_MHDR_FIELD_SIZE ) != SECURE_ELEMENT_SUCCESS ) + { + return SECURE_ELEMENT_FAIL_ENCRYPT; + } + + *versionMinor = ( ( decJoinAccept[11] & 0x80 ) == 0x80 ) ? 1 : 0; + + uint32_t mic = 0; + + mic = ( ( uint32_t ) decJoinAccept[encJoinAcceptSize - LORAMAC_MIC_FIELD_SIZE] << 0 ); + mic |= ( ( uint32_t ) decJoinAccept[encJoinAcceptSize - LORAMAC_MIC_FIELD_SIZE + 1] << 8 ); + mic |= ( ( uint32_t ) decJoinAccept[encJoinAcceptSize - LORAMAC_MIC_FIELD_SIZE + 2] << 16 ); + mic |= ( ( uint32_t ) decJoinAccept[encJoinAcceptSize - LORAMAC_MIC_FIELD_SIZE + 3] << 24 ); + + // - Header buffer to be used for MIC computation + // - LoRaWAN 1.0.x : micHeader = [MHDR(1)] + // - LoRaWAN 1.1.x : micHeader = [JoinReqType(1), JoinEUI(8), DevNonce(2), MHDR(1)] + + // Verify mic + if( *versionMinor == 0 ) + { + // For LoRaWAN 1.0.x + // cmac = aes128_cmac(NwkKey, MHDR | JoinNonce | NetID | DevAddr | DLSettings | RxDelay | CFList | + // CFListType) + if( SecureElementVerifyAesCmac( decJoinAccept, ( encJoinAcceptSize - LORAMAC_MIC_FIELD_SIZE ), mic, NWK_KEY ) != + SECURE_ELEMENT_SUCCESS ) + { + return SECURE_ELEMENT_FAIL_CMAC; + } + } +#if( USE_LRWAN_1_1_X_CRYPTO == 1 ) + else if( *versionMinor == 1 ) + { + uint8_t micHeader11[JOIN_ACCEPT_MIC_COMPUTATION_OFFSET] = { 0 }; + uint16_t bufItr = 0; + + micHeader11[bufItr++] = ( uint8_t ) joinReqType; + + memcpyr( micHeader11 + bufItr, joinEui, LORAMAC_JOIN_EUI_FIELD_SIZE ); + bufItr += LORAMAC_JOIN_EUI_FIELD_SIZE; + + micHeader11[bufItr++] = devNonce & 0xFF; + micHeader11[bufItr++] = ( devNonce >> 8 ) & 0xFF; + + // For LoRaWAN 1.1.x and later: + // cmac = aes128_cmac(JSIntKey, JoinReqType | JoinEUI | DevNonce | MHDR | JoinNonce | NetID | DevAddr | + // DLSettings | RxDelay | CFList | CFListType) + // Prepare the msg for integrity check (adding JoinReqType, JoinEUI and DevNonce) + uint8_t localBuffer[LORAMAC_JOIN_ACCEPT_FRAME_MAX_SIZE + JOIN_ACCEPT_MIC_COMPUTATION_OFFSET] = { 0 }; + + memcpy1( localBuffer, micHeader11, JOIN_ACCEPT_MIC_COMPUTATION_OFFSET ); + memcpy1( localBuffer + JOIN_ACCEPT_MIC_COMPUTATION_OFFSET - 1, decJoinAccept, encJoinAcceptSize ); + + if( SecureElementVerifyAesCmac( localBuffer, + encJoinAcceptSize + JOIN_ACCEPT_MIC_COMPUTATION_OFFSET - + LORAMAC_MHDR_FIELD_SIZE - LORAMAC_MIC_FIELD_SIZE, + mic, J_S_INT_KEY ) != SECURE_ELEMENT_SUCCESS ) + { + return SECURE_ELEMENT_FAIL_CMAC; + } + } +#endif + else + { + return SECURE_ELEMENT_ERROR_INVALID_LORAWAM_SPEC_VERSION; + } + + return SECURE_ELEMENT_SUCCESS; +} + +SecureElementStatus_t SecureElementSetDevEui( uint8_t* devEui ) +{ + if( devEui == NULL ) + { + return SECURE_ELEMENT_ERROR_NPE; + } + memcpy1( SeNvm->DevEui, devEui, SE_EUI_SIZE ); + return SECURE_ELEMENT_SUCCESS; +} + +uint8_t* SecureElementGetDevEui( void ) +{ + return SeNvm->DevEui; +} + +SecureElementStatus_t SecureElementSetJoinEui( uint8_t* joinEui ) +{ + if( joinEui == NULL ) + { + return SECURE_ELEMENT_ERROR_NPE; + } + memcpy1( SeNvm->JoinEui, joinEui, SE_EUI_SIZE ); + return SECURE_ELEMENT_SUCCESS; +} + +uint8_t* SecureElementGetJoinEui( void ) +{ + return SeNvm->JoinEui; +} + +SecureElementStatus_t SecureElementSetPin( uint8_t* pin ) +{ + if( pin == NULL ) + { + return SECURE_ELEMENT_ERROR_NPE; + } + + memcpy1( SeNvm->Pin, pin, SE_PIN_SIZE ); + return SECURE_ELEMENT_SUCCESS; +} + +uint8_t* SecureElementGetPin( void ) +{ + return SeNvm->Pin; +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/spi-board.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/spi-board.c new file mode 100644 index 0000000000..cc10dd00f5 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/spi-board.c @@ -0,0 +1,69 @@ +/*! + * \file spi-board.c + * + * \brief Target board SPI driver implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * \endcode + * + * \author Alistair Jordan + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + */ + +#include +#include +#include "utilities.h" +#include "board.h" +#include "spi-board.h" +#include "spidev_lib/spidev_lib.h" + +#define SPI_FILE "/dev/spidev2.0" + +spi_config_t spi_config; + +void SpiInit( Spi_t *obj, SpiId_t spiId) +{ + spi_config.mode=0; + spi_config.speed=1000000; + spi_config.delay=0; + spi_config.bits_per_word=8; + obj->SpiId = spiId; + obj->SpiFd = spi_open(SPI_FILE,spi_config); +} + +void SpiDeInit( Spi_t *obj ) +{ + spi_close(obj->SpiFd); +} + +void SpiFormat( Spi_t *obj, int8_t bits, int8_t cpol, int8_t cpha, int8_t slave ) +{ + spi_config.bits_per_word=bits; +} + +void SpiFrequency( Spi_t *obj, uint32_t hz ) +{ + spi_config.speed=hz; +} + +uint16_t SpiInOut( Spi_t *obj, uint16_t outData ) +{ + uint8_t tx_buffer[1]; + uint8_t rx_buffer[1]; + spi_xfer(obj->SpiFd,tx_buffer,strlen(tx_buffer),rx_buffer,strlen(tx_buffer)); + return( rx_buffer[0] ); //hmmmmmmmmmmmm this isn't quite 16bits word size........ +} + diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/spi-board.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/spi-board.h new file mode 100644 index 0000000000..7c8ee52f08 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/spi-board.h @@ -0,0 +1,41 @@ +/*! + * \file spi-board.h + * + * \brief Target board SPI driver implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * \endcode + * + * \author Alistair Jordan + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + */ +#ifndef __SPI_BOARD_H__ +#define __SPI_BOARD_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "spi.h" + +// An Spi.c file has to be implmented under system directory. + +#ifdef __cplusplus +} +#endif + +#endif // __SPI_BOARD_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/spi.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/spi.h new file mode 100644 index 0000000000..b9f5500b50 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/spi.h @@ -0,0 +1,106 @@ +/*! + * \file spi-board.h + * + * \brief SPI driver implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * \endcode + * + * \author Alistair Jordan + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + */ +#ifndef __SPI_H__ +#define __SPI_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*! + * SPI peripheral ID + */ +typedef enum +{ + SPI_1, + SPI_2, +}SpiId_t; + +/*! + * SPI object type definition + */ +typedef struct Spi_s +{ + SpiId_t SpiId; + int SpiFd; +}Spi_t; + +/*! + * \brief Initializes the SPI object and MCU peripheral + * + * \remark When NSS pin is software controlled set the pin name to NC otherwise + * set the pin name to be used. + * + * \param [IN] obj SPI object + * \param [IN] mosi SPI MOSI pin name to be used + * \param [IN] miso SPI MISO pin name to be used + * \param [IN] sclk SPI SCLK pin name to be used + * \param [IN] nss SPI NSS pin name to be used + */ +void SpiInit( Spi_t *obj, SpiId_t spiId); + +/*! + * \brief De-initializes the SPI object and MCU peripheral + * + * \param [IN] obj SPI object + */ +void SpiDeInit( Spi_t *obj ); + +/*! + * \brief Configures the SPI peripheral + * + * \remark Slave mode isn't currently handled + * + * \param [IN] obj SPI object + * \param [IN] bits Number of bits to be used. [8 or 16] + * \param [IN] cpol Clock polarity + * \param [IN] cpha Clock phase + * \param [IN] slave When set the peripheral acts in slave mode + */ +void SpiFormat( Spi_t *obj, int8_t bits, int8_t cpol, int8_t cpha, int8_t slave ); + +/*! + * \brief Sets the SPI speed + * + * \param [IN] obj SPI object + * \param [IN] hz SPI clock frequency in hz + */ +void SpiFrequency( Spi_t *obj, uint32_t hz ); + +/*! + * \brief Sends outData and receives inData + * + * \param [IN] obj SPI object + * \param [IN] outData Byte to be sent + * \retval inData Received byte. + */ +uint16_t SpiInOut( Spi_t *obj, uint16_t outData ); + +#ifdef __cplusplus +} +#endif + +#endif // __SPI_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/spidev_lib/README.md b/sysdrv/tools/board/lorawan-bridge/LoraMac/spidev_lib/README.md new file mode 100644 index 0000000000..b9595eca18 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/spidev_lib/README.md @@ -0,0 +1 @@ +https://github.com/milekium/spidev-lib/blob/master/README.md \ No newline at end of file diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/spidev_lib/spidev_lib.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/spidev_lib/spidev_lib.c new file mode 100644 index 0000000000..ce0a789272 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/spidev_lib/spidev_lib.c @@ -0,0 +1,111 @@ +/* + * + * This file is based on of pyA20. + * spi_lib.c python SPI extension. + * + * Copyright (c) 2014 Stefan Mavrodiev @ OLIMEX LTD, + * + * pyA20 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * Adpated by Philippe Van Hecke + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "spidev_lib.h" + +int spi_open(char *device, spi_config_t config) { + int fd; + + /* Open block device */ + fd = open(device, O_RDWR); + if (fd < 0) { + return fd; + } + + /* Set SPI_POL and SPI_PHA */ + if (ioctl(fd, SPI_IOC_WR_MODE, &config.mode) < 0) { + return -1; + } + if (ioctl(fd, SPI_IOC_RD_MODE, &config.mode) < 0) { + return -1; + } + + /* Set bits per word*/ + if (ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &config.bits_per_word) < 0) { + return -1; + } + if (ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &config.bits_per_word) < 0) { + return -1; + } + + /* Set SPI speed*/ + if (ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &config.speed) < 0) { + return -1; + } + if (ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &config.speed) < 0) { + return -1; + } + + /* Return file descriptor */ + return fd; +} + +int spi_close(int fd) { + return close(fd); +} + +int spi_xfer(int fd, uint8_t *tx_buffer, uint8_t tx_len, uint8_t *rx_buffer, uint8_t rx_len){ + struct spi_ioc_transfer spi_message[1]; + memset(spi_message, 0, sizeof(spi_message)); + + spi_message[0].rx_buf = (unsigned long)rx_buffer; + spi_message[0].tx_buf = (unsigned long)tx_buffer; + spi_message[0].len = tx_len; + + return ioctl(fd, SPI_IOC_MESSAGE(1), spi_message); +} + +int spi_read(int fd, uint8_t *rx_buffer, uint8_t rx_len){ + struct spi_ioc_transfer spi_message[1]; + memset(spi_message, 0, sizeof(spi_message)); + + spi_message[0].rx_buf = (unsigned long)rx_buffer; + spi_message[0].len = rx_len; + + + return ioctl(fd, SPI_IOC_MESSAGE(1), spi_message); +} + +int spi_write(int fd, uint8_t *tx_buffer, uint8_t tx_len){ + struct spi_ioc_transfer spi_message[1]; + memset(spi_message, 0, sizeof(spi_message)); + + spi_message[0].tx_buf = (unsigned long)tx_buffer; + spi_message[0].len = tx_len; + + return ioctl(fd, SPI_IOC_MESSAGE(1), spi_message); +} + diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/spidev_lib/spidev_lib.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/spidev_lib/spidev_lib.h new file mode 100644 index 0000000000..d2a991e546 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/spidev_lib/spidev_lib.h @@ -0,0 +1,50 @@ +/* + * + * This file is part of pyA20. + * spi_lib.c is python SPI extension. + * + * Copyright (c) 2014 Stefan Mavrodiev @ OLIMEX LTD, + * + * pyA20 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef _SPI_LIB_H +#define _SPI_LIB_H +#endif + +#include +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint8_t mode; + uint8_t bits_per_word; + uint32_t speed; + uint16_t delay; +} spi_config_t; + +int spi_open(char *device, spi_config_t config); +int spi_close(int fd); +int spi_xfer(int fd, uint8_t *tx_buffer, uint8_t tx_len, uint8_t *rx_buffer, uint8_t rx_len); +int spi_read(int fd, uint8_t *rx_buffer, uint8_t rx_len); +int spi_write(int fd, uint8_t *tx_buffer, uint8_t tx_len); +#ifdef __cplusplus +} +#endif + + + diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/sx1276-board.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/sx1276-board.c new file mode 100644 index 0000000000..7ef0787ad5 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/sx1276-board.c @@ -0,0 +1,344 @@ +/*! + * \file sx1276-board.c + * + * \brief Target board SX1276 driver implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * \endcode + * + * \author Alistair Jordan + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + */ +#include +#include "utilities.h" +#include "board-config.h" +//#include "delay.h" +#include "radio.h" +#include "sx1276-board.h" + +/*! + * \brief Gets the board PA selection configuration + * + * \param [IN] power Selects the right PA according to the wanted power. + * \retval PaSelect RegPaConfig PaSelect value + */ +static uint8_t SX1276GetPaSelect( int8_t power ); + +/*! + * Flag used to set the RF switch control pins in low power mode when the radio is not active. + */ +static bool RadioIsActive = false; + +/*! + * Radio driver structure initialization + */ +const struct Radio_s Radio = +{ + SX1276Init, + SX1276GetStatus, + SX1276SetModem, + SX1276SetChannel, + SX1276IsChannelFree, + SX1276Random, + SX1276SetRxConfig, + SX1276SetTxConfig, + SX1276CheckRfFrequency, + SX1276GetTimeOnAir, + SX1276Send, + SX1276SetSleep, + SX1276SetStby, + SX1276SetRx, + SX1276StartCad, + SX1276SetTxContinuousWave, + SX1276ReadRssi, + SX1276Write, + SX1276Read, + SX1276WriteBuffer, + SX1276ReadBuffer, + SX1276SetMaxPayloadLength, + SX1276SetPublicNetwork, + SX1276GetWakeupTime, + NULL, // void ( *IrqProcess )( void ) + NULL, // void ( *RxBoosted )( uint32_t timeout ) - SX126x Only + NULL, // void ( *SetRxDutyCycle )( uint32_t rxTime, uint32_t sleepTime ) - SX126x Only +}; + +// /*! +// * TCXO power control pin +// */ +// Gpio_t TcxoPower; + +// /*! +// * Antenna switch GPIO pins objects +// */ +// Gpio_t AntSwitchRx; +// Gpio_t AntSwitchTxBoost; +// Gpio_t AntSwitchTxRfo; + +/*! + * Debug GPIO pins objects + */ +#if defined( USE_RADIO_DEBUG ) +Gpio_t DbgPinTx; +Gpio_t DbgPinRx; +#endif + +void SX1276IoInit( void ) +{ + // GpioInit( &SX1276.Spi.Nss, RADIO_NSS, PIN_OUTPUT, PIN_PUSH_PULL, PIN_NO_PULL, 1 ); + + // GpioInit( &SX1276.DIO0, RADIO_DIO_0, PIN_INPUT, PIN_PUSH_PULL, PIN_PULL_UP, 0 ); + // GpioInit( &SX1276.DIO1, RADIO_DIO_1, PIN_INPUT, PIN_PUSH_PULL, PIN_PULL_UP, 0 ); + // GpioInit( &SX1276.DIO2, RADIO_DIO_2, PIN_INPUT, PIN_PUSH_PULL, PIN_PULL_UP, 0 ); + // GpioInit( &SX1276.DIO3, RADIO_DIO_3, PIN_INPUT, PIN_PUSH_PULL, PIN_PULL_UP, 0 ); + // GpioInit( &SX1276.DIO4, RADIO_DIO_4, PIN_INPUT, PIN_PUSH_PULL, PIN_PULL_UP, 0 ); + // GpioInit( &SX1276.DIO5, RADIO_DIO_5, PIN_INPUT, PIN_PUSH_PULL, PIN_PULL_UP, 0 ); +} + +void SX1276IoIrqInit( DioIrqHandler **irqHandlers ) +{ + // GpioSetInterrupt( &SX1276.DIO0, IRQ_RISING_EDGE, IRQ_HIGH_PRIORITY, irqHandlers[0] ); + // GpioSetInterrupt( &SX1276.DIO1, IRQ_RISING_FALLING_EDGE, IRQ_HIGH_PRIORITY, irqHandlers[1] ); + // GpioSetInterrupt( &SX1276.DIO2, IRQ_RISING_EDGE, IRQ_HIGH_PRIORITY, irqHandlers[2] ); + // GpioSetInterrupt( &SX1276.DIO3, IRQ_RISING_EDGE, IRQ_HIGH_PRIORITY, irqHandlers[3] ); + // GpioSetInterrupt( &SX1276.DIO4, IRQ_RISING_EDGE, IRQ_HIGH_PRIORITY, irqHandlers[4] ); + // GpioSetInterrupt( &SX1276.DIO5, IRQ_RISING_EDGE, IRQ_HIGH_PRIORITY, irqHandlers[5] ); +} + +void SX1276IoDeInit( void ) +{ + // GpioInit( &SX1276.Spi.Nss, RADIO_NSS, PIN_OUTPUT, PIN_PUSH_PULL, PIN_NO_PULL, 1 ); + + // GpioInit( &SX1276.DIO0, RADIO_DIO_0, PIN_INPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 ); + // GpioInit( &SX1276.DIO1, RADIO_DIO_1, PIN_INPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 ); + // GpioInit( &SX1276.DIO2, RADIO_DIO_2, PIN_INPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 ); + // GpioInit( &SX1276.DIO3, RADIO_DIO_3, PIN_INPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 ); + // GpioInit( &SX1276.DIO4, RADIO_DIO_4, PIN_INPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 ); + // GpioInit( &SX1276.DIO5, RADIO_DIO_5, PIN_INPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 ); +} + +void SX1276IoDbgInit( void ) +{ +// #if defined( USE_RADIO_DEBUG ) +// GpioInit( &DbgPinTx, RADIO_DBG_PIN_TX, PIN_OUTPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 ); +// GpioInit( &DbgPinRx, RADIO_DBG_PIN_RX, PIN_OUTPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 ); +// #endif +} + +void SX1276IoTcxoInit( void ) +{ + // GpioInit( &TcxoPower, RADIO_TCXO_POWER, PIN_OUTPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 ); +} + +void SX1276SetBoardTcxo( uint8_t state ) +{ + // if( state == true ) + // { + // if( GpioRead( &TcxoPower ) == 0 ) + // { // TCXO OFF power it up. + // // Power ON the TCXO + // GpioWrite( &TcxoPower, 1 ); + // DelayMs( BOARD_TCXO_WAKEUP_TIME ); + // } + // } + // else + // { + // // Power OFF the TCXO + // GpioWrite( &TcxoPower, 0 ); + // } +} + +uint32_t SX1276GetBoardTcxoWakeupTime( void ) +{ + return BOARD_TCXO_WAKEUP_TIME; +} + +void SX1276Reset( void ) +{ + // // Enables the TCXO if available on the board design + // SX1276SetBoardTcxo( true ); + + // // Set RESET pin to 0 + // GpioInit( &SX1276.Reset, RADIO_RESET, PIN_OUTPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 ); + + // // Wait 1 ms + // DelayMs( 1 ); + + // // Configure RESET as input + // GpioInit( &SX1276.Reset, RADIO_RESET, PIN_INPUT, PIN_PUSH_PULL, PIN_NO_PULL, 1 ); + + // // Wait 6 ms + // DelayMs( 6 ); +} + +void SX1276SetRfTxPower( int8_t power ) +{ + uint8_t paConfig = 0; + uint8_t paDac = 0; + + paConfig = SX1276Read( REG_PACONFIG ); + paDac = SX1276Read( REG_PADAC ); + + paConfig = ( paConfig & RF_PACONFIG_PASELECT_MASK ) | SX1276GetPaSelect( power ); + + if( ( paConfig & RF_PACONFIG_PASELECT_PABOOST ) == RF_PACONFIG_PASELECT_PABOOST ) + { + if( power > 17 ) + { + paDac = ( paDac & RF_PADAC_20DBM_MASK ) | RF_PADAC_20DBM_ON; + } + else + { + paDac = ( paDac & RF_PADAC_20DBM_MASK ) | RF_PADAC_20DBM_OFF; + } + if( ( paDac & RF_PADAC_20DBM_ON ) == RF_PADAC_20DBM_ON ) + { + if( power < 5 ) + { + power = 5; + } + if( power > 20 ) + { + power = 20; + } + paConfig = ( paConfig & RF_PACONFIG_OUTPUTPOWER_MASK ) | ( uint8_t )( ( uint16_t )( power - 5 ) & 0x0F ); + } + else + { + if( power < 2 ) + { + power = 2; + } + if( power > 17 ) + { + power = 17; + } + paConfig = ( paConfig & RF_PACONFIG_OUTPUTPOWER_MASK ) | ( uint8_t )( ( uint16_t )( power - 2 ) & 0x0F ); + } + } + else + { + if( power > 0 ) + { + if( power > 15 ) + { + power = 15; + } + paConfig = ( paConfig & RF_PACONFIG_MAX_POWER_MASK & RF_PACONFIG_OUTPUTPOWER_MASK ) | ( 7 << 4 ) | ( power ); + } + else + { + if( power < -4 ) + { + power = -4; + } + paConfig = ( paConfig & RF_PACONFIG_MAX_POWER_MASK & RF_PACONFIG_OUTPUTPOWER_MASK ) | ( 0 << 4 ) | ( power + 4 ); + } + } + SX1276Write( REG_PACONFIG, paConfig ); + SX1276Write( REG_PADAC, paDac ); +} + +static uint8_t SX1276GetPaSelect( int8_t power ) +{ + if( power > 14 ) + { + return RF_PACONFIG_PASELECT_PABOOST; + } + else + { + return RF_PACONFIG_PASELECT_RFO; + } +} + +void SX1276SetAntSwLowPower( bool status ) +{ + if( RadioIsActive != status ) + { + RadioIsActive = status; + + if( status == false ) + { + SX1276AntSwInit( ); + } + else + { + SX1276AntSwDeInit( ); + } + } +} + +void SX1276AntSwInit( void ) +{ + //GpioInit( &AntSwitchRx, RADIO_ANT_SWITCH_RX, PIN_OUTPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 ); + //GpioInit( &AntSwitchTxBoost, RADIO_ANT_SWITCH_TX_BOOST, PIN_OUTPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 ); + //GpioInit( &AntSwitchTxRfo, RADIO_ANT_SWITCH_TX_RFO, PIN_OUTPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 ); +} + +void SX1276AntSwDeInit( void ) +{ + //GpioInit( &AntSwitchRx, RADIO_ANT_SWITCH_RX, PIN_ANALOGIC, PIN_OPEN_DRAIN, PIN_NO_PULL, 0 ); + //GpioInit( &AntSwitchTxBoost, RADIO_ANT_SWITCH_TX_BOOST, PIN_ANALOGIC, PIN_OPEN_DRAIN, PIN_NO_PULL, 0 ); + //GpioInit( &AntSwitchTxRfo, RADIO_ANT_SWITCH_TX_RFO, PIN_ANALOGIC, PIN_OPEN_DRAIN, PIN_NO_PULL, 0 ); +} + +void SX1276SetAntSw( uint8_t opMode ) +{ + // uint8_t paConfig = SX1276Read( REG_PACONFIG ); + // switch( opMode ) + // { + // case RFLR_OPMODE_TRANSMITTER: + // if( ( paConfig & RF_PACONFIG_PASELECT_PABOOST ) == RF_PACONFIG_PASELECT_PABOOST ) + // { + // GpioWrite( &AntSwitchTxBoost, 1 ); + // } + // else + // { + // GpioWrite( &AntSwitchTxRfo, 1 ); + // } + // break; + // case RFLR_OPMODE_RECEIVER: + // case RFLR_OPMODE_RECEIVER_SINGLE: + // case RFLR_OPMODE_CAD: + // default: + // GpioWrite( &AntSwitchRx, 1 ); + // break; + // } +} + +bool SX1276CheckRfFrequency( uint32_t frequency ) +{ + // Implement check. Currently all frequencies are supported + return true; +} + +uint32_t SX1276GetDio1PinState( void ) +{ +// return GpioRead( &SX1276.DIO1 ); + return 0; //Not Support +} + +// #if defined( USE_RADIO_DEBUG ) +// void SX1276DbgPinTxWrite( uint8_t state ) +// { +// GpioWrite( &DbgPinTx, state ); +// } + +// void SX1276DbgPinRxWrite( uint8_t state ) +// { +// GpioWrite( &DbgPinRx, state ); +// } +// #endif diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/sx1276-board.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/sx1276-board.h new file mode 100644 index 0000000000..d72b007602 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/sx1276-board.h @@ -0,0 +1,186 @@ +/*! + * \file sx1276-board.h + * + * \brief Target board SX1276 driver implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * \endcode + * + * \author Alistair Jordan + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + */ +#ifndef __SX1276_BOARD_H__ +#define __SX1276_BOARD_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include +#include "sx1276.h" + +/*! + * \brief Radio hardware registers initialization definition + * + * \remark Can be automatically generated by the SX1276 GUI (not yet implemented) + */ +#define RADIO_INIT_REGISTERS_VALUE \ +{ \ + { MODEM_FSK , REG_LNA , 0x23 },\ + { MODEM_FSK , REG_RXCONFIG , 0x1E },\ + { MODEM_FSK , REG_RSSICONFIG , 0xD2 },\ + { MODEM_FSK , REG_AFCFEI , 0x01 },\ + { MODEM_FSK , REG_PREAMBLEDETECT , 0xAA },\ + { MODEM_FSK , REG_OSC , 0x07 },\ + { MODEM_FSK , REG_SYNCCONFIG , 0x12 },\ + { MODEM_FSK , REG_SYNCVALUE1 , 0xC1 },\ + { MODEM_FSK , REG_SYNCVALUE2 , 0x94 },\ + { MODEM_FSK , REG_SYNCVALUE3 , 0xC1 },\ + { MODEM_FSK , REG_PACKETCONFIG1 , 0xD8 },\ + /* FIFO threshold set to 32 (31+1) */ \ + { MODEM_FSK , REG_FIFOTHRESH , 0x9F },\ + { MODEM_FSK , REG_IMAGECAL , 0x02 },\ + { MODEM_FSK , REG_DIOMAPPING1 , 0x00 },\ + { MODEM_FSK , REG_DIOMAPPING2 , 0x30 },\ + { MODEM_LORA, REG_LR_PAYLOADMAXLENGTH, 0x40 },\ +} \ + +#define RF_MID_BAND_THRESH 525000000 + +/*! + * \brief Initializes the radio I/Os pins interface + */ +void SX1276IoInit( void ); + +/*! + * \brief Initializes DIO IRQ handlers + * + * \param [IN] irqHandlers Array containing the IRQ callback functions + */ +void SX1276IoIrqInit( DioIrqHandler **irqHandlers ); + +/*! + * \brief De-initializes the radio I/Os pins interface. + * + * \remark Useful when going in MCU low power modes + */ +void SX1276IoDeInit( void ); + +/*! + * \brief Initializes the TCXO power pin. + */ +void SX1276IoTcxoInit( void ); + +/*! + * \brief Initializes the radio debug pins. + */ +void SX1276IoDbgInit( void ); + +/*! + * \brief Resets the radio + */ +void SX1276Reset( void ); + +/*! + * \brief Sets the radio output power. + * + * \param [IN] power Sets the RF output power + */ +void SX1276SetRfTxPower( int8_t power ); + +/*! + * \brief Set the RF Switch I/Os pins in low power mode + * + * \param [IN] status enable or disable + */ +void SX1276SetAntSwLowPower( bool status ); + +/*! + * \brief Initializes the RF Switch I/Os pins interface + */ +void SX1276AntSwInit( void ); + +/*! + * \brief De-initializes the RF Switch I/Os pins interface + * + * \remark Needed to decrease the power consumption in MCU low power modes + */ +void SX1276AntSwDeInit( void ); + +/*! + * \brief Controls the antenna switch if necessary. + * + * \remark see errata note + * + * \param [IN] opMode Current radio operating mode + */ +void SX1276SetAntSw( uint8_t opMode ); + +/*! + * \brief Checks if the given RF frequency is supported by the hardware + * + * \param [IN] frequency RF frequency to be checked + * \retval isSupported [true: supported, false: unsupported] + */ +bool SX1276CheckRfFrequency( uint32_t frequency ); + +/*! + * \brief Enables/disables the TCXO if available on board design. + * + * \param [IN] state TCXO enabled when true and disabled when false. + */ +void SX1276SetBoardTcxo( uint8_t state ); + +/*! + * \brief Gets the Defines the time required for the TCXO to wakeup [ms]. + * + * \retval time Board TCXO wakeup time in ms. + */ +uint32_t SX1276GetBoardTcxoWakeupTime( void ); + +/*! + * \brief Gets current state of DIO1 pin state (FifoLevel). + * + * \retval state DIO1 pin current state. + */ +uint32_t SX1276GetDio1PinState( void ); + +/*! + * \brief Writes new Tx debug pin state + * + * \param [IN] state Debug pin state + */ +void SX1276DbgPinTxWrite( uint8_t state ); + +/*! + * \brief Writes new Rx debug pin state + * + * \param [IN] state Debug pin state + */ +void SX1276DbgPinRxWrite( uint8_t state ); + +/*! + * Radio hardware and global parameters + */ +extern SX1276_t SX1276; + +#ifdef __cplusplus +} +#endif + +#endif // __SX1276_BOARD_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/sx1276.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/sx1276.c new file mode 100644 index 0000000000..a2d0351ee0 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/sx1276.c @@ -0,0 +1,2024 @@ +/*! + * \file sx1276.c + * + * \brief SX1276 driver implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * \endcode + * + * \author Alistair Jordan + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Wael Guibene ( Semtech ) + */ +#include +#include +#include "utilities.h" +#include "timer.h" +#include "radio.h" +#include "delay.h" +#include "sx1276.h" +#include "sx1276-board.h" + +/*! + * \brief Internal frequency of the radio + */ +#define SX1276_XTAL_FREQ 32000000UL + +/*! + * \brief Scaling factor used to perform fixed-point operations + */ +#define SX1276_PLL_STEP_SHIFT_AMOUNT ( 8 ) + +/*! + * \brief PLL step - scaled with SX1276_PLL_STEP_SHIFT_AMOUNT + */ +#define SX1276_PLL_STEP_SCALED ( SX1276_XTAL_FREQ >> ( 19 - SX1276_PLL_STEP_SHIFT_AMOUNT ) ) + +/*! + * \brief Radio buffer size + */ +#define RX_TX_BUFFER_SIZE 256 + +/* + * Local types definition + */ + +/*! + * Radio registers definition + */ +typedef struct +{ + RadioModems_t Modem; + uint8_t Addr; + uint8_t Value; +}RadioRegisters_t; + +/*! + * FSK bandwidth definition + */ +typedef struct +{ + uint32_t bandwidth; + uint8_t RegValue; +}FskBandwidth_t; + + +/* + * Private functions prototypes + */ + +/*! + * Performs the Rx chain calibration for LF and HF bands + * \remark Must be called just after the reset so all registers are at their + * default values + */ +static void RxChainCalibration( void ); + +/*! + * \brief Sets the SX1276 in transmission mode for the given time + * \param [IN] timeout Transmission timeout [ms] [0: continuous, others timeout] + */ +static void SX1276SetTx( uint32_t timeout ); + +/*! + * \brief Writes the buffer contents to the SX1276 FIFO + * + * \param [IN] buffer Buffer containing data to be put on the FIFO. + * \param [IN] size Number of bytes to be written to the FIFO + */ +static void SX1276WriteFifo( uint8_t *buffer, uint8_t size ); + +/*! + * \brief Reads the contents of the SX1276 FIFO + * + * \param [OUT] buffer Buffer where to copy the FIFO read data. + * \param [IN] size Number of bytes to be read from the FIFO + */ +static void SX1276ReadFifo( uint8_t *buffer, uint8_t size ); + +/*! + * \brief Sets the SX1276 operating mode + * + * \param [IN] opMode New operating mode + */ +static void SX1276SetOpMode( uint8_t opMode ); + +/*! + * \brief Get frequency in Hertz for a given number of PLL steps + * + * \param [in] pllSteps Number of PLL steps + * + * \returns Frequency in Hertz + */ +static uint32_t SX1276ConvertPllStepToFreqInHz( uint32_t pllSteps ); + +/*! + * \brief Get the number of PLL steps for a given frequency in Hertz + * + * \param [in] freqInHz Frequency in Hertz + * + * \returns Number of PLL steps + */ +static uint32_t SX1276ConvertFreqInHzToPllStep( uint32_t freqInHz ); + +/*! + * \brief Get the parameter corresponding to a FSK Rx bandwith immediately above the minimum requested one. + * + * \param [in] bw Minimum required bandwith in Hz + * + * \returns parameter + */ +static uint8_t GetFskBandwidthRegValue( uint32_t bw ); + +/*! + * \brief Get the actual value in Hertz of a given LoRa bandwidth + * + * \param [in] bw LoRa bandwidth parameter + * + * \returns Actual LoRa bandwidth in Hertz + */ +static uint32_t SX1276GetLoRaBandwidthInHz( uint32_t bw ); + +/*! + * Compute the numerator for GFSK time-on-air computation. + * + * \remark To get the actual time-on-air in second, this value has to be divided by the GFSK bitrate in bits per + * second. + * + * \param [in] preambleLen + * \param [in] fixLen + * \param [in] payloadLen + * \param [in] crcOn + * + * \returns GFSK time-on-air numerator + */ +static uint32_t SX1276GetGfskTimeOnAirNumerator( uint16_t preambleLen, bool fixLen, + uint8_t payloadLen, bool crcOn ); + +/*! + * Compute the numerator for LoRa time-on-air computation. + * + * \remark To get the actual time-on-air in second, this value has to be divided by the LoRa bandwidth in Hertz. + * + * \param [in] bandwidth + * \param [in] datarate + * \param [in] coderate + * \param [in] preambleLen + * \param [in] fixLen + * \param [in] payloadLen + * \param [in] crcOn + * + * \returns LoRa time-on-air numerator + */ +static uint32_t SX1276GetLoRaTimeOnAirNumerator( uint32_t bandwidth, + uint32_t datarate, uint8_t coderate, + uint16_t preambleLen, bool fixLen, uint8_t payloadLen, + bool crcOn ); + +/* + * SX1276 DIO IRQ callback functions prototype + */ + +/*! + * \brief DIO 0 IRQ callback + */ +static void SX1276OnDio0Irq( void* context ); + +/*! + * \brief DIO 1 IRQ callback + */ +static void SX1276OnDio1Irq( void* context ); + +/*! + * \brief DIO 2 IRQ callback + */ +static void SX1276OnDio2Irq( void* context ); + +/*! + * \brief DIO 3 IRQ callback + */ +static void SX1276OnDio3Irq( void* context ); + +/*! + * \brief DIO 4 IRQ callback + */ +static void SX1276OnDio4Irq( void* context ); + +/*! + * \brief Tx & Rx timeout timer callback + */ +static void SX1276OnTimeoutIrq( void* context ); + +/* + * Private global constants + */ + +/*! + * Radio hardware registers initialization + * + * \remark RADIO_INIT_REGISTERS_VALUE is defined in sx1276-board.h file + */ +const RadioRegisters_t RadioRegsInit[] = RADIO_INIT_REGISTERS_VALUE; + +/*! + * Constant values need to compute the RSSI value + */ +#define RSSI_OFFSET_LF -164 +#define RSSI_OFFSET_HF -157 + +/*! + * Precomputed FSK bandwidth registers values + */ +const FskBandwidth_t FskBandwidths[] = +{ + { 2600 , 0x17 }, + { 3100 , 0x0F }, + { 3900 , 0x07 }, + { 5200 , 0x16 }, + { 6300 , 0x0E }, + { 7800 , 0x06 }, + { 10400 , 0x15 }, + { 12500 , 0x0D }, + { 15600 , 0x05 }, + { 20800 , 0x14 }, + { 25000 , 0x0C }, + { 31300 , 0x04 }, + { 41700 , 0x13 }, + { 50000 , 0x0B }, + { 62500 , 0x03 }, + { 83333 , 0x12 }, + { 100000, 0x0A }, + { 125000, 0x02 }, + { 166700, 0x11 }, + { 200000, 0x09 }, + { 250000, 0x01 }, + { 300000, 0x00 }, // Invalid Bandwidth +}; + +/* + * Private global variables + */ + +/*! + * Radio callbacks variable + */ +static RadioEvents_t *RadioEvents; + +/*! + * Reception buffer + */ +static uint8_t RxTxBuffer[RX_TX_BUFFER_SIZE]; + +/* + * Public global variables + */ + +/*! + * Radio hardware and global parameters + */ +SX1276_t SX1276; + +/*! + * Hardware DIO IRQ callback initialization + */ +DioIrqHandler *DioIrq[] = { SX1276OnDio0Irq, SX1276OnDio1Irq, + SX1276OnDio2Irq, SX1276OnDio3Irq, + SX1276OnDio4Irq, NULL }; + +/*! + * Tx and Rx timers + */ +TimerEvent_t TxTimeoutTimer; +TimerEvent_t RxTimeoutTimer; +TimerEvent_t RxTimeoutSyncWord; + +/* + * Radio driver functions implementation + */ + +void SX1276Init( RadioEvents_t *events ) +{ + uint8_t i; + + RadioEvents = events; + + // Initialize driver timeout timers + TimerInit( &TxTimeoutTimer, SX1276OnTimeoutIrq ); + TimerInit( &RxTimeoutTimer, SX1276OnTimeoutIrq ); + TimerInit( &RxTimeoutSyncWord, SX1276OnTimeoutIrq ); + + SX1276Reset( ); + + RxChainCalibration( ); + + SX1276SetOpMode( RF_OPMODE_SLEEP ); + + SX1276IoIrqInit( DioIrq ); + + for( i = 0; i < sizeof( RadioRegsInit ) / sizeof( RadioRegisters_t ); i++ ) + { + SX1276SetModem( RadioRegsInit[i].Modem ); + SX1276Write( RadioRegsInit[i].Addr, RadioRegsInit[i].Value ); + } + + SX1276SetModem( MODEM_FSK ); + + SX1276.Settings.State = RF_IDLE; +} + +RadioState_t SX1276GetStatus( void ) +{ + return SX1276.Settings.State; +} + +void SX1276SetChannel( uint32_t freq ) +{ + uint32_t freqInPllSteps = SX1276ConvertFreqInHzToPllStep( freq ); + + SX1276.Settings.Channel = freq; + + SX1276Write( REG_FRFMSB, ( uint8_t )( ( freqInPllSteps >> 16 ) & 0xFF ) ); + SX1276Write( REG_FRFMID, ( uint8_t )( ( freqInPllSteps >> 8 ) & 0xFF ) ); + SX1276Write( REG_FRFLSB, ( uint8_t )( freqInPllSteps & 0xFF ) ); +} + +bool SX1276IsChannelFree( uint32_t freq, uint32_t rxBandwidth, int16_t rssiThresh, uint32_t maxCarrierSenseTime ) +{ + bool status = true; + int16_t rssi = 0; + uint32_t carrierSenseTime = 0; + + SX1276SetSleep( ); + + SX1276SetModem( MODEM_FSK ); + + SX1276SetChannel( freq ); + + SX1276Write( REG_RXBW, GetFskBandwidthRegValue( rxBandwidth ) ); + SX1276Write( REG_AFCBW, GetFskBandwidthRegValue( rxBandwidth ) ); + + SX1276SetOpMode( RF_OPMODE_RECEIVER ); + + DelayMs( 1 ); + + carrierSenseTime = TimerGetCurrentTime( ); + + // Perform carrier sense for maxCarrierSenseTime + while( TimerGetElapsedTime( carrierSenseTime ) < maxCarrierSenseTime ) + { + rssi = SX1276ReadRssi( MODEM_FSK ); + + if( rssi > rssiThresh ) + { + status = false; + break; + } + } + SX1276SetSleep( ); + return status; +} + +uint32_t SX1276Random( void ) +{ + uint8_t i; + uint32_t rnd = 0; + + /* + * Radio setup for random number generation + */ + // Set LoRa modem ON + SX1276SetModem( MODEM_LORA ); + + // Disable LoRa modem interrupts + SX1276Write( REG_LR_IRQFLAGSMASK, RFLR_IRQFLAGS_RXTIMEOUT | + RFLR_IRQFLAGS_RXDONE | + RFLR_IRQFLAGS_PAYLOADCRCERROR | + RFLR_IRQFLAGS_VALIDHEADER | + RFLR_IRQFLAGS_TXDONE | + RFLR_IRQFLAGS_CADDONE | + RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL | + RFLR_IRQFLAGS_CADDETECTED ); + + // Set radio in continuous reception + SX1276SetOpMode( RF_OPMODE_RECEIVER ); + + for( i = 0; i < 32; i++ ) + { + DelayMs( 1 ); + // Unfiltered RSSI value reading. Only takes the LSB value + rnd |= ( ( uint32_t )SX1276Read( REG_LR_RSSIWIDEBAND ) & 0x01 ) << i; + } + + SX1276SetSleep( ); + + return rnd; +} + +/*! + * Performs the Rx chain calibration for LF and HF bands + * \remark Must be called just after the reset so all registers are at their + * default values + */ +static void RxChainCalibration( void ) +{ + uint8_t regPaConfigInitVal; + uint32_t initialFreq; + + // Save context + regPaConfigInitVal = SX1276Read( REG_PACONFIG ); + + initialFreq = SX1276ConvertPllStepToFreqInHz( ( ( ( uint32_t )SX1276Read( REG_FRFMSB ) << 16 ) | + ( ( uint32_t )SX1276Read( REG_FRFMID ) << 8 ) | + ( ( uint32_t )SX1276Read( REG_FRFLSB ) ) ) ); + + // Cut the PA just in case, RFO output, power = -1 dBm + SX1276Write( REG_PACONFIG, 0x00 ); + + // Launch Rx chain calibration for LF band + SX1276Write( REG_IMAGECAL, ( SX1276Read( REG_IMAGECAL ) & RF_IMAGECAL_IMAGECAL_MASK ) | RF_IMAGECAL_IMAGECAL_START ); + while( ( SX1276Read( REG_IMAGECAL ) & RF_IMAGECAL_IMAGECAL_RUNNING ) == RF_IMAGECAL_IMAGECAL_RUNNING ) + { + } + + // Sets a Frequency in HF band + SX1276SetChannel( 868000000 ); + + // Launch Rx chain calibration for HF band + SX1276Write( REG_IMAGECAL, ( SX1276Read( REG_IMAGECAL ) & RF_IMAGECAL_IMAGECAL_MASK ) | RF_IMAGECAL_IMAGECAL_START ); + while( ( SX1276Read( REG_IMAGECAL ) & RF_IMAGECAL_IMAGECAL_RUNNING ) == RF_IMAGECAL_IMAGECAL_RUNNING ) + { + } + + // Restore context + SX1276Write( REG_PACONFIG, regPaConfigInitVal ); + SX1276SetChannel( initialFreq ); +} + +void SX1276SetRxConfig( RadioModems_t modem, uint32_t bandwidth, + uint32_t datarate, uint8_t coderate, + uint32_t bandwidthAfc, uint16_t preambleLen, + uint16_t symbTimeout, bool fixLen, + uint8_t payloadLen, + bool crcOn, bool freqHopOn, uint8_t hopPeriod, + bool iqInverted, bool rxContinuous ) +{ + SX1276SetModem( modem ); + + SX1276SetStby( ); + + switch( modem ) + { + case MODEM_FSK: + { + SX1276.Settings.Fsk.Bandwidth = bandwidth; + SX1276.Settings.Fsk.Datarate = datarate; + SX1276.Settings.Fsk.BandwidthAfc = bandwidthAfc; + SX1276.Settings.Fsk.FixLen = fixLen; + SX1276.Settings.Fsk.PayloadLen = payloadLen; + SX1276.Settings.Fsk.CrcOn = crcOn; + SX1276.Settings.Fsk.IqInverted = iqInverted; + SX1276.Settings.Fsk.RxContinuous = rxContinuous; + SX1276.Settings.Fsk.PreambleLen = preambleLen; + SX1276.Settings.Fsk.RxSingleTimeout = ( uint32_t )symbTimeout * 8000UL / datarate; + + uint32_t bitRate = ( uint32_t )( SX1276_XTAL_FREQ / datarate ); + SX1276Write( REG_BITRATEMSB, ( uint8_t )( bitRate >> 8 ) ); + SX1276Write( REG_BITRATELSB, ( uint8_t )( bitRate & 0xFF ) ); + + SX1276Write( REG_RXBW, GetFskBandwidthRegValue( bandwidth ) ); + SX1276Write( REG_AFCBW, GetFskBandwidthRegValue( bandwidthAfc ) ); + + SX1276Write( REG_PREAMBLEMSB, ( uint8_t )( ( preambleLen >> 8 ) & 0xFF ) ); + SX1276Write( REG_PREAMBLELSB, ( uint8_t )( preambleLen & 0xFF ) ); + + if( fixLen == 1 ) + { + SX1276Write( REG_PAYLOADLENGTH, payloadLen ); + } + else + { + SX1276Write( REG_PAYLOADLENGTH, 0xFF ); // Set payload length to the maximum + } + + SX1276Write( REG_PACKETCONFIG1, + ( SX1276Read( REG_PACKETCONFIG1 ) & + RF_PACKETCONFIG1_CRC_MASK & + RF_PACKETCONFIG1_PACKETFORMAT_MASK ) | + ( ( fixLen == 1 ) ? RF_PACKETCONFIG1_PACKETFORMAT_FIXED : RF_PACKETCONFIG1_PACKETFORMAT_VARIABLE ) | + ( crcOn << 4 ) ); + SX1276Write( REG_PACKETCONFIG2, ( SX1276Read( REG_PACKETCONFIG2 ) | RF_PACKETCONFIG2_DATAMODE_PACKET ) ); + } + break; + case MODEM_LORA: + { + if( bandwidth > 2 ) + { + // Fatal error: When using LoRa modem only bandwidths 125, 250 and 500 kHz are supported + while( 1 ); + } + bandwidth += 7; + SX1276.Settings.LoRa.Bandwidth = bandwidth; + SX1276.Settings.LoRa.Datarate = datarate; + SX1276.Settings.LoRa.Coderate = coderate; + SX1276.Settings.LoRa.PreambleLen = preambleLen; + SX1276.Settings.LoRa.FixLen = fixLen; + SX1276.Settings.LoRa.PayloadLen = payloadLen; + SX1276.Settings.LoRa.CrcOn = crcOn; + SX1276.Settings.LoRa.FreqHopOn = freqHopOn; + SX1276.Settings.LoRa.HopPeriod = hopPeriod; + SX1276.Settings.LoRa.IqInverted = iqInverted; + SX1276.Settings.LoRa.RxContinuous = rxContinuous; + + if( datarate > 12 ) + { + datarate = 12; + } + else if( datarate < 6 ) + { + datarate = 6; + } + + if( ( ( bandwidth == 7 ) && ( ( datarate == 11 ) || ( datarate == 12 ) ) ) || + ( ( bandwidth == 8 ) && ( datarate == 12 ) ) ) + { + SX1276.Settings.LoRa.LowDatarateOptimize = 0x01; + } + else + { + SX1276.Settings.LoRa.LowDatarateOptimize = 0x00; + } + + SX1276Write( REG_LR_MODEMCONFIG1, + ( SX1276Read( REG_LR_MODEMCONFIG1 ) & + RFLR_MODEMCONFIG1_BW_MASK & + RFLR_MODEMCONFIG1_CODINGRATE_MASK & + RFLR_MODEMCONFIG1_IMPLICITHEADER_MASK ) | + ( bandwidth << 4 ) | ( coderate << 1 ) | + fixLen ); + + SX1276Write( REG_LR_MODEMCONFIG2, + ( SX1276Read( REG_LR_MODEMCONFIG2 ) & + RFLR_MODEMCONFIG2_SF_MASK & + RFLR_MODEMCONFIG2_RXPAYLOADCRC_MASK & + RFLR_MODEMCONFIG2_SYMBTIMEOUTMSB_MASK ) | + ( datarate << 4 ) | ( crcOn << 2 ) | + ( ( symbTimeout >> 8 ) & ~RFLR_MODEMCONFIG2_SYMBTIMEOUTMSB_MASK ) ); + + SX1276Write( REG_LR_MODEMCONFIG3, + ( SX1276Read( REG_LR_MODEMCONFIG3 ) & + RFLR_MODEMCONFIG3_LOWDATARATEOPTIMIZE_MASK ) | + ( SX1276.Settings.LoRa.LowDatarateOptimize << 3 ) ); + + SX1276Write( REG_LR_SYMBTIMEOUTLSB, ( uint8_t )( symbTimeout & 0xFF ) ); + + SX1276Write( REG_LR_PREAMBLEMSB, ( uint8_t )( ( preambleLen >> 8 ) & 0xFF ) ); + SX1276Write( REG_LR_PREAMBLELSB, ( uint8_t )( preambleLen & 0xFF ) ); + + if( fixLen == 1 ) + { + SX1276Write( REG_LR_PAYLOADLENGTH, payloadLen ); + } + + if( SX1276.Settings.LoRa.FreqHopOn == true ) + { + SX1276Write( REG_LR_PLLHOP, ( SX1276Read( REG_LR_PLLHOP ) & RFLR_PLLHOP_FASTHOP_MASK ) | RFLR_PLLHOP_FASTHOP_ON ); + SX1276Write( REG_LR_HOPPERIOD, SX1276.Settings.LoRa.HopPeriod ); + } + + if( ( bandwidth == 9 ) && ( SX1276.Settings.Channel > RF_MID_BAND_THRESH ) ) + { + // ERRATA 2.1 - Sensitivity Optimization with a 500 kHz Bandwidth + SX1276Write( REG_LR_HIGHBWOPTIMIZE1, 0x02 ); + SX1276Write( REG_LR_HIGHBWOPTIMIZE2, 0x64 ); + } + else if( bandwidth == 9 ) + { + // ERRATA 2.1 - Sensitivity Optimization with a 500 kHz Bandwidth + SX1276Write( REG_LR_HIGHBWOPTIMIZE1, 0x02 ); + SX1276Write( REG_LR_HIGHBWOPTIMIZE2, 0x7F ); + } + else + { + // ERRATA 2.1 - Sensitivity Optimization with a 500 kHz Bandwidth + SX1276Write( REG_LR_HIGHBWOPTIMIZE1, 0x03 ); + } + + if( datarate == 6 ) + { + SX1276Write( REG_LR_DETECTOPTIMIZE, + ( SX1276Read( REG_LR_DETECTOPTIMIZE ) & + RFLR_DETECTIONOPTIMIZE_MASK ) | + RFLR_DETECTIONOPTIMIZE_SF6 ); + SX1276Write( REG_LR_DETECTIONTHRESHOLD, + RFLR_DETECTIONTHRESH_SF6 ); + } + else + { + SX1276Write( REG_LR_DETECTOPTIMIZE, + ( SX1276Read( REG_LR_DETECTOPTIMIZE ) & + RFLR_DETECTIONOPTIMIZE_MASK ) | + RFLR_DETECTIONOPTIMIZE_SF7_TO_SF12 ); + SX1276Write( REG_LR_DETECTIONTHRESHOLD, + RFLR_DETECTIONTHRESH_SF7_TO_SF12 ); + } + } + break; + } +} + +void SX1276SetTxConfig( RadioModems_t modem, int8_t power, uint32_t fdev, + uint32_t bandwidth, uint32_t datarate, + uint8_t coderate, uint16_t preambleLen, + bool fixLen, bool crcOn, bool freqHopOn, + uint8_t hopPeriod, bool iqInverted, uint32_t timeout ) +{ + SX1276SetModem( modem ); + + SX1276SetStby( ); + + SX1276SetRfTxPower( power ); + + switch( modem ) + { + case MODEM_FSK: + { + SX1276.Settings.Fsk.Power = power; + SX1276.Settings.Fsk.Fdev = fdev; + SX1276.Settings.Fsk.Bandwidth = bandwidth; + SX1276.Settings.Fsk.Datarate = datarate; + SX1276.Settings.Fsk.PreambleLen = preambleLen; + SX1276.Settings.Fsk.FixLen = fixLen; + SX1276.Settings.Fsk.CrcOn = crcOn; + SX1276.Settings.Fsk.IqInverted = iqInverted; + SX1276.Settings.Fsk.TxTimeout = timeout; + + uint32_t fdevInPllSteps = SX1276ConvertFreqInHzToPllStep( fdev ); + SX1276Write( REG_FDEVMSB, ( uint8_t )( fdevInPllSteps >> 8 ) ); + SX1276Write( REG_FDEVLSB, ( uint8_t )( fdevInPllSteps & 0xFF ) ); + + uint32_t bitRate = ( uint32_t )( SX1276_XTAL_FREQ / datarate ); + SX1276Write( REG_BITRATEMSB, ( uint8_t )( bitRate >> 8 ) ); + SX1276Write( REG_BITRATELSB, ( uint8_t )( bitRate & 0xFF ) ); + + SX1276Write( REG_PREAMBLEMSB, ( preambleLen >> 8 ) & 0x00FF ); + SX1276Write( REG_PREAMBLELSB, preambleLen & 0xFF ); + + SX1276Write( REG_PACKETCONFIG1, + ( SX1276Read( REG_PACKETCONFIG1 ) & + RF_PACKETCONFIG1_CRC_MASK & + RF_PACKETCONFIG1_PACKETFORMAT_MASK ) | + ( ( fixLen == 1 ) ? RF_PACKETCONFIG1_PACKETFORMAT_FIXED : RF_PACKETCONFIG1_PACKETFORMAT_VARIABLE ) | + ( crcOn << 4 ) ); + SX1276Write( REG_PACKETCONFIG2, ( SX1276Read( REG_PACKETCONFIG2 ) | RF_PACKETCONFIG2_DATAMODE_PACKET ) ); + } + break; + case MODEM_LORA: + { + SX1276.Settings.LoRa.Power = power; + if( bandwidth > 2 ) + { + // Fatal error: When using LoRa modem only bandwidths 125, 250 and 500 kHz are supported + while( 1 ); + } + bandwidth += 7; + SX1276.Settings.LoRa.Bandwidth = bandwidth; + SX1276.Settings.LoRa.Datarate = datarate; + SX1276.Settings.LoRa.Coderate = coderate; + SX1276.Settings.LoRa.PreambleLen = preambleLen; + SX1276.Settings.LoRa.FixLen = fixLen; + SX1276.Settings.LoRa.FreqHopOn = freqHopOn; + SX1276.Settings.LoRa.HopPeriod = hopPeriod; + SX1276.Settings.LoRa.CrcOn = crcOn; + SX1276.Settings.LoRa.IqInverted = iqInverted; + SX1276.Settings.LoRa.TxTimeout = timeout; + + if( datarate > 12 ) + { + datarate = 12; + } + else if( datarate < 6 ) + { + datarate = 6; + } + if( ( ( bandwidth == 7 ) && ( ( datarate == 11 ) || ( datarate == 12 ) ) ) || + ( ( bandwidth == 8 ) && ( datarate == 12 ) ) ) + { + SX1276.Settings.LoRa.LowDatarateOptimize = 0x01; + } + else + { + SX1276.Settings.LoRa.LowDatarateOptimize = 0x00; + } + + if( SX1276.Settings.LoRa.FreqHopOn == true ) + { + SX1276Write( REG_LR_PLLHOP, ( SX1276Read( REG_LR_PLLHOP ) & RFLR_PLLHOP_FASTHOP_MASK ) | RFLR_PLLHOP_FASTHOP_ON ); + SX1276Write( REG_LR_HOPPERIOD, SX1276.Settings.LoRa.HopPeriod ); + } + + SX1276Write( REG_LR_MODEMCONFIG1, + ( SX1276Read( REG_LR_MODEMCONFIG1 ) & + RFLR_MODEMCONFIG1_BW_MASK & + RFLR_MODEMCONFIG1_CODINGRATE_MASK & + RFLR_MODEMCONFIG1_IMPLICITHEADER_MASK ) | + ( bandwidth << 4 ) | ( coderate << 1 ) | + fixLen ); + + SX1276Write( REG_LR_MODEMCONFIG2, + ( SX1276Read( REG_LR_MODEMCONFIG2 ) & + RFLR_MODEMCONFIG2_SF_MASK & + RFLR_MODEMCONFIG2_RXPAYLOADCRC_MASK ) | + ( datarate << 4 ) | ( crcOn << 2 ) ); + + SX1276Write( REG_LR_MODEMCONFIG3, + ( SX1276Read( REG_LR_MODEMCONFIG3 ) & + RFLR_MODEMCONFIG3_LOWDATARATEOPTIMIZE_MASK ) | + ( SX1276.Settings.LoRa.LowDatarateOptimize << 3 ) ); + + SX1276Write( REG_LR_PREAMBLEMSB, ( preambleLen >> 8 ) & 0x00FF ); + SX1276Write( REG_LR_PREAMBLELSB, preambleLen & 0xFF ); + + if( datarate == 6 ) + { + SX1276Write( REG_LR_DETECTOPTIMIZE, + ( SX1276Read( REG_LR_DETECTOPTIMIZE ) & + RFLR_DETECTIONOPTIMIZE_MASK ) | + RFLR_DETECTIONOPTIMIZE_SF6 ); + SX1276Write( REG_LR_DETECTIONTHRESHOLD, + RFLR_DETECTIONTHRESH_SF6 ); + } + else + { + SX1276Write( REG_LR_DETECTOPTIMIZE, + ( SX1276Read( REG_LR_DETECTOPTIMIZE ) & + RFLR_DETECTIONOPTIMIZE_MASK ) | + RFLR_DETECTIONOPTIMIZE_SF7_TO_SF12 ); + SX1276Write( REG_LR_DETECTIONTHRESHOLD, + RFLR_DETECTIONTHRESH_SF7_TO_SF12 ); + } + } + break; + } +} + +uint32_t SX1276GetTimeOnAir( RadioModems_t modem, uint32_t bandwidth, + uint32_t datarate, uint8_t coderate, + uint16_t preambleLen, bool fixLen, uint8_t payloadLen, + bool crcOn ) +{ + uint32_t numerator = 0; + uint32_t denominator = 1; + + switch( modem ) + { + case MODEM_FSK: + { + numerator = 1000U * SX1276GetGfskTimeOnAirNumerator( preambleLen, fixLen, payloadLen, crcOn ); + denominator = datarate; + } + break; + case MODEM_LORA: + { + numerator = 1000U * SX1276GetLoRaTimeOnAirNumerator( bandwidth, datarate, coderate, preambleLen, fixLen, + payloadLen, crcOn ); + denominator = SX1276GetLoRaBandwidthInHz( bandwidth ); + } + break; + } + // Perform integral ceil() + return ( numerator + denominator - 1 ) / denominator; +} + +void SX1276Send( uint8_t *buffer, uint8_t size ) +{ + uint32_t txTimeout = 0; + + switch( SX1276.Settings.Modem ) + { + case MODEM_FSK: + { + SX1276.Settings.FskPacketHandler.NbBytes = 0; + SX1276.Settings.FskPacketHandler.Size = size; + + if( SX1276.Settings.Fsk.FixLen == false ) + { + SX1276WriteFifo( ( uint8_t* )&size, 1 ); + } + else + { + SX1276Write( REG_PAYLOADLENGTH, size ); + } + + if( ( size > 0 ) && ( size <= 64 ) ) + { + SX1276.Settings.FskPacketHandler.ChunkSize = size; + } + else + { + memcpy1( RxTxBuffer, buffer, size ); + SX1276.Settings.FskPacketHandler.ChunkSize = 32; + } + + // Write payload buffer + SX1276WriteFifo( buffer, SX1276.Settings.FskPacketHandler.ChunkSize ); + SX1276.Settings.FskPacketHandler.NbBytes += SX1276.Settings.FskPacketHandler.ChunkSize; + txTimeout = SX1276.Settings.Fsk.TxTimeout; + } + break; + case MODEM_LORA: + { + if( SX1276.Settings.LoRa.IqInverted == true ) + { + SX1276Write( REG_LR_INVERTIQ, ( ( SX1276Read( REG_LR_INVERTIQ ) & RFLR_INVERTIQ_TX_MASK & RFLR_INVERTIQ_RX_MASK ) | RFLR_INVERTIQ_RX_OFF | RFLR_INVERTIQ_TX_ON ) ); + SX1276Write( REG_LR_INVERTIQ2, RFLR_INVERTIQ2_ON ); + } + else + { + SX1276Write( REG_LR_INVERTIQ, ( ( SX1276Read( REG_LR_INVERTIQ ) & RFLR_INVERTIQ_TX_MASK & RFLR_INVERTIQ_RX_MASK ) | RFLR_INVERTIQ_RX_OFF | RFLR_INVERTIQ_TX_OFF ) ); + SX1276Write( REG_LR_INVERTIQ2, RFLR_INVERTIQ2_OFF ); + } + + SX1276.Settings.LoRaPacketHandler.Size = size; + + // Initializes the payload size + SX1276Write( REG_LR_PAYLOADLENGTH, size ); + + // Full buffer used for Tx + SX1276Write( REG_LR_FIFOTXBASEADDR, 0 ); + SX1276Write( REG_LR_FIFOADDRPTR, 0 ); + + // FIFO operations can not take place in Sleep mode + if( ( SX1276Read( REG_OPMODE ) & ~RF_OPMODE_MASK ) == RF_OPMODE_SLEEP ) + { + SX1276SetStby( ); + DelayMs( 1 ); + } + // Write payload buffer + SX1276WriteFifo( buffer, size ); + txTimeout = SX1276.Settings.LoRa.TxTimeout; + } + break; + } + + SX1276SetTx( txTimeout ); +} + +void SX1276SetSleep( void ) +{ + TimerStop( &RxTimeoutTimer ); + TimerStop( &TxTimeoutTimer ); + TimerStop( &RxTimeoutSyncWord ); + + SX1276SetOpMode( RF_OPMODE_SLEEP ); + + // Disable TCXO radio is in SLEEP mode + SX1276SetBoardTcxo( false ); + + SX1276.Settings.State = RF_IDLE; +} + +void SX1276SetStby( void ) +{ + TimerStop( &RxTimeoutTimer ); + TimerStop( &TxTimeoutTimer ); + TimerStop( &RxTimeoutSyncWord ); + + SX1276SetOpMode( RF_OPMODE_STANDBY ); + SX1276.Settings.State = RF_IDLE; +} + +void SX1276SetRx( uint32_t timeout ) +{ + bool rxContinuous = false; + TimerStop( &TxTimeoutTimer ); + + switch( SX1276.Settings.Modem ) + { + case MODEM_FSK: + { + rxContinuous = SX1276.Settings.Fsk.RxContinuous; + + // DIO0=PayloadReady + // DIO1=FifoLevel + // DIO2=SyncAddr + // DIO3=FifoEmpty + // DIO4=Preamble + // DIO5=ModeReady + SX1276Write( REG_DIOMAPPING1, ( SX1276Read( REG_DIOMAPPING1 ) & RF_DIOMAPPING1_DIO0_MASK & + RF_DIOMAPPING1_DIO1_MASK & + RF_DIOMAPPING1_DIO2_MASK ) | + RF_DIOMAPPING1_DIO0_00 | + RF_DIOMAPPING1_DIO1_00 | + RF_DIOMAPPING1_DIO2_11 ); + + SX1276Write( REG_DIOMAPPING2, ( SX1276Read( REG_DIOMAPPING2 ) & RF_DIOMAPPING2_DIO4_MASK & + RF_DIOMAPPING2_MAP_MASK ) | + RF_DIOMAPPING2_DIO4_11 | + RF_DIOMAPPING2_MAP_PREAMBLEDETECT ); + + SX1276.Settings.FskPacketHandler.FifoThresh = SX1276Read( REG_FIFOTHRESH ) & 0x3F; + + SX1276Write( REG_RXCONFIG, RF_RXCONFIG_AFCAUTO_ON | RF_RXCONFIG_AGCAUTO_ON | RF_RXCONFIG_RXTRIGER_PREAMBLEDETECT ); + + SX1276.Settings.FskPacketHandler.PreambleDetected = false; + SX1276.Settings.FskPacketHandler.SyncWordDetected = false; + SX1276.Settings.FskPacketHandler.NbBytes = 0; + SX1276.Settings.FskPacketHandler.Size = 0; + } + break; + case MODEM_LORA: + { + if( SX1276.Settings.LoRa.IqInverted == true ) + { + SX1276Write( REG_LR_INVERTIQ, ( ( SX1276Read( REG_LR_INVERTIQ ) & RFLR_INVERTIQ_TX_MASK & RFLR_INVERTIQ_RX_MASK ) | RFLR_INVERTIQ_RX_ON | RFLR_INVERTIQ_TX_OFF ) ); + SX1276Write( REG_LR_INVERTIQ2, RFLR_INVERTIQ2_ON ); + } + else + { + SX1276Write( REG_LR_INVERTIQ, ( ( SX1276Read( REG_LR_INVERTIQ ) & RFLR_INVERTIQ_TX_MASK & RFLR_INVERTIQ_RX_MASK ) | RFLR_INVERTIQ_RX_OFF | RFLR_INVERTIQ_TX_OFF ) ); + SX1276Write( REG_LR_INVERTIQ2, RFLR_INVERTIQ2_OFF ); + } + + // ERRATA 2.3 - Receiver Spurious Reception of a LoRa Signal + if( SX1276.Settings.LoRa.Bandwidth < 9 ) + { + SX1276Write( REG_LR_DETECTOPTIMIZE, SX1276Read( REG_LR_DETECTOPTIMIZE ) & 0x7F ); + SX1276Write( REG_LR_IFFREQ2, 0x00 ); + switch( SX1276.Settings.LoRa.Bandwidth ) + { + case 0: // 7.8 kHz + SX1276Write( REG_LR_IFFREQ1, 0x48 ); + SX1276SetChannel(SX1276.Settings.Channel + 7810 ); + break; + case 1: // 10.4 kHz + SX1276Write( REG_LR_IFFREQ1, 0x44 ); + SX1276SetChannel(SX1276.Settings.Channel + 10420 ); + break; + case 2: // 15.6 kHz + SX1276Write( REG_LR_IFFREQ1, 0x44 ); + SX1276SetChannel(SX1276.Settings.Channel + 15620 ); + break; + case 3: // 20.8 kHz + SX1276Write( REG_LR_IFFREQ1, 0x44 ); + SX1276SetChannel(SX1276.Settings.Channel + 20830 ); + break; + case 4: // 31.2 kHz + SX1276Write( REG_LR_IFFREQ1, 0x44 ); + SX1276SetChannel(SX1276.Settings.Channel + 31250 ); + break; + case 5: // 41.4 kHz + SX1276Write( REG_LR_IFFREQ1, 0x44 ); + SX1276SetChannel(SX1276.Settings.Channel + 41670 ); + break; + case 6: // 62.5 kHz + SX1276Write( REG_LR_IFFREQ1, 0x40 ); + break; + case 7: // 125 kHz + SX1276Write( REG_LR_IFFREQ1, 0x40 ); + break; + case 8: // 250 kHz + SX1276Write( REG_LR_IFFREQ1, 0x40 ); + break; + } + } + else + { + SX1276Write( REG_LR_DETECTOPTIMIZE, SX1276Read( REG_LR_DETECTOPTIMIZE ) | 0x80 ); + } + + rxContinuous = SX1276.Settings.LoRa.RxContinuous; + + if( SX1276.Settings.LoRa.FreqHopOn == true ) + { + SX1276Write( REG_LR_IRQFLAGSMASK, //RFLR_IRQFLAGS_RXTIMEOUT | + //RFLR_IRQFLAGS_RXDONE | + //RFLR_IRQFLAGS_PAYLOADCRCERROR | + RFLR_IRQFLAGS_VALIDHEADER | + RFLR_IRQFLAGS_TXDONE | + RFLR_IRQFLAGS_CADDONE | + //RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL | + RFLR_IRQFLAGS_CADDETECTED ); + + // DIO0=RxDone, DIO2=FhssChangeChannel + SX1276Write( REG_DIOMAPPING1, ( SX1276Read( REG_DIOMAPPING1 ) & RFLR_DIOMAPPING1_DIO0_MASK & RFLR_DIOMAPPING1_DIO2_MASK ) | RFLR_DIOMAPPING1_DIO0_00 | RFLR_DIOMAPPING1_DIO2_00 ); + } + else + { + SX1276Write( REG_LR_IRQFLAGSMASK, //RFLR_IRQFLAGS_RXTIMEOUT | + //RFLR_IRQFLAGS_RXDONE | + //RFLR_IRQFLAGS_PAYLOADCRCERROR | + RFLR_IRQFLAGS_VALIDHEADER | + RFLR_IRQFLAGS_TXDONE | + RFLR_IRQFLAGS_CADDONE | + RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL | + RFLR_IRQFLAGS_CADDETECTED ); + + // DIO0=RxDone + SX1276Write( REG_DIOMAPPING1, ( SX1276Read( REG_DIOMAPPING1 ) & RFLR_DIOMAPPING1_DIO0_MASK ) | RFLR_DIOMAPPING1_DIO0_00 ); + } + SX1276Write( REG_LR_FIFORXBASEADDR, 0 ); + SX1276Write( REG_LR_FIFOADDRPTR, 0 ); + } + break; + } + + SX1276.Settings.State = RF_RX_RUNNING; + if( timeout != 0 ) + { + TimerSetValue( &RxTimeoutTimer, timeout ); + TimerStart( &RxTimeoutTimer ); + } + + if( SX1276.Settings.Modem == MODEM_FSK ) + { + SX1276SetOpMode( RF_OPMODE_RECEIVER ); + + if( rxContinuous == false ) + { + TimerSetValue( &RxTimeoutSyncWord, SX1276.Settings.Fsk.RxSingleTimeout ); + TimerStart( &RxTimeoutSyncWord ); + } + } + else + { + if( rxContinuous == true ) + { + SX1276SetOpMode( RFLR_OPMODE_RECEIVER ); + } + else + { + SX1276SetOpMode( RFLR_OPMODE_RECEIVER_SINGLE ); + } + } +} + +static void SX1276SetTx( uint32_t timeout ) +{ + TimerStop( &RxTimeoutTimer ); + + TimerSetValue( &TxTimeoutTimer, timeout ); + + switch( SX1276.Settings.Modem ) + { + case MODEM_FSK: + { + // DIO0=PacketSent + // DIO1=FifoLevel + // DIO2=FifoFull + // DIO3=FifoEmpty + // DIO4=LowBat + // DIO5=ModeReady + SX1276Write( REG_DIOMAPPING1, ( SX1276Read( REG_DIOMAPPING1 ) & RF_DIOMAPPING1_DIO0_MASK & + RF_DIOMAPPING1_DIO1_MASK & + RF_DIOMAPPING1_DIO2_MASK ) ); + + SX1276Write( REG_DIOMAPPING2, ( SX1276Read( REG_DIOMAPPING2 ) & RF_DIOMAPPING2_DIO4_MASK & + RF_DIOMAPPING2_MAP_MASK ) ); + SX1276.Settings.FskPacketHandler.FifoThresh = SX1276Read( REG_FIFOTHRESH ) & 0x3F; + } + break; + case MODEM_LORA: + { + if( SX1276.Settings.LoRa.FreqHopOn == true ) + { + SX1276Write( REG_LR_IRQFLAGSMASK, RFLR_IRQFLAGS_RXTIMEOUT | + RFLR_IRQFLAGS_RXDONE | + RFLR_IRQFLAGS_PAYLOADCRCERROR | + RFLR_IRQFLAGS_VALIDHEADER | + //RFLR_IRQFLAGS_TXDONE | + RFLR_IRQFLAGS_CADDONE | + //RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL | + RFLR_IRQFLAGS_CADDETECTED ); + + // DIO0=TxDone, DIO2=FhssChangeChannel + SX1276Write( REG_DIOMAPPING1, ( SX1276Read( REG_DIOMAPPING1 ) & RFLR_DIOMAPPING1_DIO0_MASK & RFLR_DIOMAPPING1_DIO2_MASK ) | RFLR_DIOMAPPING1_DIO0_01 | RFLR_DIOMAPPING1_DIO2_00 ); + } + else + { + SX1276Write( REG_LR_IRQFLAGSMASK, RFLR_IRQFLAGS_RXTIMEOUT | + RFLR_IRQFLAGS_RXDONE | + RFLR_IRQFLAGS_PAYLOADCRCERROR | + RFLR_IRQFLAGS_VALIDHEADER | + //RFLR_IRQFLAGS_TXDONE | + RFLR_IRQFLAGS_CADDONE | + RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL | + RFLR_IRQFLAGS_CADDETECTED ); + + // DIO0=TxDone + SX1276Write( REG_DIOMAPPING1, ( SX1276Read( REG_DIOMAPPING1 ) & RFLR_DIOMAPPING1_DIO0_MASK ) | RFLR_DIOMAPPING1_DIO0_01 ); + } + } + break; + } + + SX1276.Settings.State = RF_TX_RUNNING; + TimerStart( &TxTimeoutTimer ); + SX1276SetOpMode( RF_OPMODE_TRANSMITTER ); +} + +void SX1276StartCad( void ) +{ + switch( SX1276.Settings.Modem ) + { + case MODEM_FSK: + { + + } + break; + case MODEM_LORA: + { + SX1276Write( REG_LR_IRQFLAGSMASK, RFLR_IRQFLAGS_RXTIMEOUT | + RFLR_IRQFLAGS_RXDONE | + RFLR_IRQFLAGS_PAYLOADCRCERROR | + RFLR_IRQFLAGS_VALIDHEADER | + RFLR_IRQFLAGS_TXDONE | + //RFLR_IRQFLAGS_CADDONE | + RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL // | + //RFLR_IRQFLAGS_CADDETECTED + ); + + // DIO3=CADDone + SX1276Write( REG_DIOMAPPING1, ( SX1276Read( REG_DIOMAPPING1 ) & RFLR_DIOMAPPING1_DIO3_MASK ) | RFLR_DIOMAPPING1_DIO3_00 ); + + SX1276.Settings.State = RF_CAD; + SX1276SetOpMode( RFLR_OPMODE_CAD ); + } + break; + default: + break; + } +} + +void SX1276SetTxContinuousWave( uint32_t freq, int8_t power, uint16_t time ) +{ + uint32_t timeout = ( uint32_t )time * 1000; + + SX1276SetChannel( freq ); + + SX1276SetTxConfig( MODEM_FSK, power, 0, 0, 4800, 0, 5, false, false, 0, 0, 0, timeout ); + + SX1276Write( REG_PACKETCONFIG2, ( SX1276Read( REG_PACKETCONFIG2 ) & RF_PACKETCONFIG2_DATAMODE_MASK ) ); + // Disable radio interrupts + SX1276Write( REG_DIOMAPPING1, RF_DIOMAPPING1_DIO0_11 | RF_DIOMAPPING1_DIO1_11 ); + SX1276Write( REG_DIOMAPPING2, RF_DIOMAPPING2_DIO4_10 | RF_DIOMAPPING2_DIO5_10 ); + + TimerSetValue( &TxTimeoutTimer, timeout ); + + SX1276.Settings.State = RF_TX_RUNNING; + TimerStart( &TxTimeoutTimer ); + SX1276SetOpMode( RF_OPMODE_TRANSMITTER ); +} + +int16_t SX1276ReadRssi( RadioModems_t modem ) +{ + int16_t rssi = 0; + + switch( modem ) + { + case MODEM_FSK: + rssi = -( SX1276Read( REG_RSSIVALUE ) >> 1 ); + break; + case MODEM_LORA: + if( SX1276.Settings.Channel > RF_MID_BAND_THRESH ) + { + rssi = RSSI_OFFSET_HF + SX1276Read( REG_LR_RSSIVALUE ); + } + else + { + rssi = RSSI_OFFSET_LF + SX1276Read( REG_LR_RSSIVALUE ); + } + break; + default: + rssi = -1; + break; + } + return rssi; +} + +static void SX1276SetOpMode( uint8_t opMode ) +{ +#if defined( USE_RADIO_DEBUG ) + switch( opMode ) + { + case RF_OPMODE_TRANSMITTER: + SX1276DbgPinTxWrite( 1 ); + SX1276DbgPinRxWrite( 0 ); + break; + case RF_OPMODE_RECEIVER: + case RFLR_OPMODE_RECEIVER_SINGLE: + SX1276DbgPinTxWrite( 0 ); + SX1276DbgPinRxWrite( 1 ); + break; + default: + SX1276DbgPinTxWrite( 0 ); + SX1276DbgPinRxWrite( 0 ); + break; + } +#endif + if( opMode == RF_OPMODE_SLEEP ) + { + SX1276SetAntSwLowPower( true ); + } + else + { + // Enable TCXO if operating mode different from SLEEP. + SX1276SetBoardTcxo( true ); + SX1276SetAntSwLowPower( false ); + SX1276SetAntSw( opMode ); + } + SX1276Write( REG_OPMODE, ( SX1276Read( REG_OPMODE ) & RF_OPMODE_MASK ) | opMode ); +} + +void SX1276SetModem( RadioModems_t modem ) +{ + if( ( SX1276Read( REG_OPMODE ) & RFLR_OPMODE_LONGRANGEMODE_ON ) != 0 ) + { + SX1276.Settings.Modem = MODEM_LORA; + } + else + { + SX1276.Settings.Modem = MODEM_FSK; + } + + if( SX1276.Settings.Modem == modem ) + { + return; + } + + SX1276.Settings.Modem = modem; + switch( SX1276.Settings.Modem ) + { + default: + case MODEM_FSK: + SX1276SetOpMode( RF_OPMODE_SLEEP ); + SX1276Write( REG_OPMODE, ( SX1276Read( REG_OPMODE ) & RFLR_OPMODE_LONGRANGEMODE_MASK ) | RFLR_OPMODE_LONGRANGEMODE_OFF ); + + SX1276Write( REG_DIOMAPPING1, 0x00 ); + SX1276Write( REG_DIOMAPPING2, 0x30 ); // DIO5=ModeReady + break; + case MODEM_LORA: + SX1276SetOpMode( RF_OPMODE_SLEEP ); + SX1276Write( REG_OPMODE, ( SX1276Read( REG_OPMODE ) & RFLR_OPMODE_LONGRANGEMODE_MASK ) | RFLR_OPMODE_LONGRANGEMODE_ON ); + + SX1276Write( REG_DIOMAPPING1, 0x00 ); + SX1276Write( REG_DIOMAPPING2, 0x00 ); + break; + } +} + +void SX1276Write( uint32_t addr, uint8_t data ) +{ + SX1276WriteBuffer( addr, &data, 1 ); +} + +uint8_t SX1276Read( uint32_t addr ) +{ + uint8_t data; + SX1276ReadBuffer( addr, &data, 1 ); + return data; +} + +void SX1276WriteBuffer( uint32_t addr, uint8_t *buffer, uint8_t size ) +{ + uint8_t i; + + //NSS = 0; + //GpioWrite( &SX1276.Spi.Nss, 0 ); + + SpiInOut( &SX1276.Spi, addr | 0x80 ); + for( i = 0; i < size; i++ ) + { + SpiInOut( &SX1276.Spi, buffer[i] ); + } + + //NSS = 1; + //GpioWrite( &SX1276.Spi.Nss, 1 ); +} + +void SX1276ReadBuffer( uint32_t addr, uint8_t *buffer, uint8_t size ) +{ + uint8_t i; + + //NSS = 0; + //GpioWrite( &SX1276.Spi.Nss, 0 ); + + SpiInOut( &SX1276.Spi, addr & 0x7F ); + + for( i = 0; i < size; i++ ) + { + buffer[i] = SpiInOut( &SX1276.Spi, 0 ); + } + + //NSS = 1; + //GpioWrite( &SX1276.Spi.Nss, 1 ); +} + +static void SX1276WriteFifo( uint8_t *buffer, uint8_t size ) +{ + SX1276WriteBuffer( 0, buffer, size ); +} + +static void SX1276ReadFifo( uint8_t *buffer, uint8_t size ) +{ + SX1276ReadBuffer( 0, buffer, size ); +} + +void SX1276SetMaxPayloadLength( RadioModems_t modem, uint8_t max ) +{ + SX1276SetModem( modem ); + + switch( modem ) + { + case MODEM_FSK: + if( SX1276.Settings.Fsk.FixLen == false ) + { + SX1276Write( REG_PAYLOADLENGTH, max ); + } + break; + case MODEM_LORA: + SX1276Write( REG_LR_PAYLOADMAXLENGTH, max ); + break; + } +} + +void SX1276SetPublicNetwork( bool enable ) +{ + SX1276SetModem( MODEM_LORA ); + SX1276.Settings.LoRa.PublicNetwork = enable; + if( enable == true ) + { + // Change LoRa modem SyncWord + SX1276Write( REG_LR_SYNCWORD, LORA_MAC_PUBLIC_SYNCWORD ); + } + else + { + // Change LoRa modem SyncWord + SX1276Write( REG_LR_SYNCWORD, LORA_MAC_PRIVATE_SYNCWORD ); + } +} + +uint32_t SX1276GetWakeupTime( void ) +{ + return SX1276GetBoardTcxoWakeupTime( ) + RADIO_WAKEUP_TIME; +} + +static uint32_t SX1276ConvertPllStepToFreqInHz( uint32_t pllSteps ) +{ + uint32_t freqInHzInt; + uint32_t freqInHzFrac; + + // freqInHz = pllSteps * ( SX1276_XTAL_FREQ / 2^19 ) + // Get integer and fractional parts of the frequency computed with a PLL step scaled value + freqInHzInt = pllSteps >> SX1276_PLL_STEP_SHIFT_AMOUNT; + freqInHzFrac = pllSteps - ( freqInHzInt << SX1276_PLL_STEP_SHIFT_AMOUNT ); + + // Apply the scaling factor to retrieve a frequency in Hz (+ ceiling) + return freqInHzInt * SX1276_PLL_STEP_SCALED + + ( ( freqInHzFrac * SX1276_PLL_STEP_SCALED + ( 128 ) ) >> SX1276_PLL_STEP_SHIFT_AMOUNT ); +} + +static uint32_t SX1276ConvertFreqInHzToPllStep( uint32_t freqInHz ) +{ + uint32_t stepsInt; + uint32_t stepsFrac; + + // pllSteps = freqInHz / (SX1276_XTAL_FREQ / 2^19 ) + // Get integer and fractional parts of the frequency computed with a PLL step scaled value + stepsInt = freqInHz / SX1276_PLL_STEP_SCALED; + stepsFrac = freqInHz - ( stepsInt * SX1276_PLL_STEP_SCALED ); + + // Apply the scaling factor to retrieve a frequency in Hz (+ ceiling) + return ( stepsInt << SX1276_PLL_STEP_SHIFT_AMOUNT ) + + ( ( ( stepsFrac << SX1276_PLL_STEP_SHIFT_AMOUNT ) + ( SX1276_PLL_STEP_SCALED >> 1 ) ) / + SX1276_PLL_STEP_SCALED ); +} + +static uint8_t GetFskBandwidthRegValue( uint32_t bw ) +{ + uint8_t i; + + for( i = 0; i < ( sizeof( FskBandwidths ) / sizeof( FskBandwidth_t ) ) - 1; i++ ) + { + if( ( bw >= FskBandwidths[i].bandwidth ) && ( bw < FskBandwidths[i + 1].bandwidth ) ) + { + return FskBandwidths[i].RegValue; + } + } + // ERROR: Value not found + while( 1 ); +} + +static uint32_t SX1276GetLoRaBandwidthInHz( uint32_t bw ) +{ + uint32_t bandwidthInHz = 0; + + switch( bw ) + { + case 0: // 125 kHz + bandwidthInHz = 125000UL; + break; + case 1: // 250 kHz + bandwidthInHz = 250000UL; + break; + case 2: // 500 kHz + bandwidthInHz = 500000UL; + break; + } + + return bandwidthInHz; +} + +static uint32_t SX1276GetGfskTimeOnAirNumerator( uint16_t preambleLen, bool fixLen, + uint8_t payloadLen, bool crcOn ) +{ + const uint8_t syncWordLength = 3; + + return ( preambleLen << 3 ) + + ( ( fixLen == false ) ? 8 : 0 ) + + ( syncWordLength << 3 ) + + ( ( payloadLen + + ( 0 ) + // Address filter size + ( ( crcOn == true ) ? 2 : 0 ) + ) << 3 + ); +} + +static uint32_t SX1276GetLoRaTimeOnAirNumerator( uint32_t bandwidth, + uint32_t datarate, uint8_t coderate, + uint16_t preambleLen, bool fixLen, uint8_t payloadLen, + bool crcOn ) +{ + int32_t crDenom = coderate + 4; + bool lowDatareOptimize = false; + + // Ensure that the preamble length is at least 12 symbols when using SF5 or + // SF6 + if( ( datarate == 5 ) || ( datarate == 6 ) ) + { + if( preambleLen < 12 ) + { + preambleLen = 12; + } + } + + if( ( ( bandwidth == 0 ) && ( ( datarate == 11 ) || ( datarate == 12 ) ) ) || + ( ( bandwidth == 1 ) && ( datarate == 12 ) ) ) + { + lowDatareOptimize = true; + } + + int32_t ceilDenominator; + int32_t ceilNumerator = ( payloadLen << 3 ) + + ( crcOn ? 16 : 0 ) - + ( 4 * datarate ) + + ( fixLen ? 0 : 20 ); + + if( datarate <= 6 ) + { + ceilDenominator = 4 * datarate; + } + else + { + ceilNumerator += 8; + + if( lowDatareOptimize == true ) + { + ceilDenominator = 4 * ( datarate - 2 ); + } + else + { + ceilDenominator = 4 * datarate; + } + } + + if( ceilNumerator < 0 ) + { + ceilNumerator = 0; + } + + // Perform integral ceil() + int32_t intermediate = + ( ( ceilNumerator + ceilDenominator - 1 ) / ceilDenominator ) * crDenom + preambleLen + 12; + + if( datarate <= 6 ) + { + intermediate += 2; + } + + return ( uint32_t )( ( 4 * intermediate + 1 ) * ( 1 << ( datarate - 2 ) ) ); +} + +static void SX1276OnTimeoutIrq( void* context ) +{ + switch( SX1276.Settings.State ) + { + case RF_RX_RUNNING: + if( SX1276.Settings.Modem == MODEM_FSK ) + { + SX1276.Settings.FskPacketHandler.PreambleDetected = false; + SX1276.Settings.FskPacketHandler.SyncWordDetected = false; + SX1276.Settings.FskPacketHandler.NbBytes = 0; + SX1276.Settings.FskPacketHandler.Size = 0; + + // Clear Irqs + SX1276Write( REG_IRQFLAGS1, RF_IRQFLAGS1_RSSI | + RF_IRQFLAGS1_PREAMBLEDETECT | + RF_IRQFLAGS1_SYNCADDRESSMATCH ); + SX1276Write( REG_IRQFLAGS2, RF_IRQFLAGS2_FIFOOVERRUN ); + + if( SX1276.Settings.Fsk.RxContinuous == true ) + { + // Continuous mode restart Rx chain + SX1276Write( REG_RXCONFIG, SX1276Read( REG_RXCONFIG ) | RF_RXCONFIG_RESTARTRXWITHOUTPLLLOCK ); + } + else + { + SX1276.Settings.State = RF_IDLE; + TimerStop( &RxTimeoutSyncWord ); + } + } + if( ( RadioEvents != NULL ) && ( RadioEvents->RxTimeout != NULL ) ) + { + RadioEvents->RxTimeout( ); + } + break; + case RF_TX_RUNNING: + // Tx timeout shouldn't happen. + // Reported issue of SPI data corruption resulting in TX TIMEOUT + // is NOT related to a bug in radio transceiver. + // It is mainly caused by improper PCB routing of SPI lines and/or + // violation of SPI specifications. + // To mitigate redesign, Semtech offers a workaround which resets + // the radio transceiver and putting it into a known state. + + // BEGIN WORKAROUND + + // Reset the radio + SX1276Reset( ); + + // Calibrate Rx chain + RxChainCalibration( ); + + // Initialize radio default values + SX1276SetOpMode( RF_OPMODE_SLEEP ); + + for( uint8_t i = 0; i < sizeof( RadioRegsInit ) / sizeof( RadioRegisters_t ); i++ ) + { + SX1276SetModem( RadioRegsInit[i].Modem ); + SX1276Write( RadioRegsInit[i].Addr, RadioRegsInit[i].Value ); + } + SX1276SetModem( MODEM_FSK ); + + // Restore previous network type setting. + SX1276SetPublicNetwork( SX1276.Settings.LoRa.PublicNetwork ); + // END WORKAROUND + + SX1276.Settings.State = RF_IDLE; + if( ( RadioEvents != NULL ) && ( RadioEvents->TxTimeout != NULL ) ) + { + RadioEvents->TxTimeout( ); + } + break; + default: + break; + } +} + +static void SX1276OnDio0Irq( void* context ) +{ + volatile uint8_t irqFlags = 0; + + switch( SX1276.Settings.State ) + { + case RF_RX_RUNNING: + //TimerStop( &RxTimeoutTimer ); + // RxDone interrupt + switch( SX1276.Settings.Modem ) + { + case MODEM_FSK: + if( SX1276.Settings.Fsk.CrcOn == true ) + { + irqFlags = SX1276Read( REG_IRQFLAGS2 ); + if( ( irqFlags & RF_IRQFLAGS2_CRCOK ) != RF_IRQFLAGS2_CRCOK ) + { + // Clear Irqs + SX1276Write( REG_IRQFLAGS1, RF_IRQFLAGS1_RSSI | + RF_IRQFLAGS1_PREAMBLEDETECT | + RF_IRQFLAGS1_SYNCADDRESSMATCH ); + SX1276Write( REG_IRQFLAGS2, RF_IRQFLAGS2_FIFOOVERRUN ); + + TimerStop( &RxTimeoutTimer ); + + if( SX1276.Settings.Fsk.RxContinuous == false ) + { + TimerStop( &RxTimeoutSyncWord ); + SX1276.Settings.State = RF_IDLE; + } + else + { + // Continuous mode restart Rx chain + SX1276Write( REG_RXCONFIG, SX1276Read( REG_RXCONFIG ) | RF_RXCONFIG_RESTARTRXWITHOUTPLLLOCK ); + } + + if( ( RadioEvents != NULL ) && ( RadioEvents->RxError != NULL ) ) + { + RadioEvents->RxError( ); + } + SX1276.Settings.FskPacketHandler.PreambleDetected = false; + SX1276.Settings.FskPacketHandler.SyncWordDetected = false; + SX1276.Settings.FskPacketHandler.NbBytes = 0; + SX1276.Settings.FskPacketHandler.Size = 0; + break; + } + } + + // Read received packet size + if( ( SX1276.Settings.FskPacketHandler.Size == 0 ) && ( SX1276.Settings.FskPacketHandler.NbBytes == 0 ) ) + { + if( SX1276.Settings.Fsk.FixLen == false ) + { + SX1276ReadFifo( ( uint8_t* )&SX1276.Settings.FskPacketHandler.Size, 1 ); + } + else + { + SX1276.Settings.FskPacketHandler.Size = SX1276Read( REG_PAYLOADLENGTH ); + } + SX1276ReadFifo( RxTxBuffer + SX1276.Settings.FskPacketHandler.NbBytes, SX1276.Settings.FskPacketHandler.Size - SX1276.Settings.FskPacketHandler.NbBytes ); + SX1276.Settings.FskPacketHandler.NbBytes += ( SX1276.Settings.FskPacketHandler.Size - SX1276.Settings.FskPacketHandler.NbBytes ); + } + else + { + SX1276ReadFifo( RxTxBuffer + SX1276.Settings.FskPacketHandler.NbBytes, SX1276.Settings.FskPacketHandler.Size - SX1276.Settings.FskPacketHandler.NbBytes ); + SX1276.Settings.FskPacketHandler.NbBytes += ( SX1276.Settings.FskPacketHandler.Size - SX1276.Settings.FskPacketHandler.NbBytes ); + } + + TimerStop( &RxTimeoutTimer ); + + if( SX1276.Settings.Fsk.RxContinuous == false ) + { + SX1276.Settings.State = RF_IDLE; + TimerStop( &RxTimeoutSyncWord ); + } + else + { + // Continuous mode restart Rx chain + SX1276Write( REG_RXCONFIG, SX1276Read( REG_RXCONFIG ) | RF_RXCONFIG_RESTARTRXWITHOUTPLLLOCK ); + } + + if( ( RadioEvents != NULL ) && ( RadioEvents->RxDone != NULL ) ) + { + RadioEvents->RxDone( RxTxBuffer, SX1276.Settings.FskPacketHandler.Size, SX1276.Settings.FskPacketHandler.RssiValue, 0 ); + } + SX1276.Settings.FskPacketHandler.PreambleDetected = false; + SX1276.Settings.FskPacketHandler.SyncWordDetected = false; + SX1276.Settings.FskPacketHandler.NbBytes = 0; + SX1276.Settings.FskPacketHandler.Size = 0; + break; + case MODEM_LORA: + { + // Clear Irq + SX1276Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_RXDONE ); + + irqFlags = SX1276Read( REG_LR_IRQFLAGS ); + if( ( irqFlags & RFLR_IRQFLAGS_PAYLOADCRCERROR_MASK ) == RFLR_IRQFLAGS_PAYLOADCRCERROR ) + { + // Clear Irq + SX1276Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_PAYLOADCRCERROR ); + + if( SX1276.Settings.LoRa.RxContinuous == false ) + { + SX1276.Settings.State = RF_IDLE; + } + TimerStop( &RxTimeoutTimer ); + + if( ( RadioEvents != NULL ) && ( RadioEvents->RxError != NULL ) ) + { + RadioEvents->RxError( ); + } + break; + } + + // Returns SNR value [dB] rounded to the nearest integer value + SX1276.Settings.LoRaPacketHandler.SnrValue = ( ( ( int8_t )SX1276Read( REG_LR_PKTSNRVALUE ) ) + 2 ) >> 2; + + int16_t rssi = SX1276Read( REG_LR_PKTRSSIVALUE ); + if( SX1276.Settings.LoRaPacketHandler.SnrValue < 0 ) + { + if( SX1276.Settings.Channel > RF_MID_BAND_THRESH ) + { + SX1276.Settings.LoRaPacketHandler.RssiValue = RSSI_OFFSET_HF + rssi + ( rssi >> 4 ) + + SX1276.Settings.LoRaPacketHandler.SnrValue; + } + else + { + SX1276.Settings.LoRaPacketHandler.RssiValue = RSSI_OFFSET_LF + rssi + ( rssi >> 4 ) + + SX1276.Settings.LoRaPacketHandler.SnrValue; + } + } + else + { + if( SX1276.Settings.Channel > RF_MID_BAND_THRESH ) + { + SX1276.Settings.LoRaPacketHandler.RssiValue = RSSI_OFFSET_HF + rssi + ( rssi >> 4 ); + } + else + { + SX1276.Settings.LoRaPacketHandler.RssiValue = RSSI_OFFSET_LF + rssi + ( rssi >> 4 ); + } + } + + SX1276.Settings.LoRaPacketHandler.Size = SX1276Read( REG_LR_RXNBBYTES ); + SX1276Write( REG_LR_FIFOADDRPTR, SX1276Read( REG_LR_FIFORXCURRENTADDR ) ); + SX1276ReadFifo( RxTxBuffer, SX1276.Settings.LoRaPacketHandler.Size ); + + if( SX1276.Settings.LoRa.RxContinuous == false ) + { + SX1276.Settings.State = RF_IDLE; + } + TimerStop( &RxTimeoutTimer ); + + if( ( RadioEvents != NULL ) && ( RadioEvents->RxDone != NULL ) ) + { + RadioEvents->RxDone( RxTxBuffer, SX1276.Settings.LoRaPacketHandler.Size, SX1276.Settings.LoRaPacketHandler.RssiValue, SX1276.Settings.LoRaPacketHandler.SnrValue ); + } + } + break; + default: + break; + } + break; + case RF_TX_RUNNING: + TimerStop( &TxTimeoutTimer ); + // TxDone interrupt + switch( SX1276.Settings.Modem ) + { + case MODEM_LORA: + // Clear Irq + SX1276Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_TXDONE ); + // Intentional fall through + case MODEM_FSK: + default: + SX1276.Settings.State = RF_IDLE; + if( ( RadioEvents != NULL ) && ( RadioEvents->TxDone != NULL ) ) + { + RadioEvents->TxDone( ); + } + break; + } + break; + default: + break; + } +} + +static void SX1276OnDio1Irq( void* context ) +{ + switch( SX1276.Settings.State ) + { + case RF_RX_RUNNING: + switch( SX1276.Settings.Modem ) + { + case MODEM_FSK: + // Check FIFO level DIO1 pin state + // + // As DIO1 interrupt is triggered when a rising or a falling edge is detected the IRQ handler must + // verify DIO1 pin state in order to decide if something has to be done. + // When radio is operating in FSK reception mode a rising edge must be detected in order to handle the + // IRQ. + if( SX1276GetDio1PinState( ) == 0 ) + { + break; + } + // Stop timer + TimerStop( &RxTimeoutSyncWord ); + + // FifoLevel interrupt + // Read received packet size + if( ( SX1276.Settings.FskPacketHandler.Size == 0 ) && ( SX1276.Settings.FskPacketHandler.NbBytes == 0 ) ) + { + if( SX1276.Settings.Fsk.FixLen == false ) + { + SX1276ReadFifo( ( uint8_t* )&SX1276.Settings.FskPacketHandler.Size, 1 ); + } + else + { + SX1276.Settings.FskPacketHandler.Size = SX1276Read( REG_PAYLOADLENGTH ); + } + } + + // ERRATA 3.1 - PayloadReady Set for 31.25ns if FIFO is Empty + // + // When FifoLevel interrupt is used to offload the + // FIFO, the microcontroller should monitor both + // PayloadReady and FifoLevel interrupts, and + // read only (FifoThreshold-1) bytes off the FIFO + // when FifoLevel fires + if( ( SX1276.Settings.FskPacketHandler.Size - SX1276.Settings.FskPacketHandler.NbBytes ) >= SX1276.Settings.FskPacketHandler.FifoThresh ) + { + SX1276ReadFifo( ( RxTxBuffer + SX1276.Settings.FskPacketHandler.NbBytes ), SX1276.Settings.FskPacketHandler.FifoThresh - 1 ); + SX1276.Settings.FskPacketHandler.NbBytes += SX1276.Settings.FskPacketHandler.FifoThresh - 1; + } + else + { + SX1276ReadFifo( ( RxTxBuffer + SX1276.Settings.FskPacketHandler.NbBytes ), SX1276.Settings.FskPacketHandler.Size - SX1276.Settings.FskPacketHandler.NbBytes ); + SX1276.Settings.FskPacketHandler.NbBytes += ( SX1276.Settings.FskPacketHandler.Size - SX1276.Settings.FskPacketHandler.NbBytes ); + } + break; + case MODEM_LORA: + // Check RxTimeout DIO1 pin state + // + // DIO1 irq is setup to be triggered on rsing and falling edges + // As DIO1 interrupt is triggered when a rising or a falling edge is detected the IRQ handler must + // verify DIO1 pin state in order to decide if something has to be done. + // When radio is operating in LoRa reception mode a rising edge must be detected in order to handle the + // IRQ. + if( SX1276GetDio1PinState( ) == 0 ) + { + break; + } + // Sync time out + TimerStop( &RxTimeoutTimer ); + // Clear Irq + SX1276Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_RXTIMEOUT ); + + SX1276.Settings.State = RF_IDLE; + if( ( RadioEvents != NULL ) && ( RadioEvents->RxTimeout != NULL ) ) + { + RadioEvents->RxTimeout( ); + } + break; + default: + break; + } + break; + case RF_TX_RUNNING: + switch( SX1276.Settings.Modem ) + { + case MODEM_FSK: + // Check FIFO level DIO1 pin state + // + // As DIO1 interrupt is triggered when a rising or a falling edge is detected the IRQ handler must + // verify DIO1 pin state in order to decide if something has to be done. + // When radio is operating in FSK transmission mode a falling edge must be detected in order to handle + // the IRQ. + if( SX1276GetDio1PinState( ) == 1 ) + { + break; + } + + // FifoLevel interrupt + if( ( SX1276.Settings.FskPacketHandler.Size - SX1276.Settings.FskPacketHandler.NbBytes ) > SX1276.Settings.FskPacketHandler.ChunkSize ) + { + SX1276WriteFifo( ( RxTxBuffer + SX1276.Settings.FskPacketHandler.NbBytes ), SX1276.Settings.FskPacketHandler.ChunkSize ); + SX1276.Settings.FskPacketHandler.NbBytes += SX1276.Settings.FskPacketHandler.ChunkSize; + } + else + { + // Write the last chunk of data + SX1276WriteFifo( RxTxBuffer + SX1276.Settings.FskPacketHandler.NbBytes, SX1276.Settings.FskPacketHandler.Size - SX1276.Settings.FskPacketHandler.NbBytes ); + SX1276.Settings.FskPacketHandler.NbBytes += SX1276.Settings.FskPacketHandler.Size - SX1276.Settings.FskPacketHandler.NbBytes; + } + break; + case MODEM_LORA: + break; + default: + break; + } + break; + default: + break; + } +} + +static void SX1276OnDio2Irq( void* context ) +{ + switch( SX1276.Settings.State ) + { + case RF_RX_RUNNING: + switch( SX1276.Settings.Modem ) + { + case MODEM_FSK: + // Checks if DIO4 is connected. If it is not PreambleDetected is set to true. + //if( SX1276.DIO4.port == NULL ) + //{ + SX1276.Settings.FskPacketHandler.PreambleDetected = true; + //} + + if( ( SX1276.Settings.FskPacketHandler.PreambleDetected != 0 ) && ( SX1276.Settings.FskPacketHandler.SyncWordDetected == 0 ) ) + { + TimerStop( &RxTimeoutSyncWord ); + + SX1276.Settings.FskPacketHandler.SyncWordDetected = true; + + SX1276.Settings.FskPacketHandler.RssiValue = -( SX1276Read( REG_RSSIVALUE ) >> 1 ); + + SX1276.Settings.FskPacketHandler.AfcValue = ( int32_t )SX1276ConvertPllStepToFreqInHz( ( ( uint16_t )SX1276Read( REG_AFCMSB ) << 8 ) | + ( uint16_t )SX1276Read( REG_AFCLSB ) ); + SX1276.Settings.FskPacketHandler.RxGain = ( SX1276Read( REG_LNA ) >> 5 ) & 0x07; + } + break; + case MODEM_LORA: + if( SX1276.Settings.LoRa.FreqHopOn == true ) + { + // Clear Irq + SX1276Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL ); + + if( ( RadioEvents != NULL ) && ( RadioEvents->FhssChangeChannel != NULL ) ) + { + RadioEvents->FhssChangeChannel( ( SX1276Read( REG_LR_HOPCHANNEL ) & RFLR_HOPCHANNEL_CHANNEL_MASK ) ); + } + } + break; + default: + break; + } + break; + case RF_TX_RUNNING: + switch( SX1276.Settings.Modem ) + { + case MODEM_FSK: + break; + case MODEM_LORA: + if( SX1276.Settings.LoRa.FreqHopOn == true ) + { + // Clear Irq + SX1276Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL ); + + if( ( RadioEvents != NULL ) && ( RadioEvents->FhssChangeChannel != NULL ) ) + { + RadioEvents->FhssChangeChannel( ( SX1276Read( REG_LR_HOPCHANNEL ) & RFLR_HOPCHANNEL_CHANNEL_MASK ) ); + } + } + break; + default: + break; + } + break; + default: + break; + } +} + +static void SX1276OnDio3Irq( void* context ) +{ + switch( SX1276.Settings.Modem ) + { + case MODEM_FSK: + break; + case MODEM_LORA: + if( ( SX1276Read( REG_LR_IRQFLAGS ) & RFLR_IRQFLAGS_CADDETECTED ) == RFLR_IRQFLAGS_CADDETECTED ) + { + // Clear Irq + SX1276Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_CADDETECTED | RFLR_IRQFLAGS_CADDONE ); + if( ( RadioEvents != NULL ) && ( RadioEvents->CadDone != NULL ) ) + { + RadioEvents->CadDone( true ); + } + } + else + { + // Clear Irq + SX1276Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_CADDONE ); + if( ( RadioEvents != NULL ) && ( RadioEvents->CadDone != NULL ) ) + { + RadioEvents->CadDone( false ); + } + } + break; + default: + break; + } +} + +static void SX1276OnDio4Irq( void* context ) +{ + switch( SX1276.Settings.Modem ) + { + case MODEM_FSK: + { + if( SX1276.Settings.FskPacketHandler.PreambleDetected == false ) + { + SX1276.Settings.FskPacketHandler.PreambleDetected = true; + } + } + break; + case MODEM_LORA: + break; + default: + break; + } +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/sx1276.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/sx1276.h new file mode 100644 index 0000000000..82df7911f2 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/sx1276.h @@ -0,0 +1,454 @@ +/*! + * \file sx1276.h + * + * \brief SX1276 driver implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * \endcode + * + * \author Alistair Jordan + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + */ +#ifndef __SX1276_H__ +#define __SX1276_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include +//#include "gpio.h" +#include "spi.h" +#include "radio.h" +#include "sx1276Regs-Fsk.h" +#include "sx1276Regs-LoRa.h" + +/*! + * Radio wake-up time from sleep + */ +#define RADIO_WAKEUP_TIME 1 // [ms] + +/*! + * Sync word for Private LoRa networks + */ +#define LORA_MAC_PRIVATE_SYNCWORD 0x12 + +/*! + * Sync word for Public LoRa networks + */ +#define LORA_MAC_PUBLIC_SYNCWORD 0x34 + +/*! + * Radio FSK modem parameters + */ +typedef struct +{ + int8_t Power; + uint32_t Fdev; + uint32_t Bandwidth; + uint32_t BandwidthAfc; + uint32_t Datarate; + uint16_t PreambleLen; + bool FixLen; + uint8_t PayloadLen; + bool CrcOn; + bool IqInverted; + bool RxContinuous; + uint32_t TxTimeout; + uint32_t RxSingleTimeout; +}RadioFskSettings_t; + +/*! + * Radio FSK packet handler state + */ +typedef struct +{ + uint8_t PreambleDetected; + uint8_t SyncWordDetected; + int8_t RssiValue; + int32_t AfcValue; + uint8_t RxGain; + uint16_t Size; + uint16_t NbBytes; + uint8_t FifoThresh; + uint8_t ChunkSize; +}RadioFskPacketHandler_t; + +/*! + * Radio LoRa modem parameters + */ +typedef struct +{ + int8_t Power; + uint32_t Bandwidth; + uint32_t Datarate; + bool LowDatarateOptimize; + uint8_t Coderate; + uint16_t PreambleLen; + bool FixLen; + uint8_t PayloadLen; + bool CrcOn; + bool FreqHopOn; + uint8_t HopPeriod; + bool IqInverted; + bool RxContinuous; + uint32_t TxTimeout; + bool PublicNetwork; +}RadioLoRaSettings_t; + +/*! + * Radio LoRa packet handler state + */ +typedef struct +{ + int8_t SnrValue; + int16_t RssiValue; + uint8_t Size; +}RadioLoRaPacketHandler_t; + +/*! + * Radio Settings + */ +typedef struct +{ + RadioState_t State; + RadioModems_t Modem; + uint32_t Channel; + RadioFskSettings_t Fsk; + RadioFskPacketHandler_t FskPacketHandler; + RadioLoRaSettings_t LoRa; + RadioLoRaPacketHandler_t LoRaPacketHandler; +}RadioSettings_t; + +/*! + * Radio hardware and global parameters + */ +typedef struct SX1276_s +{ + // Gpio_t Reset; + // Gpio_t DIO0; + // Gpio_t DIO1; + // Gpio_t DIO2; + // Gpio_t DIO3; + // Gpio_t DIO4; + // Gpio_t DIO5; + Spi_t Spi; + RadioSettings_t Settings; +}SX1276_t; + +/*! + * Hardware IO IRQ callback function definition + */ +typedef void ( DioIrqHandler )( void* context ); + +/* + * SX1276 definitions + */ + +/*! + * ============================================================================ + * Public functions prototypes + * ============================================================================ + */ + +/*! + * \brief Initializes the radio + * + * \param [IN] events Structure containing the driver callback functions + */ +void SX1276Init( RadioEvents_t *events ); + +/*! + * Return current radio status + * + * \param status Radio status.[RF_IDLE, RF_RX_RUNNING, RF_TX_RUNNING] + */ +RadioState_t SX1276GetStatus( void ); + +/*! + * \brief Configures the radio with the given modem + * + * \param [IN] modem Modem to be used [0: FSK, 1: LoRa] + */ +void SX1276SetModem( RadioModems_t modem ); + +/*! + * \brief Sets the channel configuration + * + * \param [IN] freq Channel RF frequency + */ +void SX1276SetChannel( uint32_t freq ); + +/*! + * \brief Checks if the channel is free for the given time + * + * \remark The FSK modem is always used for this task as we can select the Rx bandwidth at will. + * + * \param [IN] freq Channel RF frequency in Hertz + * \param [IN] rxBandwidth Rx bandwidth in Hertz + * \param [IN] rssiThresh RSSI threshold in dBm + * \param [IN] maxCarrierSenseTime Max time in milliseconds while the RSSI is measured + * + * \retval isFree [true: Channel is free, false: Channel is not free] + */ +bool SX1276IsChannelFree( uint32_t freq, uint32_t rxBandwidth, int16_t rssiThresh, uint32_t maxCarrierSenseTime ); + +/*! + * \brief Generates a 32 bits random value based on the RSSI readings + * + * \remark This function sets the radio in LoRa modem mode and disables + * all interrupts. + * After calling this function either SX1276SetRxConfig or + * SX1276SetTxConfig functions must be called. + * + * \retval randomValue 32 bits random value + */ +uint32_t SX1276Random( void ); + +/*! + * \brief Sets the reception parameters + * + * \remark When using LoRa modem only bandwidths 125, 250 and 500 kHz are supported + * + * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * \param [IN] bandwidth Sets the bandwidth + * FSK : >= 2600 and <= 250000 Hz + * LoRa: [0: 125 kHz, 1: 250 kHz, + * 2: 500 kHz, 3: Reserved] + * \param [IN] datarate Sets the Datarate + * FSK : 600..300000 bits/s + * LoRa: [6: 64, 7: 128, 8: 256, 9: 512, + * 10: 1024, 11: 2048, 12: 4096 chips] + * \param [IN] coderate Sets the coding rate (LoRa only) + * FSK : N/A ( set to 0 ) + * LoRa: [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] + * \param [IN] bandwidthAfc Sets the AFC Bandwidth (FSK only) + * FSK : >= 2600 and <= 250000 Hz + * LoRa: N/A ( set to 0 ) + * \param [IN] preambleLen Sets the Preamble length + * FSK : Number of bytes + * LoRa: Length in symbols (the hardware adds 4 more symbols) + * \param [IN] symbTimeout Sets the RxSingle timeout value + * FSK : timeout number of bytes + * LoRa: timeout in symbols + * \param [IN] fixLen Fixed length packets [0: variable, 1: fixed] + * \param [IN] payloadLen Sets payload length when fixed length is used + * \param [IN] crcOn Enables/Disables the CRC [0: OFF, 1: ON] + * \param [IN] freqHopOn Enables disables the intra-packet frequency hopping + * FSK : N/A ( set to 0 ) + * LoRa: [0: OFF, 1: ON] + * \param [IN] hopPeriod Number of symbols between each hop + * FSK : N/A ( set to 0 ) + * LoRa: Number of symbols + * \param [IN] iqInverted Inverts IQ signals (LoRa only) + * FSK : N/A ( set to 0 ) + * LoRa: [0: not inverted, 1: inverted] + * \param [IN] rxContinuous Sets the reception in continuous mode + * [false: single mode, true: continuous mode] + */ +void SX1276SetRxConfig( RadioModems_t modem, uint32_t bandwidth, + uint32_t datarate, uint8_t coderate, + uint32_t bandwidthAfc, uint16_t preambleLen, + uint16_t symbTimeout, bool fixLen, + uint8_t payloadLen, + bool crcOn, bool freqHopOn, uint8_t hopPeriod, + bool iqInverted, bool rxContinuous ); + +/*! + * \brief Sets the transmission parameters + * + * \remark When using LoRa modem only bandwidths 125, 250 and 500 kHz are supported + * + * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * \param [IN] power Sets the output power [dBm] + * \param [IN] fdev Sets the frequency deviation (FSK only) + * FSK : [Hz] + * LoRa: 0 + * \param [IN] bandwidth Sets the bandwidth (LoRa only) + * FSK : 0 + * LoRa: [0: 125 kHz, 1: 250 kHz, + * 2: 500 kHz, 3: Reserved] + * \param [IN] datarate Sets the Datarate + * FSK : 600..300000 bits/s + * LoRa: [6: 64, 7: 128, 8: 256, 9: 512, + * 10: 1024, 11: 2048, 12: 4096 chips] + * \param [IN] coderate Sets the coding rate (LoRa only) + * FSK : N/A ( set to 0 ) + * LoRa: [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] + * \param [IN] preambleLen Sets the preamble length + * FSK : Number of bytes + * LoRa: Length in symbols (the hardware adds 4 more symbols) + * \param [IN] fixLen Fixed length packets [0: variable, 1: fixed] + * \param [IN] crcOn Enables disables the CRC [0: OFF, 1: ON] + * \param [IN] freqHopOn Enables disables the intra-packet frequency hopping + * FSK : N/A ( set to 0 ) + * LoRa: [0: OFF, 1: ON] + * \param [IN] hopPeriod Number of symbols between each hop + * FSK : N/A ( set to 0 ) + * LoRa: Number of symbols + * \param [IN] iqInverted Inverts IQ signals (LoRa only) + * FSK : N/A ( set to 0 ) + * LoRa: [0: not inverted, 1: inverted] + * \param [IN] timeout Transmission timeout [ms] + */ +void SX1276SetTxConfig( RadioModems_t modem, int8_t power, uint32_t fdev, + uint32_t bandwidth, uint32_t datarate, + uint8_t coderate, uint16_t preambleLen, + bool fixLen, bool crcOn, bool freqHopOn, + uint8_t hopPeriod, bool iqInverted, uint32_t timeout ); + +/*! + * \brief Computes the packet time on air in ms for the given payload + * + * \Remark Can only be called once SetRxConfig or SetTxConfig have been called + * + * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * \param [IN] bandwidth Sets the bandwidth + * FSK : >= 2600 and <= 250000 Hz + * LoRa: [0: 125 kHz, 1: 250 kHz, + * 2: 500 kHz, 3: Reserved] + * \param [IN] datarate Sets the Datarate + * FSK : 600..300000 bits/s + * LoRa: [6: 64, 7: 128, 8: 256, 9: 512, + * 10: 1024, 11: 2048, 12: 4096 chips] + * \param [IN] coderate Sets the coding rate (LoRa only) + * FSK : N/A ( set to 0 ) + * LoRa: [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] + * \param [IN] preambleLen Sets the Preamble length + * FSK : Number of bytes + * LoRa: Length in symbols (the hardware adds 4 more symbols) + * \param [IN] fixLen Fixed length packets [0: variable, 1: fixed] + * \param [IN] payloadLen Sets payload length when fixed length is used + * \param [IN] crcOn Enables/Disables the CRC [0: OFF, 1: ON] + * + * \retval airTime Computed airTime (ms) for the given packet payload length + */ +uint32_t SX1276GetTimeOnAir( RadioModems_t modem, uint32_t bandwidth, + uint32_t datarate, uint8_t coderate, + uint16_t preambleLen, bool fixLen, uint8_t payloadLen, + bool crcOn ); + +/*! + * \brief Sends the buffer of size. Prepares the packet to be sent and sets + * the radio in transmission + * + * \param [IN]: buffer Buffer pointer + * \param [IN]: size Buffer size + */ +void SX1276Send( uint8_t *buffer, uint8_t size ); + +/*! + * \brief Sets the radio in sleep mode + */ +void SX1276SetSleep( void ); + +/*! + * \brief Sets the radio in standby mode + */ +void SX1276SetStby( void ); + +/*! + * \brief Sets the radio in reception mode for the given time + * \param [IN] timeout Reception timeout [ms] [0: continuous, others timeout] + */ +void SX1276SetRx( uint32_t timeout ); + +/*! + * \brief Start a Channel Activity Detection + */ +void SX1276StartCad( void ); + +/*! + * \brief Sets the radio in continuous wave transmission mode + * + * \param [IN]: freq Channel RF frequency + * \param [IN]: power Sets the output power [dBm] + * \param [IN]: time Transmission mode timeout [s] + */ +void SX1276SetTxContinuousWave( uint32_t freq, int8_t power, uint16_t time ); + +/*! + * \brief Reads the current RSSI value + * + * \retval rssiValue Current RSSI value in [dBm] + */ +int16_t SX1276ReadRssi( RadioModems_t modem ); + +/*! + * \brief Writes the radio register at the specified address + * + * \param [IN]: addr Register address + * \param [IN]: data New register value + */ +void SX1276Write( uint32_t addr, uint8_t data ); + +/*! + * \brief Reads the radio register at the specified address + * + * \param [IN]: addr Register address + * \retval data Register value + */ +uint8_t SX1276Read( uint32_t addr ); + +/*! + * \brief Writes multiple radio registers starting at address + * + * \param [IN] addr First Radio register address + * \param [IN] buffer Buffer containing the new register's values + * \param [IN] size Number of registers to be written + */ +void SX1276WriteBuffer( uint32_t addr, uint8_t *buffer, uint8_t size ); + +/*! + * \brief Reads multiple radio registers starting at address + * + * \param [IN] addr First Radio register address + * \param [OUT] buffer Buffer where to copy the registers data + * \param [IN] size Number of registers to be read + */ +void SX1276ReadBuffer( uint32_t addr, uint8_t *buffer, uint8_t size ); + +/*! + * \brief Sets the maximum payload length. + * + * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * \param [IN] max Maximum payload length in bytes + */ +void SX1276SetMaxPayloadLength( RadioModems_t modem, uint8_t max ); + +/*! + * \brief Sets the network to public or private. Updates the sync byte. + * + * \remark Applies to LoRa modem only + * + * \param [IN] enable if true, it enables a public network + */ +void SX1276SetPublicNetwork( bool enable ); + +/*! + * \brief Gets the time required for the board plus radio to get out of sleep.[ms] + * + * \retval time Radio plus board wakeup time in ms. + */ +uint32_t SX1276GetWakeupTime( void ); + +#ifdef __cplusplus +} +#endif + +#endif // __SX1276_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/sx1276Regs-Fsk.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/sx1276Regs-Fsk.h new file mode 100644 index 0000000000..e31ad0cbe9 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/sx1276Regs-Fsk.h @@ -0,0 +1,1151 @@ +/*! + * \file sx1276Regs-Fsk.h + * + * \brief SX1276 FSK modem registers and bits definitions + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + */ +#ifndef __SX1276_REGS_FSK_H__ +#define __SX1276_REGS_FSK_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*! + * ============================================================================ + * SX1276 Internal registers Address + * ============================================================================ + */ +#define REG_FIFO 0x00 +// Common settings +#define REG_OPMODE 0x01 +#define REG_BITRATEMSB 0x02 +#define REG_BITRATELSB 0x03 +#define REG_FDEVMSB 0x04 +#define REG_FDEVLSB 0x05 +#define REG_FRFMSB 0x06 +#define REG_FRFMID 0x07 +#define REG_FRFLSB 0x08 +// Tx settings +#define REG_PACONFIG 0x09 +#define REG_PARAMP 0x0A +#define REG_OCP 0x0B +// Rx settings +#define REG_LNA 0x0C +#define REG_RXCONFIG 0x0D +#define REG_RSSICONFIG 0x0E +#define REG_RSSICOLLISION 0x0F +#define REG_RSSITHRESH 0x10 +#define REG_RSSIVALUE 0x11 +#define REG_RXBW 0x12 +#define REG_AFCBW 0x13 +#define REG_OOKPEAK 0x14 +#define REG_OOKFIX 0x15 +#define REG_OOKAVG 0x16 +#define REG_RES17 0x17 +#define REG_RES18 0x18 +#define REG_RES19 0x19 +#define REG_AFCFEI 0x1A +#define REG_AFCMSB 0x1B +#define REG_AFCLSB 0x1C +#define REG_FEIMSB 0x1D +#define REG_FEILSB 0x1E +#define REG_PREAMBLEDETECT 0x1F +#define REG_RXTIMEOUT1 0x20 +#define REG_RXTIMEOUT2 0x21 +#define REG_RXTIMEOUT3 0x22 +#define REG_RXDELAY 0x23 +// Oscillator settings +#define REG_OSC 0x24 +// Packet handler settings +#define REG_PREAMBLEMSB 0x25 +#define REG_PREAMBLELSB 0x26 +#define REG_SYNCCONFIG 0x27 +#define REG_SYNCVALUE1 0x28 +#define REG_SYNCVALUE2 0x29 +#define REG_SYNCVALUE3 0x2A +#define REG_SYNCVALUE4 0x2B +#define REG_SYNCVALUE5 0x2C +#define REG_SYNCVALUE6 0x2D +#define REG_SYNCVALUE7 0x2E +#define REG_SYNCVALUE8 0x2F +#define REG_PACKETCONFIG1 0x30 +#define REG_PACKETCONFIG2 0x31 +#define REG_PAYLOADLENGTH 0x32 +#define REG_NODEADRS 0x33 +#define REG_BROADCASTADRS 0x34 +#define REG_FIFOTHRESH 0x35 +// SM settings +#define REG_SEQCONFIG1 0x36 +#define REG_SEQCONFIG2 0x37 +#define REG_TIMERRESOL 0x38 +#define REG_TIMER1COEF 0x39 +#define REG_TIMER2COEF 0x3A +// Service settings +#define REG_IMAGECAL 0x3B +#define REG_TEMP 0x3C +#define REG_LOWBAT 0x3D +// Status +#define REG_IRQFLAGS1 0x3E +#define REG_IRQFLAGS2 0x3F +// I/O settings +#define REG_DIOMAPPING1 0x40 +#define REG_DIOMAPPING2 0x41 +// Version +#define REG_VERSION 0x42 +// Additional settings +#define REG_PLLHOP 0x44 +#define REG_TCXO 0x4B +#define REG_PADAC 0x4D +#define REG_FORMERTEMP 0x5B +#define REG_BITRATEFRAC 0x5D +#define REG_AGCREF 0x61 +#define REG_AGCTHRESH1 0x62 +#define REG_AGCTHRESH2 0x63 +#define REG_AGCTHRESH3 0x64 +#define REG_PLL 0x70 + +/*! + * ============================================================================ + * SX1276 FSK bits control definition + * ============================================================================ + */ + +/*! + * RegFifo + */ + +/*! + * RegOpMode + */ +#define RF_OPMODE_LONGRANGEMODE_MASK 0x7F +#define RF_OPMODE_LONGRANGEMODE_OFF 0x00 +#define RF_OPMODE_LONGRANGEMODE_ON 0x80 + +#define RF_OPMODE_MODULATIONTYPE_MASK 0x9F +#define RF_OPMODE_MODULATIONTYPE_FSK 0x00 // Default +#define RF_OPMODE_MODULATIONTYPE_OOK 0x20 + +#define RF_OPMODE_MODULATIONSHAPING_MASK 0xE7 +#define RF_OPMODE_MODULATIONSHAPING_00 0x00 // Default +#define RF_OPMODE_MODULATIONSHAPING_01 0x08 +#define RF_OPMODE_MODULATIONSHAPING_10 0x10 +#define RF_OPMODE_MODULATIONSHAPING_11 0x18 + +#define RF_OPMODE_MASK 0xF8 +#define RF_OPMODE_SLEEP 0x00 +#define RF_OPMODE_STANDBY 0x01 // Default +#define RF_OPMODE_SYNTHESIZER_TX 0x02 +#define RF_OPMODE_TRANSMITTER 0x03 +#define RF_OPMODE_SYNTHESIZER_RX 0x04 +#define RF_OPMODE_RECEIVER 0x05 + +/*! + * RegBitRate (bits/sec) + */ +#define RF_BITRATEMSB_1200_BPS 0x68 +#define RF_BITRATELSB_1200_BPS 0x2B +#define RF_BITRATEMSB_2400_BPS 0x34 +#define RF_BITRATELSB_2400_BPS 0x15 +#define RF_BITRATEMSB_4800_BPS 0x1A // Default +#define RF_BITRATELSB_4800_BPS 0x0B // Default +#define RF_BITRATEMSB_9600_BPS 0x0D +#define RF_BITRATELSB_9600_BPS 0x05 +#define RF_BITRATEMSB_15000_BPS 0x08 +#define RF_BITRATELSB_15000_BPS 0x55 +#define RF_BITRATEMSB_19200_BPS 0x06 +#define RF_BITRATELSB_19200_BPS 0x83 +#define RF_BITRATEMSB_38400_BPS 0x03 +#define RF_BITRATELSB_38400_BPS 0x41 +#define RF_BITRATEMSB_76800_BPS 0x01 +#define RF_BITRATELSB_76800_BPS 0xA1 +#define RF_BITRATEMSB_153600_BPS 0x00 +#define RF_BITRATELSB_153600_BPS 0xD0 +#define RF_BITRATEMSB_57600_BPS 0x02 +#define RF_BITRATELSB_57600_BPS 0x2C +#define RF_BITRATEMSB_115200_BPS 0x01 +#define RF_BITRATELSB_115200_BPS 0x16 +#define RF_BITRATEMSB_12500_BPS 0x0A +#define RF_BITRATELSB_12500_BPS 0x00 +#define RF_BITRATEMSB_25000_BPS 0x05 +#define RF_BITRATELSB_25000_BPS 0x00 +#define RF_BITRATEMSB_50000_BPS 0x02 +#define RF_BITRATELSB_50000_BPS 0x80 +#define RF_BITRATEMSB_100000_BPS 0x01 +#define RF_BITRATELSB_100000_BPS 0x40 +#define RF_BITRATEMSB_150000_BPS 0x00 +#define RF_BITRATELSB_150000_BPS 0xD5 +#define RF_BITRATEMSB_200000_BPS 0x00 +#define RF_BITRATELSB_200000_BPS 0xA0 +#define RF_BITRATEMSB_250000_BPS 0x00 +#define RF_BITRATELSB_250000_BPS 0x80 +#define RF_BITRATEMSB_32768_BPS 0x03 +#define RF_BITRATELSB_32768_BPS 0xD1 + +/*! + * RegFdev (Hz) + */ +#define RF_FDEVMSB_2000_HZ 0x00 +#define RF_FDEVLSB_2000_HZ 0x21 +#define RF_FDEVMSB_5000_HZ 0x00 // Default +#define RF_FDEVLSB_5000_HZ 0x52 // Default +#define RF_FDEVMSB_10000_HZ 0x00 +#define RF_FDEVLSB_10000_HZ 0xA4 +#define RF_FDEVMSB_15000_HZ 0x00 +#define RF_FDEVLSB_15000_HZ 0xF6 +#define RF_FDEVMSB_20000_HZ 0x01 +#define RF_FDEVLSB_20000_HZ 0x48 +#define RF_FDEVMSB_25000_HZ 0x01 +#define RF_FDEVLSB_25000_HZ 0x9A +#define RF_FDEVMSB_30000_HZ 0x01 +#define RF_FDEVLSB_30000_HZ 0xEC +#define RF_FDEVMSB_35000_HZ 0x02 +#define RF_FDEVLSB_35000_HZ 0x3D +#define RF_FDEVMSB_40000_HZ 0x02 +#define RF_FDEVLSB_40000_HZ 0x8F +#define RF_FDEVMSB_45000_HZ 0x02 +#define RF_FDEVLSB_45000_HZ 0xE1 +#define RF_FDEVMSB_50000_HZ 0x03 +#define RF_FDEVLSB_50000_HZ 0x33 +#define RF_FDEVMSB_55000_HZ 0x03 +#define RF_FDEVLSB_55000_HZ 0x85 +#define RF_FDEVMSB_60000_HZ 0x03 +#define RF_FDEVLSB_60000_HZ 0xD7 +#define RF_FDEVMSB_65000_HZ 0x04 +#define RF_FDEVLSB_65000_HZ 0x29 +#define RF_FDEVMSB_70000_HZ 0x04 +#define RF_FDEVLSB_70000_HZ 0x7B +#define RF_FDEVMSB_75000_HZ 0x04 +#define RF_FDEVLSB_75000_HZ 0xCD +#define RF_FDEVMSB_80000_HZ 0x05 +#define RF_FDEVLSB_80000_HZ 0x1F +#define RF_FDEVMSB_85000_HZ 0x05 +#define RF_FDEVLSB_85000_HZ 0x71 +#define RF_FDEVMSB_90000_HZ 0x05 +#define RF_FDEVLSB_90000_HZ 0xC3 +#define RF_FDEVMSB_95000_HZ 0x06 +#define RF_FDEVLSB_95000_HZ 0x14 +#define RF_FDEVMSB_100000_HZ 0x06 +#define RF_FDEVLSB_100000_HZ 0x66 +#define RF_FDEVMSB_110000_HZ 0x07 +#define RF_FDEVLSB_110000_HZ 0x0A +#define RF_FDEVMSB_120000_HZ 0x07 +#define RF_FDEVLSB_120000_HZ 0xAE +#define RF_FDEVMSB_130000_HZ 0x08 +#define RF_FDEVLSB_130000_HZ 0x52 +#define RF_FDEVMSB_140000_HZ 0x08 +#define RF_FDEVLSB_140000_HZ 0xF6 +#define RF_FDEVMSB_150000_HZ 0x09 +#define RF_FDEVLSB_150000_HZ 0x9A +#define RF_FDEVMSB_160000_HZ 0x0A +#define RF_FDEVLSB_160000_HZ 0x3D +#define RF_FDEVMSB_170000_HZ 0x0A +#define RF_FDEVLSB_170000_HZ 0xE1 +#define RF_FDEVMSB_180000_HZ 0x0B +#define RF_FDEVLSB_180000_HZ 0x85 +#define RF_FDEVMSB_190000_HZ 0x0C +#define RF_FDEVLSB_190000_HZ 0x29 +#define RF_FDEVMSB_200000_HZ 0x0C +#define RF_FDEVLSB_200000_HZ 0xCD + +/*! + * RegFrf (MHz) + */ +#define RF_FRFMSB_863_MHZ 0xD7 +#define RF_FRFMID_863_MHZ 0xC0 +#define RF_FRFLSB_863_MHZ 0x00 +#define RF_FRFMSB_864_MHZ 0xD8 +#define RF_FRFMID_864_MHZ 0x00 +#define RF_FRFLSB_864_MHZ 0x00 +#define RF_FRFMSB_865_MHZ 0xD8 +#define RF_FRFMID_865_MHZ 0x40 +#define RF_FRFLSB_865_MHZ 0x00 +#define RF_FRFMSB_866_MHZ 0xD8 +#define RF_FRFMID_866_MHZ 0x80 +#define RF_FRFLSB_866_MHZ 0x00 +#define RF_FRFMSB_867_MHZ 0xD8 +#define RF_FRFMID_867_MHZ 0xC0 +#define RF_FRFLSB_867_MHZ 0x00 +#define RF_FRFMSB_868_MHZ 0xD9 +#define RF_FRFMID_868_MHZ 0x00 +#define RF_FRFLSB_868_MHZ 0x00 +#define RF_FRFMSB_869_MHZ 0xD9 +#define RF_FRFMID_869_MHZ 0x40 +#define RF_FRFLSB_869_MHZ 0x00 +#define RF_FRFMSB_870_MHZ 0xD9 +#define RF_FRFMID_870_MHZ 0x80 +#define RF_FRFLSB_870_MHZ 0x00 + +#define RF_FRFMSB_902_MHZ 0xE1 +#define RF_FRFMID_902_MHZ 0x80 +#define RF_FRFLSB_902_MHZ 0x00 +#define RF_FRFMSB_903_MHZ 0xE1 +#define RF_FRFMID_903_MHZ 0xC0 +#define RF_FRFLSB_903_MHZ 0x00 +#define RF_FRFMSB_904_MHZ 0xE2 +#define RF_FRFMID_904_MHZ 0x00 +#define RF_FRFLSB_904_MHZ 0x00 +#define RF_FRFMSB_905_MHZ 0xE2 +#define RF_FRFMID_905_MHZ 0x40 +#define RF_FRFLSB_905_MHZ 0x00 +#define RF_FRFMSB_906_MHZ 0xE2 +#define RF_FRFMID_906_MHZ 0x80 +#define RF_FRFLSB_906_MHZ 0x00 +#define RF_FRFMSB_907_MHZ 0xE2 +#define RF_FRFMID_907_MHZ 0xC0 +#define RF_FRFLSB_907_MHZ 0x00 +#define RF_FRFMSB_908_MHZ 0xE3 +#define RF_FRFMID_908_MHZ 0x00 +#define RF_FRFLSB_908_MHZ 0x00 +#define RF_FRFMSB_909_MHZ 0xE3 +#define RF_FRFMID_909_MHZ 0x40 +#define RF_FRFLSB_909_MHZ 0x00 +#define RF_FRFMSB_910_MHZ 0xE3 +#define RF_FRFMID_910_MHZ 0x80 +#define RF_FRFLSB_910_MHZ 0x00 +#define RF_FRFMSB_911_MHZ 0xE3 +#define RF_FRFMID_911_MHZ 0xC0 +#define RF_FRFLSB_911_MHZ 0x00 +#define RF_FRFMSB_912_MHZ 0xE4 +#define RF_FRFMID_912_MHZ 0x00 +#define RF_FRFLSB_912_MHZ 0x00 +#define RF_FRFMSB_913_MHZ 0xE4 +#define RF_FRFMID_913_MHZ 0x40 +#define RF_FRFLSB_913_MHZ 0x00 +#define RF_FRFMSB_914_MHZ 0xE4 +#define RF_FRFMID_914_MHZ 0x80 +#define RF_FRFLSB_914_MHZ 0x00 +#define RF_FRFMSB_915_MHZ 0xE4 // Default +#define RF_FRFMID_915_MHZ 0xC0 // Default +#define RF_FRFLSB_915_MHZ 0x00 // Default +#define RF_FRFMSB_916_MHZ 0xE5 +#define RF_FRFMID_916_MHZ 0x00 +#define RF_FRFLSB_916_MHZ 0x00 +#define RF_FRFMSB_917_MHZ 0xE5 +#define RF_FRFMID_917_MHZ 0x40 +#define RF_FRFLSB_917_MHZ 0x00 +#define RF_FRFMSB_918_MHZ 0xE5 +#define RF_FRFMID_918_MHZ 0x80 +#define RF_FRFLSB_918_MHZ 0x00 +#define RF_FRFMSB_919_MHZ 0xE5 +#define RF_FRFMID_919_MHZ 0xC0 +#define RF_FRFLSB_919_MHZ 0x00 +#define RF_FRFMSB_920_MHZ 0xE6 +#define RF_FRFMID_920_MHZ 0x00 +#define RF_FRFLSB_920_MHZ 0x00 +#define RF_FRFMSB_921_MHZ 0xE6 +#define RF_FRFMID_921_MHZ 0x40 +#define RF_FRFLSB_921_MHZ 0x00 +#define RF_FRFMSB_922_MHZ 0xE6 +#define RF_FRFMID_922_MHZ 0x80 +#define RF_FRFLSB_922_MHZ 0x00 +#define RF_FRFMSB_923_MHZ 0xE6 +#define RF_FRFMID_923_MHZ 0xC0 +#define RF_FRFLSB_923_MHZ 0x00 +#define RF_FRFMSB_924_MHZ 0xE7 +#define RF_FRFMID_924_MHZ 0x00 +#define RF_FRFLSB_924_MHZ 0x00 +#define RF_FRFMSB_925_MHZ 0xE7 +#define RF_FRFMID_925_MHZ 0x40 +#define RF_FRFLSB_925_MHZ 0x00 +#define RF_FRFMSB_926_MHZ 0xE7 +#define RF_FRFMID_926_MHZ 0x80 +#define RF_FRFLSB_926_MHZ 0x00 +#define RF_FRFMSB_927_MHZ 0xE7 +#define RF_FRFMID_927_MHZ 0xC0 +#define RF_FRFLSB_927_MHZ 0x00 +#define RF_FRFMSB_928_MHZ 0xE8 +#define RF_FRFMID_928_MHZ 0x00 +#define RF_FRFLSB_928_MHZ 0x00 + +/*! + * RegPaConfig + */ +#define RF_PACONFIG_PASELECT_MASK 0x7F +#define RF_PACONFIG_PASELECT_PABOOST 0x80 +#define RF_PACONFIG_PASELECT_RFO 0x00 // Default + +#define RF_PACONFIG_MAX_POWER_MASK 0x8F + +#define RF_PACONFIG_OUTPUTPOWER_MASK 0xF0 + +/*! + * RegPaRamp + */ +#define RF_PARAMP_MODULATIONSHAPING_MASK 0x9F +#define RF_PARAMP_MODULATIONSHAPING_00 0x00 // Default +#define RF_PARAMP_MODULATIONSHAPING_01 0x20 +#define RF_PARAMP_MODULATIONSHAPING_10 0x40 +#define RF_PARAMP_MODULATIONSHAPING_11 0x60 + +#define RF_PARAMP_LOWPNTXPLL_MASK 0xEF +#define RF_PARAMP_LOWPNTXPLL_OFF 0x10 +#define RF_PARAMP_LOWPNTXPLL_ON 0x00 // Default + +#define RF_PARAMP_MASK 0xF0 +#define RF_PARAMP_3400_US 0x00 +#define RF_PARAMP_2000_US 0x01 +#define RF_PARAMP_1000_US 0x02 +#define RF_PARAMP_0500_US 0x03 +#define RF_PARAMP_0250_US 0x04 +#define RF_PARAMP_0125_US 0x05 +#define RF_PARAMP_0100_US 0x06 +#define RF_PARAMP_0062_US 0x07 +#define RF_PARAMP_0050_US 0x08 +#define RF_PARAMP_0040_US 0x09 // Default +#define RF_PARAMP_0031_US 0x0A +#define RF_PARAMP_0025_US 0x0B +#define RF_PARAMP_0020_US 0x0C +#define RF_PARAMP_0015_US 0x0D +#define RF_PARAMP_0012_US 0x0E +#define RF_PARAMP_0010_US 0x0F + +/*! + * RegOcp + */ +#define RF_OCP_MASK 0xDF +#define RF_OCP_ON 0x20 // Default +#define RF_OCP_OFF 0x00 + +#define RF_OCP_TRIM_MASK 0xE0 +#define RF_OCP_TRIM_045_MA 0x00 +#define RF_OCP_TRIM_050_MA 0x01 +#define RF_OCP_TRIM_055_MA 0x02 +#define RF_OCP_TRIM_060_MA 0x03 +#define RF_OCP_TRIM_065_MA 0x04 +#define RF_OCP_TRIM_070_MA 0x05 +#define RF_OCP_TRIM_075_MA 0x06 +#define RF_OCP_TRIM_080_MA 0x07 +#define RF_OCP_TRIM_085_MA 0x08 +#define RF_OCP_TRIM_090_MA 0x09 +#define RF_OCP_TRIM_095_MA 0x0A +#define RF_OCP_TRIM_100_MA 0x0B // Default +#define RF_OCP_TRIM_105_MA 0x0C +#define RF_OCP_TRIM_110_MA 0x0D +#define RF_OCP_TRIM_115_MA 0x0E +#define RF_OCP_TRIM_120_MA 0x0F +#define RF_OCP_TRIM_130_MA 0x10 +#define RF_OCP_TRIM_140_MA 0x11 +#define RF_OCP_TRIM_150_MA 0x12 +#define RF_OCP_TRIM_160_MA 0x13 +#define RF_OCP_TRIM_170_MA 0x14 +#define RF_OCP_TRIM_180_MA 0x15 +#define RF_OCP_TRIM_190_MA 0x16 +#define RF_OCP_TRIM_200_MA 0x17 +#define RF_OCP_TRIM_210_MA 0x18 +#define RF_OCP_TRIM_220_MA 0x19 +#define RF_OCP_TRIM_230_MA 0x1A +#define RF_OCP_TRIM_240_MA 0x1B + +/*! + * RegLna + */ +#define RF_LNA_GAIN_MASK 0x1F +#define RF_LNA_GAIN_G1 0x20 // Default +#define RF_LNA_GAIN_G2 0x40 +#define RF_LNA_GAIN_G3 0x60 +#define RF_LNA_GAIN_G4 0x80 +#define RF_LNA_GAIN_G5 0xA0 +#define RF_LNA_GAIN_G6 0xC0 + +#define RF_LNA_BOOST_MASK 0xFC +#define RF_LNA_BOOST_OFF 0x00 // Default +#define RF_LNA_BOOST_ON 0x03 + +/*! + * RegRxConfig + */ +#define RF_RXCONFIG_RESTARTRXONCOLLISION_MASK 0x7F +#define RF_RXCONFIG_RESTARTRXONCOLLISION_ON 0x80 +#define RF_RXCONFIG_RESTARTRXONCOLLISION_OFF 0x00 // Default + +#define RF_RXCONFIG_RESTARTRXWITHOUTPLLLOCK 0x40 // Write only + +#define RF_RXCONFIG_RESTARTRXWITHPLLLOCK 0x20 // Write only + +#define RF_RXCONFIG_AFCAUTO_MASK 0xEF +#define RF_RXCONFIG_AFCAUTO_ON 0x10 +#define RF_RXCONFIG_AFCAUTO_OFF 0x00 // Default + +#define RF_RXCONFIG_AGCAUTO_MASK 0xF7 +#define RF_RXCONFIG_AGCAUTO_ON 0x08 // Default +#define RF_RXCONFIG_AGCAUTO_OFF 0x00 + +#define RF_RXCONFIG_RXTRIGER_MASK 0xF8 +#define RF_RXCONFIG_RXTRIGER_OFF 0x00 +#define RF_RXCONFIG_RXTRIGER_RSSI 0x01 +#define RF_RXCONFIG_RXTRIGER_PREAMBLEDETECT 0x06 // Default +#define RF_RXCONFIG_RXTRIGER_RSSI_PREAMBLEDETECT 0x07 + +/*! + * RegRssiConfig + */ +#define RF_RSSICONFIG_OFFSET_MASK 0x07 +#define RF_RSSICONFIG_OFFSET_P_00_DB 0x00 // Default +#define RF_RSSICONFIG_OFFSET_P_01_DB 0x08 +#define RF_RSSICONFIG_OFFSET_P_02_DB 0x10 +#define RF_RSSICONFIG_OFFSET_P_03_DB 0x18 +#define RF_RSSICONFIG_OFFSET_P_04_DB 0x20 +#define RF_RSSICONFIG_OFFSET_P_05_DB 0x28 +#define RF_RSSICONFIG_OFFSET_P_06_DB 0x30 +#define RF_RSSICONFIG_OFFSET_P_07_DB 0x38 +#define RF_RSSICONFIG_OFFSET_P_08_DB 0x40 +#define RF_RSSICONFIG_OFFSET_P_09_DB 0x48 +#define RF_RSSICONFIG_OFFSET_P_10_DB 0x50 +#define RF_RSSICONFIG_OFFSET_P_11_DB 0x58 +#define RF_RSSICONFIG_OFFSET_P_12_DB 0x60 +#define RF_RSSICONFIG_OFFSET_P_13_DB 0x68 +#define RF_RSSICONFIG_OFFSET_P_14_DB 0x70 +#define RF_RSSICONFIG_OFFSET_P_15_DB 0x78 +#define RF_RSSICONFIG_OFFSET_M_16_DB 0x80 +#define RF_RSSICONFIG_OFFSET_M_15_DB 0x88 +#define RF_RSSICONFIG_OFFSET_M_14_DB 0x90 +#define RF_RSSICONFIG_OFFSET_M_13_DB 0x98 +#define RF_RSSICONFIG_OFFSET_M_12_DB 0xA0 +#define RF_RSSICONFIG_OFFSET_M_11_DB 0xA8 +#define RF_RSSICONFIG_OFFSET_M_10_DB 0xB0 +#define RF_RSSICONFIG_OFFSET_M_09_DB 0xB8 +#define RF_RSSICONFIG_OFFSET_M_08_DB 0xC0 +#define RF_RSSICONFIG_OFFSET_M_07_DB 0xC8 +#define RF_RSSICONFIG_OFFSET_M_06_DB 0xD0 +#define RF_RSSICONFIG_OFFSET_M_05_DB 0xD8 +#define RF_RSSICONFIG_OFFSET_M_04_DB 0xE0 +#define RF_RSSICONFIG_OFFSET_M_03_DB 0xE8 +#define RF_RSSICONFIG_OFFSET_M_02_DB 0xF0 +#define RF_RSSICONFIG_OFFSET_M_01_DB 0xF8 + +#define RF_RSSICONFIG_SMOOTHING_MASK 0xF8 +#define RF_RSSICONFIG_SMOOTHING_2 0x00 +#define RF_RSSICONFIG_SMOOTHING_4 0x01 +#define RF_RSSICONFIG_SMOOTHING_8 0x02 // Default +#define RF_RSSICONFIG_SMOOTHING_16 0x03 +#define RF_RSSICONFIG_SMOOTHING_32 0x04 +#define RF_RSSICONFIG_SMOOTHING_64 0x05 +#define RF_RSSICONFIG_SMOOTHING_128 0x06 +#define RF_RSSICONFIG_SMOOTHING_256 0x07 + +/*! + * RegRssiCollision + */ +#define RF_RSSICOLISION_THRESHOLD 0x0A // Default + +/*! + * RegRssiThresh + */ +#define RF_RSSITHRESH_THRESHOLD 0xFF // Default + +/*! + * RegRssiValue (Read Only) + */ + +/*! + * RegRxBw + */ +#define RF_RXBW_MANT_MASK 0xE7 +#define RF_RXBW_MANT_16 0x00 +#define RF_RXBW_MANT_20 0x08 +#define RF_RXBW_MANT_24 0x10 // Default + +#define RF_RXBW_EXP_MASK 0xF8 +#define RF_RXBW_EXP_0 0x00 +#define RF_RXBW_EXP_1 0x01 +#define RF_RXBW_EXP_2 0x02 +#define RF_RXBW_EXP_3 0x03 +#define RF_RXBW_EXP_4 0x04 +#define RF_RXBW_EXP_5 0x05 // Default +#define RF_RXBW_EXP_6 0x06 +#define RF_RXBW_EXP_7 0x07 + +/*! + * RegAfcBw + */ +#define RF_AFCBW_MANTAFC_MASK 0xE7 +#define RF_AFCBW_MANTAFC_16 0x00 +#define RF_AFCBW_MANTAFC_20 0x08 // Default +#define RF_AFCBW_MANTAFC_24 0x10 + +#define RF_AFCBW_EXPAFC_MASK 0xF8 +#define RF_AFCBW_EXPAFC_0 0x00 +#define RF_AFCBW_EXPAFC_1 0x01 +#define RF_AFCBW_EXPAFC_2 0x02 +#define RF_AFCBW_EXPAFC_3 0x03 // Default +#define RF_AFCBW_EXPAFC_4 0x04 +#define RF_AFCBW_EXPAFC_5 0x05 +#define RF_AFCBW_EXPAFC_6 0x06 +#define RF_AFCBW_EXPAFC_7 0x07 + +/*! + * RegOokPeak + */ +#define RF_OOKPEAK_BITSYNC_MASK 0xDF // Default +#define RF_OOKPEAK_BITSYNC_ON 0x20 // Default +#define RF_OOKPEAK_BITSYNC_OFF 0x00 + +#define RF_OOKPEAK_OOKTHRESHTYPE_MASK 0xE7 +#define RF_OOKPEAK_OOKTHRESHTYPE_FIXED 0x00 +#define RF_OOKPEAK_OOKTHRESHTYPE_PEAK 0x08 // Default +#define RF_OOKPEAK_OOKTHRESHTYPE_AVERAGE 0x10 + +#define RF_OOKPEAK_OOKPEAKTHRESHSTEP_MASK 0xF8 +#define RF_OOKPEAK_OOKPEAKTHRESHSTEP_0_5_DB 0x00 // Default +#define RF_OOKPEAK_OOKPEAKTHRESHSTEP_1_0_DB 0x01 +#define RF_OOKPEAK_OOKPEAKTHRESHSTEP_1_5_DB 0x02 +#define RF_OOKPEAK_OOKPEAKTHRESHSTEP_2_0_DB 0x03 +#define RF_OOKPEAK_OOKPEAKTHRESHSTEP_3_0_DB 0x04 +#define RF_OOKPEAK_OOKPEAKTHRESHSTEP_4_0_DB 0x05 +#define RF_OOKPEAK_OOKPEAKTHRESHSTEP_5_0_DB 0x06 +#define RF_OOKPEAK_OOKPEAKTHRESHSTEP_6_0_DB 0x07 + +/*! + * RegOokFix + */ +#define RF_OOKFIX_OOKFIXEDTHRESHOLD 0x0C // Default + +/*! + * RegOokAvg + */ +#define RF_OOKAVG_OOKPEAKTHRESHDEC_MASK 0x1F +#define RF_OOKAVG_OOKPEAKTHRESHDEC_000 0x00 // Default +#define RF_OOKAVG_OOKPEAKTHRESHDEC_001 0x20 +#define RF_OOKAVG_OOKPEAKTHRESHDEC_010 0x40 +#define RF_OOKAVG_OOKPEAKTHRESHDEC_011 0x60 +#define RF_OOKAVG_OOKPEAKTHRESHDEC_100 0x80 +#define RF_OOKAVG_OOKPEAKTHRESHDEC_101 0xA0 +#define RF_OOKAVG_OOKPEAKTHRESHDEC_110 0xC0 +#define RF_OOKAVG_OOKPEAKTHRESHDEC_111 0xE0 + +#define RF_OOKAVG_AVERAGEOFFSET_MASK 0xF3 +#define RF_OOKAVG_AVERAGEOFFSET_0_DB 0x00 // Default +#define RF_OOKAVG_AVERAGEOFFSET_2_DB 0x04 +#define RF_OOKAVG_AVERAGEOFFSET_4_DB 0x08 +#define RF_OOKAVG_AVERAGEOFFSET_6_DB 0x0C + +#define RF_OOKAVG_OOKAVERAGETHRESHFILT_MASK 0xFC +#define RF_OOKAVG_OOKAVERAGETHRESHFILT_00 0x00 +#define RF_OOKAVG_OOKAVERAGETHRESHFILT_01 0x01 +#define RF_OOKAVG_OOKAVERAGETHRESHFILT_10 0x02 // Default +#define RF_OOKAVG_OOKAVERAGETHRESHFILT_11 0x03 + +/*! + * RegAfcFei + */ +#define RF_AFCFEI_AGCSTART 0x10 + +#define RF_AFCFEI_AFCCLEAR 0x02 + +#define RF_AFCFEI_AFCAUTOCLEAR_MASK 0xFE +#define RF_AFCFEI_AFCAUTOCLEAR_ON 0x01 +#define RF_AFCFEI_AFCAUTOCLEAR_OFF 0x00 // Default + +/*! + * RegAfcMsb (Read Only) + */ + +/*! + * RegAfcLsb (Read Only) + */ + +/*! + * RegFeiMsb (Read Only) + */ + +/*! + * RegFeiLsb (Read Only) + */ + +/*! + * RegPreambleDetect + */ +#define RF_PREAMBLEDETECT_DETECTOR_MASK 0x7F +#define RF_PREAMBLEDETECT_DETECTOR_ON 0x80 // Default +#define RF_PREAMBLEDETECT_DETECTOR_OFF 0x00 + +#define RF_PREAMBLEDETECT_DETECTORSIZE_MASK 0x9F +#define RF_PREAMBLEDETECT_DETECTORSIZE_1 0x00 +#define RF_PREAMBLEDETECT_DETECTORSIZE_2 0x20 // Default +#define RF_PREAMBLEDETECT_DETECTORSIZE_3 0x40 +#define RF_PREAMBLEDETECT_DETECTORSIZE_4 0x60 + +#define RF_PREAMBLEDETECT_DETECTORTOL_MASK 0xE0 +#define RF_PREAMBLEDETECT_DETECTORTOL_0 0x00 +#define RF_PREAMBLEDETECT_DETECTORTOL_1 0x01 +#define RF_PREAMBLEDETECT_DETECTORTOL_2 0x02 +#define RF_PREAMBLEDETECT_DETECTORTOL_3 0x03 +#define RF_PREAMBLEDETECT_DETECTORTOL_4 0x04 +#define RF_PREAMBLEDETECT_DETECTORTOL_5 0x05 +#define RF_PREAMBLEDETECT_DETECTORTOL_6 0x06 +#define RF_PREAMBLEDETECT_DETECTORTOL_7 0x07 +#define RF_PREAMBLEDETECT_DETECTORTOL_8 0x08 +#define RF_PREAMBLEDETECT_DETECTORTOL_9 0x09 +#define RF_PREAMBLEDETECT_DETECTORTOL_10 0x0A // Default +#define RF_PREAMBLEDETECT_DETECTORTOL_11 0x0B +#define RF_PREAMBLEDETECT_DETECTORTOL_12 0x0C +#define RF_PREAMBLEDETECT_DETECTORTOL_13 0x0D +#define RF_PREAMBLEDETECT_DETECTORTOL_14 0x0E +#define RF_PREAMBLEDETECT_DETECTORTOL_15 0x0F +#define RF_PREAMBLEDETECT_DETECTORTOL_16 0x10 +#define RF_PREAMBLEDETECT_DETECTORTOL_17 0x11 +#define RF_PREAMBLEDETECT_DETECTORTOL_18 0x12 +#define RF_PREAMBLEDETECT_DETECTORTOL_19 0x13 +#define RF_PREAMBLEDETECT_DETECTORTOL_20 0x14 +#define RF_PREAMBLEDETECT_DETECTORTOL_21 0x15 +#define RF_PREAMBLEDETECT_DETECTORTOL_22 0x16 +#define RF_PREAMBLEDETECT_DETECTORTOL_23 0x17 +#define RF_PREAMBLEDETECT_DETECTORTOL_24 0x18 +#define RF_PREAMBLEDETECT_DETECTORTOL_25 0x19 +#define RF_PREAMBLEDETECT_DETECTORTOL_26 0x1A +#define RF_PREAMBLEDETECT_DETECTORTOL_27 0x1B +#define RF_PREAMBLEDETECT_DETECTORTOL_28 0x1C +#define RF_PREAMBLEDETECT_DETECTORTOL_29 0x1D +#define RF_PREAMBLEDETECT_DETECTORTOL_30 0x1E +#define RF_PREAMBLEDETECT_DETECTORTOL_31 0x1F + +/*! + * RegRxTimeout1 + */ +#define RF_RXTIMEOUT1_TIMEOUTRXRSSI 0x00 // Default + +/*! + * RegRxTimeout2 + */ +#define RF_RXTIMEOUT2_TIMEOUTRXPREAMBLE 0x00 // Default + +/*! + * RegRxTimeout3 + */ +#define RF_RXTIMEOUT3_TIMEOUTSIGNALSYNC 0x00 // Default + +/*! + * RegRxDelay + */ +#define RF_RXDELAY_INTERPACKETRXDELAY 0x00 // Default + +/*! + * RegOsc + */ +#define RF_OSC_RCCALSTART 0x08 + +#define RF_OSC_CLKOUT_MASK 0xF8 +#define RF_OSC_CLKOUT_32_MHZ 0x00 +#define RF_OSC_CLKOUT_16_MHZ 0x01 +#define RF_OSC_CLKOUT_8_MHZ 0x02 +#define RF_OSC_CLKOUT_4_MHZ 0x03 +#define RF_OSC_CLKOUT_2_MHZ 0x04 +#define RF_OSC_CLKOUT_1_MHZ 0x05 // Default +#define RF_OSC_CLKOUT_RC 0x06 +#define RF_OSC_CLKOUT_OFF 0x07 + +/*! + * RegPreambleMsb/RegPreambleLsb + */ +#define RF_PREAMBLEMSB_SIZE 0x00 // Default +#define RF_PREAMBLELSB_SIZE 0x03 // Default + +/*! + * RegSyncConfig + */ +#define RF_SYNCCONFIG_AUTORESTARTRXMODE_MASK 0x3F +#define RF_SYNCCONFIG_AUTORESTARTRXMODE_WAITPLL_ON 0x80 // Default +#define RF_SYNCCONFIG_AUTORESTARTRXMODE_WAITPLL_OFF 0x40 +#define RF_SYNCCONFIG_AUTORESTARTRXMODE_OFF 0x00 + + +#define RF_SYNCCONFIG_PREAMBLEPOLARITY_MASK 0xDF +#define RF_SYNCCONFIG_PREAMBLEPOLARITY_55 0x20 +#define RF_SYNCCONFIG_PREAMBLEPOLARITY_AA 0x00 // Default + +#define RF_SYNCCONFIG_SYNC_MASK 0xEF +#define RF_SYNCCONFIG_SYNC_ON 0x10 // Default +#define RF_SYNCCONFIG_SYNC_OFF 0x00 + + +#define RF_SYNCCONFIG_SYNCSIZE_MASK 0xF8 +#define RF_SYNCCONFIG_SYNCSIZE_1 0x00 +#define RF_SYNCCONFIG_SYNCSIZE_2 0x01 +#define RF_SYNCCONFIG_SYNCSIZE_3 0x02 +#define RF_SYNCCONFIG_SYNCSIZE_4 0x03 // Default +#define RF_SYNCCONFIG_SYNCSIZE_5 0x04 +#define RF_SYNCCONFIG_SYNCSIZE_6 0x05 +#define RF_SYNCCONFIG_SYNCSIZE_7 0x06 +#define RF_SYNCCONFIG_SYNCSIZE_8 0x07 + +/*! + * RegSyncValue1-8 + */ +#define RF_SYNCVALUE1_SYNCVALUE 0x01 // Default +#define RF_SYNCVALUE2_SYNCVALUE 0x01 // Default +#define RF_SYNCVALUE3_SYNCVALUE 0x01 // Default +#define RF_SYNCVALUE4_SYNCVALUE 0x01 // Default +#define RF_SYNCVALUE5_SYNCVALUE 0x01 // Default +#define RF_SYNCVALUE6_SYNCVALUE 0x01 // Default +#define RF_SYNCVALUE7_SYNCVALUE 0x01 // Default +#define RF_SYNCVALUE8_SYNCVALUE 0x01 // Default + +/*! + * RegPacketConfig1 + */ +#define RF_PACKETCONFIG1_PACKETFORMAT_MASK 0x7F +#define RF_PACKETCONFIG1_PACKETFORMAT_FIXED 0x00 +#define RF_PACKETCONFIG1_PACKETFORMAT_VARIABLE 0x80 // Default + +#define RF_PACKETCONFIG1_DCFREE_MASK 0x9F +#define RF_PACKETCONFIG1_DCFREE_OFF 0x00 // Default +#define RF_PACKETCONFIG1_DCFREE_MANCHESTER 0x20 +#define RF_PACKETCONFIG1_DCFREE_WHITENING 0x40 + +#define RF_PACKETCONFIG1_CRC_MASK 0xEF +#define RF_PACKETCONFIG1_CRC_ON 0x10 // Default +#define RF_PACKETCONFIG1_CRC_OFF 0x00 + +#define RF_PACKETCONFIG1_CRCAUTOCLEAR_MASK 0xF7 +#define RF_PACKETCONFIG1_CRCAUTOCLEAR_ON 0x00 // Default +#define RF_PACKETCONFIG1_CRCAUTOCLEAR_OFF 0x08 + +#define RF_PACKETCONFIG1_ADDRSFILTERING_MASK 0xF9 +#define RF_PACKETCONFIG1_ADDRSFILTERING_OFF 0x00 // Default +#define RF_PACKETCONFIG1_ADDRSFILTERING_NODE 0x02 +#define RF_PACKETCONFIG1_ADDRSFILTERING_NODEBROADCAST 0x04 + +#define RF_PACKETCONFIG1_CRCWHITENINGTYPE_MASK 0xFE +#define RF_PACKETCONFIG1_CRCWHITENINGTYPE_CCITT 0x00 // Default +#define RF_PACKETCONFIG1_CRCWHITENINGTYPE_IBM 0x01 + +/*! + * RegPacketConfig2 + */ + +#define RF_PACKETCONFIG2_WMBUS_CRC_ENABLE_MASK 0x7F +#define RF_PACKETCONFIG2_WMBUS_CRC_ENABLE 0x80 +#define RF_PACKETCONFIG2_WMBUS_CRC_DISABLE 0x00 // Default + +#define RF_PACKETCONFIG2_DATAMODE_MASK 0xBF +#define RF_PACKETCONFIG2_DATAMODE_CONTINUOUS 0x00 +#define RF_PACKETCONFIG2_DATAMODE_PACKET 0x40 // Default + +#define RF_PACKETCONFIG2_IOHOME_MASK 0xDF +#define RF_PACKETCONFIG2_IOHOME_ON 0x20 +#define RF_PACKETCONFIG2_IOHOME_OFF 0x00 // Default + +#define RF_PACKETCONFIG2_BEACON_MASK 0xF7 +#define RF_PACKETCONFIG2_BEACON_ON 0x08 +#define RF_PACKETCONFIG2_BEACON_OFF 0x00 // Default + +#define RF_PACKETCONFIG2_PAYLOADLENGTH_MSB_MASK 0xF8 + +/*! + * RegPayloadLength + */ +#define RF_PAYLOADLENGTH_LENGTH 0x40 // Default + +/*! + * RegNodeAdrs + */ +#define RF_NODEADDRESS_ADDRESS 0x00 + +/*! + * RegBroadcastAdrs + */ +#define RF_BROADCASTADDRESS_ADDRESS 0x00 + +/*! + * RegFifoThresh + */ +#define RF_FIFOTHRESH_TXSTARTCONDITION_MASK 0x7F +#define RF_FIFOTHRESH_TXSTARTCONDITION_FIFOTHRESH 0x00 // Default +#define RF_FIFOTHRESH_TXSTARTCONDITION_FIFONOTEMPTY 0x80 + +#define RF_FIFOTHRESH_FIFOTHRESHOLD_MASK 0xC0 +#define RF_FIFOTHRESH_FIFOTHRESHOLD_THRESHOLD 0x0F // Default + +/*! + * RegSeqConfig1 + */ +#define RF_SEQCONFIG1_SEQUENCER_START 0x80 + +#define RF_SEQCONFIG1_SEQUENCER_STOP 0x40 + +#define RF_SEQCONFIG1_IDLEMODE_MASK 0xDF +#define RF_SEQCONFIG1_IDLEMODE_SLEEP 0x20 +#define RF_SEQCONFIG1_IDLEMODE_STANDBY 0x00 // Default + +#define RF_SEQCONFIG1_FROMSTART_MASK 0xE7 +#define RF_SEQCONFIG1_FROMSTART_TOLPS 0x00 // Default +#define RF_SEQCONFIG1_FROMSTART_TORX 0x08 +#define RF_SEQCONFIG1_FROMSTART_TOTX 0x10 +#define RF_SEQCONFIG1_FROMSTART_TOTX_ONFIFOLEVEL 0x18 + +#define RF_SEQCONFIG1_LPS_MASK 0xFB +#define RF_SEQCONFIG1_LPS_SEQUENCER_OFF 0x00 // Default +#define RF_SEQCONFIG1_LPS_IDLE 0x04 + +#define RF_SEQCONFIG1_FROMIDLE_MASK 0xFD +#define RF_SEQCONFIG1_FROMIDLE_TOTX 0x00 // Default +#define RF_SEQCONFIG1_FROMIDLE_TORX 0x02 + +#define RF_SEQCONFIG1_FROMTX_MASK 0xFE +#define RF_SEQCONFIG1_FROMTX_TOLPS 0x00 // Default +#define RF_SEQCONFIG1_FROMTX_TORX 0x01 + +/*! + * RegSeqConfig2 + */ +#define RF_SEQCONFIG2_FROMRX_MASK 0x1F +#define RF_SEQCONFIG2_FROMRX_TOUNUSED_000 0x00 // Default +#define RF_SEQCONFIG2_FROMRX_TORXPKT_ONPLDRDY 0x20 +#define RF_SEQCONFIG2_FROMRX_TOLPS_ONPLDRDY 0x40 +#define RF_SEQCONFIG2_FROMRX_TORXPKT_ONCRCOK 0x60 +#define RF_SEQCONFIG2_FROMRX_TOSEQUENCEROFF_ONRSSI 0x80 +#define RF_SEQCONFIG2_FROMRX_TOSEQUENCEROFF_ONSYNC 0xA0 +#define RF_SEQCONFIG2_FROMRX_TOSEQUENCEROFF_ONPREAMBLE 0xC0 +#define RF_SEQCONFIG2_FROMRX_TOUNUSED_111 0xE0 + +#define RF_SEQCONFIG2_FROMRXTIMEOUT_MASK 0xE7 +#define RF_SEQCONFIG2_FROMRXTIMEOUT_TORXRESTART 0x00 // Default +#define RF_SEQCONFIG2_FROMRXTIMEOUT_TOTX 0x08 +#define RF_SEQCONFIG2_FROMRXTIMEOUT_TOLPS 0x10 +#define RF_SEQCONFIG2_FROMRXTIMEOUT_TOSEQUENCEROFF 0x18 + +#define RF_SEQCONFIG2_FROMRXPKT_MASK 0xF8 +#define RF_SEQCONFIG2_FROMRXPKT_TOSEQUENCEROFF 0x00 // Default +#define RF_SEQCONFIG2_FROMRXPKT_TOTX_ONFIFOEMPTY 0x01 +#define RF_SEQCONFIG2_FROMRXPKT_TOLPS 0x02 +#define RF_SEQCONFIG2_FROMRXPKT_TOSYNTHESIZERRX 0x03 +#define RF_SEQCONFIG2_FROMRXPKT_TORX 0x04 + +/*! + * RegTimerResol + */ +#define RF_TIMERRESOL_TIMER1RESOL_MASK 0xF3 +#define RF_TIMERRESOL_TIMER1RESOL_OFF 0x00 // Default +#define RF_TIMERRESOL_TIMER1RESOL_000064_US 0x04 +#define RF_TIMERRESOL_TIMER1RESOL_004100_US 0x08 +#define RF_TIMERRESOL_TIMER1RESOL_262000_US 0x0C + +#define RF_TIMERRESOL_TIMER2RESOL_MASK 0xFC +#define RF_TIMERRESOL_TIMER2RESOL_OFF 0x00 // Default +#define RF_TIMERRESOL_TIMER2RESOL_000064_US 0x01 +#define RF_TIMERRESOL_TIMER2RESOL_004100_US 0x02 +#define RF_TIMERRESOL_TIMER2RESOL_262000_US 0x03 + +/*! + * RegTimer1Coef + */ +#define RF_TIMER1COEF_TIMER1COEFFICIENT 0xF5 // Default + +/*! + * RegTimer2Coef + */ +#define RF_TIMER2COEF_TIMER2COEFFICIENT 0x20 // Default + +/*! + * RegImageCal + */ +#define RF_IMAGECAL_AUTOIMAGECAL_MASK 0x7F +#define RF_IMAGECAL_AUTOIMAGECAL_ON 0x80 +#define RF_IMAGECAL_AUTOIMAGECAL_OFF 0x00 // Default + +#define RF_IMAGECAL_IMAGECAL_MASK 0xBF +#define RF_IMAGECAL_IMAGECAL_START 0x40 + +#define RF_IMAGECAL_IMAGECAL_RUNNING 0x20 +#define RF_IMAGECAL_IMAGECAL_DONE 0x00 // Default + +#define RF_IMAGECAL_TEMPCHANGE_HIGHER 0x08 +#define RF_IMAGECAL_TEMPCHANGE_LOWER 0x00 + +#define RF_IMAGECAL_TEMPTHRESHOLD_MASK 0xF9 +#define RF_IMAGECAL_TEMPTHRESHOLD_05 0x00 +#define RF_IMAGECAL_TEMPTHRESHOLD_10 0x02 // Default +#define RF_IMAGECAL_TEMPTHRESHOLD_15 0x04 +#define RF_IMAGECAL_TEMPTHRESHOLD_20 0x06 + +#define RF_IMAGECAL_TEMPMONITOR_MASK 0xFE +#define RF_IMAGECAL_TEMPMONITOR_ON 0x00 // Default +#define RF_IMAGECAL_TEMPMONITOR_OFF 0x01 + +/*! + * RegTemp (Read Only) + */ + +/*! + * RegLowBat + */ +#define RF_LOWBAT_MASK 0xF7 +#define RF_LOWBAT_ON 0x08 +#define RF_LOWBAT_OFF 0x00 // Default + +#define RF_LOWBAT_TRIM_MASK 0xF8 +#define RF_LOWBAT_TRIM_1695 0x00 +#define RF_LOWBAT_TRIM_1764 0x01 +#define RF_LOWBAT_TRIM_1835 0x02 // Default +#define RF_LOWBAT_TRIM_1905 0x03 +#define RF_LOWBAT_TRIM_1976 0x04 +#define RF_LOWBAT_TRIM_2045 0x05 +#define RF_LOWBAT_TRIM_2116 0x06 +#define RF_LOWBAT_TRIM_2185 0x07 + +/*! + * RegIrqFlags1 + */ +#define RF_IRQFLAGS1_MODEREADY 0x80 + +#define RF_IRQFLAGS1_RXREADY 0x40 + +#define RF_IRQFLAGS1_TXREADY 0x20 + +#define RF_IRQFLAGS1_PLLLOCK 0x10 + +#define RF_IRQFLAGS1_RSSI 0x08 + +#define RF_IRQFLAGS1_TIMEOUT 0x04 + +#define RF_IRQFLAGS1_PREAMBLEDETECT 0x02 + +#define RF_IRQFLAGS1_SYNCADDRESSMATCH 0x01 + +/*! + * RegIrqFlags2 + */ +#define RF_IRQFLAGS2_FIFOFULL 0x80 + +#define RF_IRQFLAGS2_FIFOEMPTY 0x40 + +#define RF_IRQFLAGS2_FIFOLEVEL 0x20 + +#define RF_IRQFLAGS2_FIFOOVERRUN 0x10 + +#define RF_IRQFLAGS2_PACKETSENT 0x08 + +#define RF_IRQFLAGS2_PAYLOADREADY 0x04 + +#define RF_IRQFLAGS2_CRCOK 0x02 + +#define RF_IRQFLAGS2_LOWBAT 0x01 + +/*! + * RegDioMapping1 + */ +#define RF_DIOMAPPING1_DIO0_MASK 0x3F +#define RF_DIOMAPPING1_DIO0_00 0x00 // Default +#define RF_DIOMAPPING1_DIO0_01 0x40 +#define RF_DIOMAPPING1_DIO0_10 0x80 +#define RF_DIOMAPPING1_DIO0_11 0xC0 + +#define RF_DIOMAPPING1_DIO1_MASK 0xCF +#define RF_DIOMAPPING1_DIO1_00 0x00 // Default +#define RF_DIOMAPPING1_DIO1_01 0x10 +#define RF_DIOMAPPING1_DIO1_10 0x20 +#define RF_DIOMAPPING1_DIO1_11 0x30 + +#define RF_DIOMAPPING1_DIO2_MASK 0xF3 +#define RF_DIOMAPPING1_DIO2_00 0x00 // Default +#define RF_DIOMAPPING1_DIO2_01 0x04 +#define RF_DIOMAPPING1_DIO2_10 0x08 +#define RF_DIOMAPPING1_DIO2_11 0x0C + +#define RF_DIOMAPPING1_DIO3_MASK 0xFC +#define RF_DIOMAPPING1_DIO3_00 0x00 // Default +#define RF_DIOMAPPING1_DIO3_01 0x01 +#define RF_DIOMAPPING1_DIO3_10 0x02 +#define RF_DIOMAPPING1_DIO3_11 0x03 + +/*! + * RegDioMapping2 + */ +#define RF_DIOMAPPING2_DIO4_MASK 0x3F +#define RF_DIOMAPPING2_DIO4_00 0x00 // Default +#define RF_DIOMAPPING2_DIO4_01 0x40 +#define RF_DIOMAPPING2_DIO4_10 0x80 +#define RF_DIOMAPPING2_DIO4_11 0xC0 + +#define RF_DIOMAPPING2_DIO5_MASK 0xCF +#define RF_DIOMAPPING2_DIO5_00 0x00 // Default +#define RF_DIOMAPPING2_DIO5_01 0x10 +#define RF_DIOMAPPING2_DIO5_10 0x20 +#define RF_DIOMAPPING2_DIO5_11 0x30 + +#define RF_DIOMAPPING2_MAP_MASK 0xFE +#define RF_DIOMAPPING2_MAP_PREAMBLEDETECT 0x01 +#define RF_DIOMAPPING2_MAP_RSSI 0x00 // Default + +/*! + * RegVersion (Read Only) + */ + +/*! + * RegPllHop + */ +#define RF_PLLHOP_FASTHOP_MASK 0x7F +#define RF_PLLHOP_FASTHOP_ON 0x80 +#define RF_PLLHOP_FASTHOP_OFF 0x00 // Default + +/*! + * RegTcxo + */ +#define RF_TCXO_TCXOINPUT_MASK 0xEF +#define RF_TCXO_TCXOINPUT_ON 0x10 +#define RF_TCXO_TCXOINPUT_OFF 0x00 // Default + +/*! + * RegPaDac + */ +#define RF_PADAC_20DBM_MASK 0xF8 +#define RF_PADAC_20DBM_ON 0x07 +#define RF_PADAC_20DBM_OFF 0x04 // Default + +/*! + * RegFormerTemp + */ + +/*! + * RegBitrateFrac + */ +#define RF_BITRATEFRAC_MASK 0xF0 + +/*! + * RegAgcRef + */ + +/*! + * RegAgcThresh1 + */ + +/*! + * RegAgcThresh2 + */ + +/*! + * RegAgcThresh3 + */ + +/*! + * RegPll + */ +#define RF_PLL_BANDWIDTH_MASK 0x3F +#define RF_PLL_BANDWIDTH_75 0x00 +#define RF_PLL_BANDWIDTH_150 0x40 +#define RF_PLL_BANDWIDTH_225 0x80 +#define RF_PLL_BANDWIDTH_300 0xC0 // Default + +#ifdef __cplusplus +} +#endif + +#endif // __SX1276_REGS_FSK_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/sx1276Regs-LoRa.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/sx1276Regs-LoRa.h new file mode 100644 index 0000000000..eb7552a4af --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/sx1276Regs-LoRa.h @@ -0,0 +1,582 @@ +/*! + * \file sx1276Regs-LoRa.h + * + * \brief SX1276 LoRa modem registers and bits definitions + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + */ +#ifndef __SX1276_REGS_LORA_H__ +#define __SX1276_REGS_LORA_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*! + * ============================================================================ + * SX1276 Internal registers Address + * ============================================================================ + */ +#define REG_LR_FIFO 0x00 +// Common settings +#define REG_LR_OPMODE 0x01 +#define REG_LR_FRFMSB 0x06 +#define REG_LR_FRFMID 0x07 +#define REG_LR_FRFLSB 0x08 +// Tx settings +#define REG_LR_PACONFIG 0x09 +#define REG_LR_PARAMP 0x0A +#define REG_LR_OCP 0x0B +// Rx settings +#define REG_LR_LNA 0x0C +// LoRa registers +#define REG_LR_FIFOADDRPTR 0x0D +#define REG_LR_FIFOTXBASEADDR 0x0E +#define REG_LR_FIFORXBASEADDR 0x0F +#define REG_LR_FIFORXCURRENTADDR 0x10 +#define REG_LR_IRQFLAGSMASK 0x11 +#define REG_LR_IRQFLAGS 0x12 +#define REG_LR_RXNBBYTES 0x13 +#define REG_LR_RXHEADERCNTVALUEMSB 0x14 +#define REG_LR_RXHEADERCNTVALUELSB 0x15 +#define REG_LR_RXPACKETCNTVALUEMSB 0x16 +#define REG_LR_RXPACKETCNTVALUELSB 0x17 +#define REG_LR_MODEMSTAT 0x18 +#define REG_LR_PKTSNRVALUE 0x19 +#define REG_LR_PKTRSSIVALUE 0x1A +#define REG_LR_RSSIVALUE 0x1B +#define REG_LR_HOPCHANNEL 0x1C +#define REG_LR_MODEMCONFIG1 0x1D +#define REG_LR_MODEMCONFIG2 0x1E +#define REG_LR_SYMBTIMEOUTLSB 0x1F +#define REG_LR_PREAMBLEMSB 0x20 +#define REG_LR_PREAMBLELSB 0x21 +#define REG_LR_PAYLOADLENGTH 0x22 +#define REG_LR_PAYLOADMAXLENGTH 0x23 +#define REG_LR_HOPPERIOD 0x24 +#define REG_LR_FIFORXBYTEADDR 0x25 +#define REG_LR_MODEMCONFIG3 0x26 +#define REG_LR_FEIMSB 0x28 +#define REG_LR_FEIMID 0x29 +#define REG_LR_FEILSB 0x2A +#define REG_LR_RSSIWIDEBAND 0x2C +#define REG_LR_IFFREQ1 0x2F +#define REG_LR_IFFREQ2 0x30 +#define REG_LR_DETECTOPTIMIZE 0x31 +#define REG_LR_INVERTIQ 0x33 +#define REG_LR_HIGHBWOPTIMIZE1 0x36 +#define REG_LR_DETECTIONTHRESHOLD 0x37 +#define REG_LR_SYNCWORD 0x39 +#define REG_LR_HIGHBWOPTIMIZE2 0x3A +#define REG_LR_INVERTIQ2 0x3B + +// end of documented register in datasheet +// I/O settings +#define REG_LR_DIOMAPPING1 0x40 +#define REG_LR_DIOMAPPING2 0x41 +// Version +#define REG_LR_VERSION 0x42 +// Additional settings +#define REG_LR_PLLHOP 0x44 +#define REG_LR_TCXO 0x4B +#define REG_LR_PADAC 0x4D +#define REG_LR_FORMERTEMP 0x5B +#define REG_LR_BITRATEFRAC 0x5D +#define REG_LR_AGCREF 0x61 +#define REG_LR_AGCTHRESH1 0x62 +#define REG_LR_AGCTHRESH2 0x63 +#define REG_LR_AGCTHRESH3 0x64 +#define REG_LR_PLL 0x70 + +/*! + * ============================================================================ + * SX1276 LoRa bits control definition + * ============================================================================ + */ + +/*! + * RegFifo + */ + +/*! + * RegOpMode + */ +#define RFLR_OPMODE_LONGRANGEMODE_MASK 0x7F +#define RFLR_OPMODE_LONGRANGEMODE_OFF 0x00 // Default +#define RFLR_OPMODE_LONGRANGEMODE_ON 0x80 + +#define RFLR_OPMODE_ACCESSSHAREDREG_MASK 0xBF +#define RFLR_OPMODE_ACCESSSHAREDREG_ENABLE 0x40 +#define RFLR_OPMODE_ACCESSSHAREDREG_DISABLE 0x00 // Default + +#define RFLR_OPMODE_FREQMODE_ACCESS_MASK 0xF7 +#define RFLR_OPMODE_FREQMODE_ACCESS_LF 0x08 // Default +#define RFLR_OPMODE_FREQMODE_ACCESS_HF 0x00 + +#define RFLR_OPMODE_MASK 0xF8 +#define RFLR_OPMODE_SLEEP 0x00 +#define RFLR_OPMODE_STANDBY 0x01 // Default +#define RFLR_OPMODE_SYNTHESIZER_TX 0x02 +#define RFLR_OPMODE_TRANSMITTER 0x03 +#define RFLR_OPMODE_SYNTHESIZER_RX 0x04 +#define RFLR_OPMODE_RECEIVER 0x05 +// LoRa specific modes +#define RFLR_OPMODE_RECEIVER_SINGLE 0x06 +#define RFLR_OPMODE_CAD 0x07 + +/*! + * RegFrf (MHz) + */ +#define RFLR_FRFMSB_434_MHZ 0x6C // Default +#define RFLR_FRFMID_434_MHZ 0x80 // Default +#define RFLR_FRFLSB_434_MHZ 0x00 // Default + +/*! + * RegPaConfig + */ +#define RFLR_PACONFIG_PASELECT_MASK 0x7F +#define RFLR_PACONFIG_PASELECT_PABOOST 0x80 +#define RFLR_PACONFIG_PASELECT_RFO 0x00 // Default + +#define RFLR_PACONFIG_MAX_POWER_MASK 0x8F + +#define RFLR_PACONFIG_OUTPUTPOWER_MASK 0xF0 + +/*! + * RegPaRamp + */ +#define RFLR_PARAMP_TXBANDFORCE_MASK 0xEF +#define RFLR_PARAMP_TXBANDFORCE_BAND_SEL 0x10 +#define RFLR_PARAMP_TXBANDFORCE_AUTO 0x00 // Default + +#define RFLR_PARAMP_MASK 0xF0 +#define RFLR_PARAMP_3400_US 0x00 +#define RFLR_PARAMP_2000_US 0x01 +#define RFLR_PARAMP_1000_US 0x02 +#define RFLR_PARAMP_0500_US 0x03 +#define RFLR_PARAMP_0250_US 0x04 +#define RFLR_PARAMP_0125_US 0x05 +#define RFLR_PARAMP_0100_US 0x06 +#define RFLR_PARAMP_0062_US 0x07 +#define RFLR_PARAMP_0050_US 0x08 +#define RFLR_PARAMP_0040_US 0x09 // Default +#define RFLR_PARAMP_0031_US 0x0A +#define RFLR_PARAMP_0025_US 0x0B +#define RFLR_PARAMP_0020_US 0x0C +#define RFLR_PARAMP_0015_US 0x0D +#define RFLR_PARAMP_0012_US 0x0E +#define RFLR_PARAMP_0010_US 0x0F + +/*! + * RegOcp + */ +#define RFLR_OCP_MASK 0xDF +#define RFLR_OCP_ON 0x20 // Default +#define RFLR_OCP_OFF 0x00 + +#define RFLR_OCP_TRIM_MASK 0xE0 +#define RFLR_OCP_TRIM_045_MA 0x00 +#define RFLR_OCP_TRIM_050_MA 0x01 +#define RFLR_OCP_TRIM_055_MA 0x02 +#define RFLR_OCP_TRIM_060_MA 0x03 +#define RFLR_OCP_TRIM_065_MA 0x04 +#define RFLR_OCP_TRIM_070_MA 0x05 +#define RFLR_OCP_TRIM_075_MA 0x06 +#define RFLR_OCP_TRIM_080_MA 0x07 +#define RFLR_OCP_TRIM_085_MA 0x08 +#define RFLR_OCP_TRIM_090_MA 0x09 +#define RFLR_OCP_TRIM_095_MA 0x0A +#define RFLR_OCP_TRIM_100_MA 0x0B // Default +#define RFLR_OCP_TRIM_105_MA 0x0C +#define RFLR_OCP_TRIM_110_MA 0x0D +#define RFLR_OCP_TRIM_115_MA 0x0E +#define RFLR_OCP_TRIM_120_MA 0x0F +#define RFLR_OCP_TRIM_130_MA 0x10 +#define RFLR_OCP_TRIM_140_MA 0x11 +#define RFLR_OCP_TRIM_150_MA 0x12 +#define RFLR_OCP_TRIM_160_MA 0x13 +#define RFLR_OCP_TRIM_170_MA 0x14 +#define RFLR_OCP_TRIM_180_MA 0x15 +#define RFLR_OCP_TRIM_190_MA 0x16 +#define RFLR_OCP_TRIM_200_MA 0x17 +#define RFLR_OCP_TRIM_210_MA 0x18 +#define RFLR_OCP_TRIM_220_MA 0x19 +#define RFLR_OCP_TRIM_230_MA 0x1A +#define RFLR_OCP_TRIM_240_MA 0x1B + +/*! + * RegLna + */ +#define RFLR_LNA_GAIN_MASK 0x1F +#define RFLR_LNA_GAIN_G1 0x20 // Default +#define RFLR_LNA_GAIN_G2 0x40 +#define RFLR_LNA_GAIN_G3 0x60 +#define RFLR_LNA_GAIN_G4 0x80 +#define RFLR_LNA_GAIN_G5 0xA0 +#define RFLR_LNA_GAIN_G6 0xC0 + +#define RFLR_LNA_BOOST_LF_MASK 0xE7 +#define RFLR_LNA_BOOST_LF_DEFAULT 0x00 // Default + +#define RFLR_LNA_BOOST_HF_MASK 0xFC +#define RFLR_LNA_BOOST_HF_OFF 0x00 // Default +#define RFLR_LNA_BOOST_HF_ON 0x03 + +/*! + * RegFifoAddrPtr + */ +#define RFLR_FIFOADDRPTR 0x00 // Default + +/*! + * RegFifoTxBaseAddr + */ +#define RFLR_FIFOTXBASEADDR 0x80 // Default + +/*! + * RegFifoTxBaseAddr + */ +#define RFLR_FIFORXBASEADDR 0x00 // Default + +/*! + * RegFifoRxCurrentAddr (Read Only) + */ + +/*! + * RegIrqFlagsMask + */ +#define RFLR_IRQFLAGS_RXTIMEOUT_MASK 0x80 +#define RFLR_IRQFLAGS_RXDONE_MASK 0x40 +#define RFLR_IRQFLAGS_PAYLOADCRCERROR_MASK 0x20 +#define RFLR_IRQFLAGS_VALIDHEADER_MASK 0x10 +#define RFLR_IRQFLAGS_TXDONE_MASK 0x08 +#define RFLR_IRQFLAGS_CADDONE_MASK 0x04 +#define RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL_MASK 0x02 +#define RFLR_IRQFLAGS_CADDETECTED_MASK 0x01 + +/*! + * RegIrqFlags + */ +#define RFLR_IRQFLAGS_RXTIMEOUT 0x80 +#define RFLR_IRQFLAGS_RXDONE 0x40 +#define RFLR_IRQFLAGS_PAYLOADCRCERROR 0x20 +#define RFLR_IRQFLAGS_VALIDHEADER 0x10 +#define RFLR_IRQFLAGS_TXDONE 0x08 +#define RFLR_IRQFLAGS_CADDONE 0x04 +#define RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL 0x02 +#define RFLR_IRQFLAGS_CADDETECTED 0x01 + +/*! + * RegFifoRxNbBytes (Read Only) + */ + +/*! + * RegRxHeaderCntValueMsb (Read Only) + */ + +/*! + * RegRxHeaderCntValueLsb (Read Only) + */ + +/*! + * RegRxPacketCntValueMsb (Read Only) + */ + +/*! + * RegRxPacketCntValueLsb (Read Only) + */ + +/*! + * RegModemStat (Read Only) + */ +#define RFLR_MODEMSTAT_RX_CR_MASK 0x1F +#define RFLR_MODEMSTAT_MODEM_STATUS_MASK 0xE0 + +/*! + * RegPktSnrValue (Read Only) + */ + +/*! + * RegPktRssiValue (Read Only) + */ + +/*! + * RegRssiValue (Read Only) + */ + +/*! + * RegHopChannel (Read Only) + */ +#define RFLR_HOPCHANNEL_PLL_LOCK_TIMEOUT_MASK 0x7F +#define RFLR_HOPCHANNEL_PLL_LOCK_FAIL 0x80 +#define RFLR_HOPCHANNEL_PLL_LOCK_SUCCEED 0x00 // Default + +#define RFLR_HOPCHANNEL_CRCONPAYLOAD_MASK 0xBF +#define RFLR_HOPCHANNEL_CRCONPAYLOAD_ON 0x40 +#define RFLR_HOPCHANNEL_CRCONPAYLOAD_OFF 0x00 // Default + +#define RFLR_HOPCHANNEL_CHANNEL_MASK 0x3F + +/*! + * RegModemConfig1 + */ +#define RFLR_MODEMCONFIG1_BW_MASK 0x0F +#define RFLR_MODEMCONFIG1_BW_7_81_KHZ 0x00 +#define RFLR_MODEMCONFIG1_BW_10_41_KHZ 0x10 +#define RFLR_MODEMCONFIG1_BW_15_62_KHZ 0x20 +#define RFLR_MODEMCONFIG1_BW_20_83_KHZ 0x30 +#define RFLR_MODEMCONFIG1_BW_31_25_KHZ 0x40 +#define RFLR_MODEMCONFIG1_BW_41_66_KHZ 0x50 +#define RFLR_MODEMCONFIG1_BW_62_50_KHZ 0x60 +#define RFLR_MODEMCONFIG1_BW_125_KHZ 0x70 // Default +#define RFLR_MODEMCONFIG1_BW_250_KHZ 0x80 +#define RFLR_MODEMCONFIG1_BW_500_KHZ 0x90 + +#define RFLR_MODEMCONFIG1_CODINGRATE_MASK 0xF1 +#define RFLR_MODEMCONFIG1_CODINGRATE_4_5 0x02 +#define RFLR_MODEMCONFIG1_CODINGRATE_4_6 0x04 // Default +#define RFLR_MODEMCONFIG1_CODINGRATE_4_7 0x06 +#define RFLR_MODEMCONFIG1_CODINGRATE_4_8 0x08 + +#define RFLR_MODEMCONFIG1_IMPLICITHEADER_MASK 0xFE +#define RFLR_MODEMCONFIG1_IMPLICITHEADER_ON 0x01 +#define RFLR_MODEMCONFIG1_IMPLICITHEADER_OFF 0x00 // Default + +/*! + * RegModemConfig2 + */ +#define RFLR_MODEMCONFIG2_SF_MASK 0x0F +#define RFLR_MODEMCONFIG2_SF_6 0x60 +#define RFLR_MODEMCONFIG2_SF_7 0x70 // Default +#define RFLR_MODEMCONFIG2_SF_8 0x80 +#define RFLR_MODEMCONFIG2_SF_9 0x90 +#define RFLR_MODEMCONFIG2_SF_10 0xA0 +#define RFLR_MODEMCONFIG2_SF_11 0xB0 +#define RFLR_MODEMCONFIG2_SF_12 0xC0 + +#define RFLR_MODEMCONFIG2_TXCONTINUOUSMODE_MASK 0xF7 +#define RFLR_MODEMCONFIG2_TXCONTINUOUSMODE_ON 0x08 +#define RFLR_MODEMCONFIG2_TXCONTINUOUSMODE_OFF 0x00 + +#define RFLR_MODEMCONFIG2_RXPAYLOADCRC_MASK 0xFB +#define RFLR_MODEMCONFIG2_RXPAYLOADCRC_ON 0x04 +#define RFLR_MODEMCONFIG2_RXPAYLOADCRC_OFF 0x00 // Default + +#define RFLR_MODEMCONFIG2_SYMBTIMEOUTMSB_MASK 0xFC +#define RFLR_MODEMCONFIG2_SYMBTIMEOUTMSB 0x00 // Default + +/*! + * RegSymbTimeoutLsb + */ +#define RFLR_SYMBTIMEOUTLSB_SYMBTIMEOUT 0x64 // Default + +/*! + * RegPreambleLengthMsb + */ +#define RFLR_PREAMBLELENGTHMSB 0x00 // Default + +/*! + * RegPreambleLengthLsb + */ +#define RFLR_PREAMBLELENGTHLSB 0x08 // Default + +/*! + * RegPayloadLength + */ +#define RFLR_PAYLOADLENGTH 0x0E // Default + +/*! + * RegPayloadMaxLength + */ +#define RFLR_PAYLOADMAXLENGTH 0xFF // Default + +/*! + * RegHopPeriod + */ +#define RFLR_HOPPERIOD_FREQFOPPINGPERIOD 0x00 // Default + +/*! + * RegFifoRxByteAddr (Read Only) + */ + +/*! + * RegModemConfig3 + */ +#define RFLR_MODEMCONFIG3_LOWDATARATEOPTIMIZE_MASK 0xF7 +#define RFLR_MODEMCONFIG3_LOWDATARATEOPTIMIZE_ON 0x08 +#define RFLR_MODEMCONFIG3_LOWDATARATEOPTIMIZE_OFF 0x00 // Default + +#define RFLR_MODEMCONFIG3_AGCAUTO_MASK 0xFB +#define RFLR_MODEMCONFIG3_AGCAUTO_ON 0x04 // Default +#define RFLR_MODEMCONFIG3_AGCAUTO_OFF 0x00 + +/*! + * RegFeiMsb (Read Only) + */ + +/*! + * RegFeiMid (Read Only) + */ + +/*! + * RegFeiLsb (Read Only) + */ + +/*! + * RegRssiWideband (Read Only) + */ + +/*! + * RegDetectOptimize + */ +#define RFLR_DETECTIONOPTIMIZE_MASK 0xF8 +#define RFLR_DETECTIONOPTIMIZE_SF7_TO_SF12 0x03 // Default +#define RFLR_DETECTIONOPTIMIZE_SF6 0x05 + +/*! + * RegInvertIQ + */ +#define RFLR_INVERTIQ_RX_MASK 0xBF +#define RFLR_INVERTIQ_RX_OFF 0x00 +#define RFLR_INVERTIQ_RX_ON 0x40 +#define RFLR_INVERTIQ_TX_MASK 0xFE +#define RFLR_INVERTIQ_TX_OFF 0x01 +#define RFLR_INVERTIQ_TX_ON 0x00 + +/*! + * RegDetectionThreshold + */ +#define RFLR_DETECTIONTHRESH_SF7_TO_SF12 0x0A // Default +#define RFLR_DETECTIONTHRESH_SF6 0x0C + +/*! + * RegInvertIQ2 + */ +#define RFLR_INVERTIQ2_ON 0x19 +#define RFLR_INVERTIQ2_OFF 0x1D + +/*! + * RegDioMapping1 + */ +#define RFLR_DIOMAPPING1_DIO0_MASK 0x3F +#define RFLR_DIOMAPPING1_DIO0_00 0x00 // Default +#define RFLR_DIOMAPPING1_DIO0_01 0x40 +#define RFLR_DIOMAPPING1_DIO0_10 0x80 +#define RFLR_DIOMAPPING1_DIO0_11 0xC0 + +#define RFLR_DIOMAPPING1_DIO1_MASK 0xCF +#define RFLR_DIOMAPPING1_DIO1_00 0x00 // Default +#define RFLR_DIOMAPPING1_DIO1_01 0x10 +#define RFLR_DIOMAPPING1_DIO1_10 0x20 +#define RFLR_DIOMAPPING1_DIO1_11 0x30 + +#define RFLR_DIOMAPPING1_DIO2_MASK 0xF3 +#define RFLR_DIOMAPPING1_DIO2_00 0x00 // Default +#define RFLR_DIOMAPPING1_DIO2_01 0x04 +#define RFLR_DIOMAPPING1_DIO2_10 0x08 +#define RFLR_DIOMAPPING1_DIO2_11 0x0C + +#define RFLR_DIOMAPPING1_DIO3_MASK 0xFC +#define RFLR_DIOMAPPING1_DIO3_00 0x00 // Default +#define RFLR_DIOMAPPING1_DIO3_01 0x01 +#define RFLR_DIOMAPPING1_DIO3_10 0x02 +#define RFLR_DIOMAPPING1_DIO3_11 0x03 + +/*! + * RegDioMapping2 + */ +#define RFLR_DIOMAPPING2_DIO4_MASK 0x3F +#define RFLR_DIOMAPPING2_DIO4_00 0x00 // Default +#define RFLR_DIOMAPPING2_DIO4_01 0x40 +#define RFLR_DIOMAPPING2_DIO4_10 0x80 +#define RFLR_DIOMAPPING2_DIO4_11 0xC0 + +#define RFLR_DIOMAPPING2_DIO5_MASK 0xCF +#define RFLR_DIOMAPPING2_DIO5_00 0x00 // Default +#define RFLR_DIOMAPPING2_DIO5_01 0x10 +#define RFLR_DIOMAPPING2_DIO5_10 0x20 +#define RFLR_DIOMAPPING2_DIO5_11 0x30 + +#define RFLR_DIOMAPPING2_MAP_MASK 0xFE +#define RFLR_DIOMAPPING2_MAP_PREAMBLEDETECT 0x01 +#define RFLR_DIOMAPPING2_MAP_RSSI 0x00 // Default + +/*! + * RegVersion (Read Only) + */ + +/*! + * RegPllHop + */ +#define RFLR_PLLHOP_FASTHOP_MASK 0x7F +#define RFLR_PLLHOP_FASTHOP_ON 0x80 +#define RFLR_PLLHOP_FASTHOP_OFF 0x00 // Default + +/*! + * RegTcxo + */ +#define RFLR_TCXO_TCXOINPUT_MASK 0xEF +#define RFLR_TCXO_TCXOINPUT_ON 0x10 +#define RFLR_TCXO_TCXOINPUT_OFF 0x00 // Default + +/*! + * RegPaDac + */ +#define RFLR_PADAC_20DBM_MASK 0xF8 +#define RFLR_PADAC_20DBM_ON 0x07 +#define RFLR_PADAC_20DBM_OFF 0x04 // Default + +/*! + * RegFormerTemp + */ + +/*! + * RegBitrateFrac + */ +#define RF_BITRATEFRAC_MASK 0xF0 + +/*! + * RegAgcRef + */ + +/*! + * RegAgcThresh1 + */ + +/*! + * RegAgcThresh2 + */ + +/*! + * RegAgcThresh3 + */ + +/*! + * RegPll + */ +#define RF_PLL_BANDWIDTH_MASK 0x3F +#define RF_PLL_BANDWIDTH_75 0x00 +#define RF_PLL_BANDWIDTH_150 0x40 +#define RF_PLL_BANDWIDTH_225 0x80 +#define RF_PLL_BANDWIDTH_300 0xC0 // Default + +#ifdef __cplusplus +} +#endif + +#endif // __SX1276_REGS_LORA_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/systime.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/systime.c new file mode 100644 index 0000000000..9f1ffaf1b7 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/systime.c @@ -0,0 +1,16 @@ +#include +#include "systime.h" + +SysTime_t SysTimeAdd( SysTime_t a, SysTime_t b) { + SysTime_t ret; + ret.Seconds = (a.Seconds + b.Seconds); + ret.SubSeconds = (a.SubSeconds + b.SubSeconds); + return ret; +} + +SysTime_t SysTimeSub( SysTime_t a, SysTime_t b) { + SysTime_t ret; + ret.Seconds = (a.Seconds - b.Seconds); + ret.SubSeconds = (a.SubSeconds - b.SubSeconds); + return ret; +} \ No newline at end of file diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/systime.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/systime.h new file mode 100644 index 0000000000..21093203ab --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/systime.h @@ -0,0 +1,81 @@ +#ifndef SYSTIME_S +#define SYSTIME_S + +#include + +#ifndef TimerTime_t +typedef uint32_t TimerTime_t; +#define TIMERTIME_T_MAX ( ( uint32_t )~0 ) +#endif + +typedef struct SysTime_s { + uint32_t Seconds; + uint32_t SubSeconds; +}SysTime_t; + +/*! + * \brief Sets new system time + * + * \param sysTime New seconds/sub-seconds since UNIX epoch origin + */ +void SysTimeSet( SysTime_t sysTime ); + +/*! + * \brief Gets current system time + * + * \retval sysTime Current seconds/sub-seconds since UNIX epoch origin + */ +SysTime_t SysTimeGet( void ); +SysTime_t SysTimeAdd( SysTime_t a, SysTime_t b); +SysTime_t SysTimeSub( SysTime_t a, SysTime_t b); + +/*! + * \brief Gets current MCU system time + * + * \retval sysTime Current seconds/sub-seconds since Mcu started + */ +SysTime_t SysTimeGetMcuTime( void ); + +/*! + * Converts the given SysTime to the equivalent RTC value in milliseconds + * + * \param [IN] sysTime System time to be converted + * + * \retval timeMs The RTC converted time value in ms + */ +TimerTime_t SysTimeToMs( SysTime_t sysTime ); + +/*! + * Converts the given RTC value in milliseconds to the equivalent SysTime + * + * \param [IN] timeMs The RTC time value in ms to be converted + * + * \retval sysTime Converted system time + */ +SysTime_t SysTimeFromMs( TimerTime_t timeMs ); + +/*! + * \brief Convert a calendar time into time since UNIX epoch as a uint32_t. + * + * \param [IN] localtime Pointer to the object containing the calendar time + * \retval timestamp The calendar time as seconds since UNIX epoch. + */ +uint32_t SysTimeMkTime( const struct tm* localtime ); + +/*! + * \brief Converts a given time in seconds since UNIX epoch into calendar time. + * + * \param [IN] timestamp The time since UNIX epoch to convert into calendar time. + * \param [OUT] localtime Pointer to the calendar time object which will contain + the result of the conversion. + */ +void SysTimeLocalTime( const uint32_t timestamp, struct tm *localtime ); + +/*! + * \brief Number of seconds elapsed between Unix and GPS epoch + */ +#define UNIX_GPS_EPOCH_OFFSET 315964800 + + + +#endif \ No newline at end of file diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/timer.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/timer.c new file mode 100644 index 0000000000..a2fb516c84 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/timer.c @@ -0,0 +1,385 @@ +/*! + * \file timer.c + * + * \brief Timer objects and scheduling management implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + */ +#include "utilities.h" +#include "board.h" +#include "timer.h" + +/*! + * Safely execute call back + */ +#define ExecuteCallBack( _callback_, context ) \ + do \ + { \ + if( _callback_ == NULL ) \ + { \ + while( 1 ); \ + } \ + else \ + { \ + _callback_( context ); \ + } \ + }while( 0 ); + +/*! + * Timers list head pointer + */ +static TimerEvent_t *TimerListHead = NULL; + +/*! + * \brief Adds or replace the head timer of the list. + * + * \remark The list is automatically sorted. The list head always contains the + * next timer to expire. + * + * \param [IN] obj Timer object to be become the new head + * \param [IN] remainingTime Remaining time of the previous head to be replaced + */ +static void TimerInsertNewHeadTimer( TimerEvent_t *obj ); + +/*! + * \brief Adds a timer to the list. + * + * \remark The list is automatically sorted. The list head always contains the + * next timer to expire. + * + * \param [IN] obj Timer object to be added to the list + * \param [IN] remainingTime Remaining time of the running head after which the object may be added + */ +static void TimerInsertTimer( TimerEvent_t *obj ); + +/*! + * \brief Sets a timeout with the duration "timestamp" + * + * \param [IN] timestamp Delay duration + */ +static void TimerSetTimeout( TimerEvent_t *obj ); + +/*! + * \brief Check if the Object to be added is not already in the list + * + * \param [IN] timestamp Delay duration + * \retval true (the object is already in the list) or false + */ +static bool TimerExists( TimerEvent_t *obj ); + +void TimerInit( TimerEvent_t *obj, void ( *callback )( void *context ) ) +{ + obj->Timestamp = 0; + obj->ReloadValue = 0; + obj->IsStarted = false; + obj->IsNext2Expire = false; + obj->Callback = callback; + obj->Context = NULL; + obj->Next = NULL; +} + +void TimerSetContext( TimerEvent_t *obj, void* context ) +{ + obj->Context = context; +} + +void TimerStart( TimerEvent_t *obj ) +{ + uint32_t elapsedTime = 0; + + CRITICAL_SECTION_BEGIN( ); + + if( ( obj == NULL ) || ( TimerExists( obj ) == true ) ) + { + CRITICAL_SECTION_END( ); + return; + } + + obj->Timestamp = obj->ReloadValue; + obj->IsStarted = true; + obj->IsNext2Expire = false; + + if( TimerListHead == NULL ) + { + RtcSetTimerContext( ); + // Inserts a timer at time now + obj->Timestamp + TimerInsertNewHeadTimer( obj ); + } + else + { + elapsedTime = RtcGetTimerElapsedTime( ); + obj->Timestamp += elapsedTime; + + if( obj->Timestamp < TimerListHead->Timestamp ) + { + TimerInsertNewHeadTimer( obj ); + } + else + { + TimerInsertTimer( obj ); + } + } + CRITICAL_SECTION_END( ); +} + +static void TimerInsertTimer( TimerEvent_t *obj ) +{ + TimerEvent_t* cur = TimerListHead; + TimerEvent_t* next = TimerListHead->Next; + + while( cur->Next != NULL ) + { + if( obj->Timestamp > next->Timestamp ) + { + cur = next; + next = next->Next; + } + else + { + cur->Next = obj; + obj->Next = next; + return; + } + } + cur->Next = obj; + obj->Next = NULL; +} + +static void TimerInsertNewHeadTimer( TimerEvent_t *obj ) +{ + TimerEvent_t* cur = TimerListHead; + + if( cur != NULL ) + { + cur->IsNext2Expire = false; + } + + obj->Next = cur; + TimerListHead = obj; + TimerSetTimeout( TimerListHead ); +} + +bool TimerIsStarted( TimerEvent_t *obj ) +{ + return obj->IsStarted; +} + +void TimerIrqHandler( void ) +{ + TimerEvent_t* cur; + TimerEvent_t* next; + + uint32_t old = RtcGetTimerContext( ); + uint32_t now = RtcSetTimerContext( ); + uint32_t deltaContext = now - old; // intentional wrap around + + // Update timeStamp based upon new Time Reference + // because delta context should never exceed 2^32 + if( TimerListHead != NULL ) + { + for( cur = TimerListHead; cur->Next != NULL; cur = cur->Next ) + { + next = cur->Next; + if( next->Timestamp > deltaContext ) + { + next->Timestamp -= deltaContext; + } + else + { + next->Timestamp = 0; + } + } + } + + // Execute immediately the alarm callback + if ( TimerListHead != NULL ) + { + cur = TimerListHead; + TimerListHead = TimerListHead->Next; + cur->IsStarted = false; + ExecuteCallBack( cur->Callback, cur->Context ); + } + + // Remove all the expired object from the list + while( ( TimerListHead != NULL ) && ( TimerListHead->Timestamp < RtcGetTimerElapsedTime( ) ) ) + { + cur = TimerListHead; + TimerListHead = TimerListHead->Next; + cur->IsStarted = false; + ExecuteCallBack( cur->Callback, cur->Context ); + } + + // Start the next TimerListHead if it exists AND NOT running + if( ( TimerListHead != NULL ) && ( TimerListHead->IsNext2Expire == false ) ) + { + TimerSetTimeout( TimerListHead ); + } +} + +void TimerStop( TimerEvent_t *obj ) +{ + CRITICAL_SECTION_BEGIN( ); + + TimerEvent_t* prev = TimerListHead; + TimerEvent_t* cur = TimerListHead; + + // List is empty or the obj to stop does not exist + if( ( TimerListHead == NULL ) || ( obj == NULL ) ) + { + CRITICAL_SECTION_END( ); + return; + } + + obj->IsStarted = false; + + if( TimerListHead == obj ) // Stop the Head + { + if( TimerListHead->IsNext2Expire == true ) // The head is already running + { + TimerListHead->IsNext2Expire = false; + if( TimerListHead->Next != NULL ) + { + TimerListHead = TimerListHead->Next; + TimerSetTimeout( TimerListHead ); + } + else + { + RtcStopAlarm( ); + TimerListHead = NULL; + } + } + else // Stop the head before it is started + { + if( TimerListHead->Next != NULL ) + { + TimerListHead = TimerListHead->Next; + } + else + { + TimerListHead = NULL; + } + } + } + else // Stop an object within the list + { + while( cur != NULL ) + { + if( cur == obj ) + { + if( cur->Next != NULL ) + { + cur = cur->Next; + prev->Next = cur; + } + else + { + cur = NULL; + prev->Next = cur; + } + break; + } + else + { + prev = cur; + cur = cur->Next; + } + } + } + CRITICAL_SECTION_END( ); +} + +static bool TimerExists( TimerEvent_t *obj ) +{ + TimerEvent_t* cur = TimerListHead; + + while( cur != NULL ) + { + if( cur == obj ) + { + return true; + } + cur = cur->Next; + } + return false; +} + +void TimerReset( TimerEvent_t *obj ) +{ + TimerStop( obj ); + TimerStart( obj ); +} + +void TimerSetValue( TimerEvent_t *obj, uint32_t value ) +{ + uint32_t minValue = 0; + uint32_t ticks = RtcMs2Tick( value ); + + TimerStop( obj ); + + minValue = RtcGetMinimumTimeout( ); + + if( ticks < minValue ) + { + ticks = minValue; + } + + obj->Timestamp = ticks; + obj->ReloadValue = ticks; +} + +TimerTime_t TimerGetCurrentTime( void ) +{ + uint32_t now = RtcGetTimerValue( ); + return RtcTick2Ms( now ); +} + +TimerTime_t TimerGetElapsedTime( TimerTime_t past ) +{ + if ( past == 0 ) + { + return 0; + } + uint32_t nowInTicks = RtcGetTimerValue( ); + uint32_t pastInTicks = RtcMs2Tick( past ); + + // Intentional wrap around. Works Ok if tick duration below 1ms + return RtcTick2Ms( nowInTicks - pastInTicks ); +} + +static void TimerSetTimeout( TimerEvent_t *obj ) +{ + int32_t minTicks= RtcGetMinimumTimeout( ); + obj->IsNext2Expire = true; + + // In case deadline too soon + if( obj->Timestamp < ( RtcGetTimerElapsedTime( ) + minTicks ) ) + { + obj->Timestamp = RtcGetTimerElapsedTime( ) + minTicks; + } + RtcSetAlarm( obj->Timestamp ); +} + +TimerTime_t TimerTempCompensation( TimerTime_t period, float temperature ) +{ + return RtcTempCompensation( period, temperature ); +} + +void TimerProcess( void ) +{ + RtcProcess( ); +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/timer.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/timer.h new file mode 100644 index 0000000000..ccf4c27346 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/timer.h @@ -0,0 +1,126 @@ +#include +#include +#include "systime.h" + +#ifndef __TIMER_H__ +#define __TIMER_H__ + +/*! + * \brief Timer object description + */ +typedef struct TimerEvent_s +{ + uint32_t Timestamp; //! Current timer value + uint32_t ReloadValue; //! Timer delay value + bool IsStarted; //! Is the timer currently running + bool IsNext2Expire; //! Is the next timer to expire + void ( *Callback )( void* context ); //! Timer IRQ callback function + void *Context; //! User defined data object pointer to pass back + struct TimerEvent_s *Next; //! Pointer to the next Timer object. +}TimerEvent_t; + +/*! + * \brief Timer time variable definition + */ +#ifndef TimerTime_t +typedef uint32_t TimerTime_t; +#define TIMERTIME_T_MAX ( ( uint32_t )~0 ) +#endif + +/*! + * \brief Initializes the timer object + * + * \remark TimerSetValue function must be called before starting the timer. + * this function initializes timestamp and reload value at 0. + * + * \param [IN] obj Structure containing the timer object parameters + * \param [IN] callback Function callback called at the end of the timeout + */ +void TimerInit( TimerEvent_t *obj, void ( *callback )( void *context ) ); + +/*! + * \brief Sets a user defined object pointer + * + * \param [IN] context User defined data object pointer to pass back + * on IRQ handler callback + */ +void TimerSetContext( TimerEvent_t *obj, void* context ); + +/*! + * Timer IRQ event handler + */ +void TimerIrqHandler( void ); + +/*! + * \brief Starts and adds the timer object to the list of timer events + * + * \param [IN] obj Structure containing the timer object parameters + */ +void TimerStart( TimerEvent_t *obj ); + +/*! + * \brief Checks if the provided timer is running + * + * \param [IN] obj Structure containing the timer object parameters + * + * \retval status returns the timer activity status [true: Started, + * false: Stopped] + */ +bool TimerIsStarted( TimerEvent_t *obj ); + +/*! + * \brief Stops and removes the timer object from the list of timer events + * + * \param [IN] obj Structure containing the timer object parameters + */ +void TimerStop( TimerEvent_t *obj ); + +/*! + * \brief Resets the timer object + * + * \param [IN] obj Structure containing the timer object parameters + */ +void TimerReset( TimerEvent_t *obj ); + +/*! + * \brief Set timer new timeout value + * + * \param [IN] obj Structure containing the timer object parameters + * \param [IN] value New timer timeout value + */ +void TimerSetValue( TimerEvent_t *obj, uint32_t value ); + +/*! + * \brief Read the current time + * + * \retval time returns current time + */ +TimerTime_t TimerGetCurrentTime( void ); + +/*! + * \brief Return the Time elapsed since a fix moment in Time + * + * \remark TimerGetElapsedTime will return 0 for argument 0. + * + * \param [IN] past fix moment in Time + * \retval time returns elapsed time + */ +TimerTime_t TimerGetElapsedTime( TimerTime_t past ); + +/*! + * \brief Computes the temperature compensation for a period of time on a + * specific temperature. + * + * \param [IN] period Time period to compensate + * \param [IN] temperature Current temperature + * + * \retval Compensated time period + */ +TimerTime_t TimerTempCompensation( TimerTime_t period, float temperature ); + +/*! + * \brief Processes pending timer events + */ +void TimerProcess( void ); + +#endif \ No newline at end of file diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/utilities.c b/sysdrv/tools/board/lorawan-bridge/LoraMac/utilities.c new file mode 100644 index 0000000000..0a973f7017 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/utilities.c @@ -0,0 +1,151 @@ +/*! + * \file utilities.h + * + * \brief Helper functions implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + */ +#include +#include +#include "utilities.h" + +/*! + * Redefinition of rand() and srand() standard C functions. + * These functions are redefined in order to get the same behavior across + * different compiler toolchains implementations. + */ +// Standard random functions redefinition start +#define RAND_LOCAL_MAX 2147483647L + +static uint32_t next = 1; + +int32_t rand1( void ) +{ + return ( ( next = next * 1103515245L + 12345L ) % RAND_LOCAL_MAX ); +} + +void srand1( uint32_t seed ) +{ + next = seed; +} +// Standard random functions redefinition end + +int32_t randr( int32_t min, int32_t max ) +{ + return ( int32_t )rand1( ) % ( max - min + 1 ) + min; +} + +void memcpy1( uint8_t *dst, const uint8_t *src, uint16_t size ) +{ + while( size-- ) + { + *dst++ = *src++; + } +} + +void memcpyr( uint8_t *dst, const uint8_t *src, uint16_t size ) +{ + dst = dst + ( size - 1 ); + while( size-- ) + { + *dst-- = *src++; + } +} + +void memset1( uint8_t *dst, uint8_t value, uint16_t size ) +{ + while( size-- ) + { + *dst++ = value; + } +} + +int8_t Nibble2HexChar( uint8_t a ) +{ + if( a < 10 ) + { + return '0' + a; + } + else if( a < 16 ) + { + return 'A' + ( a - 10 ); + } + else + { + return '?'; + } +} + +uint32_t Crc32( uint8_t *buffer, uint16_t length ) +{ + // The CRC calculation follows CCITT - 0x04C11DB7 + const uint32_t reversedPolynom = 0xEDB88320; + + // CRC initial value + uint32_t crc = 0xFFFFFFFF; + + if( buffer == NULL ) + { + return 0; + } + + for( uint16_t i = 0; i < length; ++i ) + { + crc ^= ( uint32_t )buffer[i]; + for( uint16_t j = 0; j < 8; j++ ) + { + crc = ( crc >> 1 ) ^ ( reversedPolynom & ~( ( crc & 0x01 ) - 1 ) ); + } + } + + return ~crc; +} + +uint32_t Crc32Init( void ) +{ + return 0xFFFFFFFF; +} + +uint32_t Crc32Update( uint32_t crcInit, uint8_t *buffer, uint16_t length ) +{ + // The CRC calculation follows CCITT - 0x04C11DB7 + const uint32_t reversedPolynom = 0xEDB88320; + + // CRC initial value + uint32_t crc = crcInit; + + if( buffer == NULL ) + { + return 0; + } + + for( uint16_t i = 0; i < length; ++i ) + { + crc ^= ( uint32_t )buffer[i]; + for( uint16_t j = 0; j < 8; j++ ) + { + crc = ( crc >> 1 ) ^ ( reversedPolynom & ~( ( crc & 0x01 ) - 1 ) ); + } + } + return crc; +} + +uint32_t Crc32Finalize( uint32_t crc ) +{ + return ~crc; +} diff --git a/sysdrv/tools/board/lorawan-bridge/LoraMac/utilities.h b/sysdrv/tools/board/lorawan-bridge/LoraMac/utilities.h new file mode 100644 index 0000000000..2680ab1dc5 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/LoraMac/utilities.h @@ -0,0 +1,221 @@ +/*! + * \file utilities.h + * + * \brief Helper functions implementation + * + * \copyright Revised BSD License, see section \ref LICENSE. + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013-2017 Semtech + * + * \endcode + * + * \author Alistair Jordan + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + */ +#ifndef __UTILITIES_H__ +#define __UTILITIES_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +/*! + * LMN (LoRaMac-node) status + */ +typedef enum LmnStatus_e +{ + LMN_STATUS_ERROR = 0, + LMN_STATUS_OK = !LMN_STATUS_ERROR +} LmnStatus_t; + +/*! + * \brief Returns the minimum value between a and b + * + * \param [IN] a 1st value + * \param [IN] b 2nd value + * \retval minValue Minimum value + */ +#ifndef MIN +#define MIN( a, b ) ( ( ( a ) < ( b ) ) ? ( a ) : ( b ) ) +#endif + +/*! + * \brief Returns the maximum value between a and b + * + * \param [IN] a 1st value + * \param [IN] b 2nd value + * \retval maxValue Maximum value + */ +#ifndef MAX +#define MAX( a, b ) ( ( ( a ) > ( b ) ) ? ( a ) : ( b ) ) +#endif + +/*! + * \brief Returns 2 raised to the power of n + * + * \param [IN] n power value + * \retval result of raising 2 to the power n + */ +#define POW2( n ) ( 1 << n ) + +/*! + * Version + */ +typedef union Version_u +{ + struct Version_s + { + uint8_t Revision; + uint8_t Patch; + uint8_t Minor; + uint8_t Major; + }Fields; + uint32_t Value; +}Version_t; + +/*! + * \brief Initializes the pseudo random generator initial value + * + * \param [IN] seed Pseudo random generator initial value + */ +void srand1( uint32_t seed ); + +/*! + * \brief Computes a random number between min and max + * + * \param [IN] min range minimum value + * \param [IN] max range maximum value + * \retval random random value in range min..max + */ +int32_t randr( int32_t min, int32_t max ); + +/*! + * \brief Copies size elements of src array to dst array + * + * \remark STM32 Standard memcpy function only works on pointers that are aligned + * + * \param [OUT] dst Destination array + * \param [IN] src Source array + * \param [IN] size Number of bytes to be copied + */ +void memcpy1( uint8_t *dst, const uint8_t *src, uint16_t size ); + +/*! + * \brief Copies size elements of src array to dst array reversing the byte order + * + * \param [OUT] dst Destination array + * \param [IN] src Source array + * \param [IN] size Number of bytes to be copied + */ +void memcpyr( uint8_t *dst, const uint8_t *src, uint16_t size ); + +/*! + * \brief Set size elements of dst array with value + * + * \remark STM32 Standard memset function only works on pointers that are aligned + * + * \param [OUT] dst Destination array + * \param [IN] value Default value + * \param [IN] size Number of bytes to be copied + */ +void memset1( uint8_t *dst, uint8_t value, uint16_t size ); + +/*! + * \brief Converts a nibble to an hexadecimal character + * + * \param [IN] a Nibble to be converted + * \retval hexChar Converted hexadecimal character + */ +int8_t Nibble2HexChar( uint8_t a ); + +/*! + * \brief Computes a CCITT 32 bits CRC + * + * \param [IN] buffer Data buffer used to compute the CRC + * \param [IN] length Data buffer length + * + * \retval crc The computed buffer of length CRC + */ +uint32_t Crc32( uint8_t *buffer, uint16_t length ); + +/*! + * \brief Computes the initial value of the CCITT 32 bits CRC. This function + * can be used with functions \ref Crc32Update and \ref Crc32Finalize. + * + * \retval crc Initial crc value. + */ +uint32_t Crc32Init( void ); + +/*! + * \brief Updates the value of the crc value. + * + * \param [IN] crcInit Previous or initial crc value. + * \param [IN] buffer Data pointer. + * \param [IN] length Length of the data. + * + * \retval crc Updated crc value. + */ +uint32_t Crc32Update( uint32_t crcInit, uint8_t *buffer, uint16_t length ); + +/*! + * \brief Finalizes the crc value after the calls to \ref Crc32Update. + * + * \param [IN] crc Recent crc value. + * + * \retval crc Updated crc value. + */ +uint32_t Crc32Finalize( uint32_t crc ); + +/*! + * Begins critical section + */ +#define CRITICAL_SECTION_BEGIN( ) uint32_t mask; BoardCriticalSectionBegin( &mask ) + +/*! + * Begin repeated critical section + */ +#define CRITICAL_SECTION_BEGIN_REPEAT( ) BoardCriticalSectionBegin( &mask ) + +/*! + * Ends critical section + */ +#define CRITICAL_SECTION_END( ) BoardCriticalSectionEnd( &mask ) + +/* + * ============================================================================ + * Following functions must be implemented inside the specific platform + * board.c file. + * ============================================================================ + */ +/*! + * Disable interrupts, begins critical section + * + * \param [IN] mask Pointer to a variable where to store the CPU IRQ mask + */ +void BoardCriticalSectionBegin( uint32_t *mask ); + +/*! + * Ends critical section + * + * \param [IN] mask Pointer to a variable where the CPU IRQ mask was stored + */ +void BoardCriticalSectionEnd( uint32_t *mask ); + +#ifdef __cplusplus +} +#endif + +#endif // __UTILITIES_H__ diff --git a/sysdrv/tools/board/lorawan-bridge/Makefile b/sysdrv/tools/board/lorawan-bridge/Makefile index 2db38913e4..9425aaa057 100644 --- a/sysdrv/tools/board/lorawan-bridge/Makefile +++ b/sysdrv/tools/board/lorawan-bridge/Makefile @@ -1,25 +1,44 @@ +# +# Copyright (c) 2018, 2022 CivetWeb Developers +# Copyright (c) 2013 No Face Press, LLC +# License http://opensource.org/licenses/mit-license.php MIT License +# -#ifeq ($(SYSDRV_PARAM), ) - LORAWAN_BRIDGE_PARAM:=../../../Makefile.param - include $(LORAWAN_BRIDGE_PARAM) -#endif +#This makefile is used to test the other Makefiles -export LC_ALL=C -SHELL:=/bin/bash -CURRENT_DIR := $(shell pwd) -PKG_NAME := lbr -PKG_BIN := out +PROG = rest_server +SRC = rest.c cJSON/cJSON.c cJSON/cJSON_Utils.c send_json.c lorawan_send.c b64.c/encode.c b64.c/decode.c b64.c/buffer.c +LORAWAN_SRC = LoraMac/spidev_lib/spidev_lib.c LoraMac/board.c LoraMac/main.c LoraMac/spi-board.c LoraMac/delay.c LoraMac/sx1276.c LoraMac/sx1276-board.c LoraMac/LoRaMac.c LoraMac/LoRaMacAdr.c LoraMac/LoRaMacClassB.c LoraMac/LoRaMacCommands.c LoraMac/LoRaMacConfirmQueue.c LoraMac/LoRaMacCrypto.c LoraMac/LoRaMacParser.c LoraMac/LoRaMacSerializer.c LoraMac/timer.c LoraMac/utilities.c LoraMac/systime.c +LORAMAC_COMMON_SRC = LoraMac/common/LmHandler/LmHandler.c LoraMac/common/LmHandler/packages/FragDecoder.c LoraMac/common/LmHandler/packages/LmhpClockSync.c LoraMac/common/LmHandler/packages/LmhpCompliance.c LoraMac/common/LmHandler/packages/LmhpFragmentation.c LoraMac/common/LmHandler/packages/LmhpRemoteMcastSetup.c +LORAMAC_COMMON_SRC += LoraMac/common/CayenneLpp.c LoraMac/common/LmHandlerMsgDisplay.c +LORAMAC_SOFT_SE_SRC += LoraMac/soft-se/aes.c LoraMac/soft-se/cmac.c LoraMac/soft-se/soft-se-hal.c LoraMac/soft-se/soft-se.c +LORAMAC_REGION_SRC = LoraMac/region/RegionAS923.c LoraMac/region/RegionAU915.c LoraMac/region/RegionBaseUS.c LoraMac/region/Region.c LoraMac/region/RegionCN470A20.c LoraMac/region/RegionCN470A26.c LoraMac/region/RegionCN470B20.c LoraMac/region/RegionCN470B26.c LoraMac/region/RegionCN470.c LoraMac/region/RegionCN779.c LoraMac/region/RegionCommon.c LoraMac/region/RegionEU433.c LoraMac/region/RegionEU868.c LoraMac/region/RegionIN865.c LoraMac/region/RegionKR920.c LoraMac/region/RegionRU864.c LoraMac/region/RegionUS915.c +TOP = ./civetweb +CIVETWEB_LIB = libcivetweb.a -all: - @test -f $(PKG_BIN)/usr/sbin/$(PKG_NAME)_noexist || (\ - mkdir -p $(CURRENT_DIR)/$(PKG_BIN)/usr/sbin; \ - $(SYSDRV_CROSS)-gcc -g main.c -o $(CURRENT_DIR)/$(PKG_BIN)/usr/sbin/$(PKG_NAME); \ - ) - $(call MAROC_COPY_PKG_TO_SYSDRV_OUTPUT, $(SYSDRV_DIR_OUT_ROOTFS), $(PKG_BIN)) -# $(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNEL_DIR) M=$(shell pwd) $@ -j12 +CFLAGS = -I$(TOP)/include -IcJSON $(COPT) -DNO_FILES -DMG_EXPERIMENTAL_INTERFACES -DSOFT_SE +LIBS = -lpthread +INCLUDE = -ILoraMac -ILoraMac/region -ILoraMac/common -ILoraMac/common/LmHandler -ILoraMac/common/LmHandler/packages -ILoraMac/soft-se -clean: distclean +include $(TOP)/resources/Makefile.in-os + +ifeq ($(TARGET_OS),LINUX) + LIBS += -ldl + LIBS += -lm +endif + +all: $(PROG) + +$(PROG): $(CIVETWEB_LIB) $(LORAWAN_SRC) $(LORAMAC_COMMON_SRC) $(LORAMAC_REGION_SRC) $(LORAMAC_SOFT_SE_SRC) + $(CC) -o $@ $(CFLAGS) $(INCLUDE) $(LDFLAGS) $(LORAWAN_SRC) $(LORAMAC_COMMON_SRC) $(LORAMAC_REGION_SRC) $(LORAMAC_SOFT_SE_SRC) $(CIVETWEB_LIB) $(LIBS) + +$(CIVETWEB_LIB): + $(MAKE) -C $(TOP) WITH_IPV6=1 WITH_WEBSOCKET=1 COPT='-DNO_SSL -DMG_EXPERIMENTAL_INTERFACES' clean lib + cp $(TOP)/$(CIVETWEB_LIB) . + +clean: + rm -f $(CIVETWEB_LIB) $(PROG) + +.PHONY: all clean -distclean: - -rm -rf $(PKG_NAME) $(PKG_BIN) \ No newline at end of file diff --git a/sysdrv/tools/board/lorawan-bridge/Makefile_fordevice b/sysdrv/tools/board/lorawan-bridge/Makefile_fordevice new file mode 100644 index 0000000000..2db38913e4 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/Makefile_fordevice @@ -0,0 +1,25 @@ + +#ifeq ($(SYSDRV_PARAM), ) + LORAWAN_BRIDGE_PARAM:=../../../Makefile.param + include $(LORAWAN_BRIDGE_PARAM) +#endif + +export LC_ALL=C +SHELL:=/bin/bash + +CURRENT_DIR := $(shell pwd) +PKG_NAME := lbr +PKG_BIN := out + +all: + @test -f $(PKG_BIN)/usr/sbin/$(PKG_NAME)_noexist || (\ + mkdir -p $(CURRENT_DIR)/$(PKG_BIN)/usr/sbin; \ + $(SYSDRV_CROSS)-gcc -g main.c -o $(CURRENT_DIR)/$(PKG_BIN)/usr/sbin/$(PKG_NAME); \ + ) + $(call MAROC_COPY_PKG_TO_SYSDRV_OUTPUT, $(SYSDRV_DIR_OUT_ROOTFS), $(PKG_BIN)) +# $(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNEL_DIR) M=$(shell pwd) $@ -j12 + +clean: distclean + +distclean: + -rm -rf $(PKG_NAME) $(PKG_BIN) \ No newline at end of file diff --git a/sysdrv/tools/board/lorawan-bridge/README.md b/sysdrv/tools/board/lorawan-bridge/README.md new file mode 100644 index 0000000000..eb6edf1889 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/README.md @@ -0,0 +1,119 @@ +# Lorawan Bridge Design + +## Introduction +A design for the "LoraWan Bridge". +Essentially, this is a REST API to interface with supported technologies of the Semtec sx1276 chip. + +## Rest API Design + +Currently, only LoraWan will be supported, however the API has been designed to take advantage of other features of the sx1276 in the future. + +### Lorawan Bridge API + +The current endpoints are being developed: + +#### GET /lorawan +{ + "functions": ["send","receive","mac","region"] +} + +#### PUT /lorawan/send + +_Example request_: +{ + "port": 1234 + "payload64": "SGVsbG8gV29ybGQK" +} + +_Example return_: +{ + "id": 1234 +} + +#### GET /lorawan/send_status + +_Example request:_ +/lorawan/send_status/1234 + +_Example returns:_ +_Transmit done_: +{ + "status": "done" +} +_Oversize Packet:_ +{ + "status": "oversize" +} +_In-process:_ +{ + "status": "sending" +} +_No ack:_ +{ + "status": "noack" +} + +#### GET /lorawan/receive/latest + +_Example Return:_ +_OK_ +{ + "id": 14 + "port": 1234 + "payload64": "SGVsbG8gV29ybGQK" +} +Payload64 is in base64 format. + +#### GET /lorawan/receive/17 +Returns the received data by ID of packet. +_Example Return:_ +{ + "id": 17 + "port": 1234 + "payload64": "SGVsbG8gV29ybGQyCg==" +} +Payload64 is in base64 format. + +#### GET /lorawan/mac +Returns transport layer information, note return data has been randomised :) +_Example Return:_ +{ + "class": "A" + "deviceEui": "6082E95C1E1BC609" + "appEui": "6081F9474AEA66B4" + "appKey": "5D617484FB8E396D4BEFEEBB1341B849" +} + +#### PUT /lorawan/mac +Sets transport layer information +_Example Request:_ +{ + "class": "A" + "deviceEui": "6082E95C1E1BC609" + "appEui": "6081F9474AEA66B4" + "appKey": "5D617484FB8E396D4BEFEEBB1341B849" +} + +#### GET /lorawan/region +Gets the lorawan region +_Example Return:_ +{ + "region": "EU868" +} + +#### PUT /lorawan/region +Sets the lorawan region +_Example Request:_ +{ + "region": "AS923" +} +_Example Returns:_ +_Region changed, or the same_ +{ + "status": "ok" +} +_Region not recognized_ +{ + "status": "notfound" +} + diff --git a/sysdrv/tools/board/lorawan-bridge/b64.c b/sysdrv/tools/board/lorawan-bridge/b64.c new file mode 160000 index 0000000000..99adbb07d2 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/b64.c @@ -0,0 +1 @@ +Subproject commit 99adbb07d23ea01176f0eb43a3dcef602ccd3a8e diff --git a/sysdrv/tools/board/lorawan-bridge/cJSON/LICENSE b/sysdrv/tools/board/lorawan-bridge/cJSON/LICENSE new file mode 100644 index 0000000000..78deb0406d --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/cJSON/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/sysdrv/tools/board/lorawan-bridge/cJSON/README.md b/sysdrv/tools/board/lorawan-bridge/cJSON/README.md new file mode 100644 index 0000000000..dd4a7fed2b --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/cJSON/README.md @@ -0,0 +1,4 @@ +# cJSON + +cJSON Release 1.7.5 from https://github.com/DaveGamble/cJSON + diff --git a/sysdrv/tools/board/lorawan-bridge/cJSON/cJSON.c b/sysdrv/tools/board/lorawan-bridge/cJSON/cJSON.c new file mode 100644 index 0000000000..9ecafbdd3a --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/cJSON/cJSON.c @@ -0,0 +1,2933 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/* cJSON */ +/* JSON parser in C. */ + +/* disable warnings about old C89 functions in MSVC */ +#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) +#define _CRT_SECURE_NO_DEPRECATE +#endif + +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif +#if defined(_MSC_VER) +#pragma warning (push) +/* disable warning about single line comments in system headers */ +#pragma warning (disable : 4001) +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef ENABLE_LOCALES +#include +#endif + +#if defined(_MSC_VER) +#pragma warning (pop) +#endif +#ifdef __GNUC__ +#pragma GCC visibility pop +#endif + +#include "cJSON.h" + +/* define our own boolean type */ +#define true ((cJSON_bool)1) +#define false ((cJSON_bool)0) + +typedef struct { + const unsigned char *json; + size_t position; +} error; +static error global_error = { NULL, 0 }; + +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) +{ + return (const char*) (global_error.json + global_error.position); +} + +CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item) { + if (!cJSON_IsString(item)) { + return NULL; + } + + return item->valuestring; +} + +/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ +#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 5) + #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. +#endif + +CJSON_PUBLIC(const char*) cJSON_Version(void) +{ + static char version[15]; + sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); + + return version; +} + +/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ +static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) +{ + if ((string1 == NULL) || (string2 == NULL)) + { + return 1; + } + + if (string1 == string2) + { + return 0; + } + + for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) + { + if (*string1 == '\0') + { + return 0; + } + } + + return tolower(*string1) - tolower(*string2); +} + +typedef struct internal_hooks +{ + void *(*allocate)(size_t size); + void (*deallocate)(void *pointer); + void *(*reallocate)(void *pointer, size_t size); +} internal_hooks; + +#if defined(_MSC_VER) +/* work around MSVC error C2322: '...' address of dllimport '...' is not static */ +static void *internal_malloc(size_t size) +{ + return malloc(size); +} +static void internal_free(void *pointer) +{ + free(pointer); +} +static void *internal_realloc(void *pointer, size_t size) +{ + return realloc(pointer, size); +} +#else +#define internal_malloc malloc +#define internal_free free +#define internal_realloc realloc +#endif + +static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; + +static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) +{ + size_t length = 0; + unsigned char *copy = NULL; + + if (string == NULL) + { + return NULL; + } + + length = strlen((const char*)string) + sizeof(""); + copy = (unsigned char*)hooks->allocate(length); + if (copy == NULL) + { + return NULL; + } + memcpy(copy, string, length); + + return copy; +} + +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (hooks == NULL) + { + /* Reset hooks */ + global_hooks.allocate = malloc; + global_hooks.deallocate = free; + global_hooks.reallocate = realloc; + return; + } + + global_hooks.allocate = malloc; + if (hooks->malloc_fn != NULL) + { + global_hooks.allocate = hooks->malloc_fn; + } + + global_hooks.deallocate = free; + if (hooks->free_fn != NULL) + { + global_hooks.deallocate = hooks->free_fn; + } + + /* use realloc only if both free and malloc are used */ + global_hooks.reallocate = NULL; + if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) + { + global_hooks.reallocate = realloc; + } +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item(const internal_hooks * const hooks) +{ + cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); + if (node) + { + memset(node, '\0', sizeof(cJSON)); + } + + return node; +} + +/* Delete a cJSON structure. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) +{ + cJSON *next = NULL; + while (item != NULL) + { + next = item->next; + if (!(item->type & cJSON_IsReference) && (item->child != NULL)) + { + cJSON_Delete(item->child); + } + if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) + { + global_hooks.deallocate(item->valuestring); + } + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + global_hooks.deallocate(item->string); + } + global_hooks.deallocate(item); + item = next; + } +} + +/* get the decimal point character of the current locale */ +static unsigned char get_decimal_point(void) +{ +#ifdef ENABLE_LOCALES + struct lconv *lconv = localeconv(); + return (unsigned char) lconv->decimal_point[0]; +#else + return '.'; +#endif +} + +typedef struct +{ + const unsigned char *content; + size_t length; + size_t offset; + size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ + internal_hooks hooks; +} parse_buffer; + +/* check if the given size is left to read in a given parse buffer (starting with 1) */ +#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) +/* check if the buffer can be accessed at the given index (starting with 0) */ +#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) +#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) +/* get a pointer to the buffer at the position */ +#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) + +/* Parse the input text to generate a number, and populate the result into item. */ +static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) +{ + double number = 0; + unsigned char *after_end = NULL; + unsigned char number_c_string[64]; + unsigned char decimal_point = get_decimal_point(); + size_t i = 0; + + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; + } + + /* copy the number into a temporary buffer and replace '.' with the decimal point + * of the current locale (for strtod) + * This also takes care of '\0' not necessarily being available for marking the end of the input */ + for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) + { + switch (buffer_at_offset(input_buffer)[i]) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + case 'e': + case 'E': + number_c_string[i] = buffer_at_offset(input_buffer)[i]; + break; + + case '.': + number_c_string[i] = decimal_point; + break; + + default: + goto loop_end; + } + } +loop_end: + number_c_string[i] = '\0'; + + number = strtod((const char*)number_c_string, (char**)&after_end); + if (number_c_string == after_end) + { + return false; /* parse_error */ + } + + item->valuedouble = number; + + /* use saturation in case of overflow */ + if (number >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (number <= INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)number; + } + + item->type = cJSON_Number; + + input_buffer->offset += (size_t)(after_end - number_c_string); + return true; +} + +/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) +{ + if (number >= INT_MAX) + { + object->valueint = INT_MAX; + } + else if (number <= INT_MIN) + { + object->valueint = INT_MIN; + } + else + { + object->valueint = (int)number; + } + + return object->valuedouble = number; +} + +typedef struct +{ + unsigned char *buffer; + size_t length; + size_t offset; + size_t depth; /* current nesting depth (for formatted printing) */ + cJSON_bool noalloc; + cJSON_bool format; /* is this print a formatted print */ + internal_hooks hooks; +} printbuffer; + +/* realloc printbuffer if necessary to have at least "needed" bytes more */ +static unsigned char* ensure(printbuffer * const p, size_t needed) +{ + unsigned char *newbuffer = NULL; + size_t newsize = 0; + + if ((p == NULL) || (p->buffer == NULL)) + { + return NULL; + } + + if ((p->length > 0) && (p->offset >= p->length)) + { + /* make sure that offset is valid */ + return NULL; + } + + if (needed > INT_MAX) + { + /* sizes bigger than INT_MAX are currently not supported */ + return NULL; + } + + needed += p->offset + 1; + if (needed <= p->length) + { + return p->buffer + p->offset; + } + + if (p->noalloc) { + return NULL; + } + + /* calculate new buffer size */ + if (needed > (INT_MAX / 2)) + { + /* overflow of int, use INT_MAX if possible */ + if (needed <= INT_MAX) + { + newsize = INT_MAX; + } + else + { + return NULL; + } + } + else + { + newsize = needed * 2; + } + + if (p->hooks.reallocate != NULL) + { + /* reallocate with realloc if available */ + newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); + if (newbuffer == NULL) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + } + else + { + /* otherwise reallocate manually */ + newbuffer = (unsigned char*)p->hooks.allocate(newsize); + if (!newbuffer) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + if (newbuffer) + { + memcpy(newbuffer, p->buffer, p->offset + 1); + } + p->hooks.deallocate(p->buffer); + } + p->length = newsize; + p->buffer = newbuffer; + + return newbuffer + p->offset; +} + +/* calculate the new length of the string in a printbuffer and update the offset */ +static void update_offset(printbuffer * const buffer) +{ + const unsigned char *buffer_pointer = NULL; + if ((buffer == NULL) || (buffer->buffer == NULL)) + { + return; + } + buffer_pointer = buffer->buffer + buffer->offset; + + buffer->offset += strlen((const char*)buffer_pointer); +} + +/* Render the number nicely from the given item into a string. */ +static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + double d = item->valuedouble; + int length = 0; + size_t i = 0; + unsigned char number_buffer[26]; /* temporary buffer to print the number into */ + unsigned char decimal_point = get_decimal_point(); + double test; + + if (output_buffer == NULL) + { + return false; + } + + /* This checks for NaN and Infinity */ + if ((d * 0) != 0) + { + length = sprintf((char*)number_buffer, "null"); + } + else + { + /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ + length = sprintf((char*)number_buffer, "%1.15g", d); + + /* Check whether the original double can be recovered */ + if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || ((double)test != d)) + { + /* If not, print with 17 decimal places of precision */ + length = sprintf((char*)number_buffer, "%1.17g", d); + } + } + + /* sprintf failed or buffer overrun occurred */ + if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) + { + return false; + } + + /* reserve appropriate space in the output */ + output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); + if (output_pointer == NULL) + { + return false; + } + + /* copy the printed number to the output and replace locale + * dependent decimal point with '.' */ + for (i = 0; i < ((size_t)length); i++) + { + if (number_buffer[i] == decimal_point) + { + output_pointer[i] = '.'; + continue; + } + + output_pointer[i] = number_buffer[i]; + } + output_pointer[i] = '\0'; + + output_buffer->offset += (size_t)length; + + return true; +} + +/* parse 4 digit hexadecimal number */ +static unsigned parse_hex4(const unsigned char * const input) +{ + unsigned int h = 0; + size_t i = 0; + + for (i = 0; i < 4; i++) + { + /* parse digit */ + if ((input[i] >= '0') && (input[i] <= '9')) + { + h += (unsigned int) input[i] - '0'; + } + else if ((input[i] >= 'A') && (input[i] <= 'F')) + { + h += (unsigned int) 10 + input[i] - 'A'; + } + else if ((input[i] >= 'a') && (input[i] <= 'f')) + { + h += (unsigned int) 10 + input[i] - 'a'; + } + else /* invalid */ + { + return 0; + } + + if (i < 3) + { + /* shift left to make place for the next nibble */ + h = h << 4; + } + } + + return h; +} + +/* converts a UTF-16 literal to UTF-8 + * A literal can be one or two sequences of the form \uXXXX */ +static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) +{ + long unsigned int codepoint = 0; + unsigned int first_code = 0; + const unsigned char *first_sequence = input_pointer; + unsigned char utf8_length = 0; + unsigned char utf8_position = 0; + unsigned char sequence_length = 0; + unsigned char first_byte_mark = 0; + + if ((input_end - first_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + /* get the first utf16 sequence */ + first_code = parse_hex4(first_sequence + 2); + + /* check that the code is valid */ + if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) + { + goto fail; + } + + /* UTF16 surrogate pair */ + if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) + { + const unsigned char *second_sequence = first_sequence + 6; + unsigned int second_code = 0; + sequence_length = 12; /* \uXXXX\uXXXX */ + + if ((input_end - second_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) + { + /* missing second half of the surrogate pair */ + goto fail; + } + + /* get the second utf16 sequence */ + second_code = parse_hex4(second_sequence + 2); + /* check that the code is valid */ + if ((second_code < 0xDC00) || (second_code > 0xDFFF)) + { + /* invalid second half of the surrogate pair */ + goto fail; + } + + + /* calculate the unicode codepoint from the surrogate pair */ + codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); + } + else + { + sequence_length = 6; /* \uXXXX */ + codepoint = first_code; + } + + /* encode as UTF-8 + * takes at maximum 4 bytes to encode: + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (codepoint < 0x80) + { + /* normal ascii, encoding 0xxxxxxx */ + utf8_length = 1; + } + else if (codepoint < 0x800) + { + /* two bytes, encoding 110xxxxx 10xxxxxx */ + utf8_length = 2; + first_byte_mark = 0xC0; /* 11000000 */ + } + else if (codepoint < 0x10000) + { + /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ + utf8_length = 3; + first_byte_mark = 0xE0; /* 11100000 */ + } + else if (codepoint <= 0x10FFFF) + { + /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + utf8_length = 4; + first_byte_mark = 0xF0; /* 11110000 */ + } + else + { + /* invalid unicode codepoint */ + goto fail; + } + + /* encode as utf8 */ + for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) + { + /* 10xxxxxx */ + (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); + codepoint >>= 6; + } + /* encode first byte */ + if (utf8_length > 1) + { + (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); + } + else + { + (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); + } + + *output_pointer += utf8_length; + + return sequence_length; + +fail: + return 0; +} + +/* Parse the input text into an unescaped cinput, and populate item. */ +static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) +{ + const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; + const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; + unsigned char *output_pointer = NULL; + unsigned char *output = NULL; + + /* not a string */ + if (buffer_at_offset(input_buffer)[0] != '\"') + { + goto fail; + } + + { + /* calculate approximate size of the output (overestimate) */ + size_t allocation_length = 0; + size_t skipped_bytes = 0; + while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) + { + /* is escape sequence */ + if (input_end[0] == '\\') + { + if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) + { + /* prevent buffer overflow when last input character is a backslash */ + goto fail; + } + skipped_bytes++; + input_end++; + } + input_end++; + } + if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) + { + goto fail; /* string ended unexpectedly */ + } + + /* This is at most how much we need for the output */ + allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; + output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); + if (output == NULL) + { + goto fail; /* allocation failure */ + } + } + + output_pointer = output; + /* loop through the string literal */ + while (input_pointer < input_end) + { + if (*input_pointer != '\\') + { + *output_pointer++ = *input_pointer++; + } + /* escape sequence */ + else + { + unsigned char sequence_length = 2; + if ((input_end - input_pointer) < 1) + { + goto fail; + } + + switch (input_pointer[1]) + { + case 'b': + *output_pointer++ = '\b'; + break; + case 'f': + *output_pointer++ = '\f'; + break; + case 'n': + *output_pointer++ = '\n'; + break; + case 'r': + *output_pointer++ = '\r'; + break; + case 't': + *output_pointer++ = '\t'; + break; + case '\"': + case '\\': + case '/': + *output_pointer++ = input_pointer[1]; + break; + + /* UTF-16 literal */ + case 'u': + sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); + if (sequence_length == 0) + { + /* failed to convert UTF16-literal to UTF-8 */ + goto fail; + } + break; + + default: + goto fail; + } + input_pointer += sequence_length; + } + } + + /* zero terminate the output */ + *output_pointer = '\0'; + + item->type = cJSON_String; + item->valuestring = (char*)output; + + input_buffer->offset = (size_t) (input_end - input_buffer->content); + input_buffer->offset++; + + return true; + +fail: + if (output != NULL) + { + input_buffer->hooks.deallocate(output); + } + + if (input_pointer != NULL) + { + input_buffer->offset = (size_t)(input_pointer - input_buffer->content); + } + + return false; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) +{ + const unsigned char *input_pointer = NULL; + unsigned char *output = NULL; + unsigned char *output_pointer = NULL; + size_t output_length = 0; + /* numbers of additional characters needed for escaping */ + size_t escape_characters = 0; + + if (output_buffer == NULL) + { + return false; + } + + /* empty string */ + if (input == NULL) + { + output = ensure(output_buffer, sizeof("\"\"")); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "\"\""); + + return true; + } + + /* set "flag" to 1 if something needs to be escaped */ + for (input_pointer = input; *input_pointer; input_pointer++) + { + switch (*input_pointer) + { + case '\"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + /* one character escape sequence */ + escape_characters++; + break; + default: + if (*input_pointer < 32) + { + /* UTF-16 escape sequence uXXXX */ + escape_characters += 5; + } + break; + } + } + output_length = (size_t)(input_pointer - input) + escape_characters; + + output = ensure(output_buffer, output_length + sizeof("\"\"")); + if (output == NULL) + { + return false; + } + + /* no characters have to be escaped */ + if (escape_characters == 0) + { + output[0] = '\"'; + memcpy(output + 1, input, output_length); + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; + } + + output[0] = '\"'; + output_pointer = output + 1; + /* copy the string */ + for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) + { + if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) + { + /* normal character, copy */ + *output_pointer = *input_pointer; + } + else + { + /* character needs to be escaped */ + *output_pointer++ = '\\'; + switch (*input_pointer) + { + case '\\': + *output_pointer = '\\'; + break; + case '\"': + *output_pointer = '\"'; + break; + case '\b': + *output_pointer = 'b'; + break; + case '\f': + *output_pointer = 'f'; + break; + case '\n': + *output_pointer = 'n'; + break; + case '\r': + *output_pointer = 'r'; + break; + case '\t': + *output_pointer = 't'; + break; + default: + /* escape and print as unicode codepoint */ + sprintf((char*)output_pointer, "u%04x", *input_pointer); + output_pointer += 4; + break; + } + } + } + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; +} + +/* Invoke print_string_ptr (which is useful) on an item. */ +static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) +{ + return print_string_ptr((unsigned char*)item->valuestring, p); +} + +/* Predeclare these prototypes. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); + +/* Utility to jump whitespace and cr/lf */ +static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL)) + { + return NULL; + } + + while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) + { + buffer->offset++; + } + + if (buffer->offset == buffer->length) + { + buffer->offset--; + } + + return buffer; +} + +/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ +static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) + { + return NULL; + } + + if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) + { + buffer->offset += 3; + } + + return buffer; +} + +/* Parse an object - create a new root, and populate. */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + cJSON *item = NULL; + + /* reset error position */ + global_error.json = NULL; + global_error.position = 0; + + if (value == NULL) + { + goto fail; + } + + buffer.content = (const unsigned char*)value; + buffer.length = strlen((const char*)value) + sizeof(""); + buffer.offset = 0; + buffer.hooks = global_hooks; + + item = cJSON_New_Item(&global_hooks); + if (item == NULL) /* memory fail */ + { + goto fail; + } + + if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) + { + /* parse failure. ep is set. */ + goto fail; + } + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if (require_null_terminated) + { + buffer_skip_whitespace(&buffer); + if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') + { + goto fail; + } + } + if (return_parse_end) + { + *return_parse_end = (const char*)buffer_at_offset(&buffer); + } + + return item; + +fail: + if (item != NULL) + { + cJSON_Delete(item); + } + + if (value != NULL) + { + error local_error; + local_error.json = (const unsigned char*)value; + local_error.position = 0; + + if (buffer.offset < buffer.length) + { + local_error.position = buffer.offset; + } + else if (buffer.length > 0) + { + local_error.position = buffer.length - 1; + } + + if (return_parse_end != NULL) + { + *return_parse_end = (const char*)local_error.json + local_error.position; + } + + global_error = local_error; + } + + return NULL; +} + +/* Default options for cJSON_Parse */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) +{ + return cJSON_ParseWithOpts(value, 0, 0); +} + +#define cjson_min(a, b) ((a < b) ? a : b) + +static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) +{ + static const size_t default_buffer_size = 256; + printbuffer buffer[1]; + unsigned char *printed = NULL; + + memset(buffer, 0, sizeof(buffer)); + + /* create buffer */ + buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); + buffer->length = default_buffer_size; + buffer->format = format; + buffer->hooks = *hooks; + if (buffer->buffer == NULL) + { + goto fail; + } + + /* print the value */ + if (!print_value(item, buffer)) + { + goto fail; + } + update_offset(buffer); + + /* check if reallocate is available */ + if (hooks->reallocate != NULL) + { + printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); + buffer->buffer = NULL; + if (printed == NULL) { + goto fail; + } + } + else /* otherwise copy the JSON over to a new buffer */ + { + printed = (unsigned char*) hooks->allocate(buffer->offset + 1); + if (printed == NULL) + { + goto fail; + } + memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); + printed[buffer->offset] = '\0'; /* just to be sure */ + + /* free the buffer */ + hooks->deallocate(buffer->buffer); + } + + return printed; + +fail: + if (buffer->buffer != NULL) + { + hooks->deallocate(buffer->buffer); + } + + if (printed != NULL) + { + hooks->deallocate(printed); + } + + return NULL; +} + +/* Render a cJSON item/entity/structure to text. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) +{ + return (char*)print(item, true, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) +{ + return (char*)print(item, false, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if (prebuffer < 0) + { + return NULL; + } + + p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); + if (!p.buffer) + { + return NULL; + } + + p.length = (size_t)prebuffer; + p.offset = 0; + p.noalloc = false; + p.format = fmt; + p.hooks = global_hooks; + + if (!print_value(item, &p)) + { + global_hooks.deallocate(p.buffer); + return NULL; + } + + return (char*)p.buffer; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if ((len < 0) || (buf == NULL)) + { + return false; + } + + p.buffer = (unsigned char*)buf; + p.length = (size_t)len; + p.offset = 0; + p.noalloc = true; + p.format = fmt; + p.hooks = global_hooks; + + return print_value(item, &p); +} + +/* Parser core - when encountering text, process appropriately. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) +{ + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; /* no input */ + } + + /* parse the different types of values */ + /* null */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) + { + item->type = cJSON_NULL; + input_buffer->offset += 4; + return true; + } + /* false */ + if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) + { + item->type = cJSON_False; + input_buffer->offset += 5; + return true; + } + /* true */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) + { + item->type = cJSON_True; + item->valueint = 1; + input_buffer->offset += 4; + return true; + } + /* string */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) + { + return parse_string(item, input_buffer); + } + /* number */ + if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) + { + return parse_number(item, input_buffer); + } + /* array */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) + { + return parse_array(item, input_buffer); + } + /* object */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) + { + return parse_object(item, input_buffer); + } + + return false; +} + +/* Render a value to text. */ +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output = NULL; + + if ((item == NULL) || (output_buffer == NULL)) + { + return false; + } + + switch ((item->type) & 0xFF) + { + case cJSON_NULL: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "null"); + return true; + + case cJSON_False: + output = ensure(output_buffer, 6); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "false"); + return true; + + case cJSON_True: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "true"); + return true; + + case cJSON_Number: + return print_number(item, output_buffer); + + case cJSON_Raw: + { + size_t raw_length = 0; + if (item->valuestring == NULL) + { + return false; + } + + raw_length = strlen(item->valuestring) + sizeof(""); + output = ensure(output_buffer, raw_length); + if (output == NULL) + { + return false; + } + memcpy(output, item->valuestring, raw_length); + return true; + } + + case cJSON_String: + return print_string(item, output_buffer); + + case cJSON_Array: + return print_array(item, output_buffer); + + case cJSON_Object: + return print_object(item, output_buffer); + + default: + return false; + } +} + +/* Build an array from input text. */ +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* head of the linked list */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (buffer_at_offset(input_buffer)[0] != '[') + { + /* not an array */ + goto fail; + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) + { + /* empty array */ + goto success; + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse next value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') + { + goto fail; /* expected end of array */ + } + +success: + input_buffer->depth--; + + item->type = cJSON_Array; + item->child = head; + + input_buffer->offset++; + + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an array to text */ +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_element = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output array. */ + /* opening square bracket */ + output_pointer = ensure(output_buffer, 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer = '['; + output_buffer->offset++; + output_buffer->depth++; + + while (current_element != NULL) + { + if (!print_value(current_element, output_buffer)) + { + return false; + } + update_offset(output_buffer); + if (current_element->next) + { + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ','; + if(output_buffer->format) + { + *output_pointer++ = ' '; + } + *output_pointer = '\0'; + output_buffer->offset += length; + } + current_element = current_element->next; + } + + output_pointer = ensure(output_buffer, 2); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ']'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Build an object from the text. */ +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* linked list head */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) + { + goto fail; /* not an object */ + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) + { + goto success; /* empty object */ + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse the name of the child */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_string(current_item, input_buffer)) + { + goto fail; /* failed to parse name */ + } + buffer_skip_whitespace(input_buffer); + + /* swap valuestring and string, because we parsed the name */ + current_item->string = current_item->valuestring; + current_item->valuestring = NULL; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) + { + goto fail; /* invalid object */ + } + + /* parse the value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) + { + goto fail; /* expected end of object */ + } + +success: + input_buffer->depth--; + + item->type = cJSON_Object; + item->child = head; + + input_buffer->offset++; + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an object to text. */ +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_item = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output: */ + length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer++ = '{'; + output_buffer->depth++; + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + output_buffer->offset += length; + + while (current_item) + { + if (output_buffer->format) + { + size_t i; + output_pointer = ensure(output_buffer, output_buffer->depth); + if (output_pointer == NULL) + { + return false; + } + for (i = 0; i < output_buffer->depth; i++) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += output_buffer->depth; + } + + /* print key */ + if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ':'; + if (output_buffer->format) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += length; + + /* print value */ + if (!print_value(current_item, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + /* print comma if not last */ + length = (size_t) ((output_buffer->format ? 1 : 0) + (current_item->next ? 1 : 0)); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + if (current_item->next) + { + *output_pointer++ = ','; + } + + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + *output_pointer = '\0'; + output_buffer->offset += length; + + current_item = current_item->next; + } + + output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); + if (output_pointer == NULL) + { + return false; + } + if (output_buffer->format) + { + size_t i; + for (i = 0; i < (output_buffer->depth - 1); i++) + { + *output_pointer++ = '\t'; + } + } + *output_pointer++ = '}'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Get Array size/item / object item. */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) +{ + cJSON *child = NULL; + size_t size = 0; + + if (array == NULL) + { + return 0; + } + + child = array->child; + + while(child != NULL) + { + size++; + child = child->next; + } + + /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ + + return (int)size; +} + +static cJSON* get_array_item(const cJSON *array, size_t index) +{ + cJSON *current_child = NULL; + + if (array == NULL) + { + return NULL; + } + + current_child = array->child; + while ((current_child != NULL) && (index > 0)) + { + index--; + current_child = current_child->next; + } + + return current_child; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) +{ + if (index < 0) + { + return NULL; + } + + return get_array_item(array, (size_t)index); +} + +static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) +{ + cJSON *current_element = NULL; + + if ((object == NULL) || (name == NULL)) + { + return NULL; + } + + current_element = object->child; + if (case_sensitive) + { + while ((current_element != NULL) && (strcmp(name, current_element->string) != 0)) + { + current_element = current_element->next; + } + } + else + { + while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) + { + current_element = current_element->next; + } + } + + return current_element; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, false); +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) +{ + return cJSON_GetObjectItem(object, string) ? 1 : 0; +} + +/* Utility for array list handling. */ +static void suffix_object(cJSON *prev, cJSON *item) +{ + prev->next = item; + item->prev = prev; +} + +/* Utility for handling references. */ +static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) +{ + cJSON *reference = NULL; + if (item == NULL) + { + return NULL; + } + + reference = cJSON_New_Item(hooks); + if (reference == NULL) + { + return NULL; + } + + memcpy(reference, item, sizeof(cJSON)); + reference->string = NULL; + reference->type |= cJSON_IsReference; + reference->next = reference->prev = NULL; + return reference; +} + +static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) +{ + cJSON *child = NULL; + + if ((item == NULL) || (array == NULL)) + { + return false; + } + + child = array->child; + + if (child == NULL) + { + /* list is empty, start new one */ + array->child = item; + } + else + { + /* append to the end */ + while (child->next) + { + child = child->next; + } + suffix_object(child, item); + } + + return true; +} + +/* Add item to array/object. */ +CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item) +{ + add_item_to_array(array, item); +} + +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic push +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif +/* helper function to cast away const */ +static void* cast_away_const(const void* string) +{ + return (void*)string; +} +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic pop +#endif + + +static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key) +{ + char *new_key = NULL; + int new_type = cJSON_Invalid; + + if ((object == NULL) || (string == NULL) || (item == NULL)) + { + return false; + } + + if (constant_key) + { + new_key = (char*)cast_away_const(string); + new_type = item->type | cJSON_StringIsConst; + } + else + { + new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); + if (new_key == NULL) + { + return false; + } + + new_type = item->type & ~cJSON_StringIsConst; + } + + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + hooks->deallocate(item->string); + } + + item->string = new_key; + item->type = new_type; + + return add_item_to_array(object, item); +} + +CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +{ + add_item_to_object(object, string, item, &global_hooks, false); +} + +/* Add an item to an object with constant string as key */ +CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) +{ + add_item_to_object(object, string, item, &global_hooks, true); +} + +CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +{ + if (array == NULL) + { + return; + } + + add_item_to_array(array, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) +{ + if ((object == NULL) || (string == NULL)) + { + return; + } + + add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) +{ + cJSON *null = cJSON_CreateNull(); + if (add_item_to_object(object, name, null, &global_hooks, false)) + { + return null; + } + + cJSON_Delete(null); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) +{ + cJSON *true_item = cJSON_CreateTrue(); + if (add_item_to_object(object, name, true_item, &global_hooks, false)) + { + return true_item; + } + + cJSON_Delete(true_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) +{ + cJSON *false_item = cJSON_CreateFalse(); + if (add_item_to_object(object, name, false_item, &global_hooks, false)) + { + return false_item; + } + + cJSON_Delete(false_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) +{ + cJSON *bool_item = cJSON_CreateBool(boolean); + if (add_item_to_object(object, name, bool_item, &global_hooks, false)) + { + return bool_item; + } + + cJSON_Delete(bool_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) +{ + cJSON *number_item = cJSON_CreateNumber(number); + if (add_item_to_object(object, name, number_item, &global_hooks, false)) + { + return number_item; + } + + cJSON_Delete(number_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) +{ + cJSON *string_item = cJSON_CreateString(string); + if (add_item_to_object(object, name, string_item, &global_hooks, false)) + { + return string_item; + } + + cJSON_Delete(string_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) +{ + cJSON *raw_item = cJSON_CreateRaw(raw); + if (add_item_to_object(object, name, raw_item, &global_hooks, false)) + { + return raw_item; + } + + cJSON_Delete(raw_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) +{ + cJSON *object_item = cJSON_CreateObject(); + if (add_item_to_object(object, name, object_item, &global_hooks, false)) + { + return object_item; + } + + cJSON_Delete(object_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) +{ + cJSON *array = cJSON_CreateArray(); + if (add_item_to_object(object, name, array, &global_hooks, false)) + { + return array; + } + + cJSON_Delete(array); + return NULL; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) +{ + if ((parent == NULL) || (item == NULL)) + { + return NULL; + } + + if (item->prev != NULL) + { + /* not the first element */ + item->prev->next = item->next; + } + if (item->next != NULL) + { + /* not the last element */ + item->next->prev = item->prev; + } + + if (item == parent->child) + { + /* first element */ + parent->child = item->next; + } + /* make sure the detached item doesn't point anywhere anymore */ + item->prev = NULL; + item->next = NULL; + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) +{ + if (which < 0) + { + return NULL; + } + + return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) +{ + cJSON_Delete(cJSON_DetachItemFromArray(array, which)); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItem(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObject(object, string)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); +} + +/* Replace array/object items with new ones. */ +CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) +{ + cJSON *after_inserted = NULL; + + if (which < 0) + { + return; + } + + after_inserted = get_array_item(array, (size_t)which); + if (after_inserted == NULL) + { + add_item_to_array(array, newitem); + return; + } + + newitem->next = after_inserted; + newitem->prev = after_inserted->prev; + after_inserted->prev = newitem; + if (after_inserted == array->child) + { + array->child = newitem; + } + else + { + newitem->prev->next = newitem; + } +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) +{ + if ((parent == NULL) || (replacement == NULL) || (item == NULL)) + { + return false; + } + + if (replacement == item) + { + return true; + } + + replacement->next = item->next; + replacement->prev = item->prev; + + if (replacement->next != NULL) + { + replacement->next->prev = replacement; + } + if (replacement->prev != NULL) + { + replacement->prev->next = replacement; + } + if (parent->child == item) + { + parent->child = replacement; + } + + item->next = NULL; + item->prev = NULL; + cJSON_Delete(item); + + return true; +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) +{ + if (which < 0) + { + return; + } + + cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); +} + +static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) +{ + if ((replacement == NULL) || (string == NULL)) + { + return false; + } + + /* replace the name in the replacement */ + if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) + { + cJSON_free(replacement->string); + } + replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + replacement->type &= ~cJSON_StringIsConst; + + cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); + + return true; +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) +{ + replace_item_in_object(object, string, newitem, false); +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) +{ + replace_item_in_object(object, string, newitem, true); +} + +/* Create basic types: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_NULL; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_True; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool b) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = b ? cJSON_True : cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Number; + item->valuedouble = num; + + /* use saturation in case of overflow */ + if (num >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (num <= INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)num; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_String; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) + { + item->type = cJSON_String | cJSON_IsReference; + item->valuestring = (char*)cast_away_const(string); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Object | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Array | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Raw; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type=cJSON_Array; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) + { + item->type = cJSON_Object; + } + + return item; +} + +/* Create Arrays: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if (!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber((double)numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0;a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (strings == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for (i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateString(strings[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p,n); + } + p = n; + } + + return a; +} + +/* Duplication */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) +{ + cJSON *newitem = NULL; + cJSON *child = NULL; + cJSON *next = NULL; + cJSON *newchild = NULL; + + /* Bail on bad ptr */ + if (!item) + { + goto fail; + } + /* Create new item */ + newitem = cJSON_New_Item(&global_hooks); + if (!newitem) + { + goto fail; + } + /* Copy over all vars */ + newitem->type = item->type & (~cJSON_IsReference); + newitem->valueint = item->valueint; + newitem->valuedouble = item->valuedouble; + if (item->valuestring) + { + newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); + if (!newitem->valuestring) + { + goto fail; + } + } + if (item->string) + { + newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); + if (!newitem->string) + { + goto fail; + } + } + /* If non-recursive, then we're done! */ + if (!recurse) + { + return newitem; + } + /* Walk the ->next chain for the child. */ + child = item->child; + while (child != NULL) + { + newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ + if (!newchild) + { + goto fail; + } + if (next != NULL) + { + /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + next->next = newchild; + newchild->prev = next; + next = newchild; + } + else + { + /* Set newitem->child and move to it */ + newitem->child = newchild; + next = newchild; + } + child = child->next; + } + + return newitem; + +fail: + if (newitem != NULL) + { + cJSON_Delete(newitem); + } + + return NULL; +} + +CJSON_PUBLIC(void) cJSON_Minify(char *json) +{ + unsigned char *into = (unsigned char*)json; + + if (json == NULL) + { + return; + } + + while (*json) + { + if (*json == ' ') + { + json++; + } + else if (*json == '\t') + { + /* Whitespace characters. */ + json++; + } + else if (*json == '\r') + { + json++; + } + else if (*json=='\n') + { + json++; + } + else if ((*json == '/') && (json[1] == '/')) + { + /* double-slash comments, to end of line. */ + while (*json && (*json != '\n')) + { + json++; + } + } + else if ((*json == '/') && (json[1] == '*')) + { + /* multiline comments. */ + while (*json && !((*json == '*') && (json[1] == '/'))) + { + json++; + } + json += 2; + } + else if (*json == '\"') + { + /* string literals, which are \" sensitive. */ + *into++ = (unsigned char)*json++; + while (*json && (*json != '\"')) + { + if (*json == '\\') + { + *into++ = (unsigned char)*json++; + } + *into++ = (unsigned char)*json++; + } + *into++ = (unsigned char)*json++; + } + else + { + /* All other characters. */ + *into++ = (unsigned char)*json++; + } + } + + /* and null-terminate. */ + *into = '\0'; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Invalid; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_False; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xff) == cJSON_True; +} + + +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & (cJSON_True | cJSON_False)) != 0; +} +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_NULL; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Number; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_String; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Array; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Object; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Raw; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) +{ + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a)) + { + return false; + } + + /* check if type is valid */ + switch (a->type & 0xFF) + { + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + case cJSON_Number: + case cJSON_String: + case cJSON_Raw: + case cJSON_Array: + case cJSON_Object: + break; + + default: + return false; + } + + /* identical objects are equal */ + if (a == b) + { + return true; + } + + switch (a->type & 0xFF) + { + /* in these cases and equal type is enough */ + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + return true; + + case cJSON_Number: + if (a->valuedouble == b->valuedouble) + { + return true; + } + return false; + + case cJSON_String: + case cJSON_Raw: + if ((a->valuestring == NULL) || (b->valuestring == NULL)) + { + return false; + } + if (strcmp(a->valuestring, b->valuestring) == 0) + { + return true; + } + + return false; + + case cJSON_Array: + { + cJSON *a_element = a->child; + cJSON *b_element = b->child; + + for (; (a_element != NULL) && (b_element != NULL);) + { + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + + a_element = a_element->next; + b_element = b_element->next; + } + + /* one of the arrays is longer than the other */ + if (a_element != b_element) { + return false; + } + + return true; + } + + case cJSON_Object: + { + cJSON *a_element = NULL; + cJSON *b_element = NULL; + cJSON_ArrayForEach(a_element, a) + { + /* TODO This has O(n^2) runtime, which is horrible! */ + b_element = get_object_item(b, a_element->string, case_sensitive); + if (b_element == NULL) + { + return false; + } + + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + } + + /* doing this twice, once on a and b to prevent true comparison if a subset of b + * TODO: Do this the proper way, this is just a fix for now */ + cJSON_ArrayForEach(b_element, b) + { + a_element = get_object_item(a, b_element->string, case_sensitive); + if (a_element == NULL) + { + return false; + } + + if (!cJSON_Compare(b_element, a_element, case_sensitive)) + { + return false; + } + } + + return true; + } + + default: + return false; + } +} + +CJSON_PUBLIC(void *) cJSON_malloc(size_t size) +{ + return global_hooks.allocate(size); +} + +CJSON_PUBLIC(void) cJSON_free(void *object) +{ + global_hooks.deallocate(object); +} diff --git a/sysdrv/tools/board/lorawan-bridge/cJSON/cJSON.h b/sysdrv/tools/board/lorawan-bridge/cJSON/cJSON.h new file mode 100644 index 0000000000..49fd67b72a --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/cJSON/cJSON.h @@ -0,0 +1,277 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef cJSON__h +#define cJSON__h + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* project version */ +#define CJSON_VERSION_MAJOR 1 +#define CJSON_VERSION_MINOR 7 +#define CJSON_VERSION_PATCH 5 + +#include + +/* cJSON Types: */ +#define cJSON_Invalid (0) +#define cJSON_False (1 << 0) +#define cJSON_True (1 << 1) +#define cJSON_NULL (1 << 2) +#define cJSON_Number (1 << 3) +#define cJSON_String (1 << 4) +#define cJSON_Array (1 << 5) +#define cJSON_Object (1 << 6) +#define cJSON_Raw (1 << 7) /* raw json */ + +#define cJSON_IsReference 256 +#define cJSON_StringIsConst 512 + +/* The cJSON structure: */ +typedef struct cJSON +{ + /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *next; + struct cJSON *prev; + /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + struct cJSON *child; + + /* The type of the item, as above. */ + int type; + + /* The item's string, if type==cJSON_String and type == cJSON_Raw */ + char *valuestring; + /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ + int valueint; + /* The item's number, if type==cJSON_Number */ + double valuedouble; + + /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ + char *string; +} cJSON; + +typedef struct cJSON_Hooks +{ + void *(*malloc_fn)(size_t sz); + void (*free_fn)(void *ptr); +} cJSON_Hooks; + +typedef int cJSON_bool; + +#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) +#define __WINDOWS__ +#endif +#ifdef __WINDOWS__ + +/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 2 define options: + +CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols +CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) +CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol + +For *nix builds that support visibility attribute, you can define similar behavior by + +setting default visibility to hidden by adding +-fvisibility=hidden (for gcc) +or +-xldscope=hidden (for sun cc) +to CFLAGS + +then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does + +*/ + +/* export symbols by default, this is necessary for copy pasting the C and header file */ +#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_EXPORT_SYMBOLS +#endif + +#if defined(CJSON_HIDE_SYMBOLS) +#define CJSON_PUBLIC(type) type __stdcall +#elif defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllexport) type __stdcall +#elif defined(CJSON_IMPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllimport) type __stdcall +#endif +#else /* !WIN32 */ +#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) +#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type +#else +#define CJSON_PUBLIC(type) type +#endif +#endif + +/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_NESTING_LIMIT +#define CJSON_NESTING_LIMIT 1000 +#endif + +/* returns the version of cJSON as a string */ +CJSON_PUBLIC(const char*) cJSON_Version(void); + +/* Supply malloc, realloc and free functions to cJSON */ +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); + +/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); + +/* Render a cJSON entity to text for transfer/storage. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. */ +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); +/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); +/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ +/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); +/* Delete a cJSON entity and all subentities. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *c); + +/* Returns the number of items in an array (or object). */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); +/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); +/* Get item "string" from object. Case insensitive. */ +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); + +/* Check if the item is a string and return its valuestring */ +CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item); + +/* These functions check the type of an item */ +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); + +/* These calls create a cJSON item of the appropriate type. */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); +/* raw json */ +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); + +/* Create a string where valuestring references a string so + * it will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); +/* Create an object/array that only references it's elements so + * they will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); + +/* These utilities create an Array of count items. */ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count); + +/* Append item to the specified array/object. */ +CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); +/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. + * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before + * writing to `item->string` */ +CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); + +/* Remove/Detach items from Arrays/Objects. */ +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); + +/* Update array items. */ +CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); +CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); +CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); +CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); + +/* Duplicate a cJSON item */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will +need to be released. With recurse!=0, it will duplicate any children connected to the item. +The item->next and ->prev pointers are always zero on return from Duplicate. */ +/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. + * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); + + +CJSON_PUBLIC(void) cJSON_Minify(char *json); + +/* Helper functions for creating and adding items to an object at the same time. + * They return the added item or NULL on failure. */ +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) +/* helper for the cJSON_SetNumberValue macro */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); +#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) + +/* Macro for iterating over an array or object */ +#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) + +/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ +CJSON_PUBLIC(void *) cJSON_malloc(size_t size); +CJSON_PUBLIC(void) cJSON_free(void *object); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/sysdrv/tools/board/lorawan-bridge/cJSON/cJSON_Utils.c b/sysdrv/tools/board/lorawan-bridge/cJSON/cJSON_Utils.c new file mode 100644 index 0000000000..0d199f8830 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/cJSON/cJSON_Utils.c @@ -0,0 +1,1449 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/* disable warnings about old C89 functions in MSVC */ +#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) +#define _CRT_SECURE_NO_DEPRECATE +#endif + +#ifdef __GNUCC__ +#pragma GCC visibility push(default) +#endif +#if defined(_MSC_VER) +#pragma warning (push) +/* disable warning about single line comments in system headers */ +#pragma warning (disable : 4001) +#endif + +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +#pragma warning (pop) +#endif +#ifdef __GNUCC__ +#pragma GCC visibility pop +#endif + +#include "cJSON_Utils.h" + +/* define our own boolean type */ +#define true ((cJSON_bool)1) +#define false ((cJSON_bool)0) + +static unsigned char* cJSONUtils_strdup(const unsigned char* const string) +{ + size_t length = 0; + unsigned char *copy = NULL; + + length = strlen((const char*)string) + sizeof(""); + copy = (unsigned char*) cJSON_malloc(length); + if (copy == NULL) + { + return NULL; + } + memcpy(copy, string, length); + + return copy; +} + +/* string comparison which doesn't consider NULL pointers equal */ +static int compare_strings(const unsigned char *string1, const unsigned char *string2, const cJSON_bool case_sensitive) +{ + if ((string1 == NULL) || (string2 == NULL)) + { + return 1; + } + + if (string1 == string2) + { + return 0; + } + + if (case_sensitive) + { + return strcmp((const char*)string1, (const char*)string2); + } + + for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) + { + if (*string1 == '\0') + { + return 0; + } + } + + return tolower(*string1) - tolower(*string2); +} + +/* Compare the next path element of two JSON pointers, two NULL pointers are considered unequal: */ +static cJSON_bool compare_pointers(const unsigned char *name, const unsigned char *pointer, const cJSON_bool case_sensitive) +{ + if ((name == NULL) || (pointer == NULL)) + { + return false; + } + + for (; (*name != '\0') && (*pointer != '\0') && (*pointer != '/'); (void)name++, pointer++) /* compare until next '/' */ + { + if (*pointer == '~') + { + /* check for escaped '~' (~0) and '/' (~1) */ + if (((pointer[1] != '0') || (*name != '~')) && ((pointer[1] != '1') || (*name != '/'))) + { + /* invalid escape sequence or wrong character in *name */ + return false; + } + else + { + pointer++; + } + } + else if ((!case_sensitive && (tolower(*name) != tolower(*pointer))) || (case_sensitive && (*name != *pointer))) + { + return false; + } + } + if (((*pointer != 0) && (*pointer != '/')) != (*name != 0)) + { + /* one string has ended, the other not */ + return false;; + } + + return true; +} + +/* calculate the length of a string if encoded as JSON pointer with ~0 and ~1 escape sequences */ +static size_t pointer_encoded_length(const unsigned char *string) +{ + size_t length; + for (length = 0; *string != '\0'; (void)string++, length++) + { + /* character needs to be escaped? */ + if ((*string == '~') || (*string == '/')) + { + length++; + } + } + + return length; +} + +/* copy a string while escaping '~' and '/' with ~0 and ~1 JSON pointer escape codes */ +static void encode_string_as_pointer(unsigned char *destination, const unsigned char *source) +{ + for (; source[0] != '\0'; (void)source++, destination++) + { + if (source[0] == '/') + { + destination[1] = '1'; + destination++; + } + else if (source[0] == '~') + { + destination[0] = '~'; + destination[1] = '1'; + destination++; + } + else + { + destination[0] = source[0]; + } + } + + destination[0] = '\0'; +} + +CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(const cJSON * const object, const cJSON * const target) +{ + size_t child_index = 0; + cJSON *current_child = 0; + + if ((object == NULL) || (target == NULL)) + { + return NULL; + } + + if (object == target) + { + /* found */ + return (char*)cJSONUtils_strdup((const unsigned char*)""); + } + + /* recursively search all children of the object or array */ + for (current_child = object->child; current_child != NULL; (void)(current_child = current_child->next), child_index++) + { + unsigned char *target_pointer = (unsigned char*)cJSONUtils_FindPointerFromObjectTo(current_child, target); + /* found the target? */ + if (target_pointer != NULL) + { + if (cJSON_IsArray(object)) + { + /* reserve enough memory for a 64 bit integer + '/' and '\0' */ + unsigned char *full_pointer = (unsigned char*)cJSON_malloc(strlen((char*)target_pointer) + 20 + sizeof("/")); + /* check if conversion to unsigned long is valid + * This should be eliminated at compile time by dead code elimination + * if size_t is an alias of unsigned long, or if it is bigger */ + if (child_index > ULONG_MAX) + { + cJSON_free(target_pointer); + return NULL; + } + sprintf((char*)full_pointer, "/%lu%s", (unsigned long)child_index, target_pointer); /* / */ + cJSON_free(target_pointer); + + return (char*)full_pointer; + } + + if (cJSON_IsObject(object)) + { + unsigned char *full_pointer = (unsigned char*)cJSON_malloc(strlen((char*)target_pointer) + pointer_encoded_length((unsigned char*)current_child->string) + 2); + full_pointer[0] = '/'; + encode_string_as_pointer(full_pointer + 1, (unsigned char*)current_child->string); + strcat((char*)full_pointer, (char*)target_pointer); + cJSON_free(target_pointer); + + return (char*)full_pointer; + } + + /* reached leaf of the tree, found nothing */ + cJSON_free(target_pointer); + return NULL; + } + } + + /* not found */ + return NULL; +} + +/* non broken version of cJSON_GetArrayItem */ +static cJSON *get_array_item(const cJSON *array, size_t item) +{ + cJSON *child = array ? array->child : NULL; + while ((child != NULL) && (item > 0)) + { + item--; + child = child->next; + } + + return child; +} + +static cJSON_bool decode_array_index_from_pointer(const unsigned char * const pointer, size_t * const index) +{ + size_t parsed_index = 0; + size_t position = 0; + + if ((pointer[0] == '0') && ((pointer[1] != '\0') && (pointer[1] != '/'))) + { + /* leading zeroes are not permitted */ + return 0; + } + + for (position = 0; (pointer[position] >= '0') && (pointer[0] <= '9'); position++) + { + parsed_index = (10 * parsed_index) + (size_t)(pointer[position] - '0'); + + } + + if ((pointer[position] != '\0') && (pointer[position] != '/')) + { + return 0; + } + + *index = parsed_index; + + return 1; +} + +static cJSON *get_item_from_pointer(cJSON * const object, const char * pointer, const cJSON_bool case_sensitive) +{ + cJSON *current_element = object; + + if (pointer == NULL) + { + return NULL; + } + + /* follow path of the pointer */ + while ((pointer[0] == '/') && (current_element != NULL)) + { + pointer++; + if (cJSON_IsArray(current_element)) + { + size_t index = 0; + if (!decode_array_index_from_pointer((const unsigned char*)pointer, &index)) + { + return NULL; + } + + current_element = get_array_item(current_element, index); + } + else if (cJSON_IsObject(current_element)) + { + current_element = current_element->child; + /* GetObjectItem. */ + while ((current_element != NULL) && !compare_pointers((unsigned char*)current_element->string, (const unsigned char*)pointer, case_sensitive)) + { + current_element = current_element->next; + } + } + else + { + return NULL; + } + + /* skip to the next path token or end of string */ + while ((pointer[0] != '\0') && (pointer[0] != '/')) + { + pointer++; + } + } + + return current_element; +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON * const object, const char *pointer) +{ + return get_item_from_pointer(object, pointer, false); +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointerCaseSensitive(cJSON * const object, const char *pointer) +{ + return get_item_from_pointer(object, pointer, true); +} + +/* JSON Patch implementation. */ +static void decode_pointer_inplace(unsigned char *string) +{ + unsigned char *decoded_string = string; + + if (string == NULL) { + return; + } + + for (; *string; (void)decoded_string++, string++) + { + if (string[0] == '~') + { + if (string[1] == '0') + { + decoded_string[0] = '~'; + } + else if (string[1] == '1') + { + decoded_string[1] = '/'; + } + else + { + /* invalid escape sequence */ + return; + } + + string++; + } + } + + decoded_string[0] = '\0'; +} + +/* non-broken cJSON_DetachItemFromArray */ +static cJSON *detach_item_from_array(cJSON *array, size_t which) +{ + cJSON *c = array->child; + while (c && (which > 0)) + { + c = c->next; + which--; + } + if (!c) + { + /* item doesn't exist */ + return NULL; + } + if (c->prev) + { + /* not the first element */ + c->prev->next = c->next; + } + if (c->next) + { + c->next->prev = c->prev; + } + if (c==array->child) + { + array->child = c->next; + } + /* make sure the detached item doesn't point anywhere anymore */ + c->prev = c->next = NULL; + + return c; +} + +/* detach an item at the given path */ +static cJSON *detach_path(cJSON *object, const unsigned char *path, const cJSON_bool case_sensitive) +{ + unsigned char *parent_pointer = NULL; + unsigned char *child_pointer = NULL; + cJSON *parent = NULL; + cJSON *detached_item = NULL; + + /* copy path and split it in parent and child */ + parent_pointer = cJSONUtils_strdup(path); + if (parent_pointer == NULL) { + goto cleanup; + } + + child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/'); /* last '/' */ + if (child_pointer == NULL) + { + goto cleanup; + } + /* split strings */ + child_pointer[0] = '\0'; + child_pointer++; + + parent = get_item_from_pointer(object, (char*)parent_pointer, case_sensitive); + decode_pointer_inplace(child_pointer); + + if (cJSON_IsArray(parent)) + { + size_t index = 0; + if (!decode_array_index_from_pointer(child_pointer, &index)) + { + goto cleanup; + } + detached_item = detach_item_from_array(parent, index); + } + else if (cJSON_IsObject(parent)) + { + detached_item = cJSON_DetachItemFromObject(parent, (char*)child_pointer); + } + else + { + /* Couldn't find object to remove child from. */ + goto cleanup; + } + +cleanup: + if (parent_pointer != NULL) + { + cJSON_free(parent_pointer); + } + + return detached_item; +} + +/* sort lists using mergesort */ +static cJSON *sort_list(cJSON *list, const cJSON_bool case_sensitive) +{ + cJSON *first = list; + cJSON *second = list; + cJSON *current_item = list; + cJSON *result = list; + cJSON *result_tail = NULL; + + if ((list == NULL) || (list->next == NULL)) + { + /* One entry is sorted already. */ + return result; + } + + while ((current_item != NULL) && (current_item->next != NULL) && (compare_strings((unsigned char*)current_item->string, (unsigned char*)current_item->next->string, case_sensitive) < 0)) + { + /* Test for list sorted. */ + current_item = current_item->next; + } + if ((current_item == NULL) || (current_item->next == NULL)) + { + /* Leave sorted lists unmodified. */ + return result; + } + + /* reset pointer to the beginning */ + current_item = list; + while (current_item != NULL) + { + /* Walk two pointers to find the middle. */ + second = second->next; + current_item = current_item->next; + /* advances current_item two steps at a time */ + if (current_item != NULL) + { + current_item = current_item->next; + } + } + if ((second != NULL) && (second->prev != NULL)) + { + /* Split the lists */ + second->prev->next = NULL; + } + + /* Recursively sort the sub-lists. */ + first = sort_list(first, case_sensitive); + second = sort_list(second, case_sensitive); + result = NULL; + + /* Merge the sub-lists */ + while ((first != NULL) && (second != NULL)) + { + cJSON *smaller = NULL; + if (compare_strings((unsigned char*)first->string, (unsigned char*)second->string, false) < 0) + { + smaller = first; + } + else + { + smaller = second; + } + + if (result == NULL) + { + /* start merged list with the smaller element */ + result_tail = smaller; + result = smaller; + } + else + { + /* add smaller element to the list */ + result_tail->next = smaller; + smaller->prev = result_tail; + result_tail = smaller; + } + + if (first == smaller) + { + first = first->next; + } + else + { + second = second->next; + } + } + + if (first != NULL) + { + /* Append rest of first list. */ + if (result == NULL) + { + return first; + } + result_tail->next = first; + first->prev = result_tail; + } + if (second != NULL) + { + /* Append rest of second list */ + if (result == NULL) + { + return second; + } + result_tail->next = second; + second->prev = result_tail; + } + + return result; +} + +static void sort_object(cJSON * const object, const cJSON_bool case_sensitive) +{ + if (object == NULL) + { + return; + } + object->child = sort_list(object->child, case_sensitive); +} + +static cJSON_bool compare_json(cJSON *a, cJSON *b, const cJSON_bool case_sensitive) +{ + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) + { + /* mismatched type. */ + return false; + } + switch (a->type & 0xFF) + { + case cJSON_Number: + /* numeric mismatch. */ + if ((a->valueint != b->valueint) || (a->valuedouble != b->valuedouble)) + { + return false; + } + else + { + return true; + } + + case cJSON_String: + /* string mismatch. */ + if (strcmp(a->valuestring, b->valuestring) != 0) + { + return false; + } + else + { + return true; + } + + case cJSON_Array: + for ((void)(a = a->child), b = b->child; (a != NULL) && (b != NULL); (void)(a = a->next), b = b->next) + { + cJSON_bool identical = compare_json(a, b, case_sensitive); + if (!identical) + { + return false; + } + } + + /* array size mismatch? (one of both children is not NULL) */ + if ((a != NULL) || (b != NULL)) + { + return false; + } + else + { + return true; + } + + case cJSON_Object: + sort_object(a, case_sensitive); + sort_object(b, case_sensitive); + for ((void)(a = a->child), b = b->child; (a != NULL) && (b != NULL); (void)(a = a->next), b = b->next) + { + cJSON_bool identical = false; + /* compare object keys */ + if (compare_strings((unsigned char*)a->string, (unsigned char*)b->string, case_sensitive)) + { + /* missing member */ + return false; + } + identical = compare_json(a, b, case_sensitive); + if (!identical) + { + return false; + } + } + + /* object length mismatch (one of both children is not null) */ + if ((a != NULL) || (b != NULL)) + { + return false; + } + else + { + return true; + } + + default: + break; + } + + /* null, true or false */ + return true; +} + +/* non broken version of cJSON_InsertItemInArray */ +static cJSON_bool insert_item_in_array(cJSON *array, size_t which, cJSON *newitem) +{ + cJSON *child = array->child; + while (child && (which > 0)) + { + child = child->next; + which--; + } + if (which > 0) + { + /* item is after the end of the array */ + return 0; + } + if (child == NULL) + { + cJSON_AddItemToArray(array, newitem); + return 1; + } + + /* insert into the linked list */ + newitem->next = child; + newitem->prev = child->prev; + child->prev = newitem; + + /* was it at the beginning */ + if (child == array->child) + { + array->child = newitem; + } + else + { + newitem->prev->next = newitem; + } + + return 1; +} + +static cJSON *get_object_item(const cJSON * const object, const char* name, const cJSON_bool case_sensitive) +{ + if (case_sensitive) + { + return cJSON_GetObjectItemCaseSensitive(object, name); + } + + return cJSON_GetObjectItem(object, name); +} + +enum patch_operation { INVALID, ADD, REMOVE, REPLACE, MOVE, COPY, TEST }; + +static enum patch_operation decode_patch_operation(const cJSON * const patch, const cJSON_bool case_sensitive) +{ + cJSON *operation = get_object_item(patch, "op", case_sensitive); + if (!cJSON_IsString(operation)) + { + return INVALID; + } + + if (strcmp(operation->valuestring, "add") == 0) + { + return ADD; + } + + if (strcmp(operation->valuestring, "remove") == 0) + { + return REMOVE; + } + + if (strcmp(operation->valuestring, "replace") == 0) + { + return REPLACE; + } + + if (strcmp(operation->valuestring, "move") == 0) + { + return MOVE; + } + + if (strcmp(operation->valuestring, "copy") == 0) + { + return COPY; + } + + if (strcmp(operation->valuestring, "test") == 0) + { + return TEST; + } + + return INVALID; +} + +/* overwrite and existing item with another one and free resources on the way */ +static void overwrite_item(cJSON * const root, const cJSON replacement) +{ + if (root == NULL) + { + return; + } + + if (root->string != NULL) + { + cJSON_free(root->string); + } + if (root->valuestring != NULL) + { + cJSON_free(root->valuestring); + } + if (root->child != NULL) + { + cJSON_Delete(root->child); + } + + memcpy(root, &replacement, sizeof(cJSON)); +} + +static int apply_patch(cJSON *object, const cJSON *patch, const cJSON_bool case_sensitive) +{ + cJSON *path = NULL; + cJSON *value = NULL; + cJSON *parent = NULL; + enum patch_operation opcode = INVALID; + unsigned char *parent_pointer = NULL; + unsigned char *child_pointer = NULL; + int status = 0; + + path = get_object_item(patch, "path", case_sensitive); + if (!cJSON_IsString(path)) + { + /* malformed patch. */ + status = 2; + goto cleanup; + } + + opcode = decode_patch_operation(patch, case_sensitive); + if (opcode == INVALID) + { + status = 3; + goto cleanup; + } + else if (opcode == TEST) + { + /* compare value: {...} with the given path */ + status = !compare_json(get_item_from_pointer(object, path->valuestring, case_sensitive), get_object_item(patch, "value", case_sensitive), case_sensitive); + goto cleanup; + } + + /* special case for replacing the root */ + if (path->valuestring[0] == '\0') + { + if (opcode == REMOVE) + { + static const cJSON invalid = { NULL, NULL, NULL, cJSON_Invalid, NULL, 0, 0, NULL}; + + overwrite_item(object, invalid); + + status = 0; + goto cleanup; + } + + if ((opcode == REPLACE) || (opcode == ADD)) + { + value = get_object_item(patch, "value", case_sensitive); + if (value == NULL) + { + /* missing "value" for add/replace. */ + status = 7; + goto cleanup; + } + + value = cJSON_Duplicate(value, 1); + if (value == NULL) + { + /* out of memory for add/replace. */ + status = 8; + goto cleanup; + } + + overwrite_item(object, *value); + + /* delete the duplicated value */ + cJSON_free(value); + value = NULL; + + /* the string "value" isn't needed */ + if (object->string != NULL) + { + cJSON_free(object->string); + object->string = NULL; + } + + status = 0; + goto cleanup; + } + } + + if ((opcode == REMOVE) || (opcode == REPLACE)) + { + /* Get rid of old. */ + cJSON *old_item = detach_path(object, (unsigned char*)path->valuestring, case_sensitive); + if (old_item == NULL) + { + status = 13; + goto cleanup; + } + cJSON_Delete(old_item); + if (opcode == REMOVE) + { + /* For Remove, this job is done. */ + status = 0; + goto cleanup; + } + } + + /* Copy/Move uses "from". */ + if ((opcode == MOVE) || (opcode == COPY)) + { + cJSON *from = get_object_item(patch, "from", case_sensitive); + if (from == NULL) + { + /* missing "from" for copy/move. */ + status = 4; + goto cleanup; + } + + if (opcode == MOVE) + { + value = detach_path(object, (unsigned char*)from->valuestring, case_sensitive); + } + if (opcode == COPY) + { + value = get_item_from_pointer(object, from->valuestring, case_sensitive); + } + if (value == NULL) + { + /* missing "from" for copy/move. */ + status = 5; + goto cleanup; + } + if (opcode == COPY) + { + value = cJSON_Duplicate(value, 1); + } + if (value == NULL) + { + /* out of memory for copy/move. */ + status = 6; + goto cleanup; + } + } + else /* Add/Replace uses "value". */ + { + value = get_object_item(patch, "value", case_sensitive); + if (value == NULL) + { + /* missing "value" for add/replace. */ + status = 7; + goto cleanup; + } + value = cJSON_Duplicate(value, 1); + if (value == NULL) + { + /* out of memory for add/replace. */ + status = 8; + goto cleanup; + } + } + + /* Now, just add "value" to "path". */ + + /* split pointer in parent and child */ + parent_pointer = cJSONUtils_strdup((unsigned char*)path->valuestring); + child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/'); + if (child_pointer != NULL) + { + child_pointer[0] = '\0'; + child_pointer++; + } + parent = get_item_from_pointer(object, (char*)parent_pointer, case_sensitive); + decode_pointer_inplace(child_pointer); + + /* add, remove, replace, move, copy, test. */ + if ((parent == NULL) || (child_pointer == NULL)) + { + /* Couldn't find object to add to. */ + status = 9; + goto cleanup; + } + else if (cJSON_IsArray(parent)) + { + if (strcmp((char*)child_pointer, "-") == 0) + { + cJSON_AddItemToArray(parent, value); + value = NULL; + } + else + { + size_t index = 0; + if (!decode_array_index_from_pointer(child_pointer, &index)) + { + status = 11; + goto cleanup; + } + + if (!insert_item_in_array(parent, index, value)) + { + status = 10; + goto cleanup; + } + value = NULL; + } + } + else if (cJSON_IsObject(parent)) + { + if (case_sensitive) + { + cJSON_DeleteItemFromObjectCaseSensitive(parent, (char*)child_pointer); + } + else + { + cJSON_DeleteItemFromObject(parent, (char*)child_pointer); + } + cJSON_AddItemToObject(parent, (char*)child_pointer, value); + value = NULL; + } + else /* parent is not an object */ + { + /* Couldn't find object to add to. */ + status = 9; + goto cleanup; + } + +cleanup: + if (value != NULL) + { + cJSON_Delete(value); + } + if (parent_pointer != NULL) + { + cJSON_free(parent_pointer); + } + + return status; +} + +CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON * const object, const cJSON * const patches) +{ + const cJSON *current_patch = NULL; + int status = 0; + + if (!cJSON_IsArray(patches)) + { + /* malformed patches. */ + return 1; + } + + if (patches != NULL) + { + current_patch = patches->child; + } + + while (current_patch != NULL) + { + status = apply_patch(object, current_patch, false); + if (status != 0) + { + return status; + } + current_patch = current_patch->next; + } + + return 0; +} + +CJSON_PUBLIC(int) cJSONUtils_ApplyPatchesCaseSensitive(cJSON * const object, const cJSON * const patches) +{ + const cJSON *current_patch = NULL; + int status = 0; + + if (!cJSON_IsArray(patches)) + { + /* malformed patches. */ + return 1; + } + + if (patches != NULL) + { + current_patch = patches->child; + } + + while (current_patch != NULL) + { + status = apply_patch(object, current_patch, true); + if (status != 0) + { + return status; + } + current_patch = current_patch->next; + } + + return 0; +} + +static void compose_patch(cJSON * const patches, const unsigned char * const operation, const unsigned char * const path, const unsigned char *suffix, const cJSON * const value) +{ + cJSON *patch = NULL; + + if ((patches == NULL) || (operation == NULL) || (path == NULL)) + { + return; + } + + patch = cJSON_CreateObject(); + if (patch == NULL) + { + return; + } + cJSON_AddItemToObject(patch, "op", cJSON_CreateString((const char*)operation)); + + if (suffix == NULL) + { + cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)path)); + } + else + { + size_t suffix_length = pointer_encoded_length(suffix); + size_t path_length = strlen((const char*)path); + unsigned char *full_path = (unsigned char*)cJSON_malloc(path_length + suffix_length + sizeof("/")); + + sprintf((char*)full_path, "%s/", (const char*)path); + encode_string_as_pointer(full_path + path_length + 1, suffix); + + cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)full_path)); + cJSON_free(full_path); + } + + if (value != NULL) + { + cJSON_AddItemToObject(patch, "value", cJSON_Duplicate(value, 1)); + } + cJSON_AddItemToArray(patches, patch); +} + +CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON * const array, const char * const operation, const char * const path, const cJSON * const value) +{ + compose_patch(array, (const unsigned char*)operation, (const unsigned char*)path, NULL, value); +} + +static void create_patches(cJSON * const patches, const unsigned char * const path, cJSON * const from, cJSON * const to, const cJSON_bool case_sensitive) +{ + if ((from == NULL) || (to == NULL)) + { + return; + } + + if ((from->type & 0xFF) != (to->type & 0xFF)) + { + compose_patch(patches, (const unsigned char*)"replace", path, 0, to); + return; + } + + switch (from->type & 0xFF) + { + case cJSON_Number: + if ((from->valueint != to->valueint) || (from->valuedouble != to->valuedouble)) + { + compose_patch(patches, (const unsigned char*)"replace", path, NULL, to); + } + return; + + case cJSON_String: + if (strcmp(from->valuestring, to->valuestring) != 0) + { + compose_patch(patches, (const unsigned char*)"replace", path, NULL, to); + } + return; + + case cJSON_Array: + { + size_t index = 0; + cJSON *from_child = from->child; + cJSON *to_child = to->child; + unsigned char *new_path = (unsigned char*)cJSON_malloc(strlen((const char*)path) + 20 + sizeof("/")); /* Allow space for 64bit int. log10(2^64) = 20 */ + + /* generate patches for all array elements that exist in both "from" and "to" */ + for (index = 0; (from_child != NULL) && (to_child != NULL); (void)(from_child = from_child->next), (void)(to_child = to_child->next), index++) + { + /* check if conversion to unsigned long is valid + * This should be eliminated at compile time by dead code elimination + * if size_t is an alias of unsigned long, or if it is bigger */ + if (index > ULONG_MAX) + { + cJSON_free(new_path); + return; + } + sprintf((char*)new_path, "%s/%lu", path, (unsigned long)index); /* path of the current array element */ + create_patches(patches, new_path, from_child, to_child, case_sensitive); + } + + /* remove leftover elements from 'from' that are not in 'to' */ + for (; (from_child != NULL); (void)(from_child = from_child->next)) + { + /* check if conversion to unsigned long is valid + * This should be eliminated at compile time by dead code elimination + * if size_t is an alias of unsigned long, or if it is bigger */ + if (index > ULONG_MAX) + { + cJSON_free(new_path); + return; + } + sprintf((char*)new_path, "%lu", (unsigned long)index); + compose_patch(patches, (const unsigned char*)"remove", path, new_path, NULL); + } + /* add new elements in 'to' that were not in 'from' */ + for (; (to_child != NULL); (void)(to_child = to_child->next), index++) + { + compose_patch(patches, (const unsigned char*)"add", path, (const unsigned char*)"-", to_child); + } + cJSON_free(new_path); + return; + } + + case cJSON_Object: + { + cJSON *from_child = NULL; + cJSON *to_child = NULL; + sort_object(from, case_sensitive); + sort_object(to, case_sensitive); + + from_child = from->child; + to_child = to->child; + /* for all object values in the object with more of them */ + while ((from_child != NULL) || (to_child != NULL)) + { + int diff; + if (from_child == NULL) + { + diff = 1; + } + else if (to_child == NULL) + { + diff = -1; + } + else + { + diff = compare_strings((unsigned char*)from_child->string, (unsigned char*)to_child->string, case_sensitive); + } + + if (diff == 0) + { + /* both object keys are the same */ + size_t path_length = strlen((const char*)path); + size_t from_child_name_length = pointer_encoded_length((unsigned char*)from_child->string); + unsigned char *new_path = (unsigned char*)cJSON_malloc(path_length + from_child_name_length + sizeof("/")); + + sprintf((char*)new_path, "%s/", path); + encode_string_as_pointer(new_path + path_length + 1, (unsigned char*)from_child->string); + + /* create a patch for the element */ + create_patches(patches, new_path, from_child, to_child, case_sensitive); + cJSON_free(new_path); + + from_child = from_child->next; + to_child = to_child->next; + } + else if (diff < 0) + { + /* object element doesn't exist in 'to' --> remove it */ + compose_patch(patches, (const unsigned char*)"remove", path, (unsigned char*)from_child->string, NULL); + + from_child = from_child->next; + } + else + { + /* object element doesn't exist in 'from' --> add it */ + compose_patch(patches, (const unsigned char*)"add", path, (unsigned char*)to_child->string, to_child); + + to_child = to_child->next; + } + } + return; + } + + default: + break; + } +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON * const from, cJSON * const to) +{ + cJSON *patches = NULL; + + if ((from == NULL) || (to == NULL)) + { + return NULL; + } + + patches = cJSON_CreateArray(); + create_patches(patches, (const unsigned char*)"", from, to, false); + + return patches; +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatchesCaseSensitive(cJSON * const from, cJSON * const to) +{ + cJSON *patches = NULL; + + if ((from == NULL) || (to == NULL)) + { + return NULL; + } + + patches = cJSON_CreateArray(); + create_patches(patches, (const unsigned char*)"", from, to, true); + + return patches; +} + +CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON * const object) +{ + sort_object(object, false); +} + +CJSON_PUBLIC(void) cJSONUtils_SortObjectCaseSensitive(cJSON * const object) +{ + sort_object(object, true); +} + +static cJSON *merge_patch(cJSON *target, const cJSON * const patch, const cJSON_bool case_sensitive) +{ + cJSON *patch_child = NULL; + + if (!cJSON_IsObject(patch)) + { + /* scalar value, array or NULL, just duplicate */ + cJSON_Delete(target); + return cJSON_Duplicate(patch, 1); + } + + if (!cJSON_IsObject(target)) + { + cJSON_Delete(target); + target = cJSON_CreateObject(); + } + + patch_child = patch->child; + while (patch_child != NULL) + { + if (cJSON_IsNull(patch_child)) + { + /* NULL is the indicator to remove a value, see RFC7396 */ + if (case_sensitive) + { + cJSON_DeleteItemFromObjectCaseSensitive(target, patch_child->string); + } + else + { + cJSON_DeleteItemFromObject(target, patch_child->string); + } + } + else + { + cJSON *replace_me = NULL; + cJSON *replacement = NULL; + + if (case_sensitive) + { + replace_me = cJSON_DetachItemFromObjectCaseSensitive(target, patch_child->string); + } + else + { + replace_me = cJSON_DetachItemFromObject(target, patch_child->string); + } + + replacement = merge_patch(replace_me, patch_child, case_sensitive); + if (replacement == NULL) + { + return NULL; + } + + cJSON_AddItemToObject(target, patch_child->string, replacement); + } + patch_child = patch_child->next; + } + return target; +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, const cJSON * const patch) +{ + return merge_patch(target, patch, false); +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatchCaseSensitive(cJSON *target, const cJSON * const patch) +{ + return merge_patch(target, patch, true); +} + +static cJSON *generate_merge_patch(cJSON * const from, cJSON * const to, const cJSON_bool case_sensitive) +{ + cJSON *from_child = NULL; + cJSON *to_child = NULL; + cJSON *patch = NULL; + if (to == NULL) + { + /* patch to delete everything */ + return cJSON_CreateNull(); + } + if (!cJSON_IsObject(to) || !cJSON_IsObject(from)) + { + return cJSON_Duplicate(to, 1); + } + + sort_object(from, case_sensitive); + sort_object(to, case_sensitive); + + from_child = from->child; + to_child = to->child; + patch = cJSON_CreateObject(); + while (from_child || to_child) + { + int diff; + if (from_child != NULL) + { + if (to_child != NULL) + { + diff = strcmp(from_child->string, to_child->string); + } + else + { + diff = -1; + } + } + else + { + diff = 1; + } + + if (diff < 0) + { + /* from has a value that to doesn't have -> remove */ + cJSON_AddItemToObject(patch, from_child->string, cJSON_CreateNull()); + + from_child = from_child->next; + } + else if (diff > 0) + { + /* to has a value that from doesn't have -> add to patch */ + cJSON_AddItemToObject(patch, to_child->string, cJSON_Duplicate(to_child, 1)); + + to_child = to_child->next; + } + else + { + /* object key exists in both objects */ + if (!compare_json(from_child, to_child, case_sensitive)) + { + /* not identical --> generate a patch */ + cJSON_AddItemToObject(patch, to_child->string, cJSONUtils_GenerateMergePatch(from_child, to_child)); + } + + /* next key in the object */ + from_child = from_child->next; + to_child = to_child->next; + } + } + if (patch->child == NULL) + { + /* no patch generated */ + cJSON_Delete(patch); + return NULL; + } + + return patch; +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON * const from, cJSON * const to) +{ + return generate_merge_patch(from, to, false); +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatchCaseSensitive(cJSON * const from, cJSON * const to) +{ + return generate_merge_patch(from, to, true); +} diff --git a/sysdrv/tools/board/lorawan-bridge/cJSON/cJSON_Utils.h b/sysdrv/tools/board/lorawan-bridge/cJSON/cJSON_Utils.h new file mode 100644 index 0000000000..03ec10c9e2 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/cJSON/cJSON_Utils.h @@ -0,0 +1,74 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "cJSON.h" + +/* Implement RFC6901 (https://tools.ietf.org/html/rfc6901) JSON Pointer spec. */ +CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON * const object, const char *pointer); +CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointerCaseSensitive(cJSON * const object, const char *pointer); + +/* Implement RFC6902 (https://tools.ietf.org/html/rfc6902) JSON Patch spec. */ +/* NOTE: This modifies objects in 'from' and 'to' by sorting the elements by their key */ +CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON * const from, cJSON * const to); +CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatchesCaseSensitive(cJSON * const from, cJSON * const to); +/* Utility for generating patch array entries. */ +CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON * const array, const char * const operation, const char * const path, const cJSON * const value); +/* Returns 0 for success. */ +CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON * const object, const cJSON * const patches); +CJSON_PUBLIC(int) cJSONUtils_ApplyPatchesCaseSensitive(cJSON * const object, const cJSON * const patches); + +/* +// Note that ApplyPatches is NOT atomic on failure. To implement an atomic ApplyPatches, use: +//int cJSONUtils_AtomicApplyPatches(cJSON **object, cJSON *patches) +//{ +// cJSON *modme = cJSON_Duplicate(*object, 1); +// int error = cJSONUtils_ApplyPatches(modme, patches); +// if (!error) +// { +// cJSON_Delete(*object); +// *object = modme; +// } +// else +// { +// cJSON_Delete(modme); +// } +// +// return error; +//} +// Code not added to library since this strategy is a LOT slower. +*/ + +/* Implement RFC7386 (https://tools.ietf.org/html/rfc7396) JSON Merge Patch spec. */ +/* target will be modified by patch. return value is new ptr for target. */ +CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, const cJSON * const patch); +CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatchCaseSensitive(cJSON *target, const cJSON * const patch); +/* generates a patch to move from -> to */ +/* NOTE: This modifies objects in 'from' and 'to' by sorting the elements by their key */ +CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON * const from, cJSON * const to); +CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatchCaseSensitive(cJSON * const from, cJSON * const to); + +/* Given a root object and a target object, construct a pointer from one to the other. */ +CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(const cJSON * const object, const cJSON * const target); + +/* Sorts the members of the object into alphabetical order. */ +CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON * const object); +CJSON_PUBLIC(void) cJSONUtils_SortObjectCaseSensitive(cJSON * const object); diff --git a/sysdrv/tools/board/lorawan-bridge/civetweb b/sysdrv/tools/board/lorawan-bridge/civetweb new file mode 160000 index 0000000000..3da9aeaed5 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/civetweb @@ -0,0 +1 @@ +Subproject commit 3da9aeaed53c2acd15445b7ba898fe91a989d795 diff --git a/sysdrv/tools/board/lorawan-bridge/libcivetweb.a b/sysdrv/tools/board/lorawan-bridge/libcivetweb.a new file mode 100644 index 0000000000..96148345fb Binary files /dev/null and b/sysdrv/tools/board/lorawan-bridge/libcivetweb.a differ diff --git a/sysdrv/tools/board/lorawan-bridge/lorawan_send.c b/sysdrv/tools/board/lorawan-bridge/lorawan_send.c new file mode 100644 index 0000000000..b8802e5202 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/lorawan_send.c @@ -0,0 +1,192 @@ +#include +#include +#include +#include +#include +#include "cJSON.h" +#include "civetweb.h" +#include "send_json.h" +#include "b64.c/b64.h" +#include "lorawan_send.h" + +int request = 0; + +static int +LorawanSendGET(struct mg_connection *conn) +{ + cJSON *obj = cJSON_CreateObject(); + + if (!obj) { + /* insufficient memory? */ + mg_send_http_error(conn, 500, "Server error"); + return 500; + } + + cJSON_AddStringToObject(obj, "version", CIVETWEB_VERSION); + cJSON_AddStringToObject(obj, "modem_function", "lorawan"); + cJSON_AddStringToObject(obj, "operation", "send"); + cJSON_AddNumberToObject(obj, "request", ++request); + SendJSON(conn, obj); + cJSON_Delete(obj); + + return 200; +} + + +static int +LorawanSendDELETE(struct mg_connection *conn) +{ + mg_send_http_error(conn, + 204, + "%s", + ""); /* Return "deleted" = "204 No Content" */ + + return 204; +} + + +static int +LorawanSendPUT(struct mg_connection *conn) +{ + char buffer[1024]; + int dlen = mg_read(conn, buffer, sizeof(buffer) - 1); + cJSON *obj, *port_elem, *payload_elem; + char* payload; + uint newvalue, port; + + if ((dlen < 1) || (dlen >= sizeof(buffer))) { + mg_send_http_error(conn, 400, "%s", "No request body data"); + return 400; + } + buffer[dlen] = 0; + + obj = cJSON_Parse(buffer); + if (obj == NULL) { + mg_send_http_error(conn, 400, "%s", "Invalid request body data"); + return 400; + } + + port_elem = cJSON_GetObjectItemCaseSensitive(obj, "port"); + + if (!cJSON_IsNumber(port_elem)) { + cJSON_Delete(obj); + mg_send_http_error(conn, + 400, + "%s", + "No \"request\" number in body data"); + return 400; + } + + newvalue = (uint)port_elem->valuedouble; + + if ((double)newvalue != port_elem->valuedouble) { + cJSON_Delete(obj); + mg_send_http_error(conn, + 400, + "%s", + "Invalid \"request\" number in body data"); + return 400; + } + + payload_elem = cJSON_GetObjectItemCaseSensitive(obj, "payload64"); + if (cJSON_IsString(payload_elem) && (payload_elem->valuestring != NULL)) + { + printf("Checking Payload \"%s\"\n", payload_elem->valuestring); + } else { + cJSON_Delete(obj); + mg_send_http_error(conn, + 400, + "%s", + "Invalid \"payload64\" in body data"); + return 400; + } + + // Decode from base64 + payload = b64_decode(payload_elem->valuestring, strlen(payload_elem->valuestring)); + if (payload==NULL) { + cJSON_Delete(obj); + mg_send_http_error(conn, + 400, + "%s", + "Invalid \"payload64\" in body data"); + return 400; + } + + port = newvalue; + printf("%s",payload); + + //Generate JSON response + cJSON *ret = cJSON_CreateObject(); + if (ret == NULL) + { + cJSON_Delete(ret); + printf("Unable to generate response for response"); + mg_send_http_error(conn, + 500, + "%s", + "Unable to generate response"); + return 500; + } + + printf("here"); + payload_elem = cJSON_CreateString(payload); + if (payload == NULL) + { + cJSON_Delete(ret); + printf("Unable to generate response for payload"); + mg_send_http_error(conn, + 500, + "%s", + "Unable to generate response for payload"); + return 500; + } + + cJSON_AddItemToObject(ret, "payload", payload_elem); + + if (cJSON_AddNumberToObject(ret, "port", port) == NULL) { + cJSON_Delete(ret); + printf("Unable to generate response for port"); + mg_send_http_error(conn, + 500, + "%s", + "Unable to generate response for port"); + return 500; + } + + mg_printf(conn, + "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: " + "close\r\n\r\n"); + mg_printf(conn, "%s", cJSON_Print(ret)); + cJSON_Delete(obj); + return 201; +} + +int +LorawanSendHandler(struct mg_connection *conn, void *cbdata) +{ + char path1[1024], path2[1024]; + const struct mg_request_info *ri = mg_get_request_info(conn); + const char *url = ri->local_uri; + size_t url_len = strlen(url); + + (void)cbdata; /* currently unused */ + + /* According to method */ + if (0 == strcmp(ri->request_method, "GET")) { + return LorawanSendGET(conn); + } + if ((0 == strcmp(ri->request_method, "PUT")) + || (0 == strcmp(ri->request_method, "POST")) + || (0 == strcmp(ri->request_method, "PATCH"))) { + /* In this example, do the same for PUT, POST and PATCH */ + return LorawanSendPUT(conn); + } + if (0 == strcmp(ri->request_method, "DELETE")) { + return LorawanSendDELETE(conn); + } + + /* this is not a GET request */ + mg_send_http_error( + conn, 405, "Only GET, PUT, POST, DELETE and PATCH method supported"); + return 405; +} \ No newline at end of file diff --git a/sysdrv/tools/board/lorawan-bridge/lorawan_send.h b/sysdrv/tools/board/lorawan-bridge/lorawan_send.h new file mode 100644 index 0000000000..8eecbd91a5 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/lorawan_send.h @@ -0,0 +1 @@ +int LorawanSendHandler(struct mg_connection *conn, void *cbdata); \ No newline at end of file diff --git a/sysdrv/tools/board/lorawan-bridge/main b/sysdrv/tools/board/lorawan-bridge/main new file mode 100755 index 0000000000..a0c8d9cea9 Binary files /dev/null and b/sysdrv/tools/board/lorawan-bridge/main differ diff --git a/sysdrv/tools/board/lorawan-bridge/main.c b/sysdrv/tools/board/lorawan-bridge/main.c deleted file mode 100644 index 43faf7f772..0000000000 --- a/sysdrv/tools/board/lorawan-bridge/main.c +++ /dev/null @@ -1,224 +0,0 @@ -#include -#include -#include -#include -//#include -#include -#include -#include -#include -#include - -//#include "lora-ioctl.h" - -#define PF_LORAWAN PF_MAX -#define AF_LORAWAN PF_LORAWAN - -enum { - LRW_ADDR_APPEUI, - LRW_ADDR_DEVEUI, - LRW_ADDR_DEVADDR, -}; - -struct lrw_addr_in { - int addr_type; - union { - uint64_t app_eui; - uint64_t dev_eui; - uint32_t devaddr; - }; -}; - -struct sockaddr_lorawan { - sa_family_t family; /* AF_LORAWAN */ - struct lrw_addr_in addr_in; -}; - -/* DEV ioctl() commands */ - -enum LRW_IOC_CMDS { - SIOCGLRWSTATE = SIOCDEVPRIVATE, - SIOCSLRWSTATE, - SIOCGLRWFREQ, - SIOCSLRWFREQ, - SIOCGLRWBW, - SIOCSLRWBW, - SIOCGLRWTXPWR, - SIOCSLRWTXPWR, - SIOCGLRWSPRF, - SIOCSLRWSPRF, - SIOCGLRWLNA, - SIOCSLRWLNA, - SIOCGLRWRSSI, - SIOCGLRWSNR, -}; - -/* Bit 1: 1 for ready to read, 0 for not ready - * Bit 0: 1 for ready to write, 0 for not write - */ -//uint8_t ready2rw(int fd) -//{ -// fd_set read_fds, write_fds; -// struct timeval tv = {.tv_sec = 5, .tv_usec = 0}; -// uint8_t flag; -// -// /* I/O multiplexing. */ -// FD_ZERO(&read_fds); -// FD_ZERO(&write_fds); -// FD_SET(fd, &read_fds); -// FD_SET(fd, &write_fds); -// if (select(fd+1, &read_fds, &write_fds, NULL, &tv) == -1) -// perror("Select failed"); -// -// flag = 0; -// /* Read from the file descriptor if it is ready to be read. */ -// if (FD_ISSET(fd, &read_fds)) { -// flag |= (1 << 1); -// } -// /* Write to the file descriptor if it is ready to be written. */ -// if (FD_ISSET(fd, &write_fds)) { -// flag |= (1 << 0); -// } -// -// return flag; -//} - -//#define ready2read(fd) (ready2rw(fd) & (1 << 1)) -//#define ready2write(fd) (ready2rw(fd) & (1 << 0)) - -int main(int argc, char **argv) -{ - char *data; - char pstr[40]; - int sock; - struct sockaddr_lorawan src_addr, dst_addr; - struct ifreq ifr; - int32_t pwr = 100; -#define MAX_BUFFER_LEN 16 - char buf[MAX_BUFFER_LEN]; - int len; - unsigned int s; - int ret; - - /* Parse command. */ - if (argc >= 2) { - //path = argv[1]; - data = argv[1]; - } - else { - printf("Need more arguments.\r\n"); - return -1; - } - - sock = socket(PF_LORAWAN, SOCK_DGRAM, 0); - if (sock < 0) { - perror("socket error\n"); - return -1; - } - - src_addr.family = AF_LORAWAN; - src_addr.addr_in.addr_type = LRW_ADDR_DEVADDR; - src_addr.addr_in.devaddr = 0x01020304; - printf("Going to bind address %X\n", src_addr.addr_in.devaddr); - ret = bind(sock, (struct sockaddr *)&src_addr, sizeof(src_addr)); - if (sock < 0) { - perror("bind error\n"); - return ret; - } - - strcpy(ifr.ifr_name, "lora0"); - ifr.ifr_data = (void *)&pwr; - ret = ioctl(sock, SIOCSLRWTXPWR, &ifr); - if(ioctl(sock, SIOCSLRWTXPWR, &ifr) < 0) { - perror("ioctl error"); - return ret; - } - - dst_addr.family = AF_LORAWAN; - dst_addr.addr_in.addr_type = LRW_ADDR_DEVADDR; - dst_addr.addr_in.devaddr = 0xFFFFFFFF; - len = sendto(sock, data, strlen(data), 0, (struct sockaddr *)&dst_addr, sizeof(dst_addr)); - if (len < 0) { - perror("sendto"); - } - -// /* Open device node. */ -// fd = open(path, O_RDWR); -// if (fd == -1) { -// sprintf(pstr, "Open %s failed", path); -// perror(pstr); -// return -1; -// } -// printf("Opened %s\n", path); -// -// set_state(fd, LORA_START); -// /*Set the RF spreading factor. */ -// uint32_t sprf = 2048; -// set_sprfactor(fd, sprf); -// printf("Going to set the RF spreading factor %u chips\n", sprf); -// -// /* Set the RF bandwidth. */ -// uint32_t bw = 125000; -// set_bw(fd, bw); -// printf("Going to set the RF bandwith %u Hz\n", bw); -// -// /* Set the RF power. */ -// int32_t power = 10; -// set_power(fd, power); -// printf("Going to set the RF power %d dbm\n", power); -// -// printf("The current RSSI is %d dbm\n", get_rssi(fd)); -// -// /* Write to the file descriptor if it is ready to be written. */ -// printf("Going to write %s\n", path); -// s = 0; -// while (!ready2write(fd)) { -// sleep(1); -// s++; -// printf("\t%s is not ready to write, do other things.", path); -// printf(" %u s\r", s); -// } -// printf("\n"); -// len = do_write(fd, data, strlen(data)); -// if (len < 0) -// perror("Error"); -// else -// printf("Written %d bytes: %s\n", len, data); -// -// /* Read from echo if it is ready to be read. */ -// memset(buf, 0, MAX_BUFFER_LEN); -// printf("Going to read %s\n", path); -// /* Set the device in read state. */ -// set_state(fd, LORA_STATE_RX); -// s = 0; -// while (!ready2read(fd)) { -// sleep(1); -// s++; -// printf("\t%s is not ready to read, do other things.", path); -// printf(" %u s\r", s); -// } -// printf("\n"); -// len = do_read(fd, buf, MAX_BUFFER_LEN - 1); -// if (len > 0) -// printf("Read %d bytes: %s\n", len, buf); -// -// printf("The LoRa carrier frequency is %u Hz\n", get_freq(fd)); -// printf("The RF spreading factor is %u chips\n", get_sprfactor(fd)); -// printf("The RF bandwith is %u Hz\n", get_bw(fd)); -// printf("The current RSSI is %d dbm\n", get_rssi(fd)); -// printf("The last packet SNR is %d db\n", get_snr(fd)); -// printf("The output power is %d dbm\n", get_power(fd)); -// printf("The LNA gain is %d db\n", get_lna(fd)); -// -// /* Set the device in sleep state. */ -// set_state(fd, LORA_STATE_SLEEP); -// printf("The LoRa device is in 0x%X state\n", get_state(fd)); - - sleep(10); -// set_state(fd, LORA_STOP); - /* Close device node. */ - shutdown(sock, SHUT_RDWR); - close(sock); - - return 0; -} \ No newline at end of file diff --git a/sysdrv/tools/board/lorawan-bridge/make.log b/sysdrv/tools/board/lorawan-bridge/make.log new file mode 100644 index 0000000000..391d017fe0 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/make.log @@ -0,0 +1,5514 @@ +cc -o rest_server -I./civetweb/include -IcJSON -DNO_FILES -DMG_EXPERIMENTAL_INTERFACES -ILoraMac -ILoraMac/region -ILoraMac/common -ILoraMac/common/LmHandler -ILoraMac/common/LmHandler/packages -ILoraMac/soft-se LoraMac/spidev_lib/spidev_lib.c LoraMac/board.c LoraMac/main.c LoraMac/spi-board.c LoraMac/delay.c LoraMac/sx1276.c LoraMac/sx1276-board.c LoraMac/LoRaMac.c LoraMac/LoRaMacAdr.c LoraMac/LoRaMacClassB.c LoraMac/LoRaMacCommands.c LoraMac/LoRaMacConfirmQueue.c LoraMac/LoRaMacCrypto.c LoraMac/LoRaMacParser.c LoraMac/LoRaMacSerializer.c LoraMac/timer.c LoraMac/utilities.c LoraMac/systime.c LoraMac/common/LmHandler/LmHandler.c LoraMac/common/LmHandler/packages/FragDecoder.c LoraMac/common/LmHandler/packages/LmhpClockSync.c LoraMac/common/LmHandler/packages/LmhpCompliance.c LoraMac/common/LmHandler/packages/LmhpFragmentation.c LoraMac/common/LmHandler/packages/LmhpRemoteMcastSetup.c LoraMac/common/CayenneLpp.c LoraMac/common/LmHandlerMsgDisplay.c LoraMac/region/RegionAS923.c LoraMac/region/RegionAU915.c LoraMac/region/RegionBaseUS.c LoraMac/region/Region.c LoraMac/region/RegionCN470A20.c LoraMac/region/RegionCN470A26.c LoraMac/region/RegionCN470B20.c LoraMac/region/RegionCN470B26.c LoraMac/region/RegionCN470.c LoraMac/region/RegionCN779.c LoraMac/region/RegionCommon.c LoraMac/region/RegionEU433.c LoraMac/region/RegionEU868.c LoraMac/region/RegionIN865.c LoraMac/region/RegionKR920.c LoraMac/region/RegionRU864.c LoraMac/region/RegionUS915.c LoraMac/soft-se/aes.c LoraMac/soft-se/cmac.c LoraMac/soft-se/soft-se-hal.c LoraMac/soft-se/soft-se.c libcivetweb.a -lpthread -ldl +LoraMac/main.c:41:2: warning: #warning "No active region defined, LORAMAC_REGION_EU868 will be used as default." [-Wcpp] + 41 | #warning "No active region defined, LORAMAC_REGION_EU868 will be used as default." + | ^~~~~~~ +LoraMac/timer.c: In function ‘TimerStart’: +LoraMac/timer.c:119:9: warning: implicit declaration of function ‘RtcSetTimerContext’ [-Wimplicit-function-declaration] + 119 | RtcSetTimerContext( ); + | ^~~~~~~~~~~~~~~~~~ +LoraMac/timer.c:125:23: warning: implicit declaration of function ‘RtcGetTimerElapsedTime’ [-Wimplicit-function-declaration] + 125 | elapsedTime = RtcGetTimerElapsedTime( ); + | ^~~~~~~~~~~~~~~~~~~~~~ +LoraMac/timer.c: In function ‘TimerIrqHandler’: +LoraMac/timer.c:187:21: warning: implicit declaration of function ‘RtcGetTimerContext’ [-Wimplicit-function-declaration] + 187 | uint32_t old = RtcGetTimerContext( ); + | ^~~~~~~~~~~~~~~~~~ +LoraMac/timer.c: In function ‘TimerStop’: +LoraMac/timer.c:262:17: warning: implicit declaration of function ‘RtcStopAlarm’ [-Wimplicit-function-declaration] + 262 | RtcStopAlarm( ); + | ^~~~~~~~~~~~ +LoraMac/timer.c: In function ‘TimerSetValue’: +LoraMac/timer.c:330:22: warning: implicit declaration of function ‘RtcMs2Tick’ [-Wimplicit-function-declaration] + 330 | uint32_t ticks = RtcMs2Tick( value ); + | ^~~~~~~~~~ +LoraMac/timer.c:334:16: warning: implicit declaration of function ‘RtcGetMinimumTimeout’ [-Wimplicit-function-declaration] + 334 | minValue = RtcGetMinimumTimeout( ); + | ^~~~~~~~~~~~~~~~~~~~ +LoraMac/timer.c: In function ‘TimerGetCurrentTime’: +LoraMac/timer.c:347:20: warning: implicit declaration of function ‘RtcGetTimerValue’ [-Wimplicit-function-declaration] + 347 | uint32_t now = RtcGetTimerValue( ); + | ^~~~~~~~~~~~~~~~ +LoraMac/timer.c:348:13: warning: implicit declaration of function ‘RtcTick2Ms’ [-Wimplicit-function-declaration] + 348 | return RtcTick2Ms( now ); + | ^~~~~~~~~~ +LoraMac/timer.c: In function ‘TimerSetTimeout’: +LoraMac/timer.c:374:5: warning: implicit declaration of function ‘RtcSetAlarm’ [-Wimplicit-function-declaration] + 374 | RtcSetAlarm( obj->Timestamp ); + | ^~~~~~~~~~~ +LoraMac/timer.c: In function ‘TimerTempCompensation’: +LoraMac/timer.c:379:12: warning: implicit declaration of function ‘RtcTempCompensation’; did you mean ‘TimerTempCompensation’? [-Wimplicit-function-declaration] + 379 | return RtcTempCompensation( period, temperature ); + | ^~~~~~~~~~~~~~~~~~~ + | TimerTempCompensation +LoraMac/timer.c: In function ‘TimerProcess’: +LoraMac/timer.c:384:5: warning: implicit declaration of function ‘RtcProcess’ [-Wimplicit-function-declaration] + 384 | RtcProcess( ); + | ^~~~~~~~~~ +In file included from LoraMac/systime.c:2: +LoraMac/systime.h:63:38: warning: ‘struct tm’ declared inside parameter list will not be visible outside of this definition or declaration + 63 | uint32_t SysTimeMkTime( const struct tm* localtime ); + | ^~ +LoraMac/systime.h:72:57: warning: ‘struct tm’ declared inside parameter list will not be visible outside of this definition or declaration + 72 | void SysTimeLocalTime( const uint32_t timestamp, struct tm *localtime ); + | ^~ +LoraMac/soft-se/soft-se.c:53:65: error: unknown type name ‘Key_t’; did you mean ‘key_t’? + 53 | static SecureElementStatus_t GetKeyByID( KeyIdentifier_t keyID, Key_t** keyItem ) + | ^~~~~ + | key_t +LoraMac/soft-se/soft-se.c: In function ‘ComputeCmac’: +LoraMac/soft-se/soft-se.c:91:5: error: unknown type name ‘Key_t’; did you mean ‘key_t’? + 91 | Key_t* keyItem; + | ^~~~~ + | key_t +LoraMac/soft-se/soft-se.c:92:36: warning: implicit declaration of function ‘GetKeyByID’ [-Wimplicit-function-declaration] + 92 | SecureElementStatus_t retval = GetKeyByID( keyID, &keyItem ); + | ^~~~~~~~~~ +LoraMac/soft-se/soft-se.c:96:45: error: request for member ‘KeyValue’ in something not a structure or union + 96 | AES_CMAC_SetKey( aesCmacCtx, keyItem->KeyValue ); + | ^~ +LoraMac/soft-se/soft-se.c: In function ‘SecureElementInit’: +LoraMac/soft-se/soft-se.c:141:10: error: ‘SecureElementNvmData_t’ {aka ‘struct sSecureElementNvCtx’} has no member named ‘KeyList’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +In file included from LoraMac/soft-se/soft-se.c:37: +LoraMac/soft-se/se-identity.h:105:13: error: field name not in record or union initializer + 105 | .KeyID = APP_KEY, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:105:13: note: (near initialization for ‘seNvmInit.Crc32’) + 105 | .KeyID = APP_KEY, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:106:13: error: field name not in record or union initializer + 106 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:106:13: note: (near initialization for ‘seNvmInit.Crc32’) + 106 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:106:33: warning: excess elements in scalar initializer + 106 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:106:33: note: (near initialization for ‘seNvmInit.Crc32’) + 106 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:106:39: warning: excess elements in scalar initializer + 106 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:106:39: note: (near initialization for ‘seNvmInit.Crc32’) + 106 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:106:45: warning: excess elements in scalar initializer + 106 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:106:45: note: (near initialization for ‘seNvmInit.Crc32’) + 106 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:106:51: warning: excess elements in scalar initializer + 106 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:106:51: note: (near initialization for ‘seNvmInit.Crc32’) + 106 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:106:57: warning: excess elements in scalar initializer + 106 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:106:57: note: (near initialization for ‘seNvmInit.Crc32’) + 106 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:106:63: warning: excess elements in scalar initializer + 106 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:106:63: note: (near initialization for ‘seNvmInit.Crc32’) + 106 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:106:69: warning: excess elements in scalar initializer + 106 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:106:69: note: (near initialization for ‘seNvmInit.Crc32’) + 106 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:106:75: warning: excess elements in scalar initializer + 106 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:106:75: note: (near initialization for ‘seNvmInit.Crc32’) + 106 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:106:81: warning: excess elements in scalar initializer + 106 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:106:81: note: (near initialization for ‘seNvmInit.Crc32’) + 106 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:106:87: warning: excess elements in scalar initializer + 106 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:106:87: note: (near initialization for ‘seNvmInit.Crc32’) + 106 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:106:93: warning: excess elements in scalar initializer + 106 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:106:93: note: (near initialization for ‘seNvmInit.Crc32’) + 106 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:106:99: warning: excess elements in scalar initializer + 106 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:106:99: note: (near initialization for ‘seNvmInit.Crc32’) + 106 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:106:105: warning: excess elements in scalar initializer + 106 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:106:105: note: (near initialization for ‘seNvmInit.Crc32’) + 106 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:106:111: warning: excess elements in scalar initializer + 106 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:106:111: note: (near initialization for ‘seNvmInit.Crc32’) + 106 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:107:27: warning: excess elements in scalar initializer + 107 | 0x3C }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:107:27: note: (near initialization for ‘seNvmInit.Crc32’) + 107 | 0x3C }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:106:25: warning: excess elements in scalar initializer + 106 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:106:25: note: (near initialization for ‘seNvmInit.Crc32’) + 106 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:114:13: error: field name not in record or union initializer + 114 | .KeyID = NWK_KEY, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:114:13: note: (near initialization for ‘seNvmInit.Crc32’) + 114 | .KeyID = NWK_KEY, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:115:13: error: field name not in record or union initializer + 115 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:115:13: note: (near initialization for ‘seNvmInit.Crc32’) + 115 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:115:33: warning: excess elements in scalar initializer + 115 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:115:33: note: (near initialization for ‘seNvmInit.Crc32’) + 115 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:115:39: warning: excess elements in scalar initializer + 115 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:115:39: note: (near initialization for ‘seNvmInit.Crc32’) + 115 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:115:45: warning: excess elements in scalar initializer + 115 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:115:45: note: (near initialization for ‘seNvmInit.Crc32’) + 115 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:115:51: warning: excess elements in scalar initializer + 115 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:115:51: note: (near initialization for ‘seNvmInit.Crc32’) + 115 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:115:57: warning: excess elements in scalar initializer + 115 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:115:57: note: (near initialization for ‘seNvmInit.Crc32’) + 115 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:115:63: warning: excess elements in scalar initializer + 115 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:115:63: note: (near initialization for ‘seNvmInit.Crc32’) + 115 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:115:69: warning: excess elements in scalar initializer + 115 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:115:69: note: (near initialization for ‘seNvmInit.Crc32’) + 115 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:115:75: warning: excess elements in scalar initializer + 115 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:115:75: note: (near initialization for ‘seNvmInit.Crc32’) + 115 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:115:81: warning: excess elements in scalar initializer + 115 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:115:81: note: (near initialization for ‘seNvmInit.Crc32’) + 115 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:115:87: warning: excess elements in scalar initializer + 115 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:115:87: note: (near initialization for ‘seNvmInit.Crc32’) + 115 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:115:93: warning: excess elements in scalar initializer + 115 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:115:93: note: (near initialization for ‘seNvmInit.Crc32’) + 115 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:115:99: warning: excess elements in scalar initializer + 115 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:115:99: note: (near initialization for ‘seNvmInit.Crc32’) + 115 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:115:105: warning: excess elements in scalar initializer + 115 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:115:105: note: (near initialization for ‘seNvmInit.Crc32’) + 115 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:115:111: warning: excess elements in scalar initializer + 115 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:115:111: note: (near initialization for ‘seNvmInit.Crc32’) + 115 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:116:27: warning: excess elements in scalar initializer + 116 | 0x3C }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:116:27: note: (near initialization for ‘seNvmInit.Crc32’) + 116 | 0x3C }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:115:25: warning: excess elements in scalar initializer + 115 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:115:25: note: (near initialization for ‘seNvmInit.Crc32’) + 115 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:109:9: warning: excess elements in scalar initializer + 109 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:109:9: note: (near initialization for ‘seNvmInit.Crc32’) + 109 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:123:13: error: field name not in record or union initializer + 123 | .KeyID = J_S_INT_KEY, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:123:13: note: (near initialization for ‘seNvmInit.Crc32’) + 123 | .KeyID = J_S_INT_KEY, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:124:13: error: field name not in record or union initializer + 124 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:124:13: note: (near initialization for ‘seNvmInit.Crc32’) + 124 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:124:33: warning: excess elements in scalar initializer + 124 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:124:33: note: (near initialization for ‘seNvmInit.Crc32’) + 124 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:124:39: warning: excess elements in scalar initializer + 124 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:124:39: note: (near initialization for ‘seNvmInit.Crc32’) + 124 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:124:45: warning: excess elements in scalar initializer + 124 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:124:45: note: (near initialization for ‘seNvmInit.Crc32’) + 124 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:124:51: warning: excess elements in scalar initializer + 124 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:124:51: note: (near initialization for ‘seNvmInit.Crc32’) + 124 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:124:57: warning: excess elements in scalar initializer + 124 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:124:57: note: (near initialization for ‘seNvmInit.Crc32’) + 124 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:124:63: warning: excess elements in scalar initializer + 124 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:124:63: note: (near initialization for ‘seNvmInit.Crc32’) + 124 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:124:69: warning: excess elements in scalar initializer + 124 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:124:69: note: (near initialization for ‘seNvmInit.Crc32’) + 124 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:124:75: warning: excess elements in scalar initializer + 124 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:124:75: note: (near initialization for ‘seNvmInit.Crc32’) + 124 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:124:81: warning: excess elements in scalar initializer + 124 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:124:81: note: (near initialization for ‘seNvmInit.Crc32’) + 124 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:124:87: warning: excess elements in scalar initializer + 124 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:124:87: note: (near initialization for ‘seNvmInit.Crc32’) + 124 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:124:93: warning: excess elements in scalar initializer + 124 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:124:93: note: (near initialization for ‘seNvmInit.Crc32’) + 124 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:124:99: warning: excess elements in scalar initializer + 124 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:124:99: note: (near initialization for ‘seNvmInit.Crc32’) + 124 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:124:105: warning: excess elements in scalar initializer + 124 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:124:105: note: (near initialization for ‘seNvmInit.Crc32’) + 124 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:124:111: warning: excess elements in scalar initializer + 124 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:124:111: note: (near initialization for ‘seNvmInit.Crc32’) + 124 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:125:27: warning: excess elements in scalar initializer + 125 | 0x00 }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:125:27: note: (near initialization for ‘seNvmInit.Crc32’) + 125 | 0x00 }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:124:25: warning: excess elements in scalar initializer + 124 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:124:25: note: (near initialization for ‘seNvmInit.Crc32’) + 124 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:118:9: warning: excess elements in scalar initializer + 118 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:118:9: note: (near initialization for ‘seNvmInit.Crc32’) + 118 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:132:13: error: field name not in record or union initializer + 132 | .KeyID = J_S_ENC_KEY, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:132:13: note: (near initialization for ‘seNvmInit.Crc32’) + 132 | .KeyID = J_S_ENC_KEY, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:133:13: error: field name not in record or union initializer + 133 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:133:13: note: (near initialization for ‘seNvmInit.Crc32’) + 133 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:133:33: warning: excess elements in scalar initializer + 133 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:133:33: note: (near initialization for ‘seNvmInit.Crc32’) + 133 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:133:39: warning: excess elements in scalar initializer + 133 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:133:39: note: (near initialization for ‘seNvmInit.Crc32’) + 133 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:133:45: warning: excess elements in scalar initializer + 133 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:133:45: note: (near initialization for ‘seNvmInit.Crc32’) + 133 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:133:51: warning: excess elements in scalar initializer + 133 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:133:51: note: (near initialization for ‘seNvmInit.Crc32’) + 133 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:133:57: warning: excess elements in scalar initializer + 133 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:133:57: note: (near initialization for ‘seNvmInit.Crc32’) + 133 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:133:63: warning: excess elements in scalar initializer + 133 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:133:63: note: (near initialization for ‘seNvmInit.Crc32’) + 133 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:133:69: warning: excess elements in scalar initializer + 133 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:133:69: note: (near initialization for ‘seNvmInit.Crc32’) + 133 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:133:75: warning: excess elements in scalar initializer + 133 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:133:75: note: (near initialization for ‘seNvmInit.Crc32’) + 133 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:133:81: warning: excess elements in scalar initializer + 133 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:133:81: note: (near initialization for ‘seNvmInit.Crc32’) + 133 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:133:87: warning: excess elements in scalar initializer + 133 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:133:87: note: (near initialization for ‘seNvmInit.Crc32’) + 133 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:133:93: warning: excess elements in scalar initializer + 133 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:133:93: note: (near initialization for ‘seNvmInit.Crc32’) + 133 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:133:99: warning: excess elements in scalar initializer + 133 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:133:99: note: (near initialization for ‘seNvmInit.Crc32’) + 133 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:133:105: warning: excess elements in scalar initializer + 133 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:133:105: note: (near initialization for ‘seNvmInit.Crc32’) + 133 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:133:111: warning: excess elements in scalar initializer + 133 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:133:111: note: (near initialization for ‘seNvmInit.Crc32’) + 133 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:134:27: warning: excess elements in scalar initializer + 134 | 0x00 }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:134:27: note: (near initialization for ‘seNvmInit.Crc32’) + 134 | 0x00 }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:133:25: warning: excess elements in scalar initializer + 133 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:133:25: note: (near initialization for ‘seNvmInit.Crc32’) + 133 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:127:9: warning: excess elements in scalar initializer + 127 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:127:9: note: (near initialization for ‘seNvmInit.Crc32’) + 127 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:141:13: error: field name not in record or union initializer + 141 | .KeyID = F_NWK_S_INT_KEY, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:141:13: note: (near initialization for ‘seNvmInit.Crc32’) + 141 | .KeyID = F_NWK_S_INT_KEY, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:142:13: error: field name not in record or union initializer + 142 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:142:13: note: (near initialization for ‘seNvmInit.Crc32’) + 142 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:142:33: warning: excess elements in scalar initializer + 142 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:142:33: note: (near initialization for ‘seNvmInit.Crc32’) + 142 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:142:39: warning: excess elements in scalar initializer + 142 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:142:39: note: (near initialization for ‘seNvmInit.Crc32’) + 142 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:142:45: warning: excess elements in scalar initializer + 142 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:142:45: note: (near initialization for ‘seNvmInit.Crc32’) + 142 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:142:51: warning: excess elements in scalar initializer + 142 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:142:51: note: (near initialization for ‘seNvmInit.Crc32’) + 142 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:142:57: warning: excess elements in scalar initializer + 142 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:142:57: note: (near initialization for ‘seNvmInit.Crc32’) + 142 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:142:63: warning: excess elements in scalar initializer + 142 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:142:63: note: (near initialization for ‘seNvmInit.Crc32’) + 142 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:142:69: warning: excess elements in scalar initializer + 142 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:142:69: note: (near initialization for ‘seNvmInit.Crc32’) + 142 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:142:75: warning: excess elements in scalar initializer + 142 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:142:75: note: (near initialization for ‘seNvmInit.Crc32’) + 142 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:142:81: warning: excess elements in scalar initializer + 142 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:142:81: note: (near initialization for ‘seNvmInit.Crc32’) + 142 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:142:87: warning: excess elements in scalar initializer + 142 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:142:87: note: (near initialization for ‘seNvmInit.Crc32’) + 142 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:142:93: warning: excess elements in scalar initializer + 142 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:142:93: note: (near initialization for ‘seNvmInit.Crc32’) + 142 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:142:99: warning: excess elements in scalar initializer + 142 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:142:99: note: (near initialization for ‘seNvmInit.Crc32’) + 142 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:142:105: warning: excess elements in scalar initializer + 142 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:142:105: note: (near initialization for ‘seNvmInit.Crc32’) + 142 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:142:111: warning: excess elements in scalar initializer + 142 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:142:111: note: (near initialization for ‘seNvmInit.Crc32’) + 142 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:143:27: warning: excess elements in scalar initializer + 143 | 0x3C }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:143:27: note: (near initialization for ‘seNvmInit.Crc32’) + 143 | 0x3C }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:142:25: warning: excess elements in scalar initializer + 142 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:142:25: note: (near initialization for ‘seNvmInit.Crc32’) + 142 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:136:9: warning: excess elements in scalar initializer + 136 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:136:9: note: (near initialization for ‘seNvmInit.Crc32’) + 136 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:150:13: error: field name not in record or union initializer + 150 | .KeyID = S_NWK_S_INT_KEY, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:150:13: note: (near initialization for ‘seNvmInit.Crc32’) + 150 | .KeyID = S_NWK_S_INT_KEY, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:151:13: error: field name not in record or union initializer + 151 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:151:13: note: (near initialization for ‘seNvmInit.Crc32’) + 151 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:151:33: warning: excess elements in scalar initializer + 151 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:151:33: note: (near initialization for ‘seNvmInit.Crc32’) + 151 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:151:39: warning: excess elements in scalar initializer + 151 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:151:39: note: (near initialization for ‘seNvmInit.Crc32’) + 151 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:151:45: warning: excess elements in scalar initializer + 151 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:151:45: note: (near initialization for ‘seNvmInit.Crc32’) + 151 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:151:51: warning: excess elements in scalar initializer + 151 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:151:51: note: (near initialization for ‘seNvmInit.Crc32’) + 151 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:151:57: warning: excess elements in scalar initializer + 151 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:151:57: note: (near initialization for ‘seNvmInit.Crc32’) + 151 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:151:63: warning: excess elements in scalar initializer + 151 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:151:63: note: (near initialization for ‘seNvmInit.Crc32’) + 151 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:151:69: warning: excess elements in scalar initializer + 151 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:151:69: note: (near initialization for ‘seNvmInit.Crc32’) + 151 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:151:75: warning: excess elements in scalar initializer + 151 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:151:75: note: (near initialization for ‘seNvmInit.Crc32’) + 151 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:151:81: warning: excess elements in scalar initializer + 151 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:151:81: note: (near initialization for ‘seNvmInit.Crc32’) + 151 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:151:87: warning: excess elements in scalar initializer + 151 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:151:87: note: (near initialization for ‘seNvmInit.Crc32’) + 151 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:151:93: warning: excess elements in scalar initializer + 151 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:151:93: note: (near initialization for ‘seNvmInit.Crc32’) + 151 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:151:99: warning: excess elements in scalar initializer + 151 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:151:99: note: (near initialization for ‘seNvmInit.Crc32’) + 151 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:151:105: warning: excess elements in scalar initializer + 151 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:151:105: note: (near initialization for ‘seNvmInit.Crc32’) + 151 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:151:111: warning: excess elements in scalar initializer + 151 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:151:111: note: (near initialization for ‘seNvmInit.Crc32’) + 151 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:152:27: warning: excess elements in scalar initializer + 152 | 0x3C }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:152:27: note: (near initialization for ‘seNvmInit.Crc32’) + 152 | 0x3C }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:151:25: warning: excess elements in scalar initializer + 151 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:151:25: note: (near initialization for ‘seNvmInit.Crc32’) + 151 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:145:9: warning: excess elements in scalar initializer + 145 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:145:9: note: (near initialization for ‘seNvmInit.Crc32’) + 145 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:159:13: error: field name not in record or union initializer + 159 | .KeyID = NWK_S_ENC_KEY, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:159:13: note: (near initialization for ‘seNvmInit.Crc32’) + 159 | .KeyID = NWK_S_ENC_KEY, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:160:13: error: field name not in record or union initializer + 160 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:160:13: note: (near initialization for ‘seNvmInit.Crc32’) + 160 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:160:33: warning: excess elements in scalar initializer + 160 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:160:33: note: (near initialization for ‘seNvmInit.Crc32’) + 160 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:160:39: warning: excess elements in scalar initializer + 160 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:160:39: note: (near initialization for ‘seNvmInit.Crc32’) + 160 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:160:45: warning: excess elements in scalar initializer + 160 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:160:45: note: (near initialization for ‘seNvmInit.Crc32’) + 160 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:160:51: warning: excess elements in scalar initializer + 160 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:160:51: note: (near initialization for ‘seNvmInit.Crc32’) + 160 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:160:57: warning: excess elements in scalar initializer + 160 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:160:57: note: (near initialization for ‘seNvmInit.Crc32’) + 160 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:160:63: warning: excess elements in scalar initializer + 160 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:160:63: note: (near initialization for ‘seNvmInit.Crc32’) + 160 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:160:69: warning: excess elements in scalar initializer + 160 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:160:69: note: (near initialization for ‘seNvmInit.Crc32’) + 160 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:160:75: warning: excess elements in scalar initializer + 160 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:160:75: note: (near initialization for ‘seNvmInit.Crc32’) + 160 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:160:81: warning: excess elements in scalar initializer + 160 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:160:81: note: (near initialization for ‘seNvmInit.Crc32’) + 160 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:160:87: warning: excess elements in scalar initializer + 160 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:160:87: note: (near initialization for ‘seNvmInit.Crc32’) + 160 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:160:93: warning: excess elements in scalar initializer + 160 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:160:93: note: (near initialization for ‘seNvmInit.Crc32’) + 160 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:160:99: warning: excess elements in scalar initializer + 160 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:160:99: note: (near initialization for ‘seNvmInit.Crc32’) + 160 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:160:105: warning: excess elements in scalar initializer + 160 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:160:105: note: (near initialization for ‘seNvmInit.Crc32’) + 160 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:160:111: warning: excess elements in scalar initializer + 160 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:160:111: note: (near initialization for ‘seNvmInit.Crc32’) + 160 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:161:27: warning: excess elements in scalar initializer + 161 | 0x3C }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:161:27: note: (near initialization for ‘seNvmInit.Crc32’) + 161 | 0x3C }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:160:25: warning: excess elements in scalar initializer + 160 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:160:25: note: (near initialization for ‘seNvmInit.Crc32’) + 160 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:154:9: warning: excess elements in scalar initializer + 154 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:154:9: note: (near initialization for ‘seNvmInit.Crc32’) + 154 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:167:13: error: field name not in record or union initializer + 167 | .KeyID = APP_S_KEY, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:167:13: note: (near initialization for ‘seNvmInit.Crc32’) + 167 | .KeyID = APP_S_KEY, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:168:13: error: field name not in record or union initializer + 168 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:168:13: note: (near initialization for ‘seNvmInit.Crc32’) + 168 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:168:33: warning: excess elements in scalar initializer + 168 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:168:33: note: (near initialization for ‘seNvmInit.Crc32’) + 168 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:168:39: warning: excess elements in scalar initializer + 168 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:168:39: note: (near initialization for ‘seNvmInit.Crc32’) + 168 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:168:45: warning: excess elements in scalar initializer + 168 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:168:45: note: (near initialization for ‘seNvmInit.Crc32’) + 168 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:168:51: warning: excess elements in scalar initializer + 168 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:168:51: note: (near initialization for ‘seNvmInit.Crc32’) + 168 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:168:57: warning: excess elements in scalar initializer + 168 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:168:57: note: (near initialization for ‘seNvmInit.Crc32’) + 168 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:168:63: warning: excess elements in scalar initializer + 168 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:168:63: note: (near initialization for ‘seNvmInit.Crc32’) + 168 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:168:69: warning: excess elements in scalar initializer + 168 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:168:69: note: (near initialization for ‘seNvmInit.Crc32’) + 168 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:168:75: warning: excess elements in scalar initializer + 168 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:168:75: note: (near initialization for ‘seNvmInit.Crc32’) + 168 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:168:81: warning: excess elements in scalar initializer + 168 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:168:81: note: (near initialization for ‘seNvmInit.Crc32’) + 168 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:168:87: warning: excess elements in scalar initializer + 168 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:168:87: note: (near initialization for ‘seNvmInit.Crc32’) + 168 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:168:93: warning: excess elements in scalar initializer + 168 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:168:93: note: (near initialization for ‘seNvmInit.Crc32’) + 168 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:168:99: warning: excess elements in scalar initializer + 168 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:168:99: note: (near initialization for ‘seNvmInit.Crc32’) + 168 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:168:105: warning: excess elements in scalar initializer + 168 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:168:105: note: (near initialization for ‘seNvmInit.Crc32’) + 168 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:168:111: warning: excess elements in scalar initializer + 168 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:168:111: note: (near initialization for ‘seNvmInit.Crc32’) + 168 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:169:27: warning: excess elements in scalar initializer + 169 | 0x3C }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:169:27: note: (near initialization for ‘seNvmInit.Crc32’) + 169 | 0x3C }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:168:25: warning: excess elements in scalar initializer + 168 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:168:25: note: (near initialization for ‘seNvmInit.Crc32’) + 168 | .KeyValue = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:163:9: warning: excess elements in scalar initializer + 163 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:163:9: note: (near initialization for ‘seNvmInit.Crc32’) + 163 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:175:13: error: field name not in record or union initializer + 175 | .KeyID = MC_ROOT_KEY, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:175:13: note: (near initialization for ‘seNvmInit.Crc32’) + 175 | .KeyID = MC_ROOT_KEY, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:176:13: error: field name not in record or union initializer + 176 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:176:13: note: (near initialization for ‘seNvmInit.Crc32’) + 176 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:176:33: warning: excess elements in scalar initializer + 176 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:176:33: note: (near initialization for ‘seNvmInit.Crc32’) + 176 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:176:39: warning: excess elements in scalar initializer + 176 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:176:39: note: (near initialization for ‘seNvmInit.Crc32’) + 176 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:176:45: warning: excess elements in scalar initializer + 176 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:176:45: note: (near initialization for ‘seNvmInit.Crc32’) + 176 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:176:51: warning: excess elements in scalar initializer + 176 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:176:51: note: (near initialization for ‘seNvmInit.Crc32’) + 176 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:176:57: warning: excess elements in scalar initializer + 176 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:176:57: note: (near initialization for ‘seNvmInit.Crc32’) + 176 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:176:63: warning: excess elements in scalar initializer + 176 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:176:63: note: (near initialization for ‘seNvmInit.Crc32’) + 176 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:176:69: warning: excess elements in scalar initializer + 176 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:176:69: note: (near initialization for ‘seNvmInit.Crc32’) + 176 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:176:75: warning: excess elements in scalar initializer + 176 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:176:75: note: (near initialization for ‘seNvmInit.Crc32’) + 176 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:176:81: warning: excess elements in scalar initializer + 176 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:176:81: note: (near initialization for ‘seNvmInit.Crc32’) + 176 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:176:87: warning: excess elements in scalar initializer + 176 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:176:87: note: (near initialization for ‘seNvmInit.Crc32’) + 176 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:176:93: warning: excess elements in scalar initializer + 176 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:176:93: note: (near initialization for ‘seNvmInit.Crc32’) + 176 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:176:99: warning: excess elements in scalar initializer + 176 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:176:99: note: (near initialization for ‘seNvmInit.Crc32’) + 176 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:176:105: warning: excess elements in scalar initializer + 176 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:176:105: note: (near initialization for ‘seNvmInit.Crc32’) + 176 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:176:111: warning: excess elements in scalar initializer + 176 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:176:111: note: (near initialization for ‘seNvmInit.Crc32’) + 176 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:177:27: warning: excess elements in scalar initializer + 177 | 0x00 }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:177:27: note: (near initialization for ‘seNvmInit.Crc32’) + 177 | 0x00 }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:176:25: warning: excess elements in scalar initializer + 176 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:176:25: note: (near initialization for ‘seNvmInit.Crc32’) + 176 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:171:9: warning: excess elements in scalar initializer + 171 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:171:9: note: (near initialization for ‘seNvmInit.Crc32’) + 171 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:183:13: error: field name not in record or union initializer + 183 | .KeyID = MC_KE_KEY, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:183:13: note: (near initialization for ‘seNvmInit.Crc32’) + 183 | .KeyID = MC_KE_KEY, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:184:13: error: field name not in record or union initializer + 184 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:184:13: note: (near initialization for ‘seNvmInit.Crc32’) + 184 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:184:33: warning: excess elements in scalar initializer + 184 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:184:33: note: (near initialization for ‘seNvmInit.Crc32’) + 184 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:184:39: warning: excess elements in scalar initializer + 184 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:184:39: note: (near initialization for ‘seNvmInit.Crc32’) + 184 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:184:45: warning: excess elements in scalar initializer + 184 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:184:45: note: (near initialization for ‘seNvmInit.Crc32’) + 184 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:184:51: warning: excess elements in scalar initializer + 184 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:184:51: note: (near initialization for ‘seNvmInit.Crc32’) + 184 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:184:57: warning: excess elements in scalar initializer + 184 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:184:57: note: (near initialization for ‘seNvmInit.Crc32’) + 184 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:184:63: warning: excess elements in scalar initializer + 184 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:184:63: note: (near initialization for ‘seNvmInit.Crc32’) + 184 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:184:69: warning: excess elements in scalar initializer + 184 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:184:69: note: (near initialization for ‘seNvmInit.Crc32’) + 184 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:184:75: warning: excess elements in scalar initializer + 184 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:184:75: note: (near initialization for ‘seNvmInit.Crc32’) + 184 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:184:81: warning: excess elements in scalar initializer + 184 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:184:81: note: (near initialization for ‘seNvmInit.Crc32’) + 184 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:184:87: warning: excess elements in scalar initializer + 184 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:184:87: note: (near initialization for ‘seNvmInit.Crc32’) + 184 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:184:93: warning: excess elements in scalar initializer + 184 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:184:93: note: (near initialization for ‘seNvmInit.Crc32’) + 184 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:184:99: warning: excess elements in scalar initializer + 184 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:184:99: note: (near initialization for ‘seNvmInit.Crc32’) + 184 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:184:105: warning: excess elements in scalar initializer + 184 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:184:105: note: (near initialization for ‘seNvmInit.Crc32’) + 184 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:184:111: warning: excess elements in scalar initializer + 184 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:184:111: note: (near initialization for ‘seNvmInit.Crc32’) + 184 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:185:27: warning: excess elements in scalar initializer + 185 | 0x00 }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:185:27: note: (near initialization for ‘seNvmInit.Crc32’) + 185 | 0x00 }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:184:25: warning: excess elements in scalar initializer + 184 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:184:25: note: (near initialization for ‘seNvmInit.Crc32’) + 184 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:179:9: warning: excess elements in scalar initializer + 179 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:179:9: note: (near initialization for ‘seNvmInit.Crc32’) + 179 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:191:13: error: field name not in record or union initializer + 191 | .KeyID = MC_KEY_0, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:191:13: note: (near initialization for ‘seNvmInit.Crc32’) + 191 | .KeyID = MC_KEY_0, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:192:13: error: field name not in record or union initializer + 192 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:192:13: note: (near initialization for ‘seNvmInit.Crc32’) + 192 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:192:33: warning: excess elements in scalar initializer + 192 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:192:33: note: (near initialization for ‘seNvmInit.Crc32’) + 192 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:192:39: warning: excess elements in scalar initializer + 192 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:192:39: note: (near initialization for ‘seNvmInit.Crc32’) + 192 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:192:45: warning: excess elements in scalar initializer + 192 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:192:45: note: (near initialization for ‘seNvmInit.Crc32’) + 192 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:192:51: warning: excess elements in scalar initializer + 192 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:192:51: note: (near initialization for ‘seNvmInit.Crc32’) + 192 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:192:57: warning: excess elements in scalar initializer + 192 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:192:57: note: (near initialization for ‘seNvmInit.Crc32’) + 192 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:192:63: warning: excess elements in scalar initializer + 192 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:192:63: note: (near initialization for ‘seNvmInit.Crc32’) + 192 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:192:69: warning: excess elements in scalar initializer + 192 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:192:69: note: (near initialization for ‘seNvmInit.Crc32’) + 192 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:192:75: warning: excess elements in scalar initializer + 192 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:192:75: note: (near initialization for ‘seNvmInit.Crc32’) + 192 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:192:81: warning: excess elements in scalar initializer + 192 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:192:81: note: (near initialization for ‘seNvmInit.Crc32’) + 192 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:192:87: warning: excess elements in scalar initializer + 192 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:192:87: note: (near initialization for ‘seNvmInit.Crc32’) + 192 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:192:93: warning: excess elements in scalar initializer + 192 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:192:93: note: (near initialization for ‘seNvmInit.Crc32’) + 192 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:192:99: warning: excess elements in scalar initializer + 192 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:192:99: note: (near initialization for ‘seNvmInit.Crc32’) + 192 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:192:105: warning: excess elements in scalar initializer + 192 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:192:105: note: (near initialization for ‘seNvmInit.Crc32’) + 192 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:192:111: warning: excess elements in scalar initializer + 192 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:192:111: note: (near initialization for ‘seNvmInit.Crc32’) + 192 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:193:27: warning: excess elements in scalar initializer + 193 | 0x00 }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:193:27: note: (near initialization for ‘seNvmInit.Crc32’) + 193 | 0x00 }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:192:25: warning: excess elements in scalar initializer + 192 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:192:25: note: (near initialization for ‘seNvmInit.Crc32’) + 192 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:187:9: warning: excess elements in scalar initializer + 187 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:187:9: note: (near initialization for ‘seNvmInit.Crc32’) + 187 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:199:13: error: field name not in record or union initializer + 199 | .KeyID = MC_APP_S_KEY_0, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:199:13: note: (near initialization for ‘seNvmInit.Crc32’) + 199 | .KeyID = MC_APP_S_KEY_0, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:200:13: error: field name not in record or union initializer + 200 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:200:13: note: (near initialization for ‘seNvmInit.Crc32’) + 200 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:200:33: warning: excess elements in scalar initializer + 200 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:200:33: note: (near initialization for ‘seNvmInit.Crc32’) + 200 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:200:39: warning: excess elements in scalar initializer + 200 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:200:39: note: (near initialization for ‘seNvmInit.Crc32’) + 200 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:200:45: warning: excess elements in scalar initializer + 200 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:200:45: note: (near initialization for ‘seNvmInit.Crc32’) + 200 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:200:51: warning: excess elements in scalar initializer + 200 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:200:51: note: (near initialization for ‘seNvmInit.Crc32’) + 200 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:200:57: warning: excess elements in scalar initializer + 200 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:200:57: note: (near initialization for ‘seNvmInit.Crc32’) + 200 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:200:63: warning: excess elements in scalar initializer + 200 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:200:63: note: (near initialization for ‘seNvmInit.Crc32’) + 200 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:200:69: warning: excess elements in scalar initializer + 200 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:200:69: note: (near initialization for ‘seNvmInit.Crc32’) + 200 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:200:75: warning: excess elements in scalar initializer + 200 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:200:75: note: (near initialization for ‘seNvmInit.Crc32’) + 200 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:200:81: warning: excess elements in scalar initializer + 200 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:200:81: note: (near initialization for ‘seNvmInit.Crc32’) + 200 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:200:87: warning: excess elements in scalar initializer + 200 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:200:87: note: (near initialization for ‘seNvmInit.Crc32’) + 200 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:200:93: warning: excess elements in scalar initializer + 200 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:200:93: note: (near initialization for ‘seNvmInit.Crc32’) + 200 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:200:99: warning: excess elements in scalar initializer + 200 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:200:99: note: (near initialization for ‘seNvmInit.Crc32’) + 200 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:200:105: warning: excess elements in scalar initializer + 200 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:200:105: note: (near initialization for ‘seNvmInit.Crc32’) + 200 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:200:111: warning: excess elements in scalar initializer + 200 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:200:111: note: (near initialization for ‘seNvmInit.Crc32’) + 200 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:201:27: warning: excess elements in scalar initializer + 201 | 0x00 }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:201:27: note: (near initialization for ‘seNvmInit.Crc32’) + 201 | 0x00 }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:200:25: warning: excess elements in scalar initializer + 200 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:200:25: note: (near initialization for ‘seNvmInit.Crc32’) + 200 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:195:9: warning: excess elements in scalar initializer + 195 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:195:9: note: (near initialization for ‘seNvmInit.Crc32’) + 195 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:207:13: error: field name not in record or union initializer + 207 | .KeyID = MC_NWK_S_KEY_0, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:207:13: note: (near initialization for ‘seNvmInit.Crc32’) + 207 | .KeyID = MC_NWK_S_KEY_0, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:208:13: error: field name not in record or union initializer + 208 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:208:13: note: (near initialization for ‘seNvmInit.Crc32’) + 208 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:208:33: warning: excess elements in scalar initializer + 208 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:208:33: note: (near initialization for ‘seNvmInit.Crc32’) + 208 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:208:39: warning: excess elements in scalar initializer + 208 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:208:39: note: (near initialization for ‘seNvmInit.Crc32’) + 208 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:208:45: warning: excess elements in scalar initializer + 208 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:208:45: note: (near initialization for ‘seNvmInit.Crc32’) + 208 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:208:51: warning: excess elements in scalar initializer + 208 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:208:51: note: (near initialization for ‘seNvmInit.Crc32’) + 208 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:208:57: warning: excess elements in scalar initializer + 208 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:208:57: note: (near initialization for ‘seNvmInit.Crc32’) + 208 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:208:63: warning: excess elements in scalar initializer + 208 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:208:63: note: (near initialization for ‘seNvmInit.Crc32’) + 208 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:208:69: warning: excess elements in scalar initializer + 208 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:208:69: note: (near initialization for ‘seNvmInit.Crc32’) + 208 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:208:75: warning: excess elements in scalar initializer + 208 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:208:75: note: (near initialization for ‘seNvmInit.Crc32’) + 208 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:208:81: warning: excess elements in scalar initializer + 208 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:208:81: note: (near initialization for ‘seNvmInit.Crc32’) + 208 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:208:87: warning: excess elements in scalar initializer + 208 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:208:87: note: (near initialization for ‘seNvmInit.Crc32’) + 208 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:208:93: warning: excess elements in scalar initializer + 208 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:208:93: note: (near initialization for ‘seNvmInit.Crc32’) + 208 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:208:99: warning: excess elements in scalar initializer + 208 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:208:99: note: (near initialization for ‘seNvmInit.Crc32’) + 208 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:208:105: warning: excess elements in scalar initializer + 208 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:208:105: note: (near initialization for ‘seNvmInit.Crc32’) + 208 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:208:111: warning: excess elements in scalar initializer + 208 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:208:111: note: (near initialization for ‘seNvmInit.Crc32’) + 208 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:209:27: warning: excess elements in scalar initializer + 209 | 0x00 }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:209:27: note: (near initialization for ‘seNvmInit.Crc32’) + 209 | 0x00 }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:208:25: warning: excess elements in scalar initializer + 208 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:208:25: note: (near initialization for ‘seNvmInit.Crc32’) + 208 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:203:9: warning: excess elements in scalar initializer + 203 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:203:9: note: (near initialization for ‘seNvmInit.Crc32’) + 203 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:215:13: error: field name not in record or union initializer + 215 | .KeyID = MC_KEY_1, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:215:13: note: (near initialization for ‘seNvmInit.Crc32’) + 215 | .KeyID = MC_KEY_1, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:216:13: error: field name not in record or union initializer + 216 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:216:13: note: (near initialization for ‘seNvmInit.Crc32’) + 216 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:216:33: warning: excess elements in scalar initializer + 216 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:216:33: note: (near initialization for ‘seNvmInit.Crc32’) + 216 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:216:39: warning: excess elements in scalar initializer + 216 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:216:39: note: (near initialization for ‘seNvmInit.Crc32’) + 216 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:216:45: warning: excess elements in scalar initializer + 216 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:216:45: note: (near initialization for ‘seNvmInit.Crc32’) + 216 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:216:51: warning: excess elements in scalar initializer + 216 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:216:51: note: (near initialization for ‘seNvmInit.Crc32’) + 216 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:216:57: warning: excess elements in scalar initializer + 216 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:216:57: note: (near initialization for ‘seNvmInit.Crc32’) + 216 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:216:63: warning: excess elements in scalar initializer + 216 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:216:63: note: (near initialization for ‘seNvmInit.Crc32’) + 216 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:216:69: warning: excess elements in scalar initializer + 216 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:216:69: note: (near initialization for ‘seNvmInit.Crc32’) + 216 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:216:75: warning: excess elements in scalar initializer + 216 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:216:75: note: (near initialization for ‘seNvmInit.Crc32’) + 216 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:216:81: warning: excess elements in scalar initializer + 216 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:216:81: note: (near initialization for ‘seNvmInit.Crc32’) + 216 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:216:87: warning: excess elements in scalar initializer + 216 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:216:87: note: (near initialization for ‘seNvmInit.Crc32’) + 216 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:216:93: warning: excess elements in scalar initializer + 216 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:216:93: note: (near initialization for ‘seNvmInit.Crc32’) + 216 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:216:99: warning: excess elements in scalar initializer + 216 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:216:99: note: (near initialization for ‘seNvmInit.Crc32’) + 216 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:216:105: warning: excess elements in scalar initializer + 216 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:216:105: note: (near initialization for ‘seNvmInit.Crc32’) + 216 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:216:111: warning: excess elements in scalar initializer + 216 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:216:111: note: (near initialization for ‘seNvmInit.Crc32’) + 216 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:217:27: warning: excess elements in scalar initializer + 217 | 0x00 }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:217:27: note: (near initialization for ‘seNvmInit.Crc32’) + 217 | 0x00 }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:216:25: warning: excess elements in scalar initializer + 216 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:216:25: note: (near initialization for ‘seNvmInit.Crc32’) + 216 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:211:9: warning: excess elements in scalar initializer + 211 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:211:9: note: (near initialization for ‘seNvmInit.Crc32’) + 211 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:223:13: error: field name not in record or union initializer + 223 | .KeyID = MC_APP_S_KEY_1, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:223:13: note: (near initialization for ‘seNvmInit.Crc32’) + 223 | .KeyID = MC_APP_S_KEY_1, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:224:13: error: field name not in record or union initializer + 224 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:224:13: note: (near initialization for ‘seNvmInit.Crc32’) + 224 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:224:33: warning: excess elements in scalar initializer + 224 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:224:33: note: (near initialization for ‘seNvmInit.Crc32’) + 224 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:224:39: warning: excess elements in scalar initializer + 224 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:224:39: note: (near initialization for ‘seNvmInit.Crc32’) + 224 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:224:45: warning: excess elements in scalar initializer + 224 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:224:45: note: (near initialization for ‘seNvmInit.Crc32’) + 224 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:224:51: warning: excess elements in scalar initializer + 224 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:224:51: note: (near initialization for ‘seNvmInit.Crc32’) + 224 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:224:57: warning: excess elements in scalar initializer + 224 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:224:57: note: (near initialization for ‘seNvmInit.Crc32’) + 224 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:224:63: warning: excess elements in scalar initializer + 224 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:224:63: note: (near initialization for ‘seNvmInit.Crc32’) + 224 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:224:69: warning: excess elements in scalar initializer + 224 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:224:69: note: (near initialization for ‘seNvmInit.Crc32’) + 224 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:224:75: warning: excess elements in scalar initializer + 224 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:224:75: note: (near initialization for ‘seNvmInit.Crc32’) + 224 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:224:81: warning: excess elements in scalar initializer + 224 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:224:81: note: (near initialization for ‘seNvmInit.Crc32’) + 224 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:224:87: warning: excess elements in scalar initializer + 224 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:224:87: note: (near initialization for ‘seNvmInit.Crc32’) + 224 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:224:93: warning: excess elements in scalar initializer + 224 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:224:93: note: (near initialization for ‘seNvmInit.Crc32’) + 224 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:224:99: warning: excess elements in scalar initializer + 224 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:224:99: note: (near initialization for ‘seNvmInit.Crc32’) + 224 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:224:105: warning: excess elements in scalar initializer + 224 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:224:105: note: (near initialization for ‘seNvmInit.Crc32’) + 224 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:224:111: warning: excess elements in scalar initializer + 224 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:224:111: note: (near initialization for ‘seNvmInit.Crc32’) + 224 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:225:27: warning: excess elements in scalar initializer + 225 | 0x00 }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:225:27: note: (near initialization for ‘seNvmInit.Crc32’) + 225 | 0x00 }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:224:25: warning: excess elements in scalar initializer + 224 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:224:25: note: (near initialization for ‘seNvmInit.Crc32’) + 224 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:219:9: warning: excess elements in scalar initializer + 219 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:219:9: note: (near initialization for ‘seNvmInit.Crc32’) + 219 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:231:13: error: field name not in record or union initializer + 231 | .KeyID = MC_NWK_S_KEY_1, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:231:13: note: (near initialization for ‘seNvmInit.Crc32’) + 231 | .KeyID = MC_NWK_S_KEY_1, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:232:13: error: field name not in record or union initializer + 232 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:232:13: note: (near initialization for ‘seNvmInit.Crc32’) + 232 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:232:33: warning: excess elements in scalar initializer + 232 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:232:33: note: (near initialization for ‘seNvmInit.Crc32’) + 232 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:232:39: warning: excess elements in scalar initializer + 232 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:232:39: note: (near initialization for ‘seNvmInit.Crc32’) + 232 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:232:45: warning: excess elements in scalar initializer + 232 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:232:45: note: (near initialization for ‘seNvmInit.Crc32’) + 232 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:232:51: warning: excess elements in scalar initializer + 232 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:232:51: note: (near initialization for ‘seNvmInit.Crc32’) + 232 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:232:57: warning: excess elements in scalar initializer + 232 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:232:57: note: (near initialization for ‘seNvmInit.Crc32’) + 232 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:232:63: warning: excess elements in scalar initializer + 232 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:232:63: note: (near initialization for ‘seNvmInit.Crc32’) + 232 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:232:69: warning: excess elements in scalar initializer + 232 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:232:69: note: (near initialization for ‘seNvmInit.Crc32’) + 232 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:232:75: warning: excess elements in scalar initializer + 232 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:232:75: note: (near initialization for ‘seNvmInit.Crc32’) + 232 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:232:81: warning: excess elements in scalar initializer + 232 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:232:81: note: (near initialization for ‘seNvmInit.Crc32’) + 232 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:232:87: warning: excess elements in scalar initializer + 232 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:232:87: note: (near initialization for ‘seNvmInit.Crc32’) + 232 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:232:93: warning: excess elements in scalar initializer + 232 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:232:93: note: (near initialization for ‘seNvmInit.Crc32’) + 232 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:232:99: warning: excess elements in scalar initializer + 232 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:232:99: note: (near initialization for ‘seNvmInit.Crc32’) + 232 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:232:105: warning: excess elements in scalar initializer + 232 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:232:105: note: (near initialization for ‘seNvmInit.Crc32’) + 232 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:232:111: warning: excess elements in scalar initializer + 232 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:232:111: note: (near initialization for ‘seNvmInit.Crc32’) + 232 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:233:27: warning: excess elements in scalar initializer + 233 | 0x00 }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:233:27: note: (near initialization for ‘seNvmInit.Crc32’) + 233 | 0x00 }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:232:25: warning: excess elements in scalar initializer + 232 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:232:25: note: (near initialization for ‘seNvmInit.Crc32’) + 232 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:227:9: warning: excess elements in scalar initializer + 227 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:227:9: note: (near initialization for ‘seNvmInit.Crc32’) + 227 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:239:13: error: field name not in record or union initializer + 239 | .KeyID = MC_KEY_2, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:239:13: note: (near initialization for ‘seNvmInit.Crc32’) + 239 | .KeyID = MC_KEY_2, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:240:13: error: field name not in record or union initializer + 240 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:240:13: note: (near initialization for ‘seNvmInit.Crc32’) + 240 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:240:33: warning: excess elements in scalar initializer + 240 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:240:33: note: (near initialization for ‘seNvmInit.Crc32’) + 240 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:240:39: warning: excess elements in scalar initializer + 240 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:240:39: note: (near initialization for ‘seNvmInit.Crc32’) + 240 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:240:45: warning: excess elements in scalar initializer + 240 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:240:45: note: (near initialization for ‘seNvmInit.Crc32’) + 240 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:240:51: warning: excess elements in scalar initializer + 240 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:240:51: note: (near initialization for ‘seNvmInit.Crc32’) + 240 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:240:57: warning: excess elements in scalar initializer + 240 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:240:57: note: (near initialization for ‘seNvmInit.Crc32’) + 240 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:240:63: warning: excess elements in scalar initializer + 240 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:240:63: note: (near initialization for ‘seNvmInit.Crc32’) + 240 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:240:69: warning: excess elements in scalar initializer + 240 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:240:69: note: (near initialization for ‘seNvmInit.Crc32’) + 240 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:240:75: warning: excess elements in scalar initializer + 240 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:240:75: note: (near initialization for ‘seNvmInit.Crc32’) + 240 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:240:81: warning: excess elements in scalar initializer + 240 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:240:81: note: (near initialization for ‘seNvmInit.Crc32’) + 240 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:240:87: warning: excess elements in scalar initializer + 240 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:240:87: note: (near initialization for ‘seNvmInit.Crc32’) + 240 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:240:93: warning: excess elements in scalar initializer + 240 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:240:93: note: (near initialization for ‘seNvmInit.Crc32’) + 240 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:240:99: warning: excess elements in scalar initializer + 240 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:240:99: note: (near initialization for ‘seNvmInit.Crc32’) + 240 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:240:105: warning: excess elements in scalar initializer + 240 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:240:105: note: (near initialization for ‘seNvmInit.Crc32’) + 240 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:240:111: warning: excess elements in scalar initializer + 240 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:240:111: note: (near initialization for ‘seNvmInit.Crc32’) + 240 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:241:27: warning: excess elements in scalar initializer + 241 | 0x00 }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:241:27: note: (near initialization for ‘seNvmInit.Crc32’) + 241 | 0x00 }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:240:25: warning: excess elements in scalar initializer + 240 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:240:25: note: (near initialization for ‘seNvmInit.Crc32’) + 240 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:235:9: warning: excess elements in scalar initializer + 235 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:235:9: note: (near initialization for ‘seNvmInit.Crc32’) + 235 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:247:13: error: field name not in record or union initializer + 247 | .KeyID = MC_APP_S_KEY_2, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:247:13: note: (near initialization for ‘seNvmInit.Crc32’) + 247 | .KeyID = MC_APP_S_KEY_2, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:248:13: error: field name not in record or union initializer + 248 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:248:13: note: (near initialization for ‘seNvmInit.Crc32’) + 248 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:248:33: warning: excess elements in scalar initializer + 248 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:248:33: note: (near initialization for ‘seNvmInit.Crc32’) + 248 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:248:39: warning: excess elements in scalar initializer + 248 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:248:39: note: (near initialization for ‘seNvmInit.Crc32’) + 248 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:248:45: warning: excess elements in scalar initializer + 248 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:248:45: note: (near initialization for ‘seNvmInit.Crc32’) + 248 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:248:51: warning: excess elements in scalar initializer + 248 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:248:51: note: (near initialization for ‘seNvmInit.Crc32’) + 248 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:248:57: warning: excess elements in scalar initializer + 248 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:248:57: note: (near initialization for ‘seNvmInit.Crc32’) + 248 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:248:63: warning: excess elements in scalar initializer + 248 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:248:63: note: (near initialization for ‘seNvmInit.Crc32’) + 248 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:248:69: warning: excess elements in scalar initializer + 248 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:248:69: note: (near initialization for ‘seNvmInit.Crc32’) + 248 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:248:75: warning: excess elements in scalar initializer + 248 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:248:75: note: (near initialization for ‘seNvmInit.Crc32’) + 248 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:248:81: warning: excess elements in scalar initializer + 248 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:248:81: note: (near initialization for ‘seNvmInit.Crc32’) + 248 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:248:87: warning: excess elements in scalar initializer + 248 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:248:87: note: (near initialization for ‘seNvmInit.Crc32’) + 248 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:248:93: warning: excess elements in scalar initializer + 248 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:248:93: note: (near initialization for ‘seNvmInit.Crc32’) + 248 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:248:99: warning: excess elements in scalar initializer + 248 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:248:99: note: (near initialization for ‘seNvmInit.Crc32’) + 248 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:248:105: warning: excess elements in scalar initializer + 248 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:248:105: note: (near initialization for ‘seNvmInit.Crc32’) + 248 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:248:111: warning: excess elements in scalar initializer + 248 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:248:111: note: (near initialization for ‘seNvmInit.Crc32’) + 248 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:249:27: warning: excess elements in scalar initializer + 249 | 0x00 }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:249:27: note: (near initialization for ‘seNvmInit.Crc32’) + 249 | 0x00 }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:248:25: warning: excess elements in scalar initializer + 248 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:248:25: note: (near initialization for ‘seNvmInit.Crc32’) + 248 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:243:9: warning: excess elements in scalar initializer + 243 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:243:9: note: (near initialization for ‘seNvmInit.Crc32’) + 243 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:255:13: error: field name not in record or union initializer + 255 | .KeyID = MC_NWK_S_KEY_2, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:255:13: note: (near initialization for ‘seNvmInit.Crc32’) + 255 | .KeyID = MC_NWK_S_KEY_2, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:256:13: error: field name not in record or union initializer + 256 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:256:13: note: (near initialization for ‘seNvmInit.Crc32’) + 256 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:256:33: warning: excess elements in scalar initializer + 256 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:256:33: note: (near initialization for ‘seNvmInit.Crc32’) + 256 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:256:39: warning: excess elements in scalar initializer + 256 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:256:39: note: (near initialization for ‘seNvmInit.Crc32’) + 256 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:256:45: warning: excess elements in scalar initializer + 256 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:256:45: note: (near initialization for ‘seNvmInit.Crc32’) + 256 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:256:51: warning: excess elements in scalar initializer + 256 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:256:51: note: (near initialization for ‘seNvmInit.Crc32’) + 256 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:256:57: warning: excess elements in scalar initializer + 256 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:256:57: note: (near initialization for ‘seNvmInit.Crc32’) + 256 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:256:63: warning: excess elements in scalar initializer + 256 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:256:63: note: (near initialization for ‘seNvmInit.Crc32’) + 256 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:256:69: warning: excess elements in scalar initializer + 256 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:256:69: note: (near initialization for ‘seNvmInit.Crc32’) + 256 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:256:75: warning: excess elements in scalar initializer + 256 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:256:75: note: (near initialization for ‘seNvmInit.Crc32’) + 256 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:256:81: warning: excess elements in scalar initializer + 256 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:256:81: note: (near initialization for ‘seNvmInit.Crc32’) + 256 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:256:87: warning: excess elements in scalar initializer + 256 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:256:87: note: (near initialization for ‘seNvmInit.Crc32’) + 256 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:256:93: warning: excess elements in scalar initializer + 256 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:256:93: note: (near initialization for ‘seNvmInit.Crc32’) + 256 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:256:99: warning: excess elements in scalar initializer + 256 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:256:99: note: (near initialization for ‘seNvmInit.Crc32’) + 256 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:256:105: warning: excess elements in scalar initializer + 256 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:256:105: note: (near initialization for ‘seNvmInit.Crc32’) + 256 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:256:111: warning: excess elements in scalar initializer + 256 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:256:111: note: (near initialization for ‘seNvmInit.Crc32’) + 256 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:257:27: warning: excess elements in scalar initializer + 257 | 0x00 }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:257:27: note: (near initialization for ‘seNvmInit.Crc32’) + 257 | 0x00 }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:256:25: warning: excess elements in scalar initializer + 256 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:256:25: note: (near initialization for ‘seNvmInit.Crc32’) + 256 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:251:9: warning: excess elements in scalar initializer + 251 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:251:9: note: (near initialization for ‘seNvmInit.Crc32’) + 251 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:263:13: error: field name not in record or union initializer + 263 | .KeyID = MC_KEY_3, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:263:13: note: (near initialization for ‘seNvmInit.Crc32’) + 263 | .KeyID = MC_KEY_3, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:264:13: error: field name not in record or union initializer + 264 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:264:13: note: (near initialization for ‘seNvmInit.Crc32’) + 264 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:264:33: warning: excess elements in scalar initializer + 264 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:264:33: note: (near initialization for ‘seNvmInit.Crc32’) + 264 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:264:39: warning: excess elements in scalar initializer + 264 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:264:39: note: (near initialization for ‘seNvmInit.Crc32’) + 264 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:264:45: warning: excess elements in scalar initializer + 264 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:264:45: note: (near initialization for ‘seNvmInit.Crc32’) + 264 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:264:51: warning: excess elements in scalar initializer + 264 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:264:51: note: (near initialization for ‘seNvmInit.Crc32’) + 264 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:264:57: warning: excess elements in scalar initializer + 264 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:264:57: note: (near initialization for ‘seNvmInit.Crc32’) + 264 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:264:63: warning: excess elements in scalar initializer + 264 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:264:63: note: (near initialization for ‘seNvmInit.Crc32’) + 264 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:264:69: warning: excess elements in scalar initializer + 264 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:264:69: note: (near initialization for ‘seNvmInit.Crc32’) + 264 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:264:75: warning: excess elements in scalar initializer + 264 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:264:75: note: (near initialization for ‘seNvmInit.Crc32’) + 264 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:264:81: warning: excess elements in scalar initializer + 264 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:264:81: note: (near initialization for ‘seNvmInit.Crc32’) + 264 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:264:87: warning: excess elements in scalar initializer + 264 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:264:87: note: (near initialization for ‘seNvmInit.Crc32’) + 264 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:264:93: warning: excess elements in scalar initializer + 264 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:264:93: note: (near initialization for ‘seNvmInit.Crc32’) + 264 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:264:99: warning: excess elements in scalar initializer + 264 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:264:99: note: (near initialization for ‘seNvmInit.Crc32’) + 264 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:264:105: warning: excess elements in scalar initializer + 264 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:264:105: note: (near initialization for ‘seNvmInit.Crc32’) + 264 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:264:111: warning: excess elements in scalar initializer + 264 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:264:111: note: (near initialization for ‘seNvmInit.Crc32’) + 264 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:265:27: warning: excess elements in scalar initializer + 265 | 0x00 }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:265:27: note: (near initialization for ‘seNvmInit.Crc32’) + 265 | 0x00 }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:264:25: warning: excess elements in scalar initializer + 264 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:264:25: note: (near initialization for ‘seNvmInit.Crc32’) + 264 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:259:9: warning: excess elements in scalar initializer + 259 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:259:9: note: (near initialization for ‘seNvmInit.Crc32’) + 259 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:271:13: error: field name not in record or union initializer + 271 | .KeyID = MC_APP_S_KEY_3, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:271:13: note: (near initialization for ‘seNvmInit.Crc32’) + 271 | .KeyID = MC_APP_S_KEY_3, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:272:13: error: field name not in record or union initializer + 272 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:272:13: note: (near initialization for ‘seNvmInit.Crc32’) + 272 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:272:33: warning: excess elements in scalar initializer + 272 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:272:33: note: (near initialization for ‘seNvmInit.Crc32’) + 272 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:272:39: warning: excess elements in scalar initializer + 272 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:272:39: note: (near initialization for ‘seNvmInit.Crc32’) + 272 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:272:45: warning: excess elements in scalar initializer + 272 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:272:45: note: (near initialization for ‘seNvmInit.Crc32’) + 272 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:272:51: warning: excess elements in scalar initializer + 272 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:272:51: note: (near initialization for ‘seNvmInit.Crc32’) + 272 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:272:57: warning: excess elements in scalar initializer + 272 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:272:57: note: (near initialization for ‘seNvmInit.Crc32’) + 272 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:272:63: warning: excess elements in scalar initializer + 272 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:272:63: note: (near initialization for ‘seNvmInit.Crc32’) + 272 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:272:69: warning: excess elements in scalar initializer + 272 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:272:69: note: (near initialization for ‘seNvmInit.Crc32’) + 272 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:272:75: warning: excess elements in scalar initializer + 272 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:272:75: note: (near initialization for ‘seNvmInit.Crc32’) + 272 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:272:81: warning: excess elements in scalar initializer + 272 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:272:81: note: (near initialization for ‘seNvmInit.Crc32’) + 272 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:272:87: warning: excess elements in scalar initializer + 272 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:272:87: note: (near initialization for ‘seNvmInit.Crc32’) + 272 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:272:93: warning: excess elements in scalar initializer + 272 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:272:93: note: (near initialization for ‘seNvmInit.Crc32’) + 272 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:272:99: warning: excess elements in scalar initializer + 272 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:272:99: note: (near initialization for ‘seNvmInit.Crc32’) + 272 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:272:105: warning: excess elements in scalar initializer + 272 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:272:105: note: (near initialization for ‘seNvmInit.Crc32’) + 272 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:272:111: warning: excess elements in scalar initializer + 272 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:272:111: note: (near initialization for ‘seNvmInit.Crc32’) + 272 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:273:27: warning: excess elements in scalar initializer + 273 | 0x00 }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:273:27: note: (near initialization for ‘seNvmInit.Crc32’) + 273 | 0x00 }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:272:25: warning: excess elements in scalar initializer + 272 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:272:25: note: (near initialization for ‘seNvmInit.Crc32’) + 272 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:267:9: warning: excess elements in scalar initializer + 267 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:267:9: note: (near initialization for ‘seNvmInit.Crc32’) + 267 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:279:13: error: field name not in record or union initializer + 279 | .KeyID = MC_NWK_S_KEY_3, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:279:13: note: (near initialization for ‘seNvmInit.Crc32’) + 279 | .KeyID = MC_NWK_S_KEY_3, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:280:13: error: field name not in record or union initializer + 280 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:280:13: note: (near initialization for ‘seNvmInit.Crc32’) + 280 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:280:33: warning: excess elements in scalar initializer + 280 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:280:33: note: (near initialization for ‘seNvmInit.Crc32’) + 280 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:280:39: warning: excess elements in scalar initializer + 280 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:280:39: note: (near initialization for ‘seNvmInit.Crc32’) + 280 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:280:45: warning: excess elements in scalar initializer + 280 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:280:45: note: (near initialization for ‘seNvmInit.Crc32’) + 280 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:280:51: warning: excess elements in scalar initializer + 280 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:280:51: note: (near initialization for ‘seNvmInit.Crc32’) + 280 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:280:57: warning: excess elements in scalar initializer + 280 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:280:57: note: (near initialization for ‘seNvmInit.Crc32’) + 280 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:280:63: warning: excess elements in scalar initializer + 280 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:280:63: note: (near initialization for ‘seNvmInit.Crc32’) + 280 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:280:69: warning: excess elements in scalar initializer + 280 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:280:69: note: (near initialization for ‘seNvmInit.Crc32’) + 280 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:280:75: warning: excess elements in scalar initializer + 280 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:280:75: note: (near initialization for ‘seNvmInit.Crc32’) + 280 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:280:81: warning: excess elements in scalar initializer + 280 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:280:81: note: (near initialization for ‘seNvmInit.Crc32’) + 280 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:280:87: warning: excess elements in scalar initializer + 280 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:280:87: note: (near initialization for ‘seNvmInit.Crc32’) + 280 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:280:93: warning: excess elements in scalar initializer + 280 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:280:93: note: (near initialization for ‘seNvmInit.Crc32’) + 280 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:280:99: warning: excess elements in scalar initializer + 280 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:280:99: note: (near initialization for ‘seNvmInit.Crc32’) + 280 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:280:105: warning: excess elements in scalar initializer + 280 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:280:105: note: (near initialization for ‘seNvmInit.Crc32’) + 280 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:280:111: warning: excess elements in scalar initializer + 280 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:280:111: note: (near initialization for ‘seNvmInit.Crc32’) + 280 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:281:27: warning: excess elements in scalar initializer + 281 | 0x00 }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:281:27: note: (near initialization for ‘seNvmInit.Crc32’) + 281 | 0x00 }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:280:25: warning: excess elements in scalar initializer + 280 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:280:25: note: (near initialization for ‘seNvmInit.Crc32’) + 280 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:275:9: warning: excess elements in scalar initializer + 275 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:275:9: note: (near initialization for ‘seNvmInit.Crc32’) + 275 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:287:13: error: field name not in record or union initializer + 287 | .KeyID = SLOT_RAND_ZERO_KEY, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:287:13: note: (near initialization for ‘seNvmInit.Crc32’) + 287 | .KeyID = SLOT_RAND_ZERO_KEY, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:288:13: error: field name not in record or union initializer + 288 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:288:13: note: (near initialization for ‘seNvmInit.Crc32’) + 288 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:141:9: warning: braces around scalar initializer + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^ +LoraMac/soft-se/soft-se.c:141:9: note: (near initialization for ‘seNvmInit.Crc32’) +LoraMac/soft-se/se-identity.h:288:33: warning: excess elements in scalar initializer + 288 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:288:33: note: (near initialization for ‘seNvmInit.Crc32’) + 288 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:288:39: warning: excess elements in scalar initializer + 288 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:288:39: note: (near initialization for ‘seNvmInit.Crc32’) + 288 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:288:45: warning: excess elements in scalar initializer + 288 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:288:45: note: (near initialization for ‘seNvmInit.Crc32’) + 288 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:288:51: warning: excess elements in scalar initializer + 288 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:288:51: note: (near initialization for ‘seNvmInit.Crc32’) + 288 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:288:57: warning: excess elements in scalar initializer + 288 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:288:57: note: (near initialization for ‘seNvmInit.Crc32’) + 288 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:288:63: warning: excess elements in scalar initializer + 288 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:288:63: note: (near initialization for ‘seNvmInit.Crc32’) + 288 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:288:69: warning: excess elements in scalar initializer + 288 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:288:69: note: (near initialization for ‘seNvmInit.Crc32’) + 288 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:288:75: warning: excess elements in scalar initializer + 288 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:288:75: note: (near initialization for ‘seNvmInit.Crc32’) + 288 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:288:81: warning: excess elements in scalar initializer + 288 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:288:81: note: (near initialization for ‘seNvmInit.Crc32’) + 288 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:288:87: warning: excess elements in scalar initializer + 288 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:288:87: note: (near initialization for ‘seNvmInit.Crc32’) + 288 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:288:93: warning: excess elements in scalar initializer + 288 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:288:93: note: (near initialization for ‘seNvmInit.Crc32’) + 288 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:288:99: warning: excess elements in scalar initializer + 288 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:288:99: note: (near initialization for ‘seNvmInit.Crc32’) + 288 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:288:105: warning: excess elements in scalar initializer + 288 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:288:105: note: (near initialization for ‘seNvmInit.Crc32’) + 288 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:288:111: warning: excess elements in scalar initializer + 288 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:288:111: note: (near initialization for ‘seNvmInit.Crc32’) + 288 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:289:27: warning: excess elements in scalar initializer + 289 | 0x00 }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:289:27: note: (near initialization for ‘seNvmInit.Crc32’) + 289 | 0x00 }, \ + | ^~~~ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:288:25: warning: excess elements in scalar initializer + 288 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:288:25: note: (near initialization for ‘seNvmInit.Crc32’) + 288 | .KeyValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:283:9: warning: excess elements in scalar initializer + 283 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/se-identity.h:283:9: note: (near initialization for ‘seNvmInit.Crc32’) + 283 | { \ + | ^ +LoraMac/soft-se/soft-se.c:141:20: note: in expansion of macro ‘SOFT_SE_KEY_LIST’ + 141 | .KeyList = SOFT_SE_KEY_LIST + | ^~~~~~~~~~~~~~~~ +LoraMac/soft-se/soft-se.c: In function ‘SecureElementSetKey’: +LoraMac/soft-se/soft-se.c:172:29: error: ‘NUM_OF_KEYS’ undeclared (first use in this function) + 172 | for( uint8_t i = 0; i < NUM_OF_KEYS; i++ ) + | ^~~~~~~~~~~ +LoraMac/soft-se/soft-se.c:172:29: note: each undeclared identifier is reported only once for each function it appears in +LoraMac/soft-se/soft-se.c:174:18: error: ‘SecureElementNvmData_t’ {aka ‘struct sSecureElementNvCtx’} has no member named ‘KeyList’ + 174 | if( SeNvm->KeyList[i].KeyID == keyID ) + | ^~ +LoraMac/soft-se/soft-se.c:183:31: error: ‘SecureElementNvmData_t’ {aka ‘struct sSecureElementNvCtx’} has no member named ‘KeyList’ + 183 | memcpy1( SeNvm->KeyList[i].KeyValue, decryptedKey, SE_KEY_SIZE ); + | ^~ +LoraMac/soft-se/soft-se.c:188:31: error: ‘SecureElementNvmData_t’ {aka ‘struct sSecureElementNvCtx’} has no member named ‘KeyList’ + 188 | memcpy1( SeNvm->KeyList[i].KeyValue, key, SE_KEY_SIZE ); + | ^~ +LoraMac/soft-se/soft-se.c: In function ‘SecureElementAesEncrypt’: +LoraMac/soft-se/soft-se.c:250:5: error: unknown type name ‘Key_t’; did you mean ‘key_t’? + 250 | Key_t* pItem; + | ^~~~~ + | key_t +LoraMac/soft-se/soft-se.c:255:27: error: request for member ‘KeyValue’ in something not a structure or union + 255 | aes_set_key( pItem->KeyValue, 16, &aesContext ); + | ^~ +make: *** [Makefile:33: rest_server] Error 1 diff --git a/sysdrv/tools/board/lorawan-bridge/rest.c b/sysdrv/tools/board/lorawan-bridge/rest.c new file mode 100644 index 0000000000..a093e55039 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/rest.c @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2018 the CivetWeb developers + * Revisited version: Copyright (c) 2022 the CivetWeb developers + * MIT License + */ + +/* Simple demo of a REST callback. */ +#include +#include +#include +#include +#include +#include "cJSON.h" +#include "civetweb.h" +#include "send_json.h" +#include "lorawan_send.h" + +#define PORT "8089" +#define HOST_INFO "http://localhost:8089" + +#define LORAWAN_URI "/lorawan" +#define LORAWAN_SEND_URI "/lorawan/send" +#define EXIT_URI "/exit" + +int exitNow = 0; + +static unsigned request = 0; /* demo data: request counter */ + +static int +LorawanGET(struct mg_connection *conn, const char *p1) +{ + cJSON *obj = cJSON_CreateObject(); + + if (!obj) { + /* insufficient memory? */ + mg_send_http_error(conn, 500, "Server error"); + return 500; + } + + printf("GET lorawan/%s\n", p1); + cJSON_AddStringToObject(obj, "version", CIVETWEB_VERSION); + cJSON_AddStringToObject(obj, "modem_function", "lorawan"); + cJSON_AddStringToObject(obj, "operation", p1); + cJSON_AddNumberToObject(obj, "request", ++request); + SendJSON(conn, obj); + cJSON_Delete(obj); + + return 200; +} + + +static int +LorawanDELETE(struct mg_connection *conn, const char *p1, const char *p2) +{ + printf("DELETE %s/%s\n", p1, p2); + mg_send_http_error(conn, + 204, + "%s", + ""); /* Return "deleted" = "204 No Content" */ + + return 204; +} + + +static int +LorawanPUT(struct mg_connection *conn, const char *p1, const char *p2) +{ + char buffer[1024]; + int dlen = mg_read(conn, buffer, sizeof(buffer) - 1); + cJSON *obj, *elem; + unsigned newvalue; + + printf("PUT %s/%s\n", p1, p2); + if ((dlen < 1) || (dlen >= sizeof(buffer))) { + mg_send_http_error(conn, 400, "%s", "No request body data"); + return 400; + } + buffer[dlen] = 0; + + obj = cJSON_Parse(buffer); + if (obj == NULL) { + mg_send_http_error(conn, 400, "%s", "Invalid request body data"); + return 400; + } + + elem = cJSON_GetObjectItemCaseSensitive(obj, "request"); + + if (!cJSON_IsNumber(elem)) { + cJSON_Delete(obj); + mg_send_http_error(conn, + 400, + "%s", + "No \"request\" number in body data"); + return 400; + } + + newvalue = (unsigned)elem->valuedouble; + + if ((double)newvalue != elem->valuedouble) { + cJSON_Delete(obj); + mg_send_http_error(conn, + 400, + "%s", + "Invalid \"request\" number in body data"); + return 400; + } + + request = newvalue; + cJSON_Delete(obj); + + mg_send_http_error(conn, 201, "%s", ""); /* Return "201 Created" */ + + return 201; +} + +static int +LorawanHandler(struct mg_connection *conn, void *cbdata) +{ + char path1[1024], path2[1024]; + const struct mg_request_info *ri = mg_get_request_info(conn); + const char *url = ri->local_uri; + size_t url_len = strlen(url); + + struct mg_match_context mcx; + mcx.case_sensitive = 0; + ptrdiff_t ret = mg_match(LORAWAN_URI, url, &mcx); + if ((ret != url_len) || (mcx.num_matches != 1)) { + /* Note: Could have done this with a $ at the end of the match + * pattern as well. Then we would have to check for a return value + * of -1 only. Here we use this version as minimum modification + * of the existing code. */ + printf("Match ret: %i\n", (int)ret); + mg_send_http_error(conn, 404, "Invalid path: %s\n", url); + return 404; + } + memcpy(path1, mcx.match[0].str, mcx.match[0].len); + path1[mcx.match[0].len] = 0; + + (void)cbdata; /* currently unused */ + + /* According to method */ + if (0 == strcmp(ri->request_method, "GET")) { + return LorawanGET(conn, path1); + } + if ((0 == strcmp(ri->request_method, "PUT")) + || (0 == strcmp(ri->request_method, "POST")) + || (0 == strcmp(ri->request_method, "PATCH"))) { + /* In this example, do the same for PUT, POST and PATCH */ + return LorawanPUT(conn, path1, path2); + } + if (0 == strcmp(ri->request_method, "DELETE")) { + return LorawanDELETE(conn, path1, path2); + } + + /* this is not a GET request */ + mg_send_http_error( + conn, 405, "Only GET, PUT, POST, DELETE and PATCH method supported"); + return 405; +} + + +static int +ExitHandler(struct mg_connection *conn, void *cbdata) +{ + mg_printf(conn, + "HTTP/1.1 200 OK\r\nContent-Type: " + "text/plain\r\nConnection: close\r\n\r\n"); + mg_printf(conn, "Server will shut down.\n"); + mg_printf(conn, "Bye!\n"); + exitNow = 1; + return 1; +} + + +static int +log_message(const struct mg_connection *conn, const char *message) +{ + puts(message); + return 1; +} + +int +main(int argc, char *argv[]) +{ + const char *options[] = {"listening_ports", + PORT, + "request_timeout_ms", + "10000", + "error_log_file", + "error.log", + 0}; + + struct mg_callbacks callbacks; + struct mg_context *ctx; + int err = 0; + + /* Init libcivetweb. */ + mg_init_library(0); + + /* Callback will print error messages to console */ + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.log_message = log_message; + + /* Start CivetWeb web server */ + ctx = mg_start(&callbacks, 0, options); + + /* Check return value: */ + if (ctx == NULL) { + fprintf(stderr, "Cannot start CivetWeb - mg_start failed.\n"); + return EXIT_FAILURE; + } + + mg_set_request_handler(ctx, LORAWAN_SEND_URI, LorawanSendHandler, 0); + mg_set_request_handler(ctx, LORAWAN_URI, LorawanHandler, 0); + mg_set_request_handler(ctx, EXIT_URI, ExitHandler, 0); + + /* Show some info */ + printf("Start example: %s%s\n", HOST_INFO, LORAWAN_URI); + printf("Exit example: %s%s\n", HOST_INFO, EXIT_URI); + + /* Wait until the server should be closed */ + while (!exitNow) { + sleep(1); + } + + /* Stop the server */ + mg_stop(ctx); + + printf("Server stopped.\n"); + printf("Bye!\n"); + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/sysdrv/tools/board/lorawan-bridge/send_json.c b/sysdrv/tools/board/lorawan-bridge/send_json.c new file mode 100644 index 0000000000..228ceb77e2 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/send_json.c @@ -0,0 +1,26 @@ +#include +#include "cJSON.h" +#include "civetweb.h" +#include "send_json.h" + +int +SendJSON(struct mg_connection *conn, cJSON *json_obj) +{ + char *json_str = cJSON_PrintUnformatted(json_obj); + size_t json_str_len = strlen(json_str); + + /* Send HTTP message header (+1 for \n) */ + mg_send_http_ok(conn, "application/json; charset=utf-8", json_str_len + 1); + + /* Send HTTP message content */ + mg_write(conn, json_str, json_str_len); + + /* Add a newline. This is not required, but the result is more + * human-readable in a debugger. */ + mg_write(conn, "\n", 1); + + /* Free string allocated by cJSON_Print* */ + cJSON_free(json_str); + + return (int)json_str_len; +} \ No newline at end of file diff --git a/sysdrv/tools/board/lorawan-bridge/send_json.h b/sysdrv/tools/board/lorawan-bridge/send_json.h new file mode 100644 index 0000000000..542f8fd9fa --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/send_json.h @@ -0,0 +1 @@ +int SendJSON(struct mg_connection *conn, cJSON *json_obj); \ No newline at end of file diff --git a/sysdrv/tools/board/lorawan-bridge/sysdrv/tools/board/lorawan-bridge/b64.c b/sysdrv/tools/board/lorawan-bridge/sysdrv/tools/board/lorawan-bridge/b64.c new file mode 160000 index 0000000000..99adbb07d2 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/sysdrv/tools/board/lorawan-bridge/b64.c @@ -0,0 +1 @@ +Subproject commit 99adbb07d23ea01176f0eb43a3dcef602ccd3a8e diff --git a/sysdrv/tools/board/lorawan-bridge/test_cJSON.c b/sysdrv/tools/board/lorawan-bridge/test_cJSON.c new file mode 100644 index 0000000000..1836189e29 --- /dev/null +++ b/sysdrv/tools/board/lorawan-bridge/test_cJSON.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include +#include +#include "cJSON.h" +#include "b64.c/b64.h" + +char data[] = "{\n\ + \"port\": 1234,\n\ + \"payload64\": \"SGVsbG8gV29ybGQK\"\n\ +}"; + +int main() { + const cJSON *port = NULL; + const cJSON *payload = NULL; + cJSON *json = cJSON_Parse(data); + if (!json) { + printf("Cannot Decode JSON"); + } + //printf("%s",cJSON_Print(json)); + port = cJSON_GetObjectItemCaseSensitive(json, "port"); + if (cJSON_IsNumber(port)) + { + printf("Checking port \"%d\"\n", port->valueint); + } else { + printf("Cannot Decode"); + return -1; + } + payload = cJSON_GetObjectItemCaseSensitive(json, "payload64"); + if (cJSON_IsString(payload) && (payload->valuestring != NULL)) + { + printf("Checking Payload \"%s\"\n", payload->valuestring); + } else { + printf("Cannot Decode"); + return -1; + } + + // Decode from base64 + char *dec = b64_decode(payload->valuestring, strlen(payload->valuestring)); + + printf("%s\n", dec); // brian the monkey and bradley the kinkajou are friends + +} \ No newline at end of file