Files
openbts/GPRS/RLCEngine.cpp
2014-07-16 23:57:22 +02:00

1332 lines
50 KiB
C++

/*
* Copyright 2011, 2014 Range Networks, Inc.
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
* information for this specific distribution.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#define LOG_GROUP LogGroup::GPRS // Can set Log.Level.GPRS for debugging
// File contains RLCUpEngine and RLCDownEngine, which are classes that extend
// TBF (Temporary Block Flow) to do the data block uplink/downlink movement functions for a TBF.
#include "BSSG.h"
#include "RLCEngine.h"
#include "TBF.h"
#include "MAC.h"
#include "FEC.h"
#include "Sgsn.h"
#include "RLCMessages.h"
namespace GPRS {
// If WaitForStall is true, a stalled TBF will send only one block at a time
// until it gets a response from the MS.
// If false, stalled downlink TBFs transfer the blocks continually
// until the time out.
//static int WaitForStall = false;
/** RLC block size in bits for given coding standard, GSM 04.60 Table 10.2.1, plus MAC header. */
// Index is a ChannelCodingType, 0-3 for CS-1 to CS-4.
const unsigned RLCBlockSizeBytesMax = 53;
unsigned RLCBlockSizeInBits[4] =
{
// (pat) MAC header, plus RLC data block in octets, plus spare bits.
// Table 10.2.1 does not include the 8-bit MAC header, so add 1.
(1+22) * 8 + 0, // CS-1
(1+32) * 8 + 7, // CS-2
(1+38) * 8 + 3, // CS-3
(1+52) * 8 + 7 // CS-4
// (pat) What was here before, but 319 does not appear correct:
// 184, // CS-1 22 octets plus MAC header
// 271, // CS-2 32 octets plus MAC header
// 319, // CS-3 38 octets plus MAC header
// 431 // CS-4 52 octets plus MAC header
};
unsigned RLCPayloadSizeInBytes[4] =
{
// Table 10.2.1 includes the 2 octets for the RLC header, so subtract those out
// for the payload size.
(22-2), // CS-1
(32-2), // CS-2
(38-2), // CS-3
(52-2) // CS-4
};
/*
Arithmetic for the sequence numbers and arrays.
The index for mSt.VB, mSt.TxQ, mSt.VN and mSt.RxQ is straight BSN.
The downengine base index into mSt.VB is mSt.VA.
The upengine base index into mSt.VN us mSt.VQ.
In any of these arrays, the current window is base index through
(base index + mWS) % mSNS.
*/
unsigned RLCEngineBase::addSN(int sn1, int sn2) const // Allow negative numbers.
{
return ((unsigned)((int)sn1 + (int)sn2)) % (unsigned) mSNS;
}
void RLCEngineBase::incSN(unsigned &psn)
{
psn = addSN(psn,1);
}
// Previously this returned -mWS <= result <= +mWS
// Which results in the following, which should return identical numbers, returning:
// deltaSN(64, 0) = 64
// deltaSN(63, 127) = -64
// The above is the all important stall condition.
// I tried to fix this returning a number in the range: -mWS < result <= +mWS,
// but then it failed somewhere else.
// Instead of being so careful around the edge condition, I am switching to deltaSNS
// Note that this is the opposite edge condition of deltaBSN.
int RLCEngineBase::deltaSN(unsigned sn1, unsigned sn2) const
{
int delta = sn1 - sn2;
assert(!(delta >= (int) mSNS));
assert(!(delta <= - (int) mSNS));
if (delta <= -(int) mWS) delta += mSNS; // modulo the sequence space
if (delta > (int) mWS) delta -= mSNS;
return delta;
}
// New delta functions
// Assume that sn1 >= sn2 in modulo arithmetic and do the delta on that basis.
// This function is safer than deltaSN at the edge condition, which is the stall condition.
// For safety we will allow an over-run of one.
// -1 <= result < +mSNS
int RLCEngineBase::deltaSNS(unsigned sn1, unsigned sn2) const
{
int delta = sn1 - sn2;
assert(!(delta >= (int) mSNS));
assert(!(delta <= - (int) mSNS));
if (delta < -1) delta += mSNS; // modulo the sequence space
if (delta >= (int)mSNS) delta -= mSNS;
return delta;
}
// Are the RLC BSNs equal? Using a function is just documentation that
// we are doing a comparison of BSNs.
bool RLCEngineBase::deltaEQ(unsigned sn1, unsigned sn2) const
{
return sn1 == sn2;
}
static bool queueFrontExistsAndIsNotTlliChangeCommand(MSInfo *ms)
{
SGSN::GprsSgsnDownlinkPdu *dlmsg = ms->msDownlinkQueue.front();
if (!dlmsg) { return false; }
if (dlmsg->mTlli != ms->msTlli) {
// We will terminate the downlink TBF and leave this new TBF with
// the TLLI change command sitting in the queue to handle
// at the next time we talk to this MS.
LOGWATCHF("TLLI change command from %x to %x\n",(unsigned)ms->msTlli,(unsigned)dlmsg->mTlli);
return false;
}
return true;
}
// We leave persistent mode when mDownFinished is set.
bool RLCDownEngine::dlPersistentMode() { return !mDownFinished && (gL2MAC.macDownlinkPersist > 0); }
//bool RLCDownEngine::dataAvail() { return mDownPDU.size() || mtMS->msDownlinkQueue.size(); }
bool RLCDownEngine::dataAvail() {
return mDownPDU.size() || queueFrontExistsAndIsNotTlliChangeCommand(mtMS);
}
// Advance mSt.VA forward over blocks that have been previously acked;
// This is the only function permitted to modify VA.
// End condition is VA == TxQNum meaning VA is one beyond last in queue.
void RLCDownEngine::advanceVA()
{
//for (; mSt.VA<mSt.TxQNum && mSt.VB[mSt.VA]; mSt.VA++)
//for (; deltaSNS(mSt.VA,mSt.TxQNum)<0 && mSt.VB[mSt.VA]; incSN(mSt.VA))
for (; !deltaEQ(mSt.VA,mSt.TxQNum) && mSt.VB[mSt.VA]; incSN(mSt.VA)) {
// We must clean up behind ourselves now that the count wraps around.
// Where to do it? We must not delete the final block because it is resent,
// but with persistent mode the final block changes every time there
// is an incoming TBF.
//if (mSt.VB[mSt.VA] && mSt.TxQ[mSt.VA] && addSN(mSt.VA,1) != mSt.TxQNum) {
//delete mSt.TxQ[mSt.VA];
//mSt.TxQ[mSt.VA] = NULL;
//}
}
}
// Do we need to resend this block now?
bool RLCDownEngine::resendNeeded(int bsn)
{
if (mSt.VB[bsn]) { return false; } // block positively acknowledged.
if (mDownStalled) { return true; } // in this case, resend every block ever sent.
// We dont know if bsn is greater than mResendSsn so use deltaSN.
if (deltaSN(bsn,mResendSsn) < 0) {
return true;
} else {
// Even though this block has not been positively acknowledged, the MS has not
// yet had a chance to do so, so dont resend it, ie, it is after the SSN
// of the most recent ack. We may want to modify this for the case
// where the MS does not respond to an ack, and count those blocks as
// needing resend as well? Not sure.
// As of 6-12, when the MS misses an ack it is because it cant hear anything
// so all the blocks need to be resent.
return false;
}
}
// Advance mSt.VS forward to the next unacked block, if any.
// The final condition is we leave VS == TxQNum, which is one greater than the last block.
void RLCDownEngine::advanceVS()
{
// This may set VS to TxQNum, which is the next block that has never been sent yet.
if (!mtUnAckMode) { // if it is ack mode
// We used to advance or positively acknowledged blocks,
// but now we advance over blocks that have not been negatively acknowledged,
// unless we are stalled, in which case we resend
// Changed 6-11-2012
//for (; deltaSN(mSt.VS,mSt.TxQNum)<0 && mSt.VB[mSt.VS]; incSN(mSt.VS)) continue;
for (; !deltaEQ(mSt.VS,mSt.TxQNum) && !resendNeeded(mSt.VS); incSN(mSt.VS)) continue;
// Check for stall:
// Previously, when we allowed the numbers to wrap,
// we also set mDownStalled when the tbf was finished to prevent
// incrementing VS any more, but we go ahead and increment VS one past
// the end so it stops at TxQNum.
#if FAST_TBF
// VS is always >= VA
// Even though we are allowed mWS (64) outstanding blocks,
// we are only going to send only 63 to stay away from the edge condition,
// both for our own code, and possibly for bugs on the MS side as well.
if (deltaSNS(mSt.VS,mSt.VA) >= (int)(mWS-1)) {
mDownStalled = true;
mSt.VS = mSt.VA; // Start over from oldest unacked block.
}
#else
//if ((mSt.VS >= mSt.TxQNum || mSt.VS - mSt.VA >= mWS))
if (mSt.VS >= mSt.TxQNum || deltaSN(mSt.VS,mSt.VA) >= (int)mWS) {
mDownStalled = true;
mSt.VS = mSt.VA; // Start over from oldest unacked block.
}
#endif
}
}
// (pat) Process an AckNack block from the MS (cell phone)
// Here we just mark the blocks that got through ok.
// The serviceloop will resend the blocks via engineService()
void RLCDownEngine::engineRecvAckNack(const RLCMsgPacketDownlinkAckNack *msg)
{
mtRecvAck(MsgTransTransmit); // Make sure.
// mtMS->msN3101 = 0; Removed 6-7: 3101 is for uplink only.
mDownStalled = false; // Until proven otherwise.
// Mark the acks.
mNumDownBlocksSinceAckNack = 0;
const RLCMsgPacketAckNackDescriptionIE& AND = msg->mAND;
if (AND.mFinalAckIndication) {
// All done. We need to ack the entire area covered by the window,
// but we will overkill and ack the entire queue to be safe.
for (unsigned i=0; i<mSNS; i++) { mSt.VB[i] = true; }
mAllAcked = true; // should be redundant with check below.
} else {
// The logic here is really contorted; see comments at engineUpAckNack.
// And I quote: "The bitmap is indexed relative to SSN as follows:
// "BSN = (SSN - bit_number) modulo 128, for bit_number = 1 to 64.
// "The BSN values represented range from (SSN - 1) mod 128 to (SSN - 64) mod 128."
// The bits are numbered backwards in the field from 1 to 64.
// The MS selected the SSN in this message, and it is not necessarily related
// to any of our state variables, except that it must be in range.
// NOTE: SSN is VR in the receiver, which is 1 greater than highest block received.
// This is difficult to test, but I have observed that the MS resends
// the blocks we think it should, so I think this is working.
bool receivedNewAcks = false;
{ unsigned absn = AND.mSSN;
for (int i=1; i<=AND.mbitmapsize; i++) {
absn = addSN(absn,-1);
if (AND.mBitMap[AND.mbitmapsize - i]) {
if (! mSt.VB[absn]) { receivedNewAcks = true; }
mSt.VB[absn] = true;
} else {
// The MS does not necessarily set bits which have
// been acked previously, so lack of a bit means nothing.
}
}
}
// This code detects the condition that the downlinkAckNack did not advance VA at all.
// There is no speced counter to detect this condition, and under normal circumstances
// maybe it would not occur. But if a TBF fails and we send a new TBF, the Blackberry
// thinks the new TBF is a continuation of the the old and sends us an acknack instructing
// us to resend blocks from the old TBF, which is no longer possible, and the result
// is we keep doing the same downlinkAckNack handshake literally forever.
// This is despite setting of the RLCMsgPacketDownlinkAssignment->mControlAck bit in the
// downlink assignment, so it may be a bug in the Blackberry.
// Update 6-12: The above bug is definitively fixed. However, TBFs still get stuck here
// due to non-responsive MS, but that case is also detected by N3105.
bool stuck = (AND.mSSN == mPrevAckSsn);
if (stuck && !receivedNewAcks) {
LOGWATCHF("T%s STUCK at %d\n",getTBF()->tbfid(1),(int)AND.mSSN);
if ((int)mTotalBlocksSent - (int)mPrevAckBlockCount > configGetNumQ("GPRS.TBF.Downlink.NStuck",250)) {
mtCancel(MSStopCause::Stuck,TbfRetryAfterRelease);
goto finished;
}
} else {
mPrevAckBlockCount = mTotalBlocksSent;
}
mPrevAckSsn = AND.mSSN;
mResendSsn = AND.mSSN; // Default is to resend negatively acked blocks.
if (stuck || mDownFinished || mDownStalled) {
mResendSsn = mSt.TxQNum; // In these cases, resend all blocks.
} else {
// The downlink acknack lags behind VS by an amount that depends on how
// many downchannels are being used. If it looks like the SSN
// was out of sync farther than that, resend those blocks too.
int slip = 6 * mtMS->msPCHDowns.size();
if (deltaSNS(mSt.TxQNum,mResendSsn) > slip) {
mResendSsn = addSN(mSt.TxQNum,-slip);
}
}
// TEST!!! This was a total hack to test the bitmap interpretation.
// If we missed any blocks, resend them all.
// Worked in that RLCEngine resent all blocks, but did not help at all.
//if (cntmissed && AND.mSSN) {
// for (int i=1; i <= AND.mbitmapsize; i++) {
// absn = AND.mSSN - i;
// mSt.VB[absn] = false; // resend all the blocks referenced in the bitmap.
// if (absn == 0) break;
// }
//}
}
advanceVA();
//if (deltaSN(mSt.VA,mSt.TxQNum) >= 0) // Did the MS ack all the blocks?
if (deltaEQ(mSt.VA,mSt.TxQNum)) { // Did the MS ack all the blocks?
mAllAcked = true;
GPRSLOG(1) << getTBF() <<msg->str() <<engineDumpStr();
if (mDownFinished) {
mtFinishSuccess(); // All done; delete the tbf.
}
//mtRecvAck();
return;
} else {
// The last block sent must be the final block.
// To implement that, we just unack the final block.
#if FAST_TBF
// For persist mode, we dont need to unack the block
// because if we get a new TBF we dont need to resend this block any more.
// But I dont think we need to do this at all any more, because
// when VS == TxQNum, we know to resend the final block.
#else
// It doesnt matter if the MS re-acks this block or not, since it is
// the final block, we always have to resend it at the end,
// and the MS will send the FBI when it is acked.
// Effectively, there is no point in an ack indicator for the final block.
mSt.VB[addSN(mSt.TxQNum,-1)] = false;
#endif
}
// Reset mSt.VS to the oldest unacked block:
mSt.VS = mSt.VA;
advanceVS(); // Then skip over blocks that have not been negatively acknowledged.
// We dont really have to check for stall here.
// It is ok to wait until after the next block is sent.
finished:
//GPRSLOG(1) << getTBF() <<msg->str() <<LOGVAR(mSt.TxQNum) <<LOGVAR(mSt.VA) <<LOGVAR(mSt.VS) << GPRSLOG(mResendSsn);
GPRSLOG(1) << getTBF() <<msg->str() <<engineDumpStr();
}
// To debug everything in RLCUpEngine: GPRS.Debug = 1+2+64+2048+4096=6211
void RLCUpEngine::addUpPDU(BitVector& seg)
{
if (mUpPDU == NULL) {
#if INTERNAL_SGSN
mUpPDU = new ByteVector(RLC_PDU_MAX_LEN);
mUpPDU->setAppendP(0);
#else
mUpPDU = new BSSG::BSSGMsgULUnitData(RLC_PDU_MAX_LEN,mtMS->msTlli);
mUpPDU->mTBFId = mtDebugId;
#endif
}
// Do not use str() here, because it tries to print the pdu contents,
// but the pdu is incomplete.
mUpPDU->append(seg);
GPRSLOG(4096) << "addUpPDU:after="<<mUpPDU->hexstr();
}
// Send the completed PDU on its way.
void RLCUpEngine::sendPDU()
{
GPRSLOG(2048) <<"sendPDU"<<LOGVAR2("size",mUpPDU->size())<<LOGVAR2("pdu",mUpPDU->hexstr());
mtMS->msBytesUp += mUpPDU->size();
#if INTERNAL_SGSN
mtMS->sgsnWriteLowSide(*mUpPDU,getTBF()->mtTlli);
delete mUpPDU; // decrements refcnt; llc may have saved a ref to the bytevector.
#else
mUpPDU->setLength();
if (GPRSConfig::sgsnIsInternal()) {
// LLC does not want the BSSG header.
// The payload is an LLC message.
ByteVector payload = mUpPDU->getPayload();
//SGSN::Sgsn::sgsnWriteLowSide(payload,mtMS->msTlli);
mtMS->sgsnWriteLowSide(payload,getTBF()->mtTlli);
delete mUpPDU; // decrements refcnt; llc may have saved a ref to the bytevector.
} else {
BSSG::BSSGWriteLowSide(mUpPDU);
}
#endif
mUpPDU = 0;
}
struct seghdr {
unsigned LI;
unsigned M;
unsigned E;
};
static void dumpsegs(seghdr *segs, int n)
{
for (int j = 0; j < n; j++) {
struct seghdr& seg = segs[j];
GLOG(INFO) <<"\t" << LOGVAR(seg.LI) << LOGVAR(seg.M) << LOGVAR(seg.E);
}
}
// Process any blocks that have been received, advancing the window.
// The MS is allowed to send multiple PDUs in a TBF, so we have to handle it.
// Even in unack mode, we will assemble the blocks in mSt.RxQ to descramble multislot transmissions.
void RLCUpEngine::engineUpAdvanceWindow()
{
mIncompletePDU = false;
//if (BSN==mSt.VQ) {
// while (mSt.VN[mSt.VQ] && mSt.VQ!=mSt.VR) { mSt.VQ = addSN(mSt.VQ,1); }
//}
// TODO: Unacknowledged mode.
while (mSt.RxQ[mSt.VQ /*% mSNS*/]) {
RLCUplinkDataBlock *block = mSt.RxQ[mSt.VQ];
mSt.RxQ[mSt.VQ] = NULL; // redundant; they will also be deleted when suredly past
//mSt.VN[mSt.VQ] = false; // We cant do this here.
mSt.VQ = addSN(mSt.VQ,1);
RLCUplinkDataSegment payload(block->getPayload());
GPRSLOG(64) << "RLCUpEngine payload=" << payload.hexstr() <<LOGVAR2("o",payload.isOwner());
// Each block can have multiple LLC segments. The max size is payloadSize.
// The block size is specified by the channel coding command, but I saw the Blackberry
// send blocks of a lower codec sometimes of its own initiative.
// So dont bother to error check the block size.
if (block->mE) {
// Whole payload is appended to the current PDU.
GPRSLOG(64) << "RLCUpEngine E=1,"<<LOGVAR(payload.size());
addUpPDU(payload);
if (block->mmac.isFinal()) { sendPDU(); } // Annex B Example 6.
} else {
// The RLC block contains length indicator octets. Crack them out.
struct seghdr segs[16];
int n; // Number of length indicators; last one has E==1.
bool end = 0;
for (n = 0; !end; n++) {
if (n == 16) {
GLOG(ERR) << "GPRS: more than 16 segments per RLC block";
dumpsegs(segs,15);
break; // This block is almost certianly trash...
}
segs[n].LI = payload.LIByteLI();
segs[n].E = payload.LIByteE();
end = segs[n].E;
segs[n].M = payload.LIByteM();
payload.dup(payload.tail(8));
}
unsigned original_size = payload.size();
// GSM 04.60 sec 10.4.14 and Appendix B.
// Use the length indicators to slice up the payload into segments.
for (int i = 0; i < n; i++) {
unsigned lenbytes = segs[i].LI;
unsigned sizebytes = payload.size()/8;
GPRSLOG(64) << "RLCUpEngine seg:"<<LOGVAR(i)<<LOGVAR(n)<<LOGVAR(lenbytes)<<LOGVAR(sizebytes)<<LOGVAR(payload.size()) <<LOGVAR2("o",payload.isOwner());
// Update: 44.060 B.6 says if the final block (indicated by CV==0) no need to
// add any length indicators to denote termination of PDU????
if (lenbytes == 0) {
lenbytes = sizebytes; // lenbytes==0 means use the rest of the payload.
// Special case: If length == 0 in the final block,
// it means the PDU was unfinished.
if (block->mmac.isFinal()) {
GPRSLOG(64) << "RLCUpEngine mIncompletePDU";
mIncompletePDU = true; // But we dont currently use this.
}
} else {
// Sanity check here. Log any bogus blocks.
if (lenbytes > sizebytes) {
GLOG(ERR)<<"Uplink PDU with with nonsensical segments:";
//GLOG(INFO)<<"\tpayloadlen="<<original_size <<" E="<<block->mE;
GLOG(ERR)<< "\t" << LOGVAR(original_size) << LOGVAR(block->mE);
dumpsegs(segs,n);
// what to do? Just save what there really is.
lenbytes = sizebytes;
}
}
BitVector foo(payload.segment(0,8*lenbytes));
addUpPDU(foo);
if (segs[i].LI) { sendPDU(); }
payload.dup(payload.tail(8*lenbytes));
}
// Final M bit means add rest of the payload to the nextpdu.
if (payload.size() && segs[n-1].M) {
GPRSLOG(64) << "RLCUpEngine M=1:"<<LOGVAR(payload.size()) <<LOGVAR2("o",payload.isOwner());
addUpPDU(payload);
}
}
mtUpPersistTimer.setNow();
if (mtUpState == RlcUpPersistFinal) { mtUpState = RlcUpTransmit; }
if (block->mmac.isFinal()) {
if (mUpPersistentMode && ! mtPerformReassign) {
mtUpState = RlcUpQuiescent;
} else {
mtUpState = RlcUpFinished;
}
// 5-22-2012: Re-enabled this:
if (mUpPDU && !mIncompletePDU) { sendPDU(); }
} else {
// This is a new block, so we are not quiescent any more.
if (mtUpState == RlcUpQuiescent) { mtUpState = RlcUpTransmit; }
}
delete block;
}
}
// Amazingly, we need to give the MS an RRBP reservation in case it wants to change
// the priority of the TBF. All those zillions of USFs we sent it, and on which
// it responded with control blocks, were not enough.
// Update: Some MSs (iphone) send the packet resource request on the USF, ie, using PACCH.
bool RLCUpEngine::sendNonFinalAckNack(PDCHL1Downlink *down)
{
MSInfo *ms = mtMS;
// Before we issue another ack nack, wait until we have given this MS
// an uplink block to respond via usf. This is an efficiency issue
// if the uplink is being used by other MS as well. However, if the MAC does not
// find any other pressing need for the current uplink block, it will be granted
// to any one of the active uplink TBFs, so we may get a block that way too.
// Note there can be multiple uplink channels, and a USF could be granted for
// this MS on any of them. Note that this would get complicated if there
// are multiple uplink TBFs.
if (ms->msAckNackUSFGrant == ms->msNumDataUSFGrants) { return false; }
// These messages are sent without acknowledgement, except for the last one.
// If we were more clever, we would insure that the USF in this message
// is for the intended MS recipient, then if we received an uplink block
// we would know the message was (probably) received. (It can still fail
// because the USF in the header is sent with better error correction.)
// These messages are not specifically counted. The TBF cancellation
// occurs if the MS stops answering USFs.
// It would be nice to detect stuck (non-advancing) uplinks, but the MS
// will do that, cancel the TBF, stop answering USFs, and then we will cancel.
RLCMsgPacketUplinkAckNack * msg = engineUpAckNack();
GPRSLOG(1) <<getTBF() <<" "<<msg->str();
//down->send1MsgFrame(getTBF(),msg,0,MsgTransNone,NULL);
down->send1MsgFrame(getTBF(),msg,1,MsgTransTransmit,NULL);
ms->msAckNackUSFGrant = ms->msNumDataUSFGrants; // Remember this number.
mNumUpBlocksSinceAckNack = 0;
return true;
}
#if UPLINK_PERSIST
// For persistent uplink we need to know when all current TBFs have completed
// so the uplink is quiescent.
// bool RLCUpEngine::isQuiescent()
//{
// return mSt.VQ == mSt.VR && NULL==mUpPDU;
//}
#endif
// See if this up engine wants to send something on the downlink.
// It would be an AckNack message.
// Return true if we sent something.
bool RLCUpEngine::engineService(PDCHL1Downlink *down)
{
TBF *tbf = getTBF();
if (! tbf->isPrimary(down)) { return false; }
if (mtUpState == RlcUpFinished) {
finalstate:
// Send the final acknack
// We wait for the MS to send a PacketControlAcknowledgment
// to the final acknack msg we sent it, to make sure it got it.
// Otherwise, it may keep trying to send us blocks forever.
// An alternate strategy would be to resend the acknack whenever
// we receive a data block from it, but it would be harder to tell
// when to release the resources.
if (mtGotAck(MsgTransDataFinal,true)) { // Woo hoo!
mtFinishSuccess();
return false; // We did not need to use the downlink.
}
if (mtMsgPending()) { return false; }
RLCMsgPacketUplinkAckNack * msg = engineUpAckNack();
GPRSLOG(1) <<tbf <<" "<<msg->str();
int result = down->send1MsgFrame(tbf,msg,2,MsgTransDataFinal,&mtN3103);
if (result) {
if (mtUnAckMode) {
// We dont bother to find out if the acknack gets through.
// In fact, the only reason we used an RRBP was
// to allow the MS to initiate another uplink transfer.
mtFinishSuccess();
} else {
mtMsgSetWait(MsgTransDataFinal); // Wait for response before trying again.
}
}
return result;
}
if (mUpStalled || mNumUpBlocksSinceAckNack >= mNumUpPerAckNack) {
// But absolutely do not send two reservations at once:
if (! mtMsgPending(MsgTransTransmit)) return sendNonFinalAckNack(down);
}
#if UPLINK_PERSIST
// Persistent uplink uses Extended Uplink TBF defined in 44.060 9.3.1b and 9.5
if (mUpPersistentMode) {
//LOGWATCHF("X%s UpState=%d keepalive=%d persist=%d\n",tbf->tbfid(1),mtUpState,mtUpKeepAliveTimer.elapsed(),mtUpPersistTimer.elapsed());
// Note: The link is 'quiescent' if there are no new unique blocks,
// but there could be lots of duplicate blocks, so we still need to
// send acknacks.
if (mtUpState == RlcUpQuiescent) {
if (mtUpPersistTimer.elapsed() > (int)gL2MAC.macUplinkPersist || mtPerformReassign) {
// Time to kill the TBF.
// As per 44.060 9.5 we do that by sending a PacketUplinkAckNack with the
// final ack bit set. But since we are constantly transmiting USFs, there
// could be a new uplink TBF starting right now, so first we have to
// stop transmitting USFs, then wait to make sure the MS didnt start
// another TBF, before we can kill it off.
mtUpState = RlcUpPersistFinal;
// There could be USFs sent in this period, so we need to wait out
// this block period, and a USF in block N gets replied in block N+1,
// so we have to wait for N+2, or 3 block periods total.
mDataPersistFinalEndBSN = gBSNNext + 3;
return false;
} else if (mtUpKeepAliveTimer.elapsed() > (int)gL2MAC.macUplinkKeepAlive) {
// This updates the keepalive timer if the message is sent:
bool result = sendNonFinalAckNack(down);
if (result) LOGWATCHF("K%s keepalive=%d persist=%d\n",getTBF()->tbfid(1),
mtUpKeepAliveTimer.elapsed(),mtUpPersistTimer.elapsed());
return result;
}
} else if (mtUpState == RlcUpPersistFinal) {
// We hang out in this state until the expiry BSN is reached.
// If a new TBF starts in the meantime it will throw us
// back into DataTransmit state.
if (gBSNNext <= mDataPersistFinalEndBSN) {return false;}
// Now it is really time to finish.
mtUpState = RlcUpFinished;
mtSetState(TBFState::DataFinal);
goto finalstate;
}
//GLOG(ERR) << "persistent mode timer expiration: invalid TBF state:"<<tbf;
}
#endif
return false;
}
float RLCUpEngine::engineDesiredUtilization()
{
// Active uplink engine wants approximately this downlink bandwidth.
return 1.0 / mNumUpPerAckNack;
}
// (pat) Receive a block from the MS (cell phone)
// Note: function receives allocated block, someone must destroy when finished.
// The someone is engineUpAdvanceWindow or the class destructor.
// For persistent mode listen up, because this is tricky.
// The MS sends us the final block, but then continues to send
// previous blocks until it gets the acknack.
// Therefore we must not set the mtUpState based on receiving a block in this function,
// we can only change mtUpState in engineUpAdvanceWindow, which knows if the block is new or not.
void RLCUpEngine::engineRecvDataBlock(RLCUplinkDataBlock* block, int tn)
{
switch (mtGetState()) {
case TBFState::DataWaiting1:
mtMS->msT3168.reset(); // Way overkill, this should not be running.
mtSetState(TBFState::DataTransmit);
break;
case TBFState::Dead:
GLOG(ERR) <<getTBF()<<"received uplink data block after expiration";
delete block;
return;
default:
break;
}
//mtUpPersistTimer.setNow();
mtMS->msN3101 = 0;
// Mark the ack flags and save the block.
unsigned BSN = block->mBSN; // This is modulo mSNS
LOGWATCHF("B%s tn=%d block=%d cc=%d %s %s\n",getTBF()->tbfid(1),tn,BSN,(int)block->mUpCC,
block->mmac.isFinal()?"final":"",mSt.VN[BSN]?"dup":"");
if (mSt.RxQ[BSN]) {
// If BSN < VQ in modulo arithmetic, this is a duplicate block that
// we have already scanned past, and we dont need to save it.
// However, to be safe, we wont do the above test, instead we'll just save the
// block and delete it when it is surely past below.
// If we had more energy, we might check that the two blocks are the same.
if (mSt.VN[BSN] == false) { GLOG(ERR) << getTBF() << " VN out of sync" <<LOGVAR(BSN) << LOGVAR(mSt.VN); }
delete mSt.RxQ[BSN];
mtMS->msCountBlocks.addMiss();
} else {
mUniqueBlocksReceived++;
mtMS->msCountBlocks.addHit();
}
mSt.VN[BSN]=true;
mSt.RxQ[BSN]=block;
// We must use deltaSN, not deltaSNS, because we dont know which is higher.
// Have to subtract 1 first to keep the edge condition from failing.
int VRm1 = addSN(mSt.VR,-1);
int deltaR = deltaSN(BSN,VRm1);
if (deltaR>0) {
unsigned past = addSN(mSt.VR, -mWS - 2); // -2 to be safe
mSt.VR=addSN(BSN,1);
unsigned pastend = addSN(mSt.VR, -mWS - 2);
// We clear out the VN behind us.
// Since the acknack msg only stretches back the 64 acks before VR,
// it is safe to clear before those.
// 12-28-2012: Change to -2 from -1.
//mSt.VN[(mSt.VR - mWS - 2) % mSNS] = false;
for ( ; past != pastend; incSN(past)) {
mSt.VN[past] = false;
if (mSt.RxQ[past]) { delete mSt.RxQ[past]; mSt.RxQ[past] = 0; }
}
}
mUpStalled = block->mmac.mSI;
//mtSetState(mUpStalled ? TBFState::DataStalled : TBFState::DataTransmit);
mNumUpBlocksSinceAckNack++;
mTotalBlocksReceived++;
// Add any finished blocks to the PDU, possibly send PDUs.
engineUpAdvanceWindow(); // sets mtUpState to RlcUpFinished if TBF is complete.
if (mtUpState == RlcUpFinished) {
// We always send a final ack/nack for both acknowledged and unacknowledged mode.
// old: Calls mtMsgReset() - very important. update: now each msgtransaction has its own type.
mtSetState(TBFState::DataFinal);
}
}
// Send Packet Uplink Ack/Nack, GSM 04.60 11.2.6
RLCMsgPacketUplinkAckNack * RLCUpEngine::engineUpAckNack()
{
RLCMsgPacketAckNackDescriptionIE AND;
// Spec says that if mFinalAckIndication is set, the rest is ignored.
AND.mFinalAckIndication = (mtUpState == RlcUpFinished);
// GSM04.60 9.1.8.
// Also read 12.3: Ack/Nack description carefully.
// The phrase: "Mapping of the bitmap is defined on sub-clause 11" means look at the
// start of section 11, to learn that the bitmap is indexed backwards relative to SSN which
// is defined as the most recent block received (aka V(R) in the receiver),
// then modulo 128, but the bit encoding itself runs from MSB to LSB,
// so it ends up going forwards, but the bits of interest
// are at the high end of the bitmap.
AND.mSSN = mSt.VR; // Thats right: VR, not VQ.
for (int i = 1; i <= AND.mbitmapsize; i++) {
//AND.mBitMap[AND.mbitmapsize - i] = mSt.VN[(mSt.VR-i) % mSNS];
AND.mBitMap[AND.mbitmapsize - i] = mSt.VN[addSN(mSt.VR,-i)];
}
RLCMsgPacketUplinkAckNack *msg = new RLCMsgPacketUplinkAckNack(getTBF(), AND);
#if UPLINK_PERSIST
if (mUpPersistentMode) {
mtUpKeepAliveTimer.setNow();
}
LOGWATCHF("A%s SSN=%d state=%d keepalive=%d persist=%d\n",getTBF()->tbfid(1),(int)AND.mSSN,
mtUpState,mtUpKeepAliveTimer.elapsed(),mtUpPersistTimer.elapsed());
#endif
return msg;
}
// Is the downlink engine stalled, waiting for acknack messages?
// Assumes the downlink is unifinished.
// Stalled applies only to acknowledged mode.
// We never send more than SNS blocks in a single TBF,
// so we dont have to worry about wraparound.
//bool RLCDownEngine::stalled()
//{
// if (!mDownStalled && !mtUnAckMode &&
// (mSt.VS >= mSt.TxQNum || mSt.VS - mSt.VA >= mWS)) {
// mDownStalled = true;
// }
// return mDownStalled;
//}
// Deliver a PDU to the MS.
// Before FAST_TBF: This function primes the RLCEngine by chopping the PDU into RLCBlocks,
// placing them in the mSt.TxQ. They will be physically sent by the serviceloop.
void RLCDownEngine::engineWriteHighSide(SGSN::GprsSgsnDownlinkPdu *dlmsg)
{
#if FAST_TBF
// We dont do the pdus one at a time. Just leave the PDU on the queue and the
// engine will pull it off.
mtMS->msDownlinkQueue.write_front(dlmsg);
#else
// The pdus are sent one at a time.
TBF *tbf = getTBF();
tbf->mtDescription = dlmsg->mDescr;
// The mDownPDU owns the malloced memory. Many of the other downlink blocks are segments into it.
// The mDownPDU is deleted automatically with the RLCDownEngine.
mDownPDU = dlmsg->mDlData;
LOGWATCHF("engineWriteHighSide %d tn=%d\n",mDownPDU.size(),tbf->mtMS->msPacch->TN());
//assert(mDownPDU.getRefCnt() == 2); // Not true during a retry.
mDownlinkPdu = dlmsg;
//assert(mDownPDU.getRefCnt() == 1);
//std::cout<<"engineWriteHighSide, ref="<<dlmsg->mDlData.getRefCnt()<<"\n";
GPRSLOG(1) <<tbf <<" <=== engineWriteHighSide: size="<<mDownPDU.size() <<" "<< dlmsg->mDescr<<timestr();
GPRSLOG(2048)<<" <=== engineWriteHighSide size="<<mDownPDU.size()<<" pdu:"<<mDownPDU.hexstr();
//#if !FAST_TBF
unsigned bsn = 0;
// Update: We dont need to take ownership anymore.
//assert(mDownPDU.isOwner() && ! pdu.isOwner());
// (pat) old comment: if size == remaining there is a special case [but only if multiple pdus per tbf].
unsigned remaining = mDownPDU.size(); // remaining in bytes
unsigned fp = 0; // pointer into pdu
while (remaining) {
RLCDownlinkDataBlock *block = new RLCDownlinkDataBlock(mtChannelCoding());
unsigned payloadsize = block->getPayloadSize();
block->mBSN = bsn++;
if (remaining >= payloadsize) {
block->mE = 1; // No extension octet follows.
// In this case the payload ByteVector segment points directly into the mDownPDU.
// I tried allocating here, did not help the cause=3105 failure.
//block->mPayload.clone(mDownPDU.segment(fp,payloadsize));
block->mPayload.set(mDownPDU.segmentTemp(fp,payloadsize));
remaining -= payloadsize;
fp += payloadsize;
} else {
// The data will not fill the block.
// In this case, we will use a ByteVector with allocated memory.
// The confusing "singular case" mentioned in GSM04.60 10.4.14 does not happen
// to us because we do not put multiple PDUs in a block. Yet.
block->mE = 0; // redundant
ByteVector payload(payloadsize); // Allocate a new one. What horrible syntax.
ByteVector seg = mDownPDU.segmentTemp(fp,remaining);
// We have to add an extension octet to specify the PDU segment length.
int sbh = RLCSubBlockHeader::makeoctet(remaining,0,1);
payload.setByte(0, sbh);
// Add the PDU segment.
payload.setSegment((size_t)1, seg);
// Note: unused RLC data field filled with 0x2b as per 04.60 10.4.16
int fillsize = payloadsize - remaining - 1;
payload.fill(0x2b,remaining+1,fillsize);
// Try cloning the memory here, but didnt help
// block->mPayload.clone(payload);
block->mPayload = payload; // dups the memory
remaining = 0;
}
// The TFI is not set yet! TFI will be set by send1Frame just before transmit.
//block->mTFI = mtTFI;
if (!remaining) { block->mFBI = true; }
mSt.TxQ[mSt.TxQNum++] = block;
}
#endif
}
// Return the block at vs.
// If vs is past the end of the previously sent blocks, then:
// if we have sent the FBI, return that block,
// otherwise get the next block, or NULL if no more data avail,
// which can only happen in persistent mode.
RLCDownlinkDataBlock *RLCDownEngine::getBlock(unsigned vs,
int tn) // Timeslot Number, for debugging only.
{
#if FAST_TBF
mTotalDataBlocksSent++;
if (vs == mSt.TxQNum) {
// Did we finish? Look at the next-to-last block to see if it has fbi set.
RLCDownlinkDataBlock *prev = mSt.TxQ[addSN(vs,-1)];
if (prev && prev->mFBI) { return prev; }
// Manufacture the next block.
mUniqueDataBlocksSent++;
mtMS->msCountBlocks.addHit();
// Clean up behind ourselves when wrapping around.
if (mSt.TxQ[mSt.TxQNum]) { delete mSt.TxQ[mSt.TxQNum]; mSt.TxQ[mSt.TxQNum] = 0; }
RLCDownlinkDataBlock *block = engineFillBlock(mSt.TxQNum,tn);
if (block == NULL) { return NULL; }
mAllAcked = false; // It is a brand new block.
GPRSLOG(4096) << "getBlock"<<LOGVAR(vs)<<":"<<block->str();
mSt.TxQ[mSt.TxQNum] = block;
//mSt.sendTime[mSt.TxQNum] = gBSNNext;
mSt.VB[mSt.TxQNum] = false; // block needs an ack.
incSN(mSt.TxQNum);
} else {
mtMS->msCountBlocks.addMiss();
}
#endif
assert(mSt.TxQ[vs]);
return mSt.TxQ[vs];
}
#if FAST_TBF
// Depending on dlPersistentmode, when we run out of data we can either terminate
// the downlink tbf by setting the fbi indicator in the last block sent,
// or we can leave the TBF open pending further data, which means the routine
// will send NULL when data exhausted.
// This routine always returns NULL if it is out of data, but in non-persistent-mode
// the caller notices the FBI bit and does not call it any more after
// receiving the last block.
RLCDownlinkDataBlock* RLCDownEngine::engineFillBlock(unsigned bsn,
int tn) // Timeslot Number, for debugging only.
{
const int maxPdus = 10;
int li[maxPdus];
int mbit[maxPdus]; // mbit = 1 implies a new PDU starts after the current one.
ByteVector pdus[maxPdus];
int pducnt = 0;
int licnt = 0;
bool fbi = false; // final block indicator
// Create the block.
RLCDownlinkDataBlock *block = new RLCDownlinkDataBlock(mtChannelCoding());
int payloadsize = block->getPayloadSize();
int payloadavail = payloadsize;
bool nonIdle = !!mDownPDU.size();
// First make a list of the pdus to go in the rlc block.
// Dont think it is possible for licnt to reach maxPdus without pducnt hitting maxPdus first, but test anyway.
while (payloadavail>0 && pducnt < maxPdus && licnt < maxPdus) {
// Is there any more data?
if (mDownPDU.size() == 0) {
// For testing, if SinglePduMode send just one pdu at a time:
// The first pdu was loaded by engineWriteHighSide, so we just ignore the q.
if (configGetNumQ("GPRS.SinglePduMode",0)) {break;}
if (queueFrontExistsAndIsNotTlliChangeCommand(mtMS)) {
SGSN::GprsSgsnDownlinkPdu *dlmsg = mtMS->msDownlinkQueue.readNoBlock();
assert(dlmsg);
mtMS->msDownlinkQDelay.addPoint(dlmsg->mDlTime.elapsed()/1000.0);
mDownPDU = dlmsg->mDlData;
mtMS->msBytesDown += mDownPDU.size();
if (! dlmsg->isKeepAlive()) { nonIdle = true; }
// Remember the last sdu for possible retry on failure.
if (mDownlinkPdu) {delete mDownlinkPdu;}
mDownlinkPdu = dlmsg;
getTBF()->mtDescription = dlmsg->mDescr;
LOGWATCHF("pdu %d\n",mDownPDU.size());
GPRSLOG(1) <<getTBF() <<" <=== engineWriteHighSidePull: size="<<mDownPDU.size() <<" "<< dlmsg->mDescr<<timestr();
GPRSLOG(2048)<<" <=== engineWriteHighSidePull size="<<mDownPDU.size()<<" pdu:"<<mDownPDU.hexstr();
}
// DEBUG: Disable TBF wrap around.
// If the new pdu clearly wont fit, dont add it.
// 6-11: This was added for debugging but clearly works fine now and could be removed.
if (configGetNumQ("GPRS.TBF.nowrap",0)) {
if (mSt.TxQNum + (mDownPDU.size() / (payloadsize-1)) >= mSNS-1) {
LOGWATCHF("debug: Skipping wrap-around\n");
fbi = true;
break;
}
}
}
int sdusize = mDownPDU.size(); // sdu remaining bytes
if (sdusize == 0) {break;} // No more incoming data.
if (sdusize > payloadavail || (sdusize == payloadavail && pducnt)) {
if (pducnt) { mbit[licnt-1] = 1; }
pdus[pducnt++] = mDownPDU.head(payloadavail);
mDownPDU.trimLeft(payloadavail);
payloadavail = 0;
} else if (sdusize == payloadavail && pducnt == 0) {
// Special case for single pdu exactly fills the block.
// If this is the final block, we set FBI and can ommit the length indicator.
// Otherwise we put out all but the last byte of sdu and use a special zero length indicator.
// The next rlc block will get the final byte of this sdu.
// I disabled this special case out, just to be safe; the penalty
// is occassionally sending an extra block with only one byte in it.
if (0 && mtMS->msDownlinkQueue.size() == 0 && ! dlPersistentMode()) {
// This is the final block, so the fbi indicator tells the MS
// the data ends at the end of the block and we do not need
// the 'singular case' 0 li field.
fbi = true; // Make sure to set this here, in case an asynchronous
// process adds data between here and the time we
// check msDownlinkQueue.size() at the end of this loop.
pdus[pducnt++] = mDownPDU;
mDownPDU.trimLeft(payloadavail);
//LOGWATCHF("debug: exactly full block\n");
} else {
// Not the final block; need to add the 'singular case' 0 li field,
// and final byte of the pdu will go in the next block.
li[licnt] = 0;
mbit[licnt] = 0;
licnt++;
payloadavail--; // For the li field.
pdus[pducnt++] = mDownPDU.head(payloadavail);
mDownPDU.trimLeft(payloadavail);
//LOGWATCHF("debug: singular case block\n");
}
payloadavail = 0;
} else { // sdusize < payloadavail
if (payloadavail == 1) {break;} // too small to use.
if (pducnt) { mbit[licnt-1] = 1; }
pdus[pducnt++] = mDownPDU;
mDownPDU.trimLeft(sdusize);
li[licnt] = sdusize;
mbit[licnt] = 0; // Until proven otherwise.
licnt++;
payloadavail--; // For the li field
payloadavail -= sdusize;
}
}
if (pducnt == 0) {
// There is no data ready to go.
delete block;
return NULL;
}
if (!dataAvail() && !dlPersistentMode()) { fbi = true; }
if (configGetNumQ("GPRS.SinglePduMode",0)) {
// For testing, send just one pdu at a time:
if (mDownPDU.size() == 0) { fbi = true; }
}
if (fbi) mDownFinished = true;
block->mFBI = fbi;
block->mBSN = bsn;
block->mIdle = !nonIdle;
// DEBUG:
//static unsigned lastbsn = 0;
//if (fbi) { lastbsn = 0; }
//if (bsn < lastbsn) {
// // This is a BUG.
// printf("BUG\n");
// GPRSLOG(1) << "BUG HERE"<<getTBF();
//}
char report[300];
if (GPRSDebug || configGetNumQ("GPRS.WATCH",0)) sprintf(report,"T%s tn=%d block=%d cc=%d qn=%d fbi=%d",getTBF()->tbfid(1),tn,bsn,(int)block->mChannelCoding,mSt.TxQNum,fbi);
if (licnt == 0) {
// Entire block is payload.
block->mE = 1; // No extension octet follows.
assert(pducnt == 1);
assert(pdus[0].size() == (unsigned)payloadsize);
block->mPayload = pdus[0];
} else {
block->mPayload = ByteVector(payloadsize);
block->mPayload.setAppendP(0);
// Add the licnts
for (int i = 0; i < licnt; i++) {
// Add the extension octet to specify the PDU segment length.
int sbh = RLCSubBlockHeader::makeoctet(li[i],mbit[i],i == licnt-1);
if (GPRSDebug) sprintf(report+strlen(report)," li=%d:%d:%d",li[i],mbit[i],i==licnt-1);
block->mPayload.appendByte(sbh);
}
// Add the pdu segments.
for (int j = 0; j < pducnt; j++) {
block->mPayload.append(pdus[j]);
if (GPRSDebug) sprintf(report+strlen(report)," seg=%d",pdus[j].size());
}
// Add filler, if any. Unused RLC data field filled with 0x2b as per 04.60 10.4.16
int fillsize = payloadsize - block->mPayload.size();
if (GPRSDebug) sprintf(report+strlen(report)," fill=%d",fillsize);
if (fillsize) block->mPayload.appendFill(0x2b,fillsize);
}
LOGWATCHF("%s\n",report);
// The TFI is not set yet! TFI will be set by send1Frame just before transmit.
//block->mTFI = mtTFI;
return block;
}
#endif
// Is this the final unacked block?
// Note that it may not be the last block in mSt.VB
// because we may be resending some previous block.
//bool RLCDownEngine::isLastUABlock()
//{
// if (mtUnAckMode) {
// return mSt.VS == (mSt.TxQNum-1);
// }
// int cnt = 0; // count of unacked blocks.
// for (unsigned i = mSt.VA; i < mSt.TxQNum; i++) {
// if (!mSt.VB[i]) { cnt++; } // Are we still waiting for an ack for this block?
// if (cnt > 1) { return false; }
// }
// assert(cnt == 1);
// return true;
//}
// How many blocks to go, but dont bother to count more than 5.
// Now that we manufacture downlink rlc blocks on demand, this is difficult to calculate,
// so just return a guess.
//unsigned RLCDownEngine::blocksToGo()
//{
// unsigned i, cnt = 0; // count of unacked blocks remaining.
// for (i = mSt.VS; deltaSN(i,mSt.TxQNum)<0; incSN(i)) {
// if (!mSt.VB[i]) { cnt++; } // Are we still waiting for an ack for this block?
// if (cnt > 5) break;
// }
//#if FAST_TBF
// if (cnt < 5) {
// // Estimate number of blocks from remaining data.
// int payloadsize = mtPayloadSize();
// cnt += mDownPDU.size() / payloadsize;
// if (cnt > 5) return cnt;
// if (configGetNumQ("GPRS.SinglePduMode",0)) {return cnt;}
// if (mtMS->msDownlinkQueue.size()) {
// // Just assume its a bunch of data.
// return 6;
// }
// }
//#endif
// return cnt;
//}
// See if the TBF in this RLCEngine has some RLC data or control messages to send.
// Return TRUE if we sent something on the downlink.
bool RLCDownEngine::engineService(PDCHL1Downlink *down)
{
//TBFState::type state = mtGetState();
//stalled(); // Are we stalled? Sets mDownStalled.
//if (state == TBFState::DataFinal || (WaitForStall && mDownStalled)) {
// The expectedack time is set in send1DataFrame.
// The RRBP ACK is optional, so mtExpectedAckBSN may not yet
// be valid, but mtMsgPending handles that.
//if (mtMsgPending()) { return false; }
//}
// Logic restructured and this code moved below...
//if (mAllAcked && ! dataAvail()) { return false; }
// Note: When an acknack is received, mSt.VS is set back to the first
// negatively acknowledged block, so we advanceVS() after using VS, not before.
// After final block sent, VS is left == TxNum.
bool advanced = true;
RLCDownlinkDataBlock *block = getBlock(mSt.VS,down->TN());
if (block == NULL) {
// This happens when data is exhausted, in which case VS == TxNum.
// Only send the final block on the primary channel.
if (!getTBF()->isPrimary(down)) {return false;}
if (mAllAcked) {
// All current TBFs have been sent and acknowledged.
// This case does not occur if we are doing one TBF at a time,
// so we dont need to check for that case specially.
assert(dlPersistentMode());
//if (dlPersistentMode())
if (mtDownKeepAliveTimer.elapsed() > (int)gL2MAC.macDownlinkKeepAlive) {
// Start a new keep-alive on its way.
mtMS->sgsnSendKeepAlive();
mtDownKeepAliveTimer.setNow();
}
return false; // Nothing to do.
}
// After sending all unacknowledged blocks we just send the final block
// over and over. Alternatively we could start over at VA.
// If so, it should be done only if no other TBFs have something more
// important to do.
assert(mDownFinished);
// Back up one to get the final block to resend.
assert(mSt.VS == mSt.TxQNum);
block = getBlock(addSN(mSt.VS,-1),down->TN());
advanced = false;
assert(block);
}
if (block->mIdle) {
assert(dlPersistentMode());
if (!mDownFinished && mtDownPersistTimer.expired()) {
// Time to kill the tbf.
block->mFBI = true;
mDownFinished = true;
}
} else if (dlPersistentMode()) {
// We are sending a non-idle block.
if (gL2MAC.macDownlinkKeepAlive) mtDownKeepAliveTimer.setNow();
if (gL2MAC.macDownlinkPersist) mtDownPersistTimer.setNow();
}
// TODO:
// 44.060 9.3.1 says that after all pending unacked blocks have been transmitted,
// you can start over again.
// Astonishingly, we have to resend the final block every time we
// send any acknowledged blocks.
// You can not just set the FBI indicator in the last block sent.
// I am not sure if the MS will not re-ack the final block, so do not
// reset its ack indicator, just retransmit it.
if (block->mFBI)
{
if (!getTBF()->isPrimary(down)) {return false;}
// We need an acknowledgment for the final block, even in unacknowledged mode.
// We are allowed to have three RRBPs pending at a time, total for the MS, which includes
// both uplink and downlink TBFs, so we will only have one at a time
// for each of the two possible uplink and downlink tbfs,
// so wait if we already have an earlier RRBP out.
if (mtMsgPending()) { return false; }
//mtMsgReset();
if (! down->send1DataFrame(this, block, 2, MsgTransDataFinal,&mtN3105)) { return false; }
// This is not needed:
//mtMsgSetWait(); // Dont resend again until reservation passed.
if (mtUnAckMode) {
// We're done. We send it once and forget it.
// TODO: unacknowledged mode - are we supposed to wait for the RRBP reply?
mtFinishSuccess(); // We just sent the last block.
} else {
// This mandated timer is dumb. This time period is no different than
// the rest of the downlink in that we are
// still counting unanswered RRBPs, and will detect
// a non-responsive MS that way.
mtMS->msT3191.set();
}
} else {
assert(mtGetState() == TBFState::DataTransmit);
int rrbpflag = 0;
if (mtUnAckMode) {
// 9.3.3.5: For unack mode do not have to set RRBP except in final block.
rrbpflag = 0; // redundant assignment.
} else if (mDownStalled) {
if (!getTBF()->isPrimary(down)) {return false;}
// Every block we send will include a reservation.
// If there is only one ms, we should probably go ahead and send
// other blocks too, so the behavior should depend on the utilization,
// and also on how big this TBF is, ie, we should have another variable
// that tracks when we start a stall resend, send all those blocks,
// then possibly wait for the reservation.
if (mtMsgPending()) { return false; }
rrbpflag = 1;
} else if (getTBF()->isPrimary(down)) {
// Only send RRBP reservations on the primary channel.
//static const int RrbpGuardBlocks = 5;
// The mNumDownPerAckNack and the RrbpGuardBlocks must be chosen to prevent a stall,
// meaning the sum has to be less than 64; not a problem.
if (++mNumDownBlocksSinceAckNack >= mNumDownPerAckNack) {
rrbpflag=1;
}
// TODO: This test just does not work with persistent mode,
// because it prevents us from sending the rrbp in the last block;
// this test depended on that case being handled by the
// if (mFBI) branch above.
// This blocksRemaining test is only an efficiency issue,
// so get rid of it for now:
//int blocksRemaining = blocksToGo();
//if (blocksRemaining <= RrbpGuardBlocks) { rrbpflag = 0; }
// In this case we dont need to wait for any reservation.
if (rrbpflag && mtMsgPending()) {
rrbpflag = 0; // Dont do two RRBPs at once.
}
}
bool result = down->send1DataFrame(this,block,rrbpflag,MsgTransTransmit,&mtN3105);
assert(result); // always succeeds.
#if FAST_TBF
if (advanced) {
incSN(mSt.VS); // Skip block we just sent.
advanceVS();
}
#else
if (!mDownStalled) {
incSN(mSt.VS); // Skip block we just sent.
advanceVS();
}
#endif
}
mTotalBlocksSent++;
return true;
}
float RLCDownEngine::engineDesiredUtilization()
{
// Very approximately, stalled downlink TBF wants to retry every few blocks.
if (stalled()) { return 0.2; }
return 1.0; // Transmitting downlink TBF wants the entire bandwidth.
}
// Make sure the blocks are deleted.
RLCDownEngine::~RLCDownEngine()
{
unsigned i;
for (i = 0; i < mSNS; i++) { // overkill, but safe.
if (mSt.TxQ[i]) { delete mSt.TxQ[i]; mSt.TxQ[i] = NULL; }
}
#if INTERNAL_SGSN==0
if (mBSSGDlMsg) { delete mBSSGDlMsg; }
#endif
if (mDownlinkPdu) { delete mDownlinkPdu; }
mDownlinkPdu = 0; // extreme paranoia
}
RLCUpEngine::RLCUpEngine(MSInfo *wms,int wOctetCount) :
TBF(wms,RLCDir::Up),
mUpPDU(0),
mBytesPending(wOctetCount)
{
mtUpState = RlcUpTransmit;
memset(&mSt,0,sizeof(mSt));
mStartUsfGrants = wms->msNumDataUSFGrants;
// Use the same criteria for persistent mode as for extended uplink.
// Can only use this if the phone supports geran feature package I?
mUpPersistentMode = mtMS->msCanUseExtendedUplink();
}
RLCUpEngine::~RLCUpEngine()
{
unsigned i;
for (i = 0; i < mSNS; i++) { // overkill, but safe.
if (mSt.RxQ[i]) { delete mSt.RxQ[i]; mSt.RxQ[i] = NULL; }
}
if (mUpPDU) { delete mUpPDU; }
}
void RLCDownEngine::engineDump(std::ostream &os) const
{
os <<LOGVAR2("VS",mSt.VS)<<LOGVAR2("VA",mSt.VA)
<<LOGVAR2("TxQNum",mSt.TxQNum) <<LOGVAR2("stalled",stalled())
<<LOGVAR(mPrevAckSsn)<<LOGVAR(mResendSsn);
}
void RLCUpEngine::engineDump(std::ostream &os) const
{
os <<LOGVAR2("VR",mSt.VR)<<LOGVAR2("VQ",mSt.VQ)
<<LOGVAR2("stalled",stalled())
<<LOGVAR(mNumUpBlocksSinceAckNack)
<<LOGVAR(mtUpState);
}
};