From ad4fd70a1b9575ac525f4c68133d2cfa1dd4d4ca Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Thu, 15 Aug 2019 04:01:39 -0400 Subject: [PATCH] Fix #87: refactor to parse MAC messages properly --- README.md | 6 +- src/lmic/config.h | 10 +- src/lmic/lmic.c | 428 +++++++++++++++++++-------------- src/lmic/lmic.h | 20 +- src/lmic/lmic_as923.c | 36 ++- src/lmic/lmic_au921.c | 7 + src/lmic/lmic_bandplan_au921.h | 3 +- src/lmic/lmic_bandplan_us915.h | 3 +- src/lmic/lmic_eu868.c | 24 +- src/lmic/lmic_in866.c | 25 +- src/lmic/lmic_kr920.c | 25 +- src/lmic/lmic_us915.c | 7 + src/lmic/lorabase.h | 91 +++---- 13 files changed, 417 insertions(+), 268 deletions(-) diff --git a/README.md b/README.md index 016e2b61..324665dc 100644 --- a/README.md +++ b/README.md @@ -314,14 +314,14 @@ If defined, removes code needed for OTAA activation. Removes the APIs `LMIC_star #### Disabling Class A MAC commands -`DISABLE_MCMD_DCAP_REQ`, `DISABLE_MCMD_DN2P_SET`, and `DISABLE_MCMD_SNCH_REQ` respectively disable code for various Class A MAC +`DISABLE_MCMD_DutyCycleReq`, `DISABLE_MCMD_RXParamSetupReq`, and `DISABLE_MCMD_NewChannelReq` respectively disable code for various Class A MAC commands. #### Disabling Class B MAC commands -`DISABLE_MCMD_PING_SET` disables the PING_SET MAC commands. It's implied by `DISABLE_PING`. +`DISABLE_MCMD_PingSlotChannelReq` disables the PING_SET MAC commands. It's implied by `DISABLE_PING`. -`DISABLE_MCMD_BCNI_ANS` disables the next-beacon start command. It's implied by `DISABLE_BEACON` +`ENABLE_MCMD_BeaconTimingAns` enables the next-beacon start command. It's disabled by default, and overridden (if enabled) by `DISABLE_BEACON`. (This command is deprecated.) #### Disabling user events diff --git a/src/lmic/config.h b/src/lmic/config.h index 32e30a92..2e59ab59 100644 --- a/src/lmic/config.h +++ b/src/lmic/config.h @@ -116,12 +116,12 @@ // define these in lmic_project_config.h to disable the corresponding MAC commands. // Class A -//#define DISABLE_MCMD_DCAP_REQ // duty cycle cap -//#define DISABLE_MCMD_DN2P_SET // 2nd DN window param -//#define DISABLE_MCMD_SNCH_REQ // set new channel +//#define DISABLE_MCMD_DutyCycleReq // duty cycle cap +//#define DISABLE_MCMD_RXParamSetupReq // 2nd DN window param +//#define DISABLE_MCMD_NewChannelReq // set new channel // Class B -//#define DISABLE_MCMD_PING_SET // set ping freq, automatically disabled by DISABLE_PING -//#define DISABLE_MCMD_BCNI_ANS // next beacon start, automatically disabled by DISABLE_BEACON +//#define DISABLE_MCMD_PingSlotChannelReq // set ping freq, automatically disabled by DISABLE_PING +//#define ENABLE_MCMD_BeaconTimingAns // next beacon start, DEPRECATED, normally disabled by DISABLE_BEACON // DEPRECATED(tmm@mcci.com); replaced by LMIC.noRXIQinversion (dynamic). Don't define this. //#define DISABLE_INVERT_IQ_ON_RX diff --git a/src/lmic/lmic.c b/src/lmic/lmic.c index f2f0e690..fd539b7c 100644 --- a/src/lmic/lmic.c +++ b/src/lmic/lmic.c @@ -591,19 +591,13 @@ static void resetJoinParams(void) { static void stateJustJoined (void) { LMIC.seqnoDn = LMIC.seqnoUp = 0; LMIC.rejoinCnt = 0; - LMIC.dnConf = LMIC.adrChanged = LMIC.ladrAns = LMIC.devsAns = 0; -#if !defined(DISABLE_MCMD_SNCH_REQ) - LMIC.snchAns = 0; -#endif -#if !defined(DISABLE_MCMD_DN2P_SET) + LMIC.dnConf = LMIC.adrChanged = 0; +#if !defined(DISABLE_MCMD_RXParamSetupReq) LMIC.dn2Ans = 0; #endif LMIC.moreData = 0; -#if !defined(DISABLE_MCMD_DCAP_REQ) +#if !defined(DISABLE_MCMD_DutyCycleReq) LMIC.dutyCapAns = 0; -#endif -#if !defined(DISABLE_MCMD_PING_SET) && !defined(DISABLE_PING) - LMIC.pingSetAns = 0; #endif LMIC.upRepeat = 0; resetJoinParams(); @@ -653,6 +647,36 @@ static int decodeBeacon (void) { } #endif // !DISABLE_BEACONS +// put a mac response to the current output buffer. +static bit_t put_mac_uplink_byte(uint8_t b) { + uint8_t *pEnd; + uint8_t *pBuffer; + + if (LMIC.pendMacLen < 0) { + if (LMIC.pendTxLen < sizeof(LMIC.pendTxData)) { + LMIC.pendTxData[LMIC.pendTxLen++] = b; + return 1; + } else { + return 0; + } + } else { + if (LMIC.pendMacLen < sizeof(LMIC.pendMacData)) { + LMIC.pendMacData[LMIC.pendMacLen++] = b; + return 1; + } else { + return 0; + } + } +} + +static bit_t put_mac_uplink_byte2(uint8_t b1, uint8_t b2) { + return put_mac_uplink_byte(b1) && put_mac_uplink_byte(b2); +} + +static bit_t put_mac_uplink_byte3(u1_t b1, u1_t b2, u1_t b3) { + return put_mac_uplink_byte(b1) && put_mac_uplink_byte2(b2, b3); +} + static CONST_TABLE(u1_t, macCmdSize)[] = { /* 2: LinkCheckAns */ 3, /* 3: LinkADRReq */ 5, @@ -667,7 +691,7 @@ static CONST_TABLE(u1_t, macCmdSize)[] = { /* 0x0D: DeviceTimeAns */ 6, /* 0x0E, 0x0F */ 0, 0, /* 0x10: PingSlotInfoAns */ 1, - /* 0x11: PingSlotChannelReq */ 4, + /* 0x11: PingSlotChannelReq */ 5, /* 0x12: BeaconTimingAns */ 4, /* 0x13: BeaconFreqReq */ 4 }; @@ -683,138 +707,184 @@ static u1_t getMacCmdSize(u1_t macCmd) { static void applyAdrRequests( const uint8_t *opts, - int olen + int olen, + u1_t adrAns ) { - if( (LMIC.ladrAns & 0x7F) == (MCMD_LADR_ANS_POWACK | MCMD_LADR_ANS_CHACK | MCMD_LADR_ANS_DRACK) ) { - lmic_saved_adr_state_t initialState; - int oidx; - u1_t p1 = 0; - u1_t p4 = 0; + lmic_saved_adr_state_t initialState; + int const kAdrReqSize = 5; + int oidx; + u1_t p1 = 0; + u1_t p4 = 0; - LMICbandplan_saveAdrState(&initialState); + LMICbandplan_saveAdrState(&initialState); - for (oidx = 0; oidx < olen; ) { - u1_t const cmd = opts[oidx]; + for (oidx = 0; oidx < olen; ) { + if (adrAns == MCMD_LinkADRAns_PowerACK | MCMD_LinkADRAns_DataRateACK | MCMD_LinkADRAns_ChannelACK) { + u2_t chmap = os_rlsbf2(&opts[oidx+2]);// list of enabled channels - if (cmd == MCMD_LADR_REQ) { - u2_t chmap = os_rlsbf2(&opts[oidx+2]);// list of enabled channels + p1 = opts[oidx+1]; // txpow + DR, in case last + p4 = opts[oidx+4]; // ChMaskCtl, NbTrans + u1_t chpage = p4 & MCMD_LADR_CHPAGE_MASK; // channel page - p1 = opts[oidx+1]; // txpow + DR, in case last - p4 = opts[oidx+4]; - u1_t chpage = p4 & MCMD_LADR_CHPAGE_MASK; // channel page + LMICbandplan_mapChannels(chpage, chmap); + } - LMICbandplan_mapChannels(chpage, chmap); - } + oidx += kAdrReqSize; + put_mac_uplink_byte2(MCMD_LinkADRAns, adrAns); + } - int cmdlen = getMacCmdSize(cmd); + // all done scanning options + bit_t changes = LMICbandplan_compareAdrState(&initialState); - // this really is an assert, we should never here - // unless all the commands are valid. - ASSERT(cmdlen != 0); + // handle uplink repeat count + u1_t uprpt = p4 & MCMD_LADR_REPEAT_MASK; // up repeat count + if (LMIC.upRepeat != uprpt) { + LMIC.upRepeat = uprpt; + changes = 1; + } - oidx += cmdlen; + if (adrAns & MCMD_LinkADRAns_DataRateACK) { + dr_t dr = (dr_t)(p1>>MCMD_LADR_DR_SHIFT); + + // handle power changes here, too. + changes |= setDrTxpow(DRCHG_NWKCMD, dr, pow2dBm(p1)); + } + + LMIC.adrChanged = changes; // move the ADR FSM up to "time to request" +} + + +int scan_mac_cmds_link_adr( + const char *opts, + int olen + ) + { + if (olen == 0) + return 0; + + int oidx = 0; + int const kAdrReqSize = 5; + int lastOidx; + u1_t adrAns = MCMD_LinkADRAns_PowerACK | MCMD_LinkADRAns_DataRateACK | MCMD_LinkADRAns_ChannelACK; + + // process the contiguous slots + for (;;) { + lastOidx = oidx; + + // can we advance? + if (olen - oidx < kAdrReqSize) { + // ignore the malformed one at the end + break; } + u1_t p1 = opts[oidx+1]; // txpow + DR + u2_t chmap = os_rlsbf2(&opts[oidx+2]);// list of enabled channels + u1_t chpage = opts[oidx+4] & MCMD_LADR_CHPAGE_MASK; // channel page + // u1_t uprpt = opts[oidx+4] & MCMD_LADR_REPEAT_MASK; // up repeat count + dr_t dr = (dr_t)(p1>>MCMD_LADR_DR_SHIFT); - // all done scanning options - bit_t changes = LMICbandplan_compareAdrState(&initialState); + if( !LMICbandplan_canMapChannels(chpage, chmap) ) + adrAns &= ~MCMD_LinkADRAns_ChannelACK; - // handle uplink repeat count - u1_t uprpt = p4 & MCMD_LADR_REPEAT_MASK; // up repeat count - if (LMIC.upRepeat != uprpt) { - LMIC.upRepeat = uprpt; - changes = 1; + if( !validDR(dr) ) { + adrAns &= ~MCMD_LinkADRAns_DataRateACK; + } + if (pow2dBm(p1) == -128) { + adrAns &= ~MCMD_LinkADRAns_PowerACK; } - // handle power changes - dr_t dr = (dr_t)(p1>>MCMD_LADR_DR_SHIFT); - changes |= setDrTxpow(DRCHG_NWKCMD, dr, pow2dBm(p1)); + oidx += kAdrReqSize; + if (opts[oidx] != MCMD_LinkADRReq) + break; + } + + // go back and apply the ADR changes, if any -- use the effective length, + // and process. + applyAdrRequests(opts, lastOidx + kAdrReqSize, adrAns); - LMIC.adrChanged = changes; // move the ADR FSM up to "time to request" + return lastOidx; } -} // scan mac commands starting at opts[] for olen, return count of bytes consumed. +// build response either in pendMacData[] or pendTxData[], depending on whether the +// pointer is before static int scan_mac_cmds( const uint8_t *opts, - int olen + int olen, + int port ) { int oidx = 0; - // this parser is *really* fragile, especially for LinkADR requests. - // it won't crash, but acks will be wrong if all ADR requests are - // not contiguous. bit_t fSawAdrReq = 0; uint8_t cmd; + if (port == 0) + LMIC.pendMacLen = -1; + else + LMIC.pendMacLen = 0; + while( oidx < olen ) { cmd = opts[oidx]; switch( cmd ) { - case MCMD_LCHK_ANS: { + case MCMD_LinkCheckAns: { + // TODO(tmm@mcci.com) capture these, reliably.. //int gwmargin = opts[oidx+1]; //int ngws = opts[oidx+2]; break; } - case MCMD_LADR_REQ: { - u1_t p1 = opts[oidx+1]; // txpow + DR - u2_t chmap = os_rlsbf2(&opts[oidx+2]);// list of enabled channels - u1_t chpage = opts[oidx+4] & MCMD_LADR_CHPAGE_MASK; // channel page - u1_t uprpt = opts[oidx+4] & MCMD_LADR_REPEAT_MASK; // up repeat count - - // TODO(tmm@mcci.com): LoRaWAN 1.1 & 1.0.3 requires us to send one ack - // for each LinkADRReq in a given MAC message. This code only sends - // ack for all the LinkADRReqs. Fixing this is a lot of work, and TTN - // behaves correctly with the current LMIC, so we'll leave this for - // the fix of issue #87. - if (! fSawAdrReq) { - fSawAdrReq = 1; - LMIC.ladrAns = 0x80 | // Include an answer into next frame up - MCMD_LADR_ANS_POWACK | MCMD_LADR_ANS_CHACK | MCMD_LADR_ANS_DRACK; - } - if( !LMICbandplan_canMapChannels(chpage, chmap) ) - LMIC.ladrAns &= ~MCMD_LADR_ANS_CHACK; - dr_t dr = (dr_t)(p1>>MCMD_LADR_DR_SHIFT); - if( !validDR(dr) ) { - LMIC.ladrAns &= ~MCMD_LADR_ANS_DRACK; - EV(specCond, ERR, (e_.reason = EV::specCond_t::BAD_MAC_CMD, - e_.eui = MAIN::CDEV->getEui(), - e_.info = Base::lsbf4(&d[pend]), - e_.info2 = Base::msbf4(&opts[oidx-4]))); - } + // from 1.0.3 spec section 5.2: + // For the purpose of configuring the end-device channel mask, the end-device will + // process all contiguous LinkAdrReq messages, in the order present in the downlink message, + // as a single atomic block command. The end-device will accept or reject all Channel Mask + // controls in the contiguous block, and provide consistent Channel Mask ACK status + // indications for each command in the contiguous block in each LinkAdrAns message, + // reflecting the acceptance or rejection of this atomic channel mask setting. + // + // So we need to process all the contigious commands + case MCMD_LinkADRReq: { + // skip over all but the last command. + oidx += scan_mac_cmds_link_adr(opts + oidx, olen - oidx); break; } - case MCMD_DEVS_REQ: { - LMIC.devsAns = 1; + + case MCMD_DevStatusReq: { // LMIC.snr is SNR time 4, convert to real SNR; rounding towards zero. const int snr = (LMIC.snr + 2) / 4; // per [1.02] 5.5. the margin is the SNR. LMIC.devAnsMargin = (u1_t)(0b00111111 & (snr <= -32 ? -32 : snr >= 31 ? 31 : snr)); + + put_mac_uplink_byte3(MCMD_DevStatusAns, os_getBattLevel(), LMIC.devAnsMargin); break; } - case MCMD_DN2P_SET: { -#if !defined(DISABLE_MCMD_DN2P_SET) + + case MCMD_RXParamSetupReq: { +#if !defined(DISABLE_MCMD_RXParamSetupReq) dr_t dr = (dr_t)(opts[oidx+1] & 0x0F); u1_t rx1DrOffset = (u1_t)((opts[oidx+1] & 0x70) >> 4); u4_t freq = LMICbandplan_convFreq(&opts[oidx+2]); - LMIC.dn2Ans = 0x80; // answer pending + LMIC.dn2Ans = 0xC0; // answer pending, but send this one in order. if( validDR(dr) ) - LMIC.dn2Ans |= MCMD_DN2P_ANS_DRACK; + LMIC.dn2Ans |= MCMD_RXParamSetupAns_RX2DataRateACK; if( freq != 0 ) - LMIC.dn2Ans |= MCMD_DN2P_ANS_CHACK; + LMIC.dn2Ans |= MCMD_RXParamSetupAns_ChannelACK; if (rx1DrOffset <= 3) - LMIC.dn2Ans |= MCMD_DN2P_ANS_RX1DrOffsetAck; + LMIC.dn2Ans |= MCMD_RXParamSetupAns_RX1DrOffsetAck; - if( LMIC.dn2Ans == (0x80|MCMD_DN2P_ANS_DRACK|MCMD_DN2P_ANS_CHACK| MCMD_DN2P_ANS_RX1DrOffsetAck) ) { + if( LMIC.dn2Ans == (0x80|MCMD_RXParamSetupAns_RX2DataRateACK|MCMD_RXParamSetupAns_ChannelACK| MCMD_RXParamSetupAns_RX1DrOffsetAck) ) { LMIC.dn2Dr = dr; LMIC.dn2Freq = freq; LMIC.rx1DrOffset = rx1DrOffset; DO_DEVDB(LMIC.dn2Dr,dn2Dr); DO_DEVDB(LMIC.dn2Freq,dn2Freq); } -#endif // !DISABLE_MCMD_DN2P_SET + + /* put the first copy of the message */ + put_mac_uplink_byte2(MCMD_RXParamSetupAns, LMIC.dn2Ans & ~MCMD_RXParamSetupAns_RFU); +#endif // !DISABLE_MCMD_RXParamSetupReq break; } - case MCMD_DCAP_REQ: { -#if !defined(DISABLE_MCMD_DCAP_REQ) + + case MCMD_DutyCycleReq: { +#if !defined(DISABLE_MCMD_DutyCycleReq) u1_t cap = opts[oidx+1]; // A value cap=0xFF means device is OFF unless enabled again manually. if( cap==0xFF ) @@ -822,38 +892,67 @@ scan_mac_cmds( LMIC.globalDutyRate = cap & 0xF; LMIC.globalDutyAvail = os_getTime(); DO_DEVDB(cap,dutyCap); - LMIC.dutyCapAns = 1; -#endif // !DISABLE_MCMD_DCAP_REQ + + put_mac_uplink_byte(MCMD_DutyCycleAns); +#endif // !DISABLE_MCMD_DutyCycleReq break; } - case MCMD_SNCH_REQ: { -#if !defined(DISABLE_MCMD_SNCH_REQ) + + case MCMD_NewChannelReq: { +#if !defined(DISABLE_MCMD_NewChannelReq) u1_t chidx = opts[oidx+1]; // channel + u4_t raw_feq0 = opts[oidx+2] | opts[oidx+3] | opts[oidx+4]; u4_t freq = LMICbandplan_convFreq(&opts[oidx+2]); // freq u1_t drs = opts[oidx+5]; // datarate span - LMIC.snchAns = 0x80; - if( freq != 0 && LMIC_setupChannel(chidx, freq, DR_RANGE_MAP(drs&0xF,drs>>4), -1) ) - LMIC.snchAns |= MCMD_SNCH_ANS_DRACK|MCMD_SNCH_ANS_FQACK; -#endif // !DISABLE_MCMD_SNCH_REQ + u1_t ans = MCMD_NewChannelAns_DataRateACK|MCMD_NewChannelAns_ChannelACK; + + if (freq == 0 && raw_feq0 != 0) { + ans &= ~MCMD_NewChannelAns_ChannelACK; + } + u1_t MaxDR = drs >> 4; + u1_t MinDR = drs & 0xF; + if (MaxDR < MinDR || !validDR(MaxDR) || validDR(MinDR)) + ans &= ~MCMD_NewChannelAns_DataRateACK; + + if( ans == MCMD_NewChannelAns_DataRateACK|MCMD_NewChannelAns_ChannelACK) { + if ( ! LMIC_setupChannel(chidx, freq, DR_RANGE_MAP(MinDR, MaxDR), -1) ) { + ans &= ~MCMD_NewChannelAns_ChannelACK; + } + } + + put_mac_uplink_byte2(MCMD_NewChannelAns, ans); +#endif // !DISABLE_MCMD_NewChannelReq break; } - case MCMD_PING_SET: { -#if !defined(DISABLE_MCMD_PING_SET) && !defined(DISABLE_PING) + + case MCMD_PingSlotChannelReq: { +#if !defined(DISABLE_MCMD_PingSlotChannelReq) && !defined(DISABLE_PING) + u4_t raw_feq0 = opts[oidx+1] | opts[oidx+2] | opts[oidx+3]; u4_t freq = LMICbandplan_convFreq(&opts[oidx+1]); - u1_t flags = 0x80; - if( freq != 0 ) { - flags |= MCMD_PING_ANS_FQACK; + u1_t dr = opts[oidx+4] & 0xF; + u1_t ans = MCMD_PingSlotFreqAns_DataRateACK|MCMD_PingSlotFreqAns_ChannelACK; + if (raw_feq0) { + freq = FREQ_PING; + } else if (freq == 0) { + ans &= ~MCMD_PingSlotFreqAns_ChannelACK; + } + if (! validDR(dr)) + ans &= ~MCMD_PingSlotFreqAns_DataRateACK; + + if (ans == MCMD_PingSlotFreqAns_DataRateACK|MCMD_PingSlotFreqAns_ChannelACK) { LMIC.ping.freq = freq; + LMIC.ping.dr = dr; DO_DEVDB(LMIC.ping.intvExp, pingIntvExp); DO_DEVDB(LMIC.ping.freq, pingFreq); DO_DEVDB(LMIC.ping.dr, pingDr); } - LMIC.pingSetAns = flags; -#endif // !DISABLE_MCMD_PING_SET && !DISABLE_PING + put_mac_uplink_byte2(MCMD_PingSlotChannelAns, ans); +#endif // !DISABLE_MCMD_PingSlotChannelReq && !DISABLE_PING break; } - case MCMD_BCNI_ANS: { -#if !defined(DISABLE_MCMD_BCNI_ANS) && !defined(DISABLE_BEACONS) + + case MCMD_BeaconTimingAns: { +#if defined(ENABLE_MCMD_BeaconTimingAns) && !defined(DISABLE_BEACONS) // Ignore if tracking already enabled if( (LMIC.opmode & OP_TRACK) == 0 ) { LMIC.bcnChnl = opts[oidx+3]; @@ -863,13 +962,13 @@ scan_mac_cmds( ASSERT(LMIC.bcninfoTries!=0); // Setup RX parameters LMIC.bcninfo.txtime = (LMIC.rxtime - + ms2osticks(os_rlsbf2(&opts[oidx+1]) * MCMD_BCNI_TUNIT) - + ms2osticksCeil(MCMD_BCNI_TUNIT/2) + + ms2osticks(os_rlsbf2(&opts[oidx+1]) * MCMD_BeaconTimingAns_TUNIT) + + ms2osticksCeil(MCMD_BeaconTimingAns_TUNIT/2) - BCN_INTV_osticks); LMIC.bcninfo.flags = 0; // txtime above cannot be used as reference (BCN_PARTIAL|BCN_FULL cleared) - calcBcnRxWindowFromMillis(MCMD_BCNI_TUNIT,1); // error of +/-N ms + calcBcnRxWindowFromMillis(MCMD_BeaconTimingAns_TUNIT,1); // error of +/-N ms - EV(lostFrame, INFO, (e_.reason = EV::lostFrame_t::MCMD_BCNI_ANS, + EV(lostFrame, INFO, (e_.reason = EV::lostFrame_t::MCMD_BeaconTimingAns, e_.eui = MAIN::CDEV->getEui(), e_.lostmic = Base::lsbf4(&d[pend]), e_.info = (LMIC.missedBcns | @@ -877,23 +976,25 @@ scan_mac_cmds( - LMIC.bcnRxtime) << 8)), e_.time = MAIN::CDEV->ostime2ustime(LMIC.bcninfo.txtime + BCN_INTV_osticks))); } -#endif // !DISABLE_MCMD_BCNI_ANS && !DISABLE_BEACONS +#endif // !ENABLE_MCMD_BeaconTimingAns && !DISABLE_BEACONS break; } /* end case */ + case MCMD_TxParamSetupReq: { #if LMIC_ENABLE_TxParamSetupReq uint8_t txParam; txParam = opts[oidx+1]; - // we don't allow unrecognized bits to come through + // we don't allow unrecognized bits to get to txParam. txParam &= (MCMD_TxParam_RxDWELL_MASK| MCMD_TxParam_TxDWELL_MASK| MCMD_TxParam_MaxEIRP_MASK); LMIC.txParam = txParam; - LMIC.txParamSetupAns = 1; + put_mac_uplink_byte(MCMD_TxParamSetupAns); #endif // LMIC_ENABLE_TxParamSetupReq break; } /* end case */ + case MCMD_DeviceTimeAns: { #if LMIC_ENABLE_DeviceTimeReq // don't process a spurious downlink. @@ -944,10 +1045,6 @@ scan_mac_cmds( oidx += cmdlen; } /* end while */ - // go back and apply the ADR changes, if any -- use the effective length - if (fSawAdrReq) - applyAdrRequests(opts, olen); - return oidx; } @@ -1060,19 +1157,30 @@ static bit_t decodeFrame (void) { // We heard from network LMIC.adrChanged = LMIC.rejoinCnt = 0; setAdrAckCount(LINK_CHECK_INIT); +#if !defined(DISABLE_MCMD_RXParamSetupReq) + LMIC.dn2Ans = 0; +#endif // !defined(DISABLE_MCMD_RXParamSetupReq) int m = LMIC.rssi - RSSI_OFF - getSensitivity(LMIC.rps); // for legacy reasons, LMIC.margin is set to the unsigned sensitivity. It can never be negative. // it's only computed for legacy clients LMIC.margin = m < 0 ? 0 : m > 254 ? 254 : m; + if (port == 0 && olen != 0) { #if LMIC_DEBUG_LEVEL > 0 - // Process OPTS - LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": process options (olen=%#x)\n", os_getTime(), olen); + // discard, section 4.3.1.6 line 544-546 + LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": port==0 && FOptsLen=%#x: discard\n", os_getTime(), olen); #endif + goto norx; + } else { +#if LMIC_DEBUG_LEVEL > 0 + // Process OPTS + LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": process options (olen=%#x)\n", os_getTime(), olen); +#endif + } xref2u1_t opts = &d[OFF_DAT_OPTS]; - int oidx = scan_mac_cmds(opts, olen); + int oidx = scan_mac_cmds(opts, olen, port); if( oidx != olen ) { EV(specCond, ERR, (e_.reason = EV::specCond_t::CORRUPTED_FRAME, e_.eui = MAIN::CDEV->getEui(), @@ -1090,7 +1198,7 @@ static bit_t decodeFrame (void) { #if LMIC_DEBUG_LEVEL > 0 LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": process mac commands for port 0 (olen=%#x)\n", os_getTime(), pend-poff); #endif - int optendindex = scan_mac_cmds(d+poff, pend-poff); + int optendindex = scan_mac_cmds(d+poff, pend-poff, port); if (optendindex != pend-poff) { #if LMIC_DEBUG_LEVEL > 0 LMIC_DEBUG_PRINTF( @@ -1522,41 +1630,26 @@ static void buildDataFrame (void) { // Piggyback MAC options // Prioritize by importance + // highest importance are the ones in the pendMac buffer. int end = OFF_DAT_OPTS; -#if !defined(DISABLE_PING) - if( (LMIC.opmode & (OP_TRACK|OP_PINGABLE)) == (OP_TRACK|OP_PINGABLE) ) { - // Indicate pingability in every UP frame - LMIC.frame[end] = MCMD_PING_IND; - LMIC.frame[end+1] = LMIC.ping.dr | (LMIC.ping.intvExp<<4); - end += 2; - } -#endif // !DISABLE_PING -#if !defined(DISABLE_MCMD_DCAP_REQ) - if( LMIC.dutyCapAns ) { - LMIC.frame[end] = MCMD_DCAP_ANS; - end += 1; - LMIC.dutyCapAns = 0; - } -#endif // !DISABLE_MCMD_DCAP_REQ - if( LMIC.devsAns ) { // answer to device status - LMIC.frame[end+0] = MCMD_DEVS_ANS; - LMIC.frame[end+1] = os_getBattLevel(); - LMIC.frame[end+2] = LMIC.devAnsMargin; - end += 3; - LMIC.devsAns = 0; - } - if( LMIC.ladrAns ) { // answer to ADR change - LMIC.frame[end+0] = MCMD_LADR_ANS; - LMIC.frame[end+1] = LMIC.ladrAns & ~MCMD_LADR_ANS_RFU; - end += 2; - LMIC.ladrAns = 0; + + if (LMIC.pendMacLen > 0) { + os_copyMem(LMIC.frame + end, LMIC.pendMacData, LMIC.pendMacLen); + end += LMIC.pendMacLen; + LMIC.pendMacLen = 0; } -#if !defined(DISABLE_BEACONS) - if( LMIC.bcninfoTries > 0 ) { - LMIC.frame[end] = MCMD_BCNI_REQ; - end += 1; +#if !defined(DISABLE_MCMD_RXParamSetupReq) + if (LMIC.dn2Ans) { + if (LMIC.dn2Ans & 0x40) { + LMIC.dn2Ans ^= 0x40; + } else { + LMIC.frame[end + 0] = MCMD_RXParamSetupAns; + LMIC.frame[end + 1] = LMIC.dn2Ans & ~MCMD_RXParamSetupAns_RFU; + end += 2; + } } -#endif // !DISABLE_BEACONS +#endif // !DISABLE_MCMD_RXParamSetupReq + if( LMIC.adrChanged ) { // if ADR is enabled, and we were just counting down the // transmits before starting an ADR, advance the timer so @@ -1565,37 +1658,6 @@ static void buildDataFrame (void) { setAdrAckCount(LINK_CHECK_CONT); LMIC.adrChanged = 0; } -#if !defined(DISABLE_MCMD_DN2P_SET) - if (LMIC.dn2Ans) { - LMIC.frame[end + 0] = MCMD_DN2P_ANS; - LMIC.frame[end + 1] = LMIC.dn2Ans & ~MCMD_DN2P_ANS_RFU; - end += 2; - LMIC.dn2Ans = 0; - } -#endif // !DISABLE_MCMD_DN2P_SET -#if !defined(DISABLE_MCMD_PING_SET) && !defined(DISABLE_PING) - if( LMIC.pingSetAns != 0 ) { - LMIC.frame[end+0] = MCMD_PING_ANS; - LMIC.frame[end+1] = LMIC.pingSetAns & ~MCMD_PING_ANS_RFU; - end += 2; - LMIC.pingSetAns = 0; - } -#endif // !DISABLE_MCMD_PING_SET && !DISABLE_PING -#if !defined(DISABLE_MCMD_SNCH_REQ) - if( LMIC.snchAns ) { - LMIC.frame[end+0] = MCMD_SNCH_ANS; - LMIC.frame[end+1] = LMIC.snchAns & ~MCMD_SNCH_ANS_RFU; - end += 2; - LMIC.snchAns = 0; - } -#endif // !DISABLE_MCMD_SNCH_REQ -#if LMIC_ENABLE_TxParamSetupReq - if ( LMIC.txParamSetupAns ) { - LMIC.frame[end+0] = MCMD_TxParamSetupAns; - end += 1; - LMIC.txParamSetupAns = 0; - } -#endif #if LMIC_ENABLE_DeviceTimeReq if ( LMIC.txDeviceTimeReqState == lmic_RequestTimeState_tx ) { LMIC.frame[end+0] = MCMD_DeviceTimeReq; @@ -1716,7 +1778,7 @@ bit_t LMIC_enableTracking (u1_t tryBcnInfo) { if( (LMIC.opmode & (OP_SCAN|OP_TRACK|OP_SHUTDOWN)) != 0 ) return 0; // already in progress or failed to enable // If BCN info requested from NWK then app has to take are - // of sending data up so that MCMD_BCNI_REQ can be attached. + // of sending data up so that MCMD_BeaconTimingReq can be attached. if( (LMIC.bcninfoTries = tryBcnInfo) == 0 ) startScan(); return 1; // enabled @@ -1950,7 +2012,7 @@ static bit_t processDnData (void) { reportEventNoUpdate(EV_LINK_DEAD); // update? } #if !defined(DISABLE_BEACONS) - // If this falls to zero the NWK did not answer our MCMD_BCNI_REQ commands - try full scan + // If this falls to zero the NWK did not answer our MCMD_BeaconTimingReq commands - try full scan if( LMIC.bcninfoTries > 0 ) { if( (LMIC.opmode & OP_TRACK) != 0 ) { reportEventNoUpdate(EV_BEACON_FOUND); // update? diff --git a/src/lmic/lmic.h b/src/lmic/lmic.h index b9764da4..f2442579 100644 --- a/src/lmic/lmic.h +++ b/src/lmic/lmic.h @@ -399,6 +399,7 @@ struct lmic_t { #if CFG_LMIC_EU_like band_t bands[MAX_BANDS]; u4_t channelFreq[MAX_CHANNELS]; + // bit map of enabled datarates for each channel u2_t channelDrMap[MAX_CHANNELS]; u2_t channelMap; #elif CFG_LMIC_US_like @@ -449,9 +450,13 @@ struct lmic_t { u1_t pendTxPort; u1_t pendTxConf; // confirmed data - u1_t pendTxLen; // +0x80 = confirmed + u1_t pendTxLen; // count of bytes in pendTxData. u1_t pendTxData[MAX_LEN_PAYLOAD]; + s1_t pendMacLen; // number of bytes of pending Mac response data; -1 + // implies port 0. + u1_t pendMacData[LWAN_FCtrl_FOptsLen_MAX]; + u1_t nwkKey[16]; // network session key u1_t artKey[16]; // application router session key @@ -461,19 +466,13 @@ struct lmic_t { u1_t rxDelay; // Rx delay after TX u1_t margin; - bit_t ladrAns; // link adr adapt answer pending - bit_t devsAns; // device status answer pending s1_t devAnsMargin; // SNR value between -32 and 31 (inclusive) for the last successfully received DevStatusReq command u1_t adrEnabled; u1_t moreData; // NWK has more data pending -#if !defined(DISABLE_MCMD_DCAP_REQ) +#if !defined(DISABLE_MCMD_DutyCycleReq) bit_t dutyCapAns; // have to ACK duty cycle settings #endif -#if !defined(DISABLE_MCMD_SNCH_REQ) - u1_t snchAns; // answer set new channel -#endif #if LMIC_ENABLE_TxParamSetupReq - bit_t txParamSetupAns; // transmit setup answer pending. u1_t txParam; // the saved TX param byte. #endif #if LMIC_ENABLE_DeviceTimeReq @@ -486,7 +485,7 @@ struct lmic_t { // 2nd RX window (after up stream) u1_t dn2Dr; -#if !defined(DISABLE_MCMD_DN2P_SET) +#if !defined(DISABLE_MCMD_RXParamSetupReq) u1_t dn2Ans; // 0=no answer pend, 0x80+ACKs #endif @@ -494,9 +493,6 @@ struct lmic_t { #if !defined(DISABLE_BEACONS) u1_t missedBcns; // unable to track last N beacons u1_t bcninfoTries; // how often to try (scan mode only) -#endif -#if !defined(DISABLE_MCMD_PING_SET) && !defined(DISABLE_PING) - u1_t pingSetAns; // answer set cmd and ACK bits #endif // Public part of MAC state u1_t txCnt; diff --git a/src/lmic/lmic_as923.c b/src/lmic/lmic_as923.c index 5050ff79..ad478dd4 100644 --- a/src/lmic/lmic_as923.c +++ b/src/lmic/lmic_as923.c @@ -110,7 +110,6 @@ static CONST_TABLE(s1_t, TXPOWLEVELS)[] = { -10, // [5]: MaxEIRP - 10dB -12, // [6]: MaxEIRP - 12dB -14, // [7]: MaxEIRP - 14dB - 0, 0, 0, 0, 0, 0, 0, 0 }; // from LoRaWAN 5.8: mapping from txParam to MaxEIRP @@ -130,15 +129,20 @@ static int8_t LMICas923_getMaxEIRP(uint8_t mcmd_txparam) { } // translate from an encoded power to an actual power using -// the maxeirp setting. +// the maxeirp setting; return -128 if not legal. int8_t LMICas923_pow2dBm(uint8_t mcmd_ladr_p1) { - s1_t const adj = - TABLE_GET_S1( - TXPOWLEVELS, - (mcmd_ladr_p1&MCMD_LADR_POW_MASK)>>MCMD_LADR_POW_SHIFT - ); - - return LMICas923_getMaxEIRP(LMIC.txParam) + adj; + uint8_t const pindex = (mcmd_ladr_p1&MCMD_LADR_POW_MASK)>>MCMD_LADR_POW_SHIFT; + if (pindex < sizeof(TXPOWLEVELS)) { + s1_t const adj = + TABLE_GET_S1( + TXPOWLEVELS, + pindex + ); + + return LMICas923_getMaxEIRP(LMIC.txParam) + adj; + } else { + return -128; + } } // only used in this module, but used by variant macro dr2hsym(). @@ -217,6 +221,15 @@ bit_t LMIC_setupBand(u1_t bandidx, s1_t txpow, u2_t txcap) { } bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { + if (chidx < NUM_DEFAULT_CHANNELS) { + // can't disable a default channel. + if (freq == 0) + return 0; + // can't change a default channel. + else if (freq != (LMIC.channelFreq[chidx] & ~3)) + return 0; + } + bit_t fEnable = (freq != 0); if (chidx >= MAX_CHANNELS) return 0; if (band == -1) { @@ -229,7 +242,10 @@ bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { LMIC.channelDrMap[chidx] = drmap == 0 ? DR_RANGE_MAP(AS923_DR_SF12, AS923_DR_SF7B) : drmap; - LMIC.channelMap |= 1 << chidx; // enabled right away + if (fEnable) + LMIC.channelMap |= 1 << chidx; // enabled right away + else + LMIC.channelMap &= ~(1 << chidx); return 1; } diff --git a/src/lmic/lmic_au921.c b/src/lmic/lmic_au921.c index a6d0fdbc..8cfa3957 100644 --- a/src/lmic/lmic_au921.c +++ b/src/lmic/lmic_au921.c @@ -66,6 +66,13 @@ uint8_t LMICau921_maxFrameLen(uint8_t dr) { return 0xFF; } +int8_t LMICau921_pow2dbm(uint8_t mcmd_ladr_p1) { + if ((mcmd_ladr_p1 & MCMD_LADR_POW_MASK) == MCMD_LADR_POW_MASK) + return -128; + else + return ((s1_t)(30 - (((mcmd_ladr_p1)&MCMD_LADR_POW_MASK)<<1))); +} + static CONST_TABLE(ostime_t, DR2HSYM_osticks)[] = { us2osticksRound(128 << 7), // DR_SF12 us2osticksRound(128 << 6), // DR_SF11 diff --git a/src/lmic/lmic_bandplan_au921.h b/src/lmic/lmic_bandplan_au921.h index 560a1f51..4c439ad5 100644 --- a/src/lmic/lmic_bandplan_au921.h +++ b/src/lmic/lmic_bandplan_au921.h @@ -40,7 +40,8 @@ uint8_t LMICau921_maxFrameLen(uint8_t dr); #define maxFrameLen(dr) LMICau921_maxFrameLen(dr) -#define pow2dBm(mcmd_ladr_p1) ((s1_t)(30 - (((mcmd_ladr_p1)&MCMD_LADR_POW_MASK)<<1))) +int8_t LMICau921_pow2dbm(uint8_t mcmd_ladr_p1); +#define pow2dBm(mcmd_ladr_p1) LMICau921_pow2dbm(mcmd_ladr_p1) ostime_t LMICau921_dr2hsym(uint8_t dr); #define dr2hsym(dr) LMICau921_dr2hsym(dr) diff --git a/src/lmic/lmic_bandplan_us915.h b/src/lmic/lmic_bandplan_us915.h index f6870959..28ca69bb 100644 --- a/src/lmic/lmic_bandplan_us915.h +++ b/src/lmic/lmic_bandplan_us915.h @@ -40,7 +40,8 @@ uint8_t LMICus915_maxFrameLen(uint8_t dr); #define maxFrameLen(dr) LMICus915_maxFrameLen(dr) -#define pow2dBm(mcmd_ladr_p1) ((s1_t)(US915_TX_MAX_DBM - (((mcmd_ladr_p1)&MCMD_LADR_POW_MASK)<<1))) +int8_t LMICus915_pow2dbm(uint8_t mcmd_ladr_p1); +#define pow2dBm(mcmd_ladr_p1) LMICus915_pow2dbm(mcmd_ladr_p1) ostime_t LMICus915_dr2hsym(uint8_t dr); #define dr2hsym(dr) LMICus915_dr2hsym(dr) diff --git a/src/lmic/lmic_eu868.c b/src/lmic/lmic_eu868.c index e47082b2..f7d81883 100644 --- a/src/lmic/lmic_eu868.c +++ b/src/lmic/lmic_eu868.c @@ -59,11 +59,16 @@ uint8_t LMICeu868_maxFrameLen(uint8_t dr) { } static CONST_TABLE(s1_t, TXPOWLEVELS)[] = { - 16, 14, 12, 10, 8, 6, 4, 2, 0,0,0,0, 0,0,0,0 + 16, 14, 12, 10, 8, 6, 4, 2 }; int8_t LMICeu868_pow2dBm(uint8_t mcmd_ladr_p1) { - return TABLE_GET_S1(TXPOWLEVELS, (mcmd_ladr_p1&MCMD_LADR_POW_MASK)>>MCMD_LADR_POW_SHIFT); + uint8_t const pindex = (mcmd_ladr_p1&MCMD_LADR_POW_MASK)>>MCMD_LADR_POW_SHIFT; + if (pindex < sizeof(constant_table_TXPOWLEVELS)) { + return TABLE_GET_S1(TXPOWLEVELS, pindex); + } else { + return -128; + } } // only used in this module, but used by variant macro dr2hsym(). @@ -130,6 +135,15 @@ bit_t LMIC_setupBand(u1_t bandidx, s1_t txpow, u2_t txcap) { } bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { + if (chidx < NUM_DEFAULT_CHANNELS) { + // can't disable a default channel. + if (freq == 0) + return 0; + // can't change a default channel. + else if (freq != (LMIC.channelFreq[chidx] & ~3)) + return 0; + } + bit_t fEnable = (freq != 0); if (chidx >= MAX_CHANNELS) return 0; if (band == -1) { @@ -146,9 +160,11 @@ bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { freq = (freq&~3) | band; } LMIC.channelFreq[chidx] = freq; - // TODO(tmm@mcci.com): don't use US SF directly, use something from the LMIC context or a static const LMIC.channelDrMap[chidx] = drmap == 0 ? DR_RANGE_MAP(EU868_DR_SF12, EU868_DR_SF7) : drmap; - LMIC.channelMap |= 1 << chidx; // enabled right away + if (fEnable) + LMIC.channelMap |= 1 << chidx; // enabled right away + else + LMIC.channelMap &= ~(1 << chidx); return 1; } diff --git a/src/lmic/lmic_in866.c b/src/lmic/lmic_in866.c index 1788ca4d..a4e840f4 100644 --- a/src/lmic/lmic_in866.c +++ b/src/lmic/lmic_in866.c @@ -59,11 +59,16 @@ uint8_t LMICin866_maxFrameLen(uint8_t dr) { } static CONST_TABLE(s1_t, TXPOWLEVELS)[] = { - 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 0, 0,0,0,0 + 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10 }; int8_t LMICin866_pow2dBm(uint8_t mcmd_ladr_p1) { - return TABLE_GET_S1(TXPOWLEVELS, (mcmd_ladr_p1&MCMD_LADR_POW_MASK)>>MCMD_LADR_POW_SHIFT); + uint8_t const pindex = (mcmd_ladr_p1&MCMD_LADR_POW_MASK)>>MCMD_LADR_POW_SHIFT; + if (pindex < sizeof(constant_table_TXPOWLEVELS)) { + return TABLE_GET_S1(TXPOWLEVELS, pindex); + } else { + return -128; + } } // only used in this module, but used by variant macro dr2hsym(). @@ -125,17 +130,29 @@ bit_t LMIC_setupBand(u1_t bandidx, s1_t txpow, u2_t txcap) { } bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { + if (chidx < NUM_DEFAULT_CHANNELS) { + // can't disable a default channel. + if (freq == 0) + return 0; + // can't change a default channel. + else if (freq != (LMIC.channelFreq[chidx] & ~3)) + return 0; + } + bit_t fEnable = (freq != 0); if (chidx >= MAX_CHANNELS) return 0; if (band == -1) { - freq |= BAND_MILLI; + freq = (freq&~3) | BAND_MILLI; } else { if (band > BAND_MILLI) return 0; freq = (freq&~3) | band; } LMIC.channelFreq[chidx] = freq; LMIC.channelDrMap[chidx] = drmap == 0 ? DR_RANGE_MAP(IN866_DR_SF12, IN866_DR_SF7) : drmap; - LMIC.channelMap |= 1 << chidx; // enabled right away + if (fEnable) + LMIC.channelMap |= 1 << chidx; // enabled right away + else + LMIC.channelMap &= ~(1 << chidx); return 1; } diff --git a/src/lmic/lmic_kr920.c b/src/lmic/lmic_kr920.c index 468f9773..874b3626 100644 --- a/src/lmic/lmic_kr920.c +++ b/src/lmic/lmic_kr920.c @@ -57,11 +57,16 @@ uint8_t LMICkr920_maxFrameLen(uint8_t dr) { } static CONST_TABLE(s1_t, TXPOWLEVELS)[] = { - 14, 12, 10, 8, 6, 4, 2, 0, 0,0,0,0, 0,0,0,0 + 14, 12, 10, 8, 6, 4, 2, 0 }; int8_t LMICkr920_pow2dBm(uint8_t mcmd_ladr_p1) { - return TABLE_GET_S1(TXPOWLEVELS, (mcmd_ladr_p1&MCMD_LADR_POW_MASK)>>MCMD_LADR_POW_SHIFT); + uint8_t const pindex = (mcmd_ladr_p1&MCMD_LADR_POW_MASK)>>MCMD_LADR_POW_SHIFT; + if (pindex < sizeof(constant_table_TXPOWLEVELS)) { + return TABLE_GET_S1(TXPOWLEVELS, pindex); + } else { + return -128; + } } // only used in this module, but used by variant macro dr2hsym(). @@ -136,17 +141,29 @@ bit_t LMIC_setupBand(u1_t bandidx, s1_t txpow, u2_t txcap) { } bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { + if (chidx < NUM_DEFAULT_CHANNELS) { + // can't disable a default channel. + if (freq == 0) + return 0; + // can't change a default channel. + else if (freq != (LMIC.channelFreq[chidx] & ~3)) + return 0; + } + bit_t fEnable = (freq != 0); if (chidx >= MAX_CHANNELS) return 0; if (band == -1) { - freq |= BAND_MILLI; + freq = (freq&~3) | BAND_MILLI; } else { if (band > BAND_MILLI) return 0; freq = (freq&~3) | band; } LMIC.channelFreq[chidx] = freq; LMIC.channelDrMap[chidx] = drmap == 0 ? DR_RANGE_MAP(KR920_DR_SF12, KR920_DR_SF7) : drmap; - LMIC.channelMap |= 1 << chidx; // enabled right away + if (fEnable) + LMIC.channelMap |= 1 << chidx; // enabled right away + else + LMIC.channelMap &= ~(1 << chidx); return 1; } diff --git a/src/lmic/lmic_us915.c b/src/lmic/lmic_us915.c index 1d524d47..d911af2e 100644 --- a/src/lmic/lmic_us915.c +++ b/src/lmic/lmic_us915.c @@ -64,6 +64,13 @@ uint8_t LMICus915_maxFrameLen(uint8_t dr) { return 0xFF; } +int8_t LMICus915_pow2dbm(uint8_t mcmd_ladr_p1) { + if ((mcmd_ladr_p1 & MCMD_LADR_POW_MASK) == MCMD_LADR_POW_MASK) + return -128; + else + return ((s1_t)(US915_TX_MAX_DBM - (((mcmd_ladr_p1)&MCMD_LADR_POW_MASK)<<1))); +} + static CONST_TABLE(ostime_t, DR2HSYM_osticks)[] = { us2osticksRound(128 << 5), // DR_SF10 DR_SF12CR us2osticksRound(128 << 4), // DR_SF9 DR_SF11CR diff --git a/src/lmic/lorabase.h b/src/lmic/lorabase.h index 7b1525a6..5a248904 100644 --- a/src/lmic/lorabase.h +++ b/src/lmic/lorabase.h @@ -485,6 +485,11 @@ enum { // In UP direction: signals class B enabled FCT_CLASSB = FCT_MORE }; + +enum { + LWAN_FCtrl_FOptsLen_MAX = 0x0Fu, // maximum size of embedded MAC commands +}; + enum { NWKID_MASK = (int)0xFE000000, NWKID_BITS = 7 @@ -493,65 +498,69 @@ enum { // MAC uplink commands downwlink too enum { // Class A - MCMD_LCHK_REQ = 0x02, // - LinkCheckReq : - - MCMD_LADR_ANS = 0x03, // - LinkADRAnd : u1:7-3:RFU, 3/2/1: pow/DR/Ch ACK - MCMD_DCAP_ANS = 0x04, // - DutyCycleAns : - - MCMD_DN2P_ANS = 0x05, // - RxParamSetupAns : u1:7-2:RFU 1/0:datarate/channel ack - MCMD_DEVS_ANS = 0x06, // - DevStatusAns : u1:battery 0,1-254,255=?, u1:7-6:RFU,5-0:margin(-32..31) - MCMD_SNCH_ANS = 0x07, // - NewChannelAns : u1: 7-2=RFU, 1/0:DR/freq ACK - MCMD_RXTimingSetupAns = 0x08, // : - - MCMD_TxParamSetupAns = 0x09, // : - - MCMD_DIChannelAns = 0x0A, // : u1: [7-2]:RFU 1:exists 0:OK - MCMD_DeviceTimeReq = 0x0D, + MCMD_LinkCheckReq = 0x02, // - + MCMD_LinkADRAns = 0x03, // u1:7-3:RFU, 3/2/1: pow/DR/Ch ACK + MCMD_DutyCycleAns = 0x04, // - + MCMD_RXParamSetupAns = 0x05, // u1:7-2:RFU 1/0:datarate/channel ack + MCMD_DevStatusAns = 0x06, // u1:battery 0,1-254,255=?, u1:7-6:RFU,5-0:margin(-32..31) + MCMD_NewChannelAns = 0x07, // u1: 7-2=RFU, 1/0:DR/freq ACK + MCMD_RXTimingSetupAns = 0x08, // - + MCMD_TxParamSetupAns = 0x09, // - + MCMD_DIChannelAns = 0x0A, // u1: [7-2]:RFU 1:exists 0:OK + MCMD_DeviceTimeReq = 0x0D, // - // Class B - MCMD_PING_IND = 0x10, // - pingability indic : u1: 7=RFU, 6-4:interval, 3-0:datarate - MCMD_PING_ANS = 0x11, // - ack ping freq : u1: 7-1:RFU, 0:freq ok - MCMD_BCNI_REQ = 0x12, // - next beacon start : - + MCMD_PingSlotInfoReq = 0x10, // u1: 7=RFU, 6-4:interval, 3-0:datarate + MCMD_PingSlotChannelAns = 0x11, // u1: 7-1:RFU, 0:freq ok + MCMD_BeaconTimingReq = 0x12, // - (DEPRECATED) + MCMD_BeaconFreqAns = 0x13, // u1: 7-1:RFU, 0:freq ok }; // MAC downlink commands enum { // Class A - MCMD_LCHK_ANS = 0x02, // LinkCheckAns : u1:margin 0-254,255=unknown margin / u1:gwcnt LinkCheckReq - MCMD_LADR_REQ = 0x03, // LinkADRReq : u1:DR/TXPow, u2:chmask, u1:chpage/repeat - MCMD_DCAP_REQ = 0x04, // DutyCycleReq : u1:255 dead [7-4]:RFU, [3-0]:cap 2^-k - MCMD_DN2P_SET = 0x05, // RXParamSetupReq : u1:7-4:RFU/3-0:datarate, u3:freq - MCMD_DEVS_REQ = 0x06, // DevStatusReq : - - MCMD_SNCH_REQ = 0x07, // NewChannelReq : u1:chidx, u3:freq, u1:DRrange - MCMD_RXTimingSetupReq = 0x08, // : u1: [7-4]:RFU [3-0]: Delay 1-15s (0 => 1) - MCMD_TxParamSetupReq = 0x09, // : u1: [7-6]:RFU [5:4]: dl dwell/ul dwell [3:0] max EIRP - MCMD_DIChannelReq = 0x0A, // : u1: channel, u3: frequency - MCMD_DeviceTimeAns = 0x0D, + MCMD_LinkCheckAns = 0x02, // u1:margin 0-254,255=unknown margin / u1:gwcnt LinkCheckReq + MCMD_LinkADRReq = 0x03, // u1:DR/TXPow, u2:chmask, u1:chpage/repeat + MCMD_DutyCycleReq = 0x04, // u1:255 dead [7-4]:RFU, [3-0]:cap 2^-k + MCMD_RXParamSetupReq = 0x05, // u1:7-4:RFU/3-0:datarate, u3:freq + MCMD_DevStatusReq = 0x06, // - + MCMD_NewChannelReq = 0x07, // u1:chidx, u3:freq, u1:DRrange + MCMD_RXTimingSetupReq = 0x08, // u1: [7-4]:RFU [3-0]: Delay 1-15s (0 => 1) + MCMD_TxParamSetupReq = 0x09, // u1: [7-6]:RFU [5:4]: dl dwell/ul dwell [3:0] max EIRP + MCMD_DIChannelReq = 0x0A, // u1: channel, u3: frequency + MCMD_DeviceTimeAns = 0x0D, // u4: seconds since epoch, u1: fractional second // Class B - MCMD_PING_SET = 0x11, // set ping freq : u3: freq - MCMD_BCNI_ANS = 0x12, // next beacon start : u2: delay(in TUNIT millis), u1:channel + MCMD_PingSlotInfoAns = 0x10, // - + MCMD_PingSlotChannelReq = 0x11, // u3: freq, u1:dr [7-4]:RFU [3:0]:datarate + MCMD_BeaconTimingAns = 0x12, // u2: delay(in TUNIT millis), u1:channel (DEPRECATED) + MCMD_BeaconFreqReq = 0x13, // u3: freq }; enum { - MCMD_BCNI_TUNIT = 30 // time unit of delay value in millis + MCMD_BeaconTimingAns_TUNIT = 30 // time unit of delay value in millis }; enum { - MCMD_LADR_ANS_RFU = 0xF8, // RFU bits - MCMD_LADR_ANS_POWACK = 0x04, // 0=not supported power level - MCMD_LADR_ANS_DRACK = 0x02, // 0=unknown data rate - MCMD_LADR_ANS_CHACK = 0x01, // 0=unknown channel enabled + MCMD_LinkADRAns_RFU = 0xF8, // RFU bits + MCMD_LinkADRAns_PowerACK = 0x04, // 0=not supported power level + MCMD_LinkADRAns_DataRateACK = 0x02, // 0=unknown data rate + MCMD_LinkADRAns_ChannelACK = 0x01, // 0=unknown channel enabled }; enum { - MCMD_DN2P_ANS_RFU = 0xF8, // RFU bits - MCMD_DN2P_ANS_RX1DrOffsetAck = 0x04, // 0=dr2 not allowed - MCMD_DN2P_ANS_DRACK = 0x02, // 0=unknown data rate - MCMD_DN2P_ANS_CHACK = 0x01, // 0=unknown channel enabled + MCMD_RXParamSetupAns_RFU = 0xF8, // RFU bits + MCMD_RXParamSetupAns_RX1DrOffsetAck = 0x04, // 0=dr2 not allowed + MCMD_RXParamSetupAns_RX2DataRateACK = 0x02, // 0=unknown data rate + MCMD_RXParamSetupAns_ChannelACK = 0x01, // 0=unknown channel enabled }; enum { - MCMD_SNCH_ANS_RFU = 0xFC, // RFU bits - MCMD_SNCH_ANS_DRACK = 0x02, // 0=unknown data rate - MCMD_SNCH_ANS_FQACK = 0x01, // 0=rejected channel frequency + MCMD_NewChannelAns_RFU = 0xFC, // RFU bits + MCMD_NewChannelAns_DataRateACK = 0x02, // 0=unknown data rate + MCMD_NewChannelAns_ChannelACK = 0x01, // 0=rejected channel frequency }; enum { - MCMD_PING_ANS_RFU = 0xFE, - MCMD_PING_ANS_FQACK = 0x01 + MCMD_PingSlotFreqAns_RFU = 0xFC, + MCMD_PingSlotFreqAns_DataRateACK = 0x02, + MCMD_PingSlotFreqAns_ChannelACK = 0x01, }; enum { @@ -561,7 +570,7 @@ enum { MCMD_DEVS_BATT_NOINFO = 0xFF, // unknown battery level }; -// Bit fields byte#3 of MCMD_LADR_REQ payload +// Bit fields byte#3 of MCMD_LinkADRReq payload enum { MCMD_LADR_CHP_USLIKE_SPECIAL = 0x50, // first special for us-like MCMD_LADR_CHP_BANK = 0x50, // special: bits are banks. @@ -573,7 +582,7 @@ enum { MCMD_LADR_REPEAT_1 = 0x01, MCMD_LADR_CHPAGE_1 = 0x10 }; -// Bit fields byte#0 of MCMD_LADR_REQ payload +// Bit fields byte#0 of MCMD_LinkADRReq payload enum { MCMD_LADR_DR_MASK = 0xF0, MCMD_LADR_POW_MASK = 0x0F,