LMiC library swtiched to default eu868

Fork of LMiC by Semtech

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers lmic.cpp Source File

lmic.cpp

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