/* * 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. */ #ifndef RLCHDR_H #define RLCHDR_H #include #include "GPRSRLC.h" #include "ScalarTypes.h" #include "Defines.h" //#include #include #include "ByteVector.h" #include "MsgBase.h" #include "GPRSInternal.h" #include "MemoryLeak.h" #include "GSMTransfer.h" // For RadData #define CASENAME(x) case x: return #x; namespace GPRS { extern unsigned RLCPayloadSizeInBytes[4]; extern unsigned RLCBlockSizeInBits[4]; class MACPayloadType // It is two bits. GSM04.60sec10.4.7 { public: enum type { RLCData=0, RLCControl=1, // The RLC/MAC block does NOT include the optional Octets. RLCControlExt=2, // The RLC/MAC block DOES include the optional Octets. // Used only in downlink direction. // Value 3 is reserved. }; static const char *name(int val) { switch ((type)val) { CASENAME(RLCData) CASENAME(RLCControl) CASENAME(RLCControlExt) } return "unrecognized MACPayloadType"; } }; // RLC/MAC control message may be sent in an RLC/MAC control block, which is always // encoded with CS-1, so length is 176 bits (22 octets). // Some MAC messages are also sent on PBCCH, PCCCH or PACCH. // Note that no fields in any MAC or RLC header exceed one byte, // so there are no network ordering problems, and we can simply use C structs // to define the bit packing. // (pat) Data blocks defined in GSM04.60 sec 10.3. struct MACDownlinkHeader // 8 bits. See GSM04.60sec10.3.1 { // From GSM 04.60 10.4: // RRBP - expected frame delay for ACK // SP - If 1 we expect an ack and RRBP means something. // USF - User state flag for shared channels. protected: //MACPayloadType::type mPayloadType:2; Field_z<2> mPayloadType; Field_z<2> mRRBP; // RRBP: Relative Reserved Block Period. See GSM04.60sec10.4.5 // It specifies 3,4,5 or 6 block delay for ACK/NACK (to give the // MS time to decode the block.) public: Field_z<1> mSP; // Supplementary/Polling Bit - indicates whether RRBP is valid. Field_z<3> mUSF; // For uplink dynamic allocation method. // indicates owner of the next uplink radio block in the same timeslot. // Except on PCCCH, where a value of 0x111 indicates next uplink radio // block reserved for PRACH. unsigned lengthBits() { return 8; } // Size of the MAC header in bits. void writeMACHeader(MsgCommon& dest) const; void init(MACPayloadType::type wPayloadType) { mPayloadType = wPayloadType; } void setRRBP(int rrbp) { mRRBP = rrbp; mSP = 1; } bool isControlMsg() { return mPayloadType != MACPayloadType::RLCData; } bool isMacUnused() { return mSP == 0 && mUSF == 0; } }; #if RLCHDR_IMPLEMENTATION void MACDownlinkHeader::writeMACHeader(MsgCommon& dest) const { //dest.WRITE_ITEM(mPayloadType); dest.writeField(mPayloadType,2,"PayLoadType",MACPayloadType::name); dest.WRITE_ITEM(mRRBP); dest.WRITE_ITEM(mSP); dest.WRITE_ITEM(mUSF); } #endif struct MACUplinkHeader // 8 bits. See GSM04.60sec10.3.2 { public: // Note: countdown value and SI are used only for uplink data blocks; // for uplink control blocks, CountdownValue and SI are unused, always 0. // Octet 1 (and only): MACPayloadType::type mPayloadType:2; Field<4> mCountDownValue; // GSM04.60 9.3.1. // 15 until close to the end, then countdown. Last block 0. // Once MS starts countdown, it wont interrupt the // transfer for higher priority TBFs. // Update: MSs dont do the countdown, they send 15 until // the next-to-last block, then send 0. Field<1> mSI; // SI: Stall Indicator Field<1> mR; // Retry bit, sent by MS if it had to try more than once to get this through. unsigned lengthBits() { return 8; } // Size of the MAC header in bits. int parseMAC(const BitVector&src); // Since this is an uplink header, we only need to write it for testing purposes, eg, text(). void writeMACHeader(MsgCommon&dst) const; void text(std::ostream&os) const; bool isFinal() { return mCountDownValue == 0; } }; #if RLCHDR_IMPLEMENTATION // It is one byte. int MACUplinkHeader::parseMAC(const BitVector&src) { size_t rp = 0; mPayloadType = (MACPayloadType::type) src.readField(rp,2); mCountDownValue = src.readField(rp,4); mSI = src.readField(rp,1); mR = src.readField(rp,1); return 8; } // Since this is an uplink header, we only need to write it for testing purposes, eg, text(). void MACUplinkHeader::writeMACHeader(MsgCommon&dst) const { // This is special cased so we get the name of the payload type in the text. dst.writeField(mPayloadType,2,"PayLoadType",MACPayloadType::name); /*** std::ostream *os = dst.getStream(); // returned os is non-null only if caller is text(). if (os) { *os << "mPayloadType=(" << MACPayloadType::name(mPayloadType) << ")"; } else { dst.writeField(mPayloadType,2); } ***/ dst.WRITE_ITEM(mCountDownValue); dst.WRITE_ITEM(mSI); dst.WRITE_ITEM(mR); } void MACUplinkHeader::text(std::ostream&os) const { MsgCommonText dst(os); writeMACHeader(dst); //os << "mPayloadType=(" << MACPayloadType::name(mPayloadType) << ")"; //os << RN_WRITE_TEXT(mCountDownValue); //os << RN_WRITE_TEXT(mSI); //os << RN_WRITE_TEXT(mR); } #endif // An incoming block straight from the decoder. struct RLCRawBlock { RLCBSN_t mBSN; // The BSN corresponding to the GSM FN of the first received burst. GSM::RadData mRD; BitVector mData; MACUplinkHeader mmac; ChannelCodingType mUpCC; RLCRawBlock(int wfn, const BitVector &wData,float wRSSI, float wTimgingError,ChannelCodingType cc); ~RLCRawBlock() { RN_MEMCHKDEL(RLCRawBlock) } }; #if RLCHDR_IMPLEMENTATION RLCRawBlock::RLCRawBlock(int wbsn, const BitVector &wData, float wRSSI, float wTimingError, ChannelCodingType cc) { RN_MEMCHKNEW(RLCRawBlock) mData.clone(wData); // Explicit clone. mBSN = wbsn; mmac.parseMAC(mData); // Pull the MAC header out of the BitVector. mUpCC = cc; assert(mData.isOwner()); mRD = GSM::RadData(wRSSI,wTimingError); } #endif // There may be multiple RLC_sub_blocks for multiple PDUs in one RLC/MAC block. struct RLCSubBlockHeader { static int makeoctet(unsigned length, unsigned mbit, unsigned ebit) { return (length << 2) | (mbit << 1) | ebit; } // unsigned mLengthIndicator:6; // length of PDU with block, but see GSM04.60sec10.4.14 // If the LLC PDU does not fit in a single RLC block, then there is no // length indicator byte for the full RLC blocks, and the rest of the RLC block is PDU data. // (Works because the E bit in the RLC header tells us if there is a length indicator.) // Otherwise (ie, for the final RLC segment of each PDU), there is a length indicator, // and there may be additional PDUs tacked on after the end of this PDU, specified by M bit. // The description of length indicator in 10.4.14 is confusing. // In English: you need the length-indicator byte if: // (a) The PDU does not fill the RLC block, indicated by the LI field, or // (b) This is the last segment of a PDU and there are more PDUs following // in the same TBF, indicated by M=1. // The "singular" case mentioned in 10.4.14 occurs when you need the // length-indicator for (b) but not for (a). In this singular case only, // you set the LI (length) field to 0, god only knows why, and presumably M=0, // in the next-to-last segment, then presumably you put the last byte of // the PDU into the next RLC block with a LI=1 and M=1. // If the PDU is incomplete (for uplink blocks only, which presumably means // the allocation ran out before the PDU data) then the final RLC block // would be full so you normally wouldn't need an LI Byte, but to mark this fact // you must reduce the last segment size by one to make room for an LI Byte with LI=0, // and presumably M=0, which is distinguishable from the "singular case" above // by the CountDown field reaching 0. // unsigned mM:1; // More bit; if set there is another sub-block after this one. // unsigned mE:1; // Extension bit, see GSM04.60 table 10.4.13.1. // With M bit, indicates if there is more PDU data in this RLC block. // M+E = 0+0: reserved; // M+E = 0+1: no more data after the current LLC PDU segment. // M+E = 1+0: new LLC PDU after current LLC PDU, with extension octet. // M+E = 1+1: new LLC PDU after current LLC PDU, fills rest of RLC block. }; // We are not using this, because we are not doing contention resolution required // for single phase uplink. struct RLCSubBlockTLLI { RLCSubBlockHeader hdr; // 1 byte. unsigned char mTLLI[4]; // 4 bytes containing a TLLI. struct { unsigned mPFI:7; unsigned mE:1; } b5; }; struct RLCDownlinkDataBlockHeader // GSM04.60sec10.2.1 : public MACDownlinkHeader // 1 byte MAC header { // From GSM 04.60 10.4: // Octet 1: // Use of Power Reduction field described in 5.08 10.2.2. We dont need it. Field_z<2> mPR; // Power Reduction. 0 means no power reduction, 1,2 mean some, 3 means 'not usable.' Field_z<5> mTFI; // TFI - temp flow ID that this block is in Field_z<1> mFBI; // Final Block Indicator - last block of this TBF // Octet 2: Field_z<7> mBSN; // Block Sequence Number, modulo 128 // If E bit is one, followed directly by RLC data. // If E bit is zero, followed by zero or more RLC_Data_Sub_Block. Field_z<1> mE; // End of extensions bit. // Note: unused RLC data field filled with 0x2b as per 04.60 10.4.16 RLCDownlinkDataBlockHeader() { MACDownlinkHeader::init(MACPayloadType::RLCData); } void writeRLCHeader(MsgCommon& dest) const; void write(BitVector&dst) const; void text(std::ostream&os) const; //void setTFI(unsigned wTFI) { b1.mTFI = wTFI; } //void setFBI(bool wFBI) { b1.mFBI = wFBI; } //void setBSN(unsigned wBSN) { b2.mBSN = wBSN; } //void setE(bool wE) { b2.mE = wE; } }; #if RLCHDR_IMPLEMENTATION void RLCDownlinkDataBlockHeader::writeRLCHeader(MsgCommon& dest) const { dest.WRITE_ITEM(mPR); dest.WRITE_ITEM(mTFI); dest.WRITE_ITEM(mFBI); dest.WRITE_ITEM(mBSN); dest.WRITE_ITEM(mE); } void RLCDownlinkDataBlockHeader::write(BitVector&dst) const { MsgCommonWrite mcw(dst); MACDownlinkHeader::writeMACHeader(mcw); RLCDownlinkDataBlockHeader::writeRLCHeader(mcw); } void RLCDownlinkDataBlockHeader::text(std::ostream&os) const { MsgCommonText dst(os); writeMACHeader(dst); writeRLCHeader(dst); } #endif // GSM04.60sec10.2.2 struct RLCUplinkDataBlockHeader { // Octet 0 is the MAC header. MACUplinkHeader mmac; // Octet 1: RLC Header (starts at bit 8) Field<1> mSpare; Field<1> mPI; // PFI Indicator bit; If set, optional PFI is present in data. // PFI identifies a Packet Flow Context defined GSM24.008, // and mentioned in GSP04.60 table 11.2.6.2 // Range will not support these. Field<5> mTFI; // TFI - temp flow ID that this block is in Field<1> mTI; // TLLI indicator. If set, optional TLLI is present in dataa. // TLLI must be send by MS during a one phase access, because // the network does not know TLLI. It is not needed for two phase access. // Octet 2: Field<7> mBSN; // Block Sequence Number, modulo 128 Field<1> mE; // Extension bit: 0 indicates next word is length indicator, // 1 means whole block is data. public: size_t parseRLCHeader(const RLCRawBlock *src); // Since this is an uplink header, we only need to write it for testing purposes. void writeRLCHeader(MsgCommon&dst) const; void write(BitVector&dst) const; // Only needed for testing. void text(std::ostream&os) const; }; #if RLCHDR_IMPLEMENTATION size_t RLCUplinkDataBlockHeader::parseRLCHeader(const RLCRawBlock *src) { mmac = src->mmac; size_t rp = mmac.lengthBits(); rp++; // skip spare bit. mPI = src->mData.readField(rp,1); mTFI = src->mData.readField(rp,5); mTI = src->mData.readField(rp,1); mBSN = src->mData.readField(rp,7); mE = src->mData.readField(rp,1); return rp; } // Since this is an uplink header, we only need to write it for testing purposes. void RLCUplinkDataBlockHeader::writeRLCHeader(MsgCommon&dst) const { dst.WRITE_ITEM(mSpare); dst.WRITE_ITEM(mPI); dst.WRITE_ITEM(mTFI); dst.WRITE_ITEM(mTI); dst.WRITE_ITEM(mBSN); dst.WRITE_ITEM(mE); } void RLCUplinkDataBlockHeader::write(BitVector&dst) const { // Only needed for testing. MsgCommonWrite mcw(dst); mmac.writeMACHeader(mcw); writeRLCHeader(mcw); } void RLCUplinkDataBlockHeader::text(std::ostream&os) const { MsgCommonText dst(os); mmac.writeMACHeader(dst); writeRLCHeader(dst); } #endif class RLCUplinkDataSegment : public BitVector { public: RLCUplinkDataSegment(const BitVector&wPayload) : BitVector(NULL,(char*)wPayload.begin(),(char*)wPayload.end()) {} // access to potentially multiple data fields // GSM04.60 sec 10.4.13 and 10.4.14 size_t LIByteLI(size_t lp=0) const { return peekField(lp,6); } bool LIByteM(size_t lp=0) const { return peekField(lp+6,1); } bool LIByteE(size_t lp=0) const { return peekField(lp+7,1); } }; class RLCUplinkDataBlock : public RLCUplinkDataBlockHeader { BitVector mData; public: static const unsigned mHeaderSizeBits = 24; ChannelCodingType mUpCC; // We dont use this - debugging info only. // Convert a BitVector into an RLC data block. // We simply take ownership of the BitVector memory. // The default destructor will destroy the BitVector when delete is called on us. RLCUplinkDataBlock(RLCRawBlock* wSrc); ~RLCUplinkDataBlock() { RN_MEMCHKDEL(RLCUplinkDataBlock) } // Return subset of the BitVector that is payload. BitVector getPayload() { return mData.tail(mHeaderSizeBits); } void text(std::ostream&os); }; #if RLCHDR_IMPLEMENTATION RLCUplinkDataBlock::RLCUplinkDataBlock(RLCRawBlock* wSrc) { RN_MEMCHKNEW(RLCUplinkDataBlock) mData = wSrc->mData; size_t tmp = parseRLCHeader(wSrc); mUpCC = wSrc->mUpCC; assert(tmp == mHeaderSizeBits); } void RLCUplinkDataBlock::text(std::ostream&os) { os << "RLCUplinkDataBlock=("; RLCUplinkDataBlockHeader::text(os); os << "\npayload:"; // Write out the data as bytes. BitVector payload(getPayload()); payload.hex(os); /*** int i, size=payload.size(); char buf[10]; for (i=0; i < size-8; i+=8) { sprintf(buf," %02x",(int)payload.peekField(i,8)); os << buf; } ***/ os << ")"; } #endif /** GSM 04.60 10.2.1 */ class RLCDownlinkDataBlock : public RLCDownlinkDataBlockHeader, public Text2Str { public: // The mPayload does not own any allocated storage; it points into the mPDU of the TBF. // We are not currently putting multiple PDUs in a TBF, so this is ok. ByteVector mPayload; // max size is 52, may be smaller. bool mIdle; // If true, block contains only a keepalive. ChannelCodingType mChannelCoding; int getPayloadSize() const { // In bytes. return RLCPayloadSizeInBytes[mChannelCoding]; } int headerSizeBytes() { return 3; } RLCDownlinkDataBlock(ChannelCodingType wCC) : mIdle(0), mChannelCoding(wCC) {} // Convert the Downlink Data Block into a BitVector. // We do this right before sending it down to the encoder. BitVector getBitVector() const; void text(std::ostream&os, bool includePayload) const; void text(std::ostream&os) const { text(os,true); } // Default value doesnt work. Gotta love that C++. // /** Construct the block and write data into it. */ // DownlinkRLCDataBlock( // unsigned RRBP, bool SP, unsigned USF, // bool PR, unsigned TFI, bool FBI, // unsigned BSN, // const BitVector& data); }; #if RLCHDR_IMPLEMENTATION BitVector RLCDownlinkDataBlock::getBitVector() const { // Add 3 bytes for mac and rlc headers. BitVector result(8 *(3+mPayload.size())); RLCDownlinkDataBlockHeader::write(result); BitVector resultpayload(result.tail(3*8)); resultpayload.unpack(mPayload.begin()); // unpack mPayload into resultpayload return result; } void RLCDownlinkDataBlock::text(std::ostream&os, bool includePayload) const { os << "RLCDownlinkDataBlock=("; RLCDownlinkDataBlockHeader::text(os); os << LOGVAR2("CCoding",mChannelCoding) <