/* * OpenBTS provides an open source alternative to legacy telco protocols and * traditionally complex, proprietary hardware systems. * * Copyright 2014 Range Networks, Inc. * * This software is distributed under the terms of the GNU Affero General * Public License version 3. See the COPYING and NOTICE files in the main * directory for licensing information. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. */ #ifndef URLC_H #define URLC_H #include "MemoryLeak.h" #include "Utils.h" #include "ByteVector.h" #include "ScalarTypes.h" #include "Threads.h" #include "Interthread.h" #include "GSMCommon.h" #include "URRCTrCh.h" #include "URRCRB.h" #include "UMTSTransfer.h" #include typedef GSM::Z100Timer Z100; // Notes on this code: // Pat Thompson 2/2012 // // o The high end of RLC works on ByteVectors, but the low end of MAC (PHY) works on BitVectors. // The conversion occurs on the way through MAC and RLC. // The downlink from RLC->MAC is by ByteVector, and MAC does the conversion, // which is more efficient because the MAC will know how many extra bits // of header are required before it allocates the BitVector. // The uplink MAC->RLC is by BitVector, and RLC does the conversion, which allows // the RLC entitiy to throw the BitVector away without conversion if necessary. // Note that TM allows transport block sizes that are not divisible by 8, which is irrelevant; // in downlink the channel knows the exact size in bits, and in uplink, we are always // passing full bytes anyway (either to the ASN decoder, or out to the internet) // and the ByteVector in these cases has the final byte padded with 0 bits. // // o UMTS provides a huge set of RLC configuration parameters, and sadly, // the example configurations, that we will probably follow, utilize a goodly set of them. // As a way to reduce the complexity of the classes herein, // the RLC Configuration classes are completely separated from the RLC engine classes. // There is one configuration class for each type of engine class. // Essentially, the code for channel setup is in the configuration classes, which // are then invariant after RLC creation when the engine classes take over to process data. // // o There are two sets of RLC configuration structures - the ones in here and // the ones in the RRC interface headers. Why is that? // The configuration parameters herein mostly follow the naming conventions // of the RLC spec 25.322 sec 8: Primitives between RLC and Upper Layers, // are segregated by the engine subsection (Am, Um, AmUm, or Tm) they control, // and are designed for simplicity of use by the RLC engines. // Some of the RLC working parameters are computed rather than specified. // The RLC parameters specified in the RRC IEs in spec 25.331 are often wildly different, // follow a bizarre layout that is designed for efficient transfer to the UE, // and also vary based on the RRC release version. // In fact, sometimes it is even hard to figure out what RRC param corresponds to what RLC param. // // o OK, in that case then why are there RLC config structures in the RRC header // files at all, why didn't you just use ASN structures? Several reasons. // We need to specify the programming for Radio Bearers, then send // that programming both to our own RLC/MAC/etc and to the UE. // The programming examples in the specs (eg, 25.331 sec 13.7) // use yet another syntax and naming convention that matches neither the RRC IE specs // nor the RLC specs. Rather than hand-code those examples each time (into what?), // a process that I think would be extremely bug-prone, // or create a bunch of flat functions to do it, I created classes with // structure modelled closely on ASN, and added setup configuration methods // matching the alternate syntax. // Those structures are programmed using the setup functions and then become the // master configuration, which can be converted to ASN to send to the UE or // sent to this file to configure newly created RLC entities here. // // o For TM and UM the Trans and Recv classes can be allocated separately, in fact, // the ubiquitous SRB0 uses TM uplink and UM downlink. // However, the AM Trans and Recv classes are not allocated separately, you must // allocate a single URlcAm entity which encompasses both Trans and Recv. // // o The URlcTransAmUm and URlcRecvAmUm engine classes, and their respective // configuration classes, are not a strict union of the Am and Um classes. // One of the biggest pieces of code here is the fillPduData function, which // turns SDUs into PDUS and shares 95% functionality between the Am and Um cases. // Rather than duplicating that huge function in the Am and Um code paths, // it is placed in the TransAmUm class and has the functionality needed for both // Am and Um paths, and I placed the configuration data needed by that function // in the ConfigAmUm class, even though some of the configuration data is // specific only to Am or Um mode. // // o For testing, use a dummy MAC that turns the vectors back around and sends // them back up through RLC, then just send random vectors. using namespace std; namespace UMTS { // URlcSN is intrinsically unsigned, but we often due arithmetic on them // including subtraction, so use signed. typedef Int_z URlcSN; // A modulo mSNS number. class UEInfo; extern unsigned computeDlRlcSize(RBInfo *rb, RrcTfs *dltfs); typedef void (*URlcHighSideFuncType)(ByteVector &sdu, RbId rbid); void l2RlcStart(); // GSM 25.322 defines UMTS RLC. // GSM 25.331 (RRC) see: // 10.3.4.23 is the RLC setup message. // 10.3.4.21 is RB Mapping Info. // Specifies RLC sizes for each logical channel. // 10.3.4.20 RB Information to setup. // 10.3.5.23 Transport Format Set - this is where you configure // the RLC Size and the mapping of RB to logical channel (for RLC use) // and transport channel (for MAC use). // Note that there are multiple RLC sizes in this struct for different channels. // 25.322 8.1 Parameters // AM-DATA, UM-DATA TM-DATA, : // UE-ID type indicator: RNTI type, either U-RNTI or C-RNTI for this SDU. // DiscardReq // MUI - identifies SDU for use in confirmation or discard notification. // AM-DATA only: // CNF - notify upper layers when SDU reception (ie, all PDUs making up the SDU) confirmed. // General Config: // E/R - Establishment, Re-establishment (na to TM mode), release or modification. // Stop (UM/AM) // Continue (UM/AM) // Ciphering Elements (UM/AM only) // SN_Delivery (UM only) // AM_Parameters: // flexible PDU size // AMD PDU size or largest PDU size (depending on flexible size) // LI size // in-sequence delivery (to upper layers) // timer values // Use of a special value in HE field. // Protocol parameter values // Polling triggers // Status triggers // Periodic Status blocking configuration // SDU discard mode // Minimum WSN // Send MRW. // (pat) These came up while coding: // HFN // UM_Parameters: // Timer_Discard value // Alternative E-bit interpretation // largest UpLink UM PDU size // largest DownLoad RLC UM LI size // SN_delivery // There are more that are applicable only to the UE see paragraph 11. static const int AmSNS = 4096; // 12 bits wide static const int UmSNS = 128; // 7 bits wide // This is the config as used by the URLC classes. // The URLC Config is specified primarily in 25.322 8.2 Primitive Parameters, // paragraph 7 for AM_parameters, paragraph 11 for UM_parameters, // and paragraph 12 for TM_parameters. // Parameters that are implemented in the common URlcTransAmUm or URlcRecvAmUm classes // are defined in the ConfigCommon class even if they only apply to one mode. struct URlcConfigAmUm { // 25.331 RRC 10.3.4.25 Transmission RLC Discard IE ///<@name RLC SDU Discard Mode 25.322 9.7.3 // For TM and UM only TimerBasedWithoutExplicitSignaling, which is irrelevant // to us because our TM and UM implementation just ignores this field. // For AM the only AM mode we support is NoDiscard. // So this whole DiscardMode is currently irrelevant. //TransmissionRlcDiscard::SduDiscardMode mDiscardMode; TransmissionRlcDiscard mRlcDiscard; // For Am, the pdu size is specifed in the RlcInfo IE, and Um uses any of the // pdu sizes specified in the TransferFormatSet. // In REL-7, they added flexible pdu size to AM, which I think just makes it work like UM. // However, we are not going to support multiple PduSizes, so there is just one, // and here it is: // We dont use 'flexible size', so it is not even mentioned here. unsigned mDlPduSizeBytes; // The uplink and downlink LI size can be configured separately, several different ways. // AM mode is determined from the pduSize, or in REL-7, if flexiblePduSize is selected, // it is specified in the RlcInfo 25.331 10.3.4.23 // For UM mode the downlink LISize is in RlcInfo, and the uplink LIsize is determined // indirectly from the largest PDU in the TransferFormatSet using rules // in 25.322 (RLC) 9.2.2.8 (Length Indicator) unsigned mUlLiSizeBytes, mUlLiSizeBits; unsigned mDlLiSizeBytes, mDlLiSizeBits; // 9.2.2.8 Bool_z mIsSharedRlc; // This is the special RLC to run CCCH. URlcConfigAmUm(URlcMode rlcMode, URlcInfo &rlcInfo, RrcTfs &dltfs, unsigned dlPduSize); // These are UM-only parameters, but implemented in the URlcTransAmUm class, so they go here. struct { // This is post-REL-6 and is not in our ASN spec. // For AM mode, it should always be FALSE. Bool_z mSN_Delivery; // This config option causes SN indications to be passed to higher layers // in UM mode, and further indicates not to concatenate SDUs in a PDU. // Must be FALSE for AM mode. Bool_z mAltEBitInterpretation; // 9.2.2.5 } UM; static const unsigned mMaxSduSize = 1502; }; #if URLC_IMPLEMENTATION URlcConfigAmUm::URlcConfigAmUm(URlcMode rlcMode, URlcInfo &rlcInfo, RrcTfs &dltfs, unsigned dlPduSize): mRlcDiscard(rlcInfo.mul.mTransmissionRlcDiscard) { if (rlcMode == URlcModeAm) { // We dont need this, and I dont think it is even defined in our REL of the ASN description. //if (mRlcPduSizeFlexible) { // mDlPduSizeBytes = dltfs.getLargestPduSize(); // mUlLiSize = mDlLiSizeBits = rlcInfo.mdl.u.AM.mLengthIndicatorSize; //} else { // In the specs for the default RB configurations, the pdusize is sometimes specified // in the rlcInfo options, having been calculated by hand, but sometimes not. // We are going to figure out the RLC size directly // for AM and UM mode from the TB size and Mac header bits and pass it in // through the configuration constructors. //mDlPduSizeBytes = rlcInfo.mdl.u.AM.mDlRlcPduSize; mDlPduSizeBytes = dlPduSize; mUlLiSizeBits = mDlLiSizeBits = (mDlPduSizeBytes <= 126) ? 7 : 15; //mUlLiSizeBits = 7; } } else { mDlPduSizeBytes = dlPduSize; // TODO: Our version of ASN does not transmit mDlUmRlcLISize, and no one ever set it. // TODO: The RLC size should be determined from the TFS. //mDlLiSizeBits = rlcInfo.mdl.u.UM.mDlUmRlcLISize; // 8.6.4.9 of 25.331 says that Downlink UM LI is 7 bits unless explicitly indicated, regardless of PDU size mDlLiSizeBits = 7; //(mDlPduSizeBytes <= 125) ? 7 : 15; mUlLiSizeBits = (mDlPduSizeBytes <= 125) ? 7 : 15; //mUlLiSizeBits = 7; } switch (mDlLiSizeBits) { case 7: mDlLiSizeBytes = 1; break; case 15: mDlLiSizeBytes = 2; break; default: assert(0); } switch (mUlLiSizeBits) { case 7: mUlLiSizeBytes = 1; break; case 15: mUlLiSizeBytes = 2; break; default: assert(0); } //printf("mode=%s pdusize=%d dllibits=%d dllibytes=%d\n", rlcMode == URlcModeAm?"AM":"UM", //mDlPduSizeBytes,mDlLiSizeBits,mDlLiSizeBytes); } #endif // 25.322 8.2 paragraph 7: Primitive parameters for AM. // These are the primitive paramters as used by the RLC code. // We create this struct when we create an RLC entity. // The parameter names in the RLC spec do not match 25.321 RRC, and their configured values // come from several places, including 10.4.3.23 RlcInfo, or may be computed from the // TransportSet, for example the Max RLC pdu size. // Therefore we will copy the subset of parameters that we actually use from the RRC IEs // into this structure when we create an RLC entity. struct URlcConfigAm : public /*virtual*/ URlcConfigAmUm // AM only config stuff. { bool mInSequenceDeliveryIndication; ///<@name RLC Protocol Parameters, 3GPP 25.322 9.6 unsigned mMaxDAT; //unsigned mPoll_Window; unsigned mMaxRST; // Max-1 number of RESET PDUs, upper limit for VTRST unsigned mConfigured_Tx_Window_Size; unsigned mConfigured_Rx_Window_Size; //unsigned mMaxMRW; // unimplemented ///<@name RLC Timer values 25.322 9.5 // Timer_Poll, Timer_Poll_Prohibit, Timer_Poll_Periodic - see PollingInfo // mTimer_Discard; Not implemented yet, but we probably want it. // Timer_Status_Prohibit, Timer_Status_Periodic - see RLC Status Triggers. unsigned mTimerRSTValue; // 11.4.2 // Timer_MRW not implemented. ///<@name RLC Polling Triggers 25.322 9.7.1 RrcPollingInfo mPoll; ///<@name RLC Status Triggers 25.322 9.7.2 bool mStatusDetectionOfMissingPDU; // TODO: mTimer_Status_Periodic ///<@name RLC Periodical Status Blocking 25.322 9.7.2 // TODO: mTimer_Status_Prohibit TODO ///<@name RLC Minimum WSN 25.322 9.2.2.11.3 // WONT DO: unsigned mMinWSN; ///<@name RLC Send MRW. // WONT DO: unsigned mSendMRW; //unsigned mHFN; URlcConfigAm(URlcInfo &rlcInfo, RrcTfs &dltfs, unsigned dlPduSize); }; #if URLC_IMPLEMENTATION URlcConfigAm::URlcConfigAm(URlcInfo &rlcInfo, RrcTfs &dltfs, unsigned dlPduSize) : URlcConfigAmUm(URlcModeAm,rlcInfo,dltfs,dlPduSize), mInSequenceDeliveryIndication(rlcInfo.mdl.u.AM.mInSequenceDelivery), mMaxDAT(rlcInfo.mul.mTransmissionRlcDiscard.mMaxDAT), mMaxRST(rlcInfo.mul.u.AM.mMaxRST), mConfigured_Tx_Window_Size(rlcInfo.mdl.u.AM.mReceivingWindowSize), mConfigured_Rx_Window_Size(rlcInfo.mul.u.AM.mTransmissionWindowSize), mTimerRSTValue(rlcInfo.mul.u.AM.mTimerRST), mPoll(rlcInfo.mul.u.AM.mPollingInfo), mStatusDetectionOfMissingPDU(rlcInfo.mdl.u.AM.mDownlinkRlcStatusInfo.mMissingPduIndicator) {} #endif // This contains both uplink and downlink config, although it is possible // to use one without the other. struct URlcConfigUm : public /*virtual*/ URlcConfigAmUm { //unsigned largestUlUmdPduSize // 9.2.2.8 Computed from Transport Set See mPduSize. #if RLC_OUT_OF_SEQ_OPTIONS bool mOSD; // Out of Sequence delivery. // 11.2.3 bool mOSR; // Out of Sequence reception. // 11.2.3.1 //bool mUseOSD; // UM downlink only, use out-of-sequence-sdu-delivery //unsigned mOSD_Window_Size; // UM downlink only, only if UseOSD //Z100 mTimerOSD // UM downlink only, only if UseOSD, to delete stored PDUs see 11.2.3.2 //bool SN_Delivery; // REL-7 and up. Used in OSD mode to indicate SDU SN to higher layers. #endif // I dont understand why there are both Configured_Rx_Window_Size and OSD_Window_Size. // I think they are the same thing. //unsigned mConfigured_Rx_Window_Size; // In UM only needed if UseOSD. //unsigned mOSD_Window_Size; // UM downlink only, only if UseOSD. //bool mUseDAR; // UM uplink only. //unsigned mDAR_Window_Size; // UM uplink only, only if DAR. //Z100 mTimerDAR; // UM uplink only, only if DAR. URlcConfigUm(URlcInfo &rlcInfo, RrcTfs &dltfs, unsigned dlPduSize) : URlcConfigAmUm(URlcModeUm, rlcInfo, dltfs, dlPduSize) // More To Do {} }; class URlcBase { // 25.322 9.7.6 RLC Stop mode. // The RLC_STOP mode is supposed to block the RLC at the low end, both incoming and outgoing, // but continues to accept SDUs at the high end. // This functionality is needed because of the dorky way they do the cell state transition // from CELL_FACH to CELL_DCH. // I only implemented outgoing, then stopped - stopping incoming would need to a special queue // and doesnt make any sense any way becaues the corresponding RLC entity in the UE // has already changed its state, so we need to process the incoming PDUs. public: enum RlcState { RLC_RUN, RLC_STOP, RLC_SUSPEND // Not supported. } mRlcState; void rlcStop() { mRlcState = RLC_STOP; } void rlcResume() { mRlcState = RLC_RUN; } enum SufiType { // Used for AM only. SUFI_NO_MORE = 0, SUFI_WINDOW = 1, SUFI_ACK = 2, SUFI_LIST = 3, SUFI_BITMAP = 4, SUFI_RLIST = 5, SUFI_MRW = 6, // Note: we dont need to use the MRW, implementing reset is sufficient. SUFI_MRW_ACK = 7, SUFI_POLL = 8 }; enum PduType { // Used for AM only. PDUTYPE_STATUS = 0, PDUTYPE_RESET = 1, PDUTYPE_RESET_ACK = 2 }; URlcMode mRlcMode; unsigned mSNS; // The Sequence Number Space for this RLC entity. UEInfo *mUep; // The UE that owns us. May be NULL for RLC for CCCH . RbId mrbid; // The RadioBearer that we were created for. URlcBase(URlcMode wRlcMode, UEInfo *wUep, RBInfo *wRbp); // This initializer should never be called because we are a virtual class, // so only the most most derived (aka final) class needs an initializer, // and they are always provided. URlcBase() { assert(0); } // Modulo mSNS arithmetic functions. int deltaSN(URlcSN sn1, URlcSN sn2); URlcSN addSN(URlcSN sn1, URlcSN sn2); URlcSN minSN(URlcSN sn1, URlcSN sn2); URlcSN maxSN(URlcSN sn1, URlcSN sn2); void incSN(URlcSN &psn); // Currently this is used only for debug messages: virtual unsigned getRlcHeaderSize() { return 0; } // If not over-ridden, return 0. }; #if URLC_IMPLEMENTATION URlcBase::URlcBase(URlcMode wRlcMode, UEInfo *wUep, RBInfo *wRbp) : mRlcState(RLC_RUN), mRlcMode(wRlcMode), mUep(wUep), mrbid(wRbp->mRbId) { switch (wRlcMode) { case URlcModeTm: mSNS = 0; break; case URlcModeUm: mSNS = UmSNS; break; case URlcModeAm: mSNS = AmSNS; break; default: assert(0); } } #endif DEFINE_MEMORY_LEAK_DETECTOR_CLASS(URlcPdu,MemCheckURlcPdu) // All purpose pdu between rlc and mac. // Note that only TM (transparent mode) is allowed to be non-byte aligned. class URlcBasePdu : public ByteVector, public MemCheckURlcPdu { public: string mDescr; // For debugging, description of content. URlcBasePdu(unsigned size, string &wDescr); URlcBasePdu(ByteVector &other, string &wDescr); URlcBasePdu(const BitVector &bits, string &wDescr); }; #if URLC_IMPLEMENTATION URlcBasePdu::URlcBasePdu(ByteVector &other, string &wDescr) : ByteVector(other), mDescr(wDescr) {} URlcBasePdu::URlcBasePdu(unsigned size, string &wDescr) : ByteVector(size), mDescr(wDescr) {} URlcBasePdu::URlcBasePdu(const BitVector &bits, string &wDescr) : ByteVector(bits), mDescr(wDescr) {} #endif DEFINE_MEMORY_LEAK_DETECTOR_CLASS(URlcDownSdu,MemCheckURlcDownSdu) // The Downlink SDU takes possession of the ByteVector in the most efficient way. // Note that there is no "priority" sent with the SDU because there are // different RBs for different purposes and the priority is implicit // in the RLC entity on which this is sent. //struct URlcDownSdu : ByteVector, public MemCheckURlcDownSdu struct URlcDownSdu : public URlcBasePdu { ByteVector *sduData() { return this; } bool mDiscarded; // Set if one or more SDUs were discarded at this SDU position. bool mDiscardReq; // Discard request from upper layer. unsigned mMUI; // SDU identifier, aka Message Unit Identifier. string mDescr; URlcDownSdu *mNext; // The SDU can be placed in a SingleLinkedList URlcDownSdu *next() { return mNext; } void setNext(URlcDownSdu *next) { mNext = next; } URlcDownSdu(ByteVector &wData, bool wDR, unsigned wMUI, string wDescr) : URlcBasePdu(wData,wDescr), mDiscarded(0), mDiscardReq(wDR), mMUI(wMUI), mNext(0) {} // This class is always used by pointer and manually deleted, so no copy constructor // is needed. //void free() { if (mData) { delete mData; } delete this; } void free() { delete this; } size_t size() { return mDiscarded ? 0 : ByteVector::size(); } }; // The Uplink SDU is just a ByteVector. typedef ByteVector URlcUpSdu; // May be any mode, and for AM, may be data or control pdu. // PDU for UM and AM mode that use a common transmitter for reasons documented above, // so we use the same PDU also. // UM PDU: // SN:7; // Used for reassembly // E:1 // LI:7 or 15; // E:1; // Meaning configured by upper layers. Can be "normal" (below) or // // Alternative: 0 => next field is a complete SDU which // // is not segmented concatenated or padded // // 1=> next field is length indicator+E bit. // ... // Data // Optional PAD //class URlcPdu : public ByteVector, public Text2Str, public MemCheckURlcPdu class URlcPdu : public URlcBasePdu { public: URlcBase *mOwner; // The TM, UM or AM entity that owns this pdu. // For UM and AM data pdus: unsigned mPaddingStart; // Location of padding or 0, used for piggybacked status. unsigned mPaddingLILocation; // Location of LI indicator for padding, or 0. unsigned mVTDAT; // For AM only, how many times PDU has been scheduled. bool mNacked; // true if pdu has been negatively acknowledged. // Fields so this can be placed in a SingleLinkList: URlcPdu *mNext; // The SDU can be placed in a SingleLinkedList URlcPdu *next() { return mNext; } void setNext(URlcPdu *next) { mNext = next; } URlcPdu(unsigned wSize, URlcBase *wOwner,string wDescr); URlcPdu(const BitVector &bits, URlcBase *wOwner,string wDescr); explicit URlcPdu(URlcPdu *other); // UM PDU fields: void setUmE(unsigned ebit) { setBit(7,ebit); } unsigned getUmE() const { return getBit(7); } void setUmSN(unsigned sn) { setField(0,sn,7); } unsigned getUmSN() const { return getField(0,7); } // AM PDU fields: void setAmSN(unsigned sn) { setField2(0,1,sn,12); } unsigned getAmSN() const { return getField2(0,1,12); } void setAmDC(bool isData) { setField2(0,0,isData,1); } int getAmDC() const { return getBit(0); } static const int sPollBit = 13; void setAmP(bool P) { setField2(1,(sPollBit-8),P,1); } unsigned getAmP(bool P) const { return getField2(1,(sPollBit-8),1); } unsigned getAmHE() const { return getField2(1,6,2); } // but only the bottom bit of HE is used. void setAmHE(unsigned HE) { setField2(1,6,HE,2); } // Common functions: enum URlcMode rlcMode() const { return mOwner->mRlcMode; } void setSN(unsigned sn) { if (rlcMode() == URlcModeUm) setUmSN(sn); else setAmSN(sn); } unsigned getSN() const { return (rlcMode() == URlcModeUm) ? getUmSN() : getAmSN(); } void setEIndicator(bool ebit) { if (rlcMode() == URlcModeUm) setUmE(ebit); else setAmHE(ebit); } bool getEIndicator() const { return (rlcMode() == URlcModeUm) ? getUmE() : getAmHE(); } // For UM and AM data pdus: // E==1 implies there is a following LI+E (opposite of GPRS E definition.) void appendLIandE(unsigned licnt, unsigned E, unsigned lisize); // For debugging: unsigned getPayloadSize() const { return (mPaddingStart ? mPaddingStart : size()) - mOwner->getRlcHeaderSize(); } void text(std::ostream &os) const; }; #if URLC_IMPLEMENTATION URlcPdu::URlcPdu(unsigned wSize, URlcBase *wOwner, string wDescr) : URlcBasePdu(wSize,wDescr), mOwner(wOwner), mPaddingStart(0), mPaddingLILocation(0), mVTDAT(0), mNacked(0), mNext(0) {} URlcPdu::URlcPdu(const BitVector &bits, URlcBase *wOwner, string wDescr) : URlcBasePdu(bits, wDescr), mOwner(wOwner), mPaddingStart(0), mPaddingLILocation(0), mVTDAT(0), mNacked(0), mNext(0) {} URlcPdu::URlcPdu(URlcPdu *other) : URlcBasePdu(*other,other->mDescr), mOwner(other->mOwner), mPaddingStart(other->mPaddingStart), mPaddingLILocation(other->mPaddingLILocation), mVTDAT(other->mVTDAT), mNacked(other->mNacked), mNext(0) {} //URlcPdu::URlcPdu(ByteVector *other, string wDescr) // Used to manufacture URlcPdu from URlcDownSdu for RLC-TM. // : ByteVector(*other), mOwner(0), mDescr(wDescr), // mPaddingStart(0), // mPaddingLILocation(0), // mVTDAT(0), mNacked(0), mNext(0) // {} #endif //class URlcPduUm : public URlcPdu //{ public: // URlcPduUm(unsigned wsize,unsigned wlisize) : URlcPdu(wsize,wlisize) {} // void setSN(unsigned sn) { setField(0,sn,7); } // unsigned getSN() { return getField(0,7); } //}; // AM PDU: // DC:1; 1 for data pdu, 0 for control pdu // SN:12; // Used for retransmission. // P:1; // Polling bit: 1=> request a status report. // HE:2; // Header Extension Type. // // 0 => succeeding octet contains data // // 1 => succeeding octet contains LI+E // // 2 => succeeding octet contains data and the last octet // // of the PDU is last octet of SDU, but only if // // "Use special value of HE field" is configured. // // 3 => reserved. // LI:7 or 15; // Has special encoding, see 9.2.2.8 // E:1; // Always "Normal" E-bit interpretation, whcih is: // // 0 => next field is data, status or padding; // // 1=> next field is another length indicator + E bit. // ... // Data // Optional Piggybacked STATUS PDU. // Optional PAD //class URlcPduAm : public URlcPdu //{ // void setSN(unsigned sn) { setField2(0,1,sn,12); } // unsigned getSN() { return getField2(0,1,12); } // void setDC(bool isData) { setField2(0,0,isData,1); } // void setP(bool P) { setField2(0,5,P,1); } // unsigned getP(bool P) { return getField2(0,5,1); } // unsigned getHE(unsigned HE) { return getField2(0,6,2); } // void setHE(unsigned HE) { setField2(0,6,HE,2); } // void setE(bool val) { setHE(val ? 1 : 0); } //}; // AM Status PDU: Size bounded by maximum RLC PDU size used by the logical channel. // DC:1; 0 for control pdu // PDUType:3; // 0 = STATUS pdu type // SUFI(1) //SUFI defined 9.2.2.11 // ... // SUFI(k) // PAD to byte boundary, or if fixed size logical channel, to that PDU size. // RESET and RESET ACK PDU: // DC:1; // PDUType:3; //1 = RESET, 2 = RESET ACK // RSN:1; // 9.2.2.13. Dont understand. Indicates RESET is resent? // R1:3; // Byte alignment, always 0. // HFNI:20; // Indicates the Hyper Frame Number? // PAD typedef SingleLinkList SduList_t; typedef InterthreadQueue > PduList_t; // Any mode transmitter class URlcTrans : public virtual URlcBase { friend class URlcTransTm; friend class URlcTransAm; friend class URlcTransUm; friend class URlcTransAmUm; // The SduTxQ consists of the complete vectors in the list, plus mSplitSdu, // which is used only for UM and AM modes to save the SDU currently being processed. Mutex mQLock; // Lock for SduTxQ. SduList_t mSduTxQ; URlcDownSdu *mSplitSdu; // mVTSDU incremeted for every SDU transmission when the first SDU segment // is scheduled to be transmitted the first time. // When == Poll_SDU, send a poll and set this to 0. // Used when for Am mode "poll every Poll_SDU" is configured. unsigned mVTSDU; // TODO: This count will be off if an sdu was deleted, because // am empty place-holder is left in the sduq. Not worth worrying about now. unsigned getSduCnt(); virtual bool pdusFinished() = 0; unsigned rlcGetSduQBytesAvail(); // If exceeded we have to throw away some SDUs. // Where is this in the spec? unsigned mTransmissionBufferSizeBytes; string mRlcid; public: URlcTrans(); virtual unsigned rlcGetBytesAvail() = 0; // Higher layer sends something to RLC. Same function for all modes: // put in the queue, but check for overflow. void rlcWriteHighSide(ByteVector &sdu, bool DR, unsigned MUI, string descr); // The mutex lock for both of these is in URlcTransAm::readLowSidePdu() virtual void rlcPullLowSide(unsigned amt) = 0; virtual URlcBasePdu *rlcReadLowSide() = 0; // There is no guarantee that all the PDUs are the same size when in TM. // These functions that interact only with the mPduOutQ do not need // further mutex protection beyond what the Q provides. virtual unsigned rlcGetPduCnt() = 0; virtual unsigned rlcGetFirstPduSizeBits() = 0; virtual unsigned rlcGetDlPduSizeBytes() { return 0; } // Not defined for RLC-TM, so return 0. virtual void triggerReset() { } void textTrans(std::ostream &os); const char *rlcid() { return mRlcid.c_str(); } virtual void text(std::ostream &os) = 0; }; #if URLC_IMPLEMENTATION URlcTrans::URlcTrans() : mSplitSdu(0), mVTSDU(0) { mTransmissionBufferSizeBytes = gConfig.getNum("UMTS.RLC.TransmissionBufferSize"); } unsigned URlcTrans::rlcGetSduQBytesAvail() { ScopedLock lock(mQLock); unsigned partialsize = mSplitSdu ? mSplitSdu->size() : 0; return mSduTxQ.totalSize() + partialsize; } unsigned URlcTrans::getSduCnt() { ScopedLock lock(mQLock); // extra cautious return mSduTxQ.size() + (mSplitSdu?1:0); } #endif class URlcTransTm : public virtual URlcBase, public URlcTrans { public: URlcTransTm(RBInfo *rbInfo, UEInfo *uep) : URlcBase(URlcModeTm,uep,rbInfo) {} // There is only an sduq, not a pduq, and reading a pdu comes straight from the sduq. void rlcPullLowSide(unsigned amt) {} unsigned rlcGetPduCnt() { return getSduCnt(); } unsigned rlcGetFirstPduSizeBits(); unsigned rlcGetBytesAvail(); // Just turn the SDU into a PDU and send it along. URlcBasePdu *rlcReadLowSide(); void text(std::ostream &os) { textTrans(os); } bool pdusFinished() { assert(mSplitSdu == NULL); return getSduCnt() == 0; } }; #if URLC_IMPLEMENTATION unsigned URlcTransTm::rlcGetFirstPduSizeBits() { ScopedLock lock(mQLock); if (mRlcState == RLC_STOP) {return 0;} URlcDownSdu *sdu; for (sdu = mSduTxQ.front(); sdu; sdu = sdu->next()) { if (sdu->mDiscarded) { continue; } // Ignoring deleted PDUs. return sdu->sizeBits(); } return 0; } unsigned URlcTransTm::rlcGetBytesAvail() { if (mRlcState == RLC_STOP) {return 0;} return rlcGetSduQBytesAvail(); } #endif class URlcTransAmUm : // Transmit common to AM and UM modes. public URlcTrans, public virtual URlcBase { friend class URlcTransAm; friend class URlcTransUm; URlcConfigAmUm *mConfig; PduList_t mPduOutQ; // For Am and Um modes: int mLILeftOver; // Special case flag carried over from previous PDU. // 1 => the previous sdu exactly filled the previous pdu. // 2 => the previous sdu was one byte short of filling previous pdu. // These vars are required for Am mode but we maintain them for all modes. unsigned mVTPDU; // Used when for Am mode "poll every Poll_PDU" is configured. // Incremented for every PDU transmission of any kind. // When == Poll_PDU, send a poll and set this to 0. // mVTPDU is the absolute PDU count, not modulo arithmetic. bool fillPduData(URlcPdu *pdu, unsigned pduHeaderSize,bool*newPdu); // Fill the pdu with sdu data. void transDoReset(); virtual URlcPdu *readLowSidePdu() = 0; unsigned rlcGetBytesAvail(); // Pull data through the RLC to fill the output queue, up to the specified amt, // which is the maximum amount needed for any Transport Format. void rlcPullLowSide(unsigned amt); unsigned rlcGetPduCnt() { return mPduOutQ.size(); } bool pdusFinished(); public: // This class is not allocated alone; it is part of URlcTransAm or URlcTransUm. URlcTransAmUm(URlcConfigAmUm *wConfig) : mConfig(wConfig) { transDoReset(); } // MAC reads the low side with this. URlcBasePdu *rlcReadLowSide(); // Return the size of the top PDU, or 0 if none. unsigned rlcGetFirstPduSizeBits(); void textAmUm(std::ostream &os); }; #if URLC_IMPLEMENTATION unsigned URlcTransAmUm::rlcGetBytesAvail() { if (mRlcState == RLC_STOP) {return 0;} // Can the pdus move from one queue to the other in between the locks? return rlcGetSduQBytesAvail() + mPduOutQ.totalSize(); } // Return the size of the top PDU, or 0 if none. unsigned URlcTransAmUm::rlcGetFirstPduSizeBits() { if (mRlcState == RLC_STOP) {return 0;} ByteVector *pdu = mPduOutQ.front(); return pdu ? pdu->sizeBits() : 0; } // If mLILeftOver is non-zero then we still need to send another PDU. bool URlcTransAmUm::pdusFinished() { return getSduCnt() == 0 && !mLILeftOver; } #endif // Any mode receiver. class URlcRecv : public virtual URlcBase { friend class URlcRecvTm; friend class URlcRecvAm; friend class URlcRecvUm; friend class URlcRecvAmUm; URlcHighSideFuncType mHighSideFunc; string mRlcid; // This is where outgoing SDUs arrive. // The SDU is allocated and must be deleted eventually. void rlcSendHighSide(URlcUpSdu *sdu); public: // This is where pdus come in from the MAC via a routine in the UEInfo // to map to the approriate RLC entity based on the RbId. virtual void rlcWriteLowSide(const BitVector &pdu) = 0; // This is used for testing. void rlcSetHighSide(URlcHighSideFuncType wHighSideFunc) { mHighSideFunc = wHighSideFunc; } URlcRecv() : mHighSideFunc(0) {} const char *rlcid() { return mRlcid.c_str(); } virtual void text(std::ostream &os) = 0; }; class URlcRecvTm : public virtual URlcBase, public URlcRecv { public: URlcRecvTm(RBInfo *rbInfo, UEInfo *uep); void rlcWriteLowSide(const BitVector &pdu); void text(std::ostream &os) {} }; #if URLC_IMPLEMENTATION URlcRecvTm::URlcRecvTm(RBInfo *rbInfo, UEInfo *uep) : URlcBase(URlcModeTm,uep,rbInfo) {} // TM Messages just pass through. void URlcRecvTm::rlcWriteLowSide(const BitVector &pdu) { URlcUpSdu *sdu = new ByteVector((pdu.size() + 7)/8); pdu.pack(sdu->begin()); rlcSendHighSide(sdu); } #endif class URlcRecvAmUm : // Receive common to AM and UM modes. public URlcRecv, public virtual URlcBase { friend class URlcRecvAm; friend class URlcRecvUm; URlcConfigAmUm *mConfig; URlcUpSdu *mUpSdu; // Partial SDU being assembled, or NULL. void sendSdu(); // Enqueue a completed SDU. bool mLostPdu; // This is UM only, but easier to put in this class. // It is set only in UM mode to indicate a lost pdu, // so we have to continue to discard until we find the start of a new sdu. void addUpSdu(ByteVector &payload); // Add to partially assembled SDU. void discardPartialSdu(); void ChopOneByteOffSdu(ByteVector &payload); void parsePduData(URlcPdu &pdu, int headersize, bool Eindicator, bool statusOnly); void recvDoReset() { discardPartialSdu(); mLostPdu = false; } public: // This class is not allocated alone; it is part of URlcRecvAm or URlcRecvUm. URlcRecvAmUm(URlcConfigAmUm *wConfig) : mConfig(wConfig), mUpSdu(0) {} URlcRecvAmUm(): mUpSdu(0) {} void textAmUm(std::ostream &os); }; class URlcAm; class URlcRecvAm; class URlcTransAm : public URlcTransAmUm // UMTS RLC Acknowledged Mode Transmitter { friend class URlcAm; friend class URlcRecvAm; friend class URlcTransAmUm; friend class URlcRecvAmUm; URlcConfigAm *mConfig; URlcPdu *readLowSidePdu(); URlcPdu *readLowSidePdu2(); // GSM25.322 9.4: AM Send State Variables URlcSN mVTS; // SN of next AMD PDU to be transmitted for the first time. URlcSN mVTA; // SN+1 of last in-sequence positively acknowledged pdu. // This is set by an incoming Ack SUFI 9.2.2.11.2 // SN of upper edge of transmission window = VTA + VTWS. URlcSN VTMS() { return addSN(mVTA,mVTWS); } UInt_z mVTRST; // Count number of RESET PDU sent before ack received. See 11.4.2 and 11.4.5.1. // We will not use MRW. // unsigned mVTMRW; // Count number of MRW command transmitted. URlcSN mVTWS; // Window size. Init to Configured_Tx_Window_size. // (pat) The window size is how many unacked blocks you can send before stalling. // Warning will robinson: it can theoretically be set up to SN-1, // which if allowed would cause deltaSN(), etc to fail. Z100 mTimer_Poll; // Set when a poll is sent, and stopped when the poll is answered. URlcSN mTimer_Poll_VTS; // Described in 25.322 9.5 paragraph a. //unsigned mTimer_Poll_SN; // The value of VTS at the time poll was sent; timer is // stopped if ack is received for this and preceding SN. Z100 mTimer_Poll_Prohibit; //Z100 mTimer_Discard; unimplemented Z100 mTimer_Poll_Periodic; // How often to poll. //Z100 mTimer_Status_Prohibit; // How often to send unsolicited (unpolled) status reports. //Z100 mTimer_Status_Periodic; unimplemented Z100 mTimer_RST; // start when RESET PDU sent, stop when RESET ACK received. // Upon expiry, resend RESET PDU with same RSN //Z100 Timer_MRW - Resend MRW SUFI when expired. We wont use MRW; it is unneeded. //URlcRecvAm *mRecv; // Pointer to the paired receiving entity. URlcPdu* mPduTxQ[AmSNS]; // PDU array, saved for possible retransmission. // Note that only data pdus go in here, not control. // Variables pat added: bool mNackedBlocksWaiting; // True if mNackVS is valid. URlcSN mVSNack; // Next nacked block to be retransmitted. bool mPollTriggered; bool mStatusTriggered; bool mResetTriggered; // This just triggers it. An in-progress reset is indicated by mTimer_RST.active() bool resetInProgress() { return mTimer_RST.active(); } unsigned mResetTransRSN; // RSN value we sent in our last reset pdu. unsigned mResetAckRSN; // RSN value of last received reset pdu, saved to put in RESET_ACK message. //unsigned mResetTransCount; // Total Number of resets transmitted. unsigned mResetRecvCount; // Total Number of resets received, ever. //unsigned mResetAckRecvCount; // Total number of reset ack received. bool mSendResetAck; unsigned mVTPDUPollTrigger; // Next trigger for a poll if Poll_PDU option, or 0. unsigned mVTSDUPollTrigger; // Next trigger for a poll if Poll_SDU option, or 0. public: void transAmReset(); // Happens whenever we get a reset PDU. void transAmInit(); // Happens once. private: URlcAm*parent(); URlcRecvAm*receiver(); bool stalled(); void setNAck(URlcSN sn); // Set the nack indicator for queued block with this sequence number. URlcPdu *getDataPdu(); URlcPdu *getResetPdu(PduType type); URlcPdu *getStatusPdu(); void advanceVTA(URlcSN newvta); void advanceVS(bool); void processSUFIs(ByteVector *vec); void processSUFIs2(ByteVector *vec, size_t rp); bool IsPollTriggered(); unsigned rlcGetDlPduSizeBytes() { return mConfig->mDlPduSizeBytes; } public: // This class is not allocated alone; it is part of URlcAm. URlcTransAm(URlcConfigAm *wConfig) : URlcTransAmUm(wConfig), mConfig(wConfig) { mRlcid = format("AMT%d",mrbid); } void text(std::ostream &os); void triggerReset() { mResetTriggered = true; } // for testing }; class URlcRecvAm : // UMTS RLC Acknowledged Mode Receiver public URlcRecvAmUm { friend class URlcAm; friend class URlcTransAm; friend class URlcRecvAmUm; URlcConfigAm *mConfig; // This class is not allocated alone; it is part of URlcAm. //URlcTransAm *mTrans; // Pointer to the paired transmitting entity. // GSM25.322 9.4: AM Receive State Variables. // The range from mVRR to mVRH is what we need to acknowledge to the peer. // The LSN sent in the acknowledgment sufi is in the range VRR <= LSN <= VRH. URlcSN mVRR; // SN of the "last" in-sequence PDU received + 1, meaning // that SN+1 is the first PDU not yet received. URlcSN mVRH; // SN+1 of any PDU received or identified to be missing. // See 9.4 how to set it. A PDU is "identified to be missing" // by the POLL SUFI, which sends the VTS from the peer entity. // If the PDU size is small we may not be able to fit all the missing // blocks in a single status report. If you only send the oldest // status report over and over again, and there is a high PDU loss rate, // the total throughput is very slow, especially if the PDU containing // the poll bit is lost causing a wait for the mTimerPoll to expire for // each transaciton. To fix that, if we have nothing else to send, // continue to send status reports until we have reported all the missing blocks. // This variable tells us where we are in the status reports. URlcSN mStatusSN; URlcSN VRMR() { // Maximum acceptable VRR: VRR + Configured_Rx_Window_size return addSN(mVRR,mConfig->mConfigured_Rx_Window_Size); } URlcPdu *mPduRxQ[AmSNS]; // PDU array for reassembly. // 11.4.3: Reception of RESET PDU resets all state variables to initial values except VTRST. public: void recvAmReset(); // Happens whenever we get a RESET PDU. private: void recvAmInit(); // Happens once. URlcAm*parent(); URlcTransAm*transmitter(); bool addAckNack(URlcPdu *pdu); bool isReceiverOk(); public: URlcRecvAm(URlcConfigAm *wConfig) : URlcRecvAmUm(wConfig), mConfig(wConfig) { mRlcid = format("AMR%d",mrbid); } void rlcWriteLowSide(const BitVector &pdu); void text(std::ostream &os); }; class URlcAm : public URlcTransAm, public URlcRecvAm // UMTS RLC Acknowledged Mode Entity { friend class URlcRecvAm; friend class URlcTransAm; void recvResetPdu(URlcPdu*pdu); void recvResetAck(URlcPdu*pdu); unsigned getRlcHeaderSize() { return 2; } URlcConfigAm mConfig; // The one and only config for the entire class hierarchy. // Need a mutex only for RLC-AM. The MAC pulls data from the transmitter // in one thread, and the receiver may be driven asynchronously // from the FEC classes by another thread, and status pdus may cause // activity in both transmitter and receiver, so need a lock. Mutex mAmLock; string mAmid; const char *rlcid() { return mAmid.c_str(); } public: URlcAm(RBInfo *rbInfo,RrcTfs *dltfs,UEInfo *uep,unsigned dlPduSize); // See 9.2.1.7 and 9.2.2.14 // HFN defined in 25.331 8.5.8 - 8.5.10, for RRC Message Integrity Protection. UInt_z mULHFN; // Security stuff. UInt_z mDLHFN; //URlcTransAm *transmitter() { return static_cast(this); } //URlcRecvAm *receiver() { return static_cast(this); } }; #if URLC_IMPLEMENTATION URlcAm::URlcAm(RBInfo *rbInfo,RrcTfs *dltfs,UEInfo *uep,unsigned dlPduSize) : URlcBase(URlcModeAm,uep,rbInfo), URlcTransAm(&mConfig), // Warning, this is not set up yet, but gcc whines if you order correctly. URlcRecvAm(&mConfig), mConfig(*rbInfo,*dltfs,dlPduSize) { mAmid=format("AM%d",mrbid); transAmInit(); recvAmInit(); if (mConfig.mMaxRST == 0) { LOG(WARNING) << "Max_RESET not configured"; } } // Have to wait until both classes are defined before defining these: URlcAm* URlcTransAm::parent() { return static_cast(this); } URlcRecvAm* URlcTransAm::receiver() { return static_cast(parent()); } URlcAm* URlcRecvAm::parent() { return static_cast(this); } URlcTransAm* URlcRecvAm::transmitter() { return static_cast(parent()); } #endif class URlcTransUm : // UMTS RLC Unacknowledged Mode Transmitter public virtual URlcBase, public URlcTransAmUm { URlcConfigUm mConfig; // UM Send State Variables URlcSN mVTUS; // SN of next UM PDU to be transmitted. // Note: For utran side initial value may not be 0? URlcPdu *readLowSidePdu(); // Return a PDU to lower layers, or NULL if queue empty. public: // We send the RBInfo to URlcConfigUm, but all it uses is the RlcInfo from it. URlcTransUm(RBInfo *rbInfo, RrcTfs *dltfs, UEInfo *uep,unsigned dlPduSize, bool isShared=0) : URlcBase(URlcModeUm,uep,rbInfo), URlcTransAmUm(&mConfig), mConfig(*rbInfo,*dltfs,dlPduSize), mVTUS(0) {mConfig.mIsSharedRlc = isShared;} unsigned getRlcHeaderSize() { return 1; } unsigned rlcGetDlPduSizeBytes() { return mConfig.mDlPduSizeBytes; } void text(std::ostream &os); }; class URlcRecvUm : // UMTS RLC Unacknowledged Mode Receiver public virtual URlcBase, public URlcRecvAmUm { public: URlcConfigUm mConfig; URlcSN mVRUS; // SN+1 of last UM PDU received URlcRecvUm(RBInfo *rbInfo, RrcTfs *dltfs, UEInfo *uep) : URlcBase(URlcModeUm,uep,rbInfo), URlcRecvAmUm(&mConfig), mConfig(*rbInfo,*dltfs,0), // No downlink pdu size needed in uplink RLC. mVRUS(0) {} void rlcWriteLowSide(const BitVector &pdu); #if RLC_OUT_OF_SEQ_OPTIONS //unsigned VRUDR; // Expected next SN for DAR (duplicate avoidance and reordering.) //unsigned VRUDH; // Highest received SN for DAR //unsigned VRUDT; // Timer for DAR //Z100 mTimer_DAR; // For UM duplicate avoidance and reordering. // see 9.7.10 //unsigned VRUOH; // Highest SN received. Inited per 11.2.3.2 // Only used for out-of-sequence-delivery unsigned VRUM() { // SN of first UM PDU that shall be rejected. return addSN(mVRUS,mConfig->mConfigured_Rx_Window_Size); } #endif void text(std::ostream &os); }; // Rather than making the MAC delve into to the RrcMasterChConfig, and particularly, // the RBMappingInfo (which we dont even use), we keep everything the MAC needs // to know about this RB here in the RLC, which means we keep the TrCh id here too. // The UMTS spec allows mapping of RLCs to different TrCh, but we will not, so it // TrCh id is a single zero-based number, not a set. struct URlcPair { class URlcTrans *mDown; class URlcRecv *mUp; TrChId mTcid; // The transport channel to which this RLC is attached. URlcPair(RBInfo *rb, RrcTfs *dltfs, UEInfo *uep, TrChId tcid); ~URlcPair(); }; }; // namespace UMTS #endif