/* * Copyright 2008, 2009 Free Software Foundation, Inc. * * This software is distributed under the terms of the GNU Affero Public License. * See the COPYING file in the main directory for details. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 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. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ /* Many elements follow Daniele Orlandi's vISDN/Q.921 implementation, although no code is copied directly. */ /* 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 using namespace std; using namespace GSM; //#define NDEBUG ostream& GSM::operator<<(ostream& os, L2LAPDm::LAPDState state) { switch (state) { case L2LAPDm::LinkReleased: os << "LinkReleased"; break; case L2LAPDm::AwaitingEstablish: os << "AwaitingEstablish"; break; case L2LAPDm::AwaitingRelease: os << "AwaitingRelease"; break; case L2LAPDm::LinkEstablished: os << "LinkEstablished"; break; case L2LAPDm::ContentionResolution: os << "ContentionResolution"; break; default: os << "?" << (int)state << "?"; } return os; } void CCCHL2::writeHighSide(const GSM::L3Frame& l3) { OBJLOG(DEEPDEBUG) <<"CCCHL2::writeHighSide " << l3; assert(mDownstream); assert(l3.primitive()==UNIT_DATA); L2Header header(L2Length(l3.length())); mDownstream->writeHighSide(L2Frame(header,l3)); } L2LAPDm::L2LAPDm(unsigned wC, unsigned wSAPI) :mRunning(false), mC(wC),mR(1-wC),mSAPI(wSAPI), mMaster(NULL), mT200(T200ms), mIdleFrame(DATA) { // sanity checks assert(mC<2); assert(mSAPI<4); clearState(); // 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 } void L2LAPDm::writeL1(const L2Frame& frame) { OBJLOG(DEEPDEBUG) <<"L2LAPDm::writeL1 " << frame; //assert(mDownstream); if (!mDownstream) return; mL1Lock.lock(); mDownstream->writeHighSide(frame); mL1Lock.unlock(); } void L2LAPDm::writeL1NoAck(const L2Frame& frame) { // Caller need not hold mLock. OBJLOG(DEEPDEBUG) <<"L2LAPDm::writeL1NoAck " << frame; writeL1(frame); } void L2LAPDm::writeL1Ack(const L2Frame& frame) { // Caller should hold mLock. // GSM 04.06 5.4.4.2 OBJLOG(DEEPDEBUG) <<"L2LAPDm::writeL1Ack " << frame; frame.copyTo(mSentFrame); mSentFrame.primitive(frame.primitive()); writeL1(frame); mT200.set(); } void L2LAPDm::waitForAck() { // Block until any pending ack is received. // Caller should hold mLock. OBJLOG(DEBUG) <<"L2LAPDm::waitForAck state=" << mState << " VS=" << mVS << " VA=" << mVA; 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. mAckSignal.wait(mLock,N200()*T200ms); OBJLOG(DEBUG) <<"L2LAPDm::waitForAck state=" << mState << " VS=" << mVS << " VA=" << mVA; } } void L2LAPDm::releaseLink() { OBJLOG(DEBUG) << "mState=" << mState; // Caller should hold mLock. mState = LinkReleased; mEstablishmentInProgress = false; mAckSignal.signal(); if (mSAPI==0) writeL1(RELEASE); mL3Out.write(new L3Frame(RELEASE)); } void L2LAPDm::clearCounters() { OBJLOG(DEBUG) << "mState=" << mState; // Caller should hold mLock. // This is called upon establishment or re-establihment of ABM. mT200.reset(); mVS = 0; mVA = 0; mVR = 0; mRC = 0; mIdleCount=0; mRecvBuffer.clear(); discardIQueue(); } void L2LAPDm::clearState() { OBJLOG(DEBUG) << "mState=" << mState; // Caller should hold mLock. // Reset the state machine. clearCounters(); releaseLink(); } void L2LAPDm::processAck(unsigned NR) { // GSM 04.06 5.5.3, 5.7.2. // Q.921 5.6.3.2, 5.8.2. // Equivalent to vISDN datalink.c:lapd_ack_frames, // but much simpler for LAPDm. // Caller should hold mLock. OBJLOG(DEBUG) << "NR=" << NR << " VA=" << mVA << " VS=" << mVS; mVA=NR; if (mVA==mVS) { mRC=0; mT200.reset(); } mAckSignal.signal(); } void L2LAPDm::bufferIFrameData(const L2Frame& frame) { // Concatenate I-frames to form the L3 frame. /* GSM 04.06 5.5.2 states: When a data link layer entity is not in an own receiver busy condition and receives a valid I frame whose send sequence number is equal to the current receive state variable V(R), the data link layer entity shall: - if the M bit is set to "0", concatenate it with previously received frames with the M bit set to "1", if any, and pass the complete layer 3 message unit to the layer 3 entity using the primitive DL-DATA-INDICATION; - if the M bit is set to "1", store the information field of the frame and concatenate it with previously received frames with the M bit set to "1", if any (Note: no information is passed to the layer 3 entity); */ OBJLOG(DEBUG) << frame; if (!frame.M()) { // The last or only frame. if (mRecvBuffer.size()==0) { // The only frame -- just send it up. OBJLOG(DEBUG) << "single frame message"; mL3Out.write(new L3Frame(frame)); return; } // The last of several -- concat and send it up. OBJLOG(DEBUG) << "last frame of message"; mL3Out.write(new L3Frame(mRecvBuffer,frame.L3Part())); mRecvBuffer.clear(); return; } // One segment of many -- concat. // This is inefficient but simple. mRecvBuffer = L3Frame(mRecvBuffer,frame.L3Part()); OBJLOG(DEBUG) <<"buffering recvBuffer=" << mRecvBuffer; } void L2LAPDm::unexpectedMessage() { OBJLOG(NOTICE) << "mState=" << mState; // vISDN datalink.c:unexpeced_message // For LAPD, vISDN just keeps trying. // For LAPDm, just terminate the link. // Caller should hold mLock. abnormalRelease(); } void L2LAPDm::abnormalRelease() { // Caller should hold mLock. OBJLOG(INFO) << "state=" << mState; // GSM 04.06 5.6.4. // We're cutting a corner here that we'll // clean up when L3 is more stable. mL3Out.write(new L3Frame(ERROR)); sendUFrameDM(true); writeL1(ERROR); clearState(); } void L2LAPDm::retransmissionProcedure() { // Caller should hold mLock. // vISDN datalink.c:lapd_invoke_retransmission_procedure // GSM 04.08 5.5.7, bullet point (a) OBJLOG(DEBUG) << "VS=" << mVS << " VA=" << mVA << " RC=" << mRC; mRC++; writeL1(mSentFrame); mT200.set(); mAckSignal.signal(); } void L2LAPDm::open() { OBJLOG(DEBUG); mLock.lock(); if (!mRunning) { // We can't call this from the constructor, // since N201 may not be defined yet. mMaxIPayloadBits = 8*N201(L2Control::IFormat); mRunning = true; mUpstreamThread.start((void *(*)(void*))LAPDmServiceLoopAdapter,this); } mL3Out.clear(); mL1In.clear(); clearCounters(); mState = LinkReleased; mAckSignal.signal(); mLock.unlock(); if (mSAPI==0) sendIdle(); } void *GSM::LAPDmServiceLoopAdapter(L2LAPDm *lapdm) { lapdm->serviceLoop(); return NULL; } void L2LAPDm::writeHighSide(const L3Frame& frame) { OBJLOG(DEBUG) << frame; switch (frame.primitive()) { case UNIT_DATA: // Send the data in a single U-Frame. sendUFrameUI(frame); break; case DATA: // Send the data as a series of I-Frames. mLock.lock(); sendMultiframeData(frame); mLock.unlock(); break; case ESTABLISH: // GSM 04.06 5.4.1.2 // vISDN datalink.c:lapd_establish_datalink_procedure // The BTS side should never call this in SAP0. // See note in GSM 04.06 5.4.1.1. assert(mSAPI!=0 || mC==0); if (mState==LinkEstablished) break; mLock.lock(); clearCounters(); mState=AwaitingEstablish; mLock.unlock(); sendUFrameSABM(); break; case RELEASE: // GSM 04.06 5.4.4.2 // vISDN datalink.c:lapd_dl_release_request if (mState==LinkReleased) break; mLock.lock(); if (mState==LinkEstablished) waitForAck(); clearCounters(); mEstablishmentInProgress=false; mState=AwaitingRelease; mT200.set(); // HACK? // Send DISC and wait for UA. // Don't return until released. sendUFrameDISC(); waitForAck(); mLock.unlock(); break; case ERROR: // Forced release. mLock.lock(); abnormalRelease(); mLock.unlock(); break; case HARDRELEASE: mLock.lock(); clearState(); mLock.unlock(); break; default: OBJLOG(ERROR) << "unhandled primitive in L3->L2 " << frame; assert(0); } } void L2LAPDm::writeLowSide(const L2Frame& frame) { OBJLOG(DEBUG) << frame; mL1In.write(new L2Frame(frame)); } void L2LAPDm::serviceLoop() { 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. if (mMaster) { if (mMaster->mState==LinkReleased) mState=LinkReleased; } if (!mT200.active()) { if (mState==LinkReleased) timeout=3600000; else timeout = T200(); } 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); mLock.lock(); if (frame!=NULL) { OBJLOG(DEBUG) << "state=" << mState << " 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: releaseLink(); break; case ContentionResolution: case LinkEstablished: case AwaitingEstablish: if (mRC>N200()) abnormalRelease(); else retransmissionProcedure(); break; default: break; } } 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(ERROR) << "detected stuck channel, releasing in L2"; abnormalRelease(); return; } switch (frame.primitive()) { case ESTABLISH: // This primitive means the first L2 frame is on the way. clearCounters(); break; case 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; default: OBJLOG(ERROR) << "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(); 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; // 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. mL3Out.write(new L3Frame(ESTABLISH)); if (frame.L()) { // Presence of an L3 payload indicates contention resolution. // GSM 04.06 5.4.1.4. mState=ContentionResolution; mContentionCheck = frame.sum(); mL3Out.write(new L3Frame(frame.L3Part(),DATA)); // Echo back payload. sendUFrameUA(frame); } else { mState=LinkEstablished; sendUFrameUA(frame.PF()); } break; case ContentionResolution: // 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(WARN) << "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()) { abnormalRelease(); break; } // Re-establishment procedure, GSM 04.06 5.6.3. // This basically resets the ack engine. // We should not actually see this, as of rev 2.4. OBJLOG(WARN) << "reestablishment not really supported"; sendUFrameUA(frame.PF()); clearCounters(); break; default: unexpectedMessage(); return; } } void L2LAPDm::receiveUFrameDISC(const L2Frame& frame) { // Caller should hold mLock. OBJLOG(INFO) << "state=" << mState << " " << frame; mEstablishmentInProgress = false; switch (mState) { case AwaitingEstablish: clearState(); break; case LinkReleased: // GSM 04.06 5.4.5 sendUFrameDM(frame.PF()); clearState(); break; case ContentionResolution: case LinkEstablished: // Shut down the link and ack with UA. // GSM 04.06 5.4.4.2. sendUFrameUA(frame.PF()); clearState(); 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()); break; default: unexpectedMessage(); 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) << "state=" << mState << " " << frame; if (!frame.PF()) { unexpectedMessage(); return; } switch (mState) { case AwaitingEstablish: // We sent SABM and the peer responded. clearCounters(); mState = LinkEstablished; mAckSignal.signal(); mL3Out.write(new L3Frame(ESTABLISH)); break; case AwaitingRelease: // We sent DISC and the peer responded. clearState(); break; default: unexpectedMessage(); return; } } void L2LAPDm::receiveUFrameDM(const L2Frame& frame) { // Caller should hold mLock. OBJLOG(INFO) << "state=" << mState << " " << frame; // GSM 04.06 5.4.5 if (mState==LinkReleased) return; // GSM 04.06 5.4.6.3 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. clearState(); } void L2LAPDm::receiveUFrameUI(const L2Frame& frame) { // The zero-length frame is the idle frame. if (frame.L()==0) return; OBJLOG(INFO) << "state=" << mState << " " << frame; mL3Out.write(new L3Frame(frame,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(); return; } } void L2LAPDm::receiveSFrameRR(const L2Frame& frame) { // Caller should hold mLock. OBJLOG(INFO) << "state=" << mState << " " << frame; // 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... if ((frame.CR()!=mC) && (frame.PF())) { sendSFrameRR(true); } processAck(frame.NR()); break; default: // ignore return; } } void L2LAPDm::receiveSFrameREJ(const L2Frame& frame) { // Caller should hold mLock. OBJLOG(INFO) << "state=" << mState << " " << 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(); return; } } // Since k=1, there's really nothing to retransmit, // other than what was just rejected, so kust stop sending it. sendIdle(); break; default: // ignore break; } // Send an idle frame to clear any repeating junk on the channel. sendIdle(); } void L2LAPDm::receiveIFrame(const L2Frame& frame) { // Caller should hold mLock. // See GSM 04.06 5.4.1.4. mEstablishmentInProgress = false; OBJLOG(INFO) << "state=" << mState << " 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. OBJLOG(INFO) << "F=" << FBit << " state=" << mState << " VS=" << mVS << " VR=" << mVR; L2Address address(mR,mSAPI); L2Control control(L2Control::SFormat,FBit,0); static const L2Length length; L2Header header(address,control,length); header.control().NR(mVR); writeL1NoAck(L2Frame(header)); } void L2LAPDm::sendSFrameREJ(bool FBit) { // GSM 04.06 3.8.6. // The caller should hold mLock. OBJLOG(INFO) << "F=" << FBit << " state=" << mState; L2Address address(mR,mSAPI); L2Control control(L2Control::SFormat,FBit,2); static const L2Length length; L2Header header(address,control,length); header.control().NR(mVR); writeL1NoAck(L2Frame(header)); } void L2LAPDm::sendUFrameDM(bool FBit) { // The caller need not hold mLock. OBJLOG(INFO) << "F=" << FBit << " state=" << mState; L2Address address(mR,mSAPI); L2Control control(L2Control::UFormat,FBit,0x03); static const L2Length length; L2Header header(address,control,length); writeL1NoAck(L2Frame(header)); } void L2LAPDm::sendUFrameUA(bool FBit) { // The caller need not hold mLock. OBJLOG(INFO) << "F=" << FBit << " state=" << mState; L2Address address(mR,mSAPI); L2Control control(L2Control::UFormat,FBit,0x0C); static const L2Length length; L2Header header(address,control,length); writeL1NoAck(L2Frame(header)); } void L2LAPDm::sendUFrameUA(const L2Frame& frame) { // Send UA frame with a echoed payload. // This is used in the contention resolution procedure. // GSM 04.06 5.4.1.4. // The caller need not hold mLock. OBJLOG(INFO) << "state=" << mState << " " << frame; L2Address address(mR,mSAPI); L2Control control(L2Control::UFormat,frame.PF(),0x0C); L2Length length(frame.L()); L2Header header(address,control,length); writeL1NoAck(L2Frame(header,frame.L3Part())); } void L2LAPDm::sendUFrameSABM() { // GMS 04.06 3.8.2, 5.4.1 // The caller need not hold mLock. OBJLOG(INFO) << "state=" << mState; L2Address address(mC,mSAPI); L2Control control(L2Control::UFormat,1,0x07); static const L2Length length; L2Header header(address,control,length); writeL1Ack(L2Frame(header)); } void L2LAPDm::sendUFrameDISC() { // GMS 04.06 3.8.3, 5.4.4.2 // The caller need not hold mLock. OBJLOG(INFO) << "state=" << mState; L2Address address(mC,mSAPI); L2Control control(L2Control::UFormat,1,0x08); static const L2Length length; L2Header header(address,control,length); writeL1Ack(L2Frame(header)); } void L2LAPDm::sendUFrameUI(const L3Frame& l3) { // GSM 04.06 3.8.4, 5.3.2, not supporting the short header format. // The caller need not hold mLock. OBJLOG(INFO) << "state=" << mState << " payload=" << l3; L2Address address(mC,mSAPI); L2Control control(L2Control::UFormat,1,0x00); L2Length length(l3.length()); L2Header header(address,control,length); writeL1NoAck(L2Frame(header,l3)); } void L2LAPDm::sendMultiframeData(const L3Frame& l3) { // GSM 04.06 5.4.2 // Caller holds mLock. OBJLOG(INFO) << "state=" << mState << " payload=" << l3; // HACK -- Sleep before returning to prevent fast spinning // in SACCH L3 during release. if (mState==LinkReleased) { OBJLOG(ERROR) << "attempt to send DATA on released LAPm channel"; abnormalRelease(); sleepFrames(51); return; } mDiscardIQueue = false; size_t bitsRemaining = l3.size(); size_t sendIndex = 0; OBJLOG(DEBUG) << "sendIndex=" << sendIndex<< " bitsRemaining=" << bitsRemaining; while (bitsRemaining>0) { size_t thisChunkSize = bitsRemaining; bool MBit = false; if (thisChunkSize==0) break; if (thisChunkSize>mMaxIPayloadBits) { thisChunkSize = mMaxIPayloadBits; MBit = true; } // This is the point where we block and allow // the upstream thread to modify the LAPDm state. waitForAck(); // Did we abort multiframe mode while waiting? if (mDiscardIQueue) { OBJLOG(DEBUG) <<"aborting (discard)"; break; } if ((mState!=LinkEstablished) && (mState!=ContentionResolution)) { OBJLOG(DEBUG) << "aborting, state=" << mState; break; } OBJLOG(DEBUG) << "state=" << mState << " sendIndex=" << sendIndex << " thisChunkSize=" << thisChunkSize << " bitsRemaining=" << bitsRemaining << " MBit=" << MBit; sendIFrame(l3.segment(sendIndex,thisChunkSize),MBit); sendIndex += thisChunkSize; bitsRemaining -= thisChunkSize; } } void L2LAPDm::sendIFrame(const BitVector& payload, bool MBit) { // Caller should hold mLock. // GSM 04.06 5.5.1 OBJLOG(INFO) << "M=" << MBit << " VS=" << mVS << " payload=" << payload; // Lots of sanity checking. assert(mState!=LinkReleased); assert(payload.size() <= mMaxIPayloadBits); assert(payload.size()%8 == 0); if (MBit) assert(payload.size()==mMaxIPayloadBits); // Build the header and send it. L2Address address(mC,mSAPI); L2Control control(mVR,mVS,0); L2Length length(payload.size()/8,MBit); L2Header header(address,control,length); mVS = (mVS+1)%8; writeL1Ack(L2Frame(header,payload)); } bool L2LAPDm::stuckChannel(const L2Frame& frame) { // Check for excessive idling. if (frame.DCCHIdle()) mIdleCount++; else mIdleCount=0; return mIdleCount > maxIdle(); } // vim: ts=4 sw=4