/* * Copyright 2008, 2009, 2014 Free Software Foundation, Inc. * Copyright 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::GSM // Can set Log.Level.GSM for debugging /* Many elements follow Daniele Orlandi's vISDN/Q.921 implementation, although no code is copied directly. 6-2014: Pat Thompson rewrote the Layer1 and layer3 connections, primitives and messages. */ /* As a general rule, the caller need not hold mLock when sending U frames but should hold the lock when transmitting S or I frames. This implementation is for use on the BTS side, however, some MS functions are included for unit- testing. For actual use in a BTS, these will need to be compelted. */ #include "GSML2LAPDm.h" //#include "GSMSAPMux.h" #include "GSMLogicalChannel.h" #include #include #include using namespace std; using namespace GSM; //#define NDEBUG ostream& GSM::operator<<(ostream& os, LAPDState state) { switch (state) { case LinkReleased: os << "LinkReleased"; break; case AwaitingEstablish: os << "AwaitingEstablish"; break; case AwaitingRelease: os << "AwaitingRelease"; break; case LinkEstablished: os << "LinkEstablished"; break; case ContentionResolution: os << "ContentionResolution"; break; default: os << "?" << (int)state << "?"; } return os; } L2LAPDm::L2LAPDm(unsigned wC, SAPI_t wSAPI) :mRunning(false), mC(wC),mR(1-wC),mSAPI(wSAPI), mMaster(NULL), mState(LAPDStateUnused), mT200(T200ms), mIdleFrame(/*DATA*/) { // sanity checks assert(mC<2); //assert(mSAPI<4); assert(mSAPI == SAPI0 || mSAPI == SAPI3); // SAPI 1 and 2 are never used in the spec. //releaseLink(true,L3_RELEASE_INDICATION); // (pat) This sends release in both directions, which is undesirable at initialization. clearCounters(); // Set the idle frame as per GSM 04.06 5.4.2.3. mIdleFrame.fillField(8*0,(mC<<1)|1,8); // address mIdleFrame.fillField(8*1,3,8); // control mIdleFrame.fillField(8*2,1,8); // length if (gConfig.getBool("GSM.Cipher.ScrambleFiller")) mIdleFrame.randomizeFiller(8*4); } void L2LAPDm::writeL1(const L2Frame& frame) { OBJLOG(DEBUG) <<"L2LAPDm::writeL1 " << frame; if (!mL2Downstream) return; // only possible during initialization, and maybe not even then. // It is tempting not to lock this, but if we don't, // the ::open operation can result in contention in L1. ScopedLock lock(mL1Lock); //mL2Downstream->sapWriteHighSide(frame); // (pat) This blocks in L1Encoder::transmit until the time has passed. mL2Downstream->writeToL1(frame); // (pat) This blocks in L1Encoder::transmit until the start time has passed. } // For FACCHL2 and SDCCHL2 we can dispense with the service loop and send L3 messages // directly to the L3 state machine. Prior to l3rewrite this was done by DCCHDispatchMessage. void L2LAPDm::writeL3(L3Frame *frame) { OBJLOG(DEBUG) << LOGVAR(frame->size())<primitive()); //mL3Out.write(frame); mL2Downstream->writeToL3(frame); } // For SACCH we retain the L2 service loop to handle measurement reports and power levels. // If SACCHLogicalChannel::serviceLoop() does not handle the message, then it will come here and go up to Layer3. //void SACCHL2::writeL3(L3Frame *frame) //{ // OBJLOG(DEBUG) << LOGVAR(frame->size()); // mL3Out.write(frame); //} void L2LAPDm::writeL1NoAck(const L2Frame& frame) { // Caller need not hold mLock. OBJLOG(DEBUG) <<"L2LAPDm::writeL1NoAck " << frame; writeL1(frame); } // This writes something that expects an ACK. It does not write an ACK. void L2LAPDm::writeL1Ack(const L2Frame& frame) { // Caller should hold mLock. (pat) Because we are modifying mSentFrame. // (pat) Lock is held when called from sendMultiFrameData and sendUFrameDISC, but not sendUFrameSABM. Why? I'm fixing it. // GSM 04.06 5.4.4.2 OBJLOG(DEBUG) <<"L2LAPDm::writeL1Ack " << frame; frame.copyTo(mSentFrame); mSentFrame.primitive(frame.primitive()); writeL1(frame); mT200.set(T200()); } void L2LAPDm::waitForAck() { // Block until any pending ack is received. // Caller should hold mLock. //OBJLOG(DEBUG) <<"L2LAPDm::waitForAck state=" << mState << " VS=" << mVS << " VA=" << mVA; OBJLOG(DEBUG) <<"L2LAPDm::waitForAck "; // OBJLOG prints the entire LAPDm object now. while (true) { if (mState==LinkReleased) break; if ((mState==ContentionResolution) && (mVS==mVA)) break; if ((mState==LinkEstablished) && (mVS==mVA)) break; // HACK -- We should not need a timeout here. // (pat) When we send a RELEASE the calling thread blocks FOREVER so this timeout // is needed to prevent the channel from hanging. It will be gone for 30 secs. mAckSignal.wait(mLock,N200()*T200ms); //OBJLOG(DEBUG) <<"L2LAPDm::waitForAck state=" << mState << " VS=" << mVS << " VA=" << mVA; OBJLOG(DEBUG) <<"L2LAPDm::waitForAck "; } } // (pat 4-2014) The high side sends a L3ChannelRelease and then a RELEASE or HARDRELEASE. // The handset may send the UFrameDISC before we send ours, in which case this sends a RELEASE // down to L1FEC which does a close() on the main channel, and subsequently we get the RELEASE from above // and send the UFrameDISC resulting in a "writeHighSide sending on non-active channel" message. // Also note that the handset may continue to send up measurement reports for some time after the main channel is closed. // I dont see where we make the SACCH non active; I think we just wait for valid messages to stop coming and it times out via T3109. void L2LAPDm::releaseLink(bool notifyL3, Primitive releaseType) { OBJLOG(DEBUG) <L2 " << frame; devassert(0); } } void L2LAPDm::l2dlWriteLowSide(const L2Frame& frame) { OBJLOG(DEBUG) << frame; mL1In.write(new L2Frame(frame)); } void L2LAPDm::lapServiceLoop() { mLock.lock(); while (mRunning) { // Block for up to T200 expiration, then check T200. // Allow other threads to modify state while blocked. // Add 2 ms to prevent race condition due to roundoff error. unsigned timeout = mT200.remaining() + 2; // If SAP0 is released, other SAPs need to release also. // (pat 6-2014) This seems to be a bug fix to compensate for improper initialization, // and it depended on the mMaster only being set on the host channel, // since if you set it on sacch it will keep anything from ever going through since sap0 is always in idle (LinkReleased) state. // if (mMaster) { // if (mMaster->mState==LinkReleased) { // OBJLOG(DEBUG) << "master release"; // mState=LinkReleased; // } // } if (!mT200.active()) { if (mState==LinkReleased) timeout=3600000; else timeout = T200(); // currently 3.6sec (pat) Looks like .9 secs on TCH,SDCCH and 4*.9 on SACCH. Why? // (pat) FIXME: T3111 must be longer that this, right? } OBJLOG(DEBUG) << "read blocking up to " << timeout << " ms ";//, state=" << mState; mLock.unlock(); // FIXME -- If the link is released, there should be no timeout at all. L2Frame* frame = mL1In.read(timeout); LOG(DEBUG); mLock.lock(); if (frame!=NULL) { //OBJLOG(DEBUG) << "state=" << mState << " received " << *frame; OBJLOG(DEBUG) << " received " << *frame; receiveFrame(*frame); delete frame; } if (mT200.expired()) T200Expiration(); } mLock.unlock(); } void L2LAPDm::T200Expiration() { // Caller should hold mLock. // vISDN datalink.c:timer_T200. // GSM 04.06 5.4.1.3, 5.4.4.3, 5.5.7, 5.7.2. OBJLOG(INFO) << "state=" << mState << " RC=" << mRC; mT200.reset(); switch (mState) { case AwaitingRelease: // GSM 4.06 5.4.4.3 // (pat) 5.4.4.3 says retransmit the DISC. // old: releaseLink(MDL_ERROR_INDICATION); break; // Fall Through case ContentionResolution: case LinkEstablished: case AwaitingEstablish: // (pat) SABM sent. Should only happen on SAPI 3 since MS establishes link SAPI 0 // (pat) 4.06 5.5.7: L2 is supposed to query L3 whether to release link or attempt re-establishment, but we just release. // It either does nothing ("local end release") or starts a normal release procedure which would send a DISC. if (mRC>N200()) abnormalRelease(false); // (pat) do not send DM. If anything, we would send DISC. else retransmissionProcedure(); break; default: break; } } // (pat) Receive a frame from layer 1. void L2LAPDm::receiveFrame(const GSM::L2Frame& frame) { OBJLOG(DEBUG) << frame; // Caller should hold mLock. // Accept and process an incoming frame on the L1->L2 interface. // See vISDN datalink.c:lapd_dlc_recv for another example. // Since LAPDm is a lot simpler than LAPD, there are a lot fewer primitives. // FIXME -- This is a HACK to fix channels that get stuck in wierd states. // But if channels are stuck in wierd states, it means there's a bug somehwere. /* if (stuckChannel(frame)) { OBJLOG(ERR) << "detected stuck channel, releasing in L2"; abnormalRelease(); return; } */ switch (frame.primitive()) { //case ESTABLISH: case PH_CONNECT: // This primitive means the first L2 frame is on the way. // (pat) We dont send an ESTABLISH primitive upstream to L3 until we get the SABM command. clearCounters(); mState = LinkReleased; // Idle state. break; case L2_DATA: // Dispatch on the frame type. switch (frame.controlFormat()) { case L2Control::IFormat: receiveIFrame(frame); break; case L2Control::SFormat: receiveSFrame(frame); break; case L2Control::UFormat: receiveUFrame(frame); break; } break; case HANDOVER_ACCESS: devassert(0); // This primitive should now be handled in L2. writeL3(new L3Frame(mSAPI,HANDOVER_ACCESS)); break; default: OBJLOG(ERR) << "unhandled primitive in L1->L2 " << frame; assert(0); } } void L2LAPDm::receiveUFrame(const L2Frame& frame) { // Also see vISDN datalink.c:lapd_socket_handle_uframe OBJLOG(DEBUG) << frame; switch (frame.UFrameType()) { case L2Control::SABMFrame: receiveUFrameSABM(frame); break; case L2Control::DMFrame: receiveUFrameDM(frame); break; case L2Control::UIFrame: receiveUFrameUI(frame); break; case L2Control::DISCFrame: receiveUFrameDISC(frame); break; case L2Control::UAFrame: receiveUFrameUA(frame); break; default: OBJLOG(NOTICE) << " could not parse U-Bits " << frame; unexpectedMessage(1); return; } } void L2LAPDm::receiveUFrameSABM(const L2Frame& frame) { // Caller should hold mLock. // Process the incoming SABM command. // GSM 04.06 3.8.2, 5.4.1 // Q.921 5.5.1.2. // Also borrows from vISDN datalink.c:lapd_socket_handle_uframe_sabm. //OBJLOG(INFO) << "state=" << mState << " " << frame; OBJLOG(INFO) << frame; // Ignore frame if P!=1. // See GSM 04.06 5.4.1.2. if (!frame.PF()) return; // Dispatch according to current state. // BTW, LAPDm can always enter multiframe mode when requested, // so that's another big simplification over ISDN/LAPD. switch (mState) { case LinkReleased: // GSM 04.06 5.4.5, 5.4.1.2, 5.4.1.4 clearCounters(); mEstablishmentInProgress = true; // Tell L3 what happened. writeL3(new L3Frame(mSAPI,L3_ESTABLISH_INDICATION)); if (frame.L()) { // Presence of an L3 payload indicates contention resolution. // GSM 04.06 5.4.1.4. if (mSAPI==0) mState=ContentionResolution; // only SAP 0 is permitted to enter Contention Resolution mContentionCheck = frame.sum(); writeL3(new L3Frame(mSAPI,frame.L3Part(),L3_DATA)); // Echo back payload. sendUFrameUA(frame); } else { mState=LinkEstablished; sendUFrameUA(frame.PF()); } break; case ContentionResolution: // (pat) FIXME: This is wrong... Read the spec. // GSM 04.06 5.4.1.4 // This guards against the remote possibility that two handsets // are sending on the same channel at the same time. // vISDN's LAPD doesn't need/do this since peers are hard-wired if (frame.sum()!=mContentionCheck) break; mState=LinkEstablished; sendUFrameUA(frame); break; case AwaitingEstablish: // Huh? This would mean both sides sent SABM at the same time/ // That should not happen in GSM. sendUFrameUA(frame.PF()); OBJLOG(WARNING) << "simulatenous SABM attempts"; break; case AwaitingRelease: // If we are awaiting release, we will not enter ABM. // So we send DM to indicate that. sendUFrameDM(frame.PF()); break; case LinkEstablished: // Latency in contention resolution, GSM 04.06 5.4.2.1. if (mEstablishmentInProgress) { if (frame.L()) sendUFrameUA(frame); else sendUFrameUA(frame.PF()); break; } if (frame.L()) { // (pat) Means SABM includes a command, which would attempt to re-establish contention resolution. abnormalRelease(true); // (pat) send DM break; } // Re-establishment procedure, GSM 04.06 5.6.3. // This basically resets the ack engine. // The most common reason for this is failed handover. sendUFrameUA(frame.PF()); clearCounters(); break; default: unexpectedMessage(2); return; } } void L2LAPDm::receiveUFrameDISC(const L2Frame& frame) { // Caller should hold mLock. OBJLOG(INFO) << frame; mEstablishmentInProgress = false; switch (mState) { case AwaitingEstablish: releaseLink(true,L3_RELEASE_INDICATION); break; case LinkReleased: // GSM 04.06 5.4.5 sendUFrameDM(frame.PF()); releaseLink(false,(Primitive)0); break; case ContentionResolution: case LinkEstablished: // Shut down the link and ack with UA. // GSM 04.06 5.4.4.2. sendUFrameUA(frame.PF()); releaseLink(true,L3_RELEASE_INDICATION); break; case AwaitingRelease: // We can arrive here if both ends sent DISC at the same time. // GSM 04.06 5.4.6.1. sendUFrameUA(frame.PF()); releaseLink(true,L3_RELEASE_CONFIRM); break; default: unexpectedMessage(3); return; } } void L2LAPDm::receiveUFrameUA(const L2Frame& frame) { // Caller should hold mLock. // GSM 04.06 3.8.8 // vISDN datalink.c:lapd_socket_handle_uframe_ua OBJLOG(INFO) << frame; if (!frame.PF()) { // (pat) TODO: This looks wrong. GSM 4.06 5.4.1.2 quote: 'A UA response with the F bit set to "0" shall be ignored.' unexpectedMessage(4); return; } switch (mState) { case AwaitingEstablish: // We sent SABM and the peer responded. clearCounters(); mState = LinkEstablished; mAckSignal.signal(); // We used to use L3_ESTABLISH_INDICATION instead of L3_ESTABLISH_CONFIRM since there is no reason to distinguish between them. writeL3(new L3Frame(mSAPI,L3_ESTABLISH_CONFIRM)); break; case AwaitingRelease: // We sent DISC and the peer responded. releaseLink(true,L3_RELEASE_CONFIRM); break; case LinkEstablished: // Pat added: This is probably just a duplicate SABM establishment acknowledgement. // We could check more carefully if that is true, but who cares... break; case ContentionResolution: // (pat 3-13-2014) It is a bug to get a UA in ContentionResolution, but the Blackberry does this sometimes // if you do two testcalls back to back, so dont abort the link because of it... // (pat 4-7-2014) Update: The problem occurs when you do an endcall followed by a testcall in less than 30 seconds. // (pat 6-2014) and that may be because we did not deactivate SACCH properly at the end of the call? LOG(NOTICE) << "Ignoring unexpected LAPDm UA frame in ContentionResolution mode, which may be a bug in the MS."; return; default: unexpectedMessage(5); return; } } // (pat) Disconnect Mode response. Indicates some error in the peer. Per 5.4.2 table 7 we give up. void L2LAPDm::receiveUFrameDM(const L2Frame& frame) { // Caller should hold mLock. OBJLOG(INFO) << frame; // GSM 04.06 5.4.6.3: "A received DM with F=0 colliding with SABM or DISC is ignored." if (!frame.PF()) return; // Because we do not support multiple TEIs in LAPDm, // and because we should never get DM in response to SABM, // this procedure is much simpler that in vISDN LAPD. // Unlike LAPD, there's also no reason for LAPDm to not be // able to establish ABM, so if we get this message // we know the channel is screwed up. // 5.4.6.2: switch (mState) { case LAPDStateUnused: case LinkReleased: // GSM 04.06 5.4.5: In idle state all "other" frame types ignored. return; case AwaitingRelease: // GSM 4.06 5.4.4.2 releaseLink(true,L3_RELEASE_CONFIRM); return; case AwaitingEstablish: case LinkEstablished: case ContentionResolution: // GSM 4.06 4.1.3.5: Unsolicited DM response is an error. // Rel. 8 of 4.06, Sect. 5.4.1.2 says reset T200 and send up a RELEASE_INDICATION to L3. // Confusing b/c Sect. 5.4.2.2 suggests (not requires) sending an MDL_ERROR_INDICATION // But nothing about releasing the link, so don't do it! //releaseLink(true,MDL_ERROR_INDICATION); mT200.set(T200()); writeL3(new L3Frame(mSAPI,L3_RELEASE_INDICATION)); //releaseLink(true,L3_RELEASE_INDICATION); return; } } void L2LAPDm::receiveUFrameUI(const L2Frame& frame) { // The zero-length frame is the idle frame. if (frame.L()==0) return; OBJLOG(INFO) << frame; writeL3(new L3Frame(mSAPI,frame.alias().tail(24),L3_UNIT_DATA)); } void L2LAPDm::receiveSFrame(const L2Frame& frame) { // Caller should hold mLock. // See GSM 04.06 5.4.1.4. mEstablishmentInProgress = false; switch (frame.SFrameType()) { case L2Control::RRFrame: receiveSFrameRR(frame); break; case L2Control::REJFrame: receiveSFrameREJ(frame); break; default: unexpectedMessage(6); return; } } void L2LAPDm::receiveSFrameRR(const L2Frame& frame) // RR == Receive Ready. { // Caller should hold mLock. OBJLOG(INFO) << frame << LOGVAR2("PF",frame.PF()); // GSM 04.06 3.8.5. // Q.921 3.6.6. // vISDN datalink.c:lapd_handle_sframe_rr // Again, since LAPDm allows only one outstanding frame // (k=1), this is a lot simpler than in LAPD. switch (mState) { case ContentionResolution: mState = LinkEstablished; // continue to next case... case LinkEstablished: // "inquiry response procedure" // Never actually seen that happen in GSM... // (pat) It seems to be sent by the Blackberry after a channel change from SDCCH to TCH, // and it is sent on FACCH every 1/2 second forever because we dont answer. // PF is the Poll/Final bit 3.5.1. true means sender is polling receiver; receiver responds with PF=true. #if ORIGINAL if ((frame.CR()!=mC) && (frame.PF())) { sendSFrameRR(true); } #else if ((frame.CR()==mC) && (frame.PF())) { // If it is a command 5.8.1 says return an S RR frame. sendSFrameRR(true); // Must return a frame with P==1. } #endif processAck(frame.NR()); break; default: // ignore return; } } void L2LAPDm::receiveSFrameREJ(const L2Frame& frame) { // Caller should hold mLock. OBJLOG(INFO) << frame; // GSM 04.06 3.8.6, 5.5.4 // Q.921 3.7.6, 5.6.4. // vISDN datalink.c:lapd_handle_s_frame_rej. switch (mState) { case ContentionResolution: mState = LinkEstablished; // continue to next case... case LinkEstablished: // FIXME -- The spec says to do this but it breaks multiframe transmission. //mVS = mVA = frame.NR(); processAck(frame.NR()); if (frame.PF()) { if (frame.CR()!=mC) sendSFrameRR(true); else { unexpectedMessage(7); return; } } // Since k=1, there's really nothing to retransmit, // other than what was just rejected, so just stop sending it. sendIdle(); break; default: // ignore break; } // Send an idle frame to clear any repeating junk on the channel. // (pat 5-2014) these sendIdle are unnecessary and possibly wrong. On SAPI 3 we send SIB5/SIB6 and on SAPI0 // the idle frame is sent by layer1 dispatch only if necessary. sendIdle(); } void L2LAPDm::receiveIFrame(const L2Frame& frame) { // Caller should hold mLock. // See GSM 04.06 5.4.1.4. mEstablishmentInProgress = false; OBJLOG(INFO) << " NS=" << frame.NS() << " NR=" << frame.NR() << " " << frame; // vISDN datalink.c:lapd_handle_iframe // GSM 04.06 5.5.2, 5.7.1 // Q.921 5.6.2, 5.8.1 switch (mState) { case ContentionResolution: mState=LinkEstablished; // continue to next case... case LinkEstablished: processAck(frame.NR()); if (frame.NS()==mVR) { mVR = (mVR+1)%8; bufferIFrameData(frame); sendSFrameRR(frame.PF()); } else { // GSM 04.06 5.7.1. // Q.921 5.8.1. sendSFrameREJ(frame.PF()); } case LinkReleased: // GSM 04.06 5.4.5 break; default: // ignore break; } } void L2LAPDm::sendSFrameRR(bool FBit) { // GSM 04.06 3.8.5. // The caller should hold mLock. L2Address address(mR,mSAPI); // C/R == 0, ie, this is a response. L2Control control(L2Control::SFormat,FBit,0); // (pat) 4.06 3.8.1: Sbits == 0 is supervisory "Receive Ready" static const L2Length length; L2Header header(address,control,length); header.control().NR(mVR); OBJLOG(INFO) << "F=" << FBit <sapWriteHighSide(outFrame); mL2Downstream->writeToL1(outFrame); } } void L2LAPDm::text(std::ostream&os) const { os <<" LAPDm(" <mState); } } os <<")"; } ostream& GSM::operator<<(ostream& os, L2LAPDm& thing) { thing.text(os); return os; } ostream& GSM::operator<<(ostream& os, L2LAPDm* thing) { thing->text(os); return os; } // vim: ts=4 sw=4