IBM LoRa MAC in C (LMiC) mbed library port

Dependents:   lora-temperature LoRaWAN-lmic-app_HS LoRaWAN-lmic-app_huynh

LoRa WAN in C for sx1276 shield

Currently version 1.5


LoRaWAN network configuration for end-device

The following three pieces of information uniquely identifies end-device to network to allow over-the-air activation. These are stored in the end-device prior to join procedure.

AppEUI

Uniquely identifies application provider of end-device.

Least-significant byte first, 8 bytes, use reverse memcpy() to keep same order as shown on lora server.

example C code

static const u1_t APPEUI[8]  = { 0x01, 0x00, 0x01, 0x00, 0x00, 0x0C, 0x25, 0x00 };

This is copied into LMIC by os_getArtEui() callback function in application.

DevEUI

End-device ID, unique to each end-node.

Least-significant byte first, 8 bytes, use reverse memcpy() to keep same order as shown on lora server.

example C code

static const u1_t DEVEUI[8]  = { 0x00, 0x00, 0x00, 0x00, 0x01, 0x0C, 0x25, 0x00 }; 

This is copied into LMIC by os_getDevEui() callback function in application.

AppKey (aka DevKey)

128-bit (16byte) AES key.

example C code

static const u1_t DEVKEY[16] = { 0xe4, 0x72, 0x71, 0xc5, 0xf5, 0x30, 0xa9, 0x9f, 0xcf, 0xc4, 0x0e, 0xab, 0xea, 0xd7, 0x19, 0x42 };

This is copied into LMIC by os_getDevKey() callback function in application.

Using over-the air activation, the end-device (LMIC) performs a join procedure every time it starts for first time, or has lost session context information. When join procedure has successfully completed, the end-device will have a network session key (NwkSKey) and an application session key (AppSKey), which are used for encryption and message integrity check.


US915 configuration with http://us01-iot.semtech.com/

  • log in to server
  • click on Applications
  • find your application and click it
  • go to configure motes
  • to create a mote, you may enter a new DevEUI
    • you may copy-paste the 16byte application key from an already existing mote, if you desire.
CHNL_HYBRID125KHz500KHz
defined valuechannelschannel
00 to 764
18 to 1565
216 to 2366
324 to 3167
432 to 3968
540 to 4769
648 to 5570
756 to 6371
undef0 to 6364 to 71
Committer:
mluis
Date:
Thu Nov 26 17:17:08 2015 +0000
Revision:
4:85b2b647cb64
Parent:
3:519c71d29a06
Parent:
2:974cafbfb159
Merged branches

Who changed what in which revision?

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