From f01b29a3b29bf1776ce7441ad25815d27703bd93 Mon Sep 17 00:00:00 2001
From: Manuel Bleichenbacher <manuel.bleichenbacher@gmail.com>
Date: Tue, 16 Apr 2019 21:36:03 +0200
Subject: [PATCH] Upgrade to version 2.3.2 of mcci-catena/arduino-lmic

---
 .gitignore                           |   1 +
 src/aes/lmic_aes.c                   |   3 -
 src/aes/other.c                      |   0
 src/hal/hal_esp32.cpp                |  19 ++-
 src/hal/hal_esp32.h                  |   4 +-
 src/lmic/config.h                    |   7 +
 src/lmic/hal.h                       |  47 +++---
 src/lmic/lmic.c                      | 104 ++++++++++++-
 src/lmic/lmic.h                      |  49 +++++-
 src/lmic/lmic_as923.c                |   0
 src/lmic/lmic_au921.c                |   0
 src/lmic/lmic_config_preconditions.h |  31 +++-
 src/lmic/lmic_env.h                  | 217 +++++++++++++++++++++++++++
 src/lmic/lmic_eu_like.c              |   0
 src/lmic/lmic_in866.c                |   0
 src/lmic/lmic_us915.c                |   0
 src/lmic/lmic_us_like.c              |   0
 src/lmic/lmic_util.h                 |   2 +-
 src/lmic/lorabase.h                  |   2 +
 src/lmic/oslmic.c                    |   0
 src/lmic/oslmic.h                    | 132 ++--------------
 src/lmic/oslmic_types.h              |  47 ++++++
 src/lmic/radio.c                     |  74 +++++----
 23 files changed, 539 insertions(+), 200 deletions(-)
 mode change 100644 => 100755 src/aes/other.c
 mode change 100644 => 100755 src/lmic/lmic.c
 mode change 100644 => 100755 src/lmic/lmic.h
 mode change 100644 => 100755 src/lmic/lmic_as923.c
 mode change 100644 => 100755 src/lmic/lmic_au921.c
 mode change 100644 => 100755 src/lmic/lmic_config_preconditions.h
 create mode 100755 src/lmic/lmic_env.h
 mode change 100644 => 100755 src/lmic/lmic_eu_like.c
 mode change 100644 => 100755 src/lmic/lmic_in866.c
 mode change 100644 => 100755 src/lmic/lmic_us915.c
 mode change 100644 => 100755 src/lmic/lmic_us_like.c
 mode change 100644 => 100755 src/lmic/lorabase.h
 mode change 100644 => 100755 src/lmic/oslmic.c
 mode change 100644 => 100755 src/lmic/oslmic.h
 create mode 100755 src/lmic/oslmic_types.h
 mode change 100644 => 100755 src/lmic/radio.c

diff --git a/.gitignore b/.gitignore
index 57ace6f..a13c3d2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,4 @@ sdkconfig
 sdkconfig.old
 dev_keys.txt
 ttn-esp32
+.vscode/
diff --git a/src/aes/lmic_aes.c b/src/aes/lmic_aes.c
index 40eb516..2a5bca3 100755
--- a/src/aes/lmic_aes.c
+++ b/src/aes/lmic_aes.c
@@ -262,9 +262,6 @@ u4_t os_aes (u1_t mode, xref2u1_t buf, u2_t len) {
             u4_t a0, a1, a2, a3;
             u4_t t0, t1, t2, t3;
             u4_t *ki, *ke;
-            // ttn-esp32 change: prevent error 'x' may be used uninitialized in this function
-            a0 = a1 = a2 = a3 = 0;
-            t0 = t1 = 0;
 
             // load input block
             if( (mode & AES_CTR) || ((mode & AES_MIC) && (mode & AES_MICNOAUX)==0) ) { // load CTR block or first MIC block
diff --git a/src/aes/other.c b/src/aes/other.c
old mode 100644
new mode 100755
diff --git a/src/hal/hal_esp32.cpp b/src/hal/hal_esp32.cpp
index 74b63e5..901ef9b 100755
--- a/src/hal/hal_esp32.cpp
+++ b/src/hal/hal_esp32.cpp
@@ -130,6 +130,17 @@ s1_t hal_getRssiCal (void)
     return lmic_pins.rssi_cal;
 }
 
+ostime_t hal_setModuleActive (bit_t val)
+{
+    return 0;
+}
+
+bit_t hal_queryUsingTcxo(void)
+{
+    return false;
+}
+
+
 // -----------------------------------------------------------------------------
 // SPI
 
@@ -152,12 +163,12 @@ void HAL_ESP32::spiInit()
     ESP_LOGI(TAG, "SPI initialized");
 }
 
-void hal_spi_write(u1_t cmd, const u1_t *buf, int len)
+void hal_spi_write(u1_t cmd, const u1_t *buf, size_t len)
 {
     ttn_hal.spiWrite(cmd, buf, len);
 }
 
-void HAL_ESP32::spiWrite(uint8_t cmd, const uint8_t *buf, int len)
+void HAL_ESP32::spiWrite(uint8_t cmd, const uint8_t *buf, size_t len)
 {
     memset(&spiTransaction, 0, sizeof(spiTransaction));
     spiTransaction.addr = cmd;
@@ -167,12 +178,12 @@ void HAL_ESP32::spiWrite(uint8_t cmd, const uint8_t *buf, int len)
     ESP_ERROR_CHECK(err);
 }
 
-void hal_spi_read(u1_t cmd, u1_t *buf, int len)
+void hal_spi_read(u1_t cmd, u1_t *buf, size_t len)
 {
     ttn_hal.spiRead(cmd, buf, len);
 }
 
