/*
 * Copyright (c) 2014-2016 IBM Corporation.
 * Copyright (c) 2016 Matthijs Kooijman.
 * Copyright (c) 2016-2020 MCCI Corporation.
 * All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *  * Neither the name of the <organization> nor the
 *    names of its contributors may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

//! @file
//! @brief LMIC API

#ifndef _lmic_h_
#define _lmic_h_

#include "oslmic.h"
#include "lorabase.h"

#if LMIC_DEBUG_LEVEL > 0 || LMIC_X_DEBUG_LEVEL > 0
# if defined(LMIC_DEBUG_INCLUDE)
#   define LMIC_STRINGIFY_(x) #x
#   define LMIC_STRINGIFY(x) LMIC_STRINGIFY_(x)
#   include LMIC_STRINGIFY(LMIC_DEBUG_INCLUDE)
# endif
#  ifdef LMIC_DEBUG_PRINTF_FN
     extern void LMIC_DEBUG_PRINTF_FN(const char *f, ...);
#  endif // ndef LMIC_DEBUG_PRINTF_FN
#endif

// if LMIC_DEBUG_PRINTF is now defined, just use it. This lets you do anything
// you like with a sufficiently crazy header file.
#if LMIC_DEBUG_LEVEL > 0
# ifndef LMIC_DEBUG_PRINTF
//  otherwise, check whether someone configured a print-function to be used,
//  and use it if so.
#   ifdef LMIC_DEBUG_PRINTF_FN
#     define LMIC_DEBUG_PRINTF(f, ...) LMIC_DEBUG_PRINTF_FN(f, ## __VA_ARGS__)
#     ifndef LMIC_DEBUG_INCLUDE // If you use LMIC_DEBUG_INCLUDE, put the declaration in there
        void LMIC_DEBUG_PRINTF_FN(const char *f, ...);
#     endif // ndef LMIC_DEBUG_INCLUDE
#   else // ndef LMIC_DEBUG_PRINTF_FN
//    if there's no other info, just use printf. In a pure Arduino environment,
//    that's what will happen.
#     include <stdio.h>
#     define LMIC_DEBUG_PRINTF(f, ...) printf(f, ## __VA_ARGS__)
#   endif // ndef LMIC_DEBUG_PRINTF_FN
# endif // ndef LMIC_DEBUG_PRINTF
# ifndef LMIC_DEBUG_FLUSH
#   ifdef LMIC_DEBUG_FLUSH_FN
#     define LMIC_DEBUG_FLUSH() LMIC_DEBUG_FLUSH_FN()
#   else // ndef LMIC_DEBUG_FLUSH_FN
//    if there's no other info, assume that flush is not needed.
#     define LMIC_DEBUG_FLUSH() do { ; } while (0)
#   endif // ndef LMIC_DEBUG_FLUSH_FN
# endif // ndef LMIC_DEBUG_FLUSH
#else // LMIC_DEBUG_LEVEL == 0
// If debug level is zero, printf and flush expand to nothing.
# define LMIC_DEBUG_PRINTF(f, ...)      do { ; } while (0)
# define LMIC_DEBUG_FLUSH()             do { ; } while (0)
#endif // LMIC_DEBUG_LEVEL == 0

//
// LMIC_X_DEBUG_LEVEL enables additional, special print functions for debugging
// RSSI features. This is used sparingly.
#if LMIC_X_DEBUG_LEVEL > 0
#  ifdef LMIC_DEBUG_PRINTF_FN
#    define LMIC_X_DEBUG_PRINTF(f, ...) LMIC_DEBUG_PRINTF_FN(f, ## __VA_ARGS__)
#  else
#    error "LMIC_DEBUG_PRINTF_FN must be defined for LMIC_X_DEBUG_LEVEL > 0."
#  endif
#else
#  define LMIC_X_DEBUG_PRINTF(f, ...)  do {;} while(0)
#endif

#ifdef __cplusplus
extern "C"{
#endif

// LMIC version -- this is ths IBM LMIC version
#define LMIC_VERSION_MAJOR 1
#define LMIC_VERSION_MINOR 6
#define LMIC_VERSION_BUILD 1468577746

// Arduino LMIC version
#define ARDUINO_LMIC_VERSION_CALC(major, minor, patch, local)	\
	((((major)*UINT32_C(1)) << 24) | (((minor)*UINT32_C(1)) << 16) | (((patch)*UINT32_C(1)) << 8) | (((local)*UINT32_C(1)) << 0))

#define	ARDUINO_LMIC_VERSION	ARDUINO_LMIC_VERSION_CALC(3, 2, 0, 0)	/* v3.2.0 */

