LMiC LoRa Semtech + Nucleo

Dependents:   LoRaWAN-lmic-app

Fork of LMiC by Pascal Nysten

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers lmic.cpp Source File

lmic.cpp

Go to the documentation of this file.
00001 /*******************************************************************************
00002  * Copyright (c) 2014-2015 IBM Corporation.
00003  * All rights reserved. This program and the accompanying materials
00004  * are made available under the terms of the Eclipse Public License v1.0
00005  * which accompanies this distribution, and is available at
00006  * http://www.eclipse.org/legal/epl-v10.html
00007  *
00008  * Contributors:
00009  *    IBM Zurich Research Lab - initial API, implementation and documentation
00010  *******************************************************************************/
00011 
00012 //! \file
00013 
00014 
00015 #include "lmic.h"
00016 #include "debug.h"
00017 
00018 #if !defined(MINRX_SYMS)
00019 #define MINRX_SYMS 5
00020 #endif // !defined(MINRX_SYMS)
00021 #define PAMBL_SYMS 8
00022 #define PAMBL_FSK  5
00023 #define PRERX_FSK  1
00024 #define RXLEN_FSK  (1+5+2)
00025 
00026 #define BCN_INTV_osticks       sec2osticks(BCN_INTV_sec)
00027 #define TXRX_GUARD_osticks     ms2osticks(TXRX_GUARD_ms)
00028 #define JOIN_GUARD_osticks     ms2osticks(JOIN_GUARD_ms)
00029 #define DELAY_DNW1_osticks     sec2osticks(DELAY_DNW1)
00030 #define DELAY_DNW2_osticks     sec2osticks(DELAY_DNW2)
00031 #define DELAY_JACC1_osticks    sec2osticks(DELAY_JACC1)
00032 #define DELAY_JACC2_osticks    sec2osticks(DELAY_JACC2)
00033 #define DELAY_EXTDNW2_osticks  sec2osticks(DELAY_EXTDNW2)
00034 #define BCN_RESERVE_osticks    ms2osticks(BCN_RESERVE_ms)
00035 #define BCN_GUARD_osticks      ms2osticks(BCN_GUARD_ms)
00036 #define BCN_WINDOW_osticks     ms2osticks(BCN_WINDOW_ms)
00037 #define AIRTIME_BCN_osticks    us2osticks(AIRTIME_BCN)
00038 #if defined(CFG_eu868)
00039 #define DNW2_SAFETY_ZONE       ms2osticks(3000)
00040 #endif
00041 #if defined(CFG_us915)
00042 #define DNW2_SAFETY_ZONE       ms2osticks(750)
00043 #endif
00044 
00045 // Special APIs - for development or testing
00046 #define isTESTMODE() 0
00047 
00048 DEFINE_LMIC;
00049 DECL_ON_LMIC_EVENT;
00050 
00051 
00052 // Fwd decls.
00053 static void engineUpdate(void);
00054 static void startScan (void);
00055 
00056 
00057 // ================================================================================
00058 // BEG OS - default implementations for certain OS suport functions
00059 
00060 #if !defined(HAS_os_calls)
00061 
00062 #if !defined(os_rlsbf2)
00063 u2_t os_rlsbf2 (xref2cu1_t buf) {
00064     return (u2_t)(buf[0] | (buf[1]<<8));
00065 }
00066 #endif
00067 
00068 #if !defined(os_rlsbf4)
00069 u4_t os_rlsbf4 (xref2cu1_t buf) {
00070     return (u4_t)(buf[0] | (buf[1]<<8) | ((u4_t)buf[2]<<16) | ((u4_t)buf[3]<<24));
00071 }
00072 #endif
00073 
00074 
00075 #if !defined(os_rmsbf4)
00076 u4_t os_rmsbf4 (xref2cu1_t buf) {
00077     return (u4_t)(buf[3] | (buf[2]<<8) | ((u4_t)buf[1]<<16) | ((u4_t)buf[0]<<24));
00078 }
00079 #endif
00080 
00081 
00082 #if !defined(os_wlsbf2)
00083 void os_wlsbf2 (xref2u1_t buf, u2_t v) {
00084     buf[0] = v;
00085     buf[1] = v>>8;
00086 }
00087 #endif
00088 
00089 #if !defined(os_wlsbf4)
00090 void os_wlsbf4 (xref2u1_t buf, u4_t v) {
00091     buf[0] = v;
00092     buf[1] = v>>8;
00093     buf[2] = v>>16;
00094     buf[3] = v>>24;
00095 }
00096 #endif
00097 
00098 #if !defined(os_wmsbf4)
00099 void os_wmsbf4 (xref2u1_t buf, u4_t v) {
00100     buf[3] = v;
00101     buf[2] = v>>8;
00102     buf[1] = v>>16;
00103     buf[0] = v>>24;
00104 }
00105 #endif
00106 
00107 #if !defined(os_getBattLevel)
00108 u1_t os_getBattLevel (void) {
00109     return MCMD_DEVS_BATT_NOINFO;
00110 }
00111 #endif
00112 
00113 #if !defined(os_crc16)
00114 // New CRC-16 CCITT(XMODEM) checksum for beacons:
00115 u2_t os_crc16 (xref2u1_t data, uint len) {
00116     u2_t remainder = 0;
00117     u2_t polynomial = 0x1021;
00118     for( uint i = 0; i < len; i++ ) {
00119         remainder ^= data[i] << 8;
00120         for( u1_t bit = 8; bit > 0; bit--) {
00121             if( (remainder & 0x8000) )
00122                 remainder = (remainder << 1) ^ polynomial;
00123             else 
00124                 remainder <<= 1;
00125         }
00126     }
00127     return remainder;
00128 }
00129 #endif
00130 
00131 #endif // !HAS_os_calls
00132 
00133 // END OS - default implementations for certain OS suport functions
00134 // ================================================================================
00135 
00136 // ================================================================================
00137 // BEG AES
00138 
00139 static void micB0 (u4_t devaddr, u4_t seqno, int dndir, int len) {
00140     os_clearMem(AESaux,16);
00141     AESaux[0]  = 0x49;
00142     AESaux[5]  = dndir?1:0;
00143     AESaux[15] = len;
00144     os_wlsbf4(AESaux+ 6,devaddr);
00145     os_wlsbf4(AESaux+10,seqno);
00146 }
00147 
00148 
00149 static int aes_verifyMic (xref2cu1_t key, u4_t devaddr, u4_t seqno, int dndir, xref2u1_t pdu, int len) {
00150     micB0(devaddr, seqno, dndir, len);
00151     os_copyMem(AESkey,key,16);
00152     return os_aes(AES_MIC, pdu, len) == os_rmsbf4(pdu+len);
00153 }
00154 
00155 
00156 static void aes_appendMic (xref2cu1_t key, u4_t devaddr, u4_t seqno, int dndir, xref2u1_t pdu, int len) {
00157     micB0(devaddr, seqno, dndir, len);
00158     os_copyMem(AESkey,key,16);
00159     // MSB because of internal structure of AES
00160     os_wmsbf4(pdu+len, os_aes(AES_MIC, pdu, len));
00161 }
00162 
00163 
00164 static void aes_appendMic0 (xref2u1_t pdu, int len) {
00165     os_getDevKey(AESkey);
00166     os_wmsbf4(pdu+len, os_aes(AES_MIC|AES_MICNOAUX, pdu, len));  // MSB because of internal structure of AES
00167 }
00168 
00169 
00170 static int aes_verifyMic0 (xref2u1_t pdu, int len) {
00171     os_getDevKey(AESkey);
00172     return os_aes(AES_MIC|AES_MICNOAUX, pdu, len) == os_rmsbf4(pdu+len);
00173 }
00174 
00175 
00176 static void aes_encrypt (xref2u1_t pdu, int len) {
00177     os_getDevKey(AESkey);
00178     os_aes(AES_ENC, pdu, len);
00179 }
00180 
00181 
00182 static void aes_cipher (xref2cu1_t key, u4_t devaddr, u4_t seqno, int dndir, xref2u1_t payload, int len) {
00183     if( len <= 0 )
00184         return;
00185     os_clearMem(AESaux, 16);
00186     AESaux[0] = AESaux[15] = 1; // mode=cipher / dir=down / block counter=1
00187     AESaux[5] = dndir?1:0;
00188     os_wlsbf4(AESaux+ 6,devaddr);
00189     os_wlsbf4(AESaux+10,seqno);
00190     os_copyMem(AESkey,key,16);
00191     os_aes(AES_CTR, payload, len);
00192 }
00193 
00194 
00195 static void aes_sessKeys (u2_t devnonce, xref2cu1_t artnonce, xref2u1_t nwkkey, xref2u1_t artkey) {
00196     os_clearMem(nwkkey, 16);
00197     nwkkey[0] = 0x01;
00198     os_copyMem(nwkkey+1, artnonce, LEN_ARTNONCE+LEN_NETID);
00199     os_wlsbf2(nwkkey+1+LEN_ARTNONCE+LEN_NETID, devnonce);
00200     os_copyMem(artkey, nwkkey, 16);
00201     artkey[0] = 0x02;
00202 
00203     os_getDevKey(AESkey);
00204     os_aes(AES_ENC, nwkkey, 16);
00205     os_getDevKey(AESkey);
00206     os_aes(AES_ENC, artkey, 16);
00207 }
00208 
00209 // END AES
00210 // ================================================================================
00211 
00212 
00213 // ================================================================================
00214 // BEG LORA
00215 
00216 #if defined(CFG_eu868) // ========================================
00217 
00218 #define maxFrameLen(dr) ((dr)<=DR_SF9 ? maxFrameLens[(dr)] : 0xFF)
00219 const u1_t maxFrameLens [] = { 64,64,64,123 };
00220 
00221 const u1_t _DR2RPS_CRC[] = {
00222     ILLEGAL_RPS,
00223     (u1_t)MAKERPS(SF12, BW125, CR_4_5, 0, 0),
00224     (u1_t)MAKERPS(SF11, BW125, CR_4_5, 0, 0),
00225     (u1_t)MAKERPS(SF10, BW125, CR_4_5, 0, 0),
00226     (u1_t)MAKERPS(SF9,  BW125, CR_4_5, 0, 0),
00227     (u1_t)MAKERPS(SF8,  BW125, CR_4_5, 0, 0),
00228     (u1_t)MAKERPS(SF7,  BW125, CR_4_5, 0, 0),
00229     (u1_t)MAKERPS(SF7,  BW250, CR_4_5, 0, 0),
00230     (u1_t)MAKERPS(FSK,  BW125, CR_4_5, 0, 0),
00231     ILLEGAL_RPS
00232 };
00233 
00234 static const s1_t TXPOWLEVELS[] = {
00235     20, 14, 11, 8, 5, 2, 0,0, 0,0,0,0, 0,0,0,0
00236 };
00237 #define pow2dBm(mcmd_ladr_p1) (TXPOWLEVELS[(mcmd_ladr_p1&MCMD_LADR_POW_MASK)>>MCMD_LADR_POW_SHIFT])
00238 
00239 #elif defined(CFG_us915) // ========================================
00240 
00241 #define maxFrameLen(dr) ((dr)<=DR_SF11CR ? maxFrameLens[(dr)] : 0xFF)
00242 const u1_t maxFrameLens [] = { 24,66,142,255,255,255,255,255,  66,142 };
00243 
00244 const u1_t _DR2RPS_CRC[] = {
00245     ILLEGAL_RPS,
00246     MAKERPS(SF10, BW125, CR_4_5, 0, 0),
00247     MAKERPS(SF9 , BW125, CR_4_5, 0, 0),
00248     MAKERPS(SF8 , BW125, CR_4_5, 0, 0),
00249     MAKERPS(SF7 , BW125, CR_4_5, 0, 0),
00250     MAKERPS(SF8 , BW500, CR_4_5, 0, 0),
00251     ILLEGAL_RPS ,
00252     ILLEGAL_RPS ,
00253     ILLEGAL_RPS ,
00254     MAKERPS(SF12, BW500, CR_4_5, 0, 0),
00255     MAKERPS(SF11, BW500, CR_4_5, 0, 0),
00256     MAKERPS(SF10, BW500, CR_4_5, 0, 0),
00257     MAKERPS(SF9 , BW500, CR_4_5, 0, 0),
00258     MAKERPS(SF8 , BW500, CR_4_5, 0, 0),
00259     MAKERPS(SF7 , BW500, CR_4_5, 0, 0),
00260     ILLEGAL_RPS
00261 };
00262 
00263 #define pow2dBm(mcmd_ladr_p1) ((s1_t)(30 - (((mcmd_ladr_p1)&MCMD_LADR_POW_MASK)<<1)))
00264 
00265 #endif // ================================================
00266 
00267 static const u1_t SENSITIVITY[7][3] = {
00268     // ------------bw----------
00269     // 125kHz    250kHz    500kHz
00270     { 141-109,  141-109, 141-109 },  // FSK
00271     { 141-127,  141-124, 141-121 },  // SF7
00272     { 141-129,  141-126, 141-123 },  // SF8
00273     { 141-132,  141-129, 141-126 },  // SF9
00274     { 141-135,  141-132, 141-129 },  // SF10
00275     { 141-138,  141-135, 141-132 },  // SF11
00276     { 141-141,  141-138, 141-135 }   // SF12
00277 };
00278 
00279 int getSensitivity (rps_t rps) {
00280     return -141 + SENSITIVITY[getSf(rps)][getBw(rps)];
00281 }
00282 
00283 ostime_t calcAirTime (rps_t rps, u1_t plen) {
00284     u1_t bw = getBw(rps);  // 0,1,2 = 125,250,500kHz
00285     u1_t sf = getSf(rps);  // 0=FSK, 1..6 = SF7..12
00286     if( sf == FSK ) {
00287         return (plen+/*preamble*/5+/*syncword*/3+/*len*/1+/*crc*/2) * /*bits/byte*/8
00288             * (s4_t)OSTICKS_PER_SEC / /*kbit/s*/50000;
00289     }
00290     u1_t sfx = 4*(sf+(7-SF7));
00291     u1_t q = sfx - (sf >= SF11 ? 8 : 0);
00292     int tmp = 8*plen - sfx + 28 + (getNocrc(rps)?0:16) - (getIh(rps)?20:0);
00293     if( tmp > 0 ) {
00294         tmp = (tmp + q - 1) / q;
00295         tmp *= getCr(rps)+5;
00296         tmp += 8;
00297     } else {
00298         tmp = 8;
00299     }
00300     tmp = (tmp<<2) + /*preamble*/49 /* 4 * (8 + 4.25) */;
00301     // bw = 125000 = 15625 * 2^3
00302     //      250000 = 15625 * 2^4
00303     //      500000 = 15625 * 2^5
00304     // sf = 7..12
00305     //
00306     // osticks =  tmp * OSTICKS_PER_SEC * 1<<sf / bw
00307     //
00308     // 3 => counter reduced divisor 125000/8 => 15625
00309     // 2 => counter 2 shift on tmp
00310     sfx = sf+(7-SF7) - (3+2) - bw;
00311     int div = 15625;
00312     if( sfx > 4 ) {
00313         // prevent 32bit signed int overflow in last step
00314         div >>= sfx-4;
00315         sfx = 4;
00316     }
00317     // Need 32bit arithmetic for this last step
00318     return (((ostime_t)tmp << sfx) * OSTICKS_PER_SEC + div/2) / div;
00319 }
00320 
00321 extern inline s1_t  rssi2s1 (int v);
00322 extern inline int   s12rssi (s1_t v);
00323 extern inline float  s12snr (s1_t v);
00324 extern inline s1_t   snr2s1 (double v);
00325 
00326 extern inline rps_t updr2rps (dr_t dr);
00327 extern inline rps_t dndr2rps (dr_t dr);
00328 extern inline int isFasterDR (dr_t dr1, dr_t dr2);
00329 extern inline int isSlowerDR (dr_t dr1, dr_t dr2);
00330 extern inline dr_t  incDR    (dr_t dr);
00331 extern inline dr_t  decDR    (dr_t dr);
00332 extern inline dr_t  assertDR (dr_t dr);
00333 extern inline dr_t  validDR  (dr_t dr);
00334 extern inline dr_t  lowerDR  (dr_t dr, u1_t n);
00335 
00336 extern inline sf_t  getSf    (rps_t params);
00337 extern inline rps_t setSf    (rps_t params, sf_t sf);
00338 extern inline bw_t  getBw    (rps_t params);
00339 extern inline rps_t setBw    (rps_t params, bw_t cr);
00340 extern inline cr_t  getCr    (rps_t params);
00341 extern inline rps_t setCr    (rps_t params, cr_t cr);
00342 extern inline int   getNocrc (rps_t params);
00343 extern inline rps_t setNocrc (rps_t params, int nocrc);
00344 extern inline int   getIh    (rps_t params);
00345 extern inline rps_t setIh    (rps_t params, int ih);
00346 extern inline rps_t makeRps  (sf_t sf, bw_t bw, cr_t cr, int ih, int nocrc);
00347 extern inline int   sameSfBw (rps_t r1, rps_t r2);
00348 
00349 // END LORA
00350 // ================================================================================
00351 
00352 
00353 // Adjust DR for TX retries
00354 //  - indexed by retry count
00355 //  - return steps to lower DR
00356 static const u1_t DRADJUST[2+TXCONF_ATTEMPTS] = {
00357     // normal frames - 1st try / no retry
00358     0,
00359     // confirmed frames
00360     0,0,1,0,1,0,1,0,0
00361 };
00362 
00363 
00364 // Table below defines the size of one symbol as
00365 //   symtime = 256us * 2^T(sf,bw)
00366 // 256us is called one symunit. 
00367 //                 SF:                                  
00368 //      BW:      |__7___8___9__10__11__12
00369 //      125kHz   |  2   3   4   5   6   7
00370 //      250kHz   |  1   2   3   4   5   6
00371 //      500kHz   |  0   1   2   3   4   5
00372 //  
00373 // Times for half symbol per DR
00374 // Per DR table to minimize rounding errors
00375 static const ostime_t DR2HSYM_osticks[] = {
00376 #if defined(CFG_eu868)
00377 #define dr2hsym(dr) (DR2HSYM_osticks[(dr)])
00378     us2osticksRound(128<<7),  // DR_SF12
00379     us2osticksRound(128<<6),  // DR_SF11
00380     us2osticksRound(128<<5),  // DR_SF10
00381     us2osticksRound(128<<4),  // DR_SF9
00382     us2osticksRound(128<<3),  // DR_SF8
00383     us2osticksRound(128<<2),  // DR_SF7
00384     us2osticksRound(128<<1),  // DR_SF7B
00385     us2osticksRound(80)       // FSK -- not used (time for 1/2 byte)
00386 #elif defined(CFG_us915)
00387 #define dr2hsym(dr) (DR2HSYM_osticks[(dr)&7])  // map DR_SFnCR -> 0-6
00388     us2osticksRound(128<<5),  // DR_SF10   DR_SF12CR
00389     us2osticksRound(128<<4),  // DR_SF9    DR_SF11CR
00390     us2osticksRound(128<<3),  // DR_SF8    DR_SF10CR
00391     us2osticksRound(128<<2),  // DR_SF7    DR_SF9CR
00392     us2osticksRound(128<<1),  // DR_SF8C   DR_SF8CR
00393     us2osticksRound(128<<0)   // ------    DR_SF7CR
00394 #endif
00395 };
00396 
00397 
00398 static ostime_t calcRxWindow (u1_t secs, dr_t dr) {
00399     ostime_t rxoff, err;
00400     if( secs==0 ) {
00401         // aka 128 secs (next becaon)
00402         rxoff = LMIC.drift;
00403         err = LMIC.lastDriftDiff;
00404     } else {
00405         // scheduled RX window within secs into current beacon period
00406         rxoff = (LMIC.drift * (ostime_t)secs) >> BCN_INTV_exp;
00407         err = (LMIC.lastDriftDiff * (ostime_t)secs) >> BCN_INTV_exp;
00408     }
00409     u1_t rxsyms = MINRX_SYMS;
00410     err += (ostime_t)LMIC.maxDriftDiff * LMIC.missedBcns;
00411     LMIC.rxsyms = MINRX_SYMS + (err / dr2hsym(dr));
00412 
00413     return (rxsyms-PAMBL_SYMS) * dr2hsym(dr) + rxoff;
00414 }
00415 
00416 
00417 // Setup beacon RX parameters assuming we have an error of ms (aka +/-(ms/2))
00418 static void calcBcnRxWindowFromMillis (u1_t ms, bit_t ini) {
00419     if( ini ) {
00420         LMIC.drift = 0;
00421         LMIC.maxDriftDiff = 0;
00422         LMIC.missedBcns = 0;
00423         LMIC.bcninfo.flags |= BCN_NODRIFT|BCN_NODDIFF;
00424     }
00425     ostime_t hsym = dr2hsym(DR_BCN);
00426     LMIC.bcnRxsyms = MINRX_SYMS + ms2osticksCeil(ms) / hsym;
00427     LMIC.bcnRxtime = LMIC.bcninfo.txtime + BCN_INTV_osticks - (LMIC.bcnRxsyms-PAMBL_SYMS) * hsym;
00428 }
00429 
00430 
00431 // Setup scheduled RX window (ping/multicast slot)
00432 static void rxschedInit (xref2rxsched_t rxsched) {
00433     os_clearMem(AESkey,16);
00434     os_clearMem(LMIC.frame+8,8);
00435     os_wlsbf4(LMIC.frame, LMIC.bcninfo.time);
00436     os_wlsbf4(LMIC.frame+4, LMIC.devaddr);
00437     os_aes(AES_ENC,LMIC.frame,16);
00438     u1_t intvExp = rxsched->intvExp;
00439     ostime_t off = os_rlsbf2(LMIC.frame) & (0x0FFF >> (7 - intvExp)); // random offset (slot units)
00440     rxsched->rxbase = (LMIC.bcninfo.txtime +
00441                        BCN_RESERVE_osticks +
00442                        ms2osticks(BCN_SLOT_SPAN_ms * off)); // random offset osticks
00443     rxsched->slot   = 0;
00444     rxsched->rxtime = rxsched->rxbase - calcRxWindow(/*secs BCN_RESERVE*/2+(1<<intvExp),rxsched->dr);
00445     rxsched->rxsyms = LMIC.rxsyms;
00446 }
00447 
00448 
00449 static bit_t rxschedNext (xref2rxsched_t rxsched, ostime_t cando) {
00450   again:
00451     if( rxsched->rxtime - cando >= 0 )
00452         return 1;
00453     u1_t slot;
00454     if( (slot=rxsched->slot) >= 128 )
00455         return 0;
00456     u1_t intv = 1<<rxsched->intvExp;
00457     if( (rxsched->slot = (slot += (intv))) >= 128 )
00458         return 0;
00459     rxsched->rxtime = rxsched->rxbase
00460         + ((BCN_WINDOW_osticks * (ostime_t)slot) >> BCN_INTV_exp)
00461         - calcRxWindow(/*secs BCN_RESERVE*/2+slot+intv,rxsched->dr);
00462     rxsched->rxsyms = LMIC.rxsyms;
00463     goto again;
00464 }
00465 
00466 
00467 static ostime_t rndDelay (u1_t secSpan) {
00468     u2_t r = os_getRndU2();
00469     ostime_t delay = r;
00470     if( delay > OSTICKS_PER_SEC )
00471         delay = r % (u2_t)OSTICKS_PER_SEC;
00472     if( secSpan > 0 )
00473         delay += ((u1_t)r % secSpan) * OSTICKS_PER_SEC;
00474     return delay;
00475 }
00476 
00477 
00478 static void txDelay (ostime_t reftime, u1_t secSpan) {
00479     reftime += rndDelay(secSpan);
00480     if( LMIC.globalDutyRate == 0  ||  (reftime - LMIC.globalDutyAvail) > 0 ) {
00481         LMIC.globalDutyAvail = reftime;
00482         LMIC.opmode |= OP_RNDTX;
00483     }
00484 }
00485 
00486 
00487 static void setDrJoin (u1_t reason, u1_t dr) {
00488     EV(drChange, INFO, (e_.reason    = reason,
00489                         e_.deveui    = MAIN::CDEV->getEui(),
00490                         e_.dr        = dr|DR_PAGE,
00491                         e_.txpow     = LMIC.adrTxPow,
00492                         e_.prevdr    = LMIC.datarate|DR_PAGE,
00493                         e_.prevtxpow = LMIC.adrTxPow));
00494     LMIC.datarate = dr;
00495     DO_DEVDB(LMIC.datarate,datarate);
00496 }
00497 
00498 
00499 static void setDrTxpow (u1_t reason, u1_t dr, s1_t pow) {
00500     EV(drChange, INFO, (e_.reason    = reason,
00501                         e_.deveui    = MAIN::CDEV->getEui(),
00502                         e_.dr        = dr|DR_PAGE,
00503                         e_.txpow     = pow,
00504                         e_.prevdr    = LMIC.datarate|DR_PAGE,
00505                         e_.prevtxpow = LMIC.adrTxPow));
00506     
00507     if( pow != KEEP_TXPOW )
00508         LMIC.adrTxPow = pow;
00509     if( LMIC.datarate != dr ) {
00510         LMIC.datarate = dr;
00511         DO_DEVDB(LMIC.datarate,datarate);
00512         LMIC.opmode |= OP_NEXTCHNL;
00513     }
00514 }
00515 
00516 
00517 void LMIC_stopPingable (void) {
00518     LMIC.opmode &= ~(OP_PINGABLE|OP_PINGINI);
00519 }
00520 
00521 
00522 void LMIC_setPingable (u1_t intvExp) {
00523     // Change setting
00524     LMIC.ping.intvExp = (intvExp & 0x7);
00525     LMIC.opmode |= OP_PINGABLE;
00526     // App may call LMIC_enableTracking() explicitely before
00527     // Otherwise tracking is implicitly enabled here
00528     if( (LMIC.opmode & (OP_TRACK|OP_SCAN)) == 0  &&  LMIC.bcninfoTries == 0 )
00529         LMIC_enableTracking(0);
00530 }
00531 
00532 
00533 #if defined(CFG_eu868)
00534 // ================================================================================
00535 //
00536 // BEG: EU868 related stuff
00537 //
00538 enum { NUM_DEFAULT_CHANNELS=6 };
00539 static const u4_t iniChannelFreq[12] = {
00540     // Join frequencies and duty cycle limit (0.1%)
00541     EU868_F1|BAND_MILLI, EU868_J4|BAND_MILLI,
00542     EU868_F2|BAND_MILLI, EU868_J5|BAND_MILLI,
00543     EU868_F3|BAND_MILLI, EU868_J6|BAND_MILLI,
00544     // Default operational frequencies
00545     EU868_F1|BAND_CENTI, EU868_F2|BAND_CENTI, EU868_F3|BAND_CENTI,
00546     EU868_F4|BAND_MILLI, EU868_F5|BAND_MILLI, EU868_F6|BAND_DECI
00547 };
00548 
00549 static void initDefaultChannels (bit_t join) {
00550     os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq));
00551     os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap));
00552     os_clearMem(&LMIC.bands, sizeof(LMIC.bands));
00553 
00554     LMIC.channelMap = 0x1F;
00555     u1_t su = join ? 0 : 6;
00556     for( u1_t fu=0; fu<6; fu++,su++ ) {
00557         LMIC.channelFreq[fu]  = iniChannelFreq[su];
00558         LMIC.channelDrMap[fu] = DR_RANGE_MAP(DR_SF12,DR_SF7);
00559     }
00560     if( !join ) {
00561         LMIC.channelDrMap[5] = DR_RANGE_MAP(DR_SF12,DR_SF7);
00562         LMIC.channelDrMap[1] = DR_RANGE_MAP(DR_SF12,DR_FSK);
00563     }
00564 
00565     LMIC.bands[BAND_MILLI].txcap    = 1000;  // 0.1%
00566     LMIC.bands[BAND_MILLI].txpow    = 14;
00567     LMIC.bands[BAND_MILLI].lastchnl = os_getRndU1() % MAX_CHANNELS;
00568     LMIC.bands[BAND_CENTI].txcap    = 100;   // 1%
00569     LMIC.bands[BAND_CENTI].txpow    = 14;
00570     LMIC.bands[BAND_CENTI].lastchnl = os_getRndU1() % MAX_CHANNELS;
00571     LMIC.bands[BAND_DECI ].txcap    = 10;    // 10%
00572     LMIC.bands[BAND_DECI ].txpow    = 27;
00573     LMIC.bands[BAND_CENTI].lastchnl = os_getRndU1() % MAX_CHANNELS;
00574     LMIC.bands[BAND_MILLI].avail = 
00575     LMIC.bands[BAND_CENTI].avail =
00576     LMIC.bands[BAND_DECI ].avail = os_getTime();
00577 }
00578 
00579 bit_t LMIC_setupBand (u1_t bandidx, s1_t txpow, u2_t txcap) {
00580     if( bandidx > BAND_AUX ) return 0;
00581     band_t* b = &LMIC.bands[bandidx];
00582     b->txpow = txpow;
00583     b->txcap = txcap;
00584     b->avail = os_getTime();
00585     b->lastchnl = os_getRndU1() % MAX_CHANNELS;
00586     return 1;
00587 }
00588 
00589 bit_t LMIC_setupChannel (u1_t chidx, u4_t freq, u2_t drmap, s1_t band) {
00590     if( chidx >= MAX_CHANNELS )
00591         return 0;
00592     if( band == -1 ) {
00593         if( freq >= 869400000 && freq <= 869650000 )
00594             freq |= BAND_DECI;   // 10% 27dBm
00595         else if( (freq >= 868000000 && freq <= 868600000) ||
00596                  (freq >= 869700000 && freq <= 870000000)  )
00597             freq |= BAND_CENTI;  // 1% 14dBm 
00598         else 
00599             freq |= BAND_MILLI;  // 0.1% 14dBm
00600     } else {
00601         if( band > BAND_AUX ) return 0;
00602         freq = (freq&~3) | band;
00603     }
00604     LMIC.channelFreq [chidx] = freq;
00605     LMIC.channelDrMap[chidx] = drmap==0 ? DR_RANGE_MAP(DR_SF12,DR_SF7) : drmap;
00606     LMIC.channelMap |= 1<<chidx;  // enabled right away
00607     return 1;
00608 }
00609 
00610 void LMIC_disableChannel (u1_t channel) {
00611     LMIC.channelFreq[channel] = 0;
00612     LMIC.channelDrMap[channel] = 0;
00613     LMIC.channelMap &= ~(1<<channel);
00614 }
00615 
00616 static u4_t convFreq (xref2u1_t ptr) {
00617     u4_t freq = (os_rlsbf4(ptr-1) >> 8) * 100;
00618     if( freq < EU868_FREQ_MIN || freq > EU868_FREQ_MAX )
00619         freq = 0;
00620     return freq;
00621 }
00622 
00623 static u1_t mapChannels (u1_t chpage, u2_t chmap) {
00624     // Bad page, disable all channel, enable non-existent
00625     if( chpage != 0 || chmap==0 || (chmap & ~LMIC.channelMap) != 0 )
00626         return 0;  // illegal input
00627     for( u1_t chnl=0; chnl<MAX_CHANNELS; chnl++ ) {
00628         if( (chmap & (1<<chnl)) != 0 && LMIC.channelFreq[chnl] == 0 )
00629             chmap &= ~(1<<chnl); // ignore - channel is not defined
00630     }
00631     LMIC.channelMap = chmap;
00632     return 1;
00633 }
00634 
00635 
00636 static void updateTx (ostime_t txbeg) {
00637     u4_t freq = LMIC.channelFreq[LMIC.txChnl];
00638     // Update global/band specific duty cycle stats
00639     ostime_t airtime = calcAirTime(LMIC.rps, LMIC.dataLen);
00640     // Update channel/global duty cycle stats
00641     xref2band_t band = &LMIC.bands[freq & 0x3];
00642     LMIC.freq  = freq & ~(u4_t)3;
00643     LMIC.txpow = band->txpow;
00644     band->avail = txbeg + airtime * band->txcap;
00645     if( LMIC.globalDutyRate != 0 )
00646         LMIC.globalDutyAvail = txbeg + (airtime<<LMIC.globalDutyRate);
00647 }
00648 
00649 static ostime_t nextTx (ostime_t now) {
00650     u1_t bmap=0xF;
00651     do {
00652         ostime_t mintime = now + /*10h*/36000*OSTICKS_PER_SEC;
00653         u1_t band=0;
00654         for( u1_t bi=0; bi<4; bi++ ) {
00655             if( (bmap & (1<<bi)) && mintime - LMIC.bands[bi].avail > 0 )
00656                 mintime = LMIC.bands[band = bi].avail;
00657         }
00658         // Find next channel in given band
00659         u1_t chnl = LMIC.bands[band].lastchnl;
00660         for( u1_t ci=0; ci<MAX_CHANNELS; ci++ ) {
00661             if( (chnl = (chnl+1)) >= MAX_CHANNELS )
00662                 chnl -=  MAX_CHANNELS;
00663             if( (LMIC.channelMap & (1<<chnl)) != 0  &&  // channel enabled
00664                 (LMIC.channelDrMap[chnl] & (1<<(LMIC.datarate&0xF))) != 0  &&
00665                 band == (LMIC.channelFreq[chnl] & 0x3) ) { // in selected band
00666                 LMIC.txChnl = LMIC.bands[band].lastchnl = chnl;
00667                 return mintime;
00668             }
00669         }
00670         if( (bmap &= ~(1<<band)) == 0 ) {
00671             // No feasible channel  found!
00672             return mintime;
00673         }
00674     } while(1);
00675 }
00676 
00677 
00678 static void setBcnRxParams (void) {
00679     LMIC.dataLen = 0;
00680     LMIC.freq = LMIC.channelFreq[LMIC.bcnChnl] & ~(u4_t)3;
00681     LMIC.rps  = setIh(setNocrc(dndr2rps((dr_t)DR_BCN),1),LEN_BCN);
00682 }
00683 
00684 #define setRx1Params() /*LMIC.freq/rps remain unchanged*/
00685 
00686 static void initJoinLoop (void) {
00687     LMIC.txChnl = os_getRndU1() % 6;
00688     LMIC.adrTxPow = 14;
00689     setDrJoin(DRCHG_SET, DR_SF7);
00690     initDefaultChannels(1);
00691     ASSERT((LMIC.opmode & OP_NEXTCHNL)==0);
00692     LMIC.txend = LMIC.bands[BAND_MILLI].avail + rndDelay(8);
00693 }
00694 
00695 
00696 static ostime_t nextJoinState (void) {
00697     u1_t failed = 0;
00698 
00699     // Try 869.x and then 864.x with same DR
00700     // If both fail try next lower datarate
00701     if( ++LMIC.txChnl == 6 )
00702         LMIC.txChnl = 0;
00703     if( (++LMIC.txCnt & 1) == 0 ) {
00704         // Lower DR every 2nd try (having tried 868.x and 864.x with the same DR)
00705         if( LMIC.datarate == DR_SF12 )
00706             failed = 1; // we have tried all DR - signal EV_JOIN_FAILED
00707         else
00708             setDrJoin(DRCHG_NOJACC, decDR((dr_t)LMIC.datarate));
00709     }
00710     // Clear NEXTCHNL because join state engine controls channel hopping
00711     LMIC.opmode &= ~OP_NEXTCHNL;
00712     // Move txend to randomize synchronized concurrent joins.
00713     // Duty cycle is based on txend.
00714     ostime_t time = os_getTime();
00715     if( time - LMIC.bands[BAND_MILLI].avail < 0 )
00716         time = LMIC.bands[BAND_MILLI].avail;
00717     LMIC.txend = time +
00718         (isTESTMODE()
00719          // Avoid collision with JOIN ACCEPT @ SF12 being sent by GW (but we missed it)
00720          ? DNW2_SAFETY_ZONE
00721          // Otherwise: randomize join (street lamp case):
00722          // SF12:255, SF11:127, .., SF7:8secs
00723          : DNW2_SAFETY_ZONE+rndDelay(255>>LMIC.datarate));
00724     // 1 - triggers EV_JOIN_FAILED event
00725     return failed;
00726 }
00727 
00728 //
00729 // END: EU868 related stuff
00730 //
00731 // ================================================================================
00732 #elif defined(CFG_us915)
00733 // ================================================================================
00734 //
00735 // BEG: US915 related stuff
00736 //
00737 
00738 
00739 static void initDefaultChannels (void) {
00740     for( u1_t i=0; i<4; i++ )
00741         LMIC.channelMap[i] = 0xFFFF;
00742     LMIC.channelMap[4] = 0x00FF;
00743 }
00744 
00745 static u4_t convFreq (xref2u1_t ptr) {
00746     u4_t freq = (os_rlsbf4(ptr-1) >> 8) * 100;
00747     if( freq >= US915_FREQ_MIN && freq <= US915_FREQ_MAX )
00748         freq = 0;
00749     return freq;
00750 }
00751 
00752 bit_t LMIC_setupChannel (u1_t chidx, u4_t freq, u2_t drmap, s1_t band) {
00753     if( chidx < 72 || chidx >= 72+MAX_XCHANNELS )
00754         return 0; // channels 0..71 are hardwired
00755     chidx -= 72;
00756     LMIC.xchFreq[chidx] = freq;
00757     LMIC.xchDrMap[chidx] = drmap==0 ? DR_RANGE_MAP(DR_SF10,DR_SF8C) : drmap;
00758     LMIC.channelMap[chidx>>4] |= (1<<(chidx&0xF));
00759     return 1;
00760 }
00761 
00762 void LMIC_disableChannel (u1_t channel) {
00763     if( channel < 72+MAX_XCHANNELS )
00764         LMIC.channelMap[channel/4] &= ~(1<<(channel&0xF));
00765 }
00766 
00767 static u1_t mapChannels (u1_t chpage, u2_t chmap) {
00768     if( chpage == MCMD_LADR_CHP_125ON || chpage == MCMD_LADR_CHP_125OFF ) {
00769         u2_t en125 = chpage == MCMD_LADR_CHP_125ON ? 0xFFFF : 0x0000;
00770         for( u1_t u=0; u<4; u++ )
00771             LMIC.channelMap[u] = en125;
00772         LMIC.channelMap[64/16] = chmap;
00773     } else {
00774         if( chpage >= (72+MAX_XCHANNELS+15)/16 )
00775             return 0;
00776         LMIC.channelMap[chpage] = chmap;
00777     }
00778     return 1;
00779 }
00780 
00781 static void updateTx (ostime_t txbeg) {
00782     u1_t chnl = LMIC.txChnl;
00783     if( chnl < 64 ) {
00784         LMIC.freq = US915_125kHz_UPFBASE + chnl*US915_125kHz_UPFSTEP;
00785         LMIC.txpow = 30;
00786         return;
00787     }
00788     LMIC.txpow = 26;
00789     if( chnl < 64+8 ) {
00790         LMIC.freq = US915_500kHz_UPFBASE + (chnl-64)*US915_500kHz_UPFSTEP;
00791     } else {
00792         ASSERT(chnl < 64+8+MAX_XCHANNELS);
00793         LMIC.freq = LMIC.xchFreq[chnl-72];
00794     }
00795 
00796     // Update global duty cycle stats
00797     if( LMIC.globalDutyRate != 0 ) {
00798         ostime_t airtime = calcAirTime(LMIC.rps, LMIC.dataLen);
00799         LMIC.globalDutyAvail = txbeg + (airtime<<LMIC.globalDutyRate);
00800     }
00801 }
00802 
00803 // US does not have duty cycling - return now as earliest TX time
00804 #define nextTx(now) (_nextTx(),(now))
00805 static void _nextTx (void) {
00806     if( LMIC.chRnd==0 )
00807         LMIC.chRnd = os_getRndU1() & 0x3F;
00808     if( LMIC.datarate >= DR_SF8C ) { // 500kHz
00809         u1_t map = LMIC.channelMap[64/16]&0xFF;
00810         for( u1_t i=0; i<8; i++ ) {
00811             if( (map & (1<<(++LMIC.chRnd & 7))) != 0 ) {
00812                 LMIC.txChnl = 64 + (LMIC.chRnd & 7);
00813                 return;
00814             }
00815         }
00816     } else { // 125kHz
00817         for( u1_t i=0; i<64; i++ ) {
00818             u1_t chnl = ++LMIC.chRnd & 0x3F;
00819             if( (LMIC.channelMap[(chnl >> 4)] & (1<<(chnl & 0xF))) != 0 ) {
00820                 LMIC.txChnl = chnl;
00821                 return;
00822             }
00823         }
00824     }
00825     // No feasible channel  found! Keep old one.
00826 }
00827 
00828 static void setBcnRxParams (void) {
00829     LMIC.dataLen = 0;
00830     LMIC.freq = US915_500kHz_DNFBASE + LMIC.bcnChnl * US915_500kHz_DNFSTEP;
00831     LMIC.rps  = setIh(setNocrc(dndr2rps((dr_t)DR_BCN),1),LEN_BCN);
00832 }
00833 
00834 #define setRx1Params() {                                                \
00835     LMIC.freq = US915_500kHz_DNFBASE + (LMIC.txChnl & 0x7) * US915_500kHz_DNFSTEP; \
00836     if( /* TX datarate */LMIC.dndr < DR_SF8C )                          \
00837         LMIC.dndr += DR_SF10CR - DR_SF10;                               \
00838     else if( LMIC.dndr == DR_SF8C )                                     \
00839         LMIC.dndr = DR_SF7CR;                                           \
00840     LMIC.rps = dndr2rps(LMIC.dndr);                                     \
00841 }
00842 
00843 static void initJoinLoop (void) {
00844     LMIC.chRnd = 0;
00845     LMIC.txChnl = 0;
00846     LMIC.adrTxPow = 20;
00847     ASSERT((LMIC.opmode & OP_NEXTCHNL)==0);
00848     LMIC.txend = os_getTime();
00849     setDrJoin(DRCHG_SET, DR_SF7);
00850 }
00851 
00852 static ostime_t nextJoinState (void) {
00853     // Try the following:
00854     //   SF7/8/9/10  on a random channel 0..63
00855     //   SF8C        on a random channel 64..71
00856     //
00857     u1_t failed = 0;
00858     if( LMIC.datarate != DR_SF8C ) {
00859         LMIC.txChnl = 64+(LMIC.txChnl&7);
00860         setDrJoin(DRCHG_SET, DR_SF8C);
00861     } else {
00862         LMIC.txChnl = os_getRndU1() & 0x3F;
00863         s1_t dr = DR_SF7 - ++LMIC.txCnt;
00864         if( dr < DR_SF10 ) {
00865             dr = DR_SF10;
00866             failed = 1; // All DR exhausted - signal failed
00867         }
00868         setDrJoin(DRCHG_SET, dr);
00869     }
00870     LMIC.opmode &= ~OP_NEXTCHNL;
00871     LMIC.txend = os_getTime() +
00872         (isTESTMODE()
00873          // Avoid collision with JOIN ACCEPT being sent by GW (but we missed it - GW is still busy)
00874          ? DNW2_SAFETY_ZONE
00875          // Otherwise: randomize join (street lamp case):
00876          // SF10:16, SF9=8,..SF8C:1secs
00877          : rndDelay(16>>LMIC.datarate));
00878     // 1 - triggers EV_JOIN_FAILED event
00879     return failed;
00880 }
00881 
00882 //
00883 // END: US915 related stuff
00884 //
00885 // ================================================================================
00886 #else
00887 #error Unsupported frequency band!
00888 #endif
00889 
00890 
00891 static void runEngineUpdate (xref2osjob_t osjob) {
00892     engineUpdate();
00893 }
00894 
00895 
00896 static void reportEvent (ev_t ev) {
00897     EV(devCond, INFO, (e_.reason = EV::devCond_t::LMIC_EV,
00898                        e_.eui    = MAIN::CDEV->getEui(),
00899                        e_.info   = ev));
00900     ON_LMIC_EVENT(ev);
00901     engineUpdate();
00902 }
00903 
00904 
00905 static void runReset (xref2osjob_t osjob) {
00906     // Disable session
00907     LMIC_reset();
00908     LMIC_startJoining();
00909     reportEvent(EV_RESET);
00910 }
00911 
00912 static void stateJustJoined (void) {
00913     LMIC.seqnoDn     = LMIC.seqnoUp = 0;
00914     LMIC.rejoinCnt   = 0;
00915     LMIC.dnConf      = LMIC.adrChanged = LMIC.ladrAns = LMIC.devsAns = 0;
00916     LMIC.moreData    = LMIC.dn2Ans = LMIC.snchAns = LMIC.dutyCapAns = 0;
00917     LMIC.pingSetAns  = 0;
00918     LMIC.upRepeat    = 0;
00919     LMIC.adrAckReq   = LINK_CHECK_INIT;
00920     LMIC.dn2Dr       = DR_DNW2;
00921     LMIC.dn2Freq     = FREQ_DNW2;
00922     LMIC.bcnChnl     = CHNL_BCN;
00923     LMIC.ping.freq   = FREQ_PING;
00924     LMIC.ping.dr     = DR_PING;
00925 }
00926 
00927 
00928 // ================================================================================
00929 // Decoding frames
00930 
00931 
00932 // Decode beacon  - do not overwrite bcninfo unless we have a match!
00933 static int decodeBeacon (void) {
00934     ASSERT(LMIC.dataLen == LEN_BCN); // implicit header RX guarantees this
00935     xref2u1_t d = LMIC.frame;
00936     if(
00937 #if CFG_eu868
00938         d[OFF_BCN_CRC1] != (u1_t)os_crc16(d,OFF_BCN_CRC1)
00939 #elif CFG_us915
00940         os_rlsbf2(&d[OFF_BCN_CRC1]) != os_crc16(d,OFF_BCN_CRC1)
00941 #endif
00942         )
00943         return 0;   // first (common) part fails CRC check
00944     // First set of fields is ok
00945     u4_t bcnnetid = os_rlsbf4(&d[OFF_BCN_NETID]) & 0xFFFFFF;
00946     if( bcnnetid != LMIC.netid )
00947         return -1;  // not the beacon we're looking for
00948 
00949     LMIC.bcninfo.flags &= ~(BCN_PARTIAL|BCN_FULL);
00950     // Match - update bcninfo structure
00951     LMIC.bcninfo.snr    = LMIC.snr;
00952     LMIC.bcninfo.rssi   = LMIC.rssi;
00953     LMIC.bcninfo.txtime = LMIC.rxtime - AIRTIME_BCN_osticks;
00954     LMIC.bcninfo.time   = os_rlsbf4(&d[OFF_BCN_TIME]);
00955     LMIC.bcninfo.flags |= BCN_PARTIAL;
00956 
00957     // Check 2nd set
00958     if( os_rlsbf2(&d[OFF_BCN_CRC2]) != os_crc16(d,OFF_BCN_CRC2) )
00959         return 1;
00960     // Second set of fields is ok
00961     LMIC.bcninfo.lat    = (s4_t)os_rlsbf4(&d[OFF_BCN_LAT-1]) >> 8; // read as signed 24-bit
00962     LMIC.bcninfo.lon    = (s4_t)os_rlsbf4(&d[OFF_BCN_LON-1]) >> 8; // ditto
00963     LMIC.bcninfo.info   = d[OFF_BCN_INFO];
00964     LMIC.bcninfo.flags |= BCN_FULL;
00965     return 2;
00966 }
00967 
00968 
00969 static bit_t decodeFrame (void) {
00970     //debug_str("decodeFrame: ");
00971     //debug_buf(LMIC.frame, LMIC.dataLen);
00972     xref2u1_t d = LMIC.frame;
00973     u1_t hdr    = d[0];
00974     u1_t ftype  = hdr & HDR_FTYPE;
00975     int  dlen   = LMIC.dataLen;
00976     if( dlen < OFF_DAT_OPTS+4 ||
00977         (hdr & HDR_MAJOR) != HDR_MAJOR_V1 ||
00978         (ftype != HDR_FTYPE_DADN  &&  ftype != HDR_FTYPE_DCDN) ) {
00979         // Basic sanity checks failed
00980         EV(specCond, WARN, (e_.reason = EV::specCond_t::UNEXPECTED_FRAME,
00981                             e_.eui    = MAIN::CDEV->getEui(),
00982                             e_.info   = dlen < 4 ? 0 : os_rlsbf4(&d[dlen-4]),
00983                             e_.info2  = hdr + (dlen<<8)));
00984       norx:
00985         LMIC.dataLen = 0;
00986         return 0;
00987     }
00988     // Validate exact frame length
00989     // Note: device address was already read+evaluated in order to arrive here.
00990     int  fct   = d[OFF_DAT_FCT];
00991     u4_t addr  = os_rlsbf4(&d[OFF_DAT_ADDR]);
00992     u4_t seqno = os_rlsbf2(&d[OFF_DAT_SEQNO]);
00993     int  olen  = fct & FCT_OPTLEN;
00994     int  ackup = (fct & FCT_ACK) != 0 ? 1 : 0;   // ACK last up frame
00995     int  poff  = OFF_DAT_OPTS+olen;
00996     int  pend  = dlen-4;  // MIC
00997     
00998     //debug_val("fct: ", fct);
00999     //debug_val("olen: ", olen);
01000 
01001     if( addr != LMIC.devaddr ) {
01002         EV(specCond, WARN, (e_.reason = EV::specCond_t::ALIEN_ADDRESS,
01003                             e_.eui    = MAIN::CDEV->getEui(),
01004                             e_.info   = addr,
01005                             e_.info2  = LMIC.devaddr));
01006         goto norx;
01007     }
01008     if( poff > pend ) {
01009         EV(specCond, ERR, (e_.reason = EV::specCond_t::CORRUPTED_FRAME,
01010                            e_.eui    = MAIN::CDEV->getEui(),
01011                            e_.info   = 0x1000000 + (poff-pend) + (fct<<8) + (dlen<<16)));
01012         goto norx;
01013     }
01014 
01015     int port = -1;
01016     int replayConf = 0;
01017 
01018     if( pend > poff )
01019         port = d[poff++];
01020     seqno = LMIC.seqnoDn + (u2_t)(seqno - LMIC.seqnoDn);
01021 
01022     if( !aes_verifyMic(LMIC.nwkKey, LMIC.devaddr, seqno, /*dn*/1, d, pend) ) {
01023         EV(spe3Cond, ERR, (e_.reason = EV::spe3Cond_t::CORRUPTED_MIC,
01024                            e_.eui1   = MAIN::CDEV->getEui(),
01025                            e_.info1  = Base::lsbf4(&d[pend]),
01026                            e_.info2  = seqno,
01027                            e_.info3  = LMIC.devaddr));
01028         goto norx;
01029     }
01030     if( seqno < LMIC.seqnoDn ) {
01031         if( (s4_t)seqno > (s4_t)LMIC.seqnoDn ) {
01032             EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_ROLL_OVER,
01033                                 e_.eui    = MAIN::CDEV->getEui(),
01034                                 e_.info   = LMIC.seqnoDn, 
01035                                 e_.info2  = seqno));
01036             goto norx;
01037         }
01038         if( seqno != LMIC.seqnoDn-1 || !LMIC.dnConf || ftype != HDR_FTYPE_DCDN ) {
01039             EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_OBSOLETE,
01040                                 e_.eui    = MAIN::CDEV->getEui(),
01041                                 e_.info   = LMIC.seqnoDn, 
01042                                 e_.info2  = seqno));
01043             goto norx;
01044         }
01045         // Replay of previous sequence number allowed only if
01046         // previous frame and repeated both requested confirmation
01047         replayConf = 1;
01048     }
01049     else {
01050         if( seqno > LMIC.seqnoDn ) {
01051             EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_SKIP,
01052                                 e_.eui    = MAIN::CDEV->getEui(),
01053                                 e_.info   = LMIC.seqnoDn, 
01054                                 e_.info2  = seqno));
01055         }
01056         LMIC.seqnoDn = seqno+1;  // next number to be expected
01057         DO_DEVDB(LMIC.seqnoDn,seqnoDn);
01058         // DN frame requested confirmation - provide ACK once with next UP frame
01059         LMIC.dnConf = (ftype == HDR_FTYPE_DCDN ? FCT_ACK : 0);
01060     }
01061 
01062     if( LMIC.dnConf || (fct & FCT_MORE) )
01063         LMIC.opmode |= OP_POLL;
01064 
01065     // We heard from network
01066     LMIC.adrChanged = LMIC.rejoinCnt = 0;
01067     if( LMIC.adrAckReq != LINK_CHECK_OFF )
01068         LMIC.adrAckReq = LINK_CHECK_INIT;
01069 
01070     // Process OPTS
01071     int m = LMIC.rssi - RSSI_OFF - getSensitivity(LMIC.rps);
01072     LMIC.margin = m < 0 ? 0 : m > 254 ? 254 : m;
01073 
01074     xref2u1_t opts = &d[OFF_DAT_OPTS];
01075     int oidx = 0;
01076     //debug_val("Port: ", port);
01077     while( oidx < olen ) {
01078 //      debug_str("Port: ");
01079         debug_str("Process OPTS...");
01080         debug_hex(opts[oidx]);
01081         debug_str("\r\n");
01082         switch( opts[oidx] ) {
01083         case MCMD_LCHK_ANS: {
01084             debug_str("MCMD_LCHK_ANS\r\n");
01085             //int gwmargin = opts[oidx+1];
01086             //int ngws = opts[oidx+2];
01087             oidx += 3;
01088             continue;
01089         }
01090         case MCMD_LADR_REQ: {
01091             debug_str("MCMD_LADR_REQ\r\n");
01092             u1_t p1     = opts[oidx+1];            // txpow + DR
01093             u2_t chmap  = os_rlsbf2(&opts[oidx+2]);// list of enabled channels
01094             u1_t chpage = opts[oidx+4] & MCMD_LADR_CHPAGE_MASK;     // channel page
01095             u1_t uprpt  = opts[oidx+4] & MCMD_LADR_REPEAT_MASK;     // up repeat count
01096             oidx += 5;
01097 
01098             LMIC.ladrAns = 0x80 |     // Include an answer into next frame up
01099                 MCMD_LADR_ANS_POWACK | MCMD_LADR_ANS_CHACK | MCMD_LADR_ANS_DRACK;
01100             if( !mapChannels(chpage, chmap) )
01101                 LMIC.ladrAns &= ~MCMD_LADR_ANS_CHACK;
01102             dr_t dr = (dr_t)(p1>>MCMD_LADR_DR_SHIFT);
01103             if( !validDR(dr) ) {
01104                 LMIC.ladrAns &= ~MCMD_LADR_ANS_DRACK;
01105                 EV(specCond, ERR, (e_.reason = EV::specCond_t::BAD_MAC_CMD,
01106                                    e_.eui    = MAIN::CDEV->getEui(),
01107                                    e_.info   = Base::lsbf4(&d[pend]),
01108                                    e_.info2  = Base::msbf4(&opts[oidx-4])));
01109             }
01110             if( (LMIC.ladrAns & 0x7F) == (MCMD_LADR_ANS_POWACK | MCMD_LADR_ANS_CHACK | MCMD_LADR_ANS_DRACK) ) {
01111                 // Nothing went wrong - use settings
01112                 LMIC.upRepeat = uprpt;
01113                 setDrTxpow(DRCHG_NWKCMD, dr, pow2dBm(p1));
01114             }
01115             LMIC.adrChanged = 1;  // Trigger an ACK to NWK
01116             
01117             //debug_str("decodeFrame: ");
01118             //debug_buf(LMIC.frame, LMIC.dataLen);
01119 
01120             continue;
01121         }
01122         case MCMD_DEVS_REQ: {
01123             debug_str("DEVS_REQ\r\n");
01124             LMIC.devsAns = 1;
01125             oidx += 1;
01126             continue;
01127         }
01128         case MCMD_DN2P_SET: {
01129             debug_str("MCMD_DN2P_SET\r\n");
01130             dr_t dr = (dr_t)(opts[oidx+1] & 0x0F);
01131             u4_t freq = convFreq(&opts[oidx+2]);
01132             oidx += 5;
01133             LMIC.dn2Ans = 0x80;   // answer pending
01134             if( validDR(dr) )
01135                 LMIC.dn2Ans |= MCMD_DN2P_ANS_DRACK;
01136             if( freq != 0 )
01137                 LMIC.dn2Ans |= MCMD_DN2P_ANS_CHACK;
01138             if( LMIC.dn2Ans == (0x80|MCMD_DN2P_ANS_DRACK|MCMD_DN2P_ANS_CHACK) ) {
01139                 LMIC.dn2Dr = dr;
01140                 LMIC.dn2Freq = freq;
01141                 DO_DEVDB(LMIC.dn2Dr,dn2Dr);
01142                 DO_DEVDB(LMIC.dn2Freq,dn2Freq);
01143             }
01144             continue;
01145         }
01146         case MCMD_DCAP_REQ: {
01147             debug_str("MCMD_DCAP_REQ\r\n");
01148             u1_t cap = opts[oidx+1];
01149             oidx += 2;
01150             // A value cap=0xFF means device is OFF unless enabled again manually.
01151             if( cap==0xFF )
01152                 LMIC.opmode |= OP_SHUTDOWN;  // stop any sending
01153             LMIC.globalDutyRate  = cap & 0xF;
01154             LMIC.globalDutyAvail = os_getTime();
01155             DO_DEVDB(cap,dutyCap);
01156             LMIC.dutyCapAns = 1;
01157             continue;
01158         }
01159         case MCMD_SNCH_REQ: {
01160             debug_str("MCMD_SNCH_REQ\r\n");
01161             u1_t chidx = opts[oidx+1];  // channel
01162             u4_t freq  = convFreq(&opts[oidx+2]); // freq
01163             u1_t drs   = opts[oidx+5];  // datarate span
01164             LMIC.snchAns = 0x80;
01165             if( freq != 0 && LMIC_setupChannel(chidx, freq, DR_RANGE_MAP(drs&0xF,drs>>4), -1) )
01166                 LMIC.snchAns |= MCMD_SNCH_ANS_DRACK|MCMD_SNCH_ANS_FQACK;
01167             oidx += 6;
01168             continue;
01169         }
01170         case MCMD_PING_SET: {
01171             debug_str("MCMD_PING_SET\r\n");
01172             u4_t freq = convFreq(&opts[oidx+1]);
01173             oidx += 4;
01174             u1_t flags = 0x80;
01175             if( freq != 0 ) {
01176                 flags |= MCMD_PING_ANS_FQACK;
01177                 LMIC.ping.freq = freq;
01178                 DO_DEVDB(LMIC.ping.intvExp, pingIntvExp);
01179                 DO_DEVDB(LMIC.ping.freq, pingFreq);
01180                 DO_DEVDB(LMIC.ping.dr, pingDr);
01181             }
01182             LMIC.pingSetAns = flags;
01183             continue;
01184         }
01185         case MCMD_BCNI_ANS: {
01186             debug_str("MCMD_BCNI_ANS\r\n");
01187             // Ignore if tracking already enabled
01188             if( (LMIC.opmode & OP_TRACK) == 0 ) {
01189                 LMIC.bcnChnl = opts[oidx+3];
01190                 // Enable tracking - bcninfoTries
01191                 LMIC.opmode |= OP_TRACK;
01192                 // Cleared later in txComplete handling - triggers EV_BEACON_FOUND
01193                 ASSERT(LMIC.bcninfoTries!=0);
01194                 // Setup RX parameters
01195                 LMIC.bcninfo.txtime = (LMIC.rxtime
01196                                        + ms2osticks(os_rlsbf2(&opts[oidx+1]) * MCMD_BCNI_TUNIT)
01197                                        + ms2osticksCeil(MCMD_BCNI_TUNIT/2)
01198                                        - BCN_INTV_osticks);
01199                 LMIC.bcninfo.flags = 0;  // txtime above cannot be used as reference (BCN_PARTIAL|BCN_FULL cleared)
01200                 calcBcnRxWindowFromMillis(MCMD_BCNI_TUNIT,1);  // error of +/-N ms 
01201 
01202                 EV(lostFrame, INFO, (e_.reason  = EV::lostFrame_t::MCMD_BCNI_ANS,
01203                                      e_.eui     = MAIN::CDEV->getEui(),
01204                                      e_.lostmic = Base::lsbf4(&d[pend]),
01205                                      e_.info    = (LMIC.missedBcns |
01206                                                    (osticks2us(LMIC.bcninfo.txtime + BCN_INTV_osticks
01207                                                                - LMIC.bcnRxtime) << 8)),
01208                                      e_.time    = MAIN::CDEV->ostime2ustime(LMIC.bcninfo.txtime + BCN_INTV_osticks)));
01209             }
01210             oidx += 4;
01211             continue;
01212         }
01213         }
01214         EV(specCond, ERR, (e_.reason = EV::specCond_t::BAD_MAC_CMD,
01215                            e_.eui    = MAIN::CDEV->getEui(),
01216                            e_.info   = Base::lsbf4(&d[pend]),
01217                            e_.info2  = Base::msbf4(&opts[oidx])));
01218         break;
01219     }
01220     
01221     if( oidx != olen ) {
01222         EV(specCond, ERR, (e_.reason = EV::specCond_t::CORRUPTED_FRAME,
01223                            e_.eui    = MAIN::CDEV->getEui(),
01224                            e_.info   = 0x1000000 + (oidx) + (olen<<8)));
01225     }
01226 
01227     if( !replayConf ) {
01228         // Handle payload only if not a replay
01229         // Decrypt payload - if any
01230         
01231         //if( port >= 0  &&  pend-poff > 0 )
01232             //aes_cipher(port <= 0 ? LMIC.nwkKey : LMIC.artKey, LMIC.devaddr, seqno, /*dn*/1, d+poff, pend-poff);
01233 
01234         if( port == 0  &&  pend-poff > 0 )
01235         {
01236             //debug_str("Payload decrypting...\r\n");
01237             aes_cipher(port <= 0 ? LMIC.nwkKey : LMIC.artKey, LMIC.devaddr, seqno, /*dn*/1, d+poff, pend-poff);
01238         }
01239         
01240         EV(dfinfo, DEBUG, (e_.deveui  = MAIN::CDEV->getEui(),
01241                            e_.devaddr = LMIC.devaddr,
01242                            e_.seqno   = seqno,
01243                            e_.flags   = (port < 0 ? EV::dfinfo_t::NOPORT : 0) | EV::dfinfo_t::DN,
01244                            e_.mic     = Base::lsbf4(&d[pend]),
01245                            e_.hdr     = d[LORA::OFF_DAT_HDR],
01246                            e_.fct     = d[LORA::OFF_DAT_FCT],
01247                            e_.port    = port,
01248                            e_.plen    = dlen,
01249                            e_.opts.length = olen,
01250                            memcpy(&e_.opts[0], opts, olen)));
01251     } else {
01252         EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_REPLAY,
01253                             e_.eui    = MAIN::CDEV->getEui(),
01254                             e_.info   = Base::lsbf4(&d[pend]),
01255                             e_.info2  = seqno));
01256     }
01257 
01258     if( // NWK acks but we don't have a frame pending
01259         (ackup && LMIC.txCnt == 0) ||
01260         // We sent up confirmed and we got a response in DNW1/DNW2
01261         // BUT it did not carry an ACK - this should never happen
01262         // Do not resend and assume frame was not ACKed.
01263         (!ackup && LMIC.txCnt != 0) ) {
01264         EV(specCond, ERR, (e_.reason = EV::specCond_t::SPURIOUS_ACK,
01265                            e_.eui    = MAIN::CDEV->getEui(),
01266                            e_.info   = seqno,
01267                            e_.info2  = ackup));
01268     }
01269 
01270     if( LMIC.txCnt != 0 ) // we requested an ACK
01271         LMIC.txrxFlags |= ackup ? TXRX_ACK : TXRX_NACK;
01272 
01273     if( port < 0 ) {
01274         LMIC.txrxFlags |= TXRX_NOPORT;
01275         LMIC.dataBeg = poff;
01276         LMIC.dataLen = 0;
01277     } else {
01278         LMIC.txrxFlags |= TXRX_PORT;
01279         LMIC.dataBeg = poff;
01280         LMIC.dataLen = pend-poff;
01281     }
01282     //debug_str("decodeFrame End: ");
01283     //debug_buf(LMIC.frame, LMIC.dataLen);
01284 
01285 //  PANY
01286     if ((port == 0) && (olen == 0))
01287     { 
01288         oidx = 0;
01289         olen = LMIC.dataLen;
01290         //debug_str("Port 0!!!\r\n");
01291         opts = LMIC.frame + LMIC.dataBeg;
01292         
01293         //debug_val("Data Len: ", LMIC.dataLen);
01294         while( oidx < olen ) {
01295     //      debug_str("Port: ");
01296             debug_str("Process OPTS...");
01297             debug_hex(opts[oidx]);
01298             debug_str("\r\n");
01299             switch( opts[oidx] ) {
01300             case MCMD_LCHK_ANS: {
01301                 debug_str("MCMD_LCHK_ANS\r\n");
01302                 //int gwmargin = opts[oidx+1];
01303                 //int ngws = opts[oidx+2];
01304                 oidx += 3;
01305                 continue;
01306             }
01307             case MCMD_LADR_REQ: {
01308                 debug_str("MCMD_LADR_REQ\r\n");
01309                 u1_t p1     = opts[oidx+1];            // txpow + DR
01310                 u2_t chmap  = os_rlsbf2(&opts[oidx+2]);// list of enabled channels
01311                 u1_t chpage = opts[oidx+4] & MCMD_LADR_CHPAGE_MASK;     // channel page
01312                 u1_t uprpt  = opts[oidx+4] & MCMD_LADR_REPEAT_MASK;     // up repeat count
01313                 oidx += 5;
01314     
01315                 LMIC.ladrAns = 0x80 |     // Include an answer into next frame up
01316                     MCMD_LADR_ANS_POWACK | MCMD_LADR_ANS_CHACK | MCMD_LADR_ANS_DRACK;
01317                 if( !mapChannels(chpage, chmap) )
01318                     LMIC.ladrAns &= ~MCMD_LADR_ANS_CHACK;
01319                 dr_t dr = (dr_t)(p1>>MCMD_LADR_DR_SHIFT);
01320                 if( !validDR(dr) ) {
01321                     LMIC.ladrAns &= ~MCMD_LADR_ANS_DRACK;
01322                     EV(specCond, ERR, (e_.reason = EV::specCond_t::BAD_MAC_CMD,
01323                                        e_.eui    = MAIN::CDEV->getEui(),
01324                                        e_.info   = Base::lsbf4(&d[pend]),
01325                                        e_.info2  = Base::msbf4(&opts[oidx-4])));
01326                 }
01327                 if( (LMIC.ladrAns & 0x7F) == (MCMD_LADR_ANS_POWACK | MCMD_LADR_ANS_CHACK | MCMD_LADR_ANS_DRACK) ) {
01328                     // Nothing went wrong - use settings
01329                     LMIC.upRepeat = uprpt;
01330                     setDrTxpow(DRCHG_NWKCMD, dr, pow2dBm(p1));
01331                 }
01332                 LMIC.adrChanged = 1;  // Trigger an ACK to NWK
01333                 
01334                 //debug_str("decodeFrame: ");
01335                 //debug_buf(LMIC.frame, LMIC.dataLen);
01336     
01337                 continue;
01338             }
01339             case MCMD_DEVS_REQ: {
01340                 debug_str("DEVS_REQ\r\n");
01341                 LMIC.devsAns = 1;
01342                 oidx += 1;
01343                 continue;
01344             }
01345             case MCMD_DN2P_SET: {
01346                 debug_str("MCMD_DN2P_SET\r\n");
01347                 dr_t dr = (dr_t)(opts[oidx+1] & 0x0F);
01348                 u4_t freq = convFreq(&opts[oidx+2]);
01349                 oidx += 5;
01350                 LMIC.dn2Ans = 0x80;   // answer pending
01351                 if( validDR(dr) )
01352                     LMIC.dn2Ans |= MCMD_DN2P_ANS_DRACK;
01353                 if( freq != 0 )
01354                     LMIC.dn2Ans |= MCMD_DN2P_ANS_CHACK;
01355                 if( LMIC.dn2Ans == (0x80|MCMD_DN2P_ANS_DRACK|MCMD_DN2P_ANS_CHACK) ) {
01356                     LMIC.dn2Dr = dr;
01357                     LMIC.dn2Freq = freq;
01358                     DO_DEVDB(LMIC.dn2Dr,dn2Dr);
01359                     DO_DEVDB(LMIC.dn2Freq,dn2Freq);
01360                 }
01361                 continue;
01362             }
01363             case MCMD_DCAP_REQ: {
01364                 debug_str("MCMD_DCAP_REQ\r\n");
01365                 u1_t cap = opts[oidx+1];
01366                 oidx += 2;
01367                 // A value cap=0xFF means device is OFF unless enabled again manually.
01368                 if( cap==0xFF )
01369                     LMIC.opmode |= OP_SHUTDOWN;  // stop any sending
01370                 LMIC.globalDutyRate  = cap & 0xF;
01371                 LMIC.globalDutyAvail = os_getTime();
01372                 DO_DEVDB(cap,dutyCap);
01373                 LMIC.dutyCapAns = 1;
01374                 continue;
01375             }
01376             case MCMD_SNCH_REQ: {
01377                 debug_str("MCMD_SNCH_REQ\r\n");
01378                 u1_t chidx = opts[oidx+1];  // channel
01379                 u4_t freq  = convFreq(&opts[oidx+2]); // freq
01380                 u1_t drs   = opts[oidx+5];  // datarate span
01381                 LMIC.snchAns = 0x80;
01382                 if( freq != 0 && LMIC_setupChannel(chidx, freq, DR_RANGE_MAP(drs&0xF,drs>>4), -1) )
01383                     LMIC.snchAns |= MCMD_SNCH_ANS_DRACK|MCMD_SNCH_ANS_FQACK;
01384                 oidx += 6;
01385                 continue;
01386             }
01387             case MCMD_PING_SET: {
01388                 debug_str("MCMD_PING_SET\r\n");
01389                 u4_t freq = convFreq(&opts[oidx+1]);
01390                 oidx += 4;
01391                 u1_t flags = 0x80;
01392                 if( freq != 0 ) {
01393                     flags |= MCMD_PING_ANS_FQACK;
01394                     LMIC.ping.freq = freq;
01395                     DO_DEVDB(LMIC.ping.intvExp, pingIntvExp);
01396                     DO_DEVDB(LMIC.ping.freq, pingFreq);
01397                     DO_DEVDB(LMIC.ping.dr, pingDr);
01398                 }
01399                 LMIC.pingSetAns = flags;
01400                 continue;
01401             }
01402             case MCMD_BCNI_ANS: {
01403                 debug_str("MCMD_BCNI_ANS\r\n");
01404                 // Ignore if tracking already enabled
01405                 if( (LMIC.opmode & OP_TRACK) == 0 ) {
01406                     LMIC.bcnChnl = opts[oidx+3];
01407                     // Enable tracking - bcninfoTries
01408                     LMIC.opmode |= OP_TRACK;
01409                     // Cleared later in txComplete handling - triggers EV_BEACON_FOUND
01410                     ASSERT(LMIC.bcninfoTries!=0);
01411                     // Setup RX parameters
01412                     LMIC.bcninfo.txtime = (LMIC.rxtime
01413                                            + ms2osticks(os_rlsbf2(&opts[oidx+1]) * MCMD_BCNI_TUNIT)
01414                                            + ms2osticksCeil(MCMD_BCNI_TUNIT/2)
01415                                            - BCN_INTV_osticks);
01416                     LMIC.bcninfo.flags = 0;  // txtime above cannot be used as reference (BCN_PARTIAL|BCN_FULL cleared)
01417                     calcBcnRxWindowFromMillis(MCMD_BCNI_TUNIT,1);  // error of +/-N ms 
01418     
01419                     EV(lostFrame, INFO, (e_.reason  = EV::lostFrame_t::MCMD_BCNI_ANS,
01420                                          e_.eui     = MAIN::CDEV->getEui(),
01421                                          e_.lostmic = Base::lsbf4(&d[pend]),
01422                                          e_.info    = (LMIC.missedBcns |
01423                                                        (osticks2us(LMIC.bcninfo.txtime + BCN_INTV_osticks
01424                                                                    - LMIC.bcnRxtime) << 8)),
01425                                          e_.time    = MAIN::CDEV->ostime2ustime(LMIC.bcninfo.txtime + BCN_INTV_osticks)));
01426                 }
01427                 oidx += 4;
01428                 continue;
01429             }
01430             }
01431             EV(specCond, ERR, (e_.reason = EV::specCond_t::BAD_MAC_CMD,
01432                                e_.eui    = MAIN::CDEV->getEui(),
01433                                e_.info   = Base::lsbf4(&d[pend]),
01434                                e_.info2  = Base::msbf4(&opts[oidx])));
01435             break;
01436         }
01437     }
01438 //  PANY 
01439 
01440     return 1;
01441 }
01442 
01443 
01444 // ================================================================================
01445 // TX/RX transaction support
01446 
01447 
01448 static void setupRx2 (void) {
01449     LMIC.txrxFlags = TXRX_DNW2;
01450     LMIC.rps = dndr2rps(LMIC.dn2Dr);
01451     LMIC.freq = LMIC.dn2Freq;
01452     LMIC.dataLen = 0;
01453     os_radio(RADIO_RX);
01454 }
01455 
01456 
01457 static void schedRx2 (ostime_t delay, osjobcb_t func) {
01458     // Add 1.5 symbols we need 5 out of 8. Try to sync 1.5 symbols into the preamble.
01459     LMIC.rxtime = LMIC.txend + delay + (PAMBL_SYMS-MINRX_SYMS)*dr2hsym(LMIC.dn2Dr);
01460     os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, func);
01461 }
01462 
01463 static void setupRx1 (osjobcb_t func) {
01464     LMIC.txrxFlags = TXRX_DNW1;
01465     // Turn LMIC.rps from TX over to RX
01466     LMIC.rps = setNocrc(LMIC.rps,1);
01467     LMIC.dataLen = 0;
01468     LMIC.osjob.func = func;
01469     os_radio(RADIO_RX);
01470 }
01471 
01472 
01473 // Called by HAL once TX complete and delivers exact end of TX time stamp in LMIC.rxtime
01474 static void txDone (ostime_t delay, osjobcb_t func) {
01475     if( (LMIC.opmode & (OP_TRACK|OP_PINGABLE|OP_PINGINI)) == (OP_TRACK|OP_PINGABLE) ) {
01476         rxschedInit(&LMIC.ping);    // note: reuses LMIC.frame buffer!
01477         LMIC.opmode |= OP_PINGINI;
01478     }
01479     // Change RX frequency / rps (US only) before we increment txChnl
01480     setRx1Params();
01481     // LMIC.rxsyms carries the TX datarate (can be != LMIC.datarate [confirm retries etc.])
01482     // Setup receive - LMIC.rxtime is preloaded with 1.5 symbols offset to tune
01483     // into the middle of the 8 symbols preamble.
01484 #if defined(CFG_eu868)
01485     if( /* TX datarate */LMIC.rxsyms == DR_FSK ) {
01486         LMIC.rxtime = LMIC.txend + delay - PRERX_FSK*us2osticksRound(160);
01487         LMIC.rxsyms = RXLEN_FSK;
01488     }
01489     else
01490 #endif
01491     {
01492         LMIC.rxtime = LMIC.txend + delay + (PAMBL_SYMS-MINRX_SYMS)*dr2hsym(LMIC.dndr);
01493         LMIC.rxsyms = MINRX_SYMS;
01494     }
01495     os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, func);
01496 }
01497 
01498 
01499 // ======================================== Join frames
01500 
01501 
01502 static void onJoinFailed (xref2osjob_t osjob) {
01503     // Notify app - must call LMIC_reset() to stop joining
01504     // otherwise join procedure continues.
01505     reportEvent(EV_JOIN_FAILED);
01506 }
01507 
01508 
01509 static bit_t processJoinAccept (void) {
01510     ASSERT(LMIC.txrxFlags != TXRX_DNW1 || LMIC.dataLen != 0);
01511     ASSERT((LMIC.opmode & OP_TXRXPEND)!=0);
01512 
01513     if( LMIC.dataLen == 0 ) {
01514       nojoinframe:
01515         if( (LMIC.opmode & OP_JOINING) == 0 ) {
01516             ASSERT((LMIC.opmode & OP_REJOIN) != 0);
01517             // REJOIN attempt for roaming
01518             LMIC.opmode &= ~(OP_REJOIN|OP_TXRXPEND);
01519             if( LMIC.rejoinCnt < 10 )
01520                 LMIC.rejoinCnt++;
01521             reportEvent(EV_REJOIN_FAILED);
01522             return 1;
01523         }
01524         LMIC.opmode &= ~OP_TXRXPEND;
01525         ostime_t delay = nextJoinState();
01526         EV(devCond, DEBUG, (e_.reason = EV::devCond_t::NO_JACC,
01527                             e_.eui    = MAIN::CDEV->getEui(),
01528                             e_.info   = LMIC.datarate|DR_PAGE,
01529                             e_.info2  = osticks2ms(delay)));
01530         // Build next JOIN REQUEST with next engineUpdate call
01531         // Optionally, report join failed.
01532         // Both after a random/chosen amount of ticks.
01533         os_setTimedCallback(&LMIC.osjob, os_getTime()+delay,
01534                             (delay&1) != 0
01535                             ? FUNC_ADDR(onJoinFailed)      // one JOIN iteration done and failed
01536                             : FUNC_ADDR(runEngineUpdate)); // next step to be delayed
01537         return 1;
01538     }
01539     u1_t hdr  = LMIC.frame[0];
01540     u1_t dlen = LMIC.dataLen;
01541     u4_t mic  = os_rlsbf4(&LMIC.frame[dlen-4]); // safe before modified by encrypt!
01542     if( (dlen != LEN_JA && dlen != LEN_JAEXT)
01543         || (hdr & (HDR_FTYPE|HDR_MAJOR)) != (HDR_FTYPE_JACC|HDR_MAJOR_V1) ) {
01544         EV(specCond, ERR, (e_.reason = EV::specCond_t::UNEXPECTED_FRAME,
01545                            e_.eui    = MAIN::CDEV->getEui(),
01546                            e_.info   = dlen < 4 ? 0 : mic,
01547                            e_.info2  = hdr + (dlen<<8)));
01548       badframe:
01549         if( (LMIC.txrxFlags & TXRX_DNW1) != 0 )
01550             return 0;
01551         goto nojoinframe;
01552     }
01553     aes_encrypt(LMIC.frame+1, dlen-1);
01554     if( !aes_verifyMic0(LMIC.frame, dlen-4) ) {
01555         EV(specCond, ERR, (e_.reason = EV::specCond_t::JOIN_BAD_MIC,
01556                            e_.info   = mic));
01557         goto badframe;
01558     }
01559 
01560     u4_t addr = os_rlsbf4(LMIC.frame+OFF_JA_DEVADDR);
01561     LMIC.devaddr = addr;
01562     LMIC.netid = os_rlsbf4(&LMIC.frame[OFF_JA_NETID]) & 0xFFFFFF;
01563 
01564 #if defined(CFG_eu868)
01565     initDefaultChannels(0);
01566 #endif
01567     if( dlen > LEN_JA ) {
01568         dlen = OFF_CFLIST;
01569 #if defined(CFG_eu868)
01570         u1_t chidx=3;
01571 #elif defined(CFG_us915)
01572         u1_t chidx=72;
01573 #endif
01574         for( ; chidx<8; chidx++, dlen+=3 )
01575             LMIC_setupChannel(chidx, os_rlsbf4(&LMIC.frame[dlen-1]) >> 8, 0, -1);
01576     }
01577 
01578     // already incremented when JOIN REQ got sent off
01579     aes_sessKeys(LMIC.devNonce-1, &LMIC.frame[OFF_JA_ARTNONCE], LMIC.nwkKey, LMIC.artKey);
01580     DO_DEVDB(LMIC.netid,   netid);
01581     DO_DEVDB(LMIC.devaddr, devaddr);
01582     DO_DEVDB(LMIC.nwkKey,  nwkkey);
01583     DO_DEVDB(LMIC.artKey,  artkey);
01584 
01585     EV(joininfo, INFO, (e_.arteui  = MAIN::CDEV->getArtEui(),
01586                         e_.deveui  = MAIN::CDEV->getEui(),
01587                         e_.devaddr = LMIC.devaddr,
01588                         e_.oldaddr = oldaddr,
01589                         e_.nonce   = LMIC.devNonce-1,
01590                         e_.mic     = mic,
01591                         e_.reason  = ((LMIC.opmode & OP_REJOIN) != 0
01592                                       ? EV::joininfo_t::REJOIN_ACCEPT
01593                                       : EV::joininfo_t::ACCEPT)));
01594     
01595     ASSERT((LMIC.opmode & (OP_JOINING|OP_REJOIN))!=0);
01596     if( (LMIC.opmode & OP_REJOIN) != 0 ) {
01597         // Lower DR every try below current UP DR
01598         LMIC.datarate = lowerDR(LMIC.datarate, LMIC.rejoinCnt);
01599     }
01600     LMIC.opmode &= ~(OP_JOINING|OP_TRACK|OP_REJOIN|OP_TXRXPEND|OP_PINGINI) | OP_NEXTCHNL;
01601     stateJustJoined();
01602     reportEvent(EV_JOINED);
01603     return 1;
01604 }
01605 
01606 
01607 static void processRx2Jacc (xref2osjob_t osjob) {
01608     if( LMIC.dataLen == 0 )
01609         LMIC.txrxFlags = 0;  // nothing in 1st/2nd DN slot
01610     processJoinAccept();
01611 }
01612 
01613 
01614 static void setupRx2Jacc (xref2osjob_t osjob) {
01615     LMIC.osjob.func = FUNC_ADDR(processRx2Jacc);
01616     setupRx2();
01617 }
01618 
01619 
01620 static void processRx1Jacc (xref2osjob_t osjob) {
01621     if( LMIC.dataLen == 0 || !processJoinAccept() )
01622         schedRx2(DELAY_JACC2_osticks, FUNC_ADDR(setupRx2Jacc));
01623 }
01624 
01625 
01626 static void setupRx1Jacc (xref2osjob_t osjob) {
01627     setupRx1(FUNC_ADDR(processRx1Jacc));
01628 }
01629 
01630 
01631 static void jreqDone (xref2osjob_t osjob) {
01632     txDone(DELAY_JACC1_osticks, FUNC_ADDR(setupRx1Jacc));
01633 }
01634 
01635 // ======================================== Data frames
01636 
01637 // Fwd decl.
01638 static bit_t processDnData(void);
01639 
01640 static void processRx2DnDataDelay (xref2osjob_t osjob) {
01641     processDnData();
01642 }
01643 
01644 static void processRx2DnData (xref2osjob_t osjob) {
01645     if( LMIC.dataLen == 0 ) {
01646         LMIC.txrxFlags = 0;  // nothing in 1st/2nd DN slot
01647         // Delay callback processing to avoid up TX while gateway is txing our missed frame! 
01648         // Since DNW2 uses SF12 by default we wait 3 secs.
01649         os_setTimedCallback(&LMIC.osjob,
01650                             (os_getTime() + DNW2_SAFETY_ZONE + rndDelay(2)),
01651                             processRx2DnDataDelay);
01652         return;
01653     }
01654     processDnData();
01655 }
01656 
01657 
01658 static void setupRx2DnData (xref2osjob_t osjob) {
01659     LMIC.osjob.func = FUNC_ADDR(processRx2DnData);
01660     setupRx2();
01661 }
01662 
01663 
01664 static void processRx1DnData (xref2osjob_t osjob) {
01665     if( LMIC.dataLen == 0 || !processDnData() )
01666         schedRx2(DELAY_DNW2_osticks, FUNC_ADDR(setupRx2DnData));
01667 }
01668 
01669 
01670 static void setupRx1DnData (xref2osjob_t osjob) {
01671     setupRx1(FUNC_ADDR(processRx1DnData));
01672 }
01673 
01674 
01675 static void updataDone (xref2osjob_t osjob) {
01676     txDone(DELAY_DNW1_osticks, FUNC_ADDR(setupRx1DnData));
01677 }
01678 
01679 // ======================================== 
01680 
01681 
01682 static void buildDataFrame (void) {
01683     //debug_str("buildDataFrame\r\n");
01684     bit_t txdata = ((LMIC.opmode & (OP_TXDATA|OP_POLL)) != OP_POLL);
01685     u1_t dlen = txdata ? LMIC.pendTxLen : 0;
01686 
01687     // Piggyback MAC options
01688     // Prioritize by importance
01689     int  end = OFF_DAT_OPTS;
01690     if( (LMIC.opmode & (OP_TRACK|OP_PINGABLE)) == (OP_TRACK|OP_PINGABLE) ) {
01691         // Indicate pingability in every UP frame
01692         LMIC.frame[end] = MCMD_PING_IND;
01693         LMIC.frame[end+1] = LMIC.ping.dr | (LMIC.ping.intvExp<<4);
01694         end += 2;
01695     }
01696     if( LMIC.dutyCapAns ) {
01697         LMIC.frame[end] = MCMD_DCAP_ANS;
01698         end += 1;
01699         LMIC.dutyCapAns = 0;
01700     }
01701     if( LMIC.dn2Ans ) {
01702         LMIC.frame[end+0] = MCMD_DN2P_ANS;
01703         LMIC.frame[end+1] = LMIC.dn2Ans & ~MCMD_DN2P_ANS_RFU;
01704         end += 2;
01705         LMIC.dn2Ans = 0;
01706     }
01707     if( LMIC.devsAns ) {  // answer to device status
01708         debug_str("Send Device Status\r\n");
01709         LMIC.frame[end+0] = MCMD_DEVS_ANS;
01710         LMIC.frame[end+1] = LMIC.margin;
01711         LMIC.frame[end+2] = os_getBattLevel();
01712         end += 3;
01713         LMIC.devsAns = 0;
01714     }
01715     if( LMIC.ladrAns ) {  // answer to ADR change
01716         LMIC.frame[end+0] = MCMD_LADR_ANS;
01717         LMIC.frame[end+1] = LMIC.ladrAns & ~MCMD_LADR_ANS_RFU;
01718         end += 2;
01719         LMIC.ladrAns = 0;
01720     }
01721     if( LMIC.bcninfoTries > 0 ) {
01722         LMIC.frame[end] = MCMD_BCNI_REQ;
01723         end += 1;
01724     }
01725     if( LMIC.adrChanged ) {
01726         if( LMIC.adrAckReq < 0 )
01727             LMIC.adrAckReq = 0;
01728         LMIC.adrChanged = 0;
01729     }
01730     if( LMIC.pingSetAns != 0 ) {
01731         LMIC.frame[end+0] = MCMD_PING_ANS;
01732         LMIC.frame[end+1] = LMIC.pingSetAns & ~MCMD_PING_ANS_RFU;
01733         end += 2;
01734         LMIC.pingSetAns = 0;
01735     }
01736     if( LMIC.snchAns ) {
01737         LMIC.frame[end+0] = MCMD_SNCH_ANS;
01738         LMIC.frame[end+1] = LMIC.snchAns & ~MCMD_SNCH_ANS_RFU;
01739         end += 2;
01740         LMIC.snchAns = 0;
01741     }
01742     ASSERT(end <= OFF_DAT_OPTS+16);
01743 
01744     u1_t flen = end + (txdata ? 5+dlen : 4);
01745     if( flen > MAX_LEN_FRAME ) {
01746         // Options and payload too big - delay payload
01747         txdata = 0;
01748         flen = end+4;
01749     }
01750     LMIC.frame[OFF_DAT_HDR] = HDR_FTYPE_DAUP | HDR_MAJOR_V1;
01751     LMIC.frame[OFF_DAT_FCT] = (LMIC.dnConf | LMIC.adrEnabled
01752                               | (LMIC.adrAckReq >= 0 ? FCT_ADRARQ : 0)
01753                               | (end-OFF_DAT_OPTS));
01754     os_wlsbf4(LMIC.frame+OFF_DAT_ADDR,  LMIC.devaddr);
01755 
01756     if( LMIC.txCnt == 0 ) {
01757         LMIC.seqnoUp += 1;
01758         DO_DEVDB(LMIC.seqnoUp,seqnoUp);
01759     } else {
01760         EV(devCond, INFO, (e_.reason = EV::devCond_t::RE_TX,
01761                            e_.eui    = MAIN::CDEV->getEui(),
01762                            e_.info   = LMIC.seqnoUp-1,
01763                            e_.info2  = ((LMIC.txCnt+1) |
01764                                         (DRADJUST[LMIC.txCnt+1] << 8) |
01765                                         ((LMIC.datarate|DR_PAGE)<<16))));
01766     }
01767     os_wlsbf2(LMIC.frame+OFF_DAT_SEQNO, LMIC.seqnoUp-1);
01768 
01769     // Clear pending DN confirmation
01770     LMIC.dnConf = 0;
01771 
01772     if( txdata ) {
01773         if( LMIC.pendTxConf ) {
01774             // Confirmed only makes sense if we have a payload (or at least a port)
01775             LMIC.frame[OFF_DAT_HDR] = HDR_FTYPE_DCUP | HDR_MAJOR_V1;
01776             if( LMIC.txCnt == 0 ) LMIC.txCnt = 1;
01777         }
01778         LMIC.frame[end] = LMIC.pendTxPort;
01779         os_copyMem(LMIC.frame+end+1, LMIC.pendTxData, dlen);
01780         aes_cipher(LMIC.pendTxPort==0 ? LMIC.nwkKey : LMIC.artKey,
01781                    LMIC.devaddr, LMIC.seqnoUp-1,
01782                    /*up*/0, LMIC.frame+end+1, dlen);
01783     }
01784     aes_appendMic(LMIC.nwkKey, LMIC.devaddr, LMIC.seqnoUp-1, /*up*/0, LMIC.frame, flen-4);
01785 
01786     EV(dfinfo, DEBUG, (e_.deveui  = MAIN::CDEV->getEui(),
01787                        e_.devaddr = LMIC.devaddr,
01788                        e_.seqno   = LMIC.seqnoUp-1,
01789                        e_.flags   = (LMIC.pendTxPort < 0 ? EV::dfinfo_t::NOPORT : EV::dfinfo_t::NOP),
01790                        e_.mic     = Base::lsbf4(&LMIC.frame[flen-4]),
01791                        e_.hdr     = LMIC.frame[LORA::OFF_DAT_HDR],
01792                        e_.fct     = LMIC.frame[LORA::OFF_DAT_FCT],
01793                        e_.port    = LMIC.pendTxPort,
01794                        e_.plen    = txdata ? dlen : 0,
01795                        e_.opts.length = end-LORA::OFF_DAT_OPTS,
01796                        memcpy(&e_.opts[0], LMIC.frame+LORA::OFF_DAT_OPTS, end-LORA::OFF_DAT_OPTS)));
01797     LMIC.dataLen = flen;
01798 }
01799 
01800 
01801 // Callback from HAL during scan mode or when job timer expires.
01802 static void onBcnRx (xref2osjob_t job) {
01803     // If we arrive via job timer make sure to put radio to rest.
01804     os_radio(RADIO_RST);
01805     os_clearCallback(&LMIC.osjob);
01806     if( LMIC.dataLen == 0 ) {
01807         // Nothing received - timeout
01808         LMIC.opmode &= ~(OP_SCAN | OP_TRACK);
01809         reportEvent(EV_SCAN_TIMEOUT);
01810         return;
01811     }
01812     if( decodeBeacon() <= 0 ) {
01813         // Something is wrong with the beacon - continue scan
01814         LMIC.dataLen = 0;
01815         os_radio(RADIO_RXON);
01816         os_setTimedCallback(&LMIC.osjob, LMIC.bcninfo.txtime, FUNC_ADDR(onBcnRx));
01817         return;
01818     }
01819     // Found our 1st beacon
01820     // We don't have a previous beacon to calc some drift - assume
01821     // an max error of 13ms = 128sec*100ppm which is roughly +/-100ppm
01822     calcBcnRxWindowFromMillis(13,1);
01823     LMIC.opmode &= ~OP_SCAN;          // turn SCAN off
01824     LMIC.opmode |=  OP_TRACK;         // auto enable tracking
01825     reportEvent(EV_BEACON_FOUND);    // can be disabled in callback
01826 }
01827 
01828 
01829 // Enable receiver to listen to incoming beacons
01830 // netid defines when scan stops (any or specific beacon)
01831 // This mode ends with events: EV_SCAN_TIMEOUT/EV_SCAN_BEACON
01832 // Implicitely cancels any pending TX/RX transaction.
01833 // Also cancels an onpoing joining procedure.
01834 static void startScan (void) {
01835     ASSERT(LMIC.devaddr!=0 && (LMIC.opmode & OP_JOINING)==0);
01836     if( (LMIC.opmode & OP_SHUTDOWN) != 0 )
01837         return;
01838     // Cancel onging TX/RX transaction
01839     LMIC.txCnt = LMIC.dnConf = LMIC.bcninfo.flags = 0;
01840     LMIC.opmode = (LMIC.opmode | OP_SCAN) & ~(OP_TXRXPEND);
01841     setBcnRxParams();
01842     LMIC.rxtime = LMIC.bcninfo.txtime = os_getTime() + sec2osticks(BCN_INTV_sec+1);
01843     os_setTimedCallback(&LMIC.osjob, LMIC.rxtime, FUNC_ADDR(onBcnRx));
01844     os_radio(RADIO_RXON);
01845 }
01846 
01847 
01848 bit_t LMIC_enableTracking (u1_t tryBcnInfo) {
01849     if( (LMIC.opmode & (OP_SCAN|OP_TRACK|OP_SHUTDOWN)) != 0 )
01850         return 0;  // already in progress or failed to enable
01851     // If BCN info requested from NWK then app has to take are
01852     // of sending data up so that MCMD_BCNI_REQ can be attached.
01853     if( (LMIC.bcninfoTries = tryBcnInfo) == 0 )
01854         startScan();
01855     return 1;  // enabled
01856 }
01857 
01858 
01859 void LMIC_disableTracking (void) {
01860     LMIC.opmode &= ~(OP_SCAN|OP_TRACK);
01861     LMIC.bcninfoTries = 0;
01862     engineUpdate();
01863 }
01864 
01865 
01866 // ================================================================================
01867 //
01868 // Join stuff
01869 //
01870 // ================================================================================
01871 
01872 static void buildJoinRequest (u1_t ftype) {
01873     // Do not use pendTxData since we might have a pending
01874     // user level frame in there. Use RX holding area instead.
01875     xref2u1_t d = LMIC.frame;
01876     d[OFF_JR_HDR] = ftype;
01877     os_getArtEui(d + OFF_JR_ARTEUI);
01878     os_getDevEui(d + OFF_JR_DEVEUI);
01879     os_wlsbf2(d + OFF_JR_DEVNONCE, LMIC.devNonce);
01880     aes_appendMic0(d, OFF_JR_MIC);
01881 
01882     EV(joininfo,INFO,(e_.deveui  = MAIN::CDEV->getEui(),
01883                       e_.arteui  = MAIN::CDEV->getArtEui(),
01884                       e_.nonce   = LMIC.devNonce,
01885                       e_.oldaddr = LMIC.devaddr,
01886                       e_.mic     = Base::lsbf4(&d[LORA::OFF_JR_MIC]),
01887                       e_.reason  = ((LMIC.opmode & OP_REJOIN) != 0
01888                                     ? EV::joininfo_t::REJOIN_REQUEST
01889                                     : EV::joininfo_t::REQUEST)));
01890     LMIC.dataLen = LEN_JR;
01891     LMIC.devNonce++;
01892     DO_DEVDB(LMIC.devNonce,devNonce);
01893 }
01894 
01895 static void startJoining (xref2osjob_t osjob) {
01896     reportEvent(EV_JOINING);
01897 }
01898 
01899 // Start join procedure if not already joined.
01900 bit_t LMIC_startJoining (void) {
01901     if( LMIC.devaddr == 0 ) {
01902         // There should be no TX/RX going on
01903         ASSERT((LMIC.opmode & (OP_POLL|OP_TXRXPEND)) == 0);
01904         // Lift any previous duty limitation
01905         LMIC.globalDutyRate = 0;
01906         // Cancel scanning
01907         LMIC.opmode &= ~(OP_SCAN|OP_REJOIN|OP_LINKDEAD|OP_NEXTCHNL);
01908         // Setup state
01909         LMIC.rejoinCnt = LMIC.txCnt = LMIC.pendTxConf = 0;
01910         initJoinLoop();
01911         LMIC.opmode |= OP_JOINING;
01912         // reportEvent will call engineUpdate which then starts sending JOIN REQUESTS
01913         os_setCallback(&LMIC.osjob, FUNC_ADDR(startJoining));
01914         return 1;
01915     }
01916     return 0; // already joined
01917 }
01918 
01919 
01920 // ================================================================================
01921 //
01922 //
01923 //
01924 // ================================================================================
01925 
01926 static void processPingRx (xref2osjob_t osjob) {
01927     if( LMIC.dataLen != 0 ) {
01928         LMIC.txrxFlags = TXRX_PING;
01929         if( decodeFrame() ) {
01930             reportEvent(EV_RXCOMPLETE);
01931             return;
01932         }
01933     }
01934     // Pick next ping slot
01935     engineUpdate();
01936 }
01937 
01938 
01939 static bit_t processDnData (void) {
01940     ASSERT((LMIC.opmode & OP_TXRXPEND)!=0);
01941 
01942     if( LMIC.dataLen == 0 ) {
01943       norx:
01944         if( LMIC.txCnt != 0 ) {
01945             if( LMIC.txCnt < TXCONF_ATTEMPTS ) {
01946                 LMIC.txCnt += 1;
01947                 setDrTxpow(DRCHG_NOACK, lowerDR(LMIC.datarate, DRADJUST[LMIC.txCnt]), KEEP_TXPOW);
01948                 // Schedule another retransmission
01949                 txDelay(LMIC.rxtime, RETRY_PERIOD_secs);
01950                 LMIC.opmode &= ~OP_TXRXPEND;
01951                 engineUpdate();
01952                 return 1;
01953             }
01954             LMIC.txrxFlags = TXRX_NACK | TXRX_NOPORT;
01955         } else {
01956             // Nothing received - implies no port
01957             LMIC.txrxFlags = TXRX_NOPORT;
01958         }
01959         if( LMIC.adrAckReq != LINK_CHECK_OFF )
01960             LMIC.adrAckReq += 1;
01961         LMIC.dataBeg = LMIC.dataLen = 0;
01962       txcomplete:
01963         LMIC.opmode &= ~(OP_TXDATA|OP_TXRXPEND);
01964         if( (LMIC.txrxFlags & (TXRX_DNW1|TXRX_DNW2|TXRX_PING)) != 0  &&  (LMIC.opmode & OP_LINKDEAD) != 0 ) {
01965             LMIC.opmode &= ~OP_LINKDEAD;
01966             reportEvent(EV_LINK_ALIVE);
01967         }
01968         reportEvent(EV_TXCOMPLETE);
01969         // If we haven't heard from NWK in a while although we asked for a sign
01970         // assume link is dead - notify application and keep going
01971         if( LMIC.adrAckReq > LINK_CHECK_DEAD ) {
01972             // We haven't heard from NWK for some time although we
01973             // asked for a response for some time - assume we're disconnected. Lower DR one notch.
01974             EV(devCond, ERR, (e_.reason = EV::devCond_t::LINK_DEAD,
01975                               e_.eui    = MAIN::CDEV->getEui(),
01976                               e_.info   = LMIC.adrAckReq));
01977             setDrTxpow(DRCHG_NOADRACK, decDR((dr_t)LMIC.datarate), KEEP_TXPOW);
01978             LMIC.adrAckReq = LINK_CHECK_CONT;
01979             LMIC.opmode |= OP_REJOIN|OP_LINKDEAD;
01980             reportEvent(EV_LINK_DEAD);
01981         }
01982         // If this falls to zero the NWK did not answer our MCMD_BCNI_REQ commands - try full scan
01983         if( LMIC.bcninfoTries > 0 ) {
01984             if( (LMIC.opmode & OP_TRACK) != 0 ) {
01985                 reportEvent(EV_BEACON_FOUND);
01986                 LMIC.bcninfoTries = 0;
01987             }
01988             else if( --LMIC.bcninfoTries == 0 ) {
01989                 startScan();   // NWK did not answer - try scan
01990             }
01991         }
01992         return 1;
01993     }
01994     if( !decodeFrame() ) {
01995         if( (LMIC.txrxFlags & TXRX_DNW1) != 0 )
01996             return 0;
01997         goto norx;
01998     }
01999     goto txcomplete;
02000 }
02001 
02002 
02003 static void processBeacon (xref2osjob_t osjob) {
02004     ostime_t lasttx = LMIC.bcninfo.txtime;   // save here - decodeBeacon might overwrite
02005     u1_t flags = LMIC.bcninfo.flags;
02006     ev_t ev;
02007 
02008     if( LMIC.dataLen != 0 && decodeBeacon() >= 1 ) {
02009         ev = EV_BEACON_TRACKED;
02010         if( (flags & (BCN_PARTIAL|BCN_FULL)) == 0 ) {
02011             // We don't have a previous beacon to calc some drift - assume
02012             // an max error of 13ms = 128sec*100ppm which is roughly +/-100ppm
02013             calcBcnRxWindowFromMillis(13,0);
02014             goto rev;
02015         }
02016         // We have a previous BEACON to calculate some drift
02017         s2_t drift = BCN_INTV_osticks - (LMIC.bcninfo.txtime - lasttx);
02018         if( LMIC.missedBcns > 0 ) {
02019             drift = LMIC.drift + (drift - LMIC.drift) / (LMIC.missedBcns+1);
02020         }
02021         if( (LMIC.bcninfo.flags & BCN_NODRIFT) == 0 ) {
02022             s2_t diff = LMIC.drift - drift;
02023             if( diff < 0 ) diff = -diff;
02024             LMIC.lastDriftDiff = diff;
02025             if( LMIC.maxDriftDiff < diff )
02026                 LMIC.maxDriftDiff = diff;
02027             LMIC.bcninfo.flags &= ~BCN_NODDIFF;
02028         }
02029         LMIC.drift = drift;
02030         LMIC.missedBcns = LMIC.rejoinCnt = 0;
02031         LMIC.bcninfo.flags &= ~BCN_NODRIFT;
02032         EV(devCond,INFO,(e_.reason = EV::devCond_t::CLOCK_DRIFT,
02033                          e_.eui    = MAIN::CDEV->getEui(),
02034                          e_.info   = drift,
02035                          e_.info2  = /*occasion BEACON*/0));
02036         ASSERT((LMIC.bcninfo.flags & (BCN_PARTIAL|BCN_FULL)) != 0);
02037     } else {
02038         ev = EV_BEACON_MISSED;
02039         LMIC.bcninfo.txtime += BCN_INTV_osticks - LMIC.drift;
02040         LMIC.bcninfo.time   += BCN_INTV_sec;
02041         LMIC.missedBcns++;
02042         // Delay any possible TX after surmised beacon - it's there although we missed it
02043         txDelay(LMIC.bcninfo.txtime + BCN_RESERVE_osticks, 4);
02044         if( LMIC.missedBcns > MAX_MISSED_BCNS )
02045             LMIC.opmode |= OP_REJOIN;  // try if we can roam to another network
02046         if( LMIC.bcnRxsyms > MAX_RXSYMS ) {
02047             LMIC.opmode &= ~(OP_TRACK|OP_PINGABLE|OP_PINGINI|OP_REJOIN);
02048             reportEvent(EV_LOST_TSYNC);
02049             return;
02050         }
02051     }
02052     LMIC.bcnRxtime = LMIC.bcninfo.txtime + BCN_INTV_osticks - calcRxWindow(0,DR_BCN);
02053     LMIC.bcnRxsyms = LMIC.rxsyms;    
02054   rev:
02055 #if CFG_us915
02056     LMIC.bcnChnl = (LMIC.bcnChnl+1) & 7;
02057 #endif
02058     if( (LMIC.opmode & OP_PINGINI) != 0 )
02059         rxschedInit(&LMIC.ping);  // note: reuses LMIC.frame buffer!
02060     reportEvent(ev);
02061 }
02062 
02063 
02064 static void startRxBcn (xref2osjob_t osjob) {
02065     LMIC.osjob.func = FUNC_ADDR(processBeacon);
02066     os_radio(RADIO_RX);
02067 }
02068 
02069 
02070 static void startRxPing (xref2osjob_t osjob) {
02071     LMIC.osjob.func = FUNC_ADDR(processPingRx);
02072     os_radio(RADIO_RX);
02073 }
02074 
02075 
02076 // Decide what to do next for the MAC layer of a device
02077 static void engineUpdate (void) {
02078     
02079 //    debug_str("Engine Update\r\n");
02080     // Check for ongoing state: scan or TX/RX transaction
02081     if( (LMIC.opmode & (OP_SCAN|OP_TXRXPEND|OP_SHUTDOWN)) != 0 ) 
02082         return;
02083 
02084     if( LMIC.devaddr == 0 && (LMIC.opmode & OP_JOINING) == 0 ) {
02085         LMIC_startJoining();
02086         return;
02087     }
02088 
02089     ostime_t now    = os_getTime();
02090     ostime_t rxtime = 0;
02091     ostime_t txbeg  = 0;
02092 
02093     if( (LMIC.opmode & OP_TRACK) != 0 ) {
02094         // We are tracking a beacon
02095         ASSERT( now + RX_RAMPUP - LMIC.bcnRxtime <= 0 );
02096         rxtime = LMIC.bcnRxtime - RX_RAMPUP;
02097     }
02098 
02099     if( (LMIC.opmode & (OP_JOINING|OP_REJOIN|OP_TXDATA|OP_POLL)) != 0 ) {
02100         // Need to TX some data...
02101         // Assuming txChnl points to channel which first becomes available again.
02102         bit_t jacc = ((LMIC.opmode & (OP_JOINING|OP_REJOIN)) != 0 ? 1 : 0);
02103         // Find next suitable channel and return availability time
02104         if( (LMIC.opmode & OP_NEXTCHNL) != 0 ) {
02105             txbeg = LMIC.txend = nextTx(now);
02106             LMIC.opmode &= ~OP_NEXTCHNL;
02107         } else {
02108             txbeg = LMIC.txend;
02109         }
02110         // Delayed TX or waiting for duty cycle?
02111         if( (LMIC.globalDutyRate != 0 || (LMIC.opmode & OP_RNDTX) != 0)  &&  (txbeg - LMIC.globalDutyAvail) < 0 )
02112             txbeg = LMIC.globalDutyAvail;
02113         // If we're tracking a beacon...
02114         // then make sure TX-RX transaction is complete before beacon
02115         if( (LMIC.opmode & OP_TRACK) != 0 &&
02116             txbeg + (jacc ? JOIN_GUARD_osticks : TXRX_GUARD_osticks) - rxtime > 0 ) {
02117             // Not enough time to complete TX-RX before beacon - postpone after beacon.
02118             // In order to avoid clustering of postponed TX right after beacon randomize start!
02119             txDelay(rxtime + BCN_RESERVE_osticks, 16);
02120             txbeg = 0;
02121             goto checkrx;
02122         }
02123         // Earliest possible time vs overhead to setup radio
02124         if( txbeg - (now + TX_RAMPUP) < 0 ) {
02125             // We could send right now!
02126             dr_t txdr = (dr_t)LMIC.datarate;
02127             if( jacc ) {
02128                 u1_t ftype;
02129                 if( (LMIC.opmode & OP_REJOIN) != 0 ) {
02130                     txdr = lowerDR(txdr, LMIC.rejoinCnt);
02131                     ftype = HDR_FTYPE_REJOIN;
02132                 } else {
02133                     ftype = HDR_FTYPE_JREQ;
02134                 }
02135                 buildJoinRequest(ftype);
02136                 LMIC.osjob.func = FUNC_ADDR(jreqDone);
02137             } else {
02138                 if( LMIC.seqnoDn >= 0xFFFFFF80 ) {
02139                     // Imminent roll over - proactively reset MAC
02140                     EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_ROLL_OVER,
02141                                         e_.eui    = MAIN::CDEV->getEui(),
02142                                         e_.info   = LMIC.seqnoDn, 
02143                                         e_.info2  = 0));
02144                     // Device has to react! NWK will not roll over and just stop sending.
02145                     // Thus, we have N frames to detect a possible lock up.
02146                   reset:
02147                     os_setCallback(&LMIC.osjob, FUNC_ADDR(runReset));
02148                     return;
02149                 }
02150                 if( (LMIC.txCnt==0 && LMIC.seqnoUp == 0xFFFFFFFF) ) {
02151                     // Roll over of up seq counter
02152                     EV(specCond, ERR, (e_.reason = EV::specCond_t::UPSEQNO_ROLL_OVER,
02153                                        e_.eui    = MAIN::CDEV->getEui(),
02154                                        e_.info2  = LMIC.seqnoUp));
02155                     // Do not run RESET event callback from here!
02156                     // App code might do some stuff after send unaware of RESET.
02157                     goto reset;
02158                 }
02159                 buildDataFrame();
02160                 LMIC.osjob.func = FUNC_ADDR(updataDone);
02161             }
02162             LMIC.rps    = setCr(updr2rps(txdr), (cr_t)LMIC.errcr);
02163             LMIC.dndr   = txdr;  // carry TX datarate (can be != LMIC.datarate) over to txDone/setupRx1
02164             LMIC.opmode = (LMIC.opmode & ~(OP_POLL|OP_RNDTX)) | OP_TXRXPEND | OP_NEXTCHNL;
02165             updateTx(txbeg);
02166             os_radio(RADIO_TX);
02167             return;
02168         }
02169         // Cannot yet TX
02170         if( (LMIC.opmode & OP_TRACK) == 0 )
02171             goto txdelay; // We don't track the beacon - nothing else to do - so wait for the time to TX
02172         // Consider RX tasks
02173         if( txbeg == 0 ) // zero indicates no TX pending
02174             txbeg += 1;  // TX delayed by one tick (insignificant amount of time)
02175     } else {
02176         // No TX pending - no scheduled RX
02177         if( (LMIC.opmode & OP_TRACK) == 0 )
02178             return;
02179     }
02180 
02181     // Are we pingable?
02182   checkrx:
02183     if( (LMIC.opmode & OP_PINGINI) != 0 ) {
02184         // One more RX slot in this beacon period?
02185         if( rxschedNext(&LMIC.ping, now+RX_RAMPUP) ) {
02186             if( txbeg != 0  &&  (txbeg - LMIC.ping.rxtime) < 0 )
02187                 goto txdelay;
02188             LMIC.rxsyms  = LMIC.ping.rxsyms;
02189             LMIC.rxtime  = LMIC.ping.rxtime;
02190             LMIC.freq    = LMIC.ping.freq;
02191             LMIC.rps     = dndr2rps(LMIC.ping.dr);
02192             LMIC.dataLen = 0;
02193             ASSERT(LMIC.rxtime - now+RX_RAMPUP >= 0 );
02194             os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, FUNC_ADDR(startRxPing));
02195             return;
02196         }
02197         // no - just wait for the beacon
02198     }
02199 
02200     if( txbeg != 0  &&  (txbeg - rxtime) < 0 )
02201         goto txdelay;
02202 
02203     setBcnRxParams();
02204     LMIC.rxsyms = LMIC.bcnRxsyms;
02205     LMIC.rxtime = LMIC.bcnRxtime;
02206     if( now - rxtime >= 0 ) {
02207         LMIC.osjob.func = FUNC_ADDR(processBeacon);
02208         os_radio(RADIO_RX);
02209         return;
02210     }
02211     os_setTimedCallback(&LMIC.osjob, rxtime, FUNC_ADDR(startRxBcn));
02212     return;
02213 
02214   txdelay:
02215     EV(devCond, INFO, (e_.reason = EV::devCond_t::TX_DELAY,
02216                        e_.eui    = MAIN::CDEV->getEui(),
02217                        e_.info   = osticks2ms(txbeg-now),
02218                        e_.info2  = LMIC.seqnoUp-1));
02219     os_setTimedCallback(&LMIC.osjob, txbeg-TX_RAMPUP, FUNC_ADDR(runEngineUpdate));
02220 }
02221 
02222 
02223 void LMIC_setAdrMode (bit_t enabled) {
02224     LMIC.adrEnabled = enabled ? FCT_ADREN : 0;
02225 }
02226 
02227 
02228 //  Should we have/need an ext. API like this?
02229 void LMIC_setDrTxpow (dr_t dr, s1_t txpow) {
02230     setDrTxpow(DRCHG_SET, dr, txpow);
02231 }
02232 
02233 
02234 void LMIC_shutdown (void) {
02235     os_clearCallback(&LMIC.osjob);
02236     os_radio(RADIO_RST);
02237     LMIC.opmode |= OP_SHUTDOWN;
02238 }
02239 
02240 
02241 void LMIC_reset (void) {
02242     EV(devCond, INFO, (e_.reason = EV::devCond_t::LMIC_EV,
02243                        e_.eui    = MAIN::CDEV->getEui(),
02244                        e_.info   = EV_RESET));
02245     os_radio(RADIO_RST);
02246     os_clearCallback(&LMIC.osjob);
02247 
02248     os_clearMem((xref2u1_t)&LMIC,SIZEOFEXPR(LMIC));
02249     LMIC.devaddr      =  0;
02250     LMIC.devNonce     =  os_getRndU2();
02251     LMIC.opmode       =  OP_NONE;
02252     LMIC.errcr        =  CR_4_5;
02253     LMIC.adrEnabled   =  FCT_ADREN;
02254     LMIC.dn2Dr        =  DR_DNW2;   // we need this for 2nd DN window of join accept
02255     LMIC.dn2Freq      =  FREQ_DNW2; // ditto
02256     LMIC.ping.freq    =  FREQ_PING; // defaults for ping
02257     LMIC.ping.dr      =  DR_PING;   // ditto
02258     LMIC.ping.intvExp =  0xFF;
02259 #if defined(CFG_us915)
02260     initDefaultChannels();
02261 #endif
02262     DO_DEVDB(LMIC.devaddr,      devaddr);
02263     DO_DEVDB(LMIC.devNonce,     devNonce);
02264     DO_DEVDB(LMIC.dn2Dr,        dn2Dr);
02265     DO_DEVDB(LMIC.dn2Freq,      dn2Freq);
02266     DO_DEVDB(LMIC.ping.freq,    pingFreq);
02267     DO_DEVDB(LMIC.ping.dr,      pingDr);
02268     DO_DEVDB(LMIC.ping.intvExp, pingIntvExp);
02269 }
02270 
02271 
02272 void LMIC_init (void) {
02273     LMIC.opmode = OP_SHUTDOWN;
02274 }
02275 
02276 
02277 void LMIC_clrTxData (void) {
02278     LMIC.opmode &= ~(OP_TXDATA|OP_TXRXPEND|OP_POLL);
02279     LMIC.pendTxLen = 0;
02280     if( (LMIC.opmode & (OP_JOINING|OP_SCAN)) != 0 ) // do not interfere with JOINING
02281         return;
02282     os_clearCallback(&LMIC.osjob);
02283     os_radio(RADIO_RST);
02284     engineUpdate();
02285 }
02286 
02287 
02288 void LMIC_setTxData (void) {
02289     LMIC.opmode |= OP_TXDATA;
02290     if( (LMIC.opmode & OP_JOINING) == 0 )
02291     {
02292         LMIC.txCnt = 0;             // cancel any ongoing TX/RX retries
02293     }
02294     engineUpdate();
02295 }
02296 
02297 
02298 //
02299 int LMIC_setTxData2 (u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed) {
02300 /*    debug_val("Data Length: ", dlen);
02301     debug_val("Data Length: ", SIZEOFEXPR(LMIC.pendTxData));*/
02302     if( dlen > SIZEOFEXPR(LMIC.pendTxData) )
02303         return -2;
02304     if( data != (xref2u1_t)0 )
02305     {
02306         os_copyMem(LMIC.pendTxData, data, dlen);
02307     }
02308     //debug_str("Send Data\r\n");
02309     LMIC.pendTxConf = confirmed;
02310     LMIC.pendTxPort = port;
02311     LMIC.pendTxLen  = dlen;
02312     LMIC_setTxData();
02313     return 0;
02314 }
02315 
02316 /*u1_t* LMIC_getEui() {
02317     return MAIN::CDEV->getEui();
02318 }*/
02319 
02320 
02321 // Send a payload-less message to signal device is alive
02322 void LMIC_sendAlive (void) {
02323     LMIC.opmode |= OP_POLL;
02324     engineUpdate();
02325 }
02326 
02327 
02328 // Check if other networks are around.
02329 void LMIC_tryRejoin (void) {
02330     LMIC.opmode |= OP_REJOIN;
02331     engineUpdate();
02332 }
02333 
02334 //! \brief Setup given session keys
02335 //! and put the MAC in a state as if 
02336 //! a join request/accept would have negotiated just these keys.
02337 //! It is crucial that the combinations `devaddr/nwkkey` and `devaddr/artkey`
02338 //! are unique within the network identified by `netid`.
02339 //! NOTE: on Harvard architectures when session keys are in flash:
02340 //!  Caller has to fill in LMIC.{nwk,art}Key  before and pass {nwk,art}Key are NULL
02341 //! \param netid a 24 bit number describing the network id this device is using
02342 //! \param devaddr the 32 bit session address of the device. It is strongly recommended
02343 //!    to ensure that different devices use different numbers with high probability.
02344 //! \param nwkKey  the 16 byte network session key used for message integrity.
02345 //!     If NULL the caller has copied the key into `LMIC.nwkKey` before.
02346 //! \param artKey  the 16 byte application router session key used for message confidentiality.
02347 //!     If NULL the caller has copied the key into `LMIC.artKey` before.
02348 void LMIC_setSession (u4_t netid, devaddr_t devaddr, xref2u1_t nwkKey, xref2u1_t artKey) {
02349     LMIC.netid = netid;
02350     LMIC.devaddr = devaddr;
02351     if( nwkKey != (xref2u1_t)0 )
02352         os_copyMem(LMIC.nwkKey, nwkKey, 16);
02353     if( artKey != (xref2u1_t)0 )
02354         os_copyMem(LMIC.artKey, artKey, 16);
02355     
02356 #if defined(CFG_eu868)
02357     initDefaultChannels(0);
02358 #endif
02359  
02360     LMIC.opmode &= ~(OP_JOINING|OP_TRACK|OP_REJOIN|OP_TXRXPEND|OP_PINGINI);
02361     LMIC.opmode |= OP_NEXTCHNL;
02362     stateJustJoined();
02363 }
02364 
02365 // Enable/disable link check validation.
02366 // LMIC sets the ADRACKREQ bit in UP frames if there were no DN frames
02367 // for a while. It expects the network to provide a DN message to prove
02368 // connectivity with a span of UP frames. If this no such prove is coming
02369 // then the datarate is lowered and a LINK_DEAD event is generated.
02370 // This mode can be disabled and no connectivity prove (ADRACKREQ) is requested
02371 // nor is the datarate changed.
02372 // This must be called only if a session is established (e.g. after EV_JOINED)
02373 void LMIC_setLinkCheckMode (bit_t enabled) {
02374     LMIC.adrChanged = 0;
02375     LMIC.adrAckReq = enabled ? LINK_CHECK_INIT : LINK_CHECK_OFF;
02376 }
02377 
02378