Senet / lmic_MOTE_L152RC

Dependents:   Senet NAMote

Fork of lmic_MOTE_L152RC_2 by Dave Kjendal

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