#define	ARDUINO_LMIC_VERSION_GET_MAJOR(v)	\
	((((v)*UINT32_C(1)) >> 24u) & 0xFFu)

#define	ARDUINO_LMIC_VERSION_GET_MINOR(v)	\
	((((v)*UINT32_C(1)) >> 16u) & 0xFFu)

#define	ARDUINO_LMIC_VERSION_GET_PATCH(v)	\
	((((v)*UINT32_C(1)) >> 8u) & 0xFFu)

#define	ARDUINO_LMIC_VERSION_GET_LOCAL(v)	\
	((v) & 0xFFu)

//! Only For Antenna Tuning Tests !
//#define CFG_TxContinuousMode 1

// since this was annouunced as the API variable, we keep it. But it's not used,
// MAX_LEN_FRAME is what the code uses.
enum { MAX_FRAME_LEN      =  MAX_LEN_FRAME };   //!< Library cap on max frame length

enum { TXCONF_ATTEMPTS    =   8 };   //!< Transmit attempts for confirmed frames
enum { MAX_MISSED_BCNS    =  (2 * 60 * 60 + 127) / 128 };   //!< threshold for dropping out of class B, triggering rejoin requests
                                     // note that we need 100 ppm timing accuracy for
                                     // this, to keep the timing error to +/- 700ms.
enum { MAX_RXSYMS         = 350 };   // Stop tracking beacon if sync error grows beyond this. A 0.4% clock error
                                     // at SF9.125k means 512 ms; one sybol is 4.096 ms,
                                     // so this needs to be at least 125 for an STM32L0.
                                     // And for 100ppm clocks and 2 hours of beacon misses,
                                     // this needs to accomodate 1.4 seconds of error at
                                     // 4.096 ms/sym or at least 342 symbols.

enum { LINK_CHECK_CONT    =  0  ,    // continue with this after reported dead link
       LINK_CHECK_DEAD    =  32 ,    // after this UP frames and no response to ack from NWK assume link is dead (ADR_ACK_DELAY)
       LINK_CHECK_UNJOIN_MIN = LINK_CHECK_DEAD + 4,         // this is the minimum value of LINK_CHECK_UNJOIN if we parameterize
       LINK_CHECK_UNJOIN  =  LINK_CHECK_DEAD + (3 * 240),   // after this many UP frames and no response, switch to join (by default)
       LINK_CHECK_INIT    = -64 ,    // UP frame count until we ask for ack (ADR_ACK_LIMIT)
       LINK_CHECK_OFF     =-128 };   // link check disabled

enum { TIME_RESYNC        = 6*128 }; // secs
enum { TXRX_GUARD_ms      =  6000 };  // msecs - don't start TX-RX transaction before beacon
enum { JOIN_GUARD_ms      =  9000 };  // msecs - don't start Join Req/Acc transaction before beacon
enum { TXRX_BCNEXT_secs   =     2 };  // secs - earliest start after beacon time
enum { RETRY_PERIOD_secs  =     3 };  // secs - random period for retrying a confirmed send

#if CFG_LMIC_EU_like // EU868 spectrum ====================================================

enum { MAX_CHANNELS = 16 };      //!< Max supported channels
enum { MAX_BANDS    =  4 };

enum { LIMIT_CHANNELS = (1<<4) };   // EU868 will never have more channels
//! \internal
struct band_t {
    u2_t     txcap;     // duty cycle limitation: 1/txcap
    s1_t     txpow;     // maximum TX power
    u1_t     lastchnl;  // last used channel
    ostime_t avail;     // channel is blocked until this time
};
TYPEDEF_xref2band_t; //!< \internal

struct lmic_saved_adr_state_s {
    u4_t        channelFreq[MAX_CHANNELS];
    u2_t        channelMap;
};

#elif CFG_LMIC_US_like  // US915 spectrum =================================================

enum { MAX_XCHANNELS = 2 };      // extra channels in RAM, channels 0-71 are immutable

struct lmic_saved_adr_state_s {
    u2_t        channelMap[(72+MAX_XCHANNELS+15)/16];  // enabled bits
    u2_t        activeChannels125khz;
    u2_t        activeChannels500khz;
};

