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::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::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 SACCHLogicalChannel::writeToL1(const L2Frame& frame) { // The SAP may or may not be present, depending on the channel type. OBJLOG(DEBUG) << frame; switch (frame.primitive()) { case L2_DATA: mL1->writeHighSide(frame); break; default: OBJLOG(ERR) << "unhandled primitive " << frame.primitive() << " in L2->L1"; devassert(0); } }
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::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 XCCHL1Encoder::sendFrame(const L2Frame& frame) { OBJLOG(DEBUG) << "XCCHL1Encoder " << frame; // Make sure there's something down there to take the busts. if (mDownstream==NULL) { LOG(WARNING) << "XCCHL1Encoder with no downstream"; return; } // This comes from GSM 05.03 4.1 // Copy the L2 frame into u[] for processing. // GSM 05.03 4.1.1. //assert(mD.size()==headerOffset()+frame.size()); frame.copyToSegment(mU,headerOffset()); // Send to GSMTAP (must send mU = real bits !) gWriteGSMTAP(ARFCN(),TN(),mNextWriteTime.FN(), typeAndOffset(),mMapping.repeatLength()>51,false,mU); // Encode data into bursts OBJLOG(DEBUG) << "XCCHL1Encoder d[]=" << mD; mD.LSB8MSB(); OBJLOG(DEBUG) << "XCCHL1Encoder d[]=" << mD; encode(); // Encode u[] to c[], GSM 05.03 4.1.2 and 4.1.3. interleave(); // Interleave c[] to i[][], GSM 05.03 4.1.4. transmit(); // Send the bursts to the radio, GSM 05.03 4.1.5. }
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; } }
bool L2LAPDm::stuckChannel(const L2Frame& frame) { // Check for excessive idling. if (frame.DCCHIdle()) mIdleCount++; else mIdleCount=0; return mIdleCount > maxIdle(); }
void SDCCHL1Encoder::sendFrame(const L2Frame& frame) { OBJLOG(DEEPDEBUG) << "SDCCHL1Encoder " << frame; // Make sure there's something down there to take the busts. LOG(INFO) <<"SDCCHL1Encoder SENDFRAME"; if (mDownstream==NULL) { LOG(WARN) << "XCCHL1Encoder with no downstream"; return; } // This comes from GSM 05.03 4.1 // Copy the L2 frame into u[] for processing. // GSM 05.03 4.1.1. //assert(mD.size()==headerOffset()+frame.size()); frame.copyToSegment(mU,headerOffset()); OBJLOG(DEEPDEBUG) << "XCCHL1Encoder d[]=" << mD; mD.LSB8MSB(); OBJLOG(DEEPDEBUG) << "XCCHL1Encoder d[]=" << mD; encode(); // Encode u[] to c[], GSM 05.03 4.1.2 and 4.1.3. LOG(INFO) << "SDCCHL1ENCODER ENCODED"; interleave(); // Interleave c[] to i[][], GSM 05.03 4.1.4. LOG(INFO) << "SDCCHL1ENCODER INTERLEAVED"; encrypt(); // Encrypt i[][] to e[]. LOG(INFO) << "SDCCHL1ENCODER ENCRYPTED"; transmit(); // Send the bursts to the radio, GSM 05.03 4.1.5. LOG(INFO) << "SDCCHL1ENCODER TRANSMITTED"; // FIXME: is this FN OK, or do we need to back it up by 4? gWriteGSMTAP(ARFCN(),mTN,mPrevWriteTime.FN(),frame); }
void LoopbackSAPMux::writeHighSide(const L2Frame& frame) { OBJLOG(DEBUG) << "Loopback " << frame; // Substitute primitive L2Frame newFrame(frame); unsigned SAPI = frame.SAPI(); switch (frame.primitive()) { case ERROR: SAPI=0; break; case RELEASE: return; default: break; } // Because this is a test fixture, as assert here. // If this were not a text fixture, we would print a warning // and ignore the frame. assert(mUpstream[SAPI]); ScopedLock lock(mLock); mUpstream[SAPI]->writeLowSide(newFrame); }
void SAPMux::writeLowSide(const L2Frame& frame) { OBJLOG(DEBUG) << frame.SAPI() << " " << frame; unsigned SAPI = frame.SAPI(); bool data = frame.primitive()==DATA; if (data && (!mUpstream[SAPI])) { LOG(WARNING) << "received DATA for unsupported SAP " << SAPI; return; } if (data) { mUpstream[SAPI]->writeLowSide(frame); } else { // If this is a non-data primitive, copy it out to every SAP. for (int i=0; i<4; i++) { if (mUpstream[i]) mUpstream[i]->writeLowSide(frame); } } }
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::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::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 XCCHL1Encoder::writeHighSide(const L2Frame& frame) { switch (frame.primitive()) { case DATA: // Encode and send data. if (!active()) { LOG(INFO) << "XCCHL1Encoder::writeHighSide sending on non-active channel"; } resync(); sendFrame(frame); break; case ESTABLISH: // Open both sides of the link. // The phone is waiting to see the idle pattern. open(); if (sibling()) sibling()->open(); return; case RELEASE: // Normally, we get here after a DISC-DM handshake in L2. // Close both sides of the link, knowing that the phone will do the same. close(); if (sibling()) sibling()->close(); break; case HARDRELEASE: // This means that a higher layer released the link, // with certainty that is has been cleared of activity. close(); if (sibling()) sibling()->close(true); break; case ERROR: // If we got here, it means the link failed in L2 after several ack timeouts. // Close the tx side and just let the receiver L1 time out on its own. // Otherwise, we risk recycling the channel while the phone's still active. close(); break; default: LOG(ERR) << "unhandled primitive " << frame.primitive() << " in L2->L1"; 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::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 L2LogicalChannel::writeLowSide(const L2Frame& frame) { // If this is the first good frame of a new transaction, // stop T3101 and tell L2 we're alive down here. if (mT3101.active()) { mT3101.reset(); // Inform L3 that we are alive down here. // This does not block; goes to a InterthreadQueue L2LAPdm::mL1In sapWriteFromL1(L2Frame(PH_CONNECT)); } switch (frame.primitive()) { case HANDOVER_ACCESS: // Only send this on SAPI 0. writeToL3(new L3Frame(SAPI0,HANDOVER_ACCESS)); break; default: sapWriteFromL1(frame); break; } }
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; } }