lmic fork for Senet NAMote

Dependents:   Senet NAMote

Fork of lmic_MOTE_L152RC_2 by Dave Kjendal

Committer:
shaunkrnelson
Date:
Fri Jan 20 20:11:27 2017 +0000
Revision:
17:f90b064f6107
Parent:
16:d5c50bdab1f1
DisablING confirmed retries in config.h with CUSTOM_TXCONF_ATTEMPTS=0

Who changed what in which revision?

UserRevisionLine numberNew contents of line
dudmuck 0:f2716e543d97 1 /*******************************************************************************
dudmuck 0:f2716e543d97 2 * Copyright (c) 2014-2015 IBM Corporation.
dudmuck 0:f2716e543d97 3 * All rights reserved. This program and the accompanying materials
dudmuck 0:f2716e543d97 4 * are made available under the terms of the Eclipse Public License v1.0
dudmuck 0:f2716e543d97 5 * which accompanies this distribution, and is available at
dudmuck 0:f2716e543d97 6 * http://www.eclipse.org/legal/epl-v10.html
dudmuck 0:f2716e543d97 7 *
dudmuck 0:f2716e543d97 8 * Contributors:
dudmuck 0:f2716e543d97 9 * IBM Zurich Research Lab - initial API, implementation and documentation
dudmuck 0:f2716e543d97 10 *******************************************************************************/
dudmuck 0:f2716e543d97 11
dudmuck 0:f2716e543d97 12 //! \file
dudmuck 0:f2716e543d97 13 #include "lmic.h"
shaunkrnelson 17:f90b064f6107 14 #include "debug.h"
shaunkrnelson 17:f90b064f6107 15 #include "config.h"
Shaun Nelson 16:d5c50bdab1f1 16
Shaun Nelson 16:d5c50bdab1f1 17 #define JOIN_REQ_DEBUG
Shaun Nelson 16:d5c50bdab1f1 18 #define JA_DEBUG
dudmuck 0:f2716e543d97 19
dudmuck 0:f2716e543d97 20 #if !defined(MINRX_SYMS)
dudmuck 0:f2716e543d97 21 #define MINRX_SYMS 5
dudmuck 0:f2716e543d97 22 #endif // !defined(MINRX_SYMS)
dudmuck 0:f2716e543d97 23 #define PAMBL_SYMS 8
dudmuck 0:f2716e543d97 24 #define PAMBL_FSK 5
dudmuck 0:f2716e543d97 25 #define PRERX_FSK 1
dudmuck 0:f2716e543d97 26 #define RXLEN_FSK (1+5+2)
dudmuck 0:f2716e543d97 27
dudmuck 0:f2716e543d97 28 #define BCN_INTV_osticks sec2osticks(BCN_INTV_sec)
dudmuck 0:f2716e543d97 29 #define TXRX_GUARD_osticks ms2osticks(TXRX_GUARD_ms)
dudmuck 0:f2716e543d97 30 #define JOIN_GUARD_osticks ms2osticks(JOIN_GUARD_ms)
dudmuck 0:f2716e543d97 31 #define DELAY_DNW1_osticks sec2osticks(DELAY_DNW1)
dudmuck 0:f2716e543d97 32 #define DELAY_DNW2_osticks sec2osticks(DELAY_DNW2)
dudmuck 0:f2716e543d97 33 #define DELAY_JACC1_osticks sec2osticks(DELAY_JACC1)
dudmuck 0:f2716e543d97 34 #define DELAY_JACC2_osticks sec2osticks(DELAY_JACC2)
dudmuck 0:f2716e543d97 35 #define DELAY_EXTDNW2_osticks sec2osticks(DELAY_EXTDNW2)
dudmuck 0:f2716e543d97 36 #define BCN_RESERVE_osticks ms2osticks(BCN_RESERVE_ms)
dudmuck 0:f2716e543d97 37 #define BCN_GUARD_osticks ms2osticks(BCN_GUARD_ms)
dudmuck 0:f2716e543d97 38 #define BCN_WINDOW_osticks ms2osticks(BCN_WINDOW_ms)
dudmuck 0:f2716e543d97 39 #define AIRTIME_BCN_osticks us2osticks(AIRTIME_BCN)
dudmuck 0:f2716e543d97 40 #if defined(CFG_eu868)
dudmuck 0:f2716e543d97 41 #define DNW2_SAFETY_ZONE ms2osticks(3000)
dudmuck 0:f2716e543d97 42 #endif
dudmuck 0:f2716e543d97 43 #if defined(CFG_us915)
dudmuck 0:f2716e543d97 44 #define DNW2_SAFETY_ZONE ms2osticks(750)
dudmuck 0:f2716e543d97 45 #endif
dudmuck 0:f2716e543d97 46
dudmuck 0:f2716e543d97 47 // Special APIs - for development or testing
dudmuck 0:f2716e543d97 48 #define isTESTMODE() 0
dudmuck 0:f2716e543d97 49
dudmuck 0:f2716e543d97 50 DEFINE_LMIC;
dudmuck 0:f2716e543d97 51 DECL_ON_LMIC_EVENT;
dudmuck 0:f2716e543d97 52
shaunkrnelson 17:f90b064f6107 53 // confirmed uplink retransmission count override
shaunkrnelson 17:f90b064f6107 54 #ifdef CUSTOM_TXCONF_ATTEMPTS
shaunkrnelson 17:f90b064f6107 55 #define _TXCONF_ATTEMPTS CUSTOM_TXCONF_ATTEMPTS
shaunkrnelson 17:f90b064f6107 56 #else
shaunkrnelson 17:f90b064f6107 57 #define _TXCONF_ATTEMPTS TXCONF_ATTEMPTS
shaunkrnelson 17:f90b064f6107 58 #endif
shaunkrnelson 17:f90b064f6107 59
dudmuck 0:f2716e543d97 60
dudmuck 0:f2716e543d97 61 // Fwd decls.
dudmuck 0:f2716e543d97 62 static void engineUpdate(void);
dudmuck 0:f2716e543d97 63 static void startScan (void);
dudmuck 0:f2716e543d97 64
dudmuck 0:f2716e543d97 65
dudmuck 0:f2716e543d97 66 // ================================================================================
dudmuck 0:f2716e543d97 67 // BEG OS - default implementations for certain OS suport functions
dudmuck 0:f2716e543d97 68
dudmuck 0:f2716e543d97 69 #if !defined(HAS_os_calls)
dudmuck 0:f2716e543d97 70
dudmuck 0:f2716e543d97 71 #if !defined(os_rlsbf2)
dudmuck 0:f2716e543d97 72 u2_t os_rlsbf2 (xref2cu1_t buf) {
dudmuck 0:f2716e543d97 73 return (u2_t)(buf[0] | (buf[1]<<8));
dudmuck 0:f2716e543d97 74 }
dudmuck 0:f2716e543d97 75 #endif
dudmuck 0:f2716e543d97 76
dudmuck 0:f2716e543d97 77 #if !defined(os_rlsbf4)
dudmuck 0:f2716e543d97 78 u4_t os_rlsbf4 (xref2cu1_t buf) {
dudmuck 0:f2716e543d97 79 return (u4_t)(buf[0] | (buf[1]<<8) | ((u4_t)buf[2]<<16) | ((u4_t)buf[3]<<24));
dudmuck 0:f2716e543d97 80 }
dudmuck 0:f2716e543d97 81 #endif
dudmuck 0:f2716e543d97 82
dudmuck 0:f2716e543d97 83
dudmuck 0:f2716e543d97 84 #if !defined(os_rmsbf4)
dudmuck 0:f2716e543d97 85 u4_t os_rmsbf4 (xref2cu1_t buf) {
dudmuck 0:f2716e543d97 86 return (u4_t)(buf[3] | (buf[2]<<8) | ((u4_t)buf[1]<<16) | ((u4_t)buf[0]<<24));
dudmuck 0:f2716e543d97 87 }
dudmuck 0:f2716e543d97 88 #endif
dudmuck 0:f2716e543d97 89
dudmuck 0:f2716e543d97 90
dudmuck 0:f2716e543d97 91 #if !defined(os_wlsbf2)
dudmuck 0:f2716e543d97 92 void os_wlsbf2 (xref2u1_t buf, u2_t v) {
dudmuck 0:f2716e543d97 93 buf[0] = v;
dudmuck 0:f2716e543d97 94 buf[1] = v>>8;
dudmuck 0:f2716e543d97 95 }
dudmuck 0:f2716e543d97 96 #endif
dudmuck 0:f2716e543d97 97
dudmuck 0:f2716e543d97 98 #if !defined(os_wlsbf4)
dudmuck 0:f2716e543d97 99 void os_wlsbf4 (xref2u1_t buf, u4_t v) {
dudmuck 0:f2716e543d97 100 buf[0] = v;
dudmuck 0:f2716e543d97 101 buf[1] = v>>8;
dudmuck 0:f2716e543d97 102 buf[2] = v>>16;
dudmuck 0:f2716e543d97 103 buf[3] = v>>24;
dudmuck 0:f2716e543d97 104 }
dudmuck 0:f2716e543d97 105 #endif
dudmuck 0:f2716e543d97 106
dudmuck 0:f2716e543d97 107 #if !defined(os_wmsbf4)
dudmuck 0:f2716e543d97 108 void os_wmsbf4 (xref2u1_t buf, u4_t v) {
dudmuck 0:f2716e543d97 109 buf[3] = v;
dudmuck 0:f2716e543d97 110 buf[2] = v>>8;
dudmuck 0:f2716e543d97 111 buf[1] = v>>16;
dudmuck 0:f2716e543d97 112 buf[0] = v>>24;
dudmuck 0:f2716e543d97 113 }
dudmuck 0:f2716e543d97 114 #endif
dudmuck 0:f2716e543d97 115
dudmuck 0:f2716e543d97 116 #if !defined(os_getBattLevel)
dudmuck 0:f2716e543d97 117 u1_t os_getBattLevel (void) {
dudmuck 0:f2716e543d97 118 return MCMD_DEVS_BATT_NOINFO;
dudmuck 0:f2716e543d97 119 }
dudmuck 0:f2716e543d97 120 #endif
dudmuck 0:f2716e543d97 121
dudmuck 0:f2716e543d97 122 #if !defined(os_crc16)
dudmuck 0:f2716e543d97 123 // New CRC-16 CCITT(XMODEM) checksum for beacons:
dudmuck 0:f2716e543d97 124 u2_t os_crc16 (xref2u1_t data, uint len) {
dudmuck 0:f2716e543d97 125 u2_t remainder = 0;
dudmuck 0:f2716e543d97 126 u2_t polynomial = 0x1021;
dudmuck 0:f2716e543d97 127 for( uint i = 0; i < len; i++ ) {
dudmuck 0:f2716e543d97 128 remainder ^= data[i] << 8;
dudmuck 0:f2716e543d97 129 for( u1_t bit = 8; bit > 0; bit--) {
dudmuck 0:f2716e543d97 130 if( (remainder & 0x8000) )
dudmuck 0:f2716e543d97 131 remainder = (remainder << 1) ^ polynomial;
dudmuck 0:f2716e543d97 132 else
dudmuck 0:f2716e543d97 133 remainder <<= 1;
dudmuck 0:f2716e543d97 134 }
dudmuck 0:f2716e543d97 135 }
dudmuck 0:f2716e543d97 136 return remainder;
dudmuck 0:f2716e543d97 137 }
dudmuck 0:f2716e543d97 138 #endif
dudmuck 0:f2716e543d97 139
dudmuck 0:f2716e543d97 140 #endif // !HAS_os_calls
dudmuck 0:f2716e543d97 141
dudmuck 0:f2716e543d97 142 // END OS - default implementations for certain OS suport functions
dudmuck 0:f2716e543d97 143 // ================================================================================
dudmuck 0:f2716e543d97 144
dudmuck 0:f2716e543d97 145 // ================================================================================
dudmuck 0:f2716e543d97 146 // BEG AES
dudmuck 0:f2716e543d97 147
dudmuck 0:f2716e543d97 148 static void micB0 (u4_t devaddr, u4_t seqno, int dndir, int len) {
dudmuck 0:f2716e543d97 149 os_clearMem(AESaux,16);
dudmuck 0:f2716e543d97 150 AESaux[0] = 0x49;
dudmuck 0:f2716e543d97 151 AESaux[5] = dndir?1:0;
dudmuck 0:f2716e543d97 152 AESaux[15] = len;
dudmuck 0:f2716e543d97 153 os_wlsbf4(AESaux+ 6,devaddr);
dudmuck 0:f2716e543d97 154 os_wlsbf4(AESaux+10,seqno);
dudmuck 0:f2716e543d97 155 }
dudmuck 0:f2716e543d97 156
dudmuck 0:f2716e543d97 157
dudmuck 0:f2716e543d97 158 static int aes_verifyMic (xref2cu1_t key, u4_t devaddr, u4_t seqno, int dndir, xref2u1_t pdu, int len) {
dudmuck 0:f2716e543d97 159 micB0(devaddr, seqno, dndir, len);
dudmuck 0:f2716e543d97 160 os_copyMem(AESkey,key,16);
dudmuck 0:f2716e543d97 161 return os_aes(AES_MIC, pdu, len) == os_rmsbf4(pdu+len);
dudmuck 0:f2716e543d97 162 }
dudmuck 0:f2716e543d97 163
dudmuck 0:f2716e543d97 164
dudmuck 0:f2716e543d97 165 static void aes_appendMic (xref2cu1_t key, u4_t devaddr, u4_t seqno, int dndir, xref2u1_t pdu, int len) {
dudmuck 0:f2716e543d97 166 micB0(devaddr, seqno, dndir, len);
dudmuck 0:f2716e543d97 167 os_copyMem(AESkey,key,16);
dudmuck 0:f2716e543d97 168 // MSB because of internal structure of AES
dudmuck 0:f2716e543d97 169 os_wmsbf4(pdu+len, os_aes(AES_MIC, pdu, len));
dudmuck 0:f2716e543d97 170 }
dudmuck 0:f2716e543d97 171
dudmuck 0:f2716e543d97 172
dudmuck 0:f2716e543d97 173 static void aes_appendMic0 (xref2u1_t pdu, int len) {
dudmuck 0:f2716e543d97 174 os_getDevKey(AESkey);
dudmuck 0:f2716e543d97 175 os_wmsbf4(pdu+len, os_aes(AES_MIC|AES_MICNOAUX, pdu, len)); // MSB because of internal structure of AES
dudmuck 0:f2716e543d97 176 }
dudmuck 0:f2716e543d97 177
dudmuck 0:f2716e543d97 178
dudmuck 0:f2716e543d97 179 static int aes_verifyMic0 (xref2u1_t pdu, int len) {
dudmuck 0:f2716e543d97 180 os_getDevKey(AESkey);
dudmuck 0:f2716e543d97 181 return os_aes(AES_MIC|AES_MICNOAUX, pdu, len) == os_rmsbf4(pdu+len);
dudmuck 0:f2716e543d97 182 }
dudmuck 0:f2716e543d97 183
dudmuck 0:f2716e543d97 184
dudmuck 0:f2716e543d97 185 static void aes_encrypt (xref2u1_t pdu, int len) {
dudmuck 0:f2716e543d97 186 os_getDevKey(AESkey);
dudmuck 0:f2716e543d97 187 os_aes(AES_ENC, pdu, len);
dudmuck 0:f2716e543d97 188 }
dudmuck 0:f2716e543d97 189
dudmuck 0:f2716e543d97 190
dudmuck 0:f2716e543d97 191 static void aes_cipher (xref2cu1_t key, u4_t devaddr, u4_t seqno, int dndir, xref2u1_t payload, int len) {
dudmuck 0:f2716e543d97 192 if( len <= 0 )
dudmuck 0:f2716e543d97 193 return;
dudmuck 0:f2716e543d97 194 os_clearMem(AESaux, 16);
dudmuck 0:f2716e543d97 195 AESaux[0] = AESaux[15] = 1; // mode=cipher / dir=down / block counter=1
dudmuck 0:f2716e543d97 196 AESaux[5] = dndir?1:0;
dudmuck 0:f2716e543d97 197 os_wlsbf4(AESaux+ 6,devaddr);
dudmuck 0:f2716e543d97 198 os_wlsbf4(AESaux+10,seqno);
dudmuck 0:f2716e543d97 199 os_copyMem(AESkey,key,16);
dudmuck 0:f2716e543d97 200 os_aes(AES_CTR, payload, len);
dudmuck 0:f2716e543d97 201 }
dudmuck 0:f2716e543d97 202
dudmuck 0:f2716e543d97 203
dudmuck 0:f2716e543d97 204 static void aes_sessKeys (u2_t devnonce, xref2cu1_t artnonce, xref2u1_t nwkkey, xref2u1_t artkey) {
dudmuck 0:f2716e543d97 205 os_clearMem(nwkkey, 16);
dudmuck 0:f2716e543d97 206 nwkkey[0] = 0x01;
dudmuck 0:f2716e543d97 207 os_copyMem(nwkkey+1, artnonce, LEN_ARTNONCE+LEN_NETID);
dudmuck 0:f2716e543d97 208 os_wlsbf2(nwkkey+1+LEN_ARTNONCE+LEN_NETID, devnonce);
dudmuck 0:f2716e543d97 209 os_copyMem(artkey, nwkkey, 16);
dudmuck 0:f2716e543d97 210 artkey[0] = 0x02;
dudmuck 0:f2716e543d97 211
dudmuck 0:f2716e543d97 212 os_getDevKey(AESkey);
dudmuck 0:f2716e543d97 213 os_aes(AES_ENC, nwkkey, 16);
dudmuck 0:f2716e543d97 214 os_getDevKey(AESkey);
dudmuck 0:f2716e543d97 215 os_aes(AES_ENC, artkey, 16);
dudmuck 0:f2716e543d97 216 }
dudmuck 0:f2716e543d97 217
dudmuck 0:f2716e543d97 218 // END AES
dudmuck 0:f2716e543d97 219 // ================================================================================
dudmuck 0:f2716e543d97 220
dudmuck 0:f2716e543d97 221
dudmuck 0:f2716e543d97 222 // ================================================================================
dudmuck 0:f2716e543d97 223 // BEG LORA
dudmuck 0:f2716e543d97 224
dudmuck 0:f2716e543d97 225 #if defined(CFG_eu868) // ========================================
dudmuck 0:f2716e543d97 226
dudmuck 0:f2716e543d97 227 #define maxFrameLen(dr) ((dr)<=DR_SF9 ? maxFrameLens[(dr)] : 0xFF)
dudmuck 0:f2716e543d97 228 const u1_t maxFrameLens [] = { 64,64,64,123 };
dudmuck 0:f2716e543d97 229
dudmuck 0:f2716e543d97 230 const u1_t _DR2RPS_CRC[] = {
dudmuck 0:f2716e543d97 231 ILLEGAL_RPS,
dudmuck 0:f2716e543d97 232 (u1_t)MAKERPS(SF12, BW125, CR_4_5, 0, 0),
dudmuck 0:f2716e543d97 233 (u1_t)MAKERPS(SF11, BW125, CR_4_5, 0, 0),
dudmuck 0:f2716e543d97 234 (u1_t)MAKERPS(SF10, BW125, CR_4_5, 0, 0),
dudmuck 0:f2716e543d97 235 (u1_t)MAKERPS(SF9, BW125, CR_4_5, 0, 0),
dudmuck 0:f2716e543d97 236 (u1_t)MAKERPS(SF8, BW125, CR_4_5, 0, 0),
dudmuck 0:f2716e543d97 237 (u1_t)MAKERPS(SF7, BW125, CR_4_5, 0, 0),
dudmuck 0:f2716e543d97 238 (u1_t)MAKERPS(SF7, BW250, CR_4_5, 0, 0),
dudmuck 0:f2716e543d97 239 (u1_t)MAKERPS(FSK, BW125, CR_4_5, 0, 0),
dudmuck 0:f2716e543d97 240 ILLEGAL_RPS
dudmuck 0:f2716e543d97 241 };
dudmuck 0:f2716e543d97 242
dudmuck 0:f2716e543d97 243 static const s1_t TXPOWLEVELS[] = {
dudmuck 0:f2716e543d97 244 20, 14, 11, 8, 5, 2, 0,0, 0,0,0,0, 0,0,0,0
dudmuck 0:f2716e543d97 245 };
dudmuck 0:f2716e543d97 246 #define pow2dBm(mcmd_ladr_p1) (TXPOWLEVELS[(mcmd_ladr_p1&MCMD_LADR_POW_MASK)>>MCMD_LADR_POW_SHIFT])
dudmuck 0:f2716e543d97 247
dudmuck 0:f2716e543d97 248 #elif defined(CFG_us915) // ========================================
dudmuck 0:f2716e543d97 249
dudmuck 0:f2716e543d97 250 #define maxFrameLen(dr) ((dr)<=DR_SF11CR ? maxFrameLens[(dr)] : 0xFF)
dudmuck 0:f2716e543d97 251 //const u1_t maxFrameLens [] = { 24,66,142,255,255,255,255,255, 66,142 };
dudmuck 0:f2716e543d97 252
dudmuck 0:f2716e543d97 253 const u1_t _DR2RPS_CRC[] = {
dudmuck 0:f2716e543d97 254 ILLEGAL_RPS,
dudmuck 0:f2716e543d97 255 MAKERPS(SF10, BW125, CR_4_5, 0, 0),
dudmuck 0:f2716e543d97 256 MAKERPS(SF9 , BW125, CR_4_5, 0, 0),
dudmuck 0:f2716e543d97 257 MAKERPS(SF8 , BW125, CR_4_5, 0, 0),
dudmuck 0:f2716e543d97 258 MAKERPS(SF7 , BW125, CR_4_5, 0, 0),
dudmuck 0:f2716e543d97 259 MAKERPS(SF8 , BW500, CR_4_5, 0, 0),
dudmuck 0:f2716e543d97 260 ILLEGAL_RPS ,
dudmuck 0:f2716e543d97 261 ILLEGAL_RPS ,
dudmuck 0:f2716e543d97 262 ILLEGAL_RPS ,
dudmuck 0:f2716e543d97 263 MAKERPS(SF12, BW500, CR_4_5, 0, 0),
dudmuck 0:f2716e543d97 264 MAKERPS(SF11, BW500, CR_4_5, 0, 0),
dudmuck 0:f2716e543d97 265 MAKERPS(SF10, BW500, CR_4_5, 0, 0),
dudmuck 0:f2716e543d97 266 MAKERPS(SF9 , BW500, CR_4_5, 0, 0),
dudmuck 0:f2716e543d97 267 MAKERPS(SF8 , BW500, CR_4_5, 0, 0),
dudmuck 0:f2716e543d97 268 MAKERPS(SF7 , BW500, CR_4_5, 0, 0),
dudmuck 0:f2716e543d97 269 ILLEGAL_RPS
dudmuck 0:f2716e543d97 270 };
dudmuck 0:f2716e543d97 271
dudmuck 0:f2716e543d97 272 #define pow2dBm(mcmd_ladr_p1) ((s1_t)(30 - (((mcmd_ladr_p1)&MCMD_LADR_POW_MASK)<<1)))
dudmuck 0:f2716e543d97 273
dudmuck 0:f2716e543d97 274 #endif // ================================================
dudmuck 0:f2716e543d97 275
dudmuck 0:f2716e543d97 276 static const u1_t SENSITIVITY[7][3] = {
dudmuck 0:f2716e543d97 277 // ------------bw----------
dudmuck 0:f2716e543d97 278 // 125kHz 250kHz 500kHz
dudmuck 0:f2716e543d97 279 { 141-109, 141-109, 141-109 }, // FSK
dudmuck 0:f2716e543d97 280 { 141-127, 141-124, 141-121 }, // SF7
dudmuck 0:f2716e543d97 281 { 141-129, 141-126, 141-123 }, // SF8
dudmuck 0:f2716e543d97 282 { 141-132, 141-129, 141-126 }, // SF9
dudmuck 0:f2716e543d97 283 { 141-135, 141-132, 141-129 }, // SF10
dudmuck 0:f2716e543d97 284 { 141-138, 141-135, 141-132 }, // SF11
dudmuck 0:f2716e543d97 285 { 141-141, 141-138, 141-135 } // SF12
dudmuck 0:f2716e543d97 286 };
dudmuck 0:f2716e543d97 287
dudmuck 0:f2716e543d97 288 int getSensitivity (rps_t rps) {
dudmuck 0:f2716e543d97 289 return -141 + SENSITIVITY[getSf(rps)][getBw(rps)];
dudmuck 0:f2716e543d97 290 }
dudmuck 0:f2716e543d97 291
dudmuck 0:f2716e543d97 292 ostime_t calcAirTime (rps_t rps, u1_t plen) {
dudmuck 0:f2716e543d97 293 u1_t bw = getBw(rps); // 0,1,2 = 125,250,500kHz
dudmuck 0:f2716e543d97 294 u1_t sf = getSf(rps); // 0=FSK, 1..6 = SF7..12
dudmuck 0:f2716e543d97 295 if( sf == FSK ) {
dudmuck 0:f2716e543d97 296 return (plen+/*preamble*/5+/*syncword*/3+/*len*/1+/*crc*/2) * /*bits/byte*/8
dudmuck 0:f2716e543d97 297 * (s4_t)OSTICKS_PER_SEC / /*kbit/s*/50000;
dudmuck 0:f2716e543d97 298 }
dudmuck 0:f2716e543d97 299 u1_t sfx = 4*(sf+(7-SF7));
dudmuck 0:f2716e543d97 300 u1_t q = sfx - (sf >= SF11 ? 8 : 0);
dudmuck 0:f2716e543d97 301 int tmp = 8*plen - sfx + 28 + (getNocrc(rps)?0:16) - (getIh(rps)?20:0);
dudmuck 0:f2716e543d97 302 if( tmp > 0 ) {
dudmuck 0:f2716e543d97 303 tmp = (tmp + q - 1) / q;
dudmuck 0:f2716e543d97 304 tmp *= getCr(rps)+5;
dudmuck 0:f2716e543d97 305 tmp += 8;
dudmuck 0:f2716e543d97 306 } else {
dudmuck 0:f2716e543d97 307 tmp = 8;
dudmuck 0:f2716e543d97 308 }
dudmuck 0:f2716e543d97 309 tmp = (tmp<<2) + /*preamble*/49 /* 4 * (8 + 4.25) */;
dudmuck 0:f2716e543d97 310 // bw = 125000 = 15625 * 2^3
dudmuck 0:f2716e543d97 311 // 250000 = 15625 * 2^4
dudmuck 0:f2716e543d97 312 // 500000 = 15625 * 2^5
dudmuck 0:f2716e543d97 313 // sf = 7..12
dudmuck 0:f2716e543d97 314 //
dudmuck 0:f2716e543d97 315 // osticks = tmp * OSTICKS_PER_SEC * 1<<sf / bw
dudmuck 0:f2716e543d97 316 //
dudmuck 0:f2716e543d97 317 // 3 => counter reduced divisor 125000/8 => 15625
dudmuck 0:f2716e543d97 318 // 2 => counter 2 shift on tmp
dudmuck 0:f2716e543d97 319 sfx = sf+(7-SF7) - (3+2) - bw;
dudmuck 0:f2716e543d97 320 int div = 15625;
dudmuck 0:f2716e543d97 321 if( sfx > 4 ) {
dudmuck 0:f2716e543d97 322 // prevent 32bit signed int overflow in last step
dudmuck 0:f2716e543d97 323 div >>= sfx-4;
dudmuck 0:f2716e543d97 324 sfx = 4;
dudmuck 0:f2716e543d97 325 }
dudmuck 0:f2716e543d97 326 // Need 32bit arithmetic for this last step
dudmuck 0:f2716e543d97 327 return (((ostime_t)tmp << sfx) * OSTICKS_PER_SEC + div/2) / div;
dudmuck 0:f2716e543d97 328 }
dudmuck 0:f2716e543d97 329
dudmuck 0:f2716e543d97 330 extern inline rps_t updr2rps (dr_t dr);
dudmuck 0:f2716e543d97 331 extern inline rps_t dndr2rps (dr_t dr);
dudmuck 0:f2716e543d97 332 extern inline int isFasterDR (dr_t dr1, dr_t dr2);
dudmuck 0:f2716e543d97 333 extern inline int isSlowerDR (dr_t dr1, dr_t dr2);
dudmuck 0:f2716e543d97 334 extern inline dr_t incDR (dr_t dr);
dudmuck 0:f2716e543d97 335 extern inline dr_t decDR (dr_t dr);
dudmuck 0:f2716e543d97 336 extern inline dr_t assertDR (dr_t dr);
dudmuck 0:f2716e543d97 337 extern inline dr_t validDR (dr_t dr);
dudmuck 0:f2716e543d97 338 extern inline dr_t lowerDR (dr_t dr, u1_t n);
dudmuck 0:f2716e543d97 339
dudmuck 0:f2716e543d97 340 extern inline sf_t getSf (rps_t params);
dudmuck 0:f2716e543d97 341 extern inline rps_t setSf (rps_t params, sf_t sf);
dudmuck 0:f2716e543d97 342 extern inline bw_t getBw (rps_t params);
dudmuck 0:f2716e543d97 343 extern inline rps_t setBw (rps_t params, bw_t cr);
dudmuck 0:f2716e543d97 344 extern inline cr_t getCr (rps_t params);
dudmuck 0:f2716e543d97 345 extern inline rps_t setCr (rps_t params, cr_t cr);
dudmuck 0:f2716e543d97 346 extern inline int getNocrc (rps_t params);
dudmuck 0:f2716e543d97 347 extern inline rps_t setNocrc (rps_t params, int nocrc);
dudmuck 0:f2716e543d97 348 extern inline int getIh (rps_t params);
dudmuck 0:f2716e543d97 349 extern inline rps_t setIh (rps_t params, int ih);
dudmuck 0:f2716e543d97 350 extern inline rps_t makeRps (sf_t sf, bw_t bw, cr_t cr, int ih, int nocrc);
dudmuck 0:f2716e543d97 351 extern inline int sameSfBw (rps_t r1, rps_t r2);
dudmuck 0:f2716e543d97 352
dudmuck 0:f2716e543d97 353 // END LORA
dudmuck 0:f2716e543d97 354 // ================================================================================
dudmuck 0:f2716e543d97 355
dudmuck 0:f2716e543d97 356
dudmuck 0:f2716e543d97 357 // Adjust DR for TX retries
dudmuck 0:f2716e543d97 358 // - indexed by retry count
dudmuck 0:f2716e543d97 359 // - return steps to lower DR
dudmuck 0:f2716e543d97 360 static const u1_t DRADJUST[2+TXCONF_ATTEMPTS] = {
shaunkrnelson 17:f90b064f6107 361
dudmuck 0:f2716e543d97 362 // normal frames - 1st try / no retry
shaunkrnelson 17:f90b064f6107 363 0,
dudmuck 0:f2716e543d97 364 // confirmed frames
shaunkrnelson 17:f90b064f6107 365 0,0,1,0,1,0,1,0,0
dudmuck 0:f2716e543d97 366 };
dudmuck 0:f2716e543d97 367
dudmuck 0:f2716e543d97 368
dudmuck 0:f2716e543d97 369 // Table below defines the size of one symbol as
dudmuck 0:f2716e543d97 370 // symtime = 256us * 2^T(sf,bw)
dudmuck 0:f2716e543d97 371 // 256us is called one symunit.
dudmuck 0:f2716e543d97 372 // SF:
dudmuck 0:f2716e543d97 373 // BW: |__7___8___9__10__11__12
dudmuck 0:f2716e543d97 374 // 125kHz | 2 3 4 5 6 7
dudmuck 0:f2716e543d97 375 // 250kHz | 1 2 3 4 5 6
dudmuck 0:f2716e543d97 376 // 500kHz | 0 1 2 3 4 5
dudmuck 0:f2716e543d97 377 //
dudmuck 0:f2716e543d97 378 // Times for half symbol per DR
dudmuck 0:f2716e543d97 379 // Per DR table to minimize rounding errors
dudmuck 0:f2716e543d97 380 static const ostime_t DR2HSYM_osticks[] = {
dudmuck 0:f2716e543d97 381 #if defined(CFG_eu868)
dudmuck 0:f2716e543d97 382 #define dr2hsym(dr) (DR2HSYM_osticks[(dr)])
dudmuck 0:f2716e543d97 383 us2osticksRound(128<<7), // DR_SF12
dudmuck 0:f2716e543d97 384 us2osticksRound(128<<6), // DR_SF11
dudmuck 0:f2716e543d97 385 us2osticksRound(128<<5), // DR_SF10
dudmuck 0:f2716e543d97 386 us2osticksRound(128<<4), // DR_SF9
dudmuck 0:f2716e543d97 387 us2osticksRound(128<<3), // DR_SF8
dudmuck 0:f2716e543d97 388 us2osticksRound(128<<2), // DR_SF7
dudmuck 0:f2716e543d97 389 us2osticksRound(128<<1), // DR_SF7B
dudmuck 0:f2716e543d97 390 us2osticksRound(80) // FSK -- not used (time for 1/2 byte)
dudmuck 0:f2716e543d97 391 #elif defined(CFG_us915)
dudmuck 0:f2716e543d97 392 #define dr2hsym(dr) (DR2HSYM_osticks[(dr)&7]) // map DR_SFnCR -> 0-6
dudmuck 0:f2716e543d97 393 us2osticksRound(128<<5), // DR_SF10 DR_SF12CR
dudmuck 0:f2716e543d97 394 us2osticksRound(128<<4), // DR_SF9 DR_SF11CR
dudmuck 0:f2716e543d97 395 us2osticksRound(128<<3), // DR_SF8 DR_SF10CR
dudmuck 0:f2716e543d97 396 us2osticksRound(128<<2), // DR_SF7 DR_SF9CR
dudmuck 0:f2716e543d97 397 us2osticksRound(128<<1), // DR_SF8C DR_SF8CR
dudmuck 0:f2716e543d97 398 us2osticksRound(128<<0) // ------ DR_SF7CR
dudmuck 0:f2716e543d97 399 #endif
dudmuck 0:f2716e543d97 400 };
dudmuck 0:f2716e543d97 401
dudmuck 0:f2716e543d97 402
dudmuck 0:f2716e543d97 403 static ostime_t calcRxWindow (u1_t secs, dr_t dr) {
dudmuck 0:f2716e543d97 404 ostime_t rxoff, err;
dudmuck 0:f2716e543d97 405 if( secs==0 ) {
dudmuck 0:f2716e543d97 406 // aka 128 secs (next becaon)
dudmuck 0:f2716e543d97 407 rxoff = LMIC.drift;
dudmuck 0:f2716e543d97 408 err = LMIC.lastDriftDiff;
dudmuck 0:f2716e543d97 409 } else {
dudmuck 0:f2716e543d97 410 // scheduled RX window within secs into current beacon period
dudmuck 0:f2716e543d97 411 rxoff = (LMIC.drift * (ostime_t)secs) >> BCN_INTV_exp;
dudmuck 0:f2716e543d97 412 err = (LMIC.lastDriftDiff * (ostime_t)secs) >> BCN_INTV_exp;
dudmuck 0:f2716e543d97 413 }
dudmuck 0:f2716e543d97 414 u1_t rxsyms = MINRX_SYMS;
dudmuck 0:f2716e543d97 415 err += (ostime_t)LMIC.maxDriftDiff * LMIC.missedBcns;
dudmuck 0:f2716e543d97 416 LMIC.rxsyms = MINRX_SYMS + (err / dr2hsym(dr));
dudmuck 0:f2716e543d97 417
dudmuck 0:f2716e543d97 418 return (rxsyms-PAMBL_SYMS) * dr2hsym(dr) + rxoff;
dudmuck 0:f2716e543d97 419 }
dudmuck 0:f2716e543d97 420
dudmuck 0:f2716e543d97 421
dudmuck 0:f2716e543d97 422 // Setup beacon RX parameters assuming we have an error of ms (aka +/-(ms/2))
dudmuck 0:f2716e543d97 423 static void calcBcnRxWindowFromMillis (u1_t ms, bit_t ini) {
dudmuck 0:f2716e543d97 424 if( ini ) {
dudmuck 0:f2716e543d97 425 LMIC.drift = 0;
dudmuck 0:f2716e543d97 426 LMIC.maxDriftDiff = 0;
dudmuck 0:f2716e543d97 427 LMIC.missedBcns = 0;
dudmuck 0:f2716e543d97 428 LMIC.bcninfo.flags |= BCN_NODRIFT|BCN_NODDIFF;
dudmuck 0:f2716e543d97 429 }
dudmuck 0:f2716e543d97 430 ostime_t hsym = dr2hsym(DR_BCN);
dudmuck 0:f2716e543d97 431 LMIC.bcnRxsyms = MINRX_SYMS + ms2osticksCeil(ms) / hsym;
dudmuck 0:f2716e543d97 432 LMIC.bcnRxtime = LMIC.bcninfo.txtime + BCN_INTV_osticks - (LMIC.bcnRxsyms-PAMBL_SYMS) * hsym;
dudmuck 0:f2716e543d97 433 }
dudmuck 0:f2716e543d97 434
dudmuck 0:f2716e543d97 435
dudmuck 0:f2716e543d97 436 // Setup scheduled RX window (ping/multicast slot)
dudmuck 0:f2716e543d97 437 static void rxschedInit (xref2rxsched_t rxsched) {
dudmuck 0:f2716e543d97 438 os_clearMem(AESkey,16);
dudmuck 0:f2716e543d97 439 os_clearMem(LMIC.frame+8,8);
dudmuck 0:f2716e543d97 440 os_wlsbf4(LMIC.frame, LMIC.bcninfo.time);
dudmuck 0:f2716e543d97 441 os_wlsbf4(LMIC.frame+4, LMIC.devaddr);
dudmuck 0:f2716e543d97 442 os_aes(AES_ENC,LMIC.frame,16);
dudmuck 0:f2716e543d97 443 u1_t intvExp = rxsched->intvExp;
dudmuck 0:f2716e543d97 444 ostime_t off = os_rlsbf2(LMIC.frame) & (0x0FFF >> (7 - intvExp)); // random offset (slot units)
dudmuck 0:f2716e543d97 445 rxsched->rxbase = (LMIC.bcninfo.txtime +
dudmuck 0:f2716e543d97 446 BCN_RESERVE_osticks +
dudmuck 0:f2716e543d97 447 ms2osticks(BCN_SLOT_SPAN_ms * off)); // random offset osticks
dudmuck 0:f2716e543d97 448 rxsched->slot = 0;
dudmuck 0:f2716e543d97 449 rxsched->rxtime = rxsched->rxbase - calcRxWindow(/*secs BCN_RESERVE*/2+(1<<intvExp),rxsched->dr);
dudmuck 0:f2716e543d97 450 rxsched->rxsyms = LMIC.rxsyms;
dudmuck 0:f2716e543d97 451 }
dudmuck 0:f2716e543d97 452
dudmuck 0:f2716e543d97 453
dudmuck 0:f2716e543d97 454 static bit_t rxschedNext (xref2rxsched_t rxsched, ostime_t cando) {
dudmuck 0:f2716e543d97 455 again:
dudmuck 0:f2716e543d97 456 if( rxsched->rxtime - cando >= 0 )
dudmuck 0:f2716e543d97 457 return 1;
dudmuck 0:f2716e543d97 458 u1_t slot;
dudmuck 0:f2716e543d97 459 if( (slot=rxsched->slot) >= 128 )
dudmuck 0:f2716e543d97 460 return 0;
dudmuck 0:f2716e543d97 461 u1_t intv = 1<<rxsched->intvExp;
dudmuck 0:f2716e543d97 462 if( (rxsched->slot = (slot += (intv))) >= 128 )
dudmuck 0:f2716e543d97 463 return 0;
dudmuck 0:f2716e543d97 464 rxsched->rxtime = rxsched->rxbase
dudmuck 0:f2716e543d97 465 + ((BCN_WINDOW_osticks * (ostime_t)slot) >> BCN_INTV_exp)
dudmuck 0:f2716e543d97 466 - calcRxWindow(/*secs BCN_RESERVE*/2+slot+intv,rxsched->dr);
dudmuck 0:f2716e543d97 467 rxsched->rxsyms = LMIC.rxsyms;
dudmuck 0:f2716e543d97 468 goto again;
dudmuck 0:f2716e543d97 469 }
dudmuck 0:f2716e543d97 470
dudmuck 0:f2716e543d97 471
dudmuck 0:f2716e543d97 472 static ostime_t rndDelay (u1_t secSpan) {
dudmuck 0:f2716e543d97 473 u2_t r = os_getRndU2();
dudmuck 0:f2716e543d97 474 ostime_t delay = r;
dudmuck 0:f2716e543d97 475 if( delay > OSTICKS_PER_SEC )
dudmuck 0:f2716e543d97 476 delay = r % (u2_t)OSTICKS_PER_SEC;
dudmuck 0:f2716e543d97 477 if( secSpan > 0 )
dudmuck 0:f2716e543d97 478 delay += ((u1_t)r % secSpan) * OSTICKS_PER_SEC;
dudmuck 0:f2716e543d97 479 return delay;
dudmuck 0:f2716e543d97 480 }
dudmuck 0:f2716e543d97 481
dudmuck 0:f2716e543d97 482
dudmuck 0:f2716e543d97 483 static void txDelay (ostime_t reftime, u1_t secSpan) {
dudmuck 0:f2716e543d97 484 reftime += rndDelay(secSpan);
dudmuck 0:f2716e543d97 485 if( LMIC.globalDutyRate == 0 || (reftime - LMIC.globalDutyAvail) > 0 ) {
dudmuck 0:f2716e543d97 486 LMIC.globalDutyAvail = reftime;
dudmuck 0:f2716e543d97 487 LMIC.opmode |= OP_RNDTX;
dudmuck 0:f2716e543d97 488 }
dudmuck 0:f2716e543d97 489 }
dudmuck 0:f2716e543d97 490
dudmuck 0:f2716e543d97 491
dudmuck 0:f2716e543d97 492 static void setDrJoin (u1_t reason, u1_t dr) {
dudmuck 0:f2716e543d97 493 EV(drChange, INFO, (e_.reason = reason,
dudmuck 0:f2716e543d97 494 e_.deveui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 495 e_.dr = dr|DR_PAGE,
dudmuck 0:f2716e543d97 496 e_.txpow = LMIC.adrTxPow,
dudmuck 0:f2716e543d97 497 e_.prevdr = LMIC.datarate|DR_PAGE,
dudmuck 0:f2716e543d97 498 e_.prevtxpow = LMIC.adrTxPow));
dudmuck 0:f2716e543d97 499 LMIC.datarate = dr;
dudmuck 0:f2716e543d97 500 DO_DEVDB(LMIC.datarate,datarate);
dudmuck 0:f2716e543d97 501 }
dudmuck 0:f2716e543d97 502
dudmuck 0:f2716e543d97 503
dudmuck 0:f2716e543d97 504 static void setDrTxpow (u1_t reason, u1_t dr, s1_t pow) {
dudmuck 0:f2716e543d97 505 EV(drChange, INFO, (e_.reason = reason,
dudmuck 0:f2716e543d97 506 e_.deveui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 507 e_.dr = dr|DR_PAGE,
dudmuck 0:f2716e543d97 508 e_.txpow = pow,
dudmuck 0:f2716e543d97 509 e_.prevdr = LMIC.datarate|DR_PAGE,
dudmuck 0:f2716e543d97 510 e_.prevtxpow = LMIC.adrTxPow));
dudmuck 0:f2716e543d97 511
dudmuck 8:0faa1bb768b5 512 if( pow != KEEP_TXPOW ) {
dudmuck 0:f2716e543d97 513 LMIC.adrTxPow = pow;
Shaun Nelson 16:d5c50bdab1f1 514 if (pow <= LMIC.txpow_limit)
dudmuck 8:0faa1bb768b5 515 LMIC.txpow = pow;
dudmuck 8:0faa1bb768b5 516 }
dudmuck 0:f2716e543d97 517 if( LMIC.datarate != dr ) {
dudmuck 0:f2716e543d97 518 LMIC.datarate = dr;
dudmuck 0:f2716e543d97 519 DO_DEVDB(LMIC.datarate,datarate);
dudmuck 0:f2716e543d97 520 LMIC.opmode |= OP_NEXTCHNL;
dudmuck 0:f2716e543d97 521 }
dudmuck 0:f2716e543d97 522 }
dudmuck 0:f2716e543d97 523
dudmuck 0:f2716e543d97 524
dudmuck 0:f2716e543d97 525 void LMIC_stopPingable (void) {
dudmuck 0:f2716e543d97 526 LMIC.opmode &= ~(OP_PINGABLE|OP_PINGINI);
dudmuck 0:f2716e543d97 527 }
dudmuck 0:f2716e543d97 528
dudmuck 0:f2716e543d97 529
dudmuck 0:f2716e543d97 530 void LMIC_setPingable (u1_t intvExp) {
dudmuck 0:f2716e543d97 531 // Change setting
dudmuck 0:f2716e543d97 532 LMIC.ping.intvExp = (intvExp & 0x7);
dudmuck 0:f2716e543d97 533 LMIC.opmode |= OP_PINGABLE;
dudmuck 0:f2716e543d97 534 // App may call LMIC_enableTracking() explicitely before
dudmuck 0:f2716e543d97 535 // Otherwise tracking is implicitly enabled here
dudmuck 0:f2716e543d97 536 if( (LMIC.opmode & (OP_TRACK|OP_SCAN)) == 0 && LMIC.bcninfoTries == 0 )
dudmuck 0:f2716e543d97 537 LMIC_enableTracking(0);
dudmuck 0:f2716e543d97 538 }
dudmuck 0:f2716e543d97 539
dudmuck 0:f2716e543d97 540
dudmuck 0:f2716e543d97 541 #if defined(CFG_eu868)
dudmuck 0:f2716e543d97 542 // ================================================================================
dudmuck 0:f2716e543d97 543 //
dudmuck 0:f2716e543d97 544 // BEG: EU868 related stuff
dudmuck 0:f2716e543d97 545 //
dudmuck 0:f2716e543d97 546 enum { NUM_DEFAULT_CHANNELS=6 };
dudmuck 0:f2716e543d97 547 static const u4_t iniChannelFreq[12] = {
dudmuck 0:f2716e543d97 548 // Join frequencies and duty cycle limit (0.1%)
dudmuck 0:f2716e543d97 549 EU868_F1|BAND_MILLI, EU868_J4|BAND_MILLI,
dudmuck 0:f2716e543d97 550 EU868_F2|BAND_MILLI, EU868_J5|BAND_MILLI,
dudmuck 0:f2716e543d97 551 EU868_F3|BAND_MILLI, EU868_J6|BAND_MILLI,
dudmuck 0:f2716e543d97 552 // Default operational frequencies
dudmuck 0:f2716e543d97 553 EU868_F1|BAND_CENTI, EU868_F2|BAND_CENTI, EU868_F3|BAND_CENTI,
dudmuck 0:f2716e543d97 554 EU868_F4|BAND_MILLI, EU868_F5|BAND_MILLI, EU868_F6|BAND_DECI
dudmuck 0:f2716e543d97 555 };
dudmuck 0:f2716e543d97 556
dudmuck 0:f2716e543d97 557 static void initDefaultChannels (bit_t join) {
dudmuck 0:f2716e543d97 558 os_clearMem(&LMIC.channelFreq, sizeof(LMIC.channelFreq));
dudmuck 0:f2716e543d97 559 os_clearMem(&LMIC.channelDrMap, sizeof(LMIC.channelDrMap));
dudmuck 0:f2716e543d97 560 os_clearMem(&LMIC.bands, sizeof(LMIC.bands));
dudmuck 0:f2716e543d97 561
dudmuck 0:f2716e543d97 562 LMIC.channelMap = 0x3F;
dudmuck 0:f2716e543d97 563 u1_t su = join ? 0 : 6;
dudmuck 0:f2716e543d97 564 for( u1_t fu=0; fu<6; fu++,su++ ) {
dudmuck 0:f2716e543d97 565 LMIC.channelFreq[fu] = iniChannelFreq[su];
dudmuck 0:f2716e543d97 566 LMIC.channelDrMap[fu] = DR_RANGE_MAP(DR_SF12,DR_SF7);
dudmuck 0:f2716e543d97 567 }
dudmuck 0:f2716e543d97 568 if( !join ) {
dudmuck 0:f2716e543d97 569 LMIC.channelDrMap[5] = DR_RANGE_MAP(DR_SF12,DR_SF7);
dudmuck 0:f2716e543d97 570 LMIC.channelDrMap[1] = DR_RANGE_MAP(DR_SF12,DR_FSK);
dudmuck 0:f2716e543d97 571 }
dudmuck 0:f2716e543d97 572
dudmuck 0:f2716e543d97 573 LMIC.bands[BAND_MILLI].txcap = 1000; // 0.1%
dudmuck 0:f2716e543d97 574 LMIC.bands[BAND_MILLI].txpow = 14;
dudmuck 0:f2716e543d97 575 LMIC.bands[BAND_MILLI].lastchnl = os_getRndU1() % MAX_CHANNELS;
dudmuck 0:f2716e543d97 576 LMIC.bands[BAND_CENTI].txcap = 100; // 1%
dudmuck 0:f2716e543d97 577 LMIC.bands[BAND_CENTI].txpow = 14;
dudmuck 0:f2716e543d97 578 LMIC.bands[BAND_CENTI].lastchnl = os_getRndU1() % MAX_CHANNELS;
dudmuck 0:f2716e543d97 579 LMIC.bands[BAND_DECI ].txcap = 10; // 10%
dudmuck 0:f2716e543d97 580 LMIC.bands[BAND_DECI ].txpow = 27;
dudmuck 0:f2716e543d97 581 LMIC.bands[BAND_CENTI].lastchnl = os_getRndU1() % MAX_CHANNELS;
dudmuck 0:f2716e543d97 582 LMIC.bands[BAND_MILLI].avail =
dudmuck 0:f2716e543d97 583 LMIC.bands[BAND_CENTI].avail =
dudmuck 0:f2716e543d97 584 LMIC.bands[BAND_DECI ].avail = os_getTime();
dudmuck 0:f2716e543d97 585 }
dudmuck 0:f2716e543d97 586
dudmuck 0:f2716e543d97 587 bit_t LMIC_setupBand (u1_t bandidx, s1_t txpow, u2_t txcap) {
dudmuck 0:f2716e543d97 588 if( bandidx > BAND_AUX ) return 0;
dudmuck 0:f2716e543d97 589 band_t* b = &LMIC.bands[bandidx];
dudmuck 0:f2716e543d97 590 b->txpow = txpow;
dudmuck 0:f2716e543d97 591 b->txcap = txcap;
dudmuck 0:f2716e543d97 592 b->avail = os_getTime();
dudmuck 0:f2716e543d97 593 b->lastchnl = os_getRndU1() % MAX_CHANNELS;
dudmuck 0:f2716e543d97 594 return 1;
dudmuck 0:f2716e543d97 595 }
dudmuck 0:f2716e543d97 596
dudmuck 0:f2716e543d97 597 bit_t LMIC_setupChannel (u1_t chidx, u4_t freq, u2_t drmap, s1_t band) {
dudmuck 0:f2716e543d97 598 if( chidx >= MAX_CHANNELS )
dudmuck 0:f2716e543d97 599 return 0;
dudmuck 0:f2716e543d97 600 if( band == -1 ) {
dudmuck 0:f2716e543d97 601 if( freq >= 869400000 && freq <= 869650000 )
dudmuck 0:f2716e543d97 602 freq |= BAND_DECI; // 10% 27dBm
dudmuck 0:f2716e543d97 603 else if( (freq >= 868000000 && freq <= 868600000) ||
dudmuck 0:f2716e543d97 604 (freq >= 869700000 && freq <= 870000000) )
dudmuck 0:f2716e543d97 605 freq |= BAND_CENTI; // 1% 14dBm
dudmuck 0:f2716e543d97 606 else
dudmuck 0:f2716e543d97 607 freq |= BAND_MILLI; // 0.1% 14dBm
dudmuck 0:f2716e543d97 608 } else {
dudmuck 0:f2716e543d97 609 if( band > BAND_AUX ) return 0;
dudmuck 0:f2716e543d97 610 freq = (freq&~3) | band;
dudmuck 0:f2716e543d97 611 }
dudmuck 0:f2716e543d97 612 LMIC.channelFreq [chidx] = freq;
dudmuck 0:f2716e543d97 613 LMIC.channelDrMap[chidx] = drmap==0 ? DR_RANGE_MAP(DR_SF12,DR_SF7) : drmap;
dudmuck 0:f2716e543d97 614 LMIC.channelMap |= 1<<chidx; // enabled right away
dudmuck 0:f2716e543d97 615 return 1;
dudmuck 0:f2716e543d97 616 }
dudmuck 0:f2716e543d97 617
dudmuck 0:f2716e543d97 618 void LMIC_disableChannel (u1_t channel) {
dudmuck 0:f2716e543d97 619 LMIC.channelFreq[channel] = 0;
dudmuck 0:f2716e543d97 620 LMIC.channelDrMap[channel] = 0;
dudmuck 0:f2716e543d97 621 LMIC.channelMap &= ~(1<<channel);
dudmuck 0:f2716e543d97 622 }
dudmuck 0:f2716e543d97 623
dudmuck 0:f2716e543d97 624 static u4_t convFreq (xref2u1_t ptr) {
dudmuck 0:f2716e543d97 625 u4_t freq = (os_rlsbf4(ptr-1) >> 8) * 100;
dudmuck 0:f2716e543d97 626 if( freq < EU868_FREQ_MIN || freq > EU868_FREQ_MAX )
dudmuck 0:f2716e543d97 627 freq = 0;
dudmuck 0:f2716e543d97 628 return freq;
dudmuck 0:f2716e543d97 629 }
dudmuck 0:f2716e543d97 630
dudmuck 0:f2716e543d97 631 static u1_t mapChannels (u1_t chpage, u2_t chmap) {
dudmuck 0:f2716e543d97 632 // Bad page, disable all channel, enable non-existent
dudmuck 0:f2716e543d97 633 if( chpage != 0 || chmap==0 || (chmap & ~LMIC.channelMap) != 0 )
dudmuck 0:f2716e543d97 634 return 0; // illegal input
dudmuck 0:f2716e543d97 635 for( u1_t chnl=0; chnl<MAX_CHANNELS; chnl++ ) {
dudmuck 0:f2716e543d97 636 if( (chmap & (1<<chnl)) != 0 && LMIC.channelFreq[chnl] == 0 )
dudmuck 0:f2716e543d97 637 chmap &= ~(1<<chnl); // ignore - channel is not defined
dudmuck 0:f2716e543d97 638 }
dudmuck 0:f2716e543d97 639 LMIC.channelMap = chmap;
dudmuck 0:f2716e543d97 640 return 1;
dudmuck 0:f2716e543d97 641 }
dudmuck 0:f2716e543d97 642
dudmuck 0:f2716e543d97 643
dudmuck 0:f2716e543d97 644 static void updateTx (ostime_t txbeg) {
dudmuck 0:f2716e543d97 645 u4_t freq = LMIC.channelFreq[LMIC.txChnl];
dudmuck 0:f2716e543d97 646 // Update global/band specific duty cycle stats
dudmuck 0:f2716e543d97 647 ostime_t airtime = calcAirTime(LMIC.rps, LMIC.dataLen);
dudmuck 0:f2716e543d97 648 // Update channel/global duty cycle stats
dudmuck 0:f2716e543d97 649 xref2band_t band = &LMIC.bands[freq & 0x3];
dudmuck 0:f2716e543d97 650 LMIC.freq = freq & ~(u4_t)3;
dudmuck 0:f2716e543d97 651 LMIC.txpow = band->txpow;
dudmuck 0:f2716e543d97 652 band->avail = txbeg + airtime * band->txcap;
dudmuck 0:f2716e543d97 653 if( LMIC.globalDutyRate != 0 )
dudmuck 0:f2716e543d97 654 LMIC.globalDutyAvail = txbeg + (airtime<<LMIC.globalDutyRate);
dudmuck 0:f2716e543d97 655 }
dudmuck 0:f2716e543d97 656
dudmuck 0:f2716e543d97 657 static ostime_t nextTx (ostime_t now) {
dudmuck 0:f2716e543d97 658 u1_t bmap=0xF;
dudmuck 0:f2716e543d97 659 do {
dudmuck 0:f2716e543d97 660 ostime_t mintime = now + /*10h*/36000*OSTICKS_PER_SEC;
dudmuck 0:f2716e543d97 661 u1_t band=0;
dudmuck 0:f2716e543d97 662 for( u1_t bi=0; bi<4; bi++ ) {
dudmuck 0:f2716e543d97 663 if( (bmap & (1<<bi)) && mintime - LMIC.bands[bi].avail > 0 )
dudmuck 0:f2716e543d97 664 mintime = LMIC.bands[band = bi].avail;
dudmuck 0:f2716e543d97 665 }
dudmuck 0:f2716e543d97 666 // Find next channel in given band
dudmuck 0:f2716e543d97 667 u1_t chnl = LMIC.bands[band].lastchnl;
dudmuck 0:f2716e543d97 668 for( u1_t ci=0; ci<MAX_CHANNELS; ci++ ) {
dudmuck 0:f2716e543d97 669 if( (chnl = (chnl+1)) >= MAX_CHANNELS )
dudmuck 0:f2716e543d97 670 chnl -= MAX_CHANNELS;
dudmuck 0:f2716e543d97 671 if( (LMIC.channelMap & (1<<chnl)) != 0 && // channel enabled
dudmuck 0:f2716e543d97 672 (LMIC.channelDrMap[chnl] & (1<<(LMIC.datarate&0xF))) != 0 &&
dudmuck 0:f2716e543d97 673 band == (LMIC.channelFreq[chnl] & 0x3) ) { // in selected band
dudmuck 0:f2716e543d97 674 LMIC.txChnl = LMIC.bands[band].lastchnl = chnl;
dudmuck 0:f2716e543d97 675 return mintime;
dudmuck 0:f2716e543d97 676 }
dudmuck 0:f2716e543d97 677 }
dudmuck 0:f2716e543d97 678 if( (bmap &= ~(1<<band)) == 0 ) {
dudmuck 0:f2716e543d97 679 // No feasible channel found!
dudmuck 0:f2716e543d97 680 return mintime;
dudmuck 0:f2716e543d97 681 }
dudmuck 0:f2716e543d97 682 } while(1);
dudmuck 0:f2716e543d97 683 }
dudmuck 0:f2716e543d97 684
dudmuck 0:f2716e543d97 685
dudmuck 0:f2716e543d97 686 static void setBcnRxParams (void) {
dudmuck 0:f2716e543d97 687 LMIC.dataLen = 0;
dudmuck 0:f2716e543d97 688 LMIC.freq = LMIC.channelFreq[LMIC.bcnChnl] & ~(u4_t)3;
dudmuck 0:f2716e543d97 689 LMIC.rps = setIh(setNocrc(dndr2rps((dr_t)DR_BCN),1),LEN_BCN);
dudmuck 0:f2716e543d97 690 }
dudmuck 0:f2716e543d97 691
dudmuck 0:f2716e543d97 692 #define setRx1Params() /*LMIC.freq/rps remain unchanged*/
dudmuck 0:f2716e543d97 693
dudmuck 8:0faa1bb768b5 694 static void initJoinLoop (void) { // eu868
dudmuck 0:f2716e543d97 695 LMIC.txChnl = os_getRndU1() % 6;
dudmuck 0:f2716e543d97 696 LMIC.adrTxPow = 14;
dudmuck 0:f2716e543d97 697 setDrJoin(DRCHG_SET, DR_SF7);
dudmuck 0:f2716e543d97 698 initDefaultChannels(1);
dudmuck 0:f2716e543d97 699 ASSERT((LMIC.opmode & OP_NEXTCHNL)==0);
dudmuck 0:f2716e543d97 700 LMIC.txend = LMIC.bands[BAND_MILLI].avail + rndDelay(8);
dudmuck 0:f2716e543d97 701 }
dudmuck 0:f2716e543d97 702
dudmuck 0:f2716e543d97 703
dudmuck 0:f2716e543d97 704 static ostime_t nextJoinState (void) {
dudmuck 0:f2716e543d97 705 u1_t failed = 0;
dudmuck 0:f2716e543d97 706
dudmuck 0:f2716e543d97 707 // Try 869.x and then 864.x with same DR
dudmuck 0:f2716e543d97 708 // If both fail try next lower datarate
dudmuck 0:f2716e543d97 709 if( ++LMIC.txChnl == 6 )
dudmuck 0:f2716e543d97 710 LMIC.txChnl = 0;
dudmuck 0:f2716e543d97 711 if( (++LMIC.txCnt & 1) == 0 ) {
dudmuck 0:f2716e543d97 712 // Lower DR every 2nd try (having tried 868.x and 864.x with the same DR)
dudmuck 0:f2716e543d97 713 if( LMIC.datarate == DR_SF12 )
dudmuck 0:f2716e543d97 714 failed = 1; // we have tried all DR - signal EV_JOIN_FAILED
dudmuck 0:f2716e543d97 715 else
dudmuck 0:f2716e543d97 716 setDrJoin(DRCHG_NOJACC, decDR((dr_t)LMIC.datarate));
dudmuck 0:f2716e543d97 717 }
dudmuck 0:f2716e543d97 718 // Clear NEXTCHNL because join state engine controls channel hopping
dudmuck 0:f2716e543d97 719 LMIC.opmode &= ~OP_NEXTCHNL;
dudmuck 0:f2716e543d97 720 // Move txend to randomize synchronized concurrent joins.
dudmuck 0:f2716e543d97 721 // Duty cycle is based on txend.
dudmuck 0:f2716e543d97 722 ostime_t time = os_getTime();
dudmuck 0:f2716e543d97 723 if( time - LMIC.bands[BAND_MILLI].avail < 0 )
dudmuck 0:f2716e543d97 724 time = LMIC.bands[BAND_MILLI].avail;
dudmuck 0:f2716e543d97 725 LMIC.txend = time +
dudmuck 0:f2716e543d97 726 (isTESTMODE()
dudmuck 0:f2716e543d97 727 // Avoid collision with JOIN ACCEPT @ SF12 being sent by GW (but we missed it)
dudmuck 0:f2716e543d97 728 ? DNW2_SAFETY_ZONE
dudmuck 0:f2716e543d97 729 // Otherwise: randomize join (street lamp case):
dudmuck 0:f2716e543d97 730 // SF12:255, SF11:127, .., SF7:8secs
dudmuck 0:f2716e543d97 731 : DNW2_SAFETY_ZONE+rndDelay(255>>LMIC.datarate));
dudmuck 0:f2716e543d97 732 // 1 - triggers EV_JOIN_FAILED event
dudmuck 0:f2716e543d97 733 return failed;
dudmuck 0:f2716e543d97 734 }
dudmuck 0:f2716e543d97 735
dudmuck 0:f2716e543d97 736 //
dudmuck 0:f2716e543d97 737 // END: EU868 related stuff
dudmuck 0:f2716e543d97 738 //
dudmuck 0:f2716e543d97 739 // ================================================================================
dudmuck 0:f2716e543d97 740 #elif defined(CFG_us915)
dudmuck 0:f2716e543d97 741 // ================================================================================
dudmuck 0:f2716e543d97 742 //
dudmuck 0:f2716e543d97 743 // BEG: US915 related stuff
dudmuck 0:f2716e543d97 744 //
dudmuck 0:f2716e543d97 745
dudmuck 0:f2716e543d97 746
dudmuck 5:e4ba433f0ac1 747 static void initDefaultChannels (void)
dudmuck 5:e4ba433f0ac1 748 {
dudmuck 5:e4ba433f0ac1 749 #ifdef CHNL_HYBRID
dudmuck 7:9095e54e381f 750 int idx = CHNL_HYBRID >> 1;
dudmuck 7:9095e54e381f 751 LMIC.channelMap[0] = 0x0000;
dudmuck 7:9095e54e381f 752 LMIC.channelMap[1] = 0x0000;
dudmuck 7:9095e54e381f 753 LMIC.channelMap[2] = 0x0000;
dudmuck 7:9095e54e381f 754 LMIC.channelMap[3] = 0x0000;
dudmuck 7:9095e54e381f 755 if (CHNL_HYBRID & 1)
dudmuck 7:9095e54e381f 756 LMIC.channelMap[idx] = 0xff00;
dudmuck 7:9095e54e381f 757 else
dudmuck 7:9095e54e381f 758 LMIC.channelMap[idx] = 0x00ff;
dudmuck 7:9095e54e381f 759
dudmuck 7:9095e54e381f 760 LMIC.channelMap[4] = 1 << CHNL_HYBRID;
dudmuck 5:e4ba433f0ac1 761 LMIC.txpow_limit = 20;
dudmuck 5:e4ba433f0ac1 762 #else
dudmuck 0:f2716e543d97 763 for( u1_t i=0; i<4; i++ )
dudmuck 0:f2716e543d97 764 LMIC.channelMap[i] = 0xFFFF;
dudmuck 0:f2716e543d97 765 LMIC.channelMap[4] = 0x00FF;
dudmuck 5:e4ba433f0ac1 766
dudmuck 5:e4ba433f0ac1 767 LMIC.txpow_limit = 30;
dudmuck 5:e4ba433f0ac1 768 #endif
dudmuck 8:0faa1bb768b5 769
dudmuck 8:0faa1bb768b5 770 LMIC.txpow = LMIC.txpow_limit;
dudmuck 8:0faa1bb768b5 771 LMIC.adrTxPow = LMIC.txpow_limit;
dudmuck 0:f2716e543d97 772 }
dudmuck 0:f2716e543d97 773
dudmuck 0:f2716e543d97 774 static u4_t convFreq (xref2u1_t ptr) {
dudmuck 0:f2716e543d97 775 u4_t freq = (os_rlsbf4(ptr-1) >> 8) * 100;
dudmuck 0:f2716e543d97 776 if( freq < US915_FREQ_MIN || freq > US915_FREQ_MAX )
dudmuck 0:f2716e543d97 777 freq = 0;
dudmuck 0:f2716e543d97 778 return freq;
dudmuck 0:f2716e543d97 779 }
dudmuck 0:f2716e543d97 780
dudmuck 0:f2716e543d97 781 bit_t LMIC_setupChannel (u1_t chidx, u4_t freq, u2_t drmap, s1_t band) {
dudmuck 0:f2716e543d97 782 if( chidx < 72 || chidx >= 72+MAX_XCHANNELS )
dudmuck 0:f2716e543d97 783 return 0; // channels 0..71 are hardwired
dudmuck 0:f2716e543d97 784 chidx -= 72;
dudmuck 0:f2716e543d97 785 LMIC.xchFreq[chidx] = freq;
dudmuck 0:f2716e543d97 786 LMIC.xchDrMap[chidx] = drmap==0 ? DR_RANGE_MAP(DR_SF10,DR_SF8C) : drmap;
dudmuck 0:f2716e543d97 787 LMIC.channelMap[chidx>>4] |= (1<<(chidx&0xF));
dudmuck 0:f2716e543d97 788 return 1;
dudmuck 0:f2716e543d97 789 }
dudmuck 0:f2716e543d97 790
dudmuck 0:f2716e543d97 791 void LMIC_disableChannel (u1_t channel) {
dudmuck 0:f2716e543d97 792 if( channel < 72+MAX_XCHANNELS )
dudmuck 0:f2716e543d97 793 LMIC.channelMap[channel/4] &= ~(1<<(channel&0xF));
dudmuck 0:f2716e543d97 794 }
dudmuck 0:f2716e543d97 795
dudmuck 0:f2716e543d97 796 static u1_t mapChannels (u1_t chpage, u2_t chmap) {
dudmuck 0:f2716e543d97 797 if( chpage == MCMD_LADR_CHP_125ON || chpage == MCMD_LADR_CHP_125OFF ) {
dudmuck 0:f2716e543d97 798 u2_t en125 = chpage == MCMD_LADR_CHP_125ON ? 0xFFFF : 0x0000;
dudmuck 0:f2716e543d97 799 for( u1_t u=0; u<4; u++ )
dudmuck 0:f2716e543d97 800 LMIC.channelMap[u] = en125;
dudmuck 0:f2716e543d97 801 LMIC.channelMap[64/16] = chmap;
dudmuck 0:f2716e543d97 802 } else {
dkjendal 13:097c74da6ffe 803 chpage >>= 4; //DLK::chpage is only the top nibble... before this, only works for chpage=0,6,7
dudmuck 0:f2716e543d97 804 if( chpage >= (72+MAX_XCHANNELS+15)/16 )
dudmuck 0:f2716e543d97 805 return 0;
dudmuck 0:f2716e543d97 806 LMIC.channelMap[chpage] = chmap;
dudmuck 0:f2716e543d97 807 }
dudmuck 0:f2716e543d97 808 return 1;
dudmuck 0:f2716e543d97 809 }
dudmuck 0:f2716e543d97 810
dudmuck 0:f2716e543d97 811 static void updateTx (ostime_t txbeg) {
dudmuck 0:f2716e543d97 812 u1_t chnl = LMIC.txChnl;
dudmuck 8:0faa1bb768b5 813 #ifdef JOIN_REQ_DEBUG
dudmuck 8:0faa1bb768b5 814 printf("chnl%d ", chnl);
dudmuck 8:0faa1bb768b5 815 #endif /* JOIN_REQ_DEBUG */
dudmuck 0:f2716e543d97 816 if( chnl < 64 ) {
dudmuck 0:f2716e543d97 817 LMIC.freq = US915_125kHz_UPFBASE + chnl*US915_125kHz_UPFSTEP;
dudmuck 9:83ae7f34e88c 818
dudmuck 9:83ae7f34e88c 819 if (LMIC.opmode & OP_JOINING) {
dudmuck 9:83ae7f34e88c 820 /* use max allowed power for joining */
dudmuck 9:83ae7f34e88c 821 if (LMIC.txpow < LMIC.txpow_limit)
dudmuck 9:83ae7f34e88c 822 LMIC.txpow = LMIC.txpow_limit;
dudmuck 9:83ae7f34e88c 823 }
dudmuck 9:83ae7f34e88c 824
dudmuck 8:0faa1bb768b5 825 #ifdef JOIN_REQ_DEBUG
dudmuck 8:0faa1bb768b5 826 printf("%d (125khz)\r\n", LMIC.freq);
dudmuck 8:0faa1bb768b5 827 #endif /* JOIN_REQ_DEBUG */
dudmuck 0:f2716e543d97 828 return;
dudmuck 0:f2716e543d97 829 }
dudmuck 5:e4ba433f0ac1 830
dudmuck 8:0faa1bb768b5 831 /* using 500KHz channel */
dudmuck 5:e4ba433f0ac1 832 if (LMIC.txpow_limit >= 26)
dudmuck 5:e4ba433f0ac1 833 LMIC.txpow = 26;
dudmuck 5:e4ba433f0ac1 834 else
dudmuck 5:e4ba433f0ac1 835 LMIC.txpow = LMIC.txpow_limit;
dudmuck 5:e4ba433f0ac1 836
dudmuck 0:f2716e543d97 837 if( chnl < 64+8 ) {
dudmuck 0:f2716e543d97 838 LMIC.freq = US915_500kHz_UPFBASE + (chnl-64)*US915_500kHz_UPFSTEP;
dudmuck 8:0faa1bb768b5 839 #ifdef JOIN_REQ_DEBUG
dudmuck 8:0faa1bb768b5 840 printf("%d (500k)\r\n", LMIC.freq);
dudmuck 8:0faa1bb768b5 841 #endif /* JOIN_REQ_DEBUG */
dudmuck 0:f2716e543d97 842 } else {
dudmuck 0:f2716e543d97 843 ASSERT(chnl < 64+8+MAX_XCHANNELS);
dudmuck 0:f2716e543d97 844 LMIC.freq = LMIC.xchFreq[chnl-72];
dudmuck 8:0faa1bb768b5 845 #ifdef JOIN_REQ_DEBUG
dudmuck 8:0faa1bb768b5 846 printf("%d (x)\r\n", LMIC.freq);
dudmuck 8:0faa1bb768b5 847 #endif /* JOIN_REQ_DEBUG */
dudmuck 0:f2716e543d97 848 }
dudmuck 0:f2716e543d97 849
dudmuck 0:f2716e543d97 850 // Update global duty cycle stats
dudmuck 0:f2716e543d97 851 if( LMIC.globalDutyRate != 0 ) {
dudmuck 0:f2716e543d97 852 ostime_t airtime = calcAirTime(LMIC.rps, LMIC.dataLen);
dudmuck 0:f2716e543d97 853 LMIC.globalDutyAvail = txbeg + (airtime<<LMIC.globalDutyRate);
dudmuck 0:f2716e543d97 854 }
dudmuck 0:f2716e543d97 855 }
dudmuck 0:f2716e543d97 856
dudmuck 7:9095e54e381f 857 int count_bits(u2_t v)
dudmuck 7:9095e54e381f 858 {
dudmuck 7:9095e54e381f 859 int c;
dudmuck 7:9095e54e381f 860
dudmuck 7:9095e54e381f 861 for (c = 0; v; c++) {
dudmuck 7:9095e54e381f 862 v &= v - 1; // clear the last significant bit set
dudmuck 7:9095e54e381f 863 }
dudmuck 7:9095e54e381f 864
dudmuck 7:9095e54e381f 865 return c;
dudmuck 7:9095e54e381f 866 }
dudmuck 7:9095e54e381f 867
dudmuck 0:f2716e543d97 868 // US does not have duty cycling - return now as earliest TX time
dudmuck 0:f2716e543d97 869 #define nextTx(now) (_nextTx(),(now))
dudmuck 0:f2716e543d97 870 static void _nextTx (void) {
dudmuck 7:9095e54e381f 871 u1_t prev_ch = LMIC.txChnl;
dudmuck 7:9095e54e381f 872 u1_t tries = 0;
dudmuck 7:9095e54e381f 873 u1_t en_cnt;
dudmuck 7:9095e54e381f 874
dudmuck 0:f2716e543d97 875 if( LMIC.datarate >= DR_SF8C ) { // 500kHz
dudmuck 7:9095e54e381f 876 #ifdef CHNL_HYBRID
dudmuck 10:6c0830baf10f 877 LMIC.txChnl = CHNL_HYBRID + 64; // only one channel possible
dudmuck 7:9095e54e381f 878 #else
dudmuck 7:9095e54e381f 879 en_cnt = count_bits(LMIC.channelMap[4]);
dudmuck 7:9095e54e381f 880 do {
dudmuck 7:9095e54e381f 881 do {
dudmuck 7:9095e54e381f 882 LMIC.chRnd = os_getRndU1() & 7;
dudmuck 7:9095e54e381f 883 if (++tries > 48)
dudmuck 7:9095e54e381f 884 return;
dudmuck 7:9095e54e381f 885 } while ( !(LMIC.channelMap[4] & (1 << LMIC.chRnd)) );
dudmuck 7:9095e54e381f 886 LMIC.txChnl = 64 + LMIC.chRnd;
dudmuck 7:9095e54e381f 887 if (en_cnt < 2)
dudmuck 7:9095e54e381f 888 prev_ch = LMIC.txChnl + 1; // not enough enabled, skip the following test
dudmuck 7:9095e54e381f 889
dudmuck 7:9095e54e381f 890 } while (prev_ch == LMIC.txChnl);
dudmuck 7:9095e54e381f 891 #endif
dudmuck 0:f2716e543d97 892 } else { // 125kHz
dudmuck 7:9095e54e381f 893 #ifdef CHNL_HYBRID
dudmuck 7:9095e54e381f 894 u1_t idx = CHNL_HYBRID >> 1;
dudmuck 7:9095e54e381f 895 en_cnt = count_bits(LMIC.channelMap[idx]);
dudmuck 7:9095e54e381f 896 do {
dudmuck 7:9095e54e381f 897 do {
dudmuck 7:9095e54e381f 898 LMIC.chRnd = os_getRndU1() & 15;
dudmuck 7:9095e54e381f 899 if (++tries > 96)
dudmuck 7:9095e54e381f 900 return;
dudmuck 7:9095e54e381f 901 } while ( !(LMIC.channelMap[idx] & (1 << LMIC.chRnd)) );
dudmuck 7:9095e54e381f 902 LMIC.txChnl = (idx << 4) + LMIC.chRnd;
dudmuck 7:9095e54e381f 903 if (en_cnt < 2)
dudmuck 7:9095e54e381f 904 prev_ch = LMIC.txChnl + 1; // not enough enabled, skip the following test
dudmuck 7:9095e54e381f 905
dudmuck 7:9095e54e381f 906 } while (prev_ch == LMIC.txChnl);
dudmuck 7:9095e54e381f 907 #else
dudmuck 7:9095e54e381f 908 en_cnt = count_bits(LMIC.channelMap[0]);
dudmuck 7:9095e54e381f 909 en_cnt += count_bits(LMIC.channelMap[1]);
dudmuck 7:9095e54e381f 910 en_cnt += count_bits(LMIC.channelMap[2]);
dudmuck 7:9095e54e381f 911 en_cnt += count_bits(LMIC.channelMap[3]);
dudmuck 7:9095e54e381f 912 do {
dudmuck 7:9095e54e381f 913 do {
dudmuck 7:9095e54e381f 914 LMIC.chRnd = os_getRndU1() & 63;
dudmuck 7:9095e54e381f 915 } while ( !(LMIC.channelMap[LMIC.chRnd >> 4] & (1 << (LMIC.chRnd & 15))) );
dudmuck 7:9095e54e381f 916 LMIC.txChnl = LMIC.chRnd;
dudmuck 7:9095e54e381f 917 if (en_cnt < 2)
dudmuck 7:9095e54e381f 918 prev_ch = LMIC.txChnl + 1; // not enough enabled, skip the following test
dudmuck 7:9095e54e381f 919
dudmuck 7:9095e54e381f 920 } while (prev_ch == LMIC.txChnl);
dudmuck 7:9095e54e381f 921 #endif
dudmuck 0:f2716e543d97 922 }
dudmuck 0:f2716e543d97 923 }
dudmuck 0:f2716e543d97 924
dudmuck 0:f2716e543d97 925 static void setBcnRxParams (void) {
dudmuck 0:f2716e543d97 926 LMIC.dataLen = 0;
dudmuck 0:f2716e543d97 927 LMIC.freq = US915_500kHz_DNFBASE + LMIC.bcnChnl * US915_500kHz_DNFSTEP;
dudmuck 0:f2716e543d97 928 LMIC.rps = setIh(setNocrc(dndr2rps((dr_t)DR_BCN),1),LEN_BCN);
dudmuck 0:f2716e543d97 929 }
dudmuck 0:f2716e543d97 930
dudmuck 0:f2716e543d97 931 #define setRx1Params() { \
dudmuck 0:f2716e543d97 932 LMIC.freq = US915_500kHz_DNFBASE + (LMIC.txChnl & 0x7) * US915_500kHz_DNFSTEP; \
dudmuck 0:f2716e543d97 933 if( /* TX datarate */LMIC.dndr < DR_SF8C ) \
dudmuck 0:f2716e543d97 934 LMIC.dndr += DR_SF10CR - DR_SF10; \
dudmuck 0:f2716e543d97 935 else if( LMIC.dndr == DR_SF8C ) \
dudmuck 0:f2716e543d97 936 LMIC.dndr = DR_SF7CR; \
dudmuck 0:f2716e543d97 937 LMIC.rps = dndr2rps(LMIC.dndr); \
dudmuck 0:f2716e543d97 938 }
dudmuck 0:f2716e543d97 939
dudmuck 0:f2716e543d97 940 static void initJoinLoop (void) {
dudmuck 0:f2716e543d97 941 LMIC.chRnd = 0;
dudmuck 9:83ae7f34e88c 942 #ifdef CHNL_HYBRID
dudmuck 9:83ae7f34e88c 943 LMIC.joinBlockChnl = 0;
dudmuck 9:83ae7f34e88c 944 LMIC.joinBlock = CHNL_HYBRID;
dudmuck 9:83ae7f34e88c 945 LMIC.txChnl = LMIC.joinBlock << 3;
dudmuck 9:83ae7f34e88c 946 #else
dudmuck 8:0faa1bb768b5 947 LMIC.txChnl = 0;
dudmuck 8:0faa1bb768b5 948 LMIC.joinBlockChnl = 0;
dudmuck 8:0faa1bb768b5 949 LMIC.joinBlock = 0;
dudmuck 9:83ae7f34e88c 950 #endif
dudmuck 8:0faa1bb768b5 951 LMIC.datarate = DR_SF10;
dudmuck 8:0faa1bb768b5 952 LMIC.adrTxPow = LMIC.txpow_limit;
dudmuck 0:f2716e543d97 953 ASSERT((LMIC.opmode & OP_NEXTCHNL)==0);
dudmuck 0:f2716e543d97 954 LMIC.txend = os_getTime();
dudmuck 0:f2716e543d97 955 setDrJoin(DRCHG_SET, DR_SF7);
dudmuck 0:f2716e543d97 956 }
dudmuck 0:f2716e543d97 957
dudmuck 10:6c0830baf10f 958 static ostime_t nextJoinState (void) { // us915
dudmuck 8:0faa1bb768b5 959 u1_t failed = 0;
dudmuck 8:0faa1bb768b5 960
dudmuck 8:0faa1bb768b5 961 #if 0
dudmuck 0:f2716e543d97 962 // Try the following:
dudmuck 0:f2716e543d97 963 // SF7/8/9/10 on a random channel 0..63
dudmuck 0:f2716e543d97 964 // SF8C on a random channel 64..71
dudmuck 0:f2716e543d97 965 //
dudmuck 0:f2716e543d97 966 if( LMIC.datarate != DR_SF8C ) {
dudmuck 7:9095e54e381f 967 _nextTx();
dudmuck 0:f2716e543d97 968 setDrJoin(DRCHG_SET, DR_SF8C);
dudmuck 0:f2716e543d97 969 } else {
dudmuck 7:9095e54e381f 970 _nextTx();
dudmuck 0:f2716e543d97 971 s1_t dr = DR_SF7 - ++LMIC.txCnt;
dudmuck 0:f2716e543d97 972 if( dr < DR_SF10 ) {
dudmuck 0:f2716e543d97 973 dr = DR_SF10;
dudmuck 0:f2716e543d97 974 failed = 1; // All DR exhausted - signal failed
dudmuck 0:f2716e543d97 975 }
dudmuck 0:f2716e543d97 976 setDrJoin(DRCHG_SET, dr);
dudmuck 0:f2716e543d97 977 }
dudmuck 8:0faa1bb768b5 978 #endif /* #if 0 */
dudmuck 8:0faa1bb768b5 979
dudmuck 8:0faa1bb768b5 980 if( LMIC.datarate == DR_SF8C ) {
dudmuck 8:0faa1bb768b5 981 // attempted 500khz channel, try 125khz channel in next block
dudmuck 8:0faa1bb768b5 982 LMIC.datarate = DR_SF10;
Shaun Nelson 16:d5c50bdab1f1 983 LMIC.txpow = LMIC.txpow_limit;
Shaun Nelson 16:d5c50bdab1f1 984 if (++LMIC.joinBlock == 8)
dudmuck 8:0faa1bb768b5 985 LMIC.joinBlock = 0;
Shaun Nelson 16:d5c50bdab1f1 986 LMIC.joinBlockChnl = os_getRndU1() & 0x7;
dudmuck 8:0faa1bb768b5 987 LMIC.txChnl = (LMIC.joinBlock << 3) + LMIC.joinBlockChnl;
dudmuck 8:0faa1bb768b5 988 } else {
dudmuck 8:0faa1bb768b5 989 // attempted 125khz channel, try 500khz channel
dudmuck 8:0faa1bb768b5 990 LMIC.datarate = DR_SF8C;
Shaun Nelson 16:d5c50bdab1f1 991 LMIC.txpow = (LMIC.txpow_limit > 26)?26:LMIC.txpow_limit;
dudmuck 8:0faa1bb768b5 992 LMIC.txChnl = LMIC.joinBlock + 64;
dudmuck 8:0faa1bb768b5 993 }
dudmuck 8:0faa1bb768b5 994 #ifdef JOIN_REQ_DEBUG
dudmuck 8:0faa1bb768b5 995 printf("njs blk%d, dr%d, txChnl%d ", LMIC.joinBlock, LMIC.datarate, LMIC.txChnl); // crlf in updateTx()
dudmuck 8:0faa1bb768b5 996 #endif /* JOIN_REQ_DEBUG */
dudmuck 8:0faa1bb768b5 997
dudmuck 0:f2716e543d97 998 LMIC.opmode &= ~OP_NEXTCHNL;
dudmuck 0:f2716e543d97 999 LMIC.txend = os_getTime() +
dudmuck 0:f2716e543d97 1000 (isTESTMODE()
dudmuck 0:f2716e543d97 1001 // Avoid collision with JOIN ACCEPT being sent by GW (but we missed it - GW is still busy)
dudmuck 0:f2716e543d97 1002 ? DNW2_SAFETY_ZONE
dudmuck 0:f2716e543d97 1003 // Otherwise: randomize join (street lamp case):
dudmuck 0:f2716e543d97 1004 // SF10:16, SF9=8,..SF8C:1secs
dudmuck 0:f2716e543d97 1005 : rndDelay(16>>LMIC.datarate));
dudmuck 0:f2716e543d97 1006 // 1 - triggers EV_JOIN_FAILED event
dudmuck 0:f2716e543d97 1007 return failed;
dudmuck 0:f2716e543d97 1008 }
dudmuck 0:f2716e543d97 1009
dudmuck 0:f2716e543d97 1010 //
dudmuck 0:f2716e543d97 1011 // END: US915 related stuff
dudmuck 0:f2716e543d97 1012 //
dudmuck 0:f2716e543d97 1013 // ================================================================================
dudmuck 0:f2716e543d97 1014 #else
dudmuck 0:f2716e543d97 1015 #error Unsupported frequency band!
dudmuck 0:f2716e543d97 1016 #endif
dudmuck 0:f2716e543d97 1017
dudmuck 0:f2716e543d97 1018
dudmuck 0:f2716e543d97 1019 static void runEngineUpdate (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 1020 engineUpdate();
dudmuck 0:f2716e543d97 1021 }
dudmuck 0:f2716e543d97 1022
dudmuck 0:f2716e543d97 1023
dudmuck 0:f2716e543d97 1024 static void reportEvent (ev_t ev) {
dudmuck 0:f2716e543d97 1025 EV(devCond, INFO, (e_.reason = EV::devCond_t::LMIC_EV,
dudmuck 0:f2716e543d97 1026 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1027 e_.info = ev));
dudmuck 0:f2716e543d97 1028 ON_LMIC_EVENT(ev);
dudmuck 0:f2716e543d97 1029 engineUpdate();
dudmuck 0:f2716e543d97 1030 }
dudmuck 0:f2716e543d97 1031
dudmuck 0:f2716e543d97 1032
dudmuck 0:f2716e543d97 1033 static void runReset (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 1034 // Disable session
dudmuck 0:f2716e543d97 1035 LMIC_reset();
dudmuck 0:f2716e543d97 1036 LMIC_startJoining();
dudmuck 0:f2716e543d97 1037 reportEvent(EV_RESET);
dudmuck 0:f2716e543d97 1038 }
dudmuck 0:f2716e543d97 1039
dudmuck 0:f2716e543d97 1040 static void stateJustJoined (void) {
dudmuck 0:f2716e543d97 1041 LMIC.seqnoDn = LMIC.seqnoUp = 0;
dudmuck 0:f2716e543d97 1042 LMIC.rejoinCnt = 0;
dudmuck 0:f2716e543d97 1043 LMIC.dnConf = LMIC.adrChanged = LMIC.ladrAns = LMIC.devsAns = 0;
dudmuck 0:f2716e543d97 1044 LMIC.moreData = LMIC.dn2Ans = LMIC.snchAns = LMIC.dutyCapAns = 0;
dudmuck 0:f2716e543d97 1045 LMIC.pingSetAns = 0;
dudmuck 0:f2716e543d97 1046 LMIC.upRepeat = 0;
dudmuck 0:f2716e543d97 1047 LMIC.adrAckReq = LINK_CHECK_INIT;
dudmuck 0:f2716e543d97 1048 LMIC.dn2Dr = DR_DNW2;
dudmuck 0:f2716e543d97 1049 LMIC.dn2Freq = FREQ_DNW2;
dudmuck 0:f2716e543d97 1050 LMIC.bcnChnl = CHNL_BCN;
dudmuck 0:f2716e543d97 1051 LMIC.ping.freq = FREQ_PING;
dudmuck 0:f2716e543d97 1052 LMIC.ping.dr = DR_PING;
dudmuck 0:f2716e543d97 1053 }
dudmuck 0:f2716e543d97 1054
dudmuck 0:f2716e543d97 1055
dudmuck 0:f2716e543d97 1056 // ================================================================================
dudmuck 0:f2716e543d97 1057 // Decoding frames
dudmuck 0:f2716e543d97 1058
dudmuck 0:f2716e543d97 1059
dudmuck 0:f2716e543d97 1060 // Decode beacon - do not overwrite bcninfo unless we have a match!
dudmuck 0:f2716e543d97 1061 static int decodeBeacon (void) {
dudmuck 0:f2716e543d97 1062 ASSERT(LMIC.dataLen == LEN_BCN); // implicit header RX guarantees this
dudmuck 0:f2716e543d97 1063 xref2u1_t d = LMIC.frame;
dudmuck 0:f2716e543d97 1064 if(
dudmuck 0:f2716e543d97 1065 #ifdef CFG_eu868
dudmuck 0:f2716e543d97 1066 d[OFF_BCN_CRC1] != (u1_t)os_crc16(d,OFF_BCN_CRC1)
dudmuck 0:f2716e543d97 1067 #elif defined(CFG_us915)
dudmuck 0:f2716e543d97 1068 os_rlsbf2(&d[OFF_BCN_CRC1]) != os_crc16(d,OFF_BCN_CRC1)
dudmuck 0:f2716e543d97 1069 #endif
dudmuck 0:f2716e543d97 1070 )
dudmuck 0:f2716e543d97 1071 return 0; // first (common) part fails CRC check
dudmuck 0:f2716e543d97 1072 // First set of fields is ok
dudmuck 0:f2716e543d97 1073 u4_t bcnnetid = os_rlsbf4(&d[OFF_BCN_NETID]) & 0xFFFFFF;
dudmuck 0:f2716e543d97 1074 if( bcnnetid != LMIC.netid )
dudmuck 0:f2716e543d97 1075 return -1; // not the beacon we're looking for
dudmuck 0:f2716e543d97 1076
dudmuck 0:f2716e543d97 1077 LMIC.bcninfo.flags &= ~(BCN_PARTIAL|BCN_FULL);
dudmuck 0:f2716e543d97 1078 // Match - update bcninfo structure
dudmuck 0:f2716e543d97 1079 LMIC.bcninfo.snr = LMIC.snr;
dudmuck 0:f2716e543d97 1080 LMIC.bcninfo.rssi = LMIC.rssi;
dudmuck 0:f2716e543d97 1081 LMIC.bcninfo.txtime = LMIC.rxtime - AIRTIME_BCN_osticks;
dudmuck 0:f2716e543d97 1082 LMIC.bcninfo.time = os_rlsbf4(&d[OFF_BCN_TIME]);
dudmuck 0:f2716e543d97 1083 LMIC.bcninfo.flags |= BCN_PARTIAL;
dudmuck 0:f2716e543d97 1084
dudmuck 0:f2716e543d97 1085 // Check 2nd set
dudmuck 0:f2716e543d97 1086 if( os_rlsbf2(&d[OFF_BCN_CRC2]) != os_crc16(d,OFF_BCN_CRC2) )
dudmuck 0:f2716e543d97 1087 return 1;
dudmuck 0:f2716e543d97 1088 // Second set of fields is ok
dudmuck 0:f2716e543d97 1089 LMIC.bcninfo.lat = (s4_t)os_rlsbf4(&d[OFF_BCN_LAT-1]) >> 8; // read as signed 24-bit
dudmuck 0:f2716e543d97 1090 LMIC.bcninfo.lon = (s4_t)os_rlsbf4(&d[OFF_BCN_LON-1]) >> 8; // ditto
dudmuck 0:f2716e543d97 1091 LMIC.bcninfo.info = d[OFF_BCN_INFO];
dudmuck 0:f2716e543d97 1092 LMIC.bcninfo.flags |= BCN_FULL;
dudmuck 0:f2716e543d97 1093 return 2;
dudmuck 0:f2716e543d97 1094 }
dudmuck 0:f2716e543d97 1095
dudmuck 0:f2716e543d97 1096
dudmuck 0:f2716e543d97 1097 static bit_t decodeFrame (void) {
dudmuck 0:f2716e543d97 1098 xref2u1_t d = LMIC.frame;
dudmuck 0:f2716e543d97 1099 u1_t hdr = d[0];
dudmuck 0:f2716e543d97 1100 u1_t ftype = hdr & HDR_FTYPE;
dudmuck 0:f2716e543d97 1101 int dlen = LMIC.dataLen;
dudmuck 0:f2716e543d97 1102 if( dlen < OFF_DAT_OPTS+4 ||
dudmuck 0:f2716e543d97 1103 (hdr & HDR_MAJOR) != HDR_MAJOR_V1 ||
dudmuck 0:f2716e543d97 1104 (ftype != HDR_FTYPE_DADN && ftype != HDR_FTYPE_DCDN) ) {
dudmuck 0:f2716e543d97 1105 // Basic sanity checks failed
dudmuck 0:f2716e543d97 1106 EV(specCond, WARN, (e_.reason = EV::specCond_t::UNEXPECTED_FRAME,
dudmuck 0:f2716e543d97 1107 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1108 e_.info = dlen < 4 ? 0 : os_rlsbf4(&d[dlen-4]),
dudmuck 0:f2716e543d97 1109 e_.info2 = hdr + (dlen<<8)));
dudmuck 0:f2716e543d97 1110 norx:
dudmuck 0:f2716e543d97 1111 LMIC.dataLen = 0;
dudmuck 0:f2716e543d97 1112 return 0;
dudmuck 0:f2716e543d97 1113 }
dudmuck 0:f2716e543d97 1114 // Validate exact frame length
dudmuck 0:f2716e543d97 1115 // Note: device address was already read+evaluated in order to arrive here.
dudmuck 0:f2716e543d97 1116 int fct = d[OFF_DAT_FCT];
dudmuck 0:f2716e543d97 1117 u4_t addr = os_rlsbf4(&d[OFF_DAT_ADDR]);
dudmuck 0:f2716e543d97 1118 u4_t seqno = os_rlsbf2(&d[OFF_DAT_SEQNO]);
dudmuck 0:f2716e543d97 1119 int olen = fct & FCT_OPTLEN;
dudmuck 0:f2716e543d97 1120 int ackup = (fct & FCT_ACK) != 0 ? 1 : 0; // ACK last up frame
dudmuck 0:f2716e543d97 1121 int poff = OFF_DAT_OPTS+olen;
dudmuck 0:f2716e543d97 1122 int pend = dlen-4; // MIC
dudmuck 0:f2716e543d97 1123
dudmuck 0:f2716e543d97 1124 if( addr != LMIC.devaddr ) {
dudmuck 0:f2716e543d97 1125 EV(specCond, WARN, (e_.reason = EV::specCond_t::ALIEN_ADDRESS,
dudmuck 0:f2716e543d97 1126 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1127 e_.info = addr,
dudmuck 0:f2716e543d97 1128 e_.info2 = LMIC.devaddr));
dudmuck 0:f2716e543d97 1129 goto norx;
dudmuck 0:f2716e543d97 1130 }
dudmuck 0:f2716e543d97 1131 if( poff > pend ) {
dudmuck 0:f2716e543d97 1132 EV(specCond, ERR, (e_.reason = EV::specCond_t::CORRUPTED_FRAME,
dudmuck 0:f2716e543d97 1133 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1134 e_.info = 0x1000000 + (poff-pend) + (fct<<8) + (dlen<<16)));
dudmuck 0:f2716e543d97 1135 goto norx;
dudmuck 0:f2716e543d97 1136 }
dudmuck 0:f2716e543d97 1137
dudmuck 0:f2716e543d97 1138 int port = -1;
dudmuck 0:f2716e543d97 1139 int replayConf = 0;
dudmuck 0:f2716e543d97 1140
dudmuck 0:f2716e543d97 1141 if( pend > poff )
dudmuck 0:f2716e543d97 1142 port = d[poff++];
dudmuck 0:f2716e543d97 1143
dudmuck 0:f2716e543d97 1144 seqno = LMIC.seqnoDn + (u2_t)(seqno - LMIC.seqnoDn);
dudmuck 0:f2716e543d97 1145
dudmuck 0:f2716e543d97 1146 if( !aes_verifyMic(LMIC.nwkKey, LMIC.devaddr, seqno, /*dn*/1, d, pend) ) {
dudmuck 0:f2716e543d97 1147 EV(spe3Cond, ERR, (e_.reason = EV::spe3Cond_t::CORRUPTED_MIC,
dudmuck 0:f2716e543d97 1148 e_.eui1 = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1149 e_.info1 = Base::lsbf4(&d[pend]),
dudmuck 0:f2716e543d97 1150 e_.info2 = seqno,
dudmuck 0:f2716e543d97 1151 e_.info3 = LMIC.devaddr));
dudmuck 0:f2716e543d97 1152 goto norx;
dudmuck 0:f2716e543d97 1153 }
dudmuck 0:f2716e543d97 1154 if( seqno < LMIC.seqnoDn ) {
dudmuck 0:f2716e543d97 1155 if( (s4_t)seqno > (s4_t)LMIC.seqnoDn ) {
dudmuck 0:f2716e543d97 1156 EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_ROLL_OVER,
dudmuck 0:f2716e543d97 1157 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1158 e_.info = LMIC.seqnoDn,
dudmuck 0:f2716e543d97 1159 e_.info2 = seqno));
dudmuck 0:f2716e543d97 1160 goto norx;
dudmuck 0:f2716e543d97 1161 }
dudmuck 0:f2716e543d97 1162 if( seqno != LMIC.seqnoDn-1 || !LMIC.dnConf || ftype != HDR_FTYPE_DCDN ) {
dudmuck 0:f2716e543d97 1163 EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_OBSOLETE,
dudmuck 0:f2716e543d97 1164 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1165 e_.info = LMIC.seqnoDn,
dudmuck 0:f2716e543d97 1166 e_.info2 = seqno));
dudmuck 0:f2716e543d97 1167 goto norx;
dudmuck 0:f2716e543d97 1168 }
dudmuck 0:f2716e543d97 1169 // Replay of previous sequence number allowed only if
dudmuck 0:f2716e543d97 1170 // previous frame and repeated both requested confirmation
dudmuck 0:f2716e543d97 1171 replayConf = 1;
dudmuck 0:f2716e543d97 1172 }
dudmuck 0:f2716e543d97 1173 else {
dudmuck 0:f2716e543d97 1174 if( seqno > LMIC.seqnoDn ) {
dudmuck 0:f2716e543d97 1175 EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_SKIP,
dudmuck 0:f2716e543d97 1176 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1177 e_.info = LMIC.seqnoDn,
dudmuck 0:f2716e543d97 1178 e_.info2 = seqno));
dudmuck 0:f2716e543d97 1179 }
dudmuck 0:f2716e543d97 1180 LMIC.seqnoDn = seqno+1; // next number to be expected
dudmuck 0:f2716e543d97 1181 DO_DEVDB(LMIC.seqnoDn,seqnoDn);
dudmuck 0:f2716e543d97 1182 // DN frame requested confirmation - provide ACK once with next UP frame
dudmuck 0:f2716e543d97 1183 LMIC.dnConf = (ftype == HDR_FTYPE_DCDN ? FCT_ACK : 0);
dudmuck 0:f2716e543d97 1184 }
dudmuck 0:f2716e543d97 1185
dudmuck 0:f2716e543d97 1186 if( LMIC.dnConf || (fct & FCT_MORE) )
dudmuck 0:f2716e543d97 1187 LMIC.opmode |= OP_POLL;
dudmuck 0:f2716e543d97 1188
dudmuck 0:f2716e543d97 1189 // We heard from network
dudmuck 0:f2716e543d97 1190 LMIC.adrChanged = LMIC.rejoinCnt = 0;
Shaun Nelson 16:d5c50bdab1f1 1191 if( LMIC.adrAckReq != LINK_CHECK_OFF )
dudmuck 0:f2716e543d97 1192 LMIC.adrAckReq = LINK_CHECK_INIT;
dudmuck 0:f2716e543d97 1193
dudmuck 0:f2716e543d97 1194 // Process OPTS
dudmuck 0:f2716e543d97 1195 int m = LMIC.rssi - RSSI_OFF - getSensitivity(LMIC.rps);
dudmuck 0:f2716e543d97 1196 LMIC.margin = m < 0 ? 0 : m > 254 ? 254 : m;
dudmuck 0:f2716e543d97 1197
dudmuck 0:f2716e543d97 1198 xref2u1_t opts = &d[OFF_DAT_OPTS];
dudmuck 0:f2716e543d97 1199 int oidx = 0;
dudmuck 0:f2716e543d97 1200 while( oidx < olen ) {
dudmuck 0:f2716e543d97 1201 switch( opts[oidx] ) {
dudmuck 0:f2716e543d97 1202 case MCMD_LCHK_ANS: {
dudmuck 0:f2716e543d97 1203 //int gwmargin = opts[oidx+1];
dudmuck 0:f2716e543d97 1204 //int ngws = opts[oidx+2];
dudmuck 0:f2716e543d97 1205 oidx += 3;
dudmuck 0:f2716e543d97 1206 continue;
dudmuck 0:f2716e543d97 1207 }
dudmuck 0:f2716e543d97 1208 case MCMD_LADR_REQ: {
dudmuck 0:f2716e543d97 1209 u1_t p1 = opts[oidx+1]; // txpow + DR
dudmuck 0:f2716e543d97 1210 u2_t chmap = os_rlsbf2(&opts[oidx+2]);// list of enabled channels
dudmuck 0:f2716e543d97 1211 u1_t chpage = opts[oidx+4] & MCMD_LADR_CHPAGE_MASK; // channel page
dudmuck 0:f2716e543d97 1212 u1_t uprpt = opts[oidx+4] & MCMD_LADR_REPEAT_MASK; // up repeat count
dudmuck 0:f2716e543d97 1213 oidx += 5;
dudmuck 0:f2716e543d97 1214
dudmuck 0:f2716e543d97 1215 LMIC.ladrAns = 0x80 | // Include an answer into next frame up
dudmuck 0:f2716e543d97 1216 MCMD_LADR_ANS_POWACK | MCMD_LADR_ANS_CHACK | MCMD_LADR_ANS_DRACK;
dudmuck 0:f2716e543d97 1217 if( !mapChannels(chpage, chmap) )
dudmuck 0:f2716e543d97 1218 LMIC.ladrAns &= ~MCMD_LADR_ANS_CHACK;
dudmuck 0:f2716e543d97 1219 dr_t dr = (dr_t)(p1>>MCMD_LADR_DR_SHIFT);
dudmuck 0:f2716e543d97 1220 if( !validDR(dr) ) {
dudmuck 0:f2716e543d97 1221 LMIC.ladrAns &= ~MCMD_LADR_ANS_DRACK;
dudmuck 0:f2716e543d97 1222 EV(specCond, ERR, (e_.reason = EV::specCond_t::BAD_MAC_CMD,
dudmuck 0:f2716e543d97 1223 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1224 e_.info = Base::lsbf4(&d[pend]),
dudmuck 0:f2716e543d97 1225 e_.info2 = Base::msbf4(&opts[oidx-4])));
dudmuck 0:f2716e543d97 1226 }
dudmuck 0:f2716e543d97 1227 if( (LMIC.ladrAns & 0x7F) == (MCMD_LADR_ANS_POWACK | MCMD_LADR_ANS_CHACK | MCMD_LADR_ANS_DRACK) ) {
dudmuck 0:f2716e543d97 1228 // Nothing went wrong - use settings
dudmuck 0:f2716e543d97 1229 LMIC.upRepeat = uprpt;
dudmuck 0:f2716e543d97 1230 setDrTxpow(DRCHG_NWKCMD, dr, pow2dBm(p1));
dudmuck 0:f2716e543d97 1231 }
dkjendal 13:097c74da6ffe 1232 debug("MCMD_LADR_REQ:%02X%02X%02X%02X - dr:%u pw:%u - %04X%04X%04X%04X%04X\r\n",
dkjendal 13:097c74da6ffe 1233 opts[oidx+1-5],opts[oidx+2-5],opts[oidx+3-5],opts[oidx+4-5],
dkjendal 13:097c74da6ffe 1234 dr,pow2dBm(p1),LMIC.channelMap[0],LMIC.channelMap[1],LMIC.channelMap[2],LMIC.channelMap[3],LMIC.channelMap[4]);
dudmuck 0:f2716e543d97 1235 LMIC.adrChanged = 1; // Trigger an ACK to NWK
dudmuck 0:f2716e543d97 1236 continue;
dudmuck 0:f2716e543d97 1237 }
dudmuck 0:f2716e543d97 1238 case MCMD_DEVS_REQ: {
dudmuck 0:f2716e543d97 1239 LMIC.devsAns = 1;
dudmuck 0:f2716e543d97 1240 oidx += 1;
dudmuck 0:f2716e543d97 1241 continue;
dudmuck 0:f2716e543d97 1242 }
dudmuck 0:f2716e543d97 1243 case MCMD_DN2P_SET: {
dudmuck 0:f2716e543d97 1244 dr_t dr = (dr_t)(opts[oidx+1] & 0x0F);
dudmuck 0:f2716e543d97 1245 u4_t freq = convFreq(&opts[oidx+2]);
dudmuck 0:f2716e543d97 1246 oidx += 5;
dudmuck 0:f2716e543d97 1247 LMIC.dn2Ans = 0x80; // answer pending
dudmuck 0:f2716e543d97 1248 if( validDR(dr) )
dudmuck 0:f2716e543d97 1249 LMIC.dn2Ans |= MCMD_DN2P_ANS_DRACK;
dudmuck 0:f2716e543d97 1250 if( freq != 0 )
dudmuck 0:f2716e543d97 1251 LMIC.dn2Ans |= MCMD_DN2P_ANS_CHACK;
dudmuck 0:f2716e543d97 1252 if( LMIC.dn2Ans == (0x80|MCMD_DN2P_ANS_DRACK|MCMD_DN2P_ANS_CHACK) ) {
dudmuck 0:f2716e543d97 1253 LMIC.dn2Dr = dr;
dudmuck 0:f2716e543d97 1254 LMIC.dn2Freq = freq;
dudmuck 0:f2716e543d97 1255 DO_DEVDB(LMIC.dn2Dr,dn2Dr);
dudmuck 0:f2716e543d97 1256 DO_DEVDB(LMIC.dn2Freq,dn2Freq);
dudmuck 0:f2716e543d97 1257 }
dudmuck 0:f2716e543d97 1258 continue;
dudmuck 0:f2716e543d97 1259 }
dudmuck 0:f2716e543d97 1260 case MCMD_DCAP_REQ: {
dudmuck 0:f2716e543d97 1261 u1_t cap = opts[oidx+1];
dudmuck 0:f2716e543d97 1262 oidx += 2;
dudmuck 0:f2716e543d97 1263 // A value cap=0xFF means device is OFF unless enabled again manually.
dudmuck 0:f2716e543d97 1264 if( cap==0xFF )
dudmuck 0:f2716e543d97 1265 LMIC.opmode |= OP_SHUTDOWN; // stop any sending
dudmuck 0:f2716e543d97 1266 LMIC.globalDutyRate = cap & 0xF;
dudmuck 0:f2716e543d97 1267 LMIC.globalDutyAvail = os_getTime();
dudmuck 0:f2716e543d97 1268 DO_DEVDB(cap,dutyCap);
dudmuck 0:f2716e543d97 1269 LMIC.dutyCapAns = 1;
dudmuck 0:f2716e543d97 1270 continue;
dudmuck 0:f2716e543d97 1271 }
dudmuck 0:f2716e543d97 1272 case MCMD_SNCH_REQ: {
dudmuck 0:f2716e543d97 1273 u1_t chidx = opts[oidx+1]; // channel
dudmuck 0:f2716e543d97 1274 u4_t freq = convFreq(&opts[oidx+2]); // freq
dudmuck 0:f2716e543d97 1275 u1_t drs = opts[oidx+5]; // datarate span
dudmuck 0:f2716e543d97 1276 LMIC.snchAns = 0x80;
dudmuck 0:f2716e543d97 1277 if( freq != 0 && LMIC_setupChannel(chidx, freq, DR_RANGE_MAP(drs&0xF,drs>>4), -1) )
dudmuck 0:f2716e543d97 1278 LMIC.snchAns |= MCMD_SNCH_ANS_DRACK|MCMD_SNCH_ANS_FQACK;
dudmuck 0:f2716e543d97 1279 oidx += 6;
dudmuck 0:f2716e543d97 1280 continue;
dudmuck 0:f2716e543d97 1281 }
dudmuck 0:f2716e543d97 1282 case MCMD_PING_SET: {
dudmuck 0:f2716e543d97 1283 u4_t freq = convFreq(&opts[oidx+1]);
dudmuck 0:f2716e543d97 1284 oidx += 4;
dudmuck 0:f2716e543d97 1285 u1_t flags = 0x80;
dudmuck 0:f2716e543d97 1286 if( freq != 0 ) {
dudmuck 0:f2716e543d97 1287 flags |= MCMD_PING_ANS_FQACK;
dudmuck 0:f2716e543d97 1288 LMIC.ping.freq = freq;
dudmuck 0:f2716e543d97 1289 DO_DEVDB(LMIC.ping.intvExp, pingIntvExp);
dudmuck 0:f2716e543d97 1290 DO_DEVDB(LMIC.ping.freq, pingFreq);
dudmuck 0:f2716e543d97 1291 DO_DEVDB(LMIC.ping.dr, pingDr);
dudmuck 0:f2716e543d97 1292 }
dudmuck 0:f2716e543d97 1293 LMIC.pingSetAns = flags;
dudmuck 0:f2716e543d97 1294 continue;
dudmuck 0:f2716e543d97 1295 }
dudmuck 0:f2716e543d97 1296 case MCMD_BCNI_ANS: {
dudmuck 0:f2716e543d97 1297 // Ignore if tracking already enabled
dudmuck 0:f2716e543d97 1298 if( (LMIC.opmode & OP_TRACK) == 0 ) {
dudmuck 0:f2716e543d97 1299 LMIC.bcnChnl = opts[oidx+3];
dudmuck 0:f2716e543d97 1300 // Enable tracking - bcninfoTries
dudmuck 0:f2716e543d97 1301 LMIC.opmode |= OP_TRACK;
dudmuck 0:f2716e543d97 1302 // Cleared later in txComplete handling - triggers EV_BEACON_FOUND
dudmuck 0:f2716e543d97 1303 ASSERT(LMIC.bcninfoTries!=0);
dudmuck 0:f2716e543d97 1304 // Setup RX parameters
dudmuck 0:f2716e543d97 1305 LMIC.bcninfo.txtime = (LMIC.rxtime
dudmuck 0:f2716e543d97 1306 + ms2osticks(os_rlsbf2(&opts[oidx+1]) * MCMD_BCNI_TUNIT)
dudmuck 0:f2716e543d97 1307 + ms2osticksCeil(MCMD_BCNI_TUNIT/2)
dudmuck 0:f2716e543d97 1308 - BCN_INTV_osticks);
dudmuck 0:f2716e543d97 1309 LMIC.bcninfo.flags = 0; // txtime above cannot be used as reference (BCN_PARTIAL|BCN_FULL cleared)
dudmuck 0:f2716e543d97 1310 calcBcnRxWindowFromMillis(MCMD_BCNI_TUNIT,1); // error of +/-N ms
dudmuck 0:f2716e543d97 1311
dudmuck 0:f2716e543d97 1312 EV(lostFrame, INFO, (e_.reason = EV::lostFrame_t::MCMD_BCNI_ANS,
dudmuck 0:f2716e543d97 1313 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1314 e_.lostmic = Base::lsbf4(&d[pend]),
dudmuck 0:f2716e543d97 1315 e_.info = (LMIC.missedBcns |
dudmuck 0:f2716e543d97 1316 (osticks2us(LMIC.bcninfo.txtime + BCN_INTV_osticks
dudmuck 0:f2716e543d97 1317 - LMIC.bcnRxtime) << 8)),
dudmuck 0:f2716e543d97 1318 e_.time = MAIN::CDEV->ostime2ustime(LMIC.bcninfo.txtime + BCN_INTV_osticks)));
dudmuck 0:f2716e543d97 1319 }
dudmuck 0:f2716e543d97 1320 oidx += 4;
dudmuck 0:f2716e543d97 1321 continue;
dudmuck 0:f2716e543d97 1322 }
dudmuck 0:f2716e543d97 1323 }
dudmuck 0:f2716e543d97 1324 EV(specCond, ERR, (e_.reason = EV::specCond_t::BAD_MAC_CMD,
dudmuck 0:f2716e543d97 1325 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1326 e_.info = Base::lsbf4(&d[pend]),
dudmuck 0:f2716e543d97 1327 e_.info2 = Base::msbf4(&opts[oidx])));
dudmuck 0:f2716e543d97 1328 break;
dudmuck 0:f2716e543d97 1329 }
dudmuck 0:f2716e543d97 1330 if( oidx != olen ) {
dudmuck 0:f2716e543d97 1331 EV(specCond, ERR, (e_.reason = EV::specCond_t::CORRUPTED_FRAME,
dudmuck 0:f2716e543d97 1332 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1333 e_.info = 0x1000000 + (oidx) + (olen<<8)));
dudmuck 0:f2716e543d97 1334 }
dudmuck 0:f2716e543d97 1335
dudmuck 0:f2716e543d97 1336 if( !replayConf ) {
dudmuck 0:f2716e543d97 1337 // Handle payload only if not a replay
dudmuck 0:f2716e543d97 1338 // Decrypt payload - if any
dudmuck 0:f2716e543d97 1339 if( port >= 0 && pend-poff > 0 )
dudmuck 0:f2716e543d97 1340 aes_cipher(port <= 0 ? LMIC.nwkKey : LMIC.artKey, LMIC.devaddr, seqno, /*dn*/1, d+poff, pend-poff);
dudmuck 0:f2716e543d97 1341
dudmuck 0:f2716e543d97 1342 EV(dfinfo, DEBUG, (e_.deveui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1343 e_.devaddr = LMIC.devaddr,
dudmuck 0:f2716e543d97 1344 e_.seqno = seqno,
dudmuck 0:f2716e543d97 1345 e_.flags = (port < 0 ? EV::dfinfo_t::NOPORT : 0) | EV::dfinfo_t::DN,
dudmuck 0:f2716e543d97 1346 e_.mic = Base::lsbf4(&d[pend]),
dudmuck 0:f2716e543d97 1347 e_.hdr = d[LORA::OFF_DAT_HDR],
dudmuck 0:f2716e543d97 1348 e_.fct = d[LORA::OFF_DAT_FCT],
dudmuck 0:f2716e543d97 1349 e_.port = port,
dudmuck 0:f2716e543d97 1350 e_.plen = dlen,
dudmuck 0:f2716e543d97 1351 e_.opts.length = olen,
dudmuck 0:f2716e543d97 1352 memcpy(&e_.opts[0], opts, olen)));
dudmuck 0:f2716e543d97 1353 } else {
dudmuck 0:f2716e543d97 1354 EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_REPLAY,
dudmuck 0:f2716e543d97 1355 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1356 e_.info = Base::lsbf4(&d[pend]),
dudmuck 0:f2716e543d97 1357 e_.info2 = seqno));
dudmuck 0:f2716e543d97 1358 }
dudmuck 0:f2716e543d97 1359
dudmuck 0:f2716e543d97 1360 if( // NWK acks but we don't have a frame pending
dudmuck 0:f2716e543d97 1361 (ackup && LMIC.txCnt == 0) ||
dudmuck 0:f2716e543d97 1362 // We sent up confirmed and we got a response in DNW1/DNW2
dudmuck 0:f2716e543d97 1363 // BUT it did not carry an ACK - this should never happen
dudmuck 0:f2716e543d97 1364 // Do not resend and assume frame was not ACKed.
dudmuck 0:f2716e543d97 1365 (!ackup && LMIC.txCnt != 0) ) {
dudmuck 0:f2716e543d97 1366 EV(specCond, ERR, (e_.reason = EV::specCond_t::SPURIOUS_ACK,
dudmuck 0:f2716e543d97 1367 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1368 e_.info = seqno,
dudmuck 0:f2716e543d97 1369 e_.info2 = ackup));
dudmuck 0:f2716e543d97 1370 }
dudmuck 0:f2716e543d97 1371
dudmuck 0:f2716e543d97 1372 if( LMIC.txCnt != 0 ) // we requested an ACK
dudmuck 0:f2716e543d97 1373 LMIC.txrxFlags |= ackup ? TXRX_ACK : TXRX_NACK;
dudmuck 0:f2716e543d97 1374
dudmuck 0:f2716e543d97 1375 if( port < 0 ) {
dudmuck 0:f2716e543d97 1376 LMIC.txrxFlags |= TXRX_NOPORT;
dudmuck 0:f2716e543d97 1377 LMIC.dataBeg = poff;
dudmuck 0:f2716e543d97 1378 LMIC.dataLen = 0;
dudmuck 0:f2716e543d97 1379 } else {
dudmuck 0:f2716e543d97 1380 LMIC.txrxFlags |= TXRX_PORT;
dudmuck 0:f2716e543d97 1381 LMIC.dataBeg = poff;
dudmuck 0:f2716e543d97 1382 LMIC.dataLen = pend-poff;
dudmuck 0:f2716e543d97 1383 }
dudmuck 0:f2716e543d97 1384 return 1;
dudmuck 0:f2716e543d97 1385 }
dudmuck 0:f2716e543d97 1386
dudmuck 0:f2716e543d97 1387
dudmuck 0:f2716e543d97 1388 // ================================================================================
dudmuck 0:f2716e543d97 1389 // TX/RX transaction support
dudmuck 0:f2716e543d97 1390
dudmuck 0:f2716e543d97 1391
dudmuck 0:f2716e543d97 1392 static void setupRx2 (void) {
dudmuck 0:f2716e543d97 1393 LMIC.txrxFlags = TXRX_DNW2;
dudmuck 0:f2716e543d97 1394 LMIC.rps = dndr2rps(LMIC.dn2Dr);
dudmuck 0:f2716e543d97 1395 LMIC.freq = LMIC.dn2Freq;
dudmuck 0:f2716e543d97 1396 LMIC.dataLen = 0;
dudmuck 0:f2716e543d97 1397 os_radio(RADIO_RX);
dudmuck 0:f2716e543d97 1398 }
dudmuck 0:f2716e543d97 1399
dudmuck 0:f2716e543d97 1400
dudmuck 0:f2716e543d97 1401 static void schedRx2 (ostime_t delay, osjobcb_t func) {
dudmuck 0:f2716e543d97 1402 // Add 1.5 symbols we need 5 out of 8. Try to sync 1.5 symbols into the preamble.
Shaun Nelson 16:d5c50bdab1f1 1403 LMIC.rxtime = LMIC.txend + delay + (PAMBL_SYMS-MINRX_SYMS)*dr2hsym(LMIC.dn2Dr);
dudmuck 0:f2716e543d97 1404 os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, func);
dudmuck 0:f2716e543d97 1405 }
dudmuck 0:f2716e543d97 1406
dudmuck 0:f2716e543d97 1407 static void setupRx1 (osjobcb_t func) {
dudmuck 0:f2716e543d97 1408 LMIC.txrxFlags = TXRX_DNW1;
dudmuck 0:f2716e543d97 1409 // Turn LMIC.rps from TX over to RX
dudmuck 0:f2716e543d97 1410 LMIC.rps = setNocrc(LMIC.rps,1);
dudmuck 0:f2716e543d97 1411 LMIC.dataLen = 0;
dudmuck 0:f2716e543d97 1412 LMIC.osjob.func = func;
dudmuck 0:f2716e543d97 1413 os_radio(RADIO_RX);
dudmuck 0:f2716e543d97 1414 }
dudmuck 0:f2716e543d97 1415
dudmuck 0:f2716e543d97 1416
dudmuck 0:f2716e543d97 1417 // Called by HAL once TX complete and delivers exact end of TX time stamp in LMIC.rxtime
dudmuck 0:f2716e543d97 1418 static void txDone (ostime_t delay, osjobcb_t func) {
dudmuck 0:f2716e543d97 1419 if( (LMIC.opmode & (OP_TRACK|OP_PINGABLE|OP_PINGINI)) == (OP_TRACK|OP_PINGABLE) ) {
dudmuck 0:f2716e543d97 1420 rxschedInit(&LMIC.ping); // note: reuses LMIC.frame buffer!
dudmuck 0:f2716e543d97 1421 LMIC.opmode |= OP_PINGINI;
dudmuck 0:f2716e543d97 1422 }
dudmuck 0:f2716e543d97 1423 // Change RX frequency / rps (US only) before we increment txChnl
dudmuck 0:f2716e543d97 1424 setRx1Params();
dudmuck 0:f2716e543d97 1425 // LMIC.rxsyms carries the TX datarate (can be != LMIC.datarate [confirm retries etc.])
dudmuck 0:f2716e543d97 1426 // Setup receive - LMIC.rxtime is preloaded with 1.5 symbols offset to tune
dudmuck 0:f2716e543d97 1427 // into the middle of the 8 symbols preamble.
dudmuck 0:f2716e543d97 1428 #if defined(CFG_eu868)
dudmuck 0:f2716e543d97 1429 if( /* TX datarate */LMIC.rxsyms == DR_FSK ) {
dudmuck 0:f2716e543d97 1430 LMIC.rxtime = LMIC.txend + delay - PRERX_FSK*us2osticksRound(160);
dudmuck 0:f2716e543d97 1431 LMIC.rxsyms = RXLEN_FSK;
dudmuck 0:f2716e543d97 1432 }
dudmuck 0:f2716e543d97 1433 else
dudmuck 0:f2716e543d97 1434 #endif
dudmuck 0:f2716e543d97 1435 {
Shaun Nelson 16:d5c50bdab1f1 1436 LMIC.rxtime = LMIC.txend + delay + (PAMBL_SYMS-MINRX_SYMS)*dr2hsym(LMIC.dndr);
dudmuck 0:f2716e543d97 1437 LMIC.rxsyms = MINRX_SYMS;
dudmuck 0:f2716e543d97 1438 }
dudmuck 0:f2716e543d97 1439 os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, func);
dudmuck 0:f2716e543d97 1440 }
dudmuck 0:f2716e543d97 1441
dudmuck 0:f2716e543d97 1442
dudmuck 0:f2716e543d97 1443 // ======================================== Join frames
dudmuck 0:f2716e543d97 1444
dudmuck 0:f2716e543d97 1445
dudmuck 0:f2716e543d97 1446 static void onJoinFailed (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 1447 // Notify app - must call LMIC_reset() to stop joining
dudmuck 0:f2716e543d97 1448 // otherwise join procedure continues.
dudmuck 0:f2716e543d97 1449 reportEvent(EV_JOIN_FAILED);
dudmuck 0:f2716e543d97 1450 }
dudmuck 0:f2716e543d97 1451
dudmuck 0:f2716e543d97 1452
dudmuck 0:f2716e543d97 1453 static bit_t processJoinAccept (void) {
dudmuck 0:f2716e543d97 1454 ASSERT(LMIC.txrxFlags != TXRX_DNW1 || LMIC.dataLen != 0);
dudmuck 0:f2716e543d97 1455 ASSERT((LMIC.opmode & OP_TXRXPEND)!=0);
dudmuck 0:f2716e543d97 1456
dudmuck 0:f2716e543d97 1457 if( LMIC.dataLen == 0 ) {
dudmuck 0:f2716e543d97 1458 nojoinframe:
Shaun Nelson 16:d5c50bdab1f1 1459 if( (LMIC.opmode & OP_JOINING) == 0 ) {
dudmuck 0:f2716e543d97 1460 ASSERT((LMIC.opmode & OP_REJOIN) != 0);
dudmuck 0:f2716e543d97 1461 // REJOIN attempt for roaming
dudmuck 0:f2716e543d97 1462 LMIC.opmode &= ~(OP_REJOIN|OP_TXRXPEND);
dudmuck 0:f2716e543d97 1463 if( LMIC.rejoinCnt < 10 )
dudmuck 0:f2716e543d97 1464 LMIC.rejoinCnt++;
dudmuck 0:f2716e543d97 1465 reportEvent(EV_REJOIN_FAILED);
dudmuck 0:f2716e543d97 1466 return 1;
Shaun Nelson 16:d5c50bdab1f1 1467 }
dudmuck 0:f2716e543d97 1468 LMIC.opmode &= ~OP_TXRXPEND;
dudmuck 0:f2716e543d97 1469 ostime_t delay = nextJoinState();
dudmuck 0:f2716e543d97 1470 EV(devCond, DEBUG, (e_.reason = EV::devCond_t::NO_JACC,
dudmuck 0:f2716e543d97 1471 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1472 e_.info = LMIC.datarate|DR_PAGE,
dudmuck 0:f2716e543d97 1473 e_.info2 = osticks2ms(delay)));
dudmuck 0:f2716e543d97 1474 // Build next JOIN REQUEST with next engineUpdate call
dudmuck 0:f2716e543d97 1475 // Optionally, report join failed.
dudmuck 0:f2716e543d97 1476 // Both after a random/chosen amount of ticks.
dudmuck 0:f2716e543d97 1477 os_setTimedCallback(&LMIC.osjob, os_getTime()+delay,
dudmuck 0:f2716e543d97 1478 (delay&1) != 0
dudmuck 0:f2716e543d97 1479 ? FUNC_ADDR(onJoinFailed) // one JOIN iteration done and failed
dudmuck 0:f2716e543d97 1480 : FUNC_ADDR(runEngineUpdate)); // next step to be delayed
dudmuck 0:f2716e543d97 1481 return 1;
dudmuck 0:f2716e543d97 1482 }
dudmuck 0:f2716e543d97 1483 u1_t hdr = LMIC.frame[0];
dudmuck 0:f2716e543d97 1484 u1_t dlen = LMIC.dataLen;
dudmuck 0:f2716e543d97 1485 u4_t mic = os_rlsbf4(&LMIC.frame[dlen-4]); // safe before modified by encrypt!
dudmuck 0:f2716e543d97 1486 if( (dlen != LEN_JA && dlen != LEN_JAEXT)
dudmuck 0:f2716e543d97 1487 || (hdr & (HDR_FTYPE|HDR_MAJOR)) != (HDR_FTYPE_JACC|HDR_MAJOR_V1) ) {
dudmuck 0:f2716e543d97 1488 EV(specCond, ERR, (e_.reason = EV::specCond_t::UNEXPECTED_FRAME,
dudmuck 0:f2716e543d97 1489 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1490 e_.info = dlen < 4 ? 0 : mic,
dudmuck 0:f2716e543d97 1491 e_.info2 = hdr + (dlen<<8)));
dudmuck 0:f2716e543d97 1492 badframe:
Shaun Nelson 16:d5c50bdab1f1 1493 printf("pja badframe dlen:%d, hdr:%02x\r\n", dlen, hdr);
dudmuck 0:f2716e543d97 1494 if( (LMIC.txrxFlags & TXRX_DNW1) != 0 )
dudmuck 0:f2716e543d97 1495 return 0;
dudmuck 0:f2716e543d97 1496 goto nojoinframe;
dudmuck 0:f2716e543d97 1497 }
dudmuck 0:f2716e543d97 1498 aes_encrypt(LMIC.frame+1, dlen-1);
dudmuck 0:f2716e543d97 1499 if( !aes_verifyMic0(LMIC.frame, dlen-4) ) {
dudmuck 0:f2716e543d97 1500 EV(specCond, ERR, (e_.reason = EV::specCond_t::JOIN_BAD_MIC,
dudmuck 0:f2716e543d97 1501 e_.info = mic));
dudmuck 0:f2716e543d97 1502 goto badframe;
dudmuck 0:f2716e543d97 1503 }
dudmuck 0:f2716e543d97 1504
dudmuck 0:f2716e543d97 1505 u4_t addr = os_rlsbf4(LMIC.frame+OFF_JA_DEVADDR);
dudmuck 0:f2716e543d97 1506 LMIC.devaddr = addr;
dudmuck 0:f2716e543d97 1507 LMIC.netid = os_rlsbf4(&LMIC.frame[OFF_JA_NETID]) & 0xFFFFFF;
dudmuck 0:f2716e543d97 1508
dudmuck 0:f2716e543d97 1509 #if defined(CFG_eu868)
dudmuck 0:f2716e543d97 1510 initDefaultChannels(0);
dudmuck 0:f2716e543d97 1511 if( dlen > LEN_JA ) {
dudmuck 0:f2716e543d97 1512 dlen = OFF_CFLIST;
dudmuck 0:f2716e543d97 1513 for( u1_t chidx=3; chidx<8; chidx++, dlen+=3 ) {
dudmuck 0:f2716e543d97 1514 u4_t freq = convFreq(&LMIC.frame[dlen]);
dudmuck 0:f2716e543d97 1515 if( freq )
dudmuck 0:f2716e543d97 1516 LMIC_setupChannel(chidx, freq, 0, -1);
dudmuck 0:f2716e543d97 1517 }
dudmuck 0:f2716e543d97 1518 }
dudmuck 8:0faa1bb768b5 1519 #elif defined(CFG_us915)
dudmuck 8:0faa1bb768b5 1520 #ifdef JA_DEBUG
dudmuck 8:0faa1bb768b5 1521 debug_buf(LMIC.frame, dlen);
dudmuck 8:0faa1bb768b5 1522 #endif /* JA_DEBUG */
dudmuck 8:0faa1bb768b5 1523 /* 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 */
dudmuck 8:0faa1bb768b5 1524 /* 20 27 2a b1 01 00 00 d0 44 76 03 00 00 0c c5 59 6c, xx xx, xx xx, xx xx, xx xx, xx xx, xx xx, xx xx xx, xx */
dudmuck 8:0faa1bb768b5 1525 /* 0 1 2 3 4 5 6 7 8 9 a b c d e f 10, {0} , {1} , {2} , {3} , {4} , {5} , res , type */
dudmuck 8:0faa1bb768b5 1526 /* join accept: [0] to [16], CFList: [17] to [32] */
dudmuck 8:0faa1bb768b5 1527 if (dlen == LEN_JAEXT) {
dudmuck 8:0faa1bb768b5 1528 #ifdef JA_DEBUG
dudmuck 8:0faa1bb768b5 1529 printf("cflistType:%d\r\n", LMIC.frame[32]);
dudmuck 8:0faa1bb768b5 1530 #endif /* JA_DEBUG */
dudmuck 8:0faa1bb768b5 1531 if (LMIC.frame[32] == 1) {
dudmuck 8:0faa1bb768b5 1532 u2_t *u2_ptr = (u2_t *)&LMIC.frame[17]; // LoRaWAN is little endian
dudmuck 8:0faa1bb768b5 1533 for (u1_t idx = 0; idx < 5; idx++)
dudmuck 8:0faa1bb768b5 1534 LMIC.channelMap[idx] = u2_ptr[idx];
dudmuck 8:0faa1bb768b5 1535 #ifdef JA_DEBUG
dudmuck 8:0faa1bb768b5 1536 for (u1_t idx = 0; idx < 5; idx++)
dudmuck 8:0faa1bb768b5 1537 printf("%d map %04x\r\n", idx, LMIC.channelMap[idx]);
dudmuck 8:0faa1bb768b5 1538 #endif /* JA_DEBUG */
dudmuck 8:0faa1bb768b5 1539 }
dudmuck 8:0faa1bb768b5 1540 }
dudmuck 8:0faa1bb768b5 1541 #endif /* CFG_us915 */
dudmuck 0:f2716e543d97 1542
dudmuck 0:f2716e543d97 1543 // already incremented when JOIN REQ got sent off
dudmuck 0:f2716e543d97 1544 aes_sessKeys(LMIC.devNonce-1, &LMIC.frame[OFF_JA_ARTNONCE], LMIC.nwkKey, LMIC.artKey);
dudmuck 0:f2716e543d97 1545 DO_DEVDB(LMIC.netid, netid);
dudmuck 0:f2716e543d97 1546 DO_DEVDB(LMIC.devaddr, devaddr);
dudmuck 0:f2716e543d97 1547 DO_DEVDB(LMIC.nwkKey, nwkkey);
dudmuck 0:f2716e543d97 1548 DO_DEVDB(LMIC.artKey, artkey);
dudmuck 0:f2716e543d97 1549
dudmuck 0:f2716e543d97 1550 EV(joininfo, INFO, (e_.arteui = MAIN::CDEV->getArtEui(),
dudmuck 0:f2716e543d97 1551 e_.deveui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1552 e_.devaddr = LMIC.devaddr,
dudmuck 0:f2716e543d97 1553 e_.oldaddr = oldaddr,
dudmuck 0:f2716e543d97 1554 e_.nonce = LMIC.devNonce-1,
dudmuck 0:f2716e543d97 1555 e_.mic = mic,
dudmuck 0:f2716e543d97 1556 e_.reason = ((LMIC.opmode & OP_REJOIN) != 0
dudmuck 0:f2716e543d97 1557 ? EV::joininfo_t::REJOIN_ACCEPT
dudmuck 0:f2716e543d97 1558 : EV::joininfo_t::ACCEPT)));
dudmuck 0:f2716e543d97 1559
dudmuck 0:f2716e543d97 1560 ASSERT((LMIC.opmode & (OP_JOINING|OP_REJOIN))!=0);
dudmuck 0:f2716e543d97 1561 if( (LMIC.opmode & OP_REJOIN) != 0 ) {
dudmuck 0:f2716e543d97 1562 // Lower DR every try below current UP DR
dudmuck 0:f2716e543d97 1563 LMIC.datarate = lowerDR(LMIC.datarate, LMIC.rejoinCnt);
dudmuck 0:f2716e543d97 1564 }
dudmuck 0:f2716e543d97 1565 LMIC.opmode &= ~(OP_JOINING|OP_TRACK|OP_REJOIN|OP_TXRXPEND|OP_PINGINI) | OP_NEXTCHNL;
dudmuck 0:f2716e543d97 1566 stateJustJoined();
dudmuck 0:f2716e543d97 1567 reportEvent(EV_JOINED);
dudmuck 0:f2716e543d97 1568 return 1;
dudmuck 0:f2716e543d97 1569 }
dudmuck 0:f2716e543d97 1570
dudmuck 0:f2716e543d97 1571
dudmuck 0:f2716e543d97 1572 static void processRx2Jacc (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 1573 if( LMIC.dataLen == 0 )
dudmuck 0:f2716e543d97 1574 LMIC.txrxFlags = 0; // nothing in 1st/2nd DN slot
dudmuck 0:f2716e543d97 1575 processJoinAccept();
dudmuck 0:f2716e543d97 1576 }
dudmuck 0:f2716e543d97 1577
dudmuck 0:f2716e543d97 1578
dudmuck 0:f2716e543d97 1579 static void setupRx2Jacc (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 1580 LMIC.osjob.func = FUNC_ADDR(processRx2Jacc);
dudmuck 0:f2716e543d97 1581 setupRx2();
dudmuck 0:f2716e543d97 1582 }
dudmuck 0:f2716e543d97 1583
dudmuck 0:f2716e543d97 1584
dudmuck 0:f2716e543d97 1585 static void processRx1Jacc (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 1586 if( LMIC.dataLen == 0 || !processJoinAccept() )
dudmuck 0:f2716e543d97 1587 schedRx2(DELAY_JACC2_osticks, FUNC_ADDR(setupRx2Jacc));
dudmuck 0:f2716e543d97 1588 }
dudmuck 0:f2716e543d97 1589
dudmuck 0:f2716e543d97 1590
dudmuck 0:f2716e543d97 1591 static void setupRx1Jacc (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 1592 setupRx1(FUNC_ADDR(processRx1Jacc));
dudmuck 0:f2716e543d97 1593 }
dudmuck 0:f2716e543d97 1594
dudmuck 0:f2716e543d97 1595
dudmuck 0:f2716e543d97 1596 static void jreqDone (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 1597 txDone(DELAY_JACC1_osticks, FUNC_ADDR(setupRx1Jacc));
dudmuck 0:f2716e543d97 1598 }
dudmuck 0:f2716e543d97 1599
dudmuck 0:f2716e543d97 1600 // ======================================== Data frames
dudmuck 0:f2716e543d97 1601
dudmuck 0:f2716e543d97 1602 // Fwd decl.
dudmuck 0:f2716e543d97 1603 static bit_t processDnData(void);
dudmuck 0:f2716e543d97 1604
dudmuck 0:f2716e543d97 1605 static void processRx2DnDataDelay (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 1606 processDnData();
dudmuck 0:f2716e543d97 1607 }
dudmuck 0:f2716e543d97 1608
dudmuck 0:f2716e543d97 1609 static void processRx2DnData (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 1610 if( LMIC.dataLen == 0 ) {
dudmuck 0:f2716e543d97 1611 LMIC.txrxFlags = 0; // nothing in 1st/2nd DN slot
dudmuck 0:f2716e543d97 1612 // Delay callback processing to avoid up TX while gateway is txing our missed frame!
dudmuck 0:f2716e543d97 1613 // Since DNW2 uses SF12 by default we wait 3 secs.
dudmuck 0:f2716e543d97 1614 os_setTimedCallback(&LMIC.osjob,
dudmuck 0:f2716e543d97 1615 (os_getTime() + DNW2_SAFETY_ZONE + rndDelay(2)),
dudmuck 0:f2716e543d97 1616 processRx2DnDataDelay);
dudmuck 0:f2716e543d97 1617 return;
dudmuck 0:f2716e543d97 1618 }
dudmuck 0:f2716e543d97 1619 processDnData();
dudmuck 0:f2716e543d97 1620 }
dudmuck 0:f2716e543d97 1621
dudmuck 0:f2716e543d97 1622
dudmuck 0:f2716e543d97 1623 static void setupRx2DnData (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 1624 LMIC.osjob.func = FUNC_ADDR(processRx2DnData);
dudmuck 0:f2716e543d97 1625 setupRx2();
dudmuck 0:f2716e543d97 1626 }
dudmuck 0:f2716e543d97 1627
dudmuck 0:f2716e543d97 1628
dudmuck 0:f2716e543d97 1629 static void processRx1DnData (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 1630 if( LMIC.dataLen == 0 || !processDnData() )
dudmuck 0:f2716e543d97 1631 schedRx2(DELAY_DNW2_osticks, FUNC_ADDR(setupRx2DnData));
dudmuck 0:f2716e543d97 1632 }
dudmuck 0:f2716e543d97 1633
dudmuck 0:f2716e543d97 1634
dudmuck 0:f2716e543d97 1635 static void setupRx1DnData (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 1636 setupRx1(FUNC_ADDR(processRx1DnData));
dudmuck 0:f2716e543d97 1637 }
dudmuck 0:f2716e543d97 1638
dudmuck 0:f2716e543d97 1639
dudmuck 0:f2716e543d97 1640 static void updataDone (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 1641 txDone(DELAY_DNW1_osticks, FUNC_ADDR(setupRx1DnData));
dudmuck 0:f2716e543d97 1642 }
dudmuck 0:f2716e543d97 1643
dudmuck 0:f2716e543d97 1644 // ========================================
dudmuck 0:f2716e543d97 1645
dudmuck 0:f2716e543d97 1646
dudmuck 0:f2716e543d97 1647 static void buildDataFrame (void) {
dudmuck 0:f2716e543d97 1648 bit_t txdata = ((LMIC.opmode & (OP_TXDATA|OP_POLL)) != OP_POLL);
dudmuck 0:f2716e543d97 1649 u1_t dlen = txdata ? LMIC.pendTxLen : 0;
dudmuck 0:f2716e543d97 1650
dudmuck 0:f2716e543d97 1651 // Piggyback MAC options
dudmuck 0:f2716e543d97 1652 // Prioritize by importance
dudmuck 0:f2716e543d97 1653 int end = OFF_DAT_OPTS;
dudmuck 0:f2716e543d97 1654 if( (LMIC.opmode & (OP_TRACK|OP_PINGABLE)) == (OP_TRACK|OP_PINGABLE) ) {
dudmuck 0:f2716e543d97 1655 // Indicate pingability in every UP frame
dudmuck 0:f2716e543d97 1656 LMIC.frame[end] = MCMD_PING_IND;
dudmuck 0:f2716e543d97 1657 LMIC.frame[end+1] = LMIC.ping.dr | (LMIC.ping.intvExp<<4);
dudmuck 0:f2716e543d97 1658 end += 2;
dudmuck 0:f2716e543d97 1659 }
dudmuck 0:f2716e543d97 1660 if( LMIC.dutyCapAns ) {
dudmuck 0:f2716e543d97 1661 LMIC.frame[end] = MCMD_DCAP_ANS;
dudmuck 0:f2716e543d97 1662 end += 1;
dudmuck 0:f2716e543d97 1663 LMIC.dutyCapAns = 0;
dudmuck 0:f2716e543d97 1664 }
dudmuck 0:f2716e543d97 1665 if( LMIC.dn2Ans ) {
dudmuck 0:f2716e543d97 1666 LMIC.frame[end+0] = MCMD_DN2P_ANS;
dudmuck 0:f2716e543d97 1667 LMIC.frame[end+1] = LMIC.dn2Ans & ~MCMD_DN2P_ANS_RFU;
dudmuck 0:f2716e543d97 1668 end += 2;
dudmuck 0:f2716e543d97 1669 LMIC.dn2Ans = 0;
dudmuck 0:f2716e543d97 1670 }
dudmuck 0:f2716e543d97 1671 if( LMIC.devsAns ) { // answer to device status
dudmuck 0:f2716e543d97 1672 LMIC.frame[end+0] = MCMD_DEVS_ANS;
dudmuck 0:f2716e543d97 1673 LMIC.frame[end+1] = LMIC.margin;
dudmuck 0:f2716e543d97 1674 LMIC.frame[end+2] = os_getBattLevel();
dudmuck 0:f2716e543d97 1675 end += 3;
dudmuck 0:f2716e543d97 1676 LMIC.devsAns = 0;
dudmuck 0:f2716e543d97 1677 }
dudmuck 0:f2716e543d97 1678 if( LMIC.ladrAns ) { // answer to ADR change
dudmuck 0:f2716e543d97 1679 LMIC.frame[end+0] = MCMD_LADR_ANS;
dudmuck 0:f2716e543d97 1680 LMIC.frame[end+1] = LMIC.ladrAns & ~MCMD_LADR_ANS_RFU;
dudmuck 0:f2716e543d97 1681 end += 2;
dudmuck 0:f2716e543d97 1682 LMIC.ladrAns = 0;
dudmuck 0:f2716e543d97 1683 }
dudmuck 0:f2716e543d97 1684 if( LMIC.bcninfoTries > 0 ) {
dudmuck 0:f2716e543d97 1685 LMIC.frame[end] = MCMD_BCNI_REQ;
dudmuck 0:f2716e543d97 1686 end += 1;
dudmuck 0:f2716e543d97 1687 }
dudmuck 0:f2716e543d97 1688 if( LMIC.adrChanged ) {
dudmuck 0:f2716e543d97 1689 if( LMIC.adrAckReq < 0 )
dudmuck 0:f2716e543d97 1690 LMIC.adrAckReq = 0;
dudmuck 0:f2716e543d97 1691 LMIC.adrChanged = 0;
dudmuck 0:f2716e543d97 1692 }
dudmuck 0:f2716e543d97 1693 if( LMIC.pingSetAns != 0 ) {
dudmuck 0:f2716e543d97 1694 LMIC.frame[end+0] = MCMD_PING_ANS;
dudmuck 0:f2716e543d97 1695 LMIC.frame[end+1] = LMIC.pingSetAns & ~MCMD_PING_ANS_RFU;
dudmuck 0:f2716e543d97 1696 end += 2;
dudmuck 0:f2716e543d97 1697 LMIC.pingSetAns = 0;
dudmuck 0:f2716e543d97 1698 }
dudmuck 0:f2716e543d97 1699 if( LMIC.snchAns ) {
dudmuck 0:f2716e543d97 1700 LMIC.frame[end+0] = MCMD_SNCH_ANS;
dudmuck 0:f2716e543d97 1701 LMIC.frame[end+1] = LMIC.snchAns & ~MCMD_SNCH_ANS_RFU;
dudmuck 0:f2716e543d97 1702 end += 2;
dudmuck 0:f2716e543d97 1703 LMIC.snchAns = 0;
dudmuck 0:f2716e543d97 1704 }
dudmuck 0:f2716e543d97 1705 ASSERT(end <= OFF_DAT_OPTS+16);
dudmuck 0:f2716e543d97 1706
dudmuck 0:f2716e543d97 1707 u1_t flen = end + (txdata ? 5+dlen : 4);
dudmuck 0:f2716e543d97 1708 if( flen > MAX_LEN_FRAME ) {
dudmuck 0:f2716e543d97 1709 // Options and payload too big - delay payload
dudmuck 0:f2716e543d97 1710 txdata = 0;
dudmuck 0:f2716e543d97 1711 flen = end+4;
dudmuck 0:f2716e543d97 1712 }
dudmuck 0:f2716e543d97 1713 LMIC.frame[OFF_DAT_HDR] = HDR_FTYPE_DAUP | HDR_MAJOR_V1;
dudmuck 0:f2716e543d97 1714 LMIC.frame[OFF_DAT_FCT] = (LMIC.dnConf | LMIC.adrEnabled
dudmuck 0:f2716e543d97 1715 | (LMIC.adrAckReq >= 0 ? FCT_ADRARQ : 0)
dudmuck 0:f2716e543d97 1716 | (end-OFF_DAT_OPTS));
dudmuck 0:f2716e543d97 1717 os_wlsbf4(LMIC.frame+OFF_DAT_ADDR, LMIC.devaddr);
dudmuck 0:f2716e543d97 1718
dudmuck 0:f2716e543d97 1719 if( LMIC.txCnt == 0 ) {
dudmuck 0:f2716e543d97 1720 LMIC.seqnoUp += 1;
dudmuck 0:f2716e543d97 1721 DO_DEVDB(LMIC.seqnoUp,seqnoUp);
dudmuck 0:f2716e543d97 1722 } else {
dudmuck 0:f2716e543d97 1723 EV(devCond, INFO, (e_.reason = EV::devCond_t::RE_TX,
dudmuck 0:f2716e543d97 1724 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1725 e_.info = LMIC.seqnoUp-1,
dudmuck 0:f2716e543d97 1726 e_.info2 = ((LMIC.txCnt+1) |
dudmuck 0:f2716e543d97 1727 (DRADJUST[LMIC.txCnt+1] << 8) |
dudmuck 0:f2716e543d97 1728 ((LMIC.datarate|DR_PAGE)<<16))));
dudmuck 0:f2716e543d97 1729 }
dudmuck 0:f2716e543d97 1730 os_wlsbf2(LMIC.frame+OFF_DAT_SEQNO, LMIC.seqnoUp-1);
dudmuck 0:f2716e543d97 1731
dudmuck 0:f2716e543d97 1732 // Clear pending DN confirmation
dudmuck 0:f2716e543d97 1733 LMIC.dnConf = 0;
dudmuck 0:f2716e543d97 1734
dudmuck 0:f2716e543d97 1735 if( txdata ) {
dudmuck 0:f2716e543d97 1736 if( LMIC.pendTxConf ) {
dudmuck 0:f2716e543d97 1737 // Confirmed only makes sense if we have a payload (or at least a port)
dudmuck 0:f2716e543d97 1738 LMIC.frame[OFF_DAT_HDR] = HDR_FTYPE_DCUP | HDR_MAJOR_V1;
dudmuck 0:f2716e543d97 1739 if( LMIC.txCnt == 0 ) LMIC.txCnt = 1;
dudmuck 0:f2716e543d97 1740 }
dudmuck 0:f2716e543d97 1741 LMIC.frame[end] = LMIC.pendTxPort;
dudmuck 0:f2716e543d97 1742 os_copyMem(LMIC.frame+end+1, LMIC.pendTxData, dlen);
dudmuck 0:f2716e543d97 1743 aes_cipher(LMIC.pendTxPort==0 ? LMIC.nwkKey : LMIC.artKey,
dudmuck 0:f2716e543d97 1744 LMIC.devaddr, LMIC.seqnoUp-1,
dudmuck 0:f2716e543d97 1745 /*up*/0, LMIC.frame+end+1, dlen);
dudmuck 0:f2716e543d97 1746 }
dudmuck 0:f2716e543d97 1747 aes_appendMic(LMIC.nwkKey, LMIC.devaddr, LMIC.seqnoUp-1, /*up*/0, LMIC.frame, flen-4);
dudmuck 0:f2716e543d97 1748
dudmuck 0:f2716e543d97 1749 EV(dfinfo, DEBUG, (e_.deveui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1750 e_.devaddr = LMIC.devaddr,
dudmuck 0:f2716e543d97 1751 e_.seqno = LMIC.seqnoUp-1,
dudmuck 0:f2716e543d97 1752 e_.flags = (LMIC.pendTxPort < 0 ? EV::dfinfo_t::NOPORT : EV::dfinfo_t::NOP),
dudmuck 0:f2716e543d97 1753 e_.mic = Base::lsbf4(&LMIC.frame[flen-4]),
dudmuck 0:f2716e543d97 1754 e_.hdr = LMIC.frame[LORA::OFF_DAT_HDR],
dudmuck 0:f2716e543d97 1755 e_.fct = LMIC.frame[LORA::OFF_DAT_FCT],
dudmuck 0:f2716e543d97 1756 e_.port = LMIC.pendTxPort,
dudmuck 0:f2716e543d97 1757 e_.plen = txdata ? dlen : 0,
dudmuck 0:f2716e543d97 1758 e_.opts.length = end-LORA::OFF_DAT_OPTS,
dudmuck 0:f2716e543d97 1759 memcpy(&e_.opts[0], LMIC.frame+LORA::OFF_DAT_OPTS, end-LORA::OFF_DAT_OPTS)));
dudmuck 0:f2716e543d97 1760 LMIC.dataLen = flen;
dudmuck 0:f2716e543d97 1761 }
dudmuck 0:f2716e543d97 1762
dudmuck 0:f2716e543d97 1763
dudmuck 0:f2716e543d97 1764 // Callback from HAL during scan mode or when job timer expires.
dudmuck 0:f2716e543d97 1765 static void onBcnRx (xref2osjob_t job) {
dudmuck 0:f2716e543d97 1766 // If we arrive via job timer make sure to put radio to rest.
dudmuck 0:f2716e543d97 1767 os_radio(RADIO_RST);
dudmuck 0:f2716e543d97 1768 os_clearCallback(&LMIC.osjob);
dudmuck 0:f2716e543d97 1769 if( LMIC.dataLen == 0 ) {
dudmuck 0:f2716e543d97 1770 // Nothing received - timeout
dudmuck 0:f2716e543d97 1771 LMIC.opmode &= ~(OP_SCAN | OP_TRACK);
dudmuck 0:f2716e543d97 1772 reportEvent(EV_SCAN_TIMEOUT);
dudmuck 0:f2716e543d97 1773 return;
dudmuck 0:f2716e543d97 1774 }
dudmuck 0:f2716e543d97 1775 if( decodeBeacon() <= 0 ) {
dudmuck 0:f2716e543d97 1776 // Something is wrong with the beacon - continue scan
dudmuck 0:f2716e543d97 1777 LMIC.dataLen = 0;
dudmuck 0:f2716e543d97 1778 os_radio(RADIO_RXON);
dudmuck 0:f2716e543d97 1779 os_setTimedCallback(&LMIC.osjob, LMIC.bcninfo.txtime, FUNC_ADDR(onBcnRx));
dudmuck 0:f2716e543d97 1780 return;
dudmuck 0:f2716e543d97 1781 }
dudmuck 0:f2716e543d97 1782 // Found our 1st beacon
dudmuck 0:f2716e543d97 1783 // We don't have a previous beacon to calc some drift - assume
dudmuck 0:f2716e543d97 1784 // an max error of 13ms = 128sec*100ppm which is roughly +/-100ppm
dudmuck 0:f2716e543d97 1785 calcBcnRxWindowFromMillis(13,1);
dudmuck 0:f2716e543d97 1786 LMIC.opmode &= ~OP_SCAN; // turn SCAN off
dudmuck 0:f2716e543d97 1787 LMIC.opmode |= OP_TRACK; // auto enable tracking
dudmuck 0:f2716e543d97 1788 reportEvent(EV_BEACON_FOUND); // can be disabled in callback
dudmuck 0:f2716e543d97 1789 }
dudmuck 0:f2716e543d97 1790
dudmuck 0:f2716e543d97 1791
dudmuck 0:f2716e543d97 1792 // Enable receiver to listen to incoming beacons
dudmuck 0:f2716e543d97 1793 // netid defines when scan stops (any or specific beacon)
dudmuck 0:f2716e543d97 1794 // This mode ends with events: EV_SCAN_TIMEOUT/EV_SCAN_BEACON
dudmuck 0:f2716e543d97 1795 // Implicitely cancels any pending TX/RX transaction.
dudmuck 0:f2716e543d97 1796 // Also cancels an onpoing joining procedure.
dudmuck 0:f2716e543d97 1797 static void startScan (void) {
dudmuck 0:f2716e543d97 1798 ASSERT(LMIC.devaddr!=0 && (LMIC.opmode & OP_JOINING)==0);
dudmuck 0:f2716e543d97 1799 if( (LMIC.opmode & OP_SHUTDOWN) != 0 )
dudmuck 0:f2716e543d97 1800 return;
dudmuck 0:f2716e543d97 1801 // Cancel onging TX/RX transaction
dudmuck 0:f2716e543d97 1802 LMIC.txCnt = LMIC.dnConf = LMIC.bcninfo.flags = 0;
dudmuck 0:f2716e543d97 1803 LMIC.opmode = (LMIC.opmode | OP_SCAN) & ~(OP_TXRXPEND);
dudmuck 0:f2716e543d97 1804 setBcnRxParams();
dudmuck 0:f2716e543d97 1805 LMIC.rxtime = LMIC.bcninfo.txtime = os_getTime() + sec2osticks(BCN_INTV_sec+1);
dudmuck 0:f2716e543d97 1806 os_setTimedCallback(&LMIC.osjob, LMIC.rxtime, FUNC_ADDR(onBcnRx));
dudmuck 0:f2716e543d97 1807 os_radio(RADIO_RXON);
dudmuck 0:f2716e543d97 1808 }
dudmuck 0:f2716e543d97 1809
dudmuck 0:f2716e543d97 1810
dudmuck 0:f2716e543d97 1811 bit_t LMIC_enableTracking (u1_t tryBcnInfo) {
dudmuck 0:f2716e543d97 1812 if( (LMIC.opmode & (OP_SCAN|OP_TRACK|OP_SHUTDOWN)) != 0 )
dudmuck 0:f2716e543d97 1813 return 0; // already in progress or failed to enable
dudmuck 0:f2716e543d97 1814 // If BCN info requested from NWK then app has to take are
dudmuck 0:f2716e543d97 1815 // of sending data up so that MCMD_BCNI_REQ can be attached.
dudmuck 0:f2716e543d97 1816 if( (LMIC.bcninfoTries = tryBcnInfo) == 0 )
dudmuck 0:f2716e543d97 1817 startScan();
dudmuck 0:f2716e543d97 1818 return 1; // enabled
dudmuck 0:f2716e543d97 1819 }
dudmuck 0:f2716e543d97 1820
dudmuck 0:f2716e543d97 1821
dudmuck 0:f2716e543d97 1822 void LMIC_disableTracking (void) {
dudmuck 0:f2716e543d97 1823 LMIC.opmode &= ~(OP_SCAN|OP_TRACK);
dudmuck 0:f2716e543d97 1824 LMIC.bcninfoTries = 0;
dudmuck 0:f2716e543d97 1825 engineUpdate();
dudmuck 0:f2716e543d97 1826 }
dudmuck 0:f2716e543d97 1827
dudmuck 0:f2716e543d97 1828
dudmuck 0:f2716e543d97 1829 // ================================================================================
dudmuck 0:f2716e543d97 1830 //
dudmuck 0:f2716e543d97 1831 // Join stuff
dudmuck 0:f2716e543d97 1832 //
dudmuck 0:f2716e543d97 1833 // ================================================================================
dudmuck 0:f2716e543d97 1834
dudmuck 0:f2716e543d97 1835 static void buildJoinRequest (u1_t ftype) {
dudmuck 0:f2716e543d97 1836 // Do not use pendTxData since we might have a pending
dudmuck 0:f2716e543d97 1837 // user level frame in there. Use RX holding area instead.
dudmuck 0:f2716e543d97 1838 xref2u1_t d = LMIC.frame;
dudmuck 0:f2716e543d97 1839 d[OFF_JR_HDR] = ftype;
dudmuck 0:f2716e543d97 1840 os_getArtEui(d + OFF_JR_ARTEUI);
dudmuck 0:f2716e543d97 1841 os_getDevEui(d + OFF_JR_DEVEUI);
dudmuck 0:f2716e543d97 1842 os_wlsbf2(d + OFF_JR_DEVNONCE, LMIC.devNonce);
dudmuck 0:f2716e543d97 1843 aes_appendMic0(d, OFF_JR_MIC);
dudmuck 0:f2716e543d97 1844
dudmuck 0:f2716e543d97 1845 EV(joininfo,INFO,(e_.deveui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1846 e_.arteui = MAIN::CDEV->getArtEui(),
dudmuck 0:f2716e543d97 1847 e_.nonce = LMIC.devNonce,
dudmuck 0:f2716e543d97 1848 e_.oldaddr = LMIC.devaddr,
dudmuck 0:f2716e543d97 1849 e_.mic = Base::lsbf4(&d[LORA::OFF_JR_MIC]),
dudmuck 0:f2716e543d97 1850 e_.reason = ((LMIC.opmode & OP_REJOIN) != 0
dudmuck 0:f2716e543d97 1851 ? EV::joininfo_t::REJOIN_REQUEST
dudmuck 0:f2716e543d97 1852 : EV::joininfo_t::REQUEST)));
dudmuck 0:f2716e543d97 1853 LMIC.dataLen = LEN_JR;
dudmuck 0:f2716e543d97 1854 LMIC.devNonce++;
dudmuck 0:f2716e543d97 1855 DO_DEVDB(LMIC.devNonce,devNonce);
dudmuck 0:f2716e543d97 1856 }
dudmuck 0:f2716e543d97 1857
dudmuck 0:f2716e543d97 1858 static void startJoining (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 1859 reportEvent(EV_JOINING);
dudmuck 0:f2716e543d97 1860 }
dudmuck 0:f2716e543d97 1861
dudmuck 0:f2716e543d97 1862 // Start join procedure if not already joined.
dudmuck 0:f2716e543d97 1863 bit_t LMIC_startJoining (void) {
dudmuck 0:f2716e543d97 1864 if( LMIC.devaddr == 0 ) {
dudmuck 0:f2716e543d97 1865 // There should be no TX/RX going on
dudmuck 0:f2716e543d97 1866 ASSERT((LMIC.opmode & (OP_POLL|OP_TXRXPEND)) == 0);
dudmuck 0:f2716e543d97 1867 // Lift any previous duty limitation
dudmuck 0:f2716e543d97 1868 LMIC.globalDutyRate = 0;
dudmuck 0:f2716e543d97 1869 // Cancel scanning
dudmuck 0:f2716e543d97 1870 LMIC.opmode &= ~(OP_SCAN|OP_REJOIN|OP_LINKDEAD|OP_NEXTCHNL);
dudmuck 0:f2716e543d97 1871 // Setup state
dudmuck 0:f2716e543d97 1872 LMIC.rejoinCnt = LMIC.txCnt = LMIC.pendTxConf = 0;
dudmuck 0:f2716e543d97 1873 initJoinLoop();
dudmuck 0:f2716e543d97 1874 LMIC.opmode |= OP_JOINING;
dudmuck 0:f2716e543d97 1875 // reportEvent will call engineUpdate which then starts sending JOIN REQUESTS
dudmuck 0:f2716e543d97 1876 os_setCallback(&LMIC.osjob, FUNC_ADDR(startJoining));
dudmuck 0:f2716e543d97 1877 return 1;
dudmuck 0:f2716e543d97 1878 }
dudmuck 0:f2716e543d97 1879 return 0; // already joined
dudmuck 0:f2716e543d97 1880 }
dudmuck 0:f2716e543d97 1881
dudmuck 0:f2716e543d97 1882
dudmuck 0:f2716e543d97 1883 // ================================================================================
dudmuck 0:f2716e543d97 1884 //
dudmuck 0:f2716e543d97 1885 //
dudmuck 0:f2716e543d97 1886 //
dudmuck 0:f2716e543d97 1887 // ================================================================================
dudmuck 0:f2716e543d97 1888
dudmuck 0:f2716e543d97 1889 static void processPingRx (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 1890 if( LMIC.dataLen != 0 ) {
dudmuck 0:f2716e543d97 1891 LMIC.txrxFlags = TXRX_PING;
dudmuck 0:f2716e543d97 1892 if( decodeFrame() ) {
dudmuck 0:f2716e543d97 1893 reportEvent(EV_RXCOMPLETE);
dudmuck 0:f2716e543d97 1894 return;
dudmuck 0:f2716e543d97 1895 }
dudmuck 0:f2716e543d97 1896 }
dudmuck 0:f2716e543d97 1897 // Pick next ping slot
dudmuck 0:f2716e543d97 1898 engineUpdate();
dudmuck 0:f2716e543d97 1899 }
dudmuck 0:f2716e543d97 1900
dudmuck 0:f2716e543d97 1901
dudmuck 0:f2716e543d97 1902 static bit_t processDnData (void) {
dudmuck 0:f2716e543d97 1903 ASSERT((LMIC.opmode & OP_TXRXPEND)!=0);
dudmuck 0:f2716e543d97 1904
dudmuck 0:f2716e543d97 1905 if( LMIC.dataLen == 0 ) {
dudmuck 0:f2716e543d97 1906 norx:
dudmuck 0:f2716e543d97 1907 if( LMIC.txCnt != 0 ) {
shaunkrnelson 17:f90b064f6107 1908 if( (_TXCONF_ATTEMPTS > 0) && (LMIC.txCnt < _TXCONF_ATTEMPTS )) {
dudmuck 0:f2716e543d97 1909 LMIC.txCnt += 1;
dudmuck 0:f2716e543d97 1910 setDrTxpow(DRCHG_NOACK, lowerDR(LMIC.datarate, DRADJUST[LMIC.txCnt]), KEEP_TXPOW);
dudmuck 0:f2716e543d97 1911 // Schedule another retransmission
dudmuck 0:f2716e543d97 1912 txDelay(LMIC.rxtime, RETRY_PERIOD_secs);
dudmuck 0:f2716e543d97 1913 LMIC.opmode &= ~OP_TXRXPEND;
dudmuck 0:f2716e543d97 1914 engineUpdate();
dudmuck 0:f2716e543d97 1915 return 1;
dudmuck 0:f2716e543d97 1916 }
dudmuck 0:f2716e543d97 1917 LMIC.txrxFlags = TXRX_NACK | TXRX_NOPORT;
dudmuck 0:f2716e543d97 1918 } else {
dudmuck 0:f2716e543d97 1919 // Nothing received - implies no port
dudmuck 0:f2716e543d97 1920 LMIC.txrxFlags = TXRX_NOPORT;
dudmuck 0:f2716e543d97 1921 }
Shaun Nelson 16:d5c50bdab1f1 1922 if( LMIC.adrAckReq != LINK_CHECK_OFF )
dudmuck 0:f2716e543d97 1923 LMIC.adrAckReq += 1;
dudmuck 0:f2716e543d97 1924 LMIC.dataBeg = LMIC.dataLen = 0;
dudmuck 0:f2716e543d97 1925 txcomplete:
dudmuck 0:f2716e543d97 1926 LMIC.opmode &= ~(OP_TXDATA|OP_TXRXPEND);
dudmuck 0:f2716e543d97 1927 if( (LMIC.txrxFlags & (TXRX_DNW1|TXRX_DNW2|TXRX_PING)) != 0 && (LMIC.opmode & OP_LINKDEAD) != 0 ) {
dudmuck 0:f2716e543d97 1928 LMIC.opmode &= ~OP_LINKDEAD;
dudmuck 0:f2716e543d97 1929 reportEvent(EV_LINK_ALIVE);
dudmuck 0:f2716e543d97 1930 }
dudmuck 0:f2716e543d97 1931 reportEvent(EV_TXCOMPLETE);
dudmuck 0:f2716e543d97 1932 // If we haven't heard from NWK in a while although we asked for a sign
dudmuck 0:f2716e543d97 1933 // assume link is dead - notify application and keep going
dudmuck 0:f2716e543d97 1934 if( LMIC.adrAckReq > LINK_CHECK_DEAD ) {
dudmuck 0:f2716e543d97 1935 // We haven't heard from NWK for some time although we
dudmuck 0:f2716e543d97 1936 // asked for a response for some time - assume we're disconnected. Lower DR one notch.
dudmuck 0:f2716e543d97 1937 EV(devCond, ERR, (e_.reason = EV::devCond_t::LINK_DEAD,
dudmuck 0:f2716e543d97 1938 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1939 e_.info = LMIC.adrAckReq));
dudmuck 0:f2716e543d97 1940 setDrTxpow(DRCHG_NOADRACK, decDR((dr_t)LMIC.datarate), KEEP_TXPOW);
dudmuck 0:f2716e543d97 1941 LMIC.adrAckReq = LINK_CHECK_CONT;
dudmuck 0:f2716e543d97 1942 LMIC.opmode |= OP_REJOIN|OP_LINKDEAD;
dudmuck 0:f2716e543d97 1943 reportEvent(EV_LINK_DEAD);
dudmuck 0:f2716e543d97 1944 }
dudmuck 0:f2716e543d97 1945 // If this falls to zero the NWK did not answer our MCMD_BCNI_REQ commands - try full scan
dudmuck 0:f2716e543d97 1946 if( LMIC.bcninfoTries > 0 ) {
dudmuck 0:f2716e543d97 1947 if( (LMIC.opmode & OP_TRACK) != 0 ) {
dudmuck 0:f2716e543d97 1948 reportEvent(EV_BEACON_FOUND);
dudmuck 0:f2716e543d97 1949 LMIC.bcninfoTries = 0;
dudmuck 0:f2716e543d97 1950 }
dudmuck 0:f2716e543d97 1951 else if( --LMIC.bcninfoTries == 0 ) {
dudmuck 0:f2716e543d97 1952 startScan(); // NWK did not answer - try scan
dudmuck 0:f2716e543d97 1953 }
dudmuck 0:f2716e543d97 1954 }
dudmuck 0:f2716e543d97 1955 return 1;
dudmuck 0:f2716e543d97 1956 }
dudmuck 0:f2716e543d97 1957 if( !decodeFrame() ) {
dudmuck 0:f2716e543d97 1958 if( (LMIC.txrxFlags & TXRX_DNW1) != 0 )
dudmuck 0:f2716e543d97 1959 return 0;
dudmuck 0:f2716e543d97 1960 goto norx;
dudmuck 0:f2716e543d97 1961 }
dudmuck 0:f2716e543d97 1962 goto txcomplete;
dudmuck 0:f2716e543d97 1963 }
dudmuck 0:f2716e543d97 1964
dudmuck 0:f2716e543d97 1965
dudmuck 0:f2716e543d97 1966 static void processBeacon (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 1967 ostime_t lasttx = LMIC.bcninfo.txtime; // save here - decodeBeacon might overwrite
dudmuck 0:f2716e543d97 1968 u1_t flags = LMIC.bcninfo.flags;
dudmuck 0:f2716e543d97 1969 ev_t ev;
dudmuck 0:f2716e543d97 1970
dudmuck 0:f2716e543d97 1971 if( LMIC.dataLen != 0 && decodeBeacon() >= 1 ) {
dudmuck 0:f2716e543d97 1972 ev = EV_BEACON_TRACKED;
dudmuck 0:f2716e543d97 1973 if( (flags & (BCN_PARTIAL|BCN_FULL)) == 0 ) {
dudmuck 0:f2716e543d97 1974 // We don't have a previous beacon to calc some drift - assume
dudmuck 0:f2716e543d97 1975 // an max error of 13ms = 128sec*100ppm which is roughly +/-100ppm
dudmuck 0:f2716e543d97 1976 calcBcnRxWindowFromMillis(13,0);
dudmuck 0:f2716e543d97 1977 goto rev;
dudmuck 0:f2716e543d97 1978 }
dudmuck 0:f2716e543d97 1979 // We have a previous BEACON to calculate some drift
dudmuck 0:f2716e543d97 1980 s2_t drift = BCN_INTV_osticks - (LMIC.bcninfo.txtime - lasttx);
dudmuck 0:f2716e543d97 1981 if( LMIC.missedBcns > 0 ) {
dudmuck 0:f2716e543d97 1982 drift = LMIC.drift + (drift - LMIC.drift) / (LMIC.missedBcns+1);
dudmuck 0:f2716e543d97 1983 }
dudmuck 0:f2716e543d97 1984 if( (LMIC.bcninfo.flags & BCN_NODRIFT) == 0 ) {
dudmuck 0:f2716e543d97 1985 s2_t diff = LMIC.drift - drift;
dudmuck 0:f2716e543d97 1986 if( diff < 0 ) diff = -diff;
dudmuck 0:f2716e543d97 1987 LMIC.lastDriftDiff = diff;
dudmuck 0:f2716e543d97 1988 if( LMIC.maxDriftDiff < diff )
dudmuck 0:f2716e543d97 1989 LMIC.maxDriftDiff = diff;
dudmuck 0:f2716e543d97 1990 LMIC.bcninfo.flags &= ~BCN_NODDIFF;
dudmuck 0:f2716e543d97 1991 }
dudmuck 0:f2716e543d97 1992 LMIC.drift = drift;
dudmuck 0:f2716e543d97 1993 LMIC.missedBcns = LMIC.rejoinCnt = 0;
dudmuck 0:f2716e543d97 1994 LMIC.bcninfo.flags &= ~BCN_NODRIFT;
dudmuck 0:f2716e543d97 1995 EV(devCond,INFO,(e_.reason = EV::devCond_t::CLOCK_DRIFT,
dudmuck 0:f2716e543d97 1996 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 1997 e_.info = drift,
dudmuck 0:f2716e543d97 1998 e_.info2 = /*occasion BEACON*/0));
dudmuck 0:f2716e543d97 1999 ASSERT((LMIC.bcninfo.flags & (BCN_PARTIAL|BCN_FULL)) != 0);
dudmuck 0:f2716e543d97 2000 } else {
dudmuck 0:f2716e543d97 2001 ev = EV_BEACON_MISSED;
dudmuck 0:f2716e543d97 2002 LMIC.bcninfo.txtime += BCN_INTV_osticks - LMIC.drift;
dudmuck 0:f2716e543d97 2003 LMIC.bcninfo.time += BCN_INTV_sec;
dudmuck 0:f2716e543d97 2004 LMIC.missedBcns++;
dudmuck 0:f2716e543d97 2005 // Delay any possible TX after surmised beacon - it's there although we missed it
dudmuck 0:f2716e543d97 2006 txDelay(LMIC.bcninfo.txtime + BCN_RESERVE_osticks, 4);
dudmuck 0:f2716e543d97 2007 if( LMIC.missedBcns > MAX_MISSED_BCNS )
dudmuck 0:f2716e543d97 2008 LMIC.opmode |= OP_REJOIN; // try if we can roam to another network
dudmuck 0:f2716e543d97 2009 if( LMIC.bcnRxsyms > MAX_RXSYMS ) {
dudmuck 0:f2716e543d97 2010 LMIC.opmode &= ~(OP_TRACK|OP_PINGABLE|OP_PINGINI|OP_REJOIN);
dudmuck 0:f2716e543d97 2011 reportEvent(EV_LOST_TSYNC);
dudmuck 0:f2716e543d97 2012 return;
dudmuck 0:f2716e543d97 2013 }
dudmuck 0:f2716e543d97 2014 }
dudmuck 0:f2716e543d97 2015 LMIC.bcnRxtime = LMIC.bcninfo.txtime + BCN_INTV_osticks - calcRxWindow(0,DR_BCN);
dudmuck 0:f2716e543d97 2016 LMIC.bcnRxsyms = LMIC.rxsyms;
dudmuck 0:f2716e543d97 2017 rev:
dudmuck 0:f2716e543d97 2018 #ifdef CFG_us915
dudmuck 0:f2716e543d97 2019 LMIC.bcnChnl = (LMIC.bcnChnl+1) & 7;
dudmuck 0:f2716e543d97 2020 #endif
dudmuck 0:f2716e543d97 2021 if( (LMIC.opmode & OP_PINGINI) != 0 )
dudmuck 0:f2716e543d97 2022 rxschedInit(&LMIC.ping); // note: reuses LMIC.frame buffer!
dudmuck 0:f2716e543d97 2023 reportEvent(ev);
dudmuck 0:f2716e543d97 2024 }
dudmuck 0:f2716e543d97 2025
dudmuck 0:f2716e543d97 2026
dudmuck 0:f2716e543d97 2027 static void startRxBcn (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 2028 LMIC.osjob.func = FUNC_ADDR(processBeacon);
dudmuck 0:f2716e543d97 2029 os_radio(RADIO_RX);
dudmuck 0:f2716e543d97 2030 }
dudmuck 0:f2716e543d97 2031
dudmuck 0:f2716e543d97 2032
dudmuck 0:f2716e543d97 2033 static void startRxPing (xref2osjob_t osjob) {
dudmuck 0:f2716e543d97 2034 LMIC.osjob.func = FUNC_ADDR(processPingRx);
dudmuck 0:f2716e543d97 2035 os_radio(RADIO_RX);
dudmuck 0:f2716e543d97 2036 }
dudmuck 0:f2716e543d97 2037
dudmuck 0:f2716e543d97 2038
dudmuck 0:f2716e543d97 2039 // Decide what to do next for the MAC layer of a device
dudmuck 0:f2716e543d97 2040 static void engineUpdate (void) {
dudmuck 0:f2716e543d97 2041 // Check for ongoing state: scan or TX/RX transaction
Shaun Nelson 16:d5c50bdab1f1 2042 if( (LMIC.opmode & (OP_SCAN|OP_TXRXPEND|OP_SHUTDOWN)) != 0 )
dudmuck 0:f2716e543d97 2043 return;
dudmuck 0:f2716e543d97 2044
dudmuck 0:f2716e543d97 2045 if( LMIC.devaddr == 0 && (LMIC.opmode & OP_JOINING) == 0 ) {
dudmuck 0:f2716e543d97 2046 LMIC_startJoining();
dudmuck 0:f2716e543d97 2047 return;
dudmuck 0:f2716e543d97 2048 }
dudmuck 0:f2716e543d97 2049
dudmuck 0:f2716e543d97 2050 ostime_t now = os_getTime();
dudmuck 0:f2716e543d97 2051 ostime_t rxtime = 0;
dudmuck 0:f2716e543d97 2052 ostime_t txbeg = 0;
dudmuck 0:f2716e543d97 2053
dudmuck 0:f2716e543d97 2054 if( (LMIC.opmode & OP_TRACK) != 0 ) {
dudmuck 0:f2716e543d97 2055 // We are tracking a beacon
dudmuck 0:f2716e543d97 2056 ASSERT( now + RX_RAMPUP - LMIC.bcnRxtime <= 0 );
dudmuck 0:f2716e543d97 2057 rxtime = LMIC.bcnRxtime - RX_RAMPUP;
dudmuck 0:f2716e543d97 2058 }
Shaun Nelson 16:d5c50bdab1f1 2059
Shaun Nelson 16:d5c50bdab1f1 2060 if( (LMIC.opmode & (OP_JOINING|OP_REJOIN|OP_TXDATA|OP_POLL|OP_RNDTX)) != 0 ) {
dudmuck 0:f2716e543d97 2061 // Need to TX some data...
dudmuck 0:f2716e543d97 2062 // Assuming txChnl points to channel which first becomes available again.
dudmuck 0:f2716e543d97 2063 bit_t jacc = ((LMIC.opmode & (OP_JOINING|OP_REJOIN)) != 0 ? 1 : 0);
dudmuck 0:f2716e543d97 2064 // Find next suitable channel and return availability time
dudmuck 0:f2716e543d97 2065 if( (LMIC.opmode & OP_NEXTCHNL) != 0 ) {
dudmuck 0:f2716e543d97 2066 txbeg = LMIC.txend = nextTx(now);
dudmuck 0:f2716e543d97 2067 LMIC.opmode &= ~OP_NEXTCHNL;
dudmuck 0:f2716e543d97 2068 } else {
dudmuck 0:f2716e543d97 2069 txbeg = LMIC.txend;
dudmuck 0:f2716e543d97 2070 }
dudmuck 0:f2716e543d97 2071 // Delayed TX or waiting for duty cycle?
dudmuck 0:f2716e543d97 2072 if( (LMIC.globalDutyRate != 0 || (LMIC.opmode & OP_RNDTX) != 0) && (txbeg - LMIC.globalDutyAvail) < 0 )
dudmuck 0:f2716e543d97 2073 txbeg = LMIC.globalDutyAvail;
dudmuck 0:f2716e543d97 2074 // If we're tracking a beacon...
dudmuck 0:f2716e543d97 2075 // then make sure TX-RX transaction is complete before beacon
dudmuck 0:f2716e543d97 2076 if( (LMIC.opmode & OP_TRACK) != 0 &&
dudmuck 0:f2716e543d97 2077 txbeg + (jacc ? JOIN_GUARD_osticks : TXRX_GUARD_osticks) - rxtime > 0 ) {
dudmuck 0:f2716e543d97 2078 // Not enough time to complete TX-RX before beacon - postpone after beacon.
dudmuck 0:f2716e543d97 2079 // In order to avoid clustering of postponed TX right after beacon randomize start!
dudmuck 0:f2716e543d97 2080 txDelay(rxtime + BCN_RESERVE_osticks, 16);
dudmuck 0:f2716e543d97 2081 txbeg = 0;
dudmuck 0:f2716e543d97 2082 goto checkrx;
dudmuck 0:f2716e543d97 2083 }
dudmuck 0:f2716e543d97 2084 // Earliest possible time vs overhead to setup radio
dudmuck 0:f2716e543d97 2085 if( txbeg - (now + TX_RAMPUP) < 0 ) {
dudmuck 0:f2716e543d97 2086 // We could send right now!
Shaun Nelson 16:d5c50bdab1f1 2087 txbeg = now;
dudmuck 0:f2716e543d97 2088 dr_t txdr = (dr_t)LMIC.datarate;
dudmuck 0:f2716e543d97 2089 if( jacc ) {
dudmuck 0:f2716e543d97 2090 u1_t ftype;
dudmuck 0:f2716e543d97 2091 if( (LMIC.opmode & OP_REJOIN) != 0 ) {
dudmuck 0:f2716e543d97 2092 txdr = lowerDR(txdr, LMIC.rejoinCnt);
Shaun Nelson 16:d5c50bdab1f1 2093 ftype = HDR_FTYPE_REJOIN;
dudmuck 0:f2716e543d97 2094 } else {
dudmuck 0:f2716e543d97 2095 ftype = HDR_FTYPE_JREQ;
dudmuck 0:f2716e543d97 2096 }
dudmuck 0:f2716e543d97 2097 buildJoinRequest(ftype);
dudmuck 0:f2716e543d97 2098 LMIC.osjob.func = FUNC_ADDR(jreqDone);
dudmuck 0:f2716e543d97 2099 } else {
dudmuck 0:f2716e543d97 2100 if( LMIC.seqnoDn >= 0xFFFFFF80 ) {
dudmuck 0:f2716e543d97 2101 // Imminent roll over - proactively reset MAC
dudmuck 0:f2716e543d97 2102 EV(specCond, INFO, (e_.reason = EV::specCond_t::DNSEQNO_ROLL_OVER,
dudmuck 0:f2716e543d97 2103 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 2104 e_.info = LMIC.seqnoDn,
dudmuck 0:f2716e543d97 2105 e_.info2 = 0));
dudmuck 0:f2716e543d97 2106 // Device has to react! NWK will not roll over and just stop sending.
dudmuck 0:f2716e543d97 2107 // Thus, we have N frames to detect a possible lock up.
dudmuck 0:f2716e543d97 2108 reset:
dudmuck 0:f2716e543d97 2109 os_setCallback(&LMIC.osjob, FUNC_ADDR(runReset));
dudmuck 0:f2716e543d97 2110 return;
dudmuck 0:f2716e543d97 2111 }
dudmuck 0:f2716e543d97 2112 if( (LMIC.txCnt==0 && LMIC.seqnoUp == 0xFFFFFFFF) ) {
dudmuck 0:f2716e543d97 2113 // Roll over of up seq counter
dudmuck 0:f2716e543d97 2114 EV(specCond, ERR, (e_.reason = EV::specCond_t::UPSEQNO_ROLL_OVER,
dudmuck 0:f2716e543d97 2115 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 2116 e_.info2 = LMIC.seqnoUp));
dudmuck 0:f2716e543d97 2117 // Do not run RESET event callback from here!
dudmuck 0:f2716e543d97 2118 // App code might do some stuff after send unaware of RESET.
dudmuck 0:f2716e543d97 2119 goto reset;
dudmuck 0:f2716e543d97 2120 }
dudmuck 0:f2716e543d97 2121 buildDataFrame();
dudmuck 0:f2716e543d97 2122 LMIC.osjob.func = FUNC_ADDR(updataDone);
dudmuck 0:f2716e543d97 2123 }
dudmuck 0:f2716e543d97 2124 LMIC.rps = setCr(updr2rps(txdr), (cr_t)LMIC.errcr);
dudmuck 0:f2716e543d97 2125 LMIC.dndr = txdr; // carry TX datarate (can be != LMIC.datarate) over to txDone/setupRx1
dudmuck 0:f2716e543d97 2126 LMIC.opmode = (LMIC.opmode & ~(OP_POLL|OP_RNDTX)) | OP_TXRXPEND | OP_NEXTCHNL;
dudmuck 0:f2716e543d97 2127 updateTx(txbeg);
dudmuck 0:f2716e543d97 2128 os_radio(RADIO_TX);
dudmuck 0:f2716e543d97 2129 return;
dudmuck 0:f2716e543d97 2130 }
dudmuck 0:f2716e543d97 2131 // Cannot yet TX
dudmuck 0:f2716e543d97 2132 if( (LMIC.opmode & OP_TRACK) == 0 )
dudmuck 0:f2716e543d97 2133 goto txdelay; // We don't track the beacon - nothing else to do - so wait for the time to TX
dudmuck 0:f2716e543d97 2134 // Consider RX tasks
dudmuck 0:f2716e543d97 2135 if( txbeg == 0 ) // zero indicates no TX pending
dudmuck 0:f2716e543d97 2136 txbeg += 1; // TX delayed by one tick (insignificant amount of time)
dudmuck 0:f2716e543d97 2137 } else {
dudmuck 0:f2716e543d97 2138 // No TX pending - no scheduled RX
dudmuck 0:f2716e543d97 2139 if( (LMIC.opmode & OP_TRACK) == 0 )
dudmuck 0:f2716e543d97 2140 return;
dudmuck 0:f2716e543d97 2141 }
dudmuck 0:f2716e543d97 2142
dudmuck 0:f2716e543d97 2143 // Are we pingable?
dudmuck 0:f2716e543d97 2144 checkrx:
dudmuck 0:f2716e543d97 2145 if( (LMIC.opmode & OP_PINGINI) != 0 ) {
dudmuck 0:f2716e543d97 2146 // One more RX slot in this beacon period?
dudmuck 0:f2716e543d97 2147 if( rxschedNext(&LMIC.ping, now+RX_RAMPUP) ) {
dudmuck 0:f2716e543d97 2148 if( txbeg != 0 && (txbeg - LMIC.ping.rxtime) < 0 )
dudmuck 0:f2716e543d97 2149 goto txdelay;
dudmuck 0:f2716e543d97 2150 LMIC.rxsyms = LMIC.ping.rxsyms;
dudmuck 0:f2716e543d97 2151 LMIC.rxtime = LMIC.ping.rxtime;
dudmuck 0:f2716e543d97 2152 LMIC.freq = LMIC.ping.freq;
dudmuck 0:f2716e543d97 2153 LMIC.rps = dndr2rps(LMIC.ping.dr);
dudmuck 0:f2716e543d97 2154 LMIC.dataLen = 0;
dudmuck 0:f2716e543d97 2155 ASSERT(LMIC.rxtime - now+RX_RAMPUP >= 0 );
dudmuck 0:f2716e543d97 2156 os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, FUNC_ADDR(startRxPing));
dudmuck 0:f2716e543d97 2157 return;
dudmuck 0:f2716e543d97 2158 }
dudmuck 0:f2716e543d97 2159 // no - just wait for the beacon
dudmuck 0:f2716e543d97 2160 }
dudmuck 0:f2716e543d97 2161
dudmuck 0:f2716e543d97 2162 if( txbeg != 0 && (txbeg - rxtime) < 0 )
dudmuck 0:f2716e543d97 2163 goto txdelay;
dudmuck 0:f2716e543d97 2164
dudmuck 0:f2716e543d97 2165 setBcnRxParams();
dudmuck 0:f2716e543d97 2166 LMIC.rxsyms = LMIC.bcnRxsyms;
dudmuck 0:f2716e543d97 2167 LMIC.rxtime = LMIC.bcnRxtime;
dudmuck 0:f2716e543d97 2168 if( now - rxtime >= 0 ) {
dudmuck 0:f2716e543d97 2169 LMIC.osjob.func = FUNC_ADDR(processBeacon);
dudmuck 0:f2716e543d97 2170 os_radio(RADIO_RX);
dudmuck 0:f2716e543d97 2171 return;
dudmuck 0:f2716e543d97 2172 }
dudmuck 0:f2716e543d97 2173 os_setTimedCallback(&LMIC.osjob, rxtime, FUNC_ADDR(startRxBcn));
dudmuck 0:f2716e543d97 2174 return;
dudmuck 0:f2716e543d97 2175
dudmuck 0:f2716e543d97 2176 txdelay:
dudmuck 0:f2716e543d97 2177 EV(devCond, INFO, (e_.reason = EV::devCond_t::TX_DELAY,
dudmuck 0:f2716e543d97 2178 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 2179 e_.info = osticks2ms(txbeg-now),
dudmuck 0:f2716e543d97 2180 e_.info2 = LMIC.seqnoUp-1));
dudmuck 0:f2716e543d97 2181 os_setTimedCallback(&LMIC.osjob, txbeg-TX_RAMPUP, FUNC_ADDR(runEngineUpdate));
dudmuck 0:f2716e543d97 2182 }
dudmuck 0:f2716e543d97 2183
dudmuck 0:f2716e543d97 2184
dudmuck 0:f2716e543d97 2185 void LMIC_setAdrMode (bit_t enabled) {
dudmuck 0:f2716e543d97 2186 LMIC.adrEnabled = enabled ? FCT_ADREN : 0;
dudmuck 0:f2716e543d97 2187 }
dudmuck 0:f2716e543d97 2188
dudmuck 0:f2716e543d97 2189
dudmuck 0:f2716e543d97 2190 // Should we have/need an ext. API like this?
dudmuck 0:f2716e543d97 2191 void LMIC_setDrTxpow (dr_t dr, s1_t txpow) {
dudmuck 0:f2716e543d97 2192 setDrTxpow(DRCHG_SET, dr, txpow);
dudmuck 0:f2716e543d97 2193 }
dudmuck 0:f2716e543d97 2194
dudmuck 0:f2716e543d97 2195
dudmuck 0:f2716e543d97 2196 void LMIC_shutdown (void) {
dudmuck 0:f2716e543d97 2197 os_clearCallback(&LMIC.osjob);
dudmuck 0:f2716e543d97 2198 os_radio(RADIO_RST);
dudmuck 0:f2716e543d97 2199 LMIC.opmode |= OP_SHUTDOWN;
dudmuck 0:f2716e543d97 2200 }
dudmuck 0:f2716e543d97 2201
dudmuck 0:f2716e543d97 2202
dudmuck 0:f2716e543d97 2203 void LMIC_reset (void) {
dudmuck 0:f2716e543d97 2204 EV(devCond, INFO, (e_.reason = EV::devCond_t::LMIC_EV,
dudmuck 0:f2716e543d97 2205 e_.eui = MAIN::CDEV->getEui(),
dudmuck 0:f2716e543d97 2206 e_.info = EV_RESET));
dudmuck 0:f2716e543d97 2207 os_radio(RADIO_RST);
dudmuck 0:f2716e543d97 2208 os_clearCallback(&LMIC.osjob);
dudmuck 0:f2716e543d97 2209
dudmuck 0:f2716e543d97 2210 os_clearMem((xref2u1_t)&LMIC,SIZEOFEXPR(LMIC));
dudmuck 0:f2716e543d97 2211 LMIC.devaddr = 0;
dudmuck 0:f2716e543d97 2212 LMIC.devNonce = os_getRndU2();
dudmuck 0:f2716e543d97 2213 LMIC.opmode = OP_NONE;
dudmuck 0:f2716e543d97 2214 LMIC.errcr = CR_4_5;
dudmuck 8:0faa1bb768b5 2215 LMIC.adrEnabled = 0; /* FCT_ADREN */
dudmuck 0:f2716e543d97 2216 LMIC.dn2Dr = DR_DNW2; // we need this for 2nd DN window of join accept
dudmuck 0:f2716e543d97 2217 LMIC.dn2Freq = FREQ_DNW2; // ditto
dudmuck 0:f2716e543d97 2218 LMIC.ping.freq = FREQ_PING; // defaults for ping
dudmuck 0:f2716e543d97 2219 LMIC.ping.dr = DR_PING; // ditto
dudmuck 0:f2716e543d97 2220 LMIC.ping.intvExp = 0xFF;
dudmuck 0:f2716e543d97 2221 #if defined(CFG_us915)
dudmuck 0:f2716e543d97 2222 initDefaultChannels();
dudmuck 0:f2716e543d97 2223 #endif
dudmuck 0:f2716e543d97 2224 DO_DEVDB(LMIC.devaddr, devaddr);
dudmuck 0:f2716e543d97 2225 DO_DEVDB(LMIC.devNonce, devNonce);
dudmuck 0:f2716e543d97 2226 DO_DEVDB(LMIC.dn2Dr, dn2Dr);
dudmuck 0:f2716e543d97 2227 DO_DEVDB(LMIC.dn2Freq, dn2Freq);
dudmuck 0:f2716e543d97 2228 DO_DEVDB(LMIC.ping.freq, pingFreq);
dudmuck 0:f2716e543d97 2229 DO_DEVDB(LMIC.ping.dr, pingDr);
dudmuck 0:f2716e543d97 2230 DO_DEVDB(LMIC.ping.intvExp, pingIntvExp);
dudmuck 0:f2716e543d97 2231 }
dudmuck 0:f2716e543d97 2232
dudmuck 0:f2716e543d97 2233
dudmuck 0:f2716e543d97 2234 void LMIC_init (void) {
dudmuck 0:f2716e543d97 2235 LMIC.opmode = OP_SHUTDOWN;
dudmuck 0:f2716e543d97 2236 }
dudmuck 0:f2716e543d97 2237
dudmuck 0:f2716e543d97 2238
dudmuck 0:f2716e543d97 2239 void LMIC_clrTxData (void) {
dudmuck 0:f2716e543d97 2240 LMIC.opmode &= ~(OP_TXDATA|OP_TXRXPEND|OP_POLL);
dudmuck 0:f2716e543d97 2241 LMIC.pendTxLen = 0;
dudmuck 0:f2716e543d97 2242 if( (LMIC.opmode & (OP_JOINING|OP_SCAN)) != 0 ) // do not interfere with JOINING
dudmuck 0:f2716e543d97 2243 return;
dudmuck 0:f2716e543d97 2244 os_clearCallback(&LMIC.osjob);
dudmuck 0:f2716e543d97 2245 os_radio(RADIO_RST);
dudmuck 0:f2716e543d97 2246 engineUpdate();
dudmuck 0:f2716e543d97 2247 }
dudmuck 0:f2716e543d97 2248
dudmuck 0:f2716e543d97 2249
dudmuck 0:f2716e543d97 2250 void LMIC_setTxData (void) {
dudmuck 0:f2716e543d97 2251 LMIC.opmode |= OP_TXDATA;
dudmuck 0:f2716e543d97 2252 if( (LMIC.opmode & OP_JOINING) == 0 )
dudmuck 0:f2716e543d97 2253 LMIC.txCnt = 0; // cancel any ongoing TX/RX retries
dudmuck 0:f2716e543d97 2254 engineUpdate();
dudmuck 0:f2716e543d97 2255 }
dudmuck 0:f2716e543d97 2256
dudmuck 0:f2716e543d97 2257
dudmuck 0:f2716e543d97 2258 //
dudmuck 0:f2716e543d97 2259 int LMIC_setTxData2 (u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed) {
dudmuck 0:f2716e543d97 2260 if( dlen > SIZEOFEXPR(LMIC.pendTxData) )
dudmuck 0:f2716e543d97 2261 return -2;
dudmuck 0:f2716e543d97 2262 if( data != (xref2u1_t)0 )
dudmuck 0:f2716e543d97 2263 os_copyMem(LMIC.pendTxData, data, dlen);
dudmuck 0:f2716e543d97 2264 LMIC.pendTxConf = confirmed;
dudmuck 0:f2716e543d97 2265 LMIC.pendTxPort = port;
dudmuck 0:f2716e543d97 2266 LMIC.pendTxLen = dlen;
dudmuck 0:f2716e543d97 2267 LMIC_setTxData();
dudmuck 0:f2716e543d97 2268 return 0;
dudmuck 0:f2716e543d97 2269 }
dudmuck 0:f2716e543d97 2270
dudmuck 0:f2716e543d97 2271
dudmuck 0:f2716e543d97 2272 // Send a payload-less message to signal device is alive
dudmuck 0:f2716e543d97 2273 void LMIC_sendAlive (void) {
dudmuck 0:f2716e543d97 2274 LMIC.opmode |= OP_POLL;
dudmuck 0:f2716e543d97 2275 engineUpdate();
dudmuck 0:f2716e543d97 2276 }
dudmuck 0:f2716e543d97 2277
dudmuck 0:f2716e543d97 2278
dudmuck 0:f2716e543d97 2279 // Check if other networks are around.
dudmuck 0:f2716e543d97 2280 void LMIC_tryRejoin (void) {
dudmuck 0:f2716e543d97 2281 LMIC.opmode |= OP_REJOIN;
dudmuck 0:f2716e543d97 2282 engineUpdate();
dudmuck 0:f2716e543d97 2283 }
dudmuck 0:f2716e543d97 2284
dudmuck 0:f2716e543d97 2285 //! \brief Setup given session keys
dudmuck 0:f2716e543d97 2286 //! and put the MAC in a state as if
dudmuck 0:f2716e543d97 2287 //! a join request/accept would have negotiated just these keys.
dudmuck 0:f2716e543d97 2288 //! It is crucial that the combinations `devaddr/nwkkey` and `devaddr/artkey`
dudmuck 0:f2716e543d97 2289 //! are unique within the network identified by `netid`.
dudmuck 0:f2716e543d97 2290 //! NOTE: on Harvard architectures when session keys are in flash:
dudmuck 0:f2716e543d97 2291 //! Caller has to fill in LMIC.{nwk,art}Key before and pass {nwk,art}Key are NULL
dudmuck 0:f2716e543d97 2292 //! \param netid a 24 bit number describing the network id this device is using
dudmuck 0:f2716e543d97 2293 //! \param devaddr the 32 bit session address of the device. It is strongly recommended
dudmuck 0:f2716e543d97 2294 //! to ensure that different devices use different numbers with high probability.
dudmuck 0:f2716e543d97 2295 //! \param nwkKey the 16 byte network session key used for message integrity.
dudmuck 0:f2716e543d97 2296 //! If NULL the caller has copied the key into `LMIC.nwkKey` before.
dudmuck 0:f2716e543d97 2297 //! \param artKey the 16 byte application router session key used for message confidentiality.
dudmuck 0:f2716e543d97 2298 //! If NULL the caller has copied the key into `LMIC.artKey` before.
dudmuck 0:f2716e543d97 2299 void LMIC_setSession (u4_t netid, devaddr_t devaddr, xref2u1_t nwkKey, xref2u1_t artKey) {
dudmuck 0:f2716e543d97 2300 LMIC.netid = netid;
dudmuck 0:f2716e543d97 2301 LMIC.devaddr = devaddr;
dudmuck 0:f2716e543d97 2302 if( nwkKey != (xref2u1_t)0 )
dudmuck 0:f2716e543d97 2303 os_copyMem(LMIC.nwkKey, nwkKey, 16);
dudmuck 0:f2716e543d97 2304 if( artKey != (xref2u1_t)0 )
dudmuck 0:f2716e543d97 2305 os_copyMem(LMIC.artKey, artKey, 16);
dudmuck 0:f2716e543d97 2306
dudmuck 0:f2716e543d97 2307 #if defined(CFG_eu868)
dudmuck 0:f2716e543d97 2308 initDefaultChannels(0);
dudmuck 0:f2716e543d97 2309 #endif
dudmuck 0:f2716e543d97 2310
dudmuck 0:f2716e543d97 2311 LMIC.opmode &= ~(OP_JOINING|OP_TRACK|OP_REJOIN|OP_TXRXPEND|OP_PINGINI);
dudmuck 0:f2716e543d97 2312 LMIC.opmode |= OP_NEXTCHNL;
dudmuck 0:f2716e543d97 2313 stateJustJoined();
dudmuck 0:f2716e543d97 2314 DO_DEVDB(LMIC.netid, netid);
dudmuck 0:f2716e543d97 2315 DO_DEVDB(LMIC.devaddr, devaddr);
dudmuck 0:f2716e543d97 2316 DO_DEVDB(LMIC.nwkKey, nwkkey);
dudmuck 0:f2716e543d97 2317 DO_DEVDB(LMIC.artKey, artkey);
dudmuck 0:f2716e543d97 2318 DO_DEVDB(LMIC.seqnoUp, seqnoUp);
dudmuck 0:f2716e543d97 2319 DO_DEVDB(LMIC.seqnoDn, seqnoDn);
dudmuck 0:f2716e543d97 2320 }
dudmuck 0:f2716e543d97 2321
dudmuck 0:f2716e543d97 2322 // Enable/disable link check validation.
dudmuck 0:f2716e543d97 2323 // LMIC sets the ADRACKREQ bit in UP frames if there were no DN frames
dudmuck 0:f2716e543d97 2324 // for a while. It expects the network to provide a DN message to prove
dudmuck 0:f2716e543d97 2325 // connectivity with a span of UP frames. If this no such prove is coming
dudmuck 0:f2716e543d97 2326 // then the datarate is lowered and a LINK_DEAD event is generated.
dudmuck 0:f2716e543d97 2327 // This mode can be disabled and no connectivity prove (ADRACKREQ) is requested
dudmuck 0:f2716e543d97 2328 // nor is the datarate changed.
dudmuck 0:f2716e543d97 2329 // This must be called only if a session is established (e.g. after EV_JOINED)
dudmuck 0:f2716e543d97 2330 void LMIC_setLinkCheckMode (bit_t enabled) {
dudmuck 0:f2716e543d97 2331 LMIC.adrChanged = 0;
dudmuck 0:f2716e543d97 2332 LMIC.adrAckReq = enabled ? LINK_CHECK_INIT : LINK_CHECK_OFF;
dudmuck 0:f2716e543d97 2333 }
dudmuck 0:f2716e543d97 2334
dudmuck 8:0faa1bb768b5 2335 void LMIC_reverse_memcpy(u1_t *dst, const u1_t *src, size_t n)
dudmuck 8:0faa1bb768b5 2336 {
dudmuck 8:0faa1bb768b5 2337 size_t i;
dudmuck 8:0faa1bb768b5 2338
dudmuck 8:0faa1bb768b5 2339 for (i=0; i < n; ++i)
dudmuck 8:0faa1bb768b5 2340 dst[n-1-i] = src[i];
dudmuck 8:0faa1bb768b5 2341 }
dudmuck 0:f2716e543d97 2342