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

260 lines
9.2 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.
*/
/**@file GPRS RLC-MAC state machine, GSM 04.60. */
/**@file GPRS RLC-MAC state machine, GSM 04.60. */
#ifndef GPRSL2RLCENGINE_H
#define GPRSL2RLCENGINE_H
#define INTERNAL_SGSN 1 // Use internal SGSN, no support for BSSG
#define UPLINK_PERSIST 1
#include <Interthread.h>
#include <list>
#include "GPRSInternal.h"
#if INTERNAL_SGSN==0
#include "BSSGMessages.h"
#endif
#include "TBF.h"
#define FAST_TBF 1 // Use aggregated downlink TBFs.
namespace GPRS {
class PDTCHL1FEC;
// Maximum LLC PDU size is 1560 bytes. (GSM04.60 sec9.1.12) Bytes over are discarded in RLC.
// In unacknowledged mode, LLC-PDUs delivered in the order received, with 0-bits for missing blocks.
// The minimum payload size (using CS-1) is 20 bytes (see RLCPayloadSize)
// Therefore, a single PDU may take 78 blocks.
const int RLC_PDU_MAX_LEN = 1560;
//typedef std::list<BitVector*> LLCSegmentList;
class RLCEngineBase
{
public:
// WS => number of blocks sent simultaneously, awaiting ack, before blocking.
// SNS => must be double WS, by definition.
static const unsigned mWS = 64; // Window Size
static const unsigned mSNS = 128; // Sequence Number Space
/**@name SN arithmetic */
int deltaSN(unsigned sn1, unsigned sn2) const;
int deltaSNS(unsigned sn1, unsigned sn2) const;
bool deltaEQ(unsigned sn1, unsigned sn2) const;
unsigned addSN(int sn1, int sn2) const; // Allow negative numbers.
void incSN(unsigned &psn);
virtual void engineDump(std::ostream &os) const = 0;
string engineDumpStr() const { std::ostringstream os; engineDump(os); return os.str(); }
};
enum RlcUpState {
RlcUpTransmit,
RlcUpQuiescent,
RlcUpPersistFinal,
RlcUpFinished,
};
// (pat) This is an RLC endpoint as per GSM 04.60 9.1
// It sits in layer 2.
class RLCUpEngine : public TBF, public RLCEngineBase
{
protected:
/**@name RLC state variables, from GSM 04.60 9. */
// We depend on the MS not to send us blocks with BSN > VQ+mWS, and inform
// the MS of our concept of VQ in the acknack we send.
// However, the only way we know the acknack is received may be that the MS
// sends more blocks.
struct {
unsigned VR; ///< highest BSN received + 1 modulo wSNS;
// 0 <= VR <= wSNS-1;
unsigned VQ; ///< lowest BSN not yet received (window base)
// In acknowledged mode, receive window is
// defined by: VQ <= BSN <= VQ + mWS
bool VN[mSNS]; ///< receive status of previous RLC data blocks
RLCUplinkDataBlock *RxQ[mSNS]; ///< assembly queue for inbound RLC data blocks
//unsigned RBSN; ///< BSN of incoming blocks.
} mSt;
//@}
#if INTERNAL_SGSN
ByteVector *mUpPDU; // The PDU being assembled.
#else
BSSG::BSSGMsgULUnitData *mUpPDU; // The PDU being assembled.
#endif
Bool_z mIncompletePDU; // Special case: If set, the PDU did not finish in this TBF;
// MS needs another TBF to send the rest of it.
// Dont think we need this if using unlimited dynamic mode uplink.
Bool_z mUpStalled; // MS is stalled, copied from each uplink block header.
static const unsigned mNumUpPerAckNack = 10;
UInt_z mNumUpBlocksSinceAckNack; // Number of blocks sent since the last acknack
UInt_z mTotalBlocksReceived;
UInt_z mUniqueBlocksReceived;
int mBytesPending;
#if UPLINK_PERSIST
RLCBSN_t mDataPersistFinalEndBSN; // When DataPersistFinal mode started.
bool mUpPersistentMode;
GprsTimer mtUpKeepAliveTimer; // Time to next keep alive.
GprsTimer mtUpPersistTimer; // How long TBF persists while idle.
#endif
// The MS keeps a running total of USF slots granted to the MS.
// The difference between the current value and the starting value divided
// by the number of unique blocks received is a measure of the loss ratio,
// although that breaks down if there are multiple simultaneous uplink TBFs.
unsigned mStartUsfGrants;
//unsigned mNumServiceSlotsSkipped; // Reality check disaster recovery.
RlcUpState mtUpState; // Set after all blocks have been received.
public:
RLCUpEngine(MSInfo *wms,int wOctetCount);
~RLCUpEngine();
void addUpPDU(BitVector&);
void sendPDU();
bool stalled() const { return mUpStalled; }
#if UPLINK_PERSIST
bool ulPersistentMode();
bool sendNonFinalAckNack(PDCHL1Downlink *down);
#endif
void engineRecvDataBlock(RLCUplinkDataBlock* block, int tn);
void engineUpAdvanceWindow();
bool engineService(PDCHL1Downlink *down); // Thats right: we pass the downlink.
float engineDesiredUtilization();
void engineDump(std::ostream &os) const;
void engineGetStats(unsigned *pSlotsTotal, unsigned *pSlotsUsed, unsigned *pGrants) const {
*pSlotsTotal = mTotalBlocksReceived;
*pSlotsUsed = mUniqueBlocksReceived;
*pGrants = mtMS->msNumDataUSFGrants - mStartUsfGrants;
}
int engineGetBytesPending() { return mBytesPending; }
RLCMsgPacketUplinkAckNack * engineUpAckNack();
TBF *getTBF() { return dynamic_cast<TBF*>(this);}
};
class RLCDownEngine : public TBF, public RLCEngineBase
{
public:
/**@name RLC state variables, from GSM 04.60 9. */
//@{
struct {
unsigned VS; ///< BSN of next RLC data block for tx
// After receipt of acknack message, VS is set back to VA.
unsigned VA; ///< BSN of oldest un-acked RLC data block
bool VB[mSNS]; ///< ack status of pending RLC data blocks (true = acked)
RLCDownlinkDataBlock *TxQ[mSNS]; ///< unacked RLC data blocks saved for re-tx
//int sendTime[mSNS]; ///<RLCBSN when block was first sent.
// VCS is used for multi-block Control Messages, so does not apply to us.
// bool VCS; ///< 0 or 1 indicating state for multi-block RLC control messages.
unsigned TxQNum; // One greater than last block in queue. It wraps around.
} mSt;
//@}
// This is additional state:
UInt_z mPrevAckSsn; // The SSN of the previous downlinkAckNack.
UInt_z mResendSsn; // Resend blocks with SN less than this.
UInt_z mPrevAckBlockCount; // Saves value of mTotalBlocksSent from last downlinkAckNack.
Bool_z mDownStalled; // Yes, set when the downlink is stalled.
Bool_z mDownFinished; // Set when we have sent the block with the FBI indicator.
Bool_z mAllAcked; // Have all the blocks been acked?
UInt_z mNumDownBlocksSinceAckNack;
unsigned mNumDownPerAckNack;
UInt_z mTotalBlocksSent; // Total sent; some of them may have been lost in transmission
UInt_z mTotalDataBlocksSent; // Number of blocks in the TBF.
UInt_z mUniqueDataBlocksSent; // Number of blocks in the TBF.
#if INTERNAL_SGSN==0
// BSSG no longer used:
BSSG::BSSGMsgDLUnitData *mBSSGDlMsg; // The downlink message that created this TBF.
#endif
// We save it because the RLCEngine has a pointer into it,
// and delete it when we delete the TBF.
ByteVector mDownPDU; // The PDU part of mBSSGDlMsg. Automatic destruction.
SGSN::GprsSgsnDownlinkPdu *mDownlinkPdu; // This is the most recent sdu sent in the TBF.
// We keep it around so we can retry the TBF if necessary.
// Moved to class TBF:
GprsTimer mtDownKeepAliveTimer; // Time to next keep alive.
GprsTimer mtDownPersistTimer; // How long TBF persists while idle.
RLCDownEngine(MSInfo *wms) :
TBF(wms,RLCDir::Down),
#if INTERNAL_SGSN==0
mBSSGDlMsg(0), // No longer used.
#endif
mDownlinkPdu(0)
{
memset(&mSt,0,sizeof(mSt));
mNumDownPerAckNack = gConfig.getNum("GPRS.TBF.Downlink.Poll1");
}
~RLCDownEngine();
// Is the downlink engine stalled, waiting for acknack messages?
bool stalled() const { return mDownStalled; }
//bool finished() { return mSt.VS >= mSt.TxQNum; }
unsigned engineDownPDUSize() const { return mDownPDU.size(); }
RLCDownlinkDataBlock *getBlock(unsigned vs,int tn);
void engineWriteHighSide(SGSN::GprsSgsnDownlinkPdu *dlmsg); // TODO: get rid of this.
bool dlPersistentMode(); // Are we using persistent TBF mode?
bool dataAvail(); // Is more downlink data available?
RLCDownlinkDataBlock* engineFillBlock(unsigned bsn,int tn);
void engineRecvAckNack(const RLCMsgPacketDownlinkAckNack*);
bool engineService(PDCHL1Downlink *down);
float engineDesiredUtilization();
void engineDump(std::ostream &os) const;
void engineGetStats(unsigned *pSlotsTotal, unsigned *pSlotsUsed, unsigned *pGrants) const {
// We dont care about the control blocks sent, only the data.
//*pSlotsTotal = mTotalBlocksSent; // Number of blocks we have sent.
*pSlotsTotal = mTotalDataBlocksSent; // Number of blocks we have sent.
*pSlotsUsed = mUniqueDataBlocksSent; // Number of blocks MS has acknowledged.
*pGrants = 0; // Inapplicable to downlink.
}
int engineGetBytesPending() { return 0; } // TODO, but it is a negligible amount
//bool isLastUABlock();
//unsigned blocksToGo();
bool resendNeeded(int bsn);
void advanceVS();
void advanceVA();
TBF *getTBF() { return dynamic_cast<TBF*>(this);}
};
// This incorporates both an up and down engine, but only one of them is used.
//class RLCEngine :
// public RLCUpEngine,
// public RLCDownEngine
//{
// public:
// RLCEngine(TBF *);
// ~RLCEngine();
//};
}; // namespace GPRS
#endif