OpenBTS-UMTS/UMTS/URRC.h

485 lines
20 KiB
C++

/*
* OpenBTS provides an open source alternative to legacy telco protocols and
* traditionally complex, proprietary hardware systems.
*
* Copyright 2014 Range Networks, Inc.
*
* This software is distributed under the terms of the GNU Affero General
* Public License version 3. See the COPYING and NOTICE files in the main
* directory for licensing information.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
*/
#ifndef URRC_H
#define URRC_H 1
#include "UMTSConfig.h"
#include "UMTSTransfer.h"
#include "URRCTrCh.h"
#include "URRCRB.h"
#include "URLC.h"
#include "../SGSNGGSN/SgsnExport.h"
#include "asn_system.h"
#include "URRCMessages.h"
#include "IntegrityProtect.h"
namespace ASN {
//#include "InitialUE-Identity.h"
#include "UE-RadioAccessCapability.h"
struct UL_DCCH_Message;
};
// To keep it simple, we just use the logical channel id to index the RB array,
// which means 0 (for SRB0) is used only on CCCH, and SRB4 is never used.
// The UE is allowed to request data transfer on any of RB 5..15.
// When we start allowing simultaneous PS and CS connections, we may have to put the CS connect on 16,17,18.
const unsigned gsMaxRB = 16; // SRB0 - SRB4 + data rb 5..15
namespace UMTS {
extern void rrcInitCommonCh(unsigned rachSF, unsigned fachSF);
class AsnUeId;
class DCCHLogicalChannel;
// The possible RRC specified UE states we support.
// This state is used by the MAC to decide which UEs want to send messages on FACH.
// There can be multiple simultaneous requests to establish DCH state... todo...
enum UEState {
stUnused,
stIdleMode,
//stCELL_FACHPending,
stCELL_FACH,
//stCELL_DCHPending,
stCELL_DCH,
stCELL_PCH, // we dont use thse yet
stURA_PCH
};
const char *UEState2Name(UEState state);
struct UEDefs
{
enum TransType {
ttComplete, // Transaction never used, or Transaction complete.
ttRrcConnectionSetup,
ttRrcConnectionRelease,
ttRadioBearerSetup,
ttRadioBearerRelease,
ttCellUpdateConfirm,
ttSecurityModeCommand
};
const char *TransType2Name(TransType ttype);
// 4 because the transactionId is only 2 bits.
static const unsigned sMaxTransaction = 4;
};
// Master Channel Config.
// Its just an RB and TrCh config combined.
// The RBMappingInfo can specify two mappings for each RB:
// one when mapped to DCH and one when mapped to RACH/FACH;
// therefore we could have two TrChConfig here.
class RrcMasterChConfig : public RrcDefs
{
static const unsigned sMaxSRB = 3; // We dont use SRB4.
//static const unsigned sMaxDRB = 1; // Will need to be 3 for AMR voice channels.
static const unsigned sMaxTrCh = 2; // Minimum 2 for DCCH+DTCH; Will need to be 4 for AMR voice.
// This is indexed by the RB number, which means that position 0 is not used
// except for SRB0 in the CCCH MasterChConfig.
// old :Warning: the RBMappingInfo contains indicies into the TFS buried in the TrCh info
// (but we dont use RBMappingInfo any more)
RBInfo mRB[gsMaxRB]; // Has RLC info, RBMappingInfo
public:
TrChConfig mTrCh; // This has lists of TrCh, their TFS, and the master TFCS.
// One greater than the index of the maximum mRB defined; for cch it is only 1.
// This is used just to limit the loops that look through them.
unsigned mNumRB;
RrcMasterChConfig():
mNumRB(0)
{}
// The getRB works for RB or SRB.
RBInfo *getRB(unsigned rbid) { assert(rbid<mNumRB); return &mRB[rbid]; }
RBInfo *setSRB(unsigned rbid);
RBInfo *setRB(unsigned rbid,CNDomainId domain);
// These use 0 based numbering for TrCh:
TrChInfo *getUlTrChInfo(TrChId tcid) { return mTrCh.ul()->getTrChInfo(tcid); }
TrChInfo *getDlTrChInfo(TrChId tcid) { return mTrCh.dl()->getTrChInfo(tcid); }
unsigned getUlNumTrCh() { return mTrCh.ul()->mNumTrCh; }
unsigned getDlNumTrCh() { return mTrCh.dl()->mNumTrCh; }
// TFS are per-TrCh. TFCS are common for all TrCh.
RrcTfs *getUlTfs(TrChId tcid = 0) { return mTrCh.ul()->getTfs(tcid); }
RrcTfcs *getUlTfcs() { return mTrCh.ul()->getTfcs(); }
RrcTfs *getDlTfs(TrChId tcid = 0) { return mTrCh.dl()->getTfs(tcid); }
RrcTfcs *getDlTfcs() { return mTrCh.dl()->getTfcs(); }
// Create a DCH channel with the a data channel on the specified RABid.
void rrcConfigDchPS(DCHFEC *dch, int RabId, bool useTurbo);
// Config DCH for CS (circuit-switched, ie, voice).
// FIXME: Finish this. I set up the RBInfo and TrChInfo, but we still
// need to do the other stuff in rrcAllocateRabForPdp.
void rrcConfigDchCS(DCHFEC *dch);
// TODO: uplink function = none, RLC bypassed
};
extern RrcMasterChConfig *gRrcCcchConfig; // Defines TrCh, SRB0 for UE in unconnected mode.
extern RrcMasterChConfig *gRrcDcchConfig; // Defines TrCh, SRB for UE in connected mode.
extern RrcMasterChConfig *gRrcDchPSConfig;
class Rrc : public UEDefs
{
UInt32_z mRrcRNTI; // Next ue id.
Thread mRrcUplinkHandler;
//TrChConfig mRachFachTrCh;
//Tfs *getRachTfs() { rrcDoInit(); return mRachFachTrCh.ul()->getTfs(1); }
//Tfcs *getRachTfcs() { rrcDoInit(); return mRachFachTrCh.ul()->getTfcs(); }
//Tfs *getFachTfs() { rrcDoInit(); return mRachFachTrCh.dl()->getTfs(1); }
//Tfcs *getFachTfcs() { rrcDoInit(); return mRachFachTrCh.dl()->getTfcs(); }
Bool_z inited;
public:
void rrcDoInit() {
if (inited) { return; }
inited = true;
mRrcRNTI = 0xffff&time(NULL);
}
// List of UE we have heard from.
Mutex mUEListLock;
typedef std::list<UEInfo*> UEList_t;
UEList_t mUEList;
// If ueidtype is 0, look for URNTI, else CRNTI
UEInfo *findUe(bool ueidtypeCRNTI,unsigned ueid);
UEInfo *findUeByUrnti(uint32_t urnti) {return findUe(false,urnti);}
UEInfo *findUeByAsnId(AsnUeId *ueid);
void purgeUEs();
void addUE(UEInfo *ue);
// Dont init anything in the constructor to avoid an initialization race with UMTSConfig.
Rrc() { mUEListLock.unlock();}
// The crnti is 16 bits for UE id and the urnti is 12 bits for SRNC id and 20 bits for UE id.
// We will use the same UE id for both.
void newRNTI(uint32_t *urnti, uint16_t *crnti) {
unsigned ueid = ++mRrcRNTI; // skip 0. We use 0 sometimes to mean undefined UNRTI.
if (mRrcRNTI >= 65536) { mRrcRNTI = 0; }
*crnti = ueid;
unsigned srnc = gNodeB.getSrncId();
//printf("SRNC=%d ueid=%d\n",srnc,ueid); // Something is wrong.
*urnti = (srnc << 20) | ueid;
}
//URlcBase *RABFindURlc(RadioBearer *id); // Return a pointer to the URlc object associated with a RB.
};
extern Rrc gRrc;
// When we send an RB setup message of some kind, we save the transaction here
// so that we know what to do when we receive the message reply.
// This is necessary for RadioBearerSetup because the UE may request multiple
// setups simultaneously, eg, might ask for two IP addresses at the same time,
// and we need to keep the responses straight.
struct UeTransaction : UEDefs
{
TransType mTransactionType; // Event type.
UEState mNewState; // If not idle, new state when transaction complete.
unsigned mRabMask; // The mask of RBs affected by this transaction.
uint8_t mTransactionId;
Timeval mTransTime; // When the event occurred.
UeTransaction(): mTransactionType(ttComplete),mNewState(stUnused) {}
// The rabMask is just extra info saved with the transaction to be used
// by the transaction response; it is currently a mask of the RABs affected
// by a radiobearer modification, but conceptually it could be arbitrary info.
UeTransaction(UEInfo *uep,TransType type, unsigned rabMask, unsigned transactionId,
UEState nextState = stUnused);
void transClose() { mTransactionType = ttComplete; }
long elapsed(); // How long since the transaction, in ms?
const char *name() { return TransType2Name(mTransactionType); }
std::string str();
};
// TODO: After sending the RCC Connection Setup we dont know if the
// UE is in connected or unconnected mode until it returns the RRC Connection Complete message.
// In the meantime, we need to be prepared to receive messages on either CCCH or DCCH.
// We could punt and say that this UEInfo is on DCCH, and if we get another message,
// we will create a new UEInfo with a new RNTI.
//
// When switching from CELL_FACH to CELL_DCH state, RRC sends the RadioBearerSetup message
// in acknowledged mode, so RRC can know whether or not it was delivered.
// However, we still dont know what state the UE will be in, because it may not like the message,
// and stay in CELL_FACH state. We dont know for sure until it sends the RadioBearerSetupComplete
// message, 8.2.2.4 and I quote:
// "If the new state is CELL_DCH or CELL_FACH, the response message shall be transmitted
// "using the new configuration after the state transition, and the UE shall:"
// if issued a new U-RNTI, or message included IE "Downlink Counter Synchronisation" or "SR-VCC",
// (and then later on) if the PDCP_SN_INFO variable is non-null (where this var is also
// related to counter re-synchronization...) then there is a long list of special cases, and finally we find:
// "The UE shall: resume data transmission on any suspended radio bearer and signalling
// radio bearer mapped on RLC-AM or RLC-UM;"
// So specifically for SRBs we need to listen to both the old and new config simultaneously.
// Now 8.6.4.9: RLC-Info: This specifies when to re-establish the RLC. To paraphrase,
// if you change the size you re-establish it, otherwise you keep it.
// Therefore, for SRBs, the RRC MUST support two RLCs simultaneously for rlc-size changes,
// but if the size doesnt change, you need to copy the old RLC entity, including the state,
// a feature that RLC is supposed to also support for handover.
// In general, you cant use the same RLC entity even for the same-size case because you are
// permitted to change the RLC config in lots of other ways, even though they keep the internal state,
// however, for our purposes we could guarantee the config was the same and just point to the same RLC.
// In general, copying the RLCs would also require copying the table of PDUs in the RLC-AM entity,
// except I think we can guarantee that the RLC is completely empty at the time the UE changes state.
// Since the MAC also wants to know which RLC to route messages to, the easiest way for us
// is to have two sets of RLCs for state CELL_FACH and state CELL_DCH, so the MAC-c knows
// to route only to the CELL_FACH set. And in fact, we dont care what state the UE is in -
// if it ever sends us anything on FACH, we want to hear about it.
// If in the future we need to change the SRBs, then we will need a THIRD set so that we
// can have two for CELL_DCH state - the old and the new.
// Moreover, to guarantee that MAC can tell the new SRB from the old SRB, you probably want to
// specify a new TrCh when you reconfigure the SRBs.
// Incredible. They couldn't have made this any more complex.
#define RN_UE_FOR_ALL_RLC_DOWN(uep,rbid,rlcp) \
URlcTrans *rlcp; \
for (RbId rbid=0; rbid <=uep->mUeMaxRlc; rbid++) \
if ((rlcp = uep->getRlcDown(rbid)))
#define RN_UE_FOR_ALL_RLC(uep,rbid,rlc) \
URlcPair *rlc; \
for (RbId rbid = 0; rbid <= uep->mUeMaxRlc; rbid++) \
if ((rlc = uep->getRlc(rbid)))
#define RN_UE_FOR_ALL_DLTRCH(uep,tcid) \
unsigned tcid##_numTC = uep->ueGetTrChConfig()->dl()->mNumTrCh; \
for (unsigned tcid = 0; tcid < tcid##_numTC; tcid++)
// UE timers are described in 13.1 and default values are in 10.3.3.43 and 10.3.3.44
// The interesting timers and their default values are:
// T300: 1sec, start on RRC Connection Request, stop on RRC Connection Setup, retry.
// T302: 4secs, cell update retry.
// T305: 30minutes, periodic cell update in CELL_FACH, CELL_PCH or URA_PCH mode.
// T307: 30secs, dont understand start condition. drop to idle mode if
// T305 expired and no answer to periodic cell update?
// T312: 1sec, radio link no sync = failure on initial establishment of DCH
// T313: 3sec, radio link no sync = failure otherwise.
// T314: 12sec, used for CS connection, timeout to idle mode after radio link failure.
// T315: 180sec, used for PS connection, timeout to idle mode after radio link failure.
// T319: unspecified, when to start DRX mode after entering CELL_PCH or URA_PCH state.
static int sNextUeDebugId = 1; // Each UE gets a human-readable id for log messages.
class UEInfo : public SGSN::MSUEAdapter, public UEDefs
{
int mUeDebugId;
UEState mUeState;
// There are two sets of RLCs - explanation in the big comment above.
// We use the rbid directly for the rlc index, so SRB1 uses 1, etc.
// They wont all be used simultaneously, but its just easier.
// The SRB0 is never used because that rlc is in the Mac-c.
URlcPair *mRlcsCF[gsMaxRB]; // The RLCs used in CELL_FACH state.
URlcPair *mRlcsCDch[gsMaxRB]; // The RLCs used in CELL_DCH state.
public:
// TODO: Decrement mUeMaxRlc when deleting RBs.
UInt_z mUeMaxRlc; // Max index of any allocated rlc for this ue.
// I am not yet sure if we need to keep the RrcMasterChConfig for this UE around
// after we program it. Specifically, the RBs that are connected vary for each UE
// depending on which PDPs the UE requests.
// The MAC layer only needs the TrCh config part out of the RrcMasterChConfig,
// and that will be invariant at least per SF+intended use.
RrcMasterChConfig mUeDchConfig; // The config used to connect this UE in DCH mode.
RrcMasterChConfig *ueGetConfig();
TrChConfig *ueGetTrChConfig();
// We need to remember which rbs have been used.
// While we are at it, we will remember everything we told the SGSN so if
// we get a duplicate request we can return identical information.
SGSN::RabStatus mConfiguredRabs[gsMaxRB];
private:
float mTimingError;
float mRSSI;
// 25.331 13.4.27 - same TRANSACTIONS table in RRC and UE:
UeTransaction mTransactions[sMaxTransaction];
friend class UeTransaction;
UeTransaction *getTransaction(unsigned transId, TransType ttype, const char *help);
public:
UeTransaction *getTransactionRaw(unsigned transId, const char *help);
UeTransaction *getLastTransaction();
Mutex mUeLock;
Timeval mHelloTime;
Timeval mActivityTime; // When was last activity?
AsnUeId mUid; // Self-inits.
// We use neither dch nor mac pointers while running, but we keep
// the pointers so we can de-allocate everything at the end.
DCHFEC *mUeDch; // If allocated.
MacdBase *mUeMac; // If allocated a DCH, this is the Mac-d entity.
DCCHLogicalChannel *mGsmL3; // Somewhere to send L3 messages for the GSM stack.
ASN::UE_RadioAccessCapability *radioCapability; // UE capabilities, usually from a RRC Conn. Setup Complete msg.
uint32_t mURNTI; // Note: The mURNTI is 12 bits SRNC id + 20 bits UE id.
uint16_t mCRNTI; // Used by mac on the phy interface.
IntegrityProtect integrity;
// The external interface is by rbid - do not access the rlcs directly because
// they may get destroyed when the UE switches states.
// The rlc-down is always picked based on the UE state.
// In uplink we need to be able to receive messages from both states simultaneously
// during the cell state transition, so the MAC must specify the state.
URlcPair *getRlc(RbId rbid);
URlcTrans *getRlcDown(RbId rbid);
URlcRecv *getRlcUp(RbId rbid, UEState state);
friend class MacWithTfc;
friend class MaccSimple;
friend class MacdSimple;
protected:
UInt_z mNextTransactionId; // Next Transaction Id.
// The UE does not need to point downstream at all for functional purposes,
// because the MAC pulls data from the RLCs as needed using the pointer to the UE,
// but if we are in DCH state we will keep a downstream pointer past mac
// all the way to the physical channel, for reporting purposes only.
Thread* mThread;
// Stupid language.
private: void _initUEInfo() {
mUeDebugId = sNextUeDebugId++;
mUeState = stIdleMode;
mUeDch = NULL;
mUeMac = NULL;
mGsmL3 = allocateLogicalChannel();
memset(mRlcsCF,0,sizeof(mRlcsCF));
memset(mRlcsCDch,0,sizeof(mRlcsCDch));
mHelloTime.now();
mActivityTime = mHelloTime;
mURNTI = 0; // We expect these to be set immediately, but cant be too cautious.
mCRNTI = 0;
radioCapability = NULL;
}
public:
DCCHLogicalChannel *allocateLogicalChannel();
UEInfo(AsnUeId *wUid) : mUid(*wUid)
{
_initUEInfo();
// Allocate a RNTI for this new UE.
gRrc.newRNTI(&mURNTI,&mCRNTI);
//connectUeRlc(gRrcCcchConfig); // Not necessary
gRrc.addUE(this);
}
UEInfo(uint32_t urnti) {
// OK to leave AsnUeId empty.
_initUEInfo();
mURNTI = urnti;
mCRNTI = (urnti & 0xffff); // We use the same id for both; see newRNTI().
gRrc.addUE(this);
}
~UEInfo() {
ueDisconnectRlc(stCELL_FACH);
ueDisconnectRlc(stCELL_DCH);
}
// Write bytes to the high side of the rlc on rbid.
void ueWriteHighSide(RbId rbid, ByteVector &sdu, string descr);
// Write bits to the low side of the rlc on rbid for the two possible channels.
// There are two functions because we have two sets or RLCs simultaneously
// when changing the UE state - see gigantic comment above.
void ueWriteLowSide(RbId rbid, const BitVector &pdu, UEState state);
// This user data sdu popped out of the top of the RLC, and now wants to go somewhere.
void ueRecvData(ByteVector &sdu, RbId rbid) {
ueRegisterActivity();
// Currently all it can be is PS data because we do not support CS.
LOG(INFO) << "Sending SDU to SGSN: " << sdu;
sgsnWriteLowSide(sdu,mURNTI,rbid);
}
void ueRecvDcchMessage(ByteVector &bv,unsigned rbid);
void ueRecvL3Msg(ByteVector &msgframe, UEInfo *uep);
void ueRecvStatusMsg(ASN::UL_DCCH_Message *msg1);
void ueRecvRadioBearerSetupResponse(unsigned transId, bool success, const char *msgname);
void ueRecvRadioBearerReleaseResponse(unsigned transId, bool success, const char *msgname);
void ueRecvRrcConnectionSetupResponse(unsigned transId, bool success, const char *msgname);
void ueRecvRrcConnectionReleaseResponse(unsigned transId, bool success, const char *msgname);
// Connect a UE to some RLCs.
void ueConnectRlc(RrcMasterChConfig *config, UEState nextState);
void ueDisconnectRlc(UEState state); // Destroy the RLC entities.
void reestablishRlcs(); // re-establish all the AM RLCs.
bool mStateChange;
// Try to pull amtBytes through the RLC on every channel.
void uePullLowSide(unsigned amtBytes);
// Read one PDU from the RLC on the specified rb.
ByteVector *ueReadLowSide(RbId rbid);
void uePeriodicService();
void ueRegisterActivity();
// 25.331 10.3.3.36 The transaction id in the messages is only 2 bits.
unsigned newTransactionId() { return (mNextTransactionId++) % sMaxTransaction; }
void ueSetState(UEState newState);
UEState ueGetState() const { return mUeState; }
// Serving RNC [Radio Network Controller] Id. This returns what is saved in the UEInfo structure,
// but it must match the cell id broadcast in SIB2, except they are different sizes,
// so only the top 12 bits are considered.
unsigned getSrncId() { return (mURNTI>>20) & 0xfff; }
// 20 bits SRNTI = SRNC-RNTI = Serving RNC - Radio Network Temporary Id. Identifies the UE within this cell.
unsigned getSRNTI() { return mURNTI & 0xfffff; }
void setPhy(float wRSSI, float wTimingError) { mRSSI = wRSSI; mTimingError = wTimingError; }
float RSSI() { return mRSSI; }
float timingError() { return mTimingError; }
// MAC Interface:
// Return the number of bytes waiting in the highest priority queue for this UE.
unsigned getDlDataBytesAvail(unsigned *uePriority);
// Return the size of the waiting pdu, and how many pdus.
// Note that for TM entities, not all pdus may be the same size.
//unsigned peekPduSize(unsigned rbid, unsigned *pducnt) {
// URlcPair *rlc = mRlc[rbid];
// if (!rlc) return 0;
// *pducnt = rlc->mDown->rlcGetPduCnt();
// return rlc->mDown->rlcGetFirstPduSize();
//}
string ueid() const; // A human readable name for the UE.
string msid() const { return ueid(); } // Used by the SGSN.
void msWriteHighSide(ByteVector &dlpdu, uint32_t rbid, const char *descr); // Called by SGSN
void msDeactivateRabs(unsigned rabMask); // Called by SGSN.
uint32_t msGetHandle() { return mURNTI; }
};
std::ostream& operator<<(std::ostream& os, const UEInfo*uep);
ByteVector* sendDirectTransfer(UEInfo* uep, ByteVector &dlpdu, const char *descr, bool psDomain);
}; // namespace UMTS
#endif