/* * 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. */ #define LOG_GROUP LogGroup::GPRS // Can set Log.Level.GPRS for debugging //#include #include "GPRSInternal.h" #include "GSMCommon.h" #include "GSML3RRMessages.h" // for L3RRMessage #include "GSML3RRElements.h" // for L3RequestReference #define MAC_IMPLEMENTATION 1 #include "MAC.h" #include "FEC.h" #include "TBF.h" #include "RLCEngine.h" #include "RLCMessages.h" #if INTERNAL_SGSN==0 #include "BSSG.h" #endif #include "Ggsn.h" // For GgsnInit #include extern bool gLogToConsole; namespace GPRS { struct TFIList *gTFIs; RLCBSN_t gBSNNext = 0; // The next Block Sequence Number that will be sent on the downlink. RLCBSN_t gBSNPrev = 0; L2MAC gL2MAC; Stats_t Stats; static unsigned ChIdleCounter = 0; static unsigned ChCongestionCounter = 0; int ExtraClockDelay = 0; bool gFixTFIBug = 1; // Default bug fix on. bool gFixSyncUseClock = 0; int gFixIdleFrame = 0; // Default bug fix off now int gFixDRX = 1; // Assume DRX mode if MS does not respond after first try. bool gFixIAUsePoll = 1;// Default on. // 6-28-2012: The ConvertForeignTLLI definitively does not work if done every time. // The multitech modem appears to need this conversion in a special case, fixed in sgsn. bool gFixConvertForeignTLLI = 0; // Was only needed for external SGSN, which was sending bad TLLIs. // This is the downlink queue from the Sgsn. static InterthreadQueue2 sgsnDownlinkQueue; // Extended Dynamic Uplink TBF support. // If we grant a USF for an uplink TBF with more uplink TNs than downlink TNs, // first we must grant the same USF for every downlink channel for this TBF, // and second we must not grant a USF to any other TBF for the uplink // channels belonging to this TBF. // This is per-ARFCN, but we pre-sort the channels so that we process one ARFCN // at a time, and the timeslots in order from 0..7. The multislot configuration // insures that we will see the downlink channel before we see the uplink one. struct ExtDyn { int sCurrentCN; unsigned sReservedUplinkSlots; bool isUplinkReserved(PDCHL1FEC *pdch) { return sReservedUplinkSlots & (1<TN()); } void reserveUplink(PDCHL1Uplink *up) { sReservedUplinkSlots |= (1<TN()); } void edReset() { sReservedUplinkSlots = 0; sCurrentCN = -1; } // We process one ARFCN at a time in order, so we only need 8 bits of memory. // Reset it each time we start a new ARFCN. void edSetCn(int cn) { if (cn != sCurrentCN) { sReservedUplinkSlots = 0; sCurrentCN = cn; } } } extDyn; // Get the number quietly. // Use this for debug options we dont need the user to see. int configGetNumQ(const char *name, int defaultvalue) { if (gConfig.defines(name)) { //return gConfig.getNum(name,defaultvalue); const char *strval = gConfig.getStr(name).c_str(); return strtol(strval,NULL,0); // strtol allows hex } else { return defaultvalue; } } // Dont bother with a fancy specification (eg: 2x4) because we are going // to dynamically allocate channels soon. int configGprsChannelsMinCn() { return gConfig.getNum("GPRS.Channels.Min.CN"); } int configGprsChannelsMinC0() { return gConfig.getNum("GPRS.Channels.Min.C0"); } int configGprsChannelsMin() { return configGprsChannelsMinC0() + configGprsChannelsMinCn(); } #if GPRS_CHANNELS_MAX_SUPPORTED // We are currently doing only static assignment, so take this out for now. int configGprsChannelsMax() { return gConfig.getNum("GPRS.Channels.Max"); } #endif int configGprsMultislotMaxUplink() { return gConfig.getNum("GPRS.Multislot.Max.Uplink"); } int configGprsMultislotMaxDownlink() { return gConfig.getNum("GPRS.Multislot.Max.Downlink"); } //struct GPRSConfig GPRSConfig; not needed. unsigned GPRSDebug = 0; unsigned gGprsWatch = 0; void GPRSSetDebug(int value) { GPRSDebug = value; if (GPRSDebug) { // Dont change these so that we can test the normal use. } } bool GPRSConfig::IsEnabled() { // BEGINCONFIG // 'GPRS.Enable',1,0,0,'Enable GPRS service: 0 or 1. If enabled, GPRS service is advertised in the C0T0 beacon, // and GPRS service may be started on demand. See also GPRS.Channels.*' // ENDCONFIG if (gConfig.getNum("GPRS.Enable") && GPRS::configGprsChannelsMin() > 0) { return true; } return false; // nope } bool GPRSConfig::sgsnIsInternal() { // 2-2012: I added an extra sql parameter GPRS.SGSN.External that you must set to use the // external sgsn, in order to avoid people with existing sql from using it by accident. const char *sql_sgsn_host = "GPRS.SGSN.Host"; const char *sql_sgsn_external = "GPRS.SGSN.External"; if (!gConfig.defines(sql_sgsn_host)) return true; if (!gConfig.defines(sql_sgsn_external)) return true; if (0 == gConfig.getNum(sql_sgsn_external)) return true; return false; // Use external SGSN.Host } // (mike) pretty sure this function is not used anywhere... unsigned GPRSConfig::GetRAColour() { // BEGINCONFIG // 'GPRS.RA_COLOUR',0,0,0,'GPRS Routing Area Color as advertised in the C0T0 beacon' // ENDCONFIG if (gConfig.defines("GPRS.RA_COLOUR")) { return gConfig.getNum("GPRS.RA_COLOUR"); } return 0; } // GSM04.60 12.24. The T3192 code is placed in System Information 13, GSM04.08. // The code specifies one of the following values in msec. static unsigned T3192Codes[8] = { 500, 1000, 1500, 0, 80, 120, 160, 200, }; // 3GPP 04.60 12.24 GPRS Cell Options Notes: // NMO is Network Mode of Operation: See GSM 03.60 6.3.3.1 // Network Mode of Operation II (IE value 1) is the most brain-dead, // no dual-transfer-mode, the GPRS attached MS uses CCCH. // 4-24-2012: I am testing NMO 1 to be used in UMTS, however we cannot use // it in GPRS until we support paging for CS calls on the GPRS data channel. // BEGINCONFIG // 'GPRS.CellOptions.T3168Code',5,1,0,'Timer 3168 in the MS controls the wait time after sending a Packet Resource Request to initiate a TBF before giving up or reattempting a Packet Access Procedure, which may imply sending a new RACH. This code is broadcast to the MS in the C0T0 beacon in the GPRS Cell Options IE. See GSM 04.60 12.24. Range 0..7 to represent 0.5sec to 4sec in 0.5sec steps.' // 'GPRS.CellOptions.T3192Code',0,1,0,'Timer 3192 in the MS specifies the time MS continues to listen on PDCH after all downlink TBFs are finished, and is used to reduce unnecessary RACH traffic. This code is broadcast to the MS in the C0T0 beacon in the GPRS Cell Options IE. The value must be one of the codes described in GSM 04.60 12.24. Value 0 implies 500msec.' // ENDCONFIG // DRX_TIMER irrelevant since we dont use DRX mode at all. // ACCESS_BURST_TYPE 0 => use 8 bit format of Packet Channel Request Message, // (that is, if we were using PRACH, which we are not.) // CONTROL_ACK_TYPE 1 >= default format for Packet Control Acknowledgement is RLC/MAC block, // ie, not special. // BS_CV_MAX is the max number of RLC blocks used by any RLC message. // NW_EXT_UTBF is extended uplink TBF mode: 44.060 9.3.1b and 9.3.1.3 GPRSCellOptions_t::GPRSCellOptions_t() : mNMO(gConfig.getNum("GPRS.NMO")-1), // IE value is spec NMO - 1. //mNMO(1), // Dont let customers change this. mT3168Code(gConfig.getNum("GPRS.CellOptions.T3168Code")), // T3168Code 5 => 3000msec mT3192Code(gConfig.getNum("GPRS.CellOptions.T3192Code")), // T3192Code 0 => 500msec. // Took this out of the gConfig options. // mDRX_TIMER_MAX(gConfig.getNum("GPRS.CellOptions.DRX_TIMER_MAX",0)), mDRX_TIMER_MAX(7), // It is actually non-drx timer max. // The MS uses the min of this and a param sent in gprs attach. mACCESS_BURST_TYPE(0), mCONTROL_ACK_TYPE(1), // Packet Control Acknowledgement is an RLC/BLOCK, not a RACH burst. mBS_CV_MAX(1), // This is determined by the system, not the user. mNW_EXT_UTBF(gConfig.getNum("GPRS.Uplink.Persist") > 0) { // Sanity check some values. if (RN_BOUND(mNMO,0,2) != mNMO) { GLOG(ERR) << "NMO [Network Mode of Operation] must be 1,2,3"; // we subtracted 1 mNMO = 1; } if (RN_BOUND(mT3168Code,0,7) != mT3168Code) { GLOG(ERR) << "mT3168 value " << mT3168Code << " must be in range 0..7"; mT3168Code = 1; } if (RN_BOUND(mT3192Code,0,7) != mT3192Code) { GLOG(ERR) << "mT3192 value " << mT3192Code << " must be in range 0..7"; mT3192Code = 0; } } // This function probes the config file the first time it is called. GPRSCellOptions_t &GPRSGetCellOptions() { static GPRSCellOptions_t *GPRSCellOptions = NULL; if (!GPRSCellOptions) GPRSCellOptions = new GPRSCellOptions_t(); return *GPRSCellOptions; } USFList::USFList() { //mRandomUSF=0; for (int i=0;imuDeadTime.valid()) { if (info->muDeadTime.expired()) { info->muDeadTime.setInvalid(); // Can use the USF now. info->muMS = 0; } return NULL; } return info->muMS; } // Find or set a USF for this ms. Return -1 on failure. int USFList::allocateUSF(MSInfo *ms) { int usf, freeusf = -1; // We want to run through the whole list to see if there was a USF for this MS previously. for (usf = USFMIN; usf <= USFMAX; usf++) { if (mlUSFs[usf].muMS == ms) { // The MS can reuse its own USF. The muDeadTime is how long other MS cannot use it. mlUSFs[usf].muDeadTime.setInvalid(); // USF is back in use. return usf; } if (freeusf == -1 && getUSFMS(usf) == NULL) { freeusf = usf; } } if (freeusf >= USFMIN) { mlUSFs[freeusf].muMS = ms; return freeusf; } return -1; // All in use. } // Free the USFs assigned to this ms. // We could have saved the usf number in the MS to avoid searching for it, // but the list is short and this does not happen often. // If wReserve, the tbf died, so we must reserve the USF resource for 5 seconds. // Note that the same MS can reuse its own reserved USF if it positively re-establishes communication. int USFList::freeUSF(MSInfo *ms, bool wReserve) { for (int usf = USFMIN; usf <= USFMAX; usf++) { UsfInfo *info = &mlUSFs[usf]; if (info->muMS == ms) { if (wReserve) { info->muDeadTime.setFuture(5000); // This is 5 seconds, not programmable. } else { info->muMS = 0; info->muDeadTime.setInvalid(); // Make sure usf is reusable. } return usf; } } return 0; } // Return any USF that is assigned to an MS. // Used when there is nothing else to do with the uplink channel. // Not really random, the MS are serviced round-robin. // We dont want to just give the USF to the MS with the oldest response time, // because it could be out of range, or whatever, and we may never hear from it. //int USFList::getRandomUSF() //{ // for (int i = USFMIN; i <= USFMAX; i++) { // if (++mRandomUSF > USFMAX) { mRandomUSF = USFMIN; } // if (mlUSFs[mRandomUSF]) { return mRandomUSF; } // } // return 0; // There are none. //} void USFList::usfDump(std::ostream&os) { int i; os << "USFList=("; for (i = USFMIN; i <= USFMAX; i++) { os << " " << i << "=>"; MSInfo *ms = mlUSFs[i].muMS; if (ms) { os << ms; long remaining = mlUSFs[i].muDeadTime.remaining(); if (remaining) { os << LOGVAR(remaining); } } else { os << "free"; } } //os << RN_PRETTY_TEXT1(mRandomUSF); os << ")\n"; } int USFList::getUsf(unsigned upbsn) // bsn from the uplink burst. { // GSM 05.02 6.3.2.2.1: The USF field in downlink block N signals // that uplink block (N+1) is assigned to that MS. // 7-6-2012 update: This is also used for extended uplink TBF mode now. unsigned downbsn = upbsn-1; return (sRememberUsfBsn[downbsn%32] == downbsn ? ((signed)sRememberUsf[downbsn%32]) : -1); } void USFList::setUsf(unsigned downusf, unsigned downbsn) // Save usf for current downlink burst. { sRememberUsf[downbsn%32] = downusf; sRememberUsfBsn[downbsn%32] = downbsn; } void L2MAC::macConfigInit() { GPRSSetDebug(configGetNumQ("GPRS.Debug",0)); gGprsWatch = configGetNumQ("GPRS.WATCH",0); gLogToConsole = configGetNumQ("Log.ToConsole",0); GPRSCellOptions_t& gco = GPRSGetCellOptions(); // BEGINCONFIG // 'GPRS.Counters.N3101',20,0,0,'Counts unused USF responses to detect nonresponsive MS. Should be > 8. See GSM04.60 sec 13.' // 'GPRS.Counters.N3103',8,0,0,'Counts ACK/NACK attempts to detect nonresponsive MS. See GSM04.60 sec 13.' // 'GPRS.Counters.N3105',12,0,0,'Counts unused RRBP responses to detect nonresponsive MS. See GSM04.60 sec 13.' // 'GPRS.Timers.T3169',5000,0,0,'Nonresponsive uplink tbf timer, in msecs. See GSM04.60 sec 13' // 'GPRS.Timers.T3191',5000,0,0,'Nonresponsive downlink tbf timer, in msecs. See GSM04.60 sec 13' // 'GPRS.Timers.T3193',0,0,0,'Timer T3193 (in msecs) in the base station corresponds to T3192 in the MS, which is set by GPRS.CellOptions.T3192Code. The T3193 value should be slightly longer than that specified by the T3192Code. If 0, the BTS will fill in a default value based on T3192Code.' // 'GPRS.Timers.T3195',5000,0,0, 'Nonresponsive MS timer, in msecs. See GSM04.60 sec 13' // ENDCONFIG macN3101Max = gConfig.getNum("GPRS.Counters.N3101"); macN3103Max = gConfig.getNum("GPRS.Counters.N3103"); macN3105Max = gConfig.getNum("GPRS.Counters.N3105"); macT3169Value = gConfig.getNum("GPRS.Timers.T3169"); // in msecs. macT3191Value = gConfig.getNum("GPRS.Timers.T3191"); // in msecs. macT3193Value = gConfig.getNum("GPRS.Timers.T3193"); // fixed below. macT3195Value = gConfig.getNum("GPRS.Timers.T3195"); // in msecs. macT3168Value = (gco.mT3168Code + 1) * 500; // in msecs //macTNonResponsive = gConfig.getNUM("GPRS.Timers.MS.NonResponsive") // in msecs // Spec says T3193 (in network) should be longer than T3192 (in MS). // If unspecified, add 50 msecs to T3192. // TODO: We should really have a dead zone near the end of this timer // where we just leave the MS alone, but it doesnt hurt anything, // just wastes airwaves trying to contact the MS in the wrong mode. unsigned T3192Value = T3192Codes[gco.mT3192Code]; if (macT3193Value == 0) { macT3193Value = T3192Value + 50; // Add some extra msecs. } if (macT3193Value < T3192Value) { static unsigned lastT3193Value = 0, lastT3192Value = 0; if (lastT3193Value != macT3193Value || lastT3192Value != T3192Value) { GLOG(ERR) << "T3193 value " << macT3193Value << " should be longer than" " T3192 value " << T3192Value << " (T3192code=" << gco.mT3192Code << ")"; lastT3193Value = macT3193Value; lastT3192Value = T3192Value; } } static bool firsttime = true; if (firsttime) { GLOG(INFO) << "Note: GPRS T3192 = " << T3192Value; GLOG(INFO) << "Note: GPRS T3193 = " << macT3193Value; firsttime = 0; } // These 'timers' are in seconds converted to RLC block counts. // Note: We must keep the MS structure around long enough to handle downlink SGSN // message responses to an uplink message, but this period is short. // If either the MS or SGSN wants to start a new TBF, they send a new TLLI // to create a new MSInfo struct. However, in the future we may use the statistics // gathered from a previous TBF to determine the ChannelCoding (speed) to use // for future TBFs. // BEGINCONFIG // 'GPRS.Timers.MS.Idle',600,0,0,'How long an MS is idle before the BTS forgets about it.' // 'GPRS.Timers.Channels.Idle',6000,0,0,'How long a GPRS channel is idle before being returned to the pool of channels. Also depends on Channels.Min. Currently the channel cannot be returned to the pool while there is any GPRS activity on any channel.' // 'GPRS.Channels.Congestion.Timer',60,0,0,'How long GPRS congestion exceeds the Congestion.Threshold before we attempt to allocate another channel for GPRS' // 'GPRS.Channels.Congestion.Threshold',200,0,0,'The GPRS channel is considered congested if the desired bandwidth exceeds available bandwidth by this amount, specified in percent.' // ENDCONFIG macMSIdleMax = gConfig.getNum("GPRS.Timers.MS.Idle") * RLCBlocksPerSecond; macChIdleMax = gConfig.getNum("GPRS.Timers.Channels.Idle") * RLCBlocksPerSecond; macChCongestionMax = gConfig.getNum("GPRS.Channels.Congestion.Timer") * RLCBlocksPerSecond; // database number specified in percent: macChCongestionThreshold = gConfig.getNum("GPRS.Channels.Congestion.Threshold") / 100.0; macDownlinkPersist = gConfig.getNum("GPRS.Downlink.Persist"); // (pat) We dont use this. static bool thisMessageHasBeenPrinted = false; if (macDownlinkPersist && !thisMessageHasBeenPrinted) { thisMessageHasBeenPrinted = true; LOG(ALERT) << "GPRS.Downlink.Persist is not implemented and config value should be 0!"; } macDownlinkKeepAlive = gConfig.getNum("GPRS.Downlink.KeepAlive"); macUplinkPersist = gConfig.getNum("GPRS.Uplink.Persist"); macUplinkKeepAlive = gConfig.getNum("GPRS.Uplink.KeepAlive"); if (macSingleStepMode) { // Set these to maximum values so we can single step the service loop // without these timers going off. macMSIdleMax = macChIdleMax = 0x7fffffff; // For single step mode macT3191Value = macT3193Value = 0x7fffffff; // For single step mode } gFixIdleFrame = configGetNumQ("GPRS.FixIdleFrame",gFixIdleFrame); gFixTFIBug = configGetNumQ("GPRS.FixTFIBug",gFixTFIBug); // Default bug fix on. gFixDRX = configGetNumQ("GPRS.FixDRX",(int)gFixDRX); // Default to 4 sendAssignment tries. gFixIAUsePoll = configGetNumQ("GPRS.FixIAUsePoll",gFixIAUsePoll); gFixConvertForeignTLLI = configGetNumQ("GPRS.FixForeignTlli",gFixConvertForeignTLLI); } void L2MAC::macAddTBF(TBF *tbf) { macTBFs.push_back(tbf); // Usually already locked, so lock is recursive //macTBFs.push_back_safely(tbf); // Usually already locked, so lock is recursive } void L2MAC::macForgetTBF(TBF *tbf, bool forever) { GPRSLOG(2) << "forget "<tbfid(0); macTBFs.remove(tbf); // lock unnecessary, using macLock now: //macTBFs.remove_safely(tbf); // Usually already locked, so lock is recursive //ScopedLock lock2(macExpiredTBFs.mListLock); if (forever) { macExpiredTBFs.remove(tbf); // Just in case it was on this list. GPRSLOG(2) << "delete ",tbf->tbfid(0); delete tbf; return; } macExpiredTBFs.push_front(tbf); unsigned keepExpired = gConfig.getNum("GPRS.TBF.KeepExpiredCount"); while (macExpiredTBFs.size() > keepExpired) { TBF *tbf2 = macExpiredTBFs.back(); macExpiredTBFs.pop_back(); // returns void, the nitwits. GPRSLOG(2) << "delete ",tbf2->tbfid(0); delete tbf2; } } void L2MAC::macAddMS(MSInfo *ms) { macMSs.push_back(ms); } void L2MAC::macForgetMS(MSInfo *ms, bool forever) { macMSs.remove(ms); // lock unnecessary, using macLock now: //macMSs.remove_safely(ms); // Usually already locked, so lock is recursive //ScopedLock lock2(macExpiredMSs.mListLock); if (forever) { macExpiredMSs.remove(ms); // Just in case it was on this list. delete ms; return; } macExpiredMSs.push_front(ms); unsigned keepExpired = gConfig.getNum("GPRS.MS.KeepExpiredCount"); while (macExpiredMSs.size() > keepExpired) { MSInfo *ms2 = macExpiredMSs.back(); macExpiredMSs.pop_back(); delete ms2; } } // The MS list will be short. Just look through linearly. MSInfo *L2MAC::macFindMSByTlli(uint32_t tlli, int create /*=0*/) { MSInfo *ms; RN_MAC_FOR_ALL_MS(ms) { // When the MS performs a Detach procedure, it will change its existing tlli // from a local tlli to a foreign tlli. Instead of having the SGSN inform us // of these events, just ignore whether the tlli is local or foreign. if (tlliEq(ms->msTlli, tlli) || tlliEq(ms->msOldTlli,tlli)) { // This is very important. // If the SGSN looks up an MS by TLLI it is because it is about to use it, // and we dont want it to disappear before it does. ms->msIdleCounter = 0; return ms; } } if (! create) { return NULL; } ms = new MSInfo(tlli); GPRSLOG(1) << "New MS:"<CN() == 0) { cnt++; } } return cnt; } static void macAddOneChannel(TCHFACCHLogicalChannel *lchan) { PDCHL1FEC *pch = new PDCHL1FEC(lchan); gL2MAC.macPDCHs.push_back(pch); gL2MAC.macPacchs.clear(); // Must rebuild the pacch list. pch->mchStart(); GLOG(INFO) << "GPRS AddChannel " << pch << " total active=" <= configGprsChannelsMax()) { return false; } #endif if (! macActiveChannels()) { // When you first start the BTS you will not be able to allocate until some // timer expires, which I measured as 4 seconds. // So dont bother reporting until after 5 seconds. time_t now; time(&now); if (now - macStartTime < 5) { return false; } // And dont print this message any more often than 10 seconds. static time_t lastMessageTime = 0; if (now - lastMessageTime < 10) { return false; } GLOG(INFO) << "GPRS: Unable to allocate channel, all are busy"; time(&lastMessageTime); return false; } return true; } // Try to free a GPRS channel, returning it to GSM RR use. // 5-24-2012: We must not free the channel that is our PACCH. bool L2MAC::macFreeChannel() { ChIdleCounter = ChCongestionCounter = 0; if (macActiveChannels() <= configGprsChannelsMin()) { return false; } //PDCHL1FEC *pdch = gL2MAC.macPickChannel(); // pick the least busy channel; PDCHL1FEC *pdch = gL2MAC.macPDCHs.back(); GLOG(INFO) << "GPRS freeing channel" << pdch; GPRSLOG(1) << "GPRS freeing channel " << pdch; delete pdch; // Among other things, removes from macPDCHs before freeing it. macPacchs.clear(); // Must rebuild the pacch list. return true; } // This is called during channel destruction to clean up any references to the channel. // The channel better not be in use. // Delete any tbfs using the channel. Detach any MSs using the channel. // Remove the channel from the list in use by GPRS. // SVGDBG What about existing data void L2MAC::macForgetCh(PDCHL1FEC*pch) { pch->mchStop(); // TODO: This should set a timer before the channel goes back to RR use. // Delete any tbfs that used this channel. // Warning: delete tbf removes the tbf from the list we are traversing, so be careful. TBF *tbf; RN_MAC_FOR_ALL_TBF(tbf) { if (tbf->canUseDownlink(pch->downlink()) || tbf->canUseUplink(pch->uplink())) { tbf->mtCancel(MSStopCause::ShutDown,TbfNoRetry); // Deletes tbf. } } // Detach any ms that might be using this channel. // FIXME: This needs work. We need to send the MS new channel assignments which // is a long procedure and may need a new state. MSInfo *ms; RN_MAC_FOR_ALL_MS(ms) { if (ms->canUseDownlink(pch->downlink()) || ms->canUseUplink(pch->uplink())) { ms->msReassignChannels(); } // TODO: Without its PACCH the MSInfo is useless, should kill it off? if (ms->msPacch == pch) { ms->msPacch = NULL; } } macPDCHs.remove(pch); macPacchs.clear(); // Must rebuild the pacch list. GPRSLOG(1) << "macForgetChannel, remaining="<ARFCN()) { resultmask |= 1 << ch->TN(); } } return resultmask; } PDCHL1FEC *L2MAC::macFindChannel(unsigned arfcn, unsigned tn) { PDCHL1FEC *ch; RN_MAC_FOR_ALL_PDCH(ch) { if (arfcn == ch->ARFCN() && tn == ch->TN()) { return ch; } } return NULL; } static void dumpPdch() { PDCHL1FEC *ch; printf("PDCHs=%d:",gL2MAC.macPDCHs.size()); RN_MAC_FOR_ALL_PDCH(ch) { printf(" %s",ch->shortId()); } printf("\n"); printf("PACCHs=%d",gL2MAC.macPacchs.size()); RN_MAC_FOR_ALL_PACCH(ch) { printf(" %s",ch->shortId()); } printf("\n"); } // Given an list of adjacent channels, try to derive optimal PACCH // and stick them in the macPacchs list. static void macPacchAddAdjCh(PDCHL1FEC**alist,int asize) { int downslots = configGprsMultislotMaxDownlink(); // TODO: add a separate chunk size. int upslots = configGprsMultislotMaxUplink(); int chunk = upslots>downslots ? upslots : downslots; //printf("first chunk=%d\n",chunk); chunk = RN_BOUND(chunk,1,4); if (asize < chunk) { // We cannot optimize this adjacency set. if (asize == 1) { GLOG(WARNING) << "GPRS: single channel cannot be used multislot: "<= upslots) ? 2 : 0; } } int full = asize/chunk; // How many full bandwidth pacch. for (int i = 0; i < full; i++) { int n = i*chunk+offset; devassert(n < asize); if (n < asize) { GLOG(INFO)<1) { // If asize == 8 then we can only get here if chunk == 3, but check anyway: if (asize == 8 && chunk == 3) { // In this case we will have have 3 pacchs of size 3,3,2 where // the top pacch shares one tn with the one below. //printf("c:adding %d asize=%d\n",6,asize);dumpPdch(); gL2MAC.macPacchs.push_back(alist[6]); } else if (asize > chunk) { // Go ahead and make a pacch that shares with adjacent ch below. // If it were a full ARFCN, it could share above or below, so it // would not matter much what we pick for PACCH, but in that case, // we would not be in this branch. // So the only sharing opportunity is with the channels below. int lastpacch = asize - leftover + offset; if (lastpacch < 0 || lastpacch >= asize) { GLOG(ERR) << "logic error in leftover PACCH calculation"; // Dont crash. } else { gL2MAC.macPacchs.push_back(alist[lastpacch]); GLOG(INFO)<CN() && tn+1 == (int)ch->TN()) { alist[asize++] = ch; // This is an adjacent channel. } else { // This is not an adjacent ch // Process a list of adjacent channels. macPacchAddAdjCh(alist,asize); asize = 0; // Start a new adjacency list. } cn = ch->CN(); tn = ch->TN(); } if (asize) { macPacchAddAdjCh(alist,asize); } if (GPRSDebug) { printf("after macPacchRebuild:"); dumpPdch(); } // Check for disaster. This would happen if all the channels were singletons. if (gL2MAC.macPacchs.size() == 0) { GLOG(WARNING) << "GPRS: No channels found that can be used in multislot configuration"; RN_MAC_FOR_ALL_PDCH(ch) { gL2MAC.macPacchs.push_back(ch); } } } // Return a GPRS channel to use. // Try to pick the least busy channel. // For an uplink it would be nice to make sure we pick a channel that has free USFs, // but I'm not going to worry about it. PDCHL1FEC *L2MAC::macPickChannel() { int size = macPDCHs.size(); if (size == 0) { return NULL; } // Dont think this can happen. if (0) { // Phase 1: Original code: static int roundrobin = 0; if (++roundrobin >= size) { roundrobin = 0; } return macPDCHs[roundrobin]; } //printf("macPickChannel:"); dumpPdch(); // To give the phones better bandwidth, only return channels that can be // used in an optimum (or close to optimum) multislot config. // We keep the channels we will use as pacch in the macPacchs list. // Rebuild the pacch list if necessary. // The list is cleared whenever we add/remove from macPDCHs. if (macPacchs.size() == 0) { macPacchRebuild(); } //printf("macPickChannel after rebuild:"); dumpPdch(); // Determine the approximate load on each pacch and pick the least busy. int npacch = macPacchs.size(); devassert(npacch); PDCHL1FEC *ch, *bestch = NULL; int bestload = 0; // unneeded init to make gcc happy. for (RListIterator itr(macPacchs); itr.next(ch); ) { int load = 0; MSInfo *ms; RN_MAC_FOR_ALL_MS(ms) { // TODO: Use totalsize instead of size, which requires changing the q type // TODO: Add in the uplink load too. // TODO: The PACCH for assignments that favor uplink over downlink // are one off assignments that favor downlink over uplink, so test // the load on all the assigned channels, not just PACCH. // Add 1 so an unallocated pacch wins over an allocated one, even if not loaded. if (ms->msPacch == ch) { // The msTrafficMetric measures the relative past utilization of the channel in blocks sent, // while downlinkqueuesize is in bytes. Multiply to kind of even out their influence. // Add 1 in case nobody is sending anything we will still differentiate empty channels. int msload = ms->msDownlinkQueue.size() + ms->msTrafficMetric * 30; load += 1 + msload; GPRSLOG(2) << "macPickChannel loop"< 1 || configGprsMultislotMaxUplink() > 1) { const char *multislotmsg = "A multislot configuration, required for high-speed GPRS service, is suggested by the config options GPRS.Multislot.Max.Downlink or GPRS.Multislot.Max.Uplink"; #if GPRS_CHANNELS_MAX_SUPPORTED if (configGprsChannelsMax() <= 1) { GLOG(WARNING) << multislotmsg << " but is not possible because GPRS.Channels.Max <= 1"; } else #endif if (configGprsChannelsMin() <= 1) { GLOG(WARNING) << multislotmsg << " but is unlikely to be achieved because GPRS.Channels.Min <= 1"; } } // Allocate initial channels, if specified. // Update: Channel allocation will not work until OpenBTS has been // running a few seconds. I suspect the channels are created with // their recyclable timers running and we cannot allocate them until // they expire. So dont even try. The mac service loop will try again later. //int minchans = gConfig.getNum("GPRS.Channels.Min",0); //if (minchans > 0) { // for (int i = 0; i < minchans && i < 8; i++) { // if (!macAddChannel()) break; // } //} if (! macSingleStepMode) { macRunning = true; // set this first to avoid an unlikely race condition since // the lock above is released before thread starts. macThread.start(macThreadFunc,this); // macThreadFunc started here } GLOG(INFO) << "GPRS service thread started"; return true; } // External entry point. void gprsStart() { gL2MAC.macStart(); } // This is for debugging and does not try to kill off MS and TBF, // in fact, we probably want to leave those alone for post-mortem examination. void L2MAC::macStop(bool channelstoo) { // (pat) We must not set the lock or the serviceloop thread may not complete, creating deadlock. //ScopedLock lock(macLock); // prevents a RACH from interrupting us. if (macRunning) { macStopFlag = true; macThread.join(); } // Cant just delete the channels while the serviceloop is running or we crash. if (channelstoo) { for (int sanitychk = 0; sanitychk < 20 && macActiveChannels(); sanitychk++) { if (! macFreeChannel()) break; } } GPRSLOG(1) << "macStop successful\n"; } // External entry point. void gprsStop() { gL2MAC.macStop(false); } L1UplinkReservation::L1UplinkReservation() { ScopedLock lock(mLock); // Overkill. We dont need to lock this, no one is using it yet. RLCBlockReservation *rp = &mReservations[0]; for (int i = mReservationSize-1; i >= 0; i--,rp++) { rp->mrBSN = -1; } } void mac_debug() { //PDCHL1FEC *pdch; //RN_MAC_FOR_ALL_PDCH(pdch) { // pdch->debug_test(); //} } // Find an available RadioBlock on this uplink. // If restype indicates RRBP, also return the RRBP // (Relative Reserved Block Period) GSM04.60 10.4.5. // The RRBP can specify reservations 3 - 6 BSN periods in the future: // rrbp: -1: invalid; 0: 3 BSN; 1: 4 BSN; 2: 5 BSN; 3: 6 BSN. // Otherwise, make a reservation after afterBSN and return the reserved absolute BSN // as an integer, or -1 on failure. RLCBSN_t L1UplinkReservation::makeReservationInt( RLCBlockReservation::type restype, RLCBSN_t afterBSN, TBF *tbf, RadData *rd, int *prrbp, // RRBP 0..3 returned here. MsgTransactionType mttype) { ScopedLock lock(mLock); RLCBSN_t bsn, first, lastplus1; // On my Toshiba the BTS and radio are becoming desynchronized to the point // where the uplink blocks arrive at the same time as the downlink // blocks are sent! When this happens the RRBP reservations are not far // enough in advance to be answered. To fix that, use a minimum RRBP // greater than 0. int minrrbp = gConfig.getNum("GPRS.RRBP.Min"); if (tbf) { // Count the reservations for reporting purposes. switch (restype) { case RLCBlockReservation::ForPoll: tbf->mtMS->msCountCcchReservations.addTotal(); break; case RLCBlockReservation::ForRRBP: tbf->mtMS->msCountRbbpReservations.addTotal(); break; default: break; } // This I/O is so stupid... if (rd) { GPRSLOG(1) << "makeReservation"<mtMS <mtMS <mrBSN == bsn) { return rp;} return NULL; } // If TBF is NULL, clear the reservation. // If a TBF is specified, only clear the res if it is for that TBF. void L1UplinkReservation::clearReservation(RLCBSN_t bsn, TBF *tbf) { ScopedLock lock(mLock); RLCBlockReservation *rp = &mReservations[bsn % mReservationSize]; if (tbf && rp->mrTBF != tbf) { return; } rp->mrBSN = -1; rp->mrTBF = NULL; // not necessary, but lets be neat. } // See if this block the MS sent to us corresponds to a reservation, // and if so, update the counters in the corresponding TBF. RLCBlockReservation::type L1UplinkReservation::recvReservation( RLCBSN_t bsn, // The BSN of a received block. TBF**restbf, // Return tbf specified by reservation here, just for debugging. RadData *prd, // Return radio data here. PDCHL1FEC *ch) // Implicit in this pointer, but easier to pass it. { ScopedLock lock(mLock); // This lock is probably no longer necessary. RLCBlockReservation::type result = RLCBlockReservation::None; RLCBlockReservation *rp = &mReservations[bsn % mReservationSize]; *restbf = 0; if (rp->mrBSN == bsn) { result = rp->mrType; if (prd) { *prd = rp->mrRadData; } if (rp->mrTBF) { devassert(rp->mrType != RLCBlockReservation::ForRACH); TBF *rtbf = rp->mrTBF; GPRSLOG(1) << "recvReservation " <mrType <mrSubType) <mtMS <mtRecvAck(rp->mrSubType); switch (rp->mrType) { case RLCBlockReservation::ForPoll: rtbf->mtMS->msCountCcchReservations.addGood(); break; case RLCBlockReservation::ForRRBP: rtbf->mtMS->msCountRbbpReservations.addGood(); break; default: break; } } else { GPRSLOG(1) << "recvReservation"<mrType<<" tbf=null" <mrBSN.valid()) { os << " "; os << res; } } os << ")\n"; } // Return the USF of an MS that might want to use the uplink. // TODO: Fix this not to worry about RRBP since I added makeReservation. static int findNeedyUSF(PDCHL1FEC *pdch) { int usf; GPRSLOG(512) << "findNeedyUSF start for "<getUSFMS(usf); if (ms) { // This USF on this channel is in use by this MS. // Lets see if the MS wants to send something to us. TBF *tbf; RN_MS_FOR_ALL_TBF(ms,tbf) { GPRSLOG(512) << "findNeedyUSF "<mtDir != RLCDir::Up) continue; // We dont include state DataFinal, because then we have already // received all the blocks and are waiting for an RRBP reservation, // which does not need a USF. // We will include DataReassign to let an uplink proceed // during the reassignment process. TBFState::type tstate = tbf->mtGetState(); if (tstate != TBFState::DataTransmit && tstate != TBFState::DataReassign) continue; // At this point, we know the TBF wants to use this uplink slot. // Does the tbf have both uplink and downlink on this channel? // We would not have allocated the USF otherwise, so assert. unsigned tn = pdch->TN(); devassert(ms->msCanUseUplinkTn(tn)); devassert(ms->msCanUseDownlinkTn(tn)); // For extended dynamic we must reserve all the uplink channels simultaneously. if (ms->isExtendedDynamic()) { // Make sure this is the first channel of the allocation. // (This is only necessary if there are multiple down channels, // which only occurs for a 2-down/3-up config.) // We keep the lists sorted to facilitate this test. if (tn != ms->msPCHUps.front()->TN()) {goto nextusf;} PDCHL1Uplink *up2; RN_FOR_ALL(PDCHL1UplinkList_t,ms->msPCHUps,up2) { // The USF in downlink block N reserves uplink block N+1 if (up2->parent()->getReservation(gBSNNext + 1)) { // The extended dynamic TBF cannot use the uplink // if there is a reservation on any of its uplink channels. // If the extended dynamic tbf has exclusive use of // all its channels, or at least, if there are no other // PACCH sharing any channels, this will not happen except // on the PACCH of this ms, because there could be multiple // extended dynamic tbfs sharing the same PACCH. goto nextusf; } } // Success! Reserve all the uplinks belonging to this tbf. // According to the spec, we only have to transmit the USF // on the first downlink for the tbf, which will happen below. // So all we have to is reserve the channels, we dont need // to save the usf. Doesnt matter if we reserve the current // timeslot since we are already past the test, so just do all. RN_FOR_ALL(PDCHL1UplinkList_t,ms->msPCHUps,up2) { extDyn.reserveUplink(up2); } // Fall through to return the usf for this tbf. } // If an uplink tbf is stalled, the MS is waiting for an acknak // from the network, so there is not much point in giving it an uplink USF. // An interesting point is that it might get its acknack before // this downlink block gets to it, so maybe we should go ahead and // give it a usf anyway, but I didnt. It might get the usf below, anyway. // 6-7-2012: Above is WRONG. The MS will continue sending the old // blocks and it needs USF to do so; without them it will hang forever. //if (tbf->stalled()) continue; int thisage = gBSNNext - ms->msLastUsfGrant; GPRSLOG(512) << "findNeedyUSF for "<mtMS->msCountUSFGrant(besttbf->mtGetState() == TBFState::DataTransmit); GPRSLOG(4)<mUSF = findNeedyUSF(pdch); int dutyfactor = configGetNumQ("GPRS.UplinkDutyFactor",100); int nope = false; if (dutyfactor < 0) { // If it is zero, it is probably just an sql goof. GLOG(ERR) << "Invalid GPRS.UplinkDutyFactor:"<mSP = 0; block->mUSF = 0; if (makeres) { mac_debug(); RLCBSN_t bsn = pdch->makeRRBPReservation(tbf,&rrbp,mttype); if (bsn.valid()) { if (tbf) { tbf->mtSetAckExpected(bsn,mttype); } } else { devassert(rrbp == -1); if (makeres == 2) { return false; } // Caller will try again later. } } if (rrbp != -1) { block->setRRBP(rrbp); if (pcounter) (*pcounter)++; } // GSM 05.02 6.3.2.2.1: The USF field in downlink block N signals // that uplink block (N+1) is assigned to that MS. if (! pdch->getReservation(gBSNNext + 1) && ! disabledByDutyFactor() && ! extDyn.isUplinkReserved(pdch)) { block->mUSF = findNeedyUSF(pdch); // Remember the usf sent on this channel at time gBSNNext pdch->setUsf(block->mUSF,gBSNNext); // ok to be 0. } else { pdch->setUsf(0,gBSNNext); // ok to be 0. } return true; } // GSM 4.08 sec3.5.2 // Return an L3RRMessage to be sent on the AGCH or NULL. // The returned msg is a GSM RR message because the MS is still listening to the // beacon in packet-idle-mode, so this msg is sent on CCCH. // We dont even know what MS we are talking to yet; we dont have a TMSI. // Timers: // T3146 in MS, started when MS sends its maximum number of RACH (M), // stopped when it gets Immediate Assignment (or I.A. Reject) // MS gives up when it expires. // Min value T+2*S (S and T defined in 3.3.1.1.2: T is Tx-integer broadcast // on BCCH, S defined in GSM04.08 table 2.1) // Max value 5 secs. // T3141 in network started when Immediate Asignment sent (which is unacknowledged mode - // note that it is for a single block access so the receipt of that block is the acknowledgement.) // when it expires. // Power control in GSM03.64 sec6.5.8 // 2-2014: No longer uses makeCCCHReservation because we are called on demand and we know exactly when the CCCH is going out. // WARNING: This runs in a different thread than the rest of GPRS. That means that makeReservationInt must be mutex protected. L3ImmediateAssignment *makeSingleBlockImmediateAssign(RachInfo *rip, unsigned afterFrame) { if (! GPRSConfig::IsEnabled()) { return NULL; } LOG(DEBUG) <makeReservationInt(RLCBlockReservation::ForRACH, afterBSN, // Reservation must be after this time. NULL, // No TBF for this &rip->mRadData, // We remember the timing advance. NULL, // No RRBP MsgTransNone); // Dont inform anyone else. if (! RBN.valid()) { // Abject failure. This should never happen because the reservation controller can make // reservations as far in advance as necessary. GPRSLOG(1) << "serviceRACH failed to make a reservation at" <mRA,rip->mWhen), chan->packetChannelDescription(), GSM::L3TimingAdvance(GetTimingAdvance(rip->mRadData.mTimingError)), true,false); // tbf, downlink // The immediate assignment has a TFI, but we do not set it for a single block assignment. L3IAPacketAssignment *pa = result->packetAssign(); pa->setPacketUplinkAssignSingleBlock(RBN.FN()); // We dont know what MS we are talking to, so set the power params to global defaults. // We could fiddle with the power gamma based on RSSI, but not implemented, // and probably unnecessary. It is not really necessary to change these at all here. pa->setPacketPowerOptions(GetPowerAlpha(),GetPowerGamma()); GPRSLOG(1) << "GPRS serviceRACH sending L3ImmediateAssignment:" << *result; return result; } // The RLC block just arrived on pdch. Figure out to which TBF it belongs and // pass it on to the RLCEngine for assembly into a PDU. static void processRLCUplinkDataBlock(PDCHL1FEC *pdch, RLCRawBlock *src,TBF *restbf) { RLCUplinkDataBlock *rb = new RLCUplinkDataBlock(src); // If this flag is set, data blocks are supposed to arrive // only on odd numbered blocks if (gFixIdleFrame && (0==(src->mBSN&1))) { GPRSLOG(1) << "@@@OOPS: Even numbered uplink data block:"<mBSN; } // Reassociate the block with the TBF to which it belongs: //GPRSLOG(1) << "### Uplink Data Block tfi="<mTFI<< " bsn="<mBSN; TBF *tbf = pdch->getTFITBF(rb->mTFI,RLCDir::Up); if (tbf) { // The rd data is from the RACH, and is pretty old; // Use the new RSSI from RLCRawBlock. //if (rd.mValid) { // tbf->mtMS->setRadData(rd); // tbf->setRadData(rd); //} // In the calling function processUplinkBlock we looked up the TBF by USF // and already counted it. Here we are looking up the TBF by TFI. // I am leaving in both calls in case there is a problem, but we wont // double count the block for reporting purposes. tbf->mtMS->setRadData(src->mRD); tbf->mtMS->talkedUp(true); // Mark time, but dont double count. //GPRSLOG(1) << "### Uplink Data Block tfi="<mTFI // <<" tbf="<engineRecvDataBlock(rb,pdch->TN()); } else { //GPRSLOG(1) << "### Uplink Data Block with unknown tfi="<mTFI // << " bsn="<mBSN; GLOG(WARNING) << "Uplink Data Block with TFI="<mTFI << " bsn="<mBSN <<" unassociated with TBF"; } } // The MS is requesting an uplink TBF. // Create the TBF, then go ahead and try to attach it right now. // The MS may not have channels assigned yet. // (Happens if this is the first time we have heard from this MS; it is the second // part of a two phase uplink assignment.) // LISTEN UP: If there is already an uplink TBF running for this MS and you send a second // Packet Uplink Assignment, the MS *immediately* starts using the new TFI // for the in-progress TBF, which would cause that one to report a "fail". // The data may or may not be lost, because if the TBF has not yet had any acknacks, // the data will get through on the second TBF, else if there have been acknacks, // the data is lost as far as we are concerned. static void processUplinkResourceRequest( PDCHL1FEC *requestch, // The channel the request arrived on. RLCMsgPacketResourceRequest *rmsg, RLCBlockReservation::type restype, RadData &rd) { MSInfo *ms = rmsg->getMS(requestch,true); if (!ms) { // 3-20120: This happened if the MS tried to identify itself using a TFI, // but the TBF for that TFI is already deleted. Just ignore it, nothing else we can do. GPRSLOG(1) << "UplinkResourceRequest for unidentified MS on ch:" << requestch; return; } ms->talkedUp(); if (rd.mValid) { ms->setRadData(rd.mRSSI,rd.mTimingError); } // Store the channel quality report info. ms->msCValue.addPoint(rmsg->mCValue); if (rmsg->mSignVarPresent) { ms->msSigVar.addPoint(rmsg->mSignVar); } // Not present for two phase access. for (int tn=0; tn < 8; tn++) { if (rmsg->mILevelPresent[tn]) { ms->msILevel.addPoint(rmsg->mILevelTN[tn]); } } bool isRach = (restype == RLCBlockReservation::ForRACH); if (isRach) { // After an MS in PacketIdle mode contacts us, we send it a single // block uplink assignment, and this is its answer. // The MS will listen on this PDCH until T3168 expires. // Regardless of what we do about this RACH. // TODO: Should we only set this if the purpose of the rach is an assignment, // not for measurements? // This timer blocks downlink assignments so its a performance issue. ms->msT3168.set(); } // 6-8-2012: This is the previous code; now handled below. // // If there is an existing uplink tbf for this MS, ignore this request. // // We just drop it on the ground. // // FIXME TODO: That is not right - the uplink tbf request is probably changing // // the priority of the existing tbf, and we need to resend the (identical) assignment // // to make the MS happy, or it will kill off the uplink TBF after some timer. // TBF *existingtbf; // if (ms->msCountActiveTBF(RLCDir::Up, &existingtbf)) { // GPRSLOG(1) << ms << " dropping uplink request, duplicates:"<mTLLIPresent) { ms->msTlli = rmsg->mTLLI; } // TODO LATER: We may want to assign more channels based on params in the rmsg. // If this uplink request was RACH initiated, we did not know the MS before // we got this message, so channel assignment was a shot in the dark. // We need to remember the requestch because the ms is already listening on it // as PACCH, and will continue to do so unless and until we tell it otherwise. if (ms->msPacch && ms->msPacch != requestch) { // This does not really matter. GPRSLOG(1) << ms <<" ch:"<msPacch<<" different from msg ch:"<msCountActiveTBF(RLCDir::Down, &atbf)) { GLOG(ERR) << ms <<" RACH while TBF transmitting:"< RACH, anonymous --> // Immediate downlink assignment with TLLI <--- Network sends // One block assignment for RACH, anonymous <-- Network sends // MS <-- downlink assignment // MS ---> ack to downlink ---> // MS <-- One block assignment, ignored. // MS -- XXX --> No response to one block assignment. // Then the MS should not send the PacketResourceRequest, so if we // get here with a downlink tbf in transmit state, it is because // the TBF failed and the MS is retrying. // If they arrive in this order: // MS <-- one block assignment // MS --> Packet Resource Request // After sending Packet Resource Request MS leaves "Packet Idle Mode". // MS <-- Immediate Assignment. // Then the downlink TBF will be in DataWaiting1 mode. // We have two choices: // o We could pretend we did not hear this Packet Resource Request // and wait for the Immediate Assignment to arrive, but I dont think that will work // o We establish the uplink TBF on PACCH and just let the SendAssignment code // wait for the reservation time for this Downlink Immediate Assignment message to pass, // after which that code will try again on PACCH. // We dont find out they are the same MS until now. // The safe thing to do is cancel the immediate assignment and send a new // assignment on the new PACCH. // In any case, this sucks, because we did not deliver the downlink TBF reliably - // unless I kept track of the TBFs that have been fully acked and resend the others? //atbf->mtSetState(TBFState::DataReassign); if (atbf->isTransmitting()) { // I think we can retry the TBF immediately, but until that is proved, // we will wait for the timeout before retrying. atbf->mtCancel(MSStopCause::Rach,TbfRetryAfterWait); } ignore = true; GLOG(WARNING) << ms <<" Ignoring RACH while TBF active:"<msCountTransmittingTBF(RLCDir::Up, &atbf)) { if (isRach) { // I dont know if we should kill this TBF or not. // Before I supported uplink requests in downlinkacknack I thought the Blackberry was using // RACH to request new uplink TBFs but there were still so many bugs then that I'm not sure. atbf->mtCancel(MSStopCause::Rach,TbfRetryInapplicable); GLOG(WARNING) << ms <<" RACH while uplink TBF transmitting, TBF cancelled:"<mtGetState() != TBFState::DataFinal) { // GLOG(INFO) << ms <<" Uplink TBF reassignment request delayed until current TBF finished:"<msSetUplinkRequest(rmsg->mCRD); // } else { // atbf->mtSetState(TBFState::DataReassign); // GLOG(INFO) << ms <<" Uplink TBF reassignment request: "<msDeassignChannels(); ms->msPacch = requestch; } // Allocate a new uplink TBF. // If a tlli was present in the message we must send it to the sgsn for the one // special case where an ms loses registration and tries to re-register; // in this special case the request tlli will not match the msTlli because // the ms has already undergone the change tlli procedure. uint32_t tlli = rmsg->mTLLIPresent ? (uint32_t) rmsg->mTLLI : (uint32_t) ms->msTlli; TBF *tbf = TBF::newUpTBF(ms,rmsg->mCRD,tlli,true); if (tbf == NULL) return; //tbf->mtUnAckMode = rmsg->mCRD.mRLCMode; GPRSLOG(1) <<"UplinkResourceRequest" <mTLLI) <str(); // TODO: This switch is not very interesting... switch (accessType) { // GSM04.60 table 11.2.16.2 case 0: // Two Phase Access Request [second half of it] case 3: // Mobility Management Procedure. case 1: // Page Response case 2: // Cell Update processUplinkResourceRequest(pdch,rmsg,restype,rd); break; } } static void processDownlinkAckNack(PDCHL1FEC *pdch,RLCMsgPacketDownlinkAckNack* rmsg,RadData &rd) { int tfi = rmsg->getTFI(); TBF *tbf = pdch->getTFITBF(tfi,RLCDir::Down); if (tbf) { MSInfo *ms = tbf->mtMS; ms->talkedUp(); ms->setRadData(rd); // Store the Channel Quality Report for reporting purposes. ms->msCValue.addPoint(rmsg->mCQR.mCValue); ms->msRXQual.addPoint(rmsg->mCQR.mRXQual); ms->msSigVar.addPoint(rmsg->mCQR.mSignVar); for (int tn = 0; tn < 8; tn++) { // Dont bother to differentiate this per timeslot. if (rmsg->mCQR.mHaveILevel[tn]) {ms->msILevel.addPoint(rmsg->mCQR.mILevel[tn]);} } tbf->engineRecvAckNack(rmsg); // process the ack/nack part of the msg. if (rmsg->mHaveChannelRequest) { // Process the channel request, if any. TBF *uptbf = TBF::newUpTBF(ms,rmsg->mCRD,ms->msTlli,false); if (uptbf) { GPRSLOG(1) <<"Uplink TBF from downlink AckNack" <talkedUp(); ms->setRadData(rd); } else { GLOG(ERR) << "TLLI"<str(); // We dont have to do anything; recvReservation informed the TBF. uplinkCommon(rmsg->mTLLI,rd,"ControlAck"); } static void processUplinkDummy(PDCHL1FEC *pdch, RLCMsgPacketUplinkDummyControlBlock* rmsg, RadData &rd) { GPRSLOG(1) << "processDummyUplink "<str(); uplinkCommon(rmsg->mTLLI,rd,"DummyUplinkControl"); } // The src block is the decoded uplink BitVector from the radio. // The BitVector we are passed was allocated; delete when we are finished. static void processUplinkBlock(PDCHL1FEC *pdch, RLCRawBlock *src) // SVGDBG process blocks { // Possibly handle timers. See: XCCHL1Decoder:handleGoodFrame(); // TODO ... char buf[40]; GPRSLOG(1) <<"-----> processUplinkBlock mac type="<mmac.mPayloadType) << " ch:"<getAnsweringUsfText(buf,src->mBSN); RadData rd; TBF *restbf; RLCUplinkMessage::MessageType mtype; mac_debug(); RLCBlockReservation::type restype = pdch->recvReservation(src->mBSN,&restbf,&rd,pdch); // Reset N3101. We are doing it based on the presence of the burst rather. // Someday we should look inside the burst and make sure it really came from the MS we expected. // This code works for persistent (extended) uplink mode too; in that mode // the MS sends control blocks if it does not have data. // TODO: Can elide code still extant down alot of these branches that resets msN3101. int usf = pdch->getUsf(src->mBSN); if (usf != -1) { MSInfo *usfms = pdch->getUSFMS(usf); if (usfms) { usfms->msN3101 = 0; usfms->talkedUp(); } } switch (src->mmac.mPayloadType) { case MACPayloadType::RLCData: if (restype != RLCBlockReservation::None) { // This happened alot before fixing the transceiverRAD1. GLOG(ERR) << "ERROR: Received reservation in RLC data block"; } processRLCUplinkDataBlock(pdch,src,restbf); break; case MACPayloadType::RLCControl: { mtype = (RLCUplinkMessage::MessageType) src->mData.peekField(8,6); GPRSLOG(1) << "processUplinkMessage:" <mMessageType) { case RLCUplinkMessage::PacketControlAcknowledgement: processControlAcknowledgement(pdch,(RLCMsgPacketControlAcknowledgement*)msg,src->mRD); break; case RLCUplinkMessage::PacketDownlinkAckNack: processDownlinkAckNack(pdch,(RLCMsgPacketDownlinkAckNack*)msg,src->mRD); break; case RLCUplinkMessage::PacketResourceRequest: processResourceRequest(pdch,(RLCMsgPacketResourceRequest*) msg,restype,src->mRD); break; case RLCUplinkMessage::PacketUplinkDummyControlBlock: processUplinkDummy(pdch,(RLCMsgPacketUplinkDummyControlBlock*)msg,src->mRD); break; default: // Just ignore unrecognized messages. GLOG(INFO) << "GPRS: Ignoring UplinkMessage:" << RLCUplinkMessage::name(msg->mMessageType); break; } // switch msg->mMessageType delete msg; break; } case MACPayloadType::RLCControlExt: { mtype = (RLCUplinkMessage::MessageType) src->mData.peekField(8,6); GLOG(INFO) << "GPRS: Ignoring uplink RLCControlExt block, msgtype="<str()<getRefCnt() == 2); delete dmsg; //devassert(dlmsg->getRefCnt() == 1); // Find or create this MS. MSInfo *ms = NULL; if (dlmsg->mbdHaveOldTLLI) { ms = bssgMSChangeTLLI(dlmsg->mbdOldTLLI,dlmsg->mbdTLLI); } if (!ms) { ms = gL2MAC.macFindMSByTlli(dlmsg->mbdTLLI, true); } #if 0 // Code prior to internal sgsn ms->msDownlinkQueue.write(dlmsg); #else SGSN::GprsSgsnDownlinkPdu *dlpdu = new SGSN::GprsSgsnDownlinkPdu(dlmsg->mbdPDU,ms->msTlli,0,"BSSGMsgDlUnitData"); //devassert(dlmsg->getRefCnt() == 2); //dlpdu->mDlData = dlmsg->mbdPDU; //dlpdu->mDescr = "BSSGMsgDLUnitData"; delete dlmsg; ms->msDownlinkQueue.write(dlpdu); #endif // If the MS queue is too full, we should do flow control, // but the sgsn does not implement it yet, so not much point. break; } // TODO: Other BSSG messages: case BSSG::BSPDUType::RA_CAPABILITY: // network->BSS case BSSG::BSPDUType::PTM_UNITDATA: // not currently used // PDUs between GMM SAPs: case BSSG::BSPDUType::PAGING_PS: // network->BSS request to page MS for packet connection. case BSSG::BSPDUType::PAGING_CS: // network->BSS request to page MS for RR connection. case BSSG::BSPDUType::RA_CAPABILITY_UPDATE_ACK: // network->BSS Radio Access Capability and IMSI. case BSSG::BSPDUType::FLOW_CONTROL_BVC_ACK: // network->BSS case BSSG::BSPDUType::FLOW_CONTROL_MS_ACK: // network->BSS default: // See the list of unimplemented messages in BsRecvMsg() GPRSLOG(1) << "BSSG Downlink Message Ignored:"<mTlli); delete dlpdu; // Done with that. } } } // Compute a measure of GPRS channel utilization so we can decide when we need more bandwidth. // For now we will compute only downlink bandwidth and ignore uplink. // You cant just count how many RLC blocks are in TBFs, because the associated MS // could be stalled and the TBF may not be using any bandwidth. // The utilization is the approximate number of TBFs that are waiting to send blocks now, // so 1.0 means the channel is pretty much full, and 2.0 means we could // probably fully utilize two channels, at least at this instant. // Return the utilization. float L2MAC::macComputeUtilization() { ScopedLock lock(macLock); // This function called from CLI. int numReady = 0; float utilization = 0.0; // Desired downlink utilization at this moment. TBF *tbf; RN_MAC_FOR_ALL_TBF(tbf) { switch (tbf->mtGetState()) { case TBFState::DataReadyToConnect: case TBFState::DataWaiting1: case TBFState::DataWaiting2: numReady++; // These TBFs are waiting to send a message. continue; case TBFState::DataTransmit: case TBFState::DataReassign: //case TBFState::DataStalled: utilization += tbf->engineDesiredUtilization(); continue; default: continue; } } utilization += numReady; // We want to average the utilization over some timespan. // This number should be picked to match the delay we use before closing // GPRS chanels, whatever that will be. const unsigned NumFramesToAverage = 48*5; // roughly 5 seconds worth of RLC blocks. return macDownlinkUtilization = (utilization + macDownlinkUtilization * (NumFramesToAverage-1)) / NumFramesToAverage; } void L2MAC::macCheckChannels() { int minChC0 = configGprsChannelsMinC0(); int minChCn = configGprsChannelsMinCn(); if (minChC0 < 0) { minChC0 = 0; } if (minChCn < 0) { minChCn = 0; } bool addedChannels = false; //GPRSLOG(2)<<"macCheckChannel"< (int)macActiveChannels()) { // Allocate from CN0. int active0 = macActiveChannelsC(0); { TCHFACCHLogicalChannel *lchan; for ( ; active0 < minChC0 && (lchan = gBTS.getTCH(true,true)); active0++) { //GPRSLOG(2)<<"macCheckChannel loop"<TN()); // We dont "open" the logical channel, which means we dont start // the various timers referred to in L1Decoder::recyclable(). // It probably doesnt matter whether it is 'open' or not, because // we hook the bursts before they get to the GSM logical channel classes. macAddOneChannel(lchan); addedChannels = true; } } // Allocate from other ARFCNs // This should probably allow a specified number of channels // on each arfcn. { int activecn = (int)macActiveChannels() - active0; int nfound, need = minChCn - activecn; TCHFACCHLogicalChannel *results[8]; // TODO: Prevent this from allocating from C0 if user misconfigures. for (; need > 0 && (nfound = gBTS.getTCHGroup(need,results)); need -= nfound) { for (int i = 0; i < nfound; i++) { macAddOneChannel(results[i]); addedChannels = true; } } } } if (addedChannels) { // We must keep the channel list sorted all the time because // PACCH selection and extended uplink TBF both eexpect it. gL2MAC.macPDCHs.sort(chCompareFunc); } #if 0 int minchans = configGprsChannelsMin(); if (minchans > 0) { // We are doing startup. Allocate the initial channels from CN0. int need = minchans - (int)macActiveChannels(); TCHFACCHLogicalChannel *lchan; // Allocate from CN0 first. for ( ; need > 0 && (lchan = gBTS.getTCH(true,true)); need--) { macAddOneChannel(lchan); } // If we still need more, allocate them from the end of the list. int nfound; TCHFACCHLogicalChannel *results[8]; for ( ; need > 0 && (nfound = gBTS.getTCHGroup(need,results)); need -= nfound) { for (int i = 0; i < nfound; i++) { macAddOneChannel(results[i]); } } } #endif // TODO: Switch code to new channel allocator, but this code // will move to where the channels are allocated, not here. if (macTBFs.size()) { ChIdleCounter = 0; // If there are TBFs but no channels, try to allocate one. // TBFs get added not only from the MAC but also indirectly by the BSSG. // Note that we may not get the channel, in which case we will try each loop iteration. if (!macActiveChannels()) { macAddChannel(); } } else { // No TBFs exist. if (ChIdleCounter++ > macChIdleMax) { // Return a channel to GSM RR use. // We dont do this unless there is no activity at all, // which means that if there are multiple channels allocated we cant // downsize if the activity is merely low - it has to be stopped. macFreeChannel(); } } // Maybe add another channel. // We average the congestion measurement by incrementing it or decrementing it once each loop. // todo: This test is too simple; needs to take into account how many channels allocated. /**** TODO: This will be replaced by dynamic allocation. if (macComputeUtilization() > macChCongestionThreshold) { if (ChCongestionCounter++ > macChCongestionMax) { macAddChannel(); } } else { if (ChCongestionCounter > 0) { ChCongestionCounter--; } } ****/ } // Advance gBSNNext by the specified amount. // The reason this is a function instead of just adding one to gBSNNext // is to clean up old reservations behind us as we go. static void advanceBSNNext(int amt) { while (amt > 0) { amt--; gBSNPrev = gBSNNext; ++gBSNNext; PDCHL1FEC *pdch; RN_MAC_FOR_ALL_PDCH(pdch) { // for all channels assigned to GPRS. RLCBlockReservation *res; // For debug purposes, show unanswered reservations, and show them more nearly // when they are supposed to arrive than when we actually clear them out below: if (GPRSDebug) { RLCBSN_t rprevdeb(gBSNNext-(BSNLagTime+ExtraClockDelay+2)); res = pdch->getReservation(rprevdeb); if (res) { GPRSLOG(1) << "Reservation unanswered "<mrType<mrSubType)<<" "<mrTBF <<" bsn=" <clearReservation(rprev,NULL); } } } // After a crash the current time is going backwards occassionally. // which is screwing up this code. // If you reboot, it is ok, but I am putting in some code to deal with it. // Here is the /var/log/OpenBTS.log: // My message: //Nov 10 22:48:49 ToshibaLap openbts: DEBUG GPRS:now=777040 waiting for 777044 //Nov 10 22:48:49 ToshibaLap transceiver: ERR 3086998384 rnrad1Core.cpp:52:usbMsg: libusb_control_transfer failed: No such device (it may have been disconnected) //Nov 10 22:48:49 ToshibaLap transceiver: ERR 3086998384 rnrad1Core.cpp:52:usbMsg: libusb_control_transfer failed: No such device (it may have been disconnected) // My message: //Nov 10 22:48:49 ToshibaLap openbts: DEBUG GPRS:unexpected gBSNNext delta: delta=(1306) gBSNNext=(179319) fnnext=(777049) fnnow=(775743) // // I am also getting this occassionally: //Nov 10 22:48:45 ToshibaLap transceiver: ERR 3074030448 fusb.cpp:445:reload_read_buffer: No libusb events // // Work around it by checking if time has run backwards, and switching to // calling sleep instead of catching up to the goofed up GSM clock. static void serviceLoopSynchronize(bool firsttime) { static double timeprev; Time tstart = gBTS.time(); if (GPRSDebug) { // This is just a debug check that the GSM master clock is sane. // Apparently it is. static int fnprev; int fnnow = tstart.FN(); if (!firsttime) { int fndelta = GSM::FNDelta(fnnow,fnprev); GPRSLOG(16) << "GSM FN delta="< 6) { GPRSLOG(4) << "GSM FN ran forwards by " << fndelta << "\n"; } } fnprev = fnnow; } if (gFixSyncUseClock) { // Work around the bug where time goes backwards after a crash. // The whole BTS is probably non-operational, but at least I can keep debugging. // Try just waiting about a block time between each loop. sleepf(RLCBlockTime); //useconds_t usecs = (useconds_t) (1000000.0 / 48.0); //usleep(usecs); } else { // Wait for the prev frame to come around. Time tprev(gBSNPrev.FN()); tprev = tprev - configGetNumQ("GPRS.ThreadAdvance",0); gBTS.clock().wait(tprev); // Check if the wait is screwing up: It is. // On the ITX board the FN moves backwards 2 units sporadically, // which (I hope) is because the gBTS clock was resynchronized with the radio // causing it to run backwards. Time tnow = gBTS.time(); int deltaAfterWait = GSM::FNDelta(tnow.FN(),tprev.FN()); // The deltaAfterWait is usually -1, so ignore that. if (deltaAfterWait > 3 || deltaAfterWait < -1) { GPRSLOG(2) << "gBTS.clock.wait unexpected wait time: "< 0 || delta < -(51*26)) { // Set bsnFixed to the beginning frame of the RLC block containing tnow. // | gBSNPrev | gBSNNext | ... | bsnFixed | RLCBSN_t bsnFixed = FrameNumber2BSN(tnow.FN()); // Then advance gBSNNext to the next block time modulo ghyperframe. if (delta > 0) { advanceBSNNext(bsnFixed - gBSNNext); } else { // This is really a disaster. gBSNNext = bsnFixed + 1; } GPRSLOG(2) << "unexpected gBSNNext delta:" <debug_test(); while (RLCRawBlock *src = pdch->uplink()->mchUplinkData.readNoBlock()) { processUplinkBlock(pdch,src); delete src; } } GPRSLOG(16) << "macServiceLoop: after uplink service"; // Step: Service unattached TBFs. (An attached TBF has resources // assigned to its MS. Attached TBFs are handled by dlService().) // This could have been combined in servicing the MS, // but efficiency is not an issue and this seems more clear. // Be a little careful since we are deleting from the list we are iterating. { TBF *tbf; RN_MAC_FOR_ALL_TBF(tbf) { // This call may remove the tbf from the list being iterated: tbf->mtServiceUnattached(); } } // Step: Service the MSs; they may want to start new TBFs. MSInfo *ms; RN_MAC_FOR_ALL_MS(ms) { ms->msService(); } GPRSLOG(16) << "macServiceLoop: after ms service"; // Step: Feed each downlink PDCH with RadioBlocks. // As a side effect, this services all the [connected] TBFs in round robin order. extDyn.edReset(); RN_MAC_FOR_ALL_PDCH(pdch) { // for all channels assigned to GPRS. extDyn.edSetCn(pdch->CN()); pdch->downlink()->dlService(); } GPRSLOG(16) << "macServiceLoop: after downlink service"; // LONG RANGE TODO: At the end of a TBF, if the downlink TBF queue is low, // we should send flow control to BSSG. See engineRecvAckNack // But sadly, the SGSN does not implement flow control, so dont bother. // Step: Service the BSSG queue. // This mostly means moving any downlink PDUs into their MS. if (! GPRSConfig::sgsnIsInternal()) { #if INTERNAL_SGSN==0 processBSSGMessages(); #endif GPRSLOG(16) << "macServiceLoop: after bssg service"; } else { processSgsnMessages(); } // Step: gather statistics about this loop. if (GPRSDebug) { double elapsed = timef() - starttime; Stats.macServiceLoopTime.addPoint(elapsed); } } // The main processing thread static void *macThreadFunc(void *arg) { gL2MAC.macRunning = true; // Set the current RLC BSN. //Time tstart = gBTS.time().FN(); Time tstart = gBTS.time(); int fnstart = tstart.FN(); gBSNNext = FrameNumber2BSN(fnstart); ChIdleCounter = ChCongestionCounter = 0; // Lets start synced up on a 52-multiframe boundary. // Remove or is this used for testing if (0) { // why bother? int offset = fnstart % 52; if (offset) { fnstart += (52 - offset); gBTS.clock().wait(fnstart); } } GPRSLOG(1) << "macServiceLoop starting:" << LOGVAR(gBSNNext) <