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

1547 lines
53 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 L2 RLC Messages, from GSM 04.60 Section 11 */
#ifndef GPRSL2RLCMESSAGES_H
#define GPRSL2RLCMESSAGES_H
#include <iostream>
#include <stdint.h>
#include "Defines.h"
#include "BitVector.h"
#include "Logger.h"
#include "GSMCommon.h"
//#include "GSMTransfer.h"
//#include "GSMTDMA.h"
//#include <Globals.h>
#include "MsgBase.h"
#include "RLCHdr.h"
#include "TBF.h"
#include "MemoryLeak.h"
namespace GPRS {
class TBF;
/** GSM 04.60 11.2 */
class RLCMessage : public Text2Str {
public:
virtual void text(std::ostream&) const = 0;
RLCMessage() { RN_MEMCHKNEW(RLCMessage) }
// The virtual keyword on a destructor indicates that both the base destructor (this one)
// and the derived class destructor are both called. Otherwise they aren't. It is foo bar.
virtual ~RLCMessage() { RN_MEMCHKDEL(RLCMessage) }
};
std::ostream& operator<<(std::ostream& os, const RLCMessage *msg);
#if RLCMESSAGES_IMPLEMENTATION
std::ostream& operator<<(std::ostream& os, const RLCMessage *msg)
{
msg->text(os);
return os;
}
#endif
class RLCMsgUplinkIE
{
public:
virtual void parseElement(const BitVector &src, size_t &rp) = 0;
virtual void writeBody(MsgCommon&dst) const = 0;
// The indivual IE can overwrite textElement is MsgCommon does not work very well for it.
virtual void textElement(std::ostream& os) const {
MsgCommonText dst(os);
writeBody(dst);
}
};
class RLCMsgDownlinkIE
{
public:
virtual void writeBody(MsgCommon&dst) const = 0;
void writeOptional01(MsgCommon &dst, bool control) const {
if (control) {
dst.write1();
writeBody(dst);
} else {
dst.write0();
}
}
};
// The RLCDownlink and RLCUplink Messages are RLC Control messages -
// they are not L3 Messages as defined in GSM 04.18.
// These messages are used at the RLC/MAC layer to control the PDCH channels
// assigned for packet (GPRS) use. Their primary use is to transfer
// L3 Messages and user data between the SGSN and the MS.
// Mobility Management routines reside entirely inside the SGSN.
class RLCDownlinkMessage :
public MACDownlinkHeader,
public RLCMessage
{
public:
/** Message Type GSM 04.06 11.2.0.2 */
// The ones we will implement marked with <<<
// Message marked PCCCH only cannot be implemented now.
// From sec 11.1.1.1: if the high bit of a downlink control message type is 1,
// then it is a distribution message to all MS, otherwise it is a non-distribution message.
// Note that the downlink message content is byte aligned (6 bit MessageType + 2 bit PageMode)
// Also note that the uplink message content is not byte aligned.
// From sec 11.1.1.2: Non-distribution messages may have distribution contents,
// followed by an Address Information (to identify a single MS) followed by
// non-distribution contents.
// NOTE: The unused bits at the end of the control message must be packed out
// as per GSM04.60sec11: one 0 bit, followed by 0 bits to get to a byte boundary,
// followed by byte 0x2b forever.
enum MessageType {
/*100001 */ PacketAccessReject = 0x21, // PACCH or PCCCH <<<<
/*000001 */ PacketCellChangeOrder = 0x1, // PCCCH or PACCH
/*000010 */ PacketDownlinkAssignment = 0x2, // PCCCH or PACCH <<<< acknowledged
/*000011 */ PacketMeasurementOrder = 0x3, // PCCCH or PACCH
// PagingRequest is only sent on PACCH to initiate an RR connection, so skip for now:
// Note: You can send still GSM paging messages on CCCH when MS is in packet-idle mode.
/*100010 */ PacketPagingRequest = 0x22, // PCCCH or PACCH
/*100011 */ PacketPDCHRelease = 0x23, // PACCH
/*000100 */ PacketPollingRequest = 0x4, // PACCH or PCCH <<<< not used implicitly acknowldged
/*000101 */ PacketPowerControlTimingAdvance = 0x5, // PACCH
/*100100 */ PacketPRACHParameters = 0x24, // PCCCH only
/*000110 */ PacketQueueingNotification = 0x6, // PCCCH only
/*000111 */ PacketTimeslotReconfigure = 0x7, // PACCH
/*001000 */ PacketTBFRelease = 0x8, // PACCH <<< acknowledged
/*001001 */ PacketUplinkAckNack = 0x9, // PACCH <<<
// The acknowledgement to Packet Uplink Assignment is not strictly needed
// because we will know as soon as the MS starts sending blocks.
/*001010 */ PacketUplinkAssignment = 0xa, // PCCCH or PACCH <<< acknowledged
/*100101 */ PacketDownlinkDummyControlBlock = 0x25, // PCCCH or PACCH
/*110001 */ PSI1 = 0x31,
/*110010 */ PSI2 = 0x32,
/*110011 */ PSI3 = 0x33,
/*110100 */ PSI3bis = 0x34,
/*110101 */ PSI4 = 0x35,
/*110110 */ PSI5 = 0x36,
/*110000 */ PSI6 = 0x30,
/*111000 */ PSI7 = 0x38,
/*111001 */ PSI8 = 0x39,
/*110111 */ PSI13 = 0x37,
/*111010 */ PSI14 = 0x3a,
/*111100 */ PSI3ter = 0x3c,
/*111101 */ PSI3quater = 0x3d,
/*111110 */ PSI15 = 0x3e
};
static const char *name(MessageType mtype);
// There are optional additional RLC Control Block header fields here, but
// we will not use them, so they are not even mentioned here.
MessageType mMessageType; // 6 bits
unsigned mPageMode; // 2 bits
TBF *mTBF; // If this message is associated with a tbf, here it is.
RLCDownlinkMessage(MessageType mtype, bool wRequiresAck, TBF *wtbf) :
mMessageType(mtype),
mPageMode(0),
mTBF(wtbf)
{
MACDownlinkHeader::init(MACPayloadType::RLCControl);
}
// Not all the messages have setTLLI().
virtual void setTLLI(int) {};
// Not all the messages have setTimingAdvance().
virtual void setTimingAdvance(int) {};
// writeBody defined in each message class to write the message body starting after PageMode.
virtual void writeBody(MsgCommon&dst) const = 0;
void writeHeader(MsgCommon &dst) const;
/** Serialize this message into a BitVector. The caller is responsible for deleting the memory.
*/
void write(BitVector&vec) const;
void text(std::ostream &os) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCDownlinkMessage::writeHeader(MsgCommon& dst) const {
MACDownlinkHeader::writeMACHeader(dst);
dst.WRITE_FIELD(mMessageType,6);
dst.WRITE_FIELD(mPageMode,2);
}
void RLCDownlinkMessage::text(std::ostream &os) const {
MsgCommonText dst(os);
os << name(mMessageType) << ":";
writeHeader(dst);
writeBody(dst);
}
void RLCDownlinkMessage::write(BitVector&vec) const {
MsgCommonWrite dst(vec);
writeHeader(dst);
writeBody(dst);
// GSM04.60 sec 11:
// The padding bits may be the 'null' string. Otherwise, the padding
// bits starts with bit '0', followed by 'spare padding'.
if (dst.wp & 0x7) { dst.write0(); } // one optional 0 bit
while (dst.wp & 0x7) { dst.writeL(); } // pad to a byte boundary.
while (dst.wp < RLCBlockSizeInBits[0]) { dst.writeField(0x2b,8); }
}
#endif
/** GSM 04.60 11.2 */
class RLCUplinkMessage : public RLCMessage
{
public:
// GSM04.60 sec 11.2
enum MessageType {
/* 000000 */ PacketCellChangeFailure = 0x0,
/* 000001 */ PacketControlAcknowledgement = 0x1,
/* 000010 */ PacketDownlinkAckNack = 0x2,
/* 000011 */ PacketUplinkDummyControlBlock = 0x3,
/* 000100 */ PacketMeasurementReport = 0x4,
/* 001010 */ PacketEnhancedMeasurementReport = 0xa,
/* 000101 */ PacketResourceRequest = 0x5,
/* 000110 */ PacketMobileTBFStatus = 0x6,
/* 000111 */ PacketPSIStatus = 0x7,
/* 001000 */ EGPRSPacketDownlinkAckNack = 0x8,
/* 001001 */ PacketPause = 0x9,
/* 001011 */ AdditionalMSRadioAccessCapabilities = 0xb
};
static const char *name(MessageType type);
MACUplinkHeader mmac; // 8 bits
MessageType mMessageType; // 6 bits
// Parse/text just the body. These must be defined in each descendent class
//virtual void textBody(std::ostream&os) const = 0;
virtual int getTFI() { return -1; }
virtual MSInfo *getMS(PDCHL1FEC *chan, bool create) { assert(0); }
virtual MSInfo *getTBF(PDCHL1FEC *chan) { assert(0); }
virtual void parseBody(const BitVector &src, size_t &rp) = 0;
virtual void writeBody(MsgCommon&dst) const = 0;
void parse(const RLCRawBlock *src);
void writeHeader(MsgCommon &dst) const;
/** Serialize this message into a BitVector. The caller is responsible for deleting the memory.
*/
void write(BitVector&vec) const;
void text(std::ostream &os) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCUplinkMessage::parse(const RLCRawBlock*src) {
// The MACUplinkHeader is explict (mmac) instead of a base class
// to make this line of code more obvious:
mmac = src->mmac; // Parsed previously.
size_t rp = mmac.lengthBits();
mMessageType = (MessageType) src->mData.readField(rp,6);
parseBody(src->mData,rp);
}
void RLCUplinkMessage::writeHeader(MsgCommon&dst) const {
mmac.writeMACHeader(dst);
dst.WRITE_FIELD(mMessageType,6);
}
void RLCUplinkMessage::text(std::ostream &os) const {
MsgCommonText dst(os);
os << name(mMessageType) << ":";
mmac.text(os); // or: mmac.writeMACHeader(dst);
dst.WRITE_FIELD(mMessageType,6);
writeBody(dst);
}
void RLCUplinkMessage::write(BitVector&vec) const {
MsgCommonWrite dst(vec);
writeHeader(dst);
writeBody(dst);
// GSM04.60 sec 11: For RLC messages: one 0 followed by byte-aligned 0x2b
if (dst.wp & 0x7) { dst.write0(); } // one optional 0 bit
while (dst.wp & 0x7) { dst.writeL(); } // pad to a byte boundary.
while (dst.wp < RLCBlockSizeInBits[0]) { dst.writeField(0x2b,8); }
}
#endif
// (pat) This is for downlink power parameters, as per GSM04.60 sec 11.2.29.
// We will probably never use this.
//struct L3IADownlinkPowerOptionIE {
// int mPowerOption; // 1 if P0, PWR_CTRL_MODE, PR_MODE should be included.
// unsigned mP0:4;
// unsigned mBTSPwrCtrlMode:1;
// unsigned mPRMode:1;
// void writePower(L3Frame &dst, int whichDesignationMethod) {
// if (mPowerOption) {
// whichDesignationMethod ? dst.writeH() : dst.writeField(1,1);
// dst.writeField(P0,4);
// dst.writeField(mBTSPwrCtrlMode,1);
// dst.writeField(mPRMode,1);
// } else {
// whichDesignationMethod ? dst.writeL() : dst.writeField(0,1);
// }
// }
// L3AssignmentPowerOptionIE::L3AssignmentPowerOptionIE() { memset(this,0,sizeof(*this)); }
//};
// GSM04.60 12.3
// Note this IE is both uplink and downlink.
struct RLCMsgPacketAckNackDescriptionIE : public RLCMsgUplinkIE, public RLCMsgDownlinkIE
{
Field<1> mFinalAckIndication;
Field<7> mSSN; // "Starting Sequence Number" except it is not.
// It is actually the ending sequence number.
static const int mbitmapsize = 64;
bool mBitMap[mbitmapsize];
void parseElement(const BitVector &src, size_t &rp);
void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCMsgPacketAckNackDescriptionIE::parseElement(const BitVector &src, size_t &rp)
{
mFinalAckIndication = src.readField(rp,1);
mSSN = src.readField(rp,7);
for (int i = 0; i < mbitmapsize; i++) {
mBitMap[i] = src.readField(rp,1);
}
}
void RLCMsgPacketAckNackDescriptionIE::writeBody(MsgCommon&dst) const
{
dst.WRITE_ITEM(mFinalAckIndication);
dst.WRITE_ITEM(mSSN);
dst.writeBitMap((bool*)mBitMap,mbitmapsize,"Bitmap");
}
#endif
// GSM04.60 12.7
struct RLCMsgChannelRequestDescriptionIE : public RLCMsgUplinkIE
{
Field<4> mPeakThroughputClass; // See 3GPP 24.008
Field<2> mRadioPriority; // See 11.2.5 0 =highest, 3=lowest
Field<1> mRLCMode; // 0 == acknowledged, 1 == unacknowleged mode.
Field<1> mLLCPDUType; // 0 == LLC PDU is SACK or ACK[acknowledged mode], 1 == its not
Field<16> mRLCOctetCount; // Number of octets MS wants to transfer, or 0 if unspecified.
void parseElement(const BitVector &src, size_t &rp);
//void textElement(std::ostream&) const;
void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCMsgChannelRequestDescriptionIE::parseElement(const BitVector &src, size_t &rp)
{
mPeakThroughputClass = src.readField(rp,4);
mRadioPriority = src.readField(rp,2);
mRLCMode = src.readField(rp,1);
mLLCPDUType = src.readField(rp,1);
mRLCOctetCount = src.readField(rp,16);
}
void RLCMsgChannelRequestDescriptionIE::writeBody(MsgCommon&dst) const {
dst.WRITE_ITEM(mPeakThroughputClass);
dst.WRITE_ITEM(mRadioPriority);
dst.WRITE_ITEM(mRLCMode);
dst.WRITE_ITEM(mLLCPDUType);
dst.WRITE_ITEM(mRLCOctetCount);
}
/*
void RLCMsgChannelRequestDescriptionIE::textElement(std::ostream&os) const
{
os << " ChannelRequest=(";
RN_WRITE_TEXT(mPeakThroughputClass);
RN_WRITE_TEXT(mRadioPriority);
RN_WRITE_TEXT(mRLCMode);
RN_WRITE_TEXT(mLLCPDUType);
RN_WRITE_TEXT(mRLCOctetCount);
os << ")";
}
*/
#endif
struct MSRACap_s : public RLCMsgUplinkIE
{
Field<4> mAccessTechnologyType; // The caller parses and sets this.
bool mA5BitsPresent;
Field<3> mRFPowerCapability;
Field<7> mA5Bits;
Field<1> mESInd;
Field<1> mPS;
Field<1> mVGCS;
Field<1> mVBS;
bool mMultiSlotCapPresent;
bool mHSCDMultiSlotClassPresent;
Field<5> mHSCDMultiSlotClass;
bool mGPRSMultiSlotClassPresent;
Field<5> mGPRSMultiSlotClass;
Field<1> mGPRSExtendedDynamicAllocationCapability;
bool mSMSPresent;
Field<4> mSMS_VALUE; Field<4> mSM_VALUE;
// There is much more, but we dont keep it.
void parseElement(const BitVector &src, size_t &rp);
void writeBody(MsgCommon&dst) const;
//void textElement(std::ostream&) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void MSRACap_s::parseElement(const BitVector &src, size_t &rp)
{
mRFPowerCapability = src.readField(rp,3);
if ((mA5BitsPresent = src.readField(rp,1))) {
mA5Bits = src.readField(rp,7);
}
mESInd = src.readField(rp,1);
mPS = src.readField(rp,1);
mVGCS = src.readField(rp,1);
mVBS = src.readField(rp,1);
if ((mMultiSlotCapPresent = src.readField(rp,1))) {
// Multislot Capability Struct:
if ((mHSCDMultiSlotClassPresent = src.readField(rp,1))) {
mHSCDMultiSlotClass = src.readField(rp,5);
}
if ((mGPRSMultiSlotClassPresent = src.readField(rp,1))) {
mGPRSMultiSlotClass = src.readField(rp,5);
mGPRSExtendedDynamicAllocationCapability = src.readField(rp,1);
}
if ((mSMSPresent = src.readField(rp,1))) {
mSMS_VALUE = src.readField(rp,4);
mSM_VALUE = src.readField(rp,4);
}
} else {
mGPRSMultiSlotClassPresent = 0;
}
}
void MSRACap_s::writeBody(MsgCommon&dst) const {
dst.WRITE_ITEM(mAccessTechnologyType);
dst.WRITE_ITEM(mRFPowerCapability);
if (dst.write01(mA5BitsPresent)) { dst.WRITE_ITEM(mA5Bits); }
dst.WRITE_ITEM(mESInd);
dst.WRITE_ITEM(mVGCS);
dst.WRITE_ITEM(mVBS);
if (mMultiSlotCapPresent) {
if (dst.write01(mHSCDMultiSlotClassPresent)) dst.WRITE_ITEM(mHSCDMultiSlotClass);
if (dst.write01(mGPRSMultiSlotClassPresent)) {
dst.WRITE_ITEM(mGPRSMultiSlotClass);
dst.WRITE_ITEM(mGPRSExtendedDynamicAllocationCapability);
}
if (dst.write01(mSMSPresent)) {
dst.WRITE_ITEM(mSMS_VALUE);
dst.WRITE_ITEM(mSM_VALUE);
}
}
// More stuff ignored.
}
/*
void MSRACap_s::textElement(std::ostream&os) const {
RN_WRITE_TEXT(mAccessTechnologyType);
RN_WRITE_TEXT(mRFPowerCapability);
RN_WRITE_OPT_TEXT(mA5Bits,mA5BitsPresent);
RN_WRITE_TEXT(mESInd);
RN_WRITE_TEXT(mPS);
RN_WRITE_TEXT(mVGCS);
RN_WRITE_TEXT(mVBS);
RN_WRITE_OPT_TEXT(mGPRSMultiSlotClass,mGPRSMultiSlotClassPresent);
}
*/
#endif
// GSM24.008sec10.5.5.12a
struct RLCMsgMSRACapabilityValuePartIE : public RLCMsgUplinkIE
{
// There can be many of these.
// The first one is all we are interested in.
// We will map all the other caps into the second struct, which we wont even use.
MSRACap_s MSRACap[2];
int mNumMSRACap;
void parseElement(const BitVector &src, size_t &rp);
void writeBody(MsgCommon&dst) const;
//void textElement(std::ostream&) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCMsgMSRACapabilityValuePartIE::parseElement(const BitVector &src, size_t &rp)
{
mNumMSRACap = 0;
do {
int AccessTechnologyType = src.readField(rp,4);
unsigned len = src.readField(rp,7); // Length in bits of the rest of this access cap.
// Kyle at L3 reported that this was crashing in the src.readField below,
// which means this struct is invalid or not being parsed properly.
// Lets check, although it actually needs to be much bigger than this
// because this is embedded inside a large message.
if (len + 1 >= src.size()) {
LOG(INFO) << "Invalid RA Capability Value Part struct"<<LOGVAR(len)<<LOGVAR(mNumMSRACap);
}
size_t startrp = rp;
if (AccessTechnologyType != 0xf) {
if (mNumMSRACap < 2) { // Save the first two.
MSRACap[mNumMSRACap].mAccessTechnologyType = AccessTechnologyType;
MSRACap[mNumMSRACap].parseElement(src,rp);
mNumMSRACap++;
}
} else {
// Additional access technologies. Just throw it away.
}
rp = startrp + len;
} while (src.readField(rp,1));
}
void RLCMsgMSRACapabilityValuePartIE::writeBody(MsgCommon&dst) const {
std::ostream *os;
if ((os = dst.getStream())) {
for (int i=0; i < mNumMSRACap; i++) {
*os << " RACapability[" << i <<"]=(";
MSRACap[i].writeBody(dst);
*os << ")";
}
} else {
// Not implemented. It is an uplink IE so we dont need it.
}
}
/*
void RLCMsgMSRACapabilityValuePartIE::textElement(std::ostream&os) const
{
for (int i=0; i < mNumMSRACap; i++) {
os << " RACapability[" << i <<"]=("; MSRACap[i].textElement(os); os << ")";
}
}
*/
#endif
// GSM 04.60 12.10
struct RLCMsgGlobalTFIIE : public RLCMsgDownlinkIE, public RLCMsgUplinkIE
{
Field_z<1> mIsDownlinkTFI; // 1 for downlink TFI, 0 for uplink TFI
Field_z<5> mGTFI; // This is NOT a TFI assignment; it is used
// to identify the MS which receives this message.
void setTFI(bool wIsDownlinkTFI,int wTFI) {
// TBF uninitialized TFI is -1; it is an error if that value finds its way here.
assert(wTFI >= 0 && wTFI < 32);
mIsDownlinkTFI = wIsDownlinkTFI; mGTFI = wTFI;
}
void parseElement(const BitVector &src, size_t &rp);
void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCMsgGlobalTFIIE::parseElement(const BitVector &src, size_t &rp) {
mIsDownlinkTFI = src.readField(rp,1);
mGTFI = src.readField(rp,5);
}
void RLCMsgGlobalTFIIE::writeBody(MsgCommon &dst) const {
dst.WRITE_ITEM(mIsDownlinkTFI);
dst.WRITE_ITEM(mGTFI);
}
#endif
// 3GPP 44-060 11.2.6
// See 45.008 for definitions of these things.
struct ChannelQualityReportIE : public RLCMsgUplinkIE
{
// 45.008 10.2.3: "C Value is the enormalized received signal level at the MS
// as defined in 10.2.3.1".
// During Packet Transfer mode (which we are for a DownlinkAckNack)
// C Value and SIGN_VAR (variance of C Value) are measured from BCCH and
// subsequently used to determine MS output power as per 10.2.1.
Field<6> mCValue;
// RXQUAL is derived from decoder BEP [Bit Error Probability]
Field<3> mRXQual; // From decoder, low is better, meaningless for CS-4 encoding.
// SIGN_VAR from 0 to 64 encoded as: 0 to 15.75dB in 0.25dB steps.
Field<6> mSignVar;
// 45.008 10.3: I_LEVEL is measured interference level on each channel.
Bool_z mHaveILevel[8];
Field<4> mILevel[8]; // For each timeslot.
void parseElement(const BitVector &src, size_t &rp);
void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void ChannelQualityReportIE::parseElement(const BitVector &src, size_t &rp) {
mCValue = src.readField(rp,6);
mRXQual = src.readField(rp,3);
mSignVar = src.readField(rp,6);
for (int i = 0; i <= 7; i++) {
if ((mHaveILevel[i] = src.readField(rp,1))) {
mILevel[i] = src.readField(rp,4);
}
}
}
void ChannelQualityReportIE::writeBody(MsgCommon&dst) const {
dst.WRITE_ITEM(mCValue);
dst.WRITE_ITEM(mRXQual);
dst.WRITE_ITEM(mSignVar);
for (int i = 0; i <= 7; i++) {
if (mHaveILevel[i]) { dst.WRITE_ITEM(mILevel[i]); }
}
}
#endif
/** GSM04.60 11.2.6 From MS to network. */
struct RLCMsgPacketDownlinkAckNack : public RLCUplinkMessage
{
Field<5> mTFI;
RLCMsgPacketAckNackDescriptionIE mAND;
bool mHaveChannelRequest;
RLCMsgChannelRequestDescriptionIE mCRD;
ChannelQualityReportIE mCQR;
// We can ignore the rest of the message.
// struct ChannelQualityReport CQR;
// unsigned PFI:7 // (we wont use it)
RLCMsgPacketDownlinkAckNack(const RLCRawBlock *src) { parse(src); }
int getTFI() { return mTFI; }
void parseBody(const BitVector &src, size_t &rp);
void writeBody(MsgCommon&dst) const;
//void textBody(std::ostream&) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCMsgPacketDownlinkAckNack::parseBody(const BitVector &src, size_t &rp)
{
mTFI = src.readField(rp,5);
mAND.parseElement(src,rp);
mHaveChannelRequest = src.readField(rp,1);
if (mHaveChannelRequest) { mCRD.parseElement(src,rp); }
mCQR.parseElement(src,rp);
}
void RLCMsgPacketDownlinkAckNack::writeBody(MsgCommon&dst) const {
dst.WRITE_ITEM(mTFI);
mAND.writeBody(dst);
if (dst.write01(mHaveChannelRequest)) { mCRD.writeBody(dst); }
mCQR.writeBody(dst);
}
/*
void RLCMsgPacketDownlinkAckNack::textBody(std::ostream&os) const
{
RN_WRITE_TEXT(mTFI);
mAND.textElement(os);
if (mHaveChannelRequest) { mCRD.textElement(os); }
}
*/
#endif
//static uint64_t readOptField(BitVector&src,size_t&rp,int bits,bool&option) {
// if ((option = src.readField(rp,1))) {
// return src.readField(rp,bits);
// }
//}
/** GSM04.60 11.2.6 From MS to network. */
struct RLCMsgPacketControlAcknowledgement : public RLCUplinkMessage
{
Field<32> mTLLI; // Yes, this really is not byte aligned in the message.
Field<2> mCtrlAck; // Which segments of the RLC/MAC control message were received?
RLCMsgPacketControlAcknowledgement(const RLCRawBlock *src) { parse(src); }
void parseBody(const BitVector &src, size_t &rp);
void writeBody(MsgCommon&dst) const;
//void textBody(std::ostream&) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCMsgPacketControlAcknowledgement::parseBody(const BitVector&src, size_t&rp)
{
mTLLI = src.readField(rp,32);
mCtrlAck = src.readField(rp,2);
}
void RLCMsgPacketControlAcknowledgement::writeBody(MsgCommon&dst) const {
dst.writeField(mTLLI,32,"TLLI",tohex);
dst.WRITE_ITEM(mCtrlAck);
}
/*
void RLCMsgPacketControlAcknowledgement::textBody(std::ostream&os) const
{
RN_WRITE_TEXT(mTLLI);
RN_WRITE_TEXT(mCtrlAck);
}
*/
#endif
/** GSM04.60 11.2.16 From MS to network. */
struct RLCMsgPacketResourceRequest : public RLCUplinkMessage
{
bool mAccessTypePresent; // 0=two phase, 1=page response, 2=cell update, 3=mm procedure
Field<2> mAccessType;
RLCMsgGlobalTFIIE mGTFI;
bool mTLLIPresent; // 1 for TLLI present, 0 for TFI present.
Field<32> mTLLI;
bool mMSRadioAccessCapability2Present;
RLCMsgMSRACapabilityValuePartIE mMSRACap;
RLCMsgChannelRequestDescriptionIE mCRD;
bool mChangeMarkPresent;
Field<2> mChangeMark;
Field<5> mCValue;
bool mSignVarPresent;
Field<6> mSignVar; // Not present for two phase access.
bool mILevelPresent[8];
Field<4> mILevelTN[8];
bool mExtensionsPresent;
RLCMsgPacketResourceRequest(const RLCRawBlock *src) { parse(src); }
// Return the MSInfo identified by the contents of this message.
MSInfo *getMS(PDCHL1FEC *chan, bool create);
void parseBody(const BitVector &src, size_t &rp);
void writeBody(MsgCommon&dst) const;
//void textBody(std::ostream&) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCMsgPacketResourceRequest::parseBody(const BitVector &src, size_t &rp)
{
if ((mAccessTypePresent = src.readField(rp,1))) { mAccessType = src.readField(rp,2); }
if ((mTLLIPresent = src.readField(rp,1))) {
mTLLI = src.readField(rp,32);
} else {
mGTFI.parseElement(src,rp);
}
if ((mMSRadioAccessCapability2Present = src.readField(rp,1))) {
mMSRACap.parseElement(src,rp);
}
mCRD.parseElement(src,rp); // We will use this.
if ((mChangeMarkPresent = src.readField(rp,1))) { mChangeMark = src.readField(rp,2); }
mCValue = src.readField(rp,6);
if ((mSignVarPresent = src.readField(rp,1))) { mSignVar = src.readField(rp,2); }
for (int i = 0; i < 8; i++) {
mILevelTN[i] = (mILevelPresent[i] = src.readField(rp,1)) ? src.readField(rp,4) : -1;
}
mExtensionsPresent = rp < src.size() && src.readField(rp,1);
// But ignore the exensions.
}
void RLCMsgPacketResourceRequest::writeBody(MsgCommon&dst) const {
if (dst.write01(mAccessTypePresent)) { dst.WRITE_ITEM(mAccessType); }
if (dst.write01(mTLLIPresent)) {
dst.writeField(mTLLI,32,"TLLI",tohex);
} else {
mGTFI.writeBody(dst);
}
if (dst.write01(mMSRadioAccessCapability2Present)) {
mMSRACap.writeBody(dst);
}
mCRD.writeBody(dst);
if (dst.write01(mChangeMarkPresent)) { dst.WRITE_ITEM(mChangeMark); }
dst.WRITE_ITEM(mCValue);
if (dst.write01(mSignVarPresent)) { dst.WRITE_ITEM(mSignVar); }
for (int i = 0; i < 8; i++) {
if (dst.write01(mILevelPresent[i])) dst.WRITE_ITEM(mILevelTN[i]);
}
dst.writeField(mExtensionsPresent,1,"mExtensionsPresent");
}
/*
void RLCMsgPacketResourceRequest::textBody(std::ostream&os) const {
RN_WRITE_OPT_TEXT(mAccessType,mAccessTypePresent);
RN_WRITE_OPT_TEXT(mTLLI,mTLLIPresent);
RN_WRITE_OPT_TEXT(mIsDownlinkTFI,!mTLLIPresent);
RN_WRITE_OPT_TEXT(mTFI,!mTLLIPresent);
if (mMSRadioAccessCapability2Present) { mMSRACap.textElement(os); }
mCRD.textElement(os);
RN_WRITE_OPT_TEXT(mChangeMark,mChangeMarkPresent);
RN_WRITE_TEXT(mCValue);
RN_WRITE_OPT_TEXT(mSignVar,mSignVarPresent);
for (int i = 0; i < 8; i++) {
RN_WRITE_OPT_TEXT(mILevelTN[i],mILevelTN[i] >= 0);
}
}
*/
#endif
/** GSM04.60 11.2.8b From MS to network. */
struct RLCMsgPacketUplinkDummyControlBlock : public RLCUplinkMessage
{
Field<32> mTLLI;
RLCMsgPacketUplinkDummyControlBlock(const RLCRawBlock*src) { parse(src); }
void parseBody(const BitVector &src, size_t &rp);
void textBody(std::ostream&) const;
void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCMsgPacketUplinkDummyControlBlock::parseBody(const BitVector &src, size_t &rp)
{
mTLLI = src.readField(rp,32);
}
void RLCMsgPacketUplinkDummyControlBlock::writeBody(MsgCommon&dst) const {
dst.writeField(mTLLI,32,"TLLI",tohex);
}
/*
void RLCMsgPacketUplinkDummyControlBlock::textBody(std::ostream&os) const
{
RN_WRITE_TEXT(mTLLI);
};
*/
#endif
// GSM04.60 12.12
struct RLCMsgPacketTimingAdvanceIE : public RLCMsgDownlinkIE
{
// Timing Advance defined in GSM05.10
Field_z<1> mHaveTimingAdvanceValue;
Field_z<6> mTimingAdvanceValue; // New timing advance if present.
// We dont use the following:
// Timeslot number defined in GSM05.10
Field_z<1> mHaveContinuousTiming; // Controls next two fields:
Field_z<4> mTimingAdvanceIndex; // Used for continuous timing advance.
Field_z<3> mTimingAdvanceTimeslotNumber; // Timeslot that gets the continuous timing advance.
void setTimingAdvance(int ta) { mHaveTimingAdvanceValue=1; mTimingAdvanceValue=ta; }
void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCMsgPacketTimingAdvanceIE::writeBody(MsgCommon &dst) const {
if (dst.write01(mHaveTimingAdvanceValue)) {
dst.WRITE_ITEM(mTimingAdvanceValue);
}
if (dst.write01(mHaveContinuousTiming)) {
dst.WRITE_ITEM(mTimingAdvanceIndex);
dst.WRITE_ITEM(mTimingAdvanceTimeslotNumber);
}
}
#endif
// GSM04.60 12.12a - same as TimingAdvanceIE but allows separate
// values for uplink and downlink.
struct RLCMsgPacketGlobalTimingAdvanceIE : public RLCMsgDownlinkIE
{
// Timing Advance defined in GSM05.10
Bool_z mHaveTimingAdvanceValue;
Field_z<6> mTimingAdvanceValue; // New timing advance if present.
// Timeslot number defined in GSM05.10
// We dont use the following:
Bool_z mHaveUplinkContinuousTiming; // Controls next two fields:
Field_z<4> mUplinkTimingAdvanceIndex; // Used for continuous timing advance.
Field_z<3> mUplinkTimingAdvanceTimeslotNumber; // Timeslot that gets the continuous timing advance.
Bool_z mHaveDownlinkContinuousTiming; // Controls next two fields:
Field_z<4> mDownlinkTimingAdvanceIndex; // Used for continuous timing advance.
Field_z<3> mDownlinkTimingAdvanceTimeslotNumber; // Timeslot that gets the continuous timing advance.
void setTimingAdvance(int ta) { mHaveTimingAdvanceValue=1; mTimingAdvanceValue=ta; }
void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCMsgPacketGlobalTimingAdvanceIE::writeBody(MsgCommon &dst) const {
if (dst.write01(mHaveTimingAdvanceValue)) {
dst.WRITE_ITEM(mTimingAdvanceValue);
}
if (dst.write01(mHaveUplinkContinuousTiming)) {
dst.WRITE_ITEM(mUplinkTimingAdvanceIndex);
dst.WRITE_ITEM(mUplinkTimingAdvanceTimeslotNumber);
}
if (dst.write01(mHaveDownlinkContinuousTiming)) {
dst.WRITE_ITEM(mDownlinkTimingAdvanceIndex);
dst.WRITE_ITEM(mDownlinkTimingAdvanceTimeslotNumber);
}
}
#endif
// GSM04.60 12.13
struct RLCMsgPowerControlParametersIE : public RLCMsgDownlinkIE
{
Field_z<4> mAlpha; // 4 bits.
Bool_z mHaveTN[8];
Field_z<5> mGammaTN[8]; // 5 bits each
void setAlpha(int alpha) { mAlpha = alpha; }
void setGamma(int timeslot, int gamma) { mGammaTN[timeslot] = gamma; mHaveTN[timeslot] = true; }
void setFrom(TBF *tbf);
void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCMsgPowerControlParametersIE::writeBody(MsgCommon &dst) const {
dst.WRITE_ITEM(mAlpha);
for (int i=0; i<8; i++) {
if (dst.write01(mHaveTN[i])) dst.WRITE_ITEM(mGammaTN[i]);
}
}
#endif
// This is not an official Informaion Element, but is commonly used in several messages.
// Used to identify to the MS targetted for a downlink message.
// The MS may be identified by TLLI, uplink TFI, or downlink TFI.
// In some messages, can also use TQI or Packet Request Reference,
// but we dont use those.
// This is not an official "Information Element", but it is used the
// same way in many downlink messages, since they all have to target an MS.
struct RLCMsgGlobalTFIorTLLIElt : public RLCMsgDownlinkIE
{
Bool_z mHaveGlobalTFI;
// Global TFI includes the following two fields:
RLCMsgGlobalTFIIE mGlobalTFI; // This is NOT a TFI assignment; it is used
// to identify the MS which receives this message.
Bool_z mHaveTLLI;
Field_z<32> mTLLI;
void setTLLI(int tlli) {
mHaveGlobalTFI=0; mHaveTLLI=1; mTLLI=tlli;
}
void setGlobalTFI(bool wIsDownlinkTFI,int wTFI) {
mHaveGlobalTFI = 1; mGlobalTFI.setTFI(wIsDownlinkTFI,wTFI);
}
void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCMsgGlobalTFIorTLLIElt::writeBody(MsgCommon &dst) const {
if (mHaveGlobalTFI) {
dst.write0();
mGlobalTFI.writeBody(dst);
} else if (mHaveTLLI) {
dst.write1();
dst.write0();
dst.writeField(mTLLI,32,"TLLI",tohex);
} else {
// Could be TQI or Packet Request Reference
assert(0);
}
}
#endif
// GSM04.60 12.21
class RLCMsgStartingFrameNumberIE : public RLCMsgDownlinkIE
{
public:
Field_z<1> mAbsoluteOrRelative; // 0 => absolute, 1 => relative
Field_z<16> mAbsoluteFrameNumber;
Field_z<13> mRelativeFrameNumber;
void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCMsgStartingFrameNumberIE::writeBody(MsgCommon &dst) const {
//dst.writeField(mAbsoluteOrRelative,1);
dst.WRITE_ITEM(mAbsoluteOrRelative);
if (mAbsoluteOrRelative) {
dst.WRITE_ITEM(mRelativeFrameNumber);
} else {
dst.WRITE_ITEM(mAbsoluteFrameNumber);
}
}
#endif
// GSM04.60 11.2.29
// Also used in Timeslot Reconfigure message 44.060 11.2.31
class RLCMsgPacketUplinkAssignmentDynamicAllocationElt : public RLCMsgDownlinkIE
{
public:
Field_z<1> mExtendedDynamicAllocation; // Needed if number up > number down
// P0, PR_MODE unused.
Field_z<1> mUSFGranularity; // Use 0 for USF specified per-block.
// In the TimeslotReconfigure message, this is never present, always one-bit 0:
Bool_z mUplinkTFIAssignmentPresent;
Field_z<5> mUplinkTFIAssignment;
//Bool_z mRLCDataBlocksGrantedPresent;
//Field_z<8> mRLCDataBlocksGranted;
Field_z<1> mTBFStartingTimePresent;
RLCMsgStartingFrameNumberIE mTBFStartingTime;
// There are two types of Timeslot allocation, with and without power control params.
Bool_z mHavePower; // Set if gamma supplied for any timeslot.
Field_z<4> mAlpha; // 4 bits.
Bool_z mHaveTN[8];
Field_z<3> mUSFTN[8]; // 3 bits each.
Field_z<5> mGammaTN[8]; // 5 bits each
void setUplinkTFI(int tfi) { mUplinkTFIAssignmentPresent=1; mUplinkTFIAssignment=tfi; }
void setUSF(int timeslot, int usf) { mUSFTN[timeslot] = usf; mHaveTN[timeslot] = true; }
void setGamma(int timeslot, int gamma) { mGammaTN[timeslot] = gamma; mHavePower = true; }
void setAlpha(int alpha) { mAlpha = alpha; mHavePower = true; }
void setFrom(TBF *tbf,MultislotSymmetry);
void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCMsgPacketUplinkAssignmentDynamicAllocationElt::writeBody(MsgCommon &dst) const {
dst.WRITE_ITEM(mExtendedDynamicAllocation);
dst.write0(); // No P0, PR_MODE.
dst.WRITE_ITEM(mUSFGranularity);
//dst.WRITE_OPT_FIELD01(mUplinkTFIAssignment,5,mUplinkTFIAssignmentPresent);
dst.WRITE_OPT_ITEM01(mUplinkTFIAssignment,mUplinkTFIAssignmentPresent);
// 44.060 removed the RLC Data Blocks Granted field, just inserts a 0 bit here.
// dst.WRITE_OPT_ITEM01(mRLCDataBlocksGranted,mRLCDataBlocksGrantedPresent);
dst.write0(); // RLC Data Blocks Granted removed from 44.060 11.2.29
//dst.writeField(mTBFStartingTimePresent,1);
dst.WRITE_ITEM(mTBFStartingTimePresent);
if (mTBFStartingTimePresent) {
mTBFStartingTime.writeBody(dst);
}
char name[14]; // Make the output pretty.
strcpy(name,"USF_TN?");
char *end = name+strlen(name)-1;
if (dst.write01(mHavePower)) {
dst.WRITE_ITEM(mAlpha);
for (int i=0; i<8; i++) {
if (dst.write01(mHaveTN[i])) {
*end = '0' + i;
dst.writeField(mUSFTN[i],3,name);
dst.writeField(mGammaTN[i],5,"Gamma");
}
}
} else {
for (int i=0; i<8; i++) {
if (dst.write01(mHaveTN[i])) {
*end = '0' + i;
dst.writeField(mUSFTN[i],3,name);
}
}
}
}
#endif
/** GSM04.60 11.2.29 From network to MS */
class RLCMsgPacketUplinkAssignment : public RLCDownlinkMessage
{
public:
//Bool_z mPersistenceLevelPresent;
//Field_z<4> mPersistenceLevel[4]; // This is for PRACH control, so we wont use it.
// mTQI // We wont use it.
// mPacketRequestReference // We wont use it.
RLCMsgGlobalTFIorTLLIElt mMSID;
void setTLLI(int tlli) { mMSID.setTLLI(tlli); }
// Currently we only support CS-1 for uplink.
ChannelCodingType mChannelCodingCommand; // MS to use which: CS-1, etc.
// For one phase access, the MS must send the TLLI in an RLC Data Block early on.
// TLLIBlockChannelCoding controls the coding of that block. We are not using it.
Field_z<1> mTLLIBlockChannelCoding; // 0 MS to use CS-1 for TLLI block, 1 to use mChannelCoding.
RLCMsgPacketTimingAdvanceIE mPacketTimingAdvance;
void setTimingAdvance(int ta) { mPacketTimingAdvance.setTimingAdvance(ta); }
// FreqParams specifies an ARFCN, TSC (Training Sequence Code) frequency hopping, etc.
// FrequenceParameters; // We dont use it.
// Finally, the allocation itself: We only support dynamic on uplink for now.
Field_z<2> mAllocationType; // 1 => dynamic, 2 => single block, 3 => fixed
RLCMsgPacketUplinkAssignmentDynamicAllocationElt mDynamicAllocation;
RLCMsgPacketUplinkAssignmentDynamicAllocationElt *setDynamicAllocation() {
mAllocationType = 1;
return &mDynamicAllocation;
}
RLCMsgPacketUplinkAssignment(TBF *tbf)
: RLCDownlinkMessage(RLCDownlinkMessage::PacketUplinkAssignment,true,tbf),
mChannelCodingCommand(tbf->mtChannelCoding())
//mChannelCodingCommand(ChannelCodingCS1)
{
// Blackberry was using CS-1 when commanded to use CS-4, and it shouldnt matter
// what we put here, so try 1. Didnt help, back to 0
mTLLIBlockChannelCoding = 0;
}
void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCMsgPacketUplinkAssignment::writeBody(MsgCommon &dst) const {
dst.write0(); // No persistence Level field.
// MSID Could also be TQI or Packet Request Reference in this message; we dont support them.
mMSID.writeBody(dst);
dst.write0(); // Message escape, means more stuff follows.
dst.WRITE_FIELD(mChannelCodingCommand,2);
dst.WRITE_ITEM(mTLLIBlockChannelCoding);
mPacketTimingAdvance.writeBody(dst);
dst.write0(); // No frequency parameters.
dst.WRITE_ITEM(mAllocationType);
switch (mAllocationType) {
case 0: // reserved.
assert(0);
case 1: // dynamic allocation
mDynamicAllocation.writeBody(dst);
break;
case 2: // single block allocation
assert(0);
case 3: // fixed allocation
assert(0);
}
dst.write0(); // One more 0 required to mark no Packet Extended Timing Advance.
}
#endif
/** GSM 04.60 11.2.7 From network to MS */
// Unlike an uplink assignment there is no ChannelCoding in the downlink assignment
// because it is passed in the qbits in each radio block.
// See: PDCHL1Downlink::transmit(BitVector *mI, const int *qbits)
class RLCMsgPacketDownlinkAssignment : public RLCDownlinkMessage
{
public:
//Bool_z mPersistenceLevelPresent;
//Field_z<r> mPersistenceLevel[4]; // This is for PRACH control, so we wont use it.
RLCMsgGlobalTFIorTLLIElt mMSID;
void setTLLI(int tlli) { mMSID.setTLLI(tlli); }
Field_z<2> mMACMode; // 0 == dynamic, 2 = fixed. Not sure why this is here.
Field_z<1> mRLCMode; // 0 == acknowledged, 1 == unacknowleged mode.
Field_z<1> mControlAck; // Set if establishing a new downlink TBF and T3192 is running.
// We only use this message to change an existing downlink,
// so always 0. Not sure how it could ever be 1.
Field_z<8> mTimeslotAllocation;
RLCMsgPacketTimingAdvanceIE mPacketTimingAdvance;
void setTimingAdvance(int ta) { mPacketTimingAdvance.setTimingAdvance(ta); }
// No P0, BTS_PWR_CTRL_MODE, PR_MODE.
// No Frequence Parameters
Bool_z mHaveDownlinkTFIAssignment;
Field_z<5> mDownlinkTFIAssignment; // Assigned TFI for this downlink.
Bool_z mHavePowerControlParameters;
RLCMsgPowerControlParametersIE mPowerControlParameters;
// No TBF Starting Time.
// No Measurement Mapping.
RLCMsgPacketDownlinkAssignment(TBF *tbf, bool isNewAssignment);
void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
RLCMsgPacketDownlinkAssignment::RLCMsgPacketDownlinkAssignment(TBF *tbf,bool isNewAssignment)
: RLCDownlinkMessage(RLCDownlinkMessage::PacketDownlinkAssignment,true,tbf)
{
mTimeslotAllocation = tbf->mtMS->msGetDownlinkTimeslots(MultislotSymmetric);
// We dont track T3192, we track T3193, which is (almost) the same thing.
mControlAck = isNewAssignment ? tbf->mtMS->msT3193.active() : 0;
// 5-13-2012 I tried setting this to true all the time, and it seemed to make no difference
// on the Blackberry, in particular, did not get rid of the cause=3105 retries.
// 5-23-2012: After a 3105 failure we retry with a new TBF. That tbf was established
// with controlack=false for some reason, and based on its first downlinkacknack, it clearly
// took over from the previous TBF rather than establishing a new one.
// So I am setting this to true permanently.
// 6-18: The above is all fixed. The Blackberry ignores ControlAck
// but other phones do not, so it must be set properly.
assert(tbf->mtTFI >= 0);
mHaveDownlinkTFIAssignment = true;
mDownlinkTFIAssignment = tbf->mtTFI;
mHavePowerControlParameters = true;
mPowerControlParameters.setFrom(tbf);
}
void RLCMsgPacketDownlinkAssignment::writeBody(MsgCommon &dst) const {
dst.write0(); // No persistence level yet.
mMSID.writeBody(dst); // MS identified by either uplink or downlink TFI.
dst.write0(); // Message escape, means more stuff follows.
// Next comes MAC_MODE, dynamic, fixed, etc.
// This is mostly meaningless for a downlink, and I dont know why it is here.
// Except if assigned fixed slots, the MS can do other stuff when its not busy
// listening to us.
dst.WRITE_ITEM(mMACMode);
dst.WRITE_ITEM(mRLCMode);
dst.WRITE_ITEM(mControlAck);
dst.WRITE_ITEM(mTimeslotAllocation);
mPacketTimingAdvance.writeBody(dst);
dst.write0(); // No P0, PR_MODE.
dst.write0(); // No Frequency Parameters.
if (dst.write01(mHaveDownlinkTFIAssignment)) {
dst.WRITE_ITEM(mDownlinkTFIAssignment);
}
mPowerControlParameters.writeOptional01(dst,mHavePowerControlParameters);
dst.write0(); // No TBF Starting time.
dst.write0(); // No Measurement mapping.
dst.write0(); // End of message (no EGPRS, etc, stuff.)
}
#endif
// 44.060 11.2.31
// This message does not include a TLLI so it can not be used to initiate
// an uplink or downlink assignment. This message includes tfi assignment
// elements, but they are only to change the tfi of an existing TBF.
// The normal uplink and downlink assignment messages can only allocate PDCH
// symmetrically, ie, 2-up/2-down, and we must use this message to change
// to any other multislot mode.
class RLCMsgPacketTimeslotReconfigure : public RLCDownlinkMessage
{
public:
RLCMsgGlobalTFIIE mGlobalTFI; // This is NOT a TFI assignment; it is used
// to identify the MS which receives this message.
// This IE has timing advance for both uplink and downlink.
// We dont need it so unimplemented, just use three '0' bits instead.
// GLOBAL_PACKET_TIMING_ADVANCE
// Elements for downlink modification:
Field_z<1> mDownlinkRlcMode; // 0 == acknowledged, 1 == unacknowleged mode.
// Always acknowledged for us.
Field_z<1> mControlAck; // Set if T3192 is running in MS.
// DOWNLINK_TFI_ASSIGNMENT unused
// UPLINK_TFI_ASSIGNMENT unused
Field_z<8> mDownlinkTimeslotAllocation;
// FrequenceParameters; // We dont use it.
// Elements for an uplink modification:
ChannelCodingType mChannelCodingCommand;
RLCMsgPacketUplinkAssignmentDynamicAllocationElt mDynamicAllocation;
RLCMsgPacketUplinkAssignmentDynamicAllocationElt *setDynamicAllocation() {
//mAllocationType = 1;
return &mDynamicAllocation;
}
RLCMsgPacketTimeslotReconfigure(TBF *tbf);
void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
RLCMsgPacketTimeslotReconfigure::RLCMsgPacketTimeslotReconfigure(TBF *tbf)
: RLCDownlinkMessage(RLCDownlinkMessage::PacketTimeslotReconfigure,true,tbf)
{
mGlobalTFI.setTFI(tbf->mtDir == RLCDir::Down,tbf->mtTFI);
mDownlinkTimeslotAllocation = tbf->mtMS->msGetDownlinkTimeslots(MultislotFull);
mChannelCodingCommand = tbf->mtChannelCoding();
// Must not call mDynamicAllocationsetUplinkTFI()
mDynamicAllocation.setFrom(tbf,MultislotFull);
}
void RLCMsgPacketTimeslotReconfigure::writeBody(MsgCommon &dst) const
{
// PAGE_MODE is handled by the parent class
dst.write0(); // unlabeled extra 0 bit.
mGlobalTFI.writeBody(dst);
dst.write0(); // message escape: not EGPRS
dst.WRITE_FIELD(mChannelCodingCommand,2);
dst.writeField(0,3); // Skip Global Packet Timing Advance
dst.WRITE_ITEM(mDownlinkRlcMode);
dst.WRITE_ITEM(mControlAck);
dst.write0(); // no downlink tfi assignment
dst.write0(); // no uplink tfi assignment
dst.WRITE_ITEM(mDownlinkTimeslotAllocation);
dst.write0(); // no frequence parameters
dst.write0(); // just an extra 0 bit.
mDynamicAllocation.writeBody(dst);
dst.write0(); // Marks end of message, no extensions.
}
#endif
/** GSM 04.60 11.2.1 From network to MS */
class RLCMsgPacketAccessReject : public RLCDownlinkMessage
{
public:
Field_z<32> mTLLI;
// GlobalTFI mGlobalTFI; We dont use.
Bool_z mHaveWaitIndication;
Field_z<8> mWaitIndication;
Field_z<1> mWaitIndicationSize;
RLCMsgPacketAccessReject(TBF *tbf)
: RLCDownlinkMessage(RLCDownlinkMessage::PacketAccessReject,false,NULL),
mTLLI(tbf->mtMS->msTlli)
{ }
void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCMsgPacketAccessReject::writeBody(MsgCommon &dst) const {
dst.write1(); // Start of reject struct.
dst.write0(); // Indicate we have a TLLI
dst.writeField(mTLLI,32,"TLLI",tohex);
if (dst.write01(mHaveWaitIndication)) {
dst.WRITE_ITEM(mWaitIndication);
dst.WRITE_ITEM(mWaitIndicationSize);
}
dst.write0(); // No more reject structs.
}
#endif
/** GSM 04.60 11.2.26 From network to MS */
class RLCMsgPacketTBFRelease : public RLCDownlinkMessage
{
public:
RLCMsgGlobalTFIIE mGlobalTFI;
Field_z<1> mUplinkRelease;
Field_z<1> mDownlinkRelease;
Field_z<4> mTBF_RELEASE_CAUSE;
RLCMsgPacketTBFRelease(TBF *tbf);
void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
RLCMsgPacketTBFRelease::RLCMsgPacketTBFRelease(TBF *tbf)
: RLCDownlinkMessage(RLCDownlinkMessage::PacketTBFRelease,true,tbf)
{
assert(tbf->mtTFI != -1); // TBF has not been released yet.
mGlobalTFI.mGTFI = tbf->mtTFI;
if (tbf->mtDir == RLCDir::Down) {
mGlobalTFI.mIsDownlinkTFI = true;
mDownlinkRelease = true;
} else {
mGlobalTFI.mIsDownlinkTFI = false;
mUplinkRelease = true;
}
mTBF_RELEASE_CAUSE = 2; // Abnormal release, the only reason we will ever do this.
}
void RLCMsgPacketTBFRelease::writeBody(MsgCommon &dst) const {
dst.write0(); // Just an extra 0 in the spec.
mGlobalTFI.writeBody(dst);
dst.WRITE_ITEM(mUplinkRelease);
dst.WRITE_ITEM(mDownlinkRelease);
dst.WRITE_ITEM(mTBF_RELEASE_CAUSE);
}
#endif
/** GSM 04.60 11.2.28 From network to MS */
// Note that description in the GSM documentation is indented out of sync
// with the options; be careful reading it.
class RLCMsgPacketUplinkAckNack : public RLCDownlinkMessage
{
private:
Field_z<5> mUplinkTFI;
ChannelCodingType mChannelCodingCommand;
RLCMsgPacketAckNackDescriptionIE mAND;
// 12.12b: Allows larger timing advance values. We wont use.
Bool_z mPacketExtendedTimingAdvancePresent;
Field_z<2> mPacketExtendedTimingAdvance;
// 4.60 9.2.3.4 Describes TBF_EST use.
// For dynamic uplink (which we use) the MS may return either a ControlAcknowledgement
// or a PacketResourceRequest in the RRBP reservation to this message.
Field<1> mTBFEst; // If true, MS may request a new uplink TBF in the response Packet Control Acknowledgment.
// It helps avoid raches in the case where there is an
// uplink and no downlink.
// The contention resolution TLLI is needed for one-phase uplink access, for which
// the network does not know the TLLI when it does the uplink resource assignment,
// and has to get it from the uplink data; this specifies that the necessary
// TLLI is inside this block somewhere.
// We arent using this.
//bool mHaveContentionResolutionTLLI;
//unsigned mContentionResolutionTLLI;
// We dont need to supply timing advance or power control in this message,
// because we supplied these in the assignment message.
Bool_z mHavePacketTimingAdvance;
RLCMsgPacketTimingAdvanceIE mPacketTimingAdvance;
void setTimingAdvance(int ta) {
mHavePacketTimingAdvance = true;
mPacketTimingAdvance.setTimingAdvance(ta);
}
Bool_z mHavePowerControlParameters;
RLCMsgPowerControlParametersIE mPowerControlParameters;
public:
RLCMsgPacketUplinkAckNack(TBF *wtbf, const RLCMsgPacketAckNackDescriptionIE& wAND);
void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
RLCMsgPacketUplinkAckNack::RLCMsgPacketUplinkAckNack(
TBF *wtbf, const RLCMsgPacketAckNackDescriptionIE& wAND)
: RLCDownlinkMessage(RLCDownlinkMessage::PacketUplinkAckNack,false,wtbf),
mUplinkTFI(wtbf->mtTFI),
mChannelCodingCommand(wtbf->mtChannelCoding()),
mAND(wAND),
// 6-26-2012: set TBF_EST true by default.
mTBFEst(gConfig.getBool("GPRS.TBF.EST")) // Allow MS to request another uplink assignment instead of Packet Control Acknowledgment in RRBP response.
{
assert(wtbf->mtDir == RLCDir::Up);
setTimingAdvance(wtbf->mtMS->msGetTA());
}
void RLCMsgPacketUplinkAckNack::writeBody(MsgCommon &dst) const {
dst.write0(); // two extra 0 after PAGE_MODE.
dst.write0();
dst.WRITE_ITEM(mUplinkTFI);
dst.write0(); // message escape - indicates normal (non-EGPRS) message.
dst.WRITE_FIELD(mChannelCodingCommand,2);
mAND.writeBody(dst);
dst.write0(); // no CONTENTION_RESOLUTION_TLLI
if (dst.write01(mHavePacketTimingAdvance)) {
mPacketTimingAdvance.writeBody(dst);
}
if (dst.write01(mHavePowerControlParameters)) {
mPowerControlParameters.writeBody(dst);
}
dst.write0(); // no Extension Bits.
dst.write0(); // no Fixed Allocation Parameters.
// Note that the indentation in the documentation here is misleading.
dst.write1(); // Enable additions for R99
if (dst.write01(mPacketExtendedTimingAdvance)) {
dst.WRITE_ITEM(mPacketExtendedTimingAdvance);
}
dst.WRITE_ITEM(mTBFEst);
}
#endif
// GSM04.60 11.2.8 From network to MS
struct RLCMsgPacketDownlinkDummyControlBlock : public RLCDownlinkMessage
{
//Field_z<1> mPersistenceLevelPresent;
//Field_z<4> mPersistenceLevel[4]; // This is for PRACH control, so we wont use it.
RLCMsgPacketDownlinkDummyControlBlock()
: RLCDownlinkMessage(RLCDownlinkMessage::PacketDownlinkDummyControlBlock,false,NULL)
{ RN_MEMCHKNEW(RLCMsgPacketDownlinkDummyControlBlock) }
~RLCMsgPacketDownlinkDummyControlBlock() { RN_MEMCHKDEL(RLCMsgPacketDownlinkDummyControlBlock) }
void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCMsgPacketDownlinkDummyControlBlock::writeBody(MsgCommon &dst) const {
dst.write0(); // No Persistence Level
}
#endif
///** A factory function for uplink messages */
//RLCUplinkMessage *RLCUplinkFactory(RLCUplinkMessage::MTI);
///** Generic RLC uplink parser */
extern RLCUplinkMessage* RLCUplinkMessageParse(RLCRawBlock *src);
/** GSM 04.60 11.2.13 From network to MS */
// Note that description in the GSM documentation is indented out of sync
// with the options; be careful reading it.
class RLCMsgPacketPowerControlTimingAdvance : public RLCDownlinkMessage
{
// The MS may be identified by GlobalTFI, TQI, or PacketRequestReference,
// but we only use GlobalTFI, so I left the other methods out:
Bool_z mHaveGlobalTFI; // Better be true.
RLCMsgGlobalTFIIE mGlobalTFI; // 12.10
// Global Power Control Parameters not implemented:
// Bool_z mHaveGlobalPowerControlParameters;
// RLCMsgGlobalPowerControlParametersIE mPowerControlParameters; // 12.9
Bool_z mHavePowerControlParameters;
RLCMsgPowerControlParametersIE mPowerControlParameters; // 12.9
Bool_z mHaveGlobalTimingAdvance;
RLCMsgPacketGlobalTimingAdvanceIE mGlobalTimingAdvance; // 12.12a
// 12.12b: Allows larger timing advance values.
Bool_z mPacketExtendedTimingAdvancePresent;
Field_z<2> mPacketExtendedTimingAdvance;
public:
RLCMsgPacketPowerControlTimingAdvance(TBF *wtbf)
: RLCDownlinkMessage(RLCDownlinkMessage::PacketPowerControlTimingAdvance,false,wtbf)
{
assert(wtbf->mtTFI >= 0);
setGlobalTFI(wtbf->mtDir == RLCDir::Down,wtbf->mtTFI);
setTimingAdvance(wtbf->mtMS->msGetTA());
}
void setTimingAdvance(int ta) {
mHaveGlobalTimingAdvance = true;
mGlobalTimingAdvance.setTimingAdvance(ta);
}
void setGlobalTFI(bool wIsDownlinkTFI,int wTFI) {
mHaveGlobalTFI = 1; mGlobalTFI.setTFI(wIsDownlinkTFI,wTFI);
}
void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCMsgPacketPowerControlTimingAdvance::writeBody(MsgCommon &dst) const {
// page_mode is written by RLCDownlinkMessage writeHeader()
if (mHaveGlobalTFI) {
dst.write0();
mGlobalTFI.writeBody(dst);
} else {
assert(0); // Other MS ID methods not supported.
}
dst.write0(); // message escape.
dst.write0(); // No Global Power Parameters.
if (mHaveGlobalTimingAdvance && mHavePowerControlParameters) {
dst.write0();
mGlobalTimingAdvance.writeBody(dst);
mPowerControlParameters.writeBody(dst);
} else {
dst.write1();
if (mHaveGlobalTimingAdvance) {
dst.write0();
mGlobalTimingAdvance.writeBody(dst);
} else {
dst.write1();
mPowerControlParameters.writeBody(dst);
}
}
// The following is confusing in the spec because
// the indentation is misleading.
// Look carefully and note that there is no ending
// curly brace on the line " null | 0 bit"
if (dst.write01(mPacketExtendedTimingAdvancePresent)) {
if (dst.write01(mPacketExtendedTimingAdvancePresent)) {
dst.WRITE_ITEM(mPacketExtendedTimingAdvance);
}
}
}
#endif
// GSM04.60 11.2.9b.
// When you set the NETWORK_CONTROL_ORDER to NC2 (network does cell reselection)
// then the MS sends measurement reports at the reporting periods defined below.
// These messages clog up our very limited ACCH.
// This message is one of the ways to change the reporting period.
// I'm not sure there is any other way for us.
class RLCMsgPacketMeasurementOrder : public RLCDownlinkMessage
{
RLCMsgGlobalTFIorTLLIElt mMSID;
void setTLLI(int tlli) { mMSID.setTLLI(tlli); }
void setGlobalTFI(bool wIsDownlinkTFI,int wTFI) {
mMSID.setGlobalTFI(wIsDownlinkTFI,wTFI);
}
Field_z<3> mPMOIndex;
Field_z<3> mPMOCount;
Bool_z mHaveNCMeasurementParameters;
Field<2> mNetworkControlOrder;
Field<3> mNCNonDrxPeriod; // Non-drx period after MS sends a measurement.
// Reporting period is documented in PSI5
// It is a 3 bit code. Larger values mean longer times.
// Min is 0.48 sec and max is 61.44 sec.
Field<3> mNCReportingPeriodI; // GPRS default is 7 == 61.44 sec.
Field<3> mNCReportingPeriodT; // GPRS default is 3 == 3.84 sec.
RLCMsgPacketMeasurementOrder(TBF*wtbf) :
RLCDownlinkMessage(RLCDownlinkMessage::PacketMeasurementOrder,false,wtbf),
mNetworkControlOrder(2),
mNCNonDrxPeriod(0),
mNCReportingPeriodI(7), // Max these out
mNCReportingPeriodT(7)
{}
void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCMsgPacketMeasurementOrder::writeBody(MsgCommon &dst) const {
// page_mode is written by RLCDownlinkMessage writeHeader()
mMSID.writeBody(dst); // MS identified by either uplink or downlink TFI.
dst.WRITE_ITEM(mPMOIndex);
dst.WRITE_ITEM(mPMOCount);
if (dst.write01(mHaveNCMeasurementParameters)) {
dst.WRITE_ITEM(mNetworkControlOrder);
dst.write1(); // We always include this optional section.
dst.WRITE_ITEM(mNCNonDrxPeriod);
dst.WRITE_ITEM(mNCReportingPeriodI);
dst.WRITE_ITEM(mNCReportingPeriodT);
dst.write0(); // No NC_FREQUENCY_LIST.
}
dst.write0(); // No EXT Measurement Parameters.
dst.write0(); // No optional stuff follows.
}
#endif
}; // namespace GPRS
#endif