#endif // ==========================================================================

typedef struct lmic_saved_adr_state_s   lmic_saved_adr_state_t;

// Keep in sync with evdefs.hpp::drChange
enum { DRCHG_SET, DRCHG_NOJACC, DRCHG_NOACK, DRCHG_NOADRACK, DRCHG_NWKCMD, DRCHG_FRAMESIZE };
enum { KEEP_TXPOW = -128 };


#if !defined(DISABLE_PING)
//! \internal
struct rxsched_t {
    dr_t     dr;
    u1_t     intvExp;   // 0..7
    u1_t     slot;      // runs from 0 to 128
    rxsyms_t rxsyms;
    ostime_t rxbase;
    ostime_t rxtime;    // start of next spot
    u4_t     freq;
};
TYPEDEF_xref2rxsched_t;  //!< \internal
#endif // !DISABLE_PING


#if !defined(DISABLE_BEACONS)
//! Parsing and tracking states of beacons.
enum { BCN_NONE    = 0x00,   //!< No beacon received
       BCN_PARTIAL = 0x01,   //!< Only first (common) part could be decoded (info,lat,lon invalid/previous)
       BCN_FULL    = 0x02,   //!< Full beacon decoded
       BCN_NODRIFT = 0x04,   //!< No drift value measured yet
       BCN_NODDIFF = 0x08 }; //!< No differential drift measured yet
//! Information about the last and previous beacons.
struct bcninfo_t {
    ostime_t txtime;  //!< Time when the beacon was sent
    u4_t     time;    //!< GPS time in seconds of last beacon (received or surrogate)
    s4_t     lat;     //!< Lat field of last beacon (valid only if BCN_FULL set)
    s4_t     lon;     //!< Lon field of last beacon (valid only if BCN_FULL set)
    s1_t     rssi;    //!< Adjusted RSSI value of last received beacon
    s1_t     snr;     //!< Scaled SNR value of last received beacon
    u1_t     flags;   //!< Last beacon reception and tracking states. See BCN_* values.
    //
    u1_t     info;    //!< Info field of last beacon (valid only if BCN_FULL set)
};
#endif // !DISABLE_BEACONS

// purpose of receive window - lmic_t.rxState
enum { RADIO_RST=0, RADIO_TX=1, RADIO_RX=2, RADIO_RXON=3, RADIO_TX_AT=4, };
// Netid values /  lmic_t.netid
enum { NETID_NONE=(int)~0U, NETID_MASK=(int)0xFFFFFF };
// MAC operation modes (lmic_t.opmode).
enum { OP_NONE     = 0x0000,
       OP_SCAN     = 0x0001, // radio scan to find a beacon
       OP_TRACK    = 0x0002, // track my networks beacon (netid)
       OP_JOINING  = 0x0004, // device joining in progress (blocks other activities)
       OP_TXDATA   = 0x0008, // TX user data (buffered in pendTxData)
       OP_POLL     = 0x0010, // send empty UP frame to ACK confirmed DN/fetch more DN data
       OP_REJOIN   = 0x0020, // occasionally send JOIN REQUEST
       OP_SHUTDOWN = 0x0040, // prevent MAC from doing anything
       OP_TXRXPEND = 0x0080, // TX/RX transaction pending
       OP_RNDTX    = 0x0100, // prevent TX lining up after a beacon
       OP_PINGINI  = 0x0200, // pingable is initialized and scheduling active
       OP_PINGABLE = 0x0400, // we're pingable
       OP_NEXTCHNL = 0x0800, // find a new channel
       OP_LINKDEAD = 0x1000, // link was reported as dead
       OP_TESTMODE = 0x2000, // developer test mode
       OP_UNJOIN   = 0x4000, // unjoin and rejoin on next engineUpdate().
};
// TX-RX transaction flags - report back to user
enum { TXRX_ACK    = 0x80,   // confirmed UP frame was acked
       TXRX_NACK   = 0x40,   // confirmed UP frame was not acked
       TXRX_NOPORT = 0x20,   // set if a frame with a port was RXed, clr if no frame/no port
       TXRX_PORT   = 0x10,   // set if a frame with a port was RXed, LMIC.frame[LMIC.dataBeg-1] => port
       TXRX_LENERR = 0x08,   // set if frame was discarded due to length error.
       TXRX_PING   = 0x04,   // received in a scheduled RX slot
       TXRX_DNW2   = 0x02,   // received in 2dn DN slot
       TXRX_DNW1   = 0x01,   // received in 1st DN slot
};

// Event types for event callback
enum _ev_t { EV_SCAN_TIMEOUT=1, EV_BEACON_FOUND,
             EV_BEACON_MISSED, EV_BEACON_TRACKED, EV_JOINING,
             EV_JOINED, EV_RFU1, EV_JOIN_FAILED, EV_REJOIN_FAILED,
             EV_TXCOMPLETE, EV_LOST_TSYNC, EV_RESET,
             EV_RXCOMPLETE, EV_LINK_DEAD, EV_LINK_ALIVE, EV_SCAN_FOUND,
             EV_TXSTART, EV_TXCANCELED, EV_RXSTART, EV_JOIN_TXCOMPLETE };
typedef enum _ev_t ev_t;

// this macro can be used to initalize a normal table of event strings
#define LMIC_EVENT_NAME_TABLE__INIT                                         \
    "<<zero>>",                                                             \
    "EV_SCAN_TIMEOUT", "EV_BEACON_FOUND",                                   \
    "EV_BEACON_MISSED", "EV_BEACON_TRACKED", "EV_JOINING",                  \
    "EV_JOINED", "EV_RFU1", "EV_JOIN_FAILED", "EV_REJOIN_FAILED",           \
    "EV_TXCOMPLETE", "EV_LOST_TSYNC", "EV_RESET",                           \
    "EV_RXCOMPLETE", "EV_LINK_DEAD", "EV_LINK_ALIVE", "EV_SCAN_FOUND",      \
    "EV_TXSTART", "EV_TXCANCELED", "EV_RXSTART", "EV_JOIN_TXCOMPLETE"

// if working on an AVR (or worried about it), you can use this multi-zero
// string and put this in a single const F() string.  Index through this
// counting up from 0, until you get to the entry you want or to an
// entry that begins with a \0.
#define LMIC_EVENT_NAME_MULTISZ__INIT                                       \
    "<<zero>>\0"                                                            \
    "EV_SCAN_TIMEOUT\0" "EV_BEACON_FOUND\0"                                 \
    "EV_BEACON_MISSED\0" "EV_BEACON_TRACKED\0" "EV_JOINING\0"               \
    "EV_JOINED\0" "EV_RFU1\0" "EV_JOIN_FAILED\0" "EV_REJOIN_FAILED\0"       \
    "EV_TXCOMPLETE\0" "EV_LOST_TSYNC\0" "EV_RESET\0"                        \
    "EV_RXCOMPLETE\0" "EV_LINK_DEAD\0" "EV_LINK_ALIVE\0" "EV_SCAN_FOUND\0"  \
    "EV_TXSTART\0" "EV_TXCANCELED\0" "EV_RXSTART\0" "EV_JOIN_TXCOMPLETE\0"

enum {
    LMIC_ERROR_SUCCESS = 0,
    LMIC_ERROR_TX_BUSY = -1,
    LMIC_ERROR_TX_TOO_LARGE = -2,
    LMIC_ERROR_TX_NOT_FEASIBLE = -3,
    LMIC_ERROR_TX_FAILED = -4,
};

typedef int lmic_tx_error_t;

#define LMIC_ERROR_NAME__INIT                                               \
    "LMIC_ERROR_SUCCESS",                                                   \
    "LMIC_ERROR_TX_BUSY",                                                   \
    "LMIC_ERROR_TX_TOO_LARGE",                                              \
    "LMIC_ERROR_TX_NOT_FEASIBLE",                                           \
    "LMIC_ERROR_TX_FAILED"

#define LMIC_ERROR_NAME_MULTISZ__INIT                                       \
    "LMIC_ERROR_SUCCESS\0"                                                  \
    "LMIC_ERROR_TX_BUSY\0"                                                  \
    "LMIC_ERROR_TX_TOO_LARGE\0"                                             \
    "LMIC_ERROR_TX_NOT_FEASIBLE\0"                                          \
    "LMIC_ERROR_TX_FAILED"

enum {
    LMIC_BEACON_ERROR_INVALID   = -2,
    LMIC_BEACON_ERROR_WRONG_NETWORK = -1,
    LMIC_BEACON_ERROR_SUCCESS_PARTIAL = 0,
    LMIC_BEACON_ERROR_SUCCESS_FULL = 1,
};

typedef s1_t lmic_beacon_error_t;

static inline bit_t LMIC_BEACON_SUCCESSFUL(lmic_beacon_error_t e) {
    return e < 0;
}

// LMIC_CFG_max_clock_error_ppm
#if !defined(LMIC_CFG_max_clock_error_ppm)
# define LMIC_CFG_max_clock_error_ppm	2000	/* max clock error: 0.2% (2000 ppm) */
#endif


enum {
        // This value represents 100% error in LMIC.clockError
        MAX_CLOCK_ERROR = 65536,
        //! \brief maximum clock error that users can specify: 2000 ppm (0.2%).
        //! \details This is the limit for clock error, unless LMIC_ENABLE_arbitrary_clock_error is set.
        //! The default is 4,000 ppm, which is .004, or 0.4%; this is what you get on an
        //! STM32L0 running with the HSI oscillator after cal. If your clock error is bigger,
        //! usually you want to calibrate it so that millis() and micros() are reasonably
        //! accurate. Important: do not use clock error to compensate for late serving
        //! of the LMIC. If you see that LMIC.radio.rxlate_count is increasing, you need
        //! to adjust your application logic so the LMIC gets serviced promptly when a
        //! Class A downlink (or beacon) is pending.
        LMIC_kMaxClockError_ppm = 4000,
};

// callbacks for client alerts.
// types and functions are always defined, to reduce #ifs in example code and libraries.
typedef void LMIC_ABI_STD lmic_rxmessage_cb_t(void *pUserData, uint8_t port, const uint8_t *pMessage, size_t nMessage);
typedef void LMIC_ABI_STD lmic_txmessage_cb_t(void *pUserData, int fSuccess);
typedef void LMIC_ABI_STD lmic_event_cb_t(void *pUserData, ev_t e);

// network time request callback function
// defined unconditionally, because APIs and types can't change based on config.
// This is called when a time-request succeeds or when we get a downlink
// without time request, "completing" the pending time request.
typedef void LMIC_ABI_STD lmic_request_network_time_cb_t(void *pUserData, int flagSuccess);

// how the network represents time.
typedef u4_t lmic_gpstime_t;

// rather than deal with 1/256 second tick, we adjust ostime back
// (as it's high res) to match tNetwork.
typedef struct lmic_time_reference_s lmic_time_reference_t;

struct lmic_time_reference_s {
    // our best idea of when we sent the uplink (end of packet).
    ostime_t tLocal;
    // the network's best idea of when we sent the uplink.
    lmic_gpstime_t tNetwork;
};

enum lmic_request_time_state_e {
    lmic_RequestTimeState_idle = 0,     // we're not doing anything
    lmic_RequestTimeState_tx,           // we want to tx a time request on next uplink
    lmic_RequestTimeState_rx,           // we have tx'ed, next downlink completes.
    lmic_RequestTimeState_success       // we sucessfully got time.
};

typedef u1_t lmic_request_time_state_t;

enum lmic_engine_update_state_e {
    lmic_EngineUpdateState_idle = 0,    // engineUpdate is idle.
    lmic_EngineUpdateState_busy = 1,    // engineUpdate is busy, but has not been reentered.
    lmic_EngineUpdateState_again = 2,   // engineUpdate is busy, and has to be evaluated again.
};

typedef u1_t lmic_engine_update_state_t;

/*

Structure:  lmic_client_data_t

Function:
        Holds LMIC client data that must live through LMIC_reset().

Description:
        There are a variety of client registration linkage items that
        must live through LMIC_reset(), because LMIC_reset() is called
        at frame rollover time.  We group them together into a structure
        to make copies easy.

*/

//! abstract type for collection of client data that survives LMIC_reset().
typedef struct lmic_client_data_s lmic_client_data_t;

//! contents of lmic_client_data_t
struct lmic_client_data_s {

    /* pointer-width things come first */
#if LMIC_ENABLE_DeviceTimeReq
    lmic_request_network_time_cb_t *pNetworkTimeCb; //! call-back routine for network time
    void        *pNetworkTimeUserData;              //! call-back data for network time.
#endif

#if LMIC_ENABLE_user_events
    lmic_event_cb_t     *eventCb;           //! user-supplied callback function for events.
    void                *eventUserData;     //! data for eventCb
    lmic_rxmessage_cb_t *rxMessageCb;       //! user-supplied message-received callback
    void                *rxMessageUserData; //! data for rxMessageCb
    lmic_txmessage_cb_t *txMessageCb;       //! transmit-complete message handler; reset on each tx complete.
    void                *txMessageUserData; //! data for txMessageCb.
#endif // LMIC_ENABLE_user_events

    /* next we have things that are (u)int32_t */
    /* none at the moment */

    /* next we have things that are (u)int16_t */

    u2_t        clockError;                 //! Inaccuracy in the clock. CLOCK_ERROR_MAX represents +/-100% error

    /* finally, things that are (u)int8_t */
    /* none at the moment */
};

/*

Structure:  lmic_radio_data_t

Function:
    Holds LMIC radio driver.

Description:
    Eventually this will be used for all portable things for the radio driver,
    but for now it's where we can start to add things.

*/

typedef struct lmic_radio_data_s lmic_radio_data_t;

struct lmic_radio_data_s {
    // total os ticks of accumulated delay error. Can overflow!
    ostime_t    rxlate_ticks;
    // number of rx late launches.
    unsigned    rxlate_count;
    // total os ticks of accumulated tx delay error. Can overflow!
    ostime_t    txlate_ticks;
    // number of tx late launches.
    unsigned    txlate_count;
};

/*

Structure:  lmic_t

Function:
        Provides the instance data for the LMIC.

*/

struct lmic_t {
    // client setup data, survives LMIC_reset().
    lmic_client_data_t  client;

    // the OS job object. pointer alignment.
    osjob_t     osjob;

#if !defined(DISABLE_BEACONS)
    bcninfo_t   bcninfo;      // Last received beacon info
#endif

#if !defined(DISABLE_PING)
    rxsched_t   ping;         // pingable setup
#endif

    // the radio driver portable context
    lmic_radio_data_t   radio;

    /* (u)int32_t things */

    // Radio settings TX/RX (also accessed by HAL)
    ostime_t    txend;
    ostime_t    rxtime;

    // LBT info
    ostime_t    lbt_ticks;      // ticks to listen

    u4_t        freq;

    ostime_t    globalDutyAvail; // time device can send again

    u4_t        netid;        // current network id (~0 - none)
    devaddr_t   devaddr;
    u4_t        seqnoDn;      // device level down stream seqno
    u4_t        seqnoUp;
    u4_t        dn2Freq;

#if !defined(DISABLE_BEACONS)
    ostime_t    bcnRxtime;
#endif

#if LMIC_ENABLE_DeviceTimeReq
    // put here for alignment, to reduce RAM use.
    ostime_t    localDeviceTime;    // the LMIC.txend value for last DeviceTimeAns
    lmic_gpstime_t netDeviceTime;   // the netDeviceTime for lastDeviceTimeAns
                                    // zero ==> not valid.
#endif // LMIC_ENABLE_DeviceTimeReq

    // Channel scheduling -- very much private
#if CFG_LMIC_EU_like
    band_t      bands[MAX_BANDS];
    u4_t        channelFreq[MAX_CHANNELS];
#if !defined(DISABLE_MCMD_DlChannelReq)
    u4_t        channelDlFreq[MAX_CHANNELS];
#endif
    // bit map of enabled datarates for each channel
    u2_t        channelDrMap[MAX_CHANNELS];
    u2_t        channelMap;
#elif CFG_LMIC_US_like
    u4_t        xchFreq[MAX_XCHANNELS];    // extra channel frequencies (if device is behind a repeater)
    u2_t        xchDrMap[MAX_XCHANNELS];   // extra channel datarate ranges  ---XXX: ditto
    u2_t        channelMap[(72+MAX_XCHANNELS+15)/16];  // enabled bits
    u2_t        activeChannels125khz;
    u2_t        activeChannels500khz;
#endif

    /* (u)int16_t things */
    rps_t       rps;            // radio parameter selections: SF, BW, CodingRate, NoCrc, implicit hdr
    u2_t        opmode;         // engineUpdate() operating mode flags
    u2_t        devNonce;       // last generated nonce

    s2_t        adrAckReq;      // counter for link integrity tracking (LINK_CHECK_OFF=off)

#if !defined(DISABLE_BEACONS)
    s2_t        drift;          // last measured drift
    s2_t        lastDriftDiff;
    s2_t        maxDriftDiff;
    rxsyms_t    bcnRxsyms;      //
#endif

    /* (u)int8_t things */
    lmic_engine_update_state_t engineUpdateState;   // state of the engineUpdate() evaluator.
    s1_t        rssi;
    s1_t        snr;            // LMIC.snr is SNR times 4
    rxsyms_t    rxsyms;         // symbols for receive timeout.
    u1_t        dndr;
    s1_t        txpow;          // transmit dBm (administrative)
    s1_t        radio_txpow;    // the radio driver's copy of txpow, in dB limited by adrTxPow, and
				// also adjusted for EIRP/antenna gain considerations.
				// This is just the radio's idea of power. So if you are
				// controlling EIRP, and you have 3 dB antenna gain, this
				// needs to reduced by 3 dB.
    s1_t        lbt_dbmax;      // max permissible dB on our channel (eg -80)

    u1_t        txChnl;          // channel for next TX
    u1_t        globalDutyRate;  // max rate: 1/2^k

    u1_t        upRepeat;     // configured up repeat
    s1_t        adrTxPow;     // ADR adjusted TX power
    u1_t        datarate;     // current data rate
    u1_t        errcr;        // error coding rate (used for TX only)
    u1_t        rejoinCnt;    // adjustment for rejoin datarate

    u1_t        upRepeatCount;  // current up-repeat
    bit_t       initBandplanAfterReset; // cleared by LMIC_reset(), set by first join. See issue #244

    u1_t        pendTxPort;
    u1_t        pendTxConf;   // confirmed data
    u1_t        pendTxLen;    // count of bytes in pendTxData.
    u1_t        pendTxData[MAX_LEN_PAYLOAD];

    u1_t        pendMacLen;         // number of bytes of pending Mac response data
    bit_t       pendMacPiggyback;   // received on port 0 or piggyback?
    // response data if piggybacked
    u1_t        pendMacData[LWAN_FCtrl_FOptsLen_MAX];

    u1_t        nwkKey[16];   // network session key
    u1_t        artKey[16];   // application router session key

    u1_t        dnConf;       // dn frame confirm pending: LORA::FCT_ACK or 0
    u1_t        lastDnConf;   // downlink with seqnoDn-1 requested confirmation
    u1_t        adrChanged;

    u1_t        rxDelay;      // Rx delay after TX

    u1_t        margin;
    s1_t        devAnsMargin; // SNR value between -32 and 31 (inclusive) for the last successfully received DevStatusReq command
    u1_t        adrEnabled;
    u1_t        moreData;     // NWK has more data pending
#if LMIC_ENABLE_TxParamSetupReq
    u1_t        txParam;        // the saved TX param byte.
#endif
#if LMIC_ENABLE_DeviceTimeReq
    lmic_request_time_state_t txDeviceTimeReqState;  // current state, initially idle.
    u1_t        netDeviceTimeFrac;     // updated on any DeviceTimeAns.
#endif

    // rx1DrOffset is the offset from uplink to downlink datarate
    u1_t        rx1DrOffset;  // captured from join. zero by default.

    // 2nd RX window (after up stream)
    u1_t        dn2Dr;
#if !defined(DISABLE_MCMD_RXParamSetupReq)
    u1_t        dn2Ans;       // 0=no answer pend, 0x80+ACKs
#endif
#if !defined(DISABLE_MCMD_DlChannelReq)
    u1_t        macDlChannelAns;        // 0 ==> no answer pending, 0x80+ACK bits
#endif
#if !defined(DISABLE_MCMD_RXTimingSetupReq)
    bit_t       macRxTimingSetupAns;    // 0 ==> no answer pend, non-zero inserts response.
#endif

    // Class B state
#if !defined(DISABLE_BEACONS)
    u1_t        missedBcns;   // unable to track last N beacons
    u1_t        bcninfoTries; // how often to try (scan mode only)
#endif
    // Public part of MAC state
    u1_t        txCnt;
    u1_t        txrxFlags;  // transaction flags (TX-RX combo)
    u1_t        dataBeg;    // 0 or start of data (dataBeg-1 is port)
    u1_t        dataLen;    // 0 no data or zero length data, >0 byte count of data
    u1_t        frame[MAX_LEN_FRAME];

#if !defined(DISABLE_BEACONS)
    u1_t        bcnChnl;
#endif

    u1_t        noRXIQinversion;
    u1_t        saveIrqFlags;   // last LoRa IRQ flags
};

//! \var struct lmic_t LMIC
//! The state of LMIC MAC layer is encapsulated in this variable.
DECLARE_LMIC; //!< \internal

//! Construct a bit map of allowed datarates from drlo to drhi (both included).
#define DR_RANGE_MAP(drlo,drhi) (((u2_t)0xFFFF<<(drlo)) & ((u2_t)0xFFFF>>(15-(drhi))))
bit_t LMIC_setupBand (u1_t bandidx, s1_t txpow, u2_t txcap);
bit_t LMIC_setupChannel (u1_t channel, u4_t freq, u2_t drmap, s1_t band);
bit_t LMIC_disableChannel (u1_t channel);
bit_t LMIC_enableSubBand(u1_t band);
bit_t LMIC_enableChannel(u1_t channel);
bit_t LMIC_disableSubBand(u1_t band);
bit_t LMIC_selectSubBand(u1_t band);

void  LMIC_setDrTxpow   (dr_t dr, s1_t txpow);  // set default/start DR/txpow
void  LMIC_setAdrMode   (bit_t enabled);        // set ADR mode (if mobile turn off)

#if !defined(DISABLE_JOIN)
bit_t LMIC_startJoining (void);
void  LMIC_tryRejoin    (void);
void  LMIC_unjoin       (void);
void  LMIC_unjoinAndRejoin (void);
#endif

void  LMIC_shutdown     (void);
void  LMIC_init         (void);
void  LMIC_reset        (void);
void  LMIC_clrTxData    (void);
void  LMIC_setTxData    (void);
void  LMIC_setTxData_strict(void);
lmic_tx_error_t LMIC_setTxData2(u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed);
lmic_tx_error_t LMIC_setTxData2_strict(u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed);
lmic_tx_error_t LMIC_sendWithCallback(u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed, lmic_txmessage_cb_t *pCb, void *pUserData);
lmic_tx_error_t LMIC_sendWithCallback_strict(u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed, lmic_txmessage_cb_t *pCb, void *pUserData);
void  LMIC_sendAlive    (void);

#if !defined(DISABLE_BEACONS)
bit_t LMIC_enableTracking  (u1_t tryBcnInfo);
void  LMIC_disableTracking (void);
#endif

#if !defined(DISABLE_PING)
void  LMIC_stopPingable  (void);
void  LMIC_setPingable   (u1_t intvExp);
#endif

void LMIC_setSession (u4_t netid, devaddr_t devaddr, xref2u1_t nwkKey, xref2u1_t artKey);
void LMIC_setLinkCheckMode (bit_t enabled);
void LMIC_setClockError(u2_t error);

u4_t LMIC_getSeqnoUp    (void);
u4_t LMIC_setSeqnoUp    (u4_t);
void LMIC_getSessionKeys (u4_t *netid, devaddr_t *devaddr, xref2u1_t nwkKey, xref2u1_t artKey);

void LMIC_requestNetworkTime(lmic_request_network_time_cb_t *pCallbackfn, void *pUserData);
int LMIC_getNetworkTimeReference(lmic_time_reference_t *pReference);

int LMIC_registerRxMessageCb(lmic_rxmessage_cb_t *pRxMessageCb, void *pUserData);
int LMIC_registerEventCb(lmic_event_cb_t *pEventCb, void *pUserData);

// APIs for client half of compliance.
typedef u1_t lmic_compliance_rx_action_t;

enum lmic_compliance_rx_action_e {
    LMIC_COMPLIANCE_RX_ACTION_PROCESS = 0,  // process this message normally
    LMIC_COMPLIANCE_RX_ACTION_START,        // enter compliance mode, discard this message
    LMIC_COMPLIANCE_RX_ACTION_IGNORE,       // continue in compliance mode, discard this message
    LMIC_COMPLIANCE_RX_ACTION_END           // exit compliance mode, discard this message
};

lmic_compliance_rx_action_t LMIC_complianceRxMessage(u1_t port, const u1_t *pMessage, size_t nMessage);

// Declare onEvent() function, to make sure any definition will have the
// C conventions, even when in a C++ file.
#if LMIC_ENABLE_onEvent
DECL_ON_LMIC_EVENT;
#endif /* LMIC_ENABLE_onEvent */

// Special APIs - for development or testing
// !!!See implementation for caveats!!!

#ifdef __cplusplus
} // extern "C"
#endif

// names for backward compatibility
#include "lmic_compat.h"

#endif // _lmic_h_
