mirror of
https://github.com/RangeNetworks/openbts.git
synced 2025-10-23 07:42:01 +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);
|
|
}
|
|
|
|
};
|