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::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::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::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::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::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::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::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;
	}
}