Senet-ized LMIC for MOTE_L152RC

Fork of lmic_MOTE_L152RC by Semtech

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