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

509 lines
21 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 Common-use GSM declarations, most from the GSM 04.xx and 05.xx series. */
#ifndef GPRSL2MAC_H
#define GPRSL2MAC_H
#include "MemoryLeak.h"
#include "GPRSRLC.h"
#include "GSML1FEC.h"
#include "GSMTDMA.h"
#include "GSMTransfer.h" // For GSM::L2Frame
#include "Interthread.h"
//#include "GSMCommon.h" // For ChannelType
#include "GSML3RRElements.h" // For RequestReference
#include "TBF.h"
#include "RList.h"
#include "Utils.h"
#include <list>
namespace GPRS {
extern void mac_debug();
// (pat) About BSN (radio block sequence numbers):
// We need a period for our internal BSNs, and it is somewhat arbitrary.
// For timing, we need a cyclic period of at least 8 52-multiframes. (GSM03.64 Figure 19)
// There are specified timeouts of up to 5 seconds over which it would be convenient
// if radio block numbers were non-repeating, which would be about 1000 frames, 250 radio blocks.
// But there is no reason not to just use the hyperframe, so we will.
// A hyperframe is 2048 * 26 * 51 frames; (2048*26*51 * 12/52) = 626688 Radio Blocks.
// A 52 multiframe takes 240ms, so each radio block averages 20ms.
// btw, if the block sequence numbers went on forever, stored in an int, it would last 497 days.
// There are 52 frames for 12 blocks.
// In GSM the downlink block numbers trail the uplink block numbers slightly.
// I observed the incoming blocks are this far behind gBSNNext.
// If you are expecting an answer at time N,
// look for it when gBSNext == N+BSNLagTime.
const int BSNLagTime = 4;
extern bool gFixTFIBug;
extern bool gFixSyncUseClock; // For debugging: Use wall time for service loop synchronization
// instead of GSM frames. TODO: Get rid of this.
extern int gFixIdleFrame; // Works around this bug - see code.
extern int gFixDRX; // Works around DRX mode bug. The value is the number of assignments
// that are unanswered before we assume the MS is in DRX mode.
// Set to 1 to request a poll in the ImmediateAssignment on CCCH.
// We still have to wait for the poll time anyway, so this is here
// only for debugging.
extern bool gFixIAUsePoll;
extern bool gFixConvertForeignTLLI;
typedef RList<PDCHL1FEC*> PDCHL1FECList_t;
typedef RList<MSInfo*> MSInfoList_t;
struct Stats_t {
Statistic<double> macServiceLoopTime;
UInt_z countPDCH;
UInt_z countMSInfo;
UInt_z countTBF;
UInt_z countRach;
};
extern struct Stats_t Stats;
// For now, there is only one pool of TFIs shared by all channels.
// It should be per-ARFCN, but I expect there to only be one.
// Sharing TFIs between channels on the ARFCN makes multislot tfi allocation easy.
// There are 32 uplink and 32 downlink TFIs, which should be enough for some time to come.
// If this gets congested, can be split up into one pool of TFIs per uplink/downlink channel,
// but then you have to be careful when allocating multislot tfis that the
// tfi is unique across all channels.
// TODO: When we allocate multislot tfis, the tfi must be unique in all slots.
// The easiest way to do that is to have a single tfilist for the entire system.
class TBF;
struct TFIList {
TBF *mTFIs[2][32]; // One list for uplink, one list for downlink.
// 12-22-2011: It looked like the Blackberry abandoned an uplink TBF when
// a downlink TBF was established using the same TFI. This seems like a horrible
// bug in the MS, but to work around it, I added the gFixTFIBug which uses
// a single TFI space for both uplink and downlink. This eliminated
// a bunch of msStop calls with cause T3101, so I think this is a genuine
// problem with MSs and we need this fix in permanently.
// Update: The TFI is reserved during the time after a downlink ends, so the MS
// may have been justifiably upset about seeing it reissued for an uplink too soon.
RLCDir::type fixdir(RLCDir::type dir) {
return gFixTFIBug ? RLCDir::Up : dir;
}
TFIList();
TBF *getTFITBF(RLCDirType dir, int tfi) { return mTFIs[fixdir(dir)][tfi]; }
void setTFITBF(int tfi,RLCDirType dir,TBF *tbf) { mTFIs[fixdir(dir)][tfi] = tbf; }
int findFreeTFI(RLCDirType dir);
void tfiDump(std::ostream&os);
};
extern struct TFIList *gTFIs;
#if MAC_IMPLEMENTATION
TFIList::TFIList() { for (int i=0;i<32;i++) { mTFIs[0][i] = mTFIs[1][i] = NULL; } }
int TFIList::findFreeTFI(RLCDirType dir)
{
static int lasttfi = 0;
dir = fixdir(dir);
// TODO: After the TBF the tfi may only be reused by the same MS for some
// period of time. See "TBF release" section.
// Temporary work around is to round-robin the tfis.
for (int i = 0; i < 32; i++) {
lasttfi++;
if (lasttfi == 32) { lasttfi = 0; }
if (!mTFIs[dir][lasttfi]) { return lasttfi; }
}
#if 0
// DEBUG: start at 1 instead of 0
for (int tfi = 1; tfi < 32; tfi++) {
// DEBUG: try incrementing tfi to avoid duplication errors:
if (tfi == lasttfi) { continue; }
if (!mTFIs[dir][tfi]) {
lasttfi = tfi;
return tfi;
}
}
#endif
return -1;
}
void TFIList::tfiDump(std::ostream&os)
{
int dir, tfi;
for (dir = 0; dir <= (gFixTFIBug ? 0 : 1); dir++) {
os << "TFI=(";
if (!gFixTFIBug) {
os << RLCDir::name(dir) << ":";
}
for (tfi = 0; tfi < 32; tfi++) {
TBF *tbf = mTFIs[dir][tfi];
if (tbf) { os << " " << tfi << "=>" << tbf; }
}
}
os << ")\n";
}
#endif
// The USF associates radio blocks with an MS.
// It is placed in the downlink block header to indicate that the next uplink block
// is allocated to the MS assigned that USF.
// There may be multiple simultaneous uplink TBFs from the same MS; all will use the same USF.
// The MS does that if a higher priority or throughput TBF comes in while one is in progress.
// The USF is only 3 bits, and 0x7 is reserved (to indicate PRACH) on PCCCH channels.
// We are not using PCCCH, but I am going to avoid 0x7 anyway.
// Additionally, for RACH initiated single block uplink assignments, we need a USF
// that is not in use by any MS, for which we will reserve USF=0.
// so really only 6 USFs (1-6) are available per uplink channel, which should be plenty.
// Note that there are alot more TFIs than USFs, because TFIs are per-TBF,
// while USFs are per-MS.
// The USFList information applies to an uplink channel, but it is used primarily by
// the downlink channel to set the USF in the MAC header of each downlink block.
// Note that there is no pointer to the TBFs (could be multiple ones) from this USF struct,
// because arriving uplink blocks are associated with their TBFs using the TFI, not the USF.
// The USFs are numbered 0..7.
const int USFMIN = 1;
const int USFMAX = 6;
const int USFTotal = 8;
#define USF_VALID(usf) ((usf) >= USFMIN && (usf) <= USFMAX)
class USFList
{
//PDCHL1FEC *mlchan; // The channel these USFs are being used on.
// We use the usf as the index, so mlUSFs[0] is unused.
struct UsfInfo {
MSInfo *muMS; // The MS assigned this USF on this channel.
GprsTimer muDeadTime; // When a TBF dies reserve the USF for this ms until this expires.
};
UsfInfo mlUSFs[USFTotal]; // Some slots in this array are unused.
//int mRandomUSF; // Used to pick one of the USFs.
// When the channel is running, we save the usf that is sent on each downlink block,
// so that we can correlate the uplink responses independently of their content.
// We save them in an array indexed by bsn, length only has to cover the difference
// between uplink and downlink BSNs plus some slop, so 32 is way overkill.
unsigned char sRememberUsf[32]; // The usf transmitted in the downlink block.
unsigned sRememberUsfBsn[32];
public:
USFList();
// Return the usf that was specified on the downlink burst, given the bsn from the uplink burst.
int getUsf(unsigned upbsn);
// Remember the usf transmitted on specified downlink burst. OK to pass 0 for usf.
void setUsf(unsigned downusf, unsigned downbsn); // Save usf for current downlink burst.
// Which MS is using this USF?
MSInfo *getUSFMS(int usf);
int allocateUSF(MSInfo *ms);
int freeUSF(MSInfo *ms,bool wReserve);
//int getRandomUSF();
void usfDump(std::ostream&os);
};
extern void serviceRach();
// There is only one of these.
// It holds the lists used to find all the other stuff.
class L2MAC
{
Thread macThread;
// The entire MAC runs in a single thread.
// This Mutex is used at startup to make sure we only start one.
// Also used to lock the serviceloop so the CLI does not modify MS or TBF lists simultaneously.
public:
mutable Mutex macLock;
// The RACH bursts come in unsychronized to the rest of the GPRS code.
// The primary purpose of this queue is just to allow the MAC service loop
// to handle the RACH in its single thread by saving the RACH until the MAC service
// loop gets around to it. If GPRS is running, we dont really expect multiple
// simultaneous RACHs to queue up here because we service the RACH queue on each loop.
// However, if a RACH comes in while GPRS service is stopped and all channels
// are in use for RR connections, the as-yet-unserviced RACHs may queue up here.
// When GPRS service resumes, we should disard RACHs that are too old.
// Note that there could be multiple RACH for the same MS, a case we cannot detect.
InterthreadQueue<GSM::RachInfo> macRachQ;
// We are doing a linear search through these lists, but there should only be a few of them.
PDCHL1FECList_t macPDCHs; // channels assigned to GPRS.
PDCHL1FECList_t macPacchs; // The subset of macPDCHs that we assign as PACCH.
//Mutex macTbfListLock;
TBFList_t macTBFs; // active TBFs.
MSInfoList_t macMSs; // The MS we know about.
// For debugging, we keep expired TBF and MS around for post-mortem examination:
TBFList_t macExpiredTBFs;
MSInfoList_t macExpiredMSs;
#define RN_MAC_FOR_ALL_PDCH(ch) RN_FOR_ALL(PDCHL1FECList_t,gL2MAC.macPDCHs,ch)
#define RN_MAC_FOR_ALL_PACCH(ch) RN_FOR_ALL(PDCHL1FECList_t,gL2MAC.macPacchs,ch)
#define RN_MAC_FOR_ALL_MS(ms) for (RListIterator<MSInfo*> itr(gL2MAC.macMSs); itr.next(ms); )
#define RN_MAC_FOR_ALL_TBF(tbf) for (RListIterator<TBF*> itr(gL2MAC.macTBFs); itr.next(tbf); )
L2MAC()
{
gTFIs = new TFIList();
}
~L2MAC() { delete gTFIs; }
public:
unsigned macN3101Max;
unsigned macN3103Max;
unsigned macN3105Max;
unsigned macT3169Value;
unsigned macT3191Value;
unsigned macT3193Value;
unsigned macT3168Value;
unsigned macT3195Value;
unsigned macMSIdleMax;
unsigned macChIdleMax;
unsigned macChCongestionMax;
unsigned macDownlinkPersist;
unsigned macDownlinkKeepAlive;
unsigned macUplinkPersist;
unsigned macUplinkKeepAlive;
float macChCongestionThreshold;
Float_z macDownlinkUtilization;
Bool_z macRunning; // The macServiceLoop is running.
time_t macStartTime;
Bool_z macStopFlag; // Set this to terminate the service thread.
Bool_z macSingleStepMode; // For debugging.
MSInfo *macFindMSByTlli(uint32_t tlli, int create = 0);
void macAddMS(MSInfo *ms);
void macForgetMS(MSInfo *ms,bool forever);
// When deleting tbfs, macForgetTBF could be called on a tbf already removed
// from the list, which is ok.
void macAddTBF(TBF *tbf);
void macForgetTBF(TBF *tbf,bool forever);
void macServiceLoop();
PDCHL1FEC *macPickChannel(); // pick the least busy channel;
PDCHL1FEC *macFindChannel(unsigned arfcn, unsigned tn); // find specified channel, or null
unsigned macFindChannels(unsigned arfcn);
bool macAddChannel(); // Add a GSM RR channel to GPRS use.
bool macFreeChannel(); // Restore a GPRS channel back to GSM RR use.
void macForgetCh(PDCHL1FEC*ch);
void macConfigInit();
bool macStart(); // Fire it up.
void macStop(bool channelstoo); // Try to kill it.
int macActiveChannels(); // Is GPRS running, ie, are there channels allocated?
int macActiveChannelsC(unsigned cn); // Number of channels on specified 0-based ARFCN
float macComputeUtilization();
void macCheckChannels();
void macServiceRachQ();
};
extern L2MAC gL2MAC;
//const int TFIInvalid = -1;
//const int TFIUnknown = -2;
// The master clock is not exactly synced up with the radio clock.
// It is corrected at intervals. This means there is a variable delay
// between the time we send a message to the MS and when we can expect an answer.
// It does not affect RRBP reservations, which are relative, but it affects
// L3 messages sent on CCCH, which must specify an exact time for the response.
// I'm not sure what to do about this.
// I am just adding some ExtraDelay, and if reservations are unanswered,
// increasing this value until they start getting answered.
// We could probably set this back to 0 when we observe the time() run backwards,
// which means the clock is synced back up.
extern int ExtraClockDelay; // in blocks.
// This specifies a Radio Block reservation in an uplink channel.
// Reservations are used for:
// o response to CCCH Immediate Assignment One BLock initiated by MS on RACH.
// In this case we dont even know what MS the message was coming from, so if
// it does not arrive, nothing we can do but wait for the MS to try again later.
// o For an Uplink TBF: The RLCUpEngine sends AckNack every N blocks.
// We could require a response, but I dont think we will unless it gets stalled.
// o For a stalled Uplink TBF: The RLCUpEngine sends an AckNack with an RRBP
// reservation. The MS may respond with any type of message.
// If that response does not arrive, the RLCUpEngine network immediately sends
// another AckNack. Serviced when Uplink serviced.
// o For a completed Uplink TBF: network sends a final acknack with RRBP reservation,
// which must be repeated until received.
// Could be handled by the engine or MsgTransaction.
// o For a Downlink TBF: send an RRBP reservation every N blocks, which we expect
// the MS to use to send us an AckNack, or some other message. If we dont get
// a response, send another RRBP reservation immediately.
// o For a stalled Downlink TBF: resend the oldest block with another RRBP reservation.
// Note: a completed Downlink TBF can be destroyed immediately, since we received
// the final ack nack.
// The following are handled by the MsgTransaction class:
// o response to Packet Polling Request message.
// If the message does not arrive, we may want to try again.
// o RRBP responses in downlink TBF data blocks or control blocks.
// If these dont arrive from the MS, it doesnt matter.
// The response type is actually unknown: it could be a Packet Downlink Ack/Nack,
// or anything else the MS wants to send. The uplink message will have all the required
// info so we dont have to save anything in the RLCBlockReservation;
// o Packet Control Acknowledgement responses. Could come from:
// - Packet uplink/downlink assignment message, which may require network to resend.
// - Packet TBF release message, which requires network to resend.
// o WRONG: For an Uplink TBF: The RLCUpEngine sends AckNack after N blocks and provides an RRBP
// uplink response. The MS may respond with any type of message.
// If that response does not arrive, the RLCUpEngine network immediately sends
// another AckNack.
struct RLCBlockReservation : public Text2Str {
enum type {
None,
ForRACH,
ForPoll, // For the poll response to a downlink immediate assignment when
// the MS is in packet idle mode. see sendAssignment()
ForRRBP // This has many subtypes of type MsgTransactionType
};
type mrType; // Primary type
MsgTransactionType mrSubType; // Subtype, only valid if mrType is ForRRBP
RLCBSN_t mrBSN; // The block number that has been reserved.
TBF *mrTBF; // tbf if applicable. (Not known for MS initiated RACH.)
// TODO: Is it stupid to save mrRadData? We will get new data when the MS responds.
GSM::RadData mrRadData; // Saved from a RACH to be put in MS when it responds.
static const char *name(RLCBlockReservation::type type);
void text(std::ostream&) const;
};
std::ostream& operator<<(std::ostream& os, RLCBlockReservation::type &type);
std::ostream& operator<<(std::ostream& os, RLCBlockReservation &res);
#if MAC_IMPLEMENTATION
const char *RLCBlockReservation::name(RLCBlockReservation::type mode)
{
switch (mode) {
CASENAME(None)
CASENAME(ForRACH)
CASENAME(ForPoll)
CASENAME(ForRRBP)
default: return "unrecognized"; // Not reached, but makes gcc happy.
}
}
std::ostream& operator<<(std::ostream& os, RLCBlockReservation::type &type)
{
os << RLCBlockReservation::name(type);
return os;
}
void RLCBlockReservation::text(std::ostream &os) const
{
os << "res=(";
os << LOGVAR2("bsn",mrBSN) << " " << name(mrType);
if (mrTBF) { os << " " << mrTBF; }
os <<")";
}
std::ostream& operator<<(std::ostream& os, RLCBlockReservation &res)
{
res.text(os);
return os;
}
#endif
// The uplink reservation system.
// It is used by both uplink and downlink parts.
// I put it in its own class to avoid clutter elsewhere.
// Note that reservations are kept around after the current time passes,
// so that uplink acknowledgements can be paired with the message to which they belong.
//
// In order to efficiently utilize the uplink resource, we need to make reservations
// of future uplink RLCBlocks for various purposes.
// There is a problem in that the RRBP field is limited to identifying
// blocks 3-6 blocks in the future. The obvious solution would be to reserve
// them first, but that will not work because the control messages occur asynchronously
// with respect to the data streams and (should) have higher priority.
// My K.I.S.S. system to efficiently use these resources is to reserve even numbered
// uplink blocks for RRBP responses, which are typically Ack/Nack blocks,
// but could actually be any type of block, and to reserve odd numbered uplink blocks
// for all other control blocks, which include Single-Block accesses initiated
// by RACH (in which case, there is no TFI or TLLI) and all other control block
// responses to network initiated messages.
// A minimum sized downlink response is 2 blocks long, so this method tends to fully
// utilize the channel and still limit latency (as opposed to alternative schemes
// that would assign fixed slots for various messages, or lump all the blocks together
// which would mean that RRBP responses would sometimes not have a reservation available.)
// Note that a single-block downlink resend as a result of an Ack/Nack message with
// a single Nack may be only one block long, so it is still possible to run
// out of odd numbered uplink blocks for RRBP responses.
// When downlink blocks are finally queued for transmission, any unreserved uplink
// blocks are utilized for current uplink data transfers using dynamic allocation using USF.
// Using this method, the reservations are also monotonically increasing in each
// domain (RRBP and non-RRBP) which makes it easy.
class L1UplinkReservation
{
private:
// TODO: mLock no longer needed because RACH processing synchronous now.
Mutex mLock; // The reservation controller can be called from GSM threads, so protect it.
public:
// There should only be a few reservations at a time, probably limited to
// around one per actively attached MS.
// Update 8-8-2012: well, the MS can be in two sub-modes of transmit mode simultaneously,
// specifically, persistent keep-alive and reassignment, so I am upgrading this
// with the sub-type.
// There are timeouts of up to 5 seconds (250 blocks), so we should keep history that long.
// The maximum reservation in advance is probably from AGCH, which can stack up
// waiting for CCCH downlink spots up to a maximum (defined by a config option)
// in AccessGrantResponder(), but currently 1.5 seconds.
// There is no penalty for making this array larger, so just go ahead and overkill it.
static const int mReservationSize = (2*500);
RLCBlockReservation mReservations[mReservationSize];
L1UplinkReservation();
public:
RLCBSN_t makeReservationInt(RLCBlockReservation::type restype, RLCBSN_t afterBSN,
TBF *tbf, GSM::RadData *rd, int *prrbp, MsgTransactionType mttype);
RLCBSN_t makeCCCHReservation(GSM::CCCHLogicalChannel *AGCH,
RLCBlockReservation::type type, TBF *tbf, GSM::RadData *rd, bool forDRX, MsgTransactionType mttype);
RLCBSN_t makeRRBPReservation(TBF *tbf, int *prrbp, MsgTransactionType ttype);
// Get the reservation for the specified block timeslot.
// Return true if found, and return TFI in *TFI.
// bsn can be in the past or future.
RLCBlockReservation *getReservation(RLCBSN_t bsn);
void clearReservation(RLCBSN_t bsn, TBF *tbf);
RLCBlockReservation::type recvReservation(RLCBSN_t bsn, TBF**restbf, GSM::RadData *prd,PDCHL1FEC *ch);
void dumpReservations(std::ostream&os);
};
extern bool setMACFields(MACDownlinkHeader *block, PDCHL1FEC *pdch, TBF *tbf, int makeres,MsgTransactionType mttype,unsigned *pcounter);
extern int configGetNumQ(const char *name, int defaultvalue);
extern int configGprsMultislotMaxUplink();
extern int configGprsMultislotMaxDownlink();
// The USF is assigned in each downlink block to indicate if the uplink
// block in the same frame position is available for uplink data,
// or reserved for some other purpose.
// There are only 7 USFs available, so we have to share.
// TODO: MOVE TO UPLINK RESERVATION:
//class L1USFTable
//{
// TBF *mUSFTable[8];
// public:
// void setUSF(RLCBSN_t bsn, TBF* tbf) { mUSFTable[bsn % mUSFTableSize] = tbf; }
// TBF *getTBFByUSF(RLCBSN_t bsn) { return mUSFTable[bsn % mUSFTableSize]; }
//
// L1USFTable() { for (int i = mUSFTableSize-1; i>= 0; i--) { mUSFTable[i] = 0; } }
//};
}; // namespace GPRS
#endif