mirror of
https://github.com/RangeNetworks/openbts.git
synced 2025-10-22 23:32:00 +00:00
605 lines
28 KiB
C++
605 lines
28 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.
|
|
*/
|
|
#ifndef MSINFO_H
|
|
#define MSINFO_H
|
|
|
|
#include <Interthread.h>
|
|
//#include <list>
|
|
|
|
#include "GPRSInternal.h"
|
|
#include "GPRSRLC.h"
|
|
//#include "RLCHdr.h"
|
|
#include "RList.h"
|
|
//#include "BSSG.h"
|
|
#include "Utils.h"
|
|
#include "SgsnExport.h"
|
|
|
|
#define CASENAME(x) case x: return #x;
|
|
|
|
namespace GSM { struct RadData; }
|
|
namespace GPRS {
|
|
|
|
typedef RList<PDCHL1Downlink*> PDCHL1DownlinkList_t;
|
|
typedef RList<PDCHL1Uplink*> PDCHL1UplinkList_t;
|
|
typedef RList<TBF*> TBFList_t;
|
|
|
|
// A way to describe a collection of tbf states.
|
|
// See TBF.h isActive() and isTransmitting().
|
|
enum TbfMacroState {
|
|
TbfMAny, // any state
|
|
TbfMActive, // any active state
|
|
TbfMTransmitting // any transmitting state.
|
|
};
|
|
|
|
// This should be in TBF.h but classes TBF and MSInfo are circularly referential.
|
|
enum TbfCancelMode {
|
|
TbfRetryInapplicable, // Tbf retry is inapplicable to an uplink tbf.
|
|
TbfNoRetry, // Kill the tbf forever
|
|
TbfRetryAfterRelease, // Retry tbf after sending a TbfRelease message.
|
|
// If the tbf release message fails, fall back to RetryAfterTimeout.
|
|
TbfRetryAfterWait // Retry tbf after timeout.
|
|
};
|
|
|
|
enum MultislotSymmetry {
|
|
MultislotSymmetric, // Use only symmetric multislot assignment, eg 2-down/2-up.
|
|
MultislotFull // Use full multislot assignment, which may be assymetric.
|
|
};
|
|
|
|
// GSM03.64 sec 6.2
|
|
// Note: The RROperatingMode is a state of the MS and known only at the layer2 MAC level.
|
|
// The GSM spec says essentially that the RROperatingMode is simply whether
|
|
// the MS thinks there is an active TBF running, which is not particularly useful to us.
|
|
// What we need to track is whether the MS is listening to
|
|
// CCCH (PacketIdle mode) or PDCH (PacketTransfer mode.) Generally when all TBFs are
|
|
// finished the MS goes back to PacketIdle mode, but after a downlink transaction
|
|
// we keep it camped on PDCH for a time determined by the T3192 timer, whose value
|
|
// we broadcast in the System Information messages.
|
|
//
|
|
// Note: For T3192 timeout determination, dont forget to add the time delay
|
|
// of the outgoing message queue, but that should be short.
|
|
// But messages enqueued on CCCH may have long delays.
|
|
//
|
|
// This mode has almost nothing to do with Mobility Management Status, which is up in Layer3.
|
|
// When the docs talk about "GPRS attached" they usually mean Mobility Mangement State, not this.
|
|
// An MS in GMM state "Ready" (meaning the MS and SGSN have negotiated an SGSN supplied
|
|
// TLLI for the MS instead of using the random TLLI the MS uses for its first uplink message)
|
|
// will usually be in PacketIdle mode unless there is an ongoing TBF transaction.
|
|
// For a non dual-transfer-mode MS, the MS must relinquish (or suspend) its GMM "Ready" state
|
|
// to make a voice call, in which case it would no longer be in PacketIdle mode,
|
|
// but that has almost no meaning at the MAC level. If the voice call ends and the MS wants
|
|
// to use GPRS again, it will send us another RACH and we dont need to know that
|
|
// a voice call was made in the meanwhile.
|
|
class RROperatingMode {
|
|
public:
|
|
enum type {
|
|
PacketIdle,
|
|
PacketTransfer,
|
|
//DualTransfer,
|
|
// This mode was unnecessary:
|
|
//Camped // This is our own mode, not an official RROperatingMode.
|
|
// It marks the time MS is camped on Packet Channel between PacketTransfer
|
|
// and PacketIdle, when the T3192 timer is running.
|
|
// If a new transfer does not commence before T3192 expires,
|
|
// MS is in PacketIdle mode.
|
|
};
|
|
static const char *name(RROperatingMode::type mode);
|
|
};
|
|
|
|
std::ostream& operator<<(std::ostream& os, const RROperatingMode::type &mode);
|
|
|
|
// When we lose contact with the MS or something bad happens, we stop talking to it.
|
|
// This says why. Reserve the first 100 numbers so the MsgTransactionType can be used
|
|
// as a stop cause when the corresponding MsgTransactionType counter expires.
|
|
class MSStopCause {
|
|
public:
|
|
enum type {
|
|
AssignCounter = 1,
|
|
// These were the values in release 3.0:
|
|
//ShutDown = 2,
|
|
//Stuck = 3,
|
|
//Reassign = 4, // Reassignment failed.
|
|
ShutDown = 102,
|
|
Stuck = 103,
|
|
//Reassign = 104, // Reassignment failed.
|
|
Rach = 105, // Running TBF killed by a RACH.
|
|
ReleaseCounter = 106,
|
|
ReassignCounter = 107,
|
|
NonResponsive = 108, // MS does not talk to us any more.
|
|
Goof = 109,
|
|
N3101 = 3101,
|
|
N3103 = 3103,
|
|
N3105 = 3105,
|
|
T3191 = 3191,
|
|
T3168 = 3168,
|
|
CauseUnknown = 9999 // Used for unrecoverable internal inconsistency.
|
|
};
|
|
//int mValue;
|
|
//MSStopCause(/*enum MsgTransactionType*/ int wMsgTransType) : mValue(wMsgTransType) {}
|
|
};
|
|
|
|
|
|
struct SignalQuality {
|
|
// TODO: Get the Channel Quality Report from packet downlink ack/nack GSM04.60 11.2.6
|
|
Statistic<float> msTimingError;
|
|
Statistic<int> msRSSI; // Dont bother saving RSSI as a float
|
|
Statistic<int> msChannelCoding;
|
|
Statistic<int> msCValue;
|
|
Statistic<int> msILevel;
|
|
Statistic<int> msRXQual;
|
|
Statistic<int> msSigVar;
|
|
void setRadData(GSM::RadData &rd);
|
|
void setRadData(float wRSSI,float wTimingError);
|
|
void dumpSignalQuality(std::ostream &os) const;
|
|
};
|
|
|
|
struct StatTotalHits {
|
|
// Use ints instead of unsigned in case some statistic is buggy and runs backwards.
|
|
Int_z mTotal;
|
|
Int_z mGood;
|
|
void clear() { mGood = mTotal = 0; }
|
|
};
|
|
|
|
// keep the history of some success rate over the last few seconds for reporting purposes.
|
|
class StatHits {
|
|
// Keep totals for each of the last NumHist 48-block-multiframes, which is approx one second.
|
|
public: static const int cNumHist = 20;
|
|
private:
|
|
StatTotalHits mTotal; // Totals for all time.
|
|
StatTotalHits mHistory[cNumHist]; // Recent history.
|
|
UInt_z mWhen[cNumHist]; // When the historical data in this history bucket was collected.
|
|
// Return index in arrays above, which is the current 48-block-multiframe
|
|
unsigned histind();
|
|
|
|
public:
|
|
void addTotal() {
|
|
mTotal.mTotal++;
|
|
mHistory[histind()].mTotal++;
|
|
}
|
|
void addGood() { // increment only good count, not total.
|
|
mTotal.mGood++;
|
|
mHistory[histind()].mGood++;
|
|
}
|
|
void addMiss() { addTotal(); }
|
|
void addHit() { // increment good and total.
|
|
unsigned i = histind();
|
|
mTotal.mTotal++; mTotal.mGood++;
|
|
mHistory[i].mTotal++; mHistory[i].mGood++;
|
|
}
|
|
|
|
void getStats(float *pER, int *pTotal, float *pWorstER, int *pWorstTotal);
|
|
void textRecent(std::ostream &os); // Print the average for the last N seconds and worst second.
|
|
void textTotal(std::ostream&os); // Print totals.
|
|
};
|
|
|
|
// More MS statistics. Separate from MSInfo just because MSInfo is so large.
|
|
struct MSStat {
|
|
// This is a measure of the instantaneous traffic, used to pick the least busy channel.
|
|
// It is incremented every time a block is sent/received, and decayed on a regular time schedule.
|
|
UInt_z msTrafficMetric;
|
|
|
|
StatHits msCountCcchReservations;
|
|
StatHits msCountRbbpReservations;
|
|
|
|
StatHits msCountBlocks; // Counts both uplink and downlink.
|
|
|
|
UInt_z msConnectTime;
|
|
void msAddConnectTime(unsigned msecs) { msConnectTime += msecs; }
|
|
UInt_z msCountTbfs, msCountTbfFail, msCountTbfNoConnect;
|
|
UInt_z msBytesUp, msBytesDown;
|
|
|
|
|
|
//UInt_z msCountCcchReservations;
|
|
//UInt_z msCountCcchReservationReplies;
|
|
//UInt_z msCountRbbpReservations;
|
|
//UInt_z msCountRbbpReservationReplies;
|
|
//void service() {
|
|
// // There are approx 48 blocks per second.
|
|
// if (gBSNNext % 48 != 0) { return; }
|
|
// unsigned ind = (gBSNNext / 48) % numHist;
|
|
// msHistoryCcchReservations.sethits(int,msCountCcchReservations,msCountCcchReplies);
|
|
// msHistoryRbbpReservations.sethits(int,msCountRbbpReservations,msCountRbbpReplies);
|
|
// fivesecavg.countCcchReservations = msCountCcchReservations.
|
|
//}
|
|
|
|
// We use talkUp/talkDown to determine when the MS is non-responsive:
|
|
GprsTimer msTalkUpTime; // When the MS last talked to us.
|
|
GprsTimer msTalkDownTime; // When we last talked to the MS.
|
|
|
|
// pat 3-2014: Use msDownlinkAttempts to detect that a high-side initiated downlink failed.
|
|
UInt_z msDownlinkAttempts;
|
|
|
|
// Called at every uplink/downlink communication from MS.
|
|
// These are not strictly statistics because they are also used to kill a non-responsive MS.
|
|
// These timers differ from the persistent mode timers in that those
|
|
// count only data, and these count anything.
|
|
// SVGDBG this means we are got something from the phone
|
|
void talkedUp(bool doubleCount=false) { msDownlinkAttempts=0; msTalkUpTime.setNow(); if (!doubleCount) {msTrafficMetric++;} }
|
|
void talkedDown() { msDownlinkAttempts=0; msTalkDownTime.setNow(); msTrafficMetric++; }
|
|
|
|
// Dump all except traffic metric.
|
|
void msStatDump(const char *indent,std::ostream &os);
|
|
};
|
|
|
|
// MS Info a.k.a. Radio Context. There is one of these for each TLLI (not per-MS, per-TLLI)
|
|
// GSM04.08 4.7.1.4 talks about GPRS attach, P-TMSI, and TLLI.
|
|
// An MS, for our purposes in L2, is defined by its TLLI. No TLLI, no MSInfo struct.
|
|
// The TLLI identifies the MS in all transactions except the initial RACH.
|
|
// The RACH creates an anonymous packet uplink assignment for the MS, still identified
|
|
// only by the RACH time, to transmit one block, which will be a Packet Resource Request
|
|
// containing the all important TLLI. However, knowing the TLLI does not identify
|
|
// a unique MS; the MS may (and usually does) use several of them.
|
|
// Note that the MS can pick a "random" TLLI for itself when it does its first
|
|
// GPRS-attach, but the SGSN issues a new "local" TLLI based on P-TMSI on AttachAccept.
|
|
// The TLLI is specific to the sgsn, so if you switched sgsns, you would have a new TLLI,
|
|
// however, the MS remembers its old TLLIs and will try calling in with them,
|
|
// converted to foreign TLLIs.
|
|
//
|
|
// The spec is entirely botched up about whether the critical information needed
|
|
// to communicate with the MS is in L3 (the SGSN) or L2 (the BTS.)
|
|
// The SGSN stores the MS capabilities (RACap and DRX mode), which is ok but unnecessary,
|
|
// since the MS retransmits them in every RAUpdate.
|
|
// (The SGSN copies would be forwarded to the new cell during a cell change though.)
|
|
// The problem is that we need to remember the radio parameters for the MS
|
|
// (RSSI and TimingError), and a whole bunch of ad-hoc timers running in the MS
|
|
// here in L2, where we identify MS using TLLI,
|
|
// but the mapping of TLLI to MS (as known by IMSI) is in L3. Whoops!
|
|
// Also note that during a TLLI reassignment procedure using BSSG, the SGSN commands
|
|
// the BTS to switch TLLIs *after* it has received the Attach Complete Message,
|
|
// which has already arrived [possibly but not always] using the new TLLI,
|
|
// so here at L2 the MSInfo for the new TLLI already exists.
|
|
// We are supposed to combine the two RadioContexts (MSInfos) when we get
|
|
// the TLLI reassignment, and then recognize either TLLI.
|
|
// The RSSI and TimingError are updated separately per-TLLI (ie, per-MSInfo struct)
|
|
// because the MS initiates each conversation.
|
|
//
|
|
// The entire communication system between the MS and the SGSN can best
|
|
// be described in terms of two different state-universes, corresponding
|
|
// to the GPRS-Registration state: Registered (GPRS-Attached) or not.
|
|
// In the pre-gprs-attach state the MS may call in with several TLLIs,
|
|
// and we dont know how to correlate them to an actual MS.
|
|
// In this case it is quite easy to lose communication with the MS, because
|
|
// when an incoming RACH+PacketResourceRequest is answered, a new PACCH
|
|
// may be assigned at random, and it may conflict with a previous assignment
|
|
// that is on-going or in-flight on AGCH.
|
|
// Also in this state I think there is simply no way to know for sure the state of the
|
|
// per-MS timers, which are needed to know how to send the Immediate Assignment.
|
|
// TODO: Maybe we should use a single PACCH timeslot for all unregistered TLLIs,
|
|
// which implies communicationg the registration state from the SGSN to L2.
|
|
|
|
// In the Registered-state we know that the new and old TLLI are the same physical MS,
|
|
// and communication is more secure. We can assign a new PACCH for the MS.
|
|
// By Registered we mean that both the SGSN and the MS agree on the
|
|
// Registration state and the P-TMSI, which agreement is handshaked in both
|
|
// directions (3 messages) and consumated by the AttachComplete message sent by MS to SGSN.
|
|
|
|
#define TLLI_LOCAL_BIT 0x40000000
|
|
#define TLLI_MASK_LOCAL(tlli) ((tlli) & ~ TLLI_LOCAL_BIT)
|
|
static __inline__ uint32_t tlliEq(uint32_t tlli1, uint32_t tlli2) {
|
|
// Temporarily provide a way to disable this in case it does not work:
|
|
if (gConfig.getBool("GPRS.LocalTLLI.Enable")) {
|
|
return TLLI_MASK_LOCAL(tlli1) == TLLI_MASK_LOCAL(tlli2);
|
|
} else {
|
|
return tlli1 == tlli2;
|
|
}
|
|
}
|
|
|
|
|
|
// vvv OLD COMMENT:
|
|
// Formerly I changed the TLLI in the MSInfo struct to an oldTLLI on the command of the SGSN,
|
|
// but that is incorrect - if the MS later sends another RACH using the oldTLLI,
|
|
// we need to respond with that oldTLLI, not the newTLLI, although possibly we
|
|
// should just ignore it in that case.
|
|
// The RACH creates an anonymous packet uplink assignment for the MS, still identified
|
|
// only by the RACH time, to transmit one block, which will be a Packet Resource Request
|
|
// with a TLLI. If that TLLI maps to an old TLLI, it is because we have already
|
|
// succeeded with the Attach Complete, and therefore it is safe to use the new TLLI.
|
|
// Therefore, an MSInfo structure has one and only one TLLI, and the sole purpose of
|
|
// TLLI is as a layer-2 transport identifier for the MS.
|
|
// ^^^ OLD COMMENT
|
|
|
|
// The MSInfo struct needs to hang around as long as the MS is in packet-transfer mode,
|
|
// which means as long as it has TBFs, or the MS is in the T3192 period when it is camped
|
|
// on the PACCH channel instead of the CCCH channel.
|
|
// If we lose a connection with an MS, we keep the dead TBF around too until we
|
|
// are sure it is no longer in use (config option, default 5 seconds),
|
|
// so we dont need the MSInfo to survive after that. Doesn't hurt to keep it around, either.
|
|
// An unused MSInfo eventually decays and is destroyed; this delay must be longer
|
|
// than the expected use of the MSInfo by the SGSN - at least several seconds.
|
|
// The SGSN is what remembers GPRS-attached MS, and will send us both a TLLI and the
|
|
// MS capabilities (ie, multislot) in any transactions so that we can recreate the MSInfo at need.
|
|
class MSInfo : public SGSN::MSUEAdapter, public SignalQuality, public MSStat
|
|
{
|
|
public:
|
|
unsigned msDebugId;
|
|
|
|
// Use of TLLI is in GSM04.08 4.7.1.5: P-TMSI handling.
|
|
// From GSM03.03 sec 2.6: Structure of TLLI:
|
|
// local TLLI is built by MS that has a valid P-TMSI:
|
|
// top 2 bits 11, lower 30 bits are low 30 bits of P-TMSI.
|
|
// foreign TLLI is built by an MS that has a valid P-TMSI from elswhere:
|
|
// top 2 bits 10, lower 30 bits from P-TMSI;
|
|
// random TLLI is built by an MS with no P-TMSI:
|
|
// top top 5 bits 01111, lower 27 bits random.
|
|
// auxiliary TLLI is built by SGSN:
|
|
// top 5 bits 01110, lower 27 bits at SGSN discretion.
|
|
// GSM03.03 sec 2.4: Structure of TMSI
|
|
// The 32-bit TMSI has only local significance (within VLR or SGSN) and
|
|
// is created at manufacturer discretion, however, for SGSN top
|
|
// 2 bits must be 11, and it may not be all 1s, which value is reserved
|
|
// to mean invalid. We also reserve value 0 to mean unset.
|
|
// TMSI is stored in hex notation in 4 octets, and always ciphered.
|
|
// GPRS-L2 doesn't care about any of the above, the SGSN knows those things.
|
|
// In GPRS-L2 we just use whatever TLLI we are told.
|
|
|
|
// An MSInfo structure is created as a result of an MS communication with a TLLI.
|
|
// No TLLI, no MSInfo structure. These things can time out and die whenever
|
|
// they want - their lifetime only needs to be as long as the RSSI and TimingError is valid.
|
|
// They will be recreated if the MS RAChes us again.
|
|
// In the spec they can also be created by a page from the SGSN, but not implemented here.
|
|
// The MSInfo struct corresponds to an SgsnInfo in the SGSN, but because
|
|
// either can be deleted any time we dont keep pointers between them, we always
|
|
// look them up by TLLI for communication to/from the SGSN.
|
|
//
|
|
// This is as per the spec:
|
|
// The msTlli is the TLLI we (the L2 layer) always use to communicate with the MS.
|
|
// It is initialzied from the TLLI the MS used to communicate with us.
|
|
// It is only changed if we get a 'change tlli' command from the SGSN,
|
|
// which happens only after a successfully completed attach procedure,
|
|
// (which is a fully acknowledged procedure with 3 way handshake)
|
|
// at which time msTlli becomes msOldTlli, and we must subsequently recognize
|
|
// either msTlli (the new sgsn-assigned one) or msOldTlli (the original one)
|
|
// for uplink communication, but use only msTlli for downlink communication.
|
|
//
|
|
// This is not as per the spec:
|
|
// After the attach is successful, we use only the assigned TLLI,
|
|
// only one MSInfo structure, and everything is copascetic.
|
|
// However, prior to a successful attach, I have seen the MS just use
|
|
// several different TLLIs in uplink messages one right after the other,
|
|
// maybe trying to find one that already works?
|
|
// So many of these MSInfo refer to the same phone.
|
|
// The spec deals with this by letting these things time out and die,
|
|
// however, this results in conflicting in-flight assignments on AGCH
|
|
// for different TLLIs that, in fact, refer to the same phone.
|
|
// The spec does not provide any way for the L2 layer (us) to find that out.
|
|
// So I introduced the AltTlli, which points to another MSInfo struct that
|
|
// refers the same phone. It MUST NOT be used for communication with the MS,
|
|
// however, it can be used to avoid launching conflicting assignments.
|
|
// The Sgsn sends us AltTlli as the AliasTlli in the DownlinkQPdu.
|
|
UInt32_z msTlli; // Identifies the MS, and is the TLLI used for downlink communication.
|
|
UInt32_z msOldTlli; // Also identifies the MS, and must be recognized in uplink communication.
|
|
UInt32_z msAltTlli; // Used to 'point' to another MSInfo struct that refers to the same MS,
|
|
// as reported to us by the SGSN, which knows these things.
|
|
// We save the TLLI instead of using a pointer because the other MSInfo
|
|
// could disappear at any time.
|
|
Bool_z msDeprecated; // This MS has been replaced by some other, which is another way
|
|
// of saying that the active MS's oldTlli points to this one.
|
|
|
|
|
|
// If the MS is in packet-idle state, there should be no TBFs.
|
|
TBFList_t msTBFs; // TBFs for this MS, both uplink and downlink.
|
|
#define RN_MS_FOR_ALL_TBF(ms,tbf) for (RListIterator<TBF*> itr(ms->msTBFs); itr.next(tbf); )
|
|
Int_z msUSFs[8]; // USF in each timeslot.
|
|
// These are used by the RLCEngine to know when the MS has been granted a USF, ie, a chance to respond.
|
|
Int_z msNumDataUSFGrants; // Total number of USF grants; reset when last TBF detached.
|
|
Int_z msAckNackUSFGrant; // The msNumDataUSFGrants value of the last acknack.
|
|
|
|
// Note: The uplink/downlink channels must all be in the same ARFCN that the MS is
|
|
// camped on, and for multislot, follow strict timeslot adjacency rules.
|
|
// The spec says that it is possible for different simultaneous TBFs for the same MS
|
|
// to use different channel assignments, for exmaple, if the MS is sending a low-data-rate
|
|
// TBF1 on a single channel and then wants to send a high-data-rate TBF2, it will interrupt
|
|
// TBF2, may request a multislot allocation, and do TBF2 first.
|
|
// Or another example, if TBF2 comes in and the TBF1 channel is on is congested,
|
|
// the MAC can pick a different channel for TBF2.
|
|
// However, we are not going to support that. The channels will be assigned to the MS
|
|
// permanently, and all TBFs will use the same ones, which is why these lists are
|
|
// here instead of in the TBF, where they really belong.
|
|
// We may, however, someday change the channel assignments dynamically based on the
|
|
// relative utilization of up and down links, for example, change from 4 down 1 up
|
|
// to 4 up 1 down, etc., but if we do that we will have to wait until there are
|
|
// no active TBFs, or reconfigure the existing TBFs. Having all the channels
|
|
// here in the MSInfo instead of scattered in different TBFs will make
|
|
// such reconfiguration easier.
|
|
PDCHL1UplinkList_t msPCHUps; // uplink channels assigned to the MS; usually just one.
|
|
PDCHL1DownlinkList_t msPCHDowns; // downlink channels assigned to the MS; usually just one.
|
|
bool msCanUseUplinkTn(unsigned tn);
|
|
bool msCanUseDownlinkTn(unsigned tn);
|
|
|
|
// This is the channel the MS is listening to for messages.
|
|
// It is set before the msPCH assignments above.
|
|
// How did the MS come to be listening to this channel, you wonder?
|
|
// When a RACH comes in to the BTS, we do not know what MS it belongs to, so we pick
|
|
// the least busy GPRS channel and tell the MS to send its request on that channel.
|
|
// From then on the MS listens to that channel until we tell it differently
|
|
// in a channel assignment, which should be the next thing we send to it.
|
|
|
|
// TODO: Is the below correct? Doesnt the MS always need to monitor PAACH which must
|
|
// be one of its assigned channels?
|
|
// It is possible for the msPCH assignments to not include the msPacch in several cases:
|
|
// 1. If we deliberately give the MS a different channel assignment
|
|
// for an uplink/downlink transfer, maybe because GPRS is underutilized and
|
|
// we decided to close the channel. (Channel closing not implemented in first draft.)
|
|
// 2. Maybe we decide on a different set of channels to satisfy this particular
|
|
// MSs multislot requirements.
|
|
// 3. If a previously attached MS starts a new RACH request, which will assign
|
|
// a new PCH at random (because we dont know what MS it is yet) and for some
|
|
// reason we havent cleared the msPCH channels, maybe because our timeouts are out of phase.
|
|
// In this weird case a BSSG downlink command might try to talk to the MS on
|
|
// the old channels, which might even possibly work if the new assignment and the
|
|
// old happen to be the same. The special cases are complicated.
|
|
// Note that if we used one-phase uplink access, this case 2 would extend in time out
|
|
// into the uplink transfer, but we wont do that.
|
|
PDCHL1FEC *msPacch;
|
|
|
|
//RROperatingMode::type msMode; // Our belief about the state of MS: packet-idle, packet-transfer.
|
|
|
|
|
|
UInt_z msIdleCounter; // Counts how long MS is without TBFs; eventually we delete it.
|
|
UInt_z msStalled; // If MS is blocked, this is why, for error reporting.
|
|
|
|
//Bool_z msUplinkRequest; // Request from phone to establish uplink was delayed due to existing uplink tbf.
|
|
//RLCMsgChannelRequestDescriptionIE msUplinkRequestCRD; // The CRD for delayed request above.
|
|
|
|
|
|
// GSM04.18 11.1.2:
|
|
// T3141 - Started at Immediate Assignment, stopped when MS starts TBF.
|
|
// We dont need it because we poll instead.
|
|
|
|
// Note: Using Z100Timer is overkill for our single-threaded application;
|
|
// could just use RLCBlockTime counters instead.
|
|
// Counters and Timers defined in GSM04.60 sec 13.
|
|
// We dont calculate N3105 and N3101 exactly the way the spec says,
|
|
// but doesnt matter if they are off by 1 or 2.
|
|
// NOTE: The blackberry sometimes waits 3 block periods before it starts
|
|
// answering USFs, so N3101 better be bigger than that.
|
|
UInt_z msN3101; // Number of unacknowledged USF grants (off by one.)
|
|
|
|
// GSM04.60 sec 13:
|
|
// Note: The MS may take advantage of this time period by keeping the TBF open
|
|
// after a PDU finishes, and not sending anything for a long time, then
|
|
// sending sending additional PDUs in the same TBF later, but before the timer expires.
|
|
GSM::Z100Timer msT3191; // Waiting for acknowledgement of final TBF data block.
|
|
|
|
// GSM04.60 sec 13:
|
|
GSM::Z100Timer msT3193; // After downlink TBF finished, MS camps on PDCH this long.
|
|
// MS runs same timer but called T3192.
|
|
|
|
// GSM04.60 sec 13:
|
|
GSM::Z100Timer msT3168; // MS camped on PDCH waiting for uplink assignment.
|
|
// This timer is defined to be in the MS, not the BTS,
|
|
// and we do not really need to track it as long as we
|
|
// are sure we send the downlink assignment message
|
|
// before this timer expires. The timer value is in
|
|
// the sql and broadcast in the beacon.
|
|
// However, I am using the timer as a way of tracking
|
|
// whether the assignment is for a RACH, rather
|
|
// than setting some other variable.
|
|
|
|
// GSM04.60 sec 13:
|
|
//GSM::Z100Timer msT3169; // Final timeout for dead tbf.
|
|
//GSM::Z100Timer msT3195; // Final timeout for dead tbf.
|
|
//GSM::Z100Timer msTxxxx; // Combined T3169, T3191, T3195 - timeout for
|
|
// resource release after abnormal condition, during which time USF and TFI may not be reused.
|
|
|
|
// When this MS was last granted a USF.
|
|
// We use this when multiple MS are in contention for the uplink to make it fair.
|
|
RLCBSN_t msLastUsfGrant;
|
|
|
|
// Called when a USF is granted for this MS.
|
|
// If penalize, if the MS does not answer we kill off the tbf.
|
|
void msCountUSFGrant(bool penalize);
|
|
|
|
// Incoming downlink data queue.
|
|
// This queue is not between separate threads for BSSG,
|
|
// and it is no longer for the internal sgsn either.
|
|
//InterthreadQueue<BSSG::BSSGMsgDLUnitData> msDownlinkQueue;
|
|
InterthreadQueue2<SGSN::GprsSgsnDownlinkPdu,SingleLinkList<> > msDownlinkQueue;
|
|
Statistic<unsigned> msDownlinkQStat;
|
|
Statistic<double> msDownlinkQDelay;
|
|
Timeval msDownlinkQOldest; // The timeval from the last guy in the queue.
|
|
|
|
// Can this TBF use the specified uplink?
|
|
bool canUseUplink(PDCHL1Uplink*up) {
|
|
return msPCHUps.find(up);
|
|
}
|
|
// Can this TBF use the specified downlink?
|
|
bool canUseDownlink(PDCHL1Downlink*down) {
|
|
return msPCHDowns.find(down);
|
|
}
|
|
// Return the downlink channels as a bitmask for PacketDownlinkAssignment msg.
|
|
unsigned char msGetDownlinkTimeslots(MultislotSymmetry sym);
|
|
|
|
//PDCHL1Downlink *msPrimaryDownlink() { return msPCHDowns.front(); }
|
|
bool msAssignChannels(); // Get channel(s) for this MS.
|
|
private:
|
|
bool msAddCh(unsigned chmask, const char *tnlist);
|
|
bool msTrySlots(unsigned chmask,int down,int up);
|
|
bool msAssignChannels2(int maxdown, int maxup, int sum);
|
|
public:
|
|
void msDeassignChannels(); // Release all channels for this MS.
|
|
void msReassignChannels(); // Not implemented specially yet.
|
|
|
|
//int msLastUplinkMsgBSN; // When did we last hear from this MS?
|
|
|
|
MSInfo(uint32_t tlli);
|
|
// Use msDelete instead of calling ~MSinfo() directly.
|
|
void msDelete(bool forever=0); // If forever, do not move to expired list, just kill it.
|
|
|
|
void msAddTBF(TBF *tbf) {
|
|
devassert(! msTBFs.find(tbf));
|
|
msTBFs.push_back(tbf);
|
|
}
|
|
void msForgetTBF(TBF *tbf) {
|
|
devassert(msTBFs.find(tbf));
|
|
msTBFs.remove(tbf);
|
|
}
|
|
|
|
|
|
// Called when a TBF goes dead. If it was the last active uplink TBF, surrender our USFs.
|
|
void msCleanUSFs();
|
|
void msFailUSFs();
|
|
|
|
unsigned msGetDownlinkQueuedBytes();
|
|
//TBF * msGetDownlinkActiveTBF();
|
|
private:
|
|
int msCountTBF1(RLCDirType dir, enum TbfMacroState tbfstate, TBF**ptbf=0) const;
|
|
int msCountTBF2(RLCDirType dir, enum TbfMacroState tbfstate, TBF**ptbf=0);
|
|
public:
|
|
int msCountActiveTBF(RLCDirType dir, TBF**ptbf=0);
|
|
int msCountTransmittingTBF(RLCDirType dir, TBF**ptbf=0);
|
|
void msService();
|
|
void msStop(RLCDir::type dir, MSStopCause::type cause, TbfCancelMode cmode, int unsigned howlong);
|
|
MSStopCause::type msStopCause;
|
|
//void msRestart();
|
|
ChannelCodingType msGetChannelCoding(RLCDirType wdir) const;
|
|
int msGetTA() const { return GetTimingAdvance(msTimingError.getCurrent()); }
|
|
// All MS use the same power params at the moment.
|
|
int msGetAlpha() const { return GetPowerAlpha(); }
|
|
int msGetGamma() const { return GetPowerGamma(); }
|
|
void msDump(std::ostream&os, SGSN::PrintOptions options);
|
|
void msDumpCommon(std::ostream&os) const;
|
|
void msDumpChannels(std::ostream&os) const;
|
|
RROperatingMode::type getRROperatingMode();
|
|
string id() const;
|
|
void msAliasTlli(uint32_t newTlli);
|
|
void msChangeTlli(uint32_t newTlli);
|
|
|
|
//void msSetUplinkRequest(RLCMsgChannelRequestDescriptionIE &wCRD) {
|
|
// msUplinkRequest = true;
|
|
// msUplinkRequestCRD = wCRD;
|
|
//}
|
|
|
|
// These are the functions required by the MSUEAdapter:
|
|
uint32_t msGetHandle() { return msTlli; }
|
|
string msid() const { return id(); }
|
|
//void msWriteHighSide(ByteVector &dlpdu, uint32_t tlli, const char *descr) {
|
|
//msDownlinkQueue.write(new GprsSgsnDownlinkPdu(dlpdu,tlli,descr));
|
|
//}
|
|
void msDeactivateRabs(unsigned rabMask) {} // no-op in GPRS.
|
|
|
|
bool msIsSuspended(); // Is the MS in suspended mode?
|
|
bool msIsRegistered(); // Is the MS GPRS registered?
|
|
bool isExtendedDynamic() { return msPCHUps.size() > msPCHDowns.size(); }
|
|
bool msCanUseExtendedUplink();
|
|
};
|
|
extern unsigned gMSDebugId;
|
|
|
|
std::ostream& operator<<(std::ostream& os, const MSInfo*ms);
|
|
|
|
MSInfo *bssgMSChangeTLLI(unsigned oldTLLI,unsigned newTLLI);
|
|
|
|
}; // namespace
|
|
#endif
|