mirror of
				https://github.com/RangeNetworks/openbts.git
				synced 2025-11-04 05:43:14 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1332 lines
		
	
	
		
			50 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			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);
 | 
						|
}
 | 
						|
 | 
						|
};
 |