-void HAL_ESP32::spiRead(uint8_t cmd, uint8_t *buf, int len)
+void HAL_ESP32::spiRead(uint8_t cmd, uint8_t *buf, size_t len)
 {
     memset(buf, 0, len);
     memset(&spiTransaction, 0, sizeof(spiTransaction));
diff --git a/src/hal/hal_esp32.h b/src/hal/hal_esp32.h
index 3dd5ad2..5a7a3c6 100644
--- a/src/hal/hal_esp32.h
+++ b/src/hal/hal_esp32.h
@@ -63,8 +63,8 @@ public:
     void initCriticalSection();
     void enterCriticalSection();
     void leaveCriticalSection();
-    void spiWrite(uint8_t cmd, const uint8_t *buf, int len);
-    void spiRead(uint8_t cmd, uint8_t *buf, int len);
+    void spiWrite(uint8_t cmd, const uint8_t *buf, size_t len);
+    void spiRead(uint8_t cmd, uint8_t *buf, size_t len);
     uint8_t checkTimer(uint32_t time);
     void sleep();
     void waitUntil(uint32_t time);
diff --git a/src/lmic/config.h b/src/lmic/config.h
index 1750907..253fd0a 100755
--- a/src/lmic/config.h
+++ b/src/lmic/config.h
@@ -171,4 +171,11 @@
 # endif // defined(LMIC_DISABLE_DR_LEGACY)
 #endif // LMIC_DR_LEGACY
 
+// LMIC_ENABLE_DeviceTimeReq
+// enable support for MCMD_DeviceTimeReq and MCMD_DeviceTimeAns
+// this is always defined, and non-zero to enable it.
+#if !defined(LMIC_ENABLE_DeviceTimeReq)
+# define LMIC_ENABLE_DeviceTimeReq 0
+#endif
+
 #endif // _lmic_config_h_
diff --git a/src/lmic/hal.h b/src/lmic/hal.h
index dab1f67..5b610f4 100755
--- a/src/lmic/hal.h
+++ b/src/lmic/hal.h
@@ -26,8 +26,12 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef _hal_hpp_
-#define _hal_hpp_
+#ifndef _lmic_hal_h_
+#define _lmic_hal_h_
+
+#ifndef _oslmic_types_h_
+# include "oslmic_types.h"
+#endif
 
 #ifdef __cplusplus
 extern "C"{
@@ -35,21 +39,20 @@ extern "C"{
 
 /*
  * initialize hardware (IO, SPI, TIMER, IRQ).
+ * This API is deprecated as it uses the const global lmic_pins,
+ * which the platform can't control or change.
  */
 void hal_init (void);
 
 /*
- * initialize hardware, passing in platform-specific context
+ * Initialize hardware, passing in platform-specific context
+ * The pointer is to a HalPinmap_t.
  */
 void hal_init_ex (const void *pContext);
 
 /*
- * drive radio NSS pin (0=low, 1=high).
- */
-void hal_pin_nss (u1_t val);
-
-/*
- * drive radio RX/TX pins (0=rx, 1=tx).
+ * drive radio RX/TX pins (0=rx, 1=tx). Actual polarity
+ * is determined by the value of HalPinmap_t::rxtx_rx_active.
  */
 void hal_pin_rxtx (u1_t val);
 
@@ -58,29 +61,19 @@ void hal_pin_rxtx (u1_t val);
  */
 void hal_pin_rst (u1_t val);
 
-// BEGIN ttn-esp32 change
-// use higher level SPI functions
 /*
  * Perform SPI write transaction with radio chip
  *   - write the command byte 'cmd'
  *   - write 'len' bytes out of 'buf'
  */
-void hal_spi_write(u1_t cmd, const u1_t* buf, int len);
+void hal_spi_write(u1_t cmd, const u1_t* buf, size_t len);
 
 /*
  * Perform SPI read transaction with radio chip
  *   - write the command byte 'cmd'
  *   - read 'len' bytes into 'buf'
  */
-void hal_spi_read(u1_t cmd, u1_t* buf, int len);
-
-/*
- * perform 8-bit SPI transaction with radio.
- *   - write given byte 'outval'
- *   - read byte and return value
- */
-//u1_t hal_spi (u1_t outval);
-// END ttn-esp32 change
+void hal_spi_read(u1_t cmd, u1_t* buf, size_t len);
 
 /*
  * disable all CPU interrupts.
@@ -128,8 +121,18 @@ void hal_failed (const char *file, u2_t line);
  */
 s1_t hal_getRssiCal (void);
 
+/*
+ * control the radio state
+ *   - if val == 0, turn tcxo off and otherwise prepare for sleep
+ *   - if val == 1, turn tcxo on and otherwise prep for activity
+ *   - return the number of ticks that we need to wait
+ */
+ostime_t hal_setModuleActive (bit_t val);
+
+bit_t hal_queryUsingTcxo(void);
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
 
-#endif // _hal_hpp_
+#endif // _lmic_hal_h_
diff --git a/src/lmic/lmic.c b/src/lmic/lmic.c
old mode 100644
new mode 100755
index 9f11d56..a7f253c
--- a/src/lmic/lmic.c
+++ b/src/lmic/lmic.c
@@ -713,6 +713,38 @@ scan_mac_cmds(
             oidx += 2;
             continue;
         } /* end case */
+        case MCMD_DeviceTimeAns: {
+#if LMIC_ENABLE_DeviceTimeReq
+            // don't process a spurious downlink.
+            if ( LMIC.txDeviceTimeReqState == lmic_RequestTimeState_rx ) {
+                // remember that it's time to notify the client.
+                LMIC.txDeviceTimeReqState = lmic_RequestTimeState_success;
+
+                // the network time is linked to the time of the last TX.
+                LMIC.localDeviceTime = LMIC.txend;
+
+                // save the network time.
+                // The first 4 bytes contain the seconds since the GPS epoch
+                // (i.e January the 6th 1980 at 00:00:00 UTC).
+                // Note: as per the LoRaWAN specs, the octet order for all
+                //       multi-octet fields is little endian
+                // Note: the casts are necessary, because opts is an array of
+                //       single byte values, and they might overflow when shifted
+                LMIC.netDeviceTime = ( (lmic_gpstime_t) opts[oidx + 1]       ) |
+                                     (((lmic_gpstime_t) opts[oidx + 2]) <<  8) |
+                                     (((lmic_gpstime_t) opts[oidx + 3]) << 16) |
+                                     (((lmic_gpstime_t) opts[oidx + 4]) << 24);
+
+                // The 5th byte contains the fractional seconds in 2^-8 second steps
+                LMIC.netDeviceTimeFrac = opts[oidx + 5];
+#if LMIC_DEBUG_LEVEL > 0
+                LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": MAC command DeviceTimeAns received: seconds_since_gps_epoch=%"PRIu32", fractional_seconds=%d\n", os_getTime(), LMIC.netDeviceTime, LMIC.netDeviceTimeFrac);
+#endif
+            }
+#endif // LMIC_ENABLE_DeviceTimeReq
+            oidx += 6;
+            continue;
+        } /* end case */
         } /* end switch */
         /* unrecognized mac commands fall out of switch to here */
         EV(specCond, ERR, (e_.reason = EV::specCond_t::BAD_MAC_CMD,
@@ -1139,7 +1171,8 @@ static bit_t processJoinAccept (void) {
         LMIC.datarate = AS923_DR_SF10;
 #endif
     }
-    LMIC.opmode &= ~(OP_JOINING|OP_TRACK|OP_REJOIN|OP_TXRXPEND|OP_PINGINI) | OP_NEXTCHNL;
+    LMIC.opmode &= ~(OP_JOINING|OP_TRACK|OP_REJOIN|OP_TXRXPEND|OP_PINGINI);
+    LMIC.opmode |= OP_NEXTCHNL;
     LMIC.txCnt = 0;
     stateJustJoined();
     LMIC.dn2Dr = LMIC.frame[OFF_JA_DLSET] & 0x0F;
@@ -1328,6 +1361,13 @@ static void buildDataFrame (void) {
         LMIC.txParamSetupAns = 0;
     }
 #endif
+#if LMIC_ENABLE_DeviceTimeReq
+    if ( LMIC.txDeviceTimeReqState == lmic_RequestTimeState_tx ) {
+        LMIC.frame[end+0] = MCMD_DeviceTimeReq;
+        end += 1;
+        LMIC.txDeviceTimeReqState = lmic_RequestTimeState_rx;
+    }
+#endif // LMIC_ENABLE_DeviceTimeReq
     ASSERT(end <= OFF_DAT_OPTS+16);
 
     u1_t flen = end + (txdata ? 5+dlen : 4);
@@ -1587,6 +1627,24 @@ static bit_t processDnData (void) {
         LMIC.dataBeg = LMIC.dataLen = 0;
       txcomplete:
         LMIC.opmode &= ~(OP_TXDATA|OP_TXRXPEND);
+
+#if LMIC_ENABLE_DeviceTimeReq
+        lmic_request_time_state_t const requestTimeState = LMIC.txDeviceTimeReqState;
+        if ( requestTimeState != lmic_RequestTimeState_idle ) {
+            lmic_request_network_time_cb_t * const pNetworkTimeCb = LMIC.pNetworkTimeCb;
+            int flagSuccess = (LMIC.txDeviceTimeReqState == lmic_RequestTimeState_success);
+            LMIC.txDeviceTimeReqState = lmic_RequestTimeState_idle;
+            if (pNetworkTimeCb != NULL) {
+                // reset the callback, so that the user's routine
+                // can post another request if desired.
+                LMIC.pNetworkTimeCb = NULL;
+
+                // call the user's notification routine.
+                (*pNetworkTimeCb)(LMIC.pNetworkTimeUserData, flagSuccess);
+            }
+        }
+#endif // LMIC_ENABLE_DeviceTimeReq
+
         if( (LMIC.txrxFlags & (TXRX_DNW1|TXRX_DNW2|TXRX_PING)) != 0  &&  (LMIC.opmode & OP_LINKDEAD) != 0 ) {
             LMIC.opmode &= ~OP_LINKDEAD;
             reportEvent(EV_LINK_ALIVE);
@@ -1728,12 +1786,11 @@ static void engineUpdate (void) {
 #endif // !DISABLE_JOIN
 
     ostime_t now    = os_getTime();
-    ostime_t rxtime = 0;
     ostime_t txbeg  = 0;
-    // ttn-esp32: suppress unused variable
-    LMIC_UNREFERENCED_VARIABLE(rxtime);
 
 #if !defined(DISABLE_BEACONS)
+    ostime_t rxtime = 0;
+
     if( (LMIC.opmode & OP_TRACK) != 0 ) {
         // We are tracking a beacon
         ASSERT( now + RX_RAMPUP - LMIC.bcnRxtime <= 0 );
@@ -1929,6 +1986,11 @@ void LMIC_reset (void) {
     DO_DEVDB(LMIC.ping.dr,      pingDr);
     DO_DEVDB(LMIC.ping.intvExp, pingIntvExp);
 #endif // !DISABLE_PING
+#if LMIC_ENABLE_DeviceTimeReq
+    LMIC.txDeviceTimeReqState = lmic_RequestTimeState_idle;
+    LMIC.netDeviceTime = 0;     // the "invalid" time.
+    LMIC.netDeviceTimeFrac = 0;
+#endif // LMIC_ENABLE_DeviceTimeReq
 }
 
 
@@ -1970,7 +2032,6 @@ int LMIC_setTxData2 (u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed) {
     return 0;
 }
 
-
 // Send a payload-less message to signal device is alive
 void LMIC_sendAlive (void) {
     LMIC.opmode |= OP_POLL;
@@ -2068,3 +2129,36 @@ void LMIC_getSessionKeys (u4_t *netid, devaddr_t *devaddr, xref2u1_t nwkKey, xre
     memcpy(artKey, LMIC.artKey, sizeof(LMIC.artKey));
     memcpy(nwkKey, LMIC.nwkKey, sizeof(LMIC.nwkKey));
 }
+
+// \brief post an asynchronous request for the network time.
+void LMIC_requestNetworkTime(lmic_request_network_time_cb_t *pCallbackfn, void *pUserData) {
+#if LMIC_ENABLE_DeviceTimeReq
+    if (LMIC.txDeviceTimeReqState == lmic_RequestTimeState_idle) {
+        LMIC.txDeviceTimeReqState = lmic_RequestTimeState_tx;
+        LMIC.pNetworkTimeCb = pCallbackfn;
+        LMIC.pNetworkTimeUserData = pUserData;
+        return;
+    }
+#endif // LMIC_ENABLE_DeviceTimeReq
+    // if no device time support, or if not in proper state,
+    // report a failure.
+    if (pCallbackfn != NULL)
+        (*pCallbackfn)(pUserData, /* false */ 0);
+}
+
+// \brief return local/remote time pair (if valid, and DeviceTimeReq enabled),
+// return true for success, false for error. We adjust the sampled OS time
+// back in time to the nearest second boundary.
+int LMIC_getNetworkTimeReference(lmic_time_reference_t *pReference) {
+#if LMIC_ENABLE_DeviceTimeReq
+    if (pReference != NULL &&       // valid parameter, and
+        LMIC.netDeviceTime != 0) {  // ... we have a reasonable answer.
+        const ostime_t tAdjust = LMIC.netDeviceTimeFrac * ms2osticks(1000) / 256;
+
+        pReference->tLocal = LMIC.localDeviceTime - tAdjust;
+        pReference->tNetwork = LMIC.netDeviceTime;
+        return 1;
+    }
+#endif // LMIC_ENABLE_DeviceTimeReq
+    return 0;
+}
diff --git a/src/lmic/lmic.h b/src/lmic/lmic.h
old mode 100644
new mode 100755
index fa27d96..331d5a3
--- a/src/lmic/lmic.h
+++ b/src/lmic/lmic.h
@@ -1,7 +1,7 @@
 /*
  * Copyright (c) 2014-2016 IBM Corporation.
  * Copyright (c) 2016 Matthijs Kooijman.
- * Copyright (c) 2016-2018 MCCI Corporation.
+ * Copyright (c) 2016-2019 MCCI Corporation.
  * All rights reserved.
  *
  *  Redistribution and use in source and binary forms, with or without
@@ -105,7 +105,7 @@ extern "C"{
 #define ARDUINO_LMIC_VERSION_CALC(major, minor, patch, local)	\
 	(((major) << 24u) | ((minor) << 16u) | ((patch) << 8u) | (local))
 
-#define	ARDUINO_LMIC_VERSION	ARDUINO_LMIC_VERSION_CALC(2, 2, 2, 0)
+#define	ARDUINO_LMIC_VERSION	ARDUINO_LMIC_VERSION_CALC(2, 3, 2, 0)	/* v2.3.2 */
 
 #define	ARDUINO_LMIC_VERSION_GET_MAJOR(v)	\
 	(((v) >> 24u) & 0xFFu)
@@ -243,6 +243,35 @@ enum {
         MAX_CLOCK_ERROR = 65536,
 };
 
+// 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;
+
 struct lmic_t {
     // Radio settings TX/RX (also accessed by HAL)
     ostime_t    txend;
@@ -306,6 +335,14 @@ struct lmic_t {
     devaddr_t   devaddr;
     u4_t        seqnoDn;      // device level down stream seqno
     u4_t        seqnoUp;
+#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.
+    lmic_request_network_time_cb_t *pNetworkTimeCb;	// call-back routine
+    void        *pNetworkTimeUserData; // call-back data
+#endif // LMIC_ENABLE_DeviceTimeReq
 
     u1_t        dnConf;       // dn frame confirm pending: LORA::FCT_ACK or 0
     s1_t        adrAckReq;    // counter until we reset data rate (0=off)
@@ -329,6 +366,10 @@ struct lmic_t {
     bit_t       txParamSetupAns; // transmit setup answer pending.
     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.
@@ -368,6 +409,7 @@ struct lmic_t {
 
     u1_t        noRXIQinversion;
 };
+
 //! \var struct lmic_t LMIC
 //! The state of LMIC MAC layer is encapsulated in this variable.
 DECLARE_LMIC; //!< \internal
@@ -417,6 +459,9 @@ 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);
+
 // Declare onEvent() function, to make sure any definition will have the
 // C conventions, even when in a C++ file.
 DECL_ON_LMIC_EVENT;
diff --git a/src/lmic/lmic_as923.c b/src/lmic/lmic_as923.c
old mode 100644
new mode 100755
diff --git a/src/lmic/lmic_au921.c b/src/lmic/lmic_au921.c
old mode 100644
new mode 100755
diff --git a/src/lmic/lmic_config_preconditions.h b/src/lmic/lmic_config_preconditions.h
old mode 100644
new mode 100755
index 6f5bbcc..49be1d5
--- a/src/lmic/lmic_config_preconditions.h
+++ b/src/lmic/lmic_config_preconditions.h
@@ -114,6 +114,12 @@ Revision history:
 // following values. These are in order of the sections in the manual. Not all of the
 // below are supported yet.
 //
+// CFG_as923jp is treated as a special case of CFG_as923, so it's not included in
+// the below.
+//
+// TODO(tmm@mcci.com) consider moving this block to a central file as it's not
+// user-editable.
+//
 # define CFG_LMIC_REGION_MASK   \
                         ((defined(CFG_eu868) << LMIC_REGION_eu868) | \
                          (defined(CFG_us915) << LMIC_REGION_us915) | \
@@ -127,6 +133,8 @@ Revision history:
                          0)
 
 // the selected region.
+// TODO(tmm@mcci.com) consider moving this block to a central file as it's not
+// user-editable.
 #if defined(CFG_eu868)
 # define CFG_region     LMIC_REGION_eu868
 #elif defined(CFG_us915)
@@ -139,6 +147,10 @@ Revision history:
 # define CFG_region     LMIC_REGION_au921
 #elif defined(CFG_cn490)
 # define CFG_region     LMIC_REGION_cn490
+#elif defined(CFG_as923jp)
+# define CFG_as923	1			/* CFG_as923jp implies CFG_as923 */
+# define CFG_region     LMIC_REGION_as923
+# define LMIC_COUNTRY_CODE  LMIC_COUNTRY_CODE_JP
 #elif defined(CFG_as923)
 # define CFG_region     LMIC_REGION_as923
 #elif defined(CFG_kr921)
@@ -149,7 +161,11 @@ Revision history:
 # define CFG_region     0
 #endif
 
-// finally the mask of` US-like and EU-like regions
+// a bitmask of EU-like regions -- these are regions which have up to 16
+// channels indidually programmable via downloink.
+//
+// TODO(tmm@mcci.com) consider moving this block to a central file as it's not
+// user-editable.
 #define CFG_LMIC_EU_like_MASK   (                               \
                                 (1 << LMIC_REGION_eu868) |      \
                              /* (1 << LMIC_REGION_us915) | */   \
@@ -162,6 +178,12 @@ Revision history:
                                 (1 << LMIC_REGION_in866) |      \
                                 0)
 
+// a bitmask of` US-like regions -- these are regions with 64 fixed 125 kHz channels
+// overlaid by 8 500 kHz channels. The channel frequencies can't be changed, but
+// subsets of channels can be selected via masks.
+//
+// TODO(tmm@mcci.com) consider moving this block to a central file as it's not
+// user-editable.
 #define CFG_LMIC_US_like_MASK   (                               \
                              /* (1 << LMIC_REGION_eu868) | */   \
                                 (1 << LMIC_REGION_us915) |      \
@@ -174,9 +196,12 @@ Revision history:
                              /* (1 << LMIC_REGION_in866) | */   \
                                 0)
 
+//
+// booleans that are true if the configured region is EU-like or US-like.
+// TODO(tmm@mcci.com) consider moving this block to a central file as it's not
+// user-editable.
+//
 #define CFG_LMIC_EU_like        (!!(CFG_LMIC_REGION_MASK & CFG_LMIC_EU_like_MASK))
 #define CFG_LMIC_US_like        (!!(CFG_LMIC_REGION_MASK & CFG_LMIC_US_like_MASK))
 
-
-
 #endif /* _LMIC_CONFIG_PRECONDITIONS_H_ */
diff --git a/src/lmic/lmic_env.h b/src/lmic/lmic_env.h
new file mode 100755
index 0000000..4610fb3
--- /dev/null
+++ b/src/lmic/lmic_env.h
@@ -0,0 +1,217 @@
+/*
+
+Module:  lmic_env.h
+
+Function:
+	Sets up macros etc. to make things a little easier for portabilty
+
+Copyright notice and license info:
+	See LICENSE file accompanying this project.
+ 
+Author:
+	Terry Moore, MCCI Corporation	November 2018
+
+Description:
+	This file is an adaptation of MCCI's standard IOCTL framework.
+	We duplicate a bit of functionality that we might get from other
+	libraries, so that the LMIC library can continue to stand alone.
+
+*/
+
+#ifndef _lmic_env_h_	/* prevent multiple includes */
+#define _lmic_env_h_
+
+/*
+
+Macro:	LMIC_C_ASSERT()
+
+Function:
+	Declaration-like macro that will cause a compile error if arg is FALSE.
+
+Definition:
+	LMIC_C_ASSERT(
+		BOOL fErrorIfFalse
+		);
+
+Description:
+	This macro, if used where an external reference declarataion is
+	permitted, will either compile cleanly, or will cause a compilation
+	error. The results of using this macro where a declaration is not
+	permitted are unspecified.
+
+	This is different from #if !(fErrorIfFalse) / #error in that the 
+	expression is evaluated by the compiler rather than by the pre-
+	processor. Therefore things like sizeof() can be used.
+
+Returns:
+	No explicit result -- either compiles cleanly or causes a compile
+	error.
+
+*/
+
+#ifndef LMIC_C_ASSERT
+# define LMIC_C_ASSERT(e)	\
+ void  LMIC_C_ASSERT__(int LMIC_C_ASSERT_x[(e) ? 1: -1])
+#endif
+
+/****************************************************************************\
+|
+|	Define the begin/end declaration tags for C++ co-existance
+|
+\****************************************************************************/
+
+#ifdef __cplusplus
+# define LMIC_BEGIN_DECLS	extern "C" {
+# define LMIC_END_DECLS	}
+#else
+# define LMIC_BEGIN_DECLS	/* nothing */
+# define LMIC_END_DECLS	/* nothing */
+#endif
+
+//----------------------------------------------------------------------------
+// Annotations to avoid various "unused" warnings. These must appear as a
+// statement in the function body; the macro annotates the variable to quiet
+// compiler warnings.  The way this is done is compiler-specific, and so these
+// definitions are fall-backs, which might be overridden.
+//
+// Although these are all similar, we don't want extra macro expansions,
+// so we define each one explicitly rather than relying on a common macro.
+//----------------------------------------------------------------------------
+
+// signal that a parameter is intentionally unused.
+#ifndef LMIC_UNREFERENCED_PARAMETER
+# define LMIC_UNREFERENCED_PARAMETER(v)      do { (void) (v); } while (0)
+#endif
+
+// an API parameter is a parameter that is required by an API definition, but
+// happens to be unreferenced in this implementation. This is a stronger
+// assertion than LMIC_UNREFERENCED_PARAMETER(): this parameter is here
+// becuase of an API contract, but we have no use for it in this function.
+#ifndef LMIC_API_PARAMETER
+# define LMIC_API_PARAMETER(v)               do { (void) (v); } while (0)
+#endif
+
+// an intentionally-unreferenced variable.
+#ifndef LMIC_UNREFERENCED_VARIABLE
+# define LMIC_UNREFERENCED_VARIABLE(v)       do { (void) (v); } while (0)
+#endif
+
+// we have three (!) debug levels (LMIC_DEBUG_LEVEL > 0, LMIC_DEBUG_LEVEL > 1,
+// and LMIC_X_DEBUG_LEVEL > 0. In each case we might have parameters or
+// or varables that are only refereneced at the target debug level.
+
+// Parameter referenced only if debugging at level > 0.
+#ifndef LMIC_DEBUG1_PARAMETER
+# if LMIC_DEBUG_LEVEL > 0
+#  define LMIC_DEBUG1_PARAMETER(v)           do { ; } while (0)
+# else
+#  define LMIC_DEBUG1_PARAMETER(v)           do { (void) (v); } while (0)
+# endif
+#endif
+
+// variable referenced only if debugging at level > 0
+#ifndef LMIC_DEBUG1_VARIABLE
+# if LMIC_DEBUG_LEVEL > 0
+#  define LMIC_DEBUG1_VARIABLE(v)            do { ; } while (0)
+# else
+#  define LMIC_DEBUG1_VARIABLE(v)            do { (void) (v); } while (0)
+# endif
+#endif
+
+// parameter referenced only if debugging at level > 1
+#ifndef LMIC_DEBUG2_PARAMETER
+# if LMIC_DEBUG_LEVEL > 1
+#  define LMIC_DEBUG2_PARAMETER(v)           do { ; } while (0)
+# else
+#  define LMIC_DEBUG2_PARAMETER(v)           do { (void) (v); } while (0)
+# endif
+#endif
+
+// variable referenced only if debugging at level > 1
+#ifndef LMIC_DEBUG2_VARIABLE
+# if LMIC_DEBUG_LEVEL > 1
+#  define LMIC_DEBUG2_VARIABLE(v)            do { ; } while (0)
+# else
+#  define LMIC_DEBUG2_VARIABLE(v)            do { (void) (v); } while (0)
+# endif
+#endif
+
+// parameter referenced only if LMIC_X_DEBUG_LEVEL > 0
+#ifndef LMIC_X_DEBUG_PARAMETER
+# if LMIC_X_DEBUG_LEVEL > 0
+#  define LMIC_X_DEBUG_PARAMETER(v)           do { ; } while (0)
+# else
+#  define LMIC_X_DEBUG_PARAMETER(v)           do { (void) (v); } while (0)
+# endif
+#endif
+
+// variable referenced only if LMIC_X_DEBUG_LEVEL > 0
+#ifndef LMIC_X_DEBUG_VARIABLE
+# if LMIC_X_DEBUG_LEVEL > 0
+#  define LMIC_X_DEBUG_VARIABLE(v)            do { ; } while (0)
+# else
+#  define LMIC_X_DEBUG_VARIABLE(v)            do { (void) (v); } while (0)
+# endif
+#endif
+
+// parameter referenced only if EV() macro is enabled (which it never is)
+// TODO(tmm@mcci.com) take out the EV() framework as it reuqires C++, and
+// this code is really C-99 to its bones.
+#ifndef LMIC_EV_PARAMETER
+# define LMIC_EV_PARAMETER(v)                 do { (void) (v); } while (0)
+#endif
+
+// variable referenced only if EV() macro is defined.
+#ifndef LMIC_EV_VARIABLE
+# define LMIC_EV_VARIABLE(v)                  do { (void) (v); } while (0)
+#endif
+
+/*
+
+Macro:	LMIC_ABI_STD
+
+Index:	Macro:	LMIC_ABI_VARARGS
+
+Function:
+	Annotation macros to force a particular binary calling sequence.
+
+Definition:
+	#define LMIC_ABI_STD		compiler-specific
+	#define	LMIC_ABI_VARARGS 	compiler-specific
+
+Description:
+	These macros are used when declaring a function type, and indicate
+	that a particular calling sequence is to be used. They are normally
+	used between the type portion of the function declaration and the
+	name of the function.  For example:
+
+	typedef void LMIC_ABI_STD myCallBack_t(void);
+
+	It's important to use this in libraries on platforms with multiple
+	calling sequences, because different components can be compiled with
+	different defaults.
+
+Returns:
+	Not applicable.
+
+*/
+
+/* ABI marker for normal (fixed parameter count) functions -- used for function types */
+#ifndef LMIC_ABI_STD
+# ifdef _MSC_VER
+#  define LMIC_ABI_STD	__stdcall
+# else
+#  define LMIC_ABI_STD	/* nothing */
+# endif
+#endif
+
+/* ABI marker for VARARG functions -- used for function types */
+#ifndef LMIC_ABI_VARARGS
+# ifdef _MSC_VER
+#  define LMIC_ABI_VARARGS	__cdecl
+# else
+#  define LMIC_ABI_VARARGS	/* nothing */
+# endif
+#endif
+
+#endif /* _lmic_env_h_ */
\ No newline at end of file
diff --git a/src/lmic/lmic_eu_like.c b/src/lmic/lmic_eu_like.c
old mode 100644
new mode 100755
diff --git a/src/lmic/lmic_in866.c b/src/lmic/lmic_in866.c
old mode 100644
new mode 100755
diff --git a/src/lmic/lmic_us915.c b/src/lmic/lmic_us915.c
old mode 100644
new mode 100755
diff --git a/src/lmic/lmic_us_like.c b/src/lmic/lmic_us_like.c
old mode 100644
new mode 100755
diff --git a/src/lmic/lmic_util.h b/src/lmic/lmic_util.h
index d99217d..7e1b3c8 100755
--- a/src/lmic/lmic_util.h
+++ b/src/lmic/lmic_util.h
@@ -9,7 +9,7 @@ Copyright & License:
         See accompanying LICENSE file.
 
 Author:
-        Terry Moore, MCCI       September 2019
+        Terry Moore, MCCI       September 2018
 
 */
 
diff --git a/src/lmic/lorabase.h b/src/lmic/lorabase.h
old mode 100644
new mode 100755
index a4e570f..656ea2c
--- a/src/lmic/lorabase.h
+++ b/src/lmic/lorabase.h
@@ -449,6 +449,7 @@ enum {
     MCMD_RXTimingSetupAns = 0x08,       //         : -
     MCMD_TxParamSetupAns = 0x09,        //         : -
     MCMD_DIChannelAns = 0x0A,           //         : u1: [7-2]:RFU 1:exists 0:OK
+    MCMD_DeviceTimeReq = 0x0D,
 
     // Class B
     MCMD_PING_IND = 0x10, // -  pingability indic  : u1: 7=RFU, 6-4:interval, 3-0:datarate
@@ -468,6 +469,7 @@ enum {
     MCMD_RXTimingSetupReq = 0x08,       //      : u1: [7-4]:RFU [3-0]: Delay 1-15s (0 => 1)
     MCMD_TxParamSetupReq = 0x09,        //      : u1: [7-6]:RFU [5:4]: dl dwell/ul dwell [3:0] max EIRP
     MCMD_DIChannelReq = 0x0A,           //      : u1: channel, u3: frequency
+    MCMD_DeviceTimeAns = 0x0D,
 
     // Class B
     MCMD_PING_SET = 0x11, // set ping freq      : u3: freq
diff --git a/src/lmic/oslmic.c b/src/lmic/oslmic.c
old mode 100644
new mode 100755
diff --git a/src/lmic/oslmic.h b/src/lmic/oslmic.h
old mode 100644
new mode 100755
index b4f10d1..7710c1d
--- a/src/lmic/oslmic.h
+++ b/src/lmic/oslmic.h
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2014-2016 IBM Corporation.
+ * Copyright (c) 2018 MCCI Corporation
  * All rights reserved.
  *
  *  Redistribution and use in source and binary forms, with or without
@@ -29,29 +30,22 @@
 #ifndef _oslmic_h_
 #define _oslmic_h_
 
-// Dependencies required for the LoRa MAC in C to run.
+// Dependencies required for the LMIC to run.
 // These settings can be adapted to the underlying system.
-// You should not, however, change the lmic.[hc]
+// You should not, however, change the lmic merely for porting purposes.[hc]
 
 #include "config.h"
-#include <stdint.h>
 
-#ifdef __cplusplus
-extern "C"{
+#ifndef _lmic_env_h_
+# include "lmic_env.h"
 #endif
 
-//================================================================================
-//================================================================================
-// Target platform as C library
-typedef uint8_t            bit_t;
-typedef uint8_t            u1_t;
-typedef int8_t             s1_t;
-typedef uint16_t           u2_t;
-typedef int16_t            s2_t;
-typedef uint32_t           u4_t;
-typedef int32_t            s4_t;
-typedef unsigned int       uint;
-typedef const char* str_t;
+#ifndef _oslmic_types_h_
+# include "oslmic_types.h"
+#endif
+
+LMIC_BEGIN_DECLS
+
 
 #include <string.h>
 #include "hal.h"
@@ -73,7 +67,6 @@ typedef   struct rxsched_t rxsched_t;
 typedef   struct bcninfo_t bcninfo_t;
 typedef        const u1_t* xref2cu1_t;
 typedef              u1_t* xref2u1_t;
-typedef              s4_t  ostime_t;
 
 // int32_t == s4_t is long on some platforms; and someday
 // we will want 64-bit ostime_t. So, we will use a macro for the
@@ -91,105 +84,6 @@ typedef              s4_t  ostime_t;
 
 #define SIZEOFEXPR(x) sizeof(x)
 
-//----------------------------------------------------------------------------
-// Annotations to avoid various "unused" warnings. These must appear as a
-// statement in the function body; the macro annotates the variable to quiet
-// compiler warnings.  The way this is done is compiler-specific, and so these
-// definitions are fall-backs, which might be overridden.
-//
-// Although these are all similar, we don't want extra macro expansions,
-// so we define each one explicitly rather than relying on a common macro.
-//----------------------------------------------------------------------------
-
-// signal that a parameter is intentionally unused.
-#ifndef LMIC_UNREFERENCED_PARAMETER
-# define LMIC_UNREFERENCED_PARAMETER(v)      do { (void) (v); } while (0)
-#endif
-
-// an API parameter is a parameter that is required by an API definition, but
-// happens to be unreferenced in this implementation. This is a stronger
-// assertion than LMIC_UNREFERENCED_PARAMETER(): this parameter is here
-// becuase of an API contract, but we have no use for it in this function.
-#ifndef LMIC_API_PARAMETER
-# define LMIC_API_PARAMETER(v)               do { (void) (v); } while (0)
-#endif
-
-// an intentionally-unreferenced variable.
-#ifndef LMIC_UNREFERENCED_VARIABLE
-# define LMIC_UNREFERENCED_VARIABLE(v)       do { (void) (v); } while (0)
-#endif
-
-// we have three (!) debug levels (LMIC_DEBUG_LEVEL > 0, LMIC_DEBUG_LEVEL > 1,
-// and LMIC_X_DEBUG_LEVEL > 0. In each case we might have parameters or
-// or varables that are only refereneced at the target debug level.
-
-// Parameter referenced only if debugging at level > 0.
-#ifndef LMIC_DEBUG1_PARAMETER
-# if LMIC_DEBUG_LEVEL > 0
-#  define LMIC_DEBUG1_PARAMETER(v)           do { ; } while (0)
-# else
-#  define LMIC_DEBUG1_PARAMETER(v)           do { (void) (v); } while (0)
-# endif
-#endif
-
-// variable referenced only if debugging at level > 0
-#ifndef LMIC_DEBUG1_VARIABLE
-# if LMIC_DEBUG_LEVEL > 0
-#  define LMIC_DEBUG1_VARIABLE(v)            do { ; } while (0)
-# else
-#  define LMIC_DEBUG1_VARIABLE(v)            do { (void) (v); } while (0)
-# endif
-#endif
-
-// parameter referenced only if debugging at level > 1
-#ifndef LMIC_DEBUG2_PARAMETER
-# if LMIC_DEBUG_LEVEL > 1
-#  define LMIC_DEBUG2_PARAMETER(v)           do { ; } while (0)
-# else
-#  define LMIC_DEBUG2_PARAMETER(v)           do { (void) (v); } while (0)
-# endif
-#endif
-
-// variable referenced only if debugging at level > 1
-#ifndef LMIC_DEBUG2_VARIABLE
-# if LMIC_DEBUG_LEVEL > 1
-#  define LMIC_DEBUG2_VARIABLE(v)            do { ; } while (0)
-# else
-#  define LMIC_DEBUG2_VARIABLE(v)            do { (void) (v); } while (0)
-# endif
-#endif
-
-// parameter referenced only if LMIC_X_DEBUG_LEVEL > 0
-#ifndef LMIC_X_DEBUG_PARAMETER
-# if LMIC_X_DEBUG_LEVEL > 0
-#  define LMIC_X_DEBUG_PARAMETER(v)           do { ; } while (0)
-# else
-#  define LMIC_X_DEBUG_PARAMETER(v)           do { (void) (v); } while (0)
-# endif
-#endif
-
-// variable referenced only if LMIC_X_DEBUG_LEVEL > 0
-#ifndef LMIC_X_DEBUG_VARIABLE
-# if LMIC_X_DEBUG_LEVEL > 0
-#  define LMIC_X_DEBUG_VARIABLE(v)            do { ; } while (0)
-# else
-#  define LMIC_X_DEBUG_VARIABLE(v)            do { (void) (v); } while (0)
-# endif
-#endif
-
-// parameter referenced only if EV() macro is enabled (which it never is)
-// TODO(tmm@mcci.com) take out the EV() framework as it reuqires C++, and
-// this code is really C-99 to its bones.
-#ifndef LMIC_EV_PARAMETER
-# define LMIC_EV_PARAMETER(v)                 do { (void) (v); } while (0)
-#endif
-
-// variable referenced only if EV() macro is defined.
-#ifndef LMIC_EV_VARIABLE
-# define LMIC_EV_VARIABLE(v)                  do { (void) (v); } while (0)
-#endif
-
-
 #define ON_LMIC_EVENT(ev)  onEvent(ev)
 #define DECL_ON_LMIC_EVENT void onEvent(ev_t e)
 
@@ -416,8 +310,6 @@ extern xref2u1_t AESaux;
 u4_t os_aes (u1_t mode, xref2u1_t buf, u2_t len);
 #endif
 
-#ifdef __cplusplus
-} // extern "C"
-#endif
+LMIC_END_DECLS
 
 #endif // _oslmic_h_
diff --git a/src/lmic/oslmic_types.h b/src/lmic/oslmic_types.h
new file mode 100755
index 0000000..d790a37
--- /dev/null
+++ b/src/lmic/oslmic_types.h
@@ -0,0 +1,47 @@
+/*
+
+Module:  oslmic_types.h
+
+Function:
+        Basic types from oslmic.h, shared by all layers.
+
+Copyright & License:
+        See accompanying LICENSE file.
+
+Author:
+        Terry Moore, MCCI       November 2018
+        (based on oslmic.h from IBM).
+
+*/
+
+#ifndef _oslmic_types_h_
+# define _oslmic_types_h_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//================================================================================
+//================================================================================
+// Target platform as C library
+typedef uint8_t            bit_t;
+typedef uint8_t            u1_t;
+typedef int8_t             s1_t;
+typedef uint16_t           u2_t;
+typedef int16_t            s2_t;
+typedef uint32_t           u4_t;
+typedef int32_t            s4_t;
+typedef unsigned int       uint;
+typedef const char*        str_t;
+
+// the HAL needs to give us ticks, so it ought to know the right type.
+typedef              s4_t  ostime_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+/* end of oslmic_types.h */
+#endif /* _oslmic_types_h_ */
diff --git a/src/lmic/radio.c b/src/lmic/radio.c
old mode 100644
new mode 100755
index 8f943ac..5320a1c
--- a/src/lmic/radio.c
+++ b/src/lmic/radio.c
@@ -135,13 +135,22 @@
 // #define RegAgcThresh2                              0x45 // common
 // #define RegAgcThresh3                              0x46 // common
 // #define RegPllHop                                  0x4B // common
-#define RegPaDac                                   0x4D // common
 // #define RegTcxo                                    0x58 // common
 // #define RegPll                                     0x5C // common
 // #define RegPllLowPn                                0x5E // common
 // #define RegFormerTemp                              0x6C // common
 // #define RegBitRateFrac                             0x70 // common
 
+#if defined(CFG_sx1276_radio)
+#define RegTcxo                                    0x4B // common
+#define RegPaDac                                   0x4D // common
+#elif defined(CFG_sx1272_radio)
+#define RegTcxo                                    0x58 // common
+#define RegPaDac                                   0x5A // common
+#endif
+
+#define RegTcxo_TcxoInputOn                        (1u << 4)
+
 // ----------------------------------------
 // spread factors and mode for RegModemConfig2
 #define SX1272_MC2_FSK  0x00
@@ -290,58 +299,41 @@ static u1_t randbuf[16];
 
 
 static void writeReg (u1_t addr, u1_t data ) {
-    // ttn-esp32 change: higher level SPI interface
     hal_spi_write(addr | 0x80, &data, 1);
-    /*
-    hal_pin_nss(0);
-    hal_spi(addr | 0x80);
-    hal_spi(data);
-    hal_pin_nss(1);
-    */
 }
 
 static u1_t readReg (u1_t addr) {
-    // ttn-esp32 change: higher level SPI interface
     u1_t buf[1];
     hal_spi_read(addr & 0x7f, buf, 1);
     return buf[0];
-    /*
-    hal_pin_nss(0);
-    hal_spi(addr & 0x7F);
-    u1_t val = hal_spi(0x00);
-    hal_pin_nss(1);
-    return val;
-    */
 }
 
 static void writeBuf (u1_t addr, xref2u1_t buf, u1_t len) {
-    // ttn-esp32 change: higher level SPI interface
     hal_spi_write(addr | 0x80, buf, len);
-    /*
-    hal_pin_nss(0);
-    hal_spi(addr | 0x80);
-    for (u1_t i=0; i<len; i++) {
-        hal_spi(buf[i]);
-    }
-    hal_pin_nss(1);
-    */
 }
 
 static void readBuf (u1_t addr, xref2u1_t buf, u1_t len) {
-    // ttn-esp32 change: higher level SPI interface
     hal_spi_read(addr & 0x7f, buf, len);
-    /*
-    hal_pin_nss(0);
-    hal_spi(addr & 0x7F);
-    for (u1_t i=0; i<len; i++) {
-        buf[i] = hal_spi(0x00);
-    }
-    hal_pin_nss(1);
-    */
+}
+
+static void requestModuleActive(bit_t state) {
+    ostime_t const ticks = hal_setModuleActive(state);
+
+    if (ticks)
+        hal_waitUntil(os_getTime() + ticks);;
+}
+
+static void writeOpmode(u1_t mode) {
+    u1_t const maskedMode = mode & OPMODE_MASK;
+    if (maskedMode != OPMODE_SLEEP)
+        requestModuleActive(1);
+    writeReg(RegOpMode, mode);
+    if (maskedMode == OPMODE_SLEEP)
+        requestModuleActive(0);
 }
 
 static void opmode (u1_t mode) {
-    writeReg(RegOpMode, (readReg(RegOpMode) & ~OPMODE_MASK) | mode);
+    writeOpmode((readReg(RegOpMode) & ~OPMODE_MASK) | mode);
 }
 
 static void opmodeLora() {
@@ -349,7 +341,7 @@ static void opmodeLora() {
 #ifdef CFG_sx1276_radio
     u |= 0x8;   // TBD: sx1276 high freq
 #endif
-    writeReg(RegOpMode, u);
+    writeOpmode(u);
 }
 
 static void opmodeFSK() {
@@ -357,7 +349,7 @@ static void opmodeFSK() {
 #ifdef CFG_sx1276_radio
     u |= 0x8;   // TBD: sx1276 high freq
 #endif
-    writeReg(RegOpMode, u);
+    writeOpmode(u);
 }
 
 // configure LoRa modem (cfg1, cfg2)
@@ -483,7 +475,7 @@ static void configPower () {
 
 static void txfsk () {
     // select FSK modem (from sleep mode)
-    writeReg(RegOpMode, 0x10); // FSK, BT=0.5
+    writeOpmode(0x10); // FSK, BT=0.5
     ASSERT(readReg(RegOpMode) == 0x10);
     // enter standby mode (required for FIFO loading))
     opmode(OPMODE_STANDBY);
@@ -765,6 +757,8 @@ static void startrx (u1_t rxmode) {
 int radio_init () {
     hal_disableIRQs();
 
+    requestModuleActive(1);
+
     // manually reset radio
 #ifdef CFG_sx1276_radio
     hal_pin_rst(0); // drive RST pin low
@@ -788,6 +782,10 @@ int radio_init () {
 #else
 #error Missing CFG_sx1272_radio/CFG_sx1276_radio
 #endif
+    // set the tcxo input, if needed
+    if (hal_queryUsingTcxo())
+        writeReg(RegTcxo, readReg(RegTcxo) | RegTcxo_TcxoInputOn);
+
     // seed 15-byte randomness via noise rssi
     rxlora(RXMODE_RSSI);
     while( (readReg(RegOpMode) & OPMODE_MASK) != OPMODE_RX ); // continuous rx