bluetooth/btstack/l2cap/L2CapEnhancedDataController.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 19 Aug 2010 11:01:00 +0300
branchRCL_3
changeset 22 786b94c6f0a4
parent 0 29b1cd4cb562
permissions -rw-r--r--
Revision: 201031 Kit: 201033

// Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
//

#include <bluetooth/logger.h>
#include "btsockettimer.h"
#include "L2CapEnhancedDataController.h"
#include "L2CapSDUQueue.h"
#include "l2signalmgr.h"
#include "l2util.h"
#include "L2CapDebugControlInterface.h"

#ifdef __FLOG_ACTIVE
_LIT8(KLogComponent, LOG_COMPONENT_L2CAP_DATA_CONTROLLER);
#endif

using namespace L2CapDataUtils;


CL2CapStreamingController::CL2CapStreamingController(TL2CAPPort aLocalCID,
													 TL2CAPPort aRemoteCID,
													 CL2CAPMux& aMuxer,
													 CL2CapSDUQueue& aSDUQueue,
													 TL2CapDataControllerConfig* aConfig)
 :	CL2CapBasicDataController(aLocalCID, aRemoteCID, aMuxer, aSDUQueue, aConfig, EFalse)
	{
	LOG_FUNC
	}

CL2CapStreamingController::~CL2CapStreamingController()
	{
	LOG_FUNC
	}

void CL2CapStreamingController::ProcessFlushTimerExpiry()
	{
	LOG_FUNC
	__ASSERT_DEBUG(EFalse, Panic(EL2CAPFlushingNotReallySupported));
	}

HL2CapPDU* CL2CapStreamingController::GetPduL()
	{
	LOG_FUNC
	// This is called from the signal manager.
	HL2CapPDU* pduToSend = NULL;

	pduToSend = iSDUQueue.GetPDU();

	if(pduToSend)
		{
		pduToSend->DeliverOutgoingPDU(*this);
		}
	
    if (iSDUQueue.HavePDUToSend())
        {
        iMuxer.PDUAvailable();
        }
	
	return pduToSend;
	}

void CL2CapStreamingController::HandleIncomingIFrameL(RMBufChain& aIFrame)
	{
	LOG_FUNC

	const TUint8 txSeq = HIFramePDU::TxSeqNumber(aIFrame);
	
	if (txSeq != iExpectedTxSeq)
		{
		// SDU currently being assembled is no good now. Need to tell the SDU Q to flush
		// it, otherwise if we lost the right amount of I-Frames, we would end up assembling
		// an FDU (Frankenstein Data Unit) - half SDU number n, half SDU number n+1.
		// Note that FDUs are still possible if 63 frames are lost. There'se no notion of
		// SDU checksumming in the protocol, so that can't be prevented.
		iSDUQueue.FlushCurrentlyAssembledSdu();
		}

	if (iSDUQueue.IsIncomingQueueFull())
		{
		// Make room for new data. From the spec:
		// "If there is no buffer space for the received I-frame an existing I-frame
		// (i.e. the oldest) shall be discarded (flushed) freeing up buffer space for
		// the new I-frame.	The discarded I-frame shall be marked as missing."
		iSDUQueue.FlushOldestIncomingSdu();
		}
	LEAVEIFERRORL(iSDUQueue.PutIFramePDU(aIFrame));

	iExpectedTxSeq = Mod64(txSeq + 1);

	// SDU Q sucks out the content, so we can free the frame whether it's been consumed or dropped.
	aIFrame.Free();
	}

TInt CL2CapStreamingController::HandleOutgoingIFrame(HIFramePDU* aIFrame)
	{
	LOG_FUNC
	aIFrame->SetPDUCID(iRemoteCID);
	aIFrame->SetTxSeqNumber(iNextTxSeq);
	iNextTxSeq = Mod64(iNextTxSeq + 1);

	// The Control field is zero-ed when the frame is created.

	aIFrame->CalculateAndSetFCS();

	return KErrNone;
	}

void CL2CapStreamingController::HandlePduSendComplete(HL2CapPDU& aPdu)
	{
	LOG_FUNC
	delete &aPdu;
	}

void CL2CapStreamingController::HandlePduSendError(HL2CapPDU& aPdu)
	{
	LOG_FUNC
	delete &aPdu;
	}


RL2CapErtmTimerManager::RL2CapErtmTimerManager(CL2CapEnhancedReTxController& aClient)
 :	RL2CapRetransmissionModeTimerManager(aClient),
 	iLocalBusyDelayTimerRunning(EFalse)
	{
	LOG_FUNC
	}

void RL2CapErtmTimerManager::Close()
	{
	LOG_FUNC
	StopLocalBusyDelayTimer();
	RL2CapRetransmissionModeTimerManager::Close();
	}

void RL2CapErtmTimerManager::StartLocalBusyDelayTimer()
	{
	LOG_FUNC
	// Shouldn't happen, but no big deal if it does.
	__ASSERT_DEBUG(!iLocalBusyDelayTimerRunning, Panic(EL2CAPInvalidDataControllerTimerState));

	if (!iLocalBusyDelayTimerRunning)
		{
		TCallBack cb(LocalBusyDelayTimerExpired, this);
		iLocalBusyDelayTimerEntry.Set(cb);
	
		// Set the timeout. The value is in Milliseconds.
		BTSocketTimer::Queue((iClient.PeerRetransmissionTimeout() * 1000) / KLocalBusyDelayTimerDenominator,
							 iLocalBusyDelayTimerEntry);
		iLocalBusyDelayTimerRunning = ETrue;
		}
	}

void RL2CapErtmTimerManager::StopLocalBusyDelayTimer()
	{
	LOG_FUNC
	if (iLocalBusyDelayTimerRunning)
		{
		BTSocketTimer::Remove(iLocalBusyDelayTimerEntry);
		iLocalBusyDelayTimerRunning = EFalse;
		}
	}

void RL2CapErtmTimerManager::HandleLocalBusyDelayTimerExpired()
	{
	LOG_FUNC
	iLocalBusyDelayTimerRunning = EFalse;
	static_cast<CL2CapEnhancedReTxController&>(iClient).LocalBusyDelayTimerExpired();
	}

/*static*/ TInt RL2CapErtmTimerManager::LocalBusyDelayTimerExpired(TAny* aTimerMan)
	{
	LOG_STATIC_FUNC
	RL2CapErtmTimerManager* timerMan = reinterpret_cast<RL2CapErtmTimerManager*>(aTimerMan);
	timerMan->HandleLocalBusyDelayTimerExpired();
	return EFalse;
	}



RL2CapErtmUnacknowledgedIFrames::RL2CapErtmUnacknowledgedIFrames()
 :	iFrameList(_FOFF(HIFramePDU, iLink))
	{
	LOG_FUNC
	iFrameIndex.Reset();
	}

void RL2CapErtmUnacknowledgedIFrames::Close()
	{
	LOG_FUNC

	// Some PDUs may hold a reference to this data controller.
	TDblQueIter<HIFramePDU> unackedIter(iFrameList);
	HIFramePDU* pdu;

	while((pdu = unackedIter++) != NULL)
		{
		Remove(*pdu);
		if (!pdu->IsAwaitingHciCompletion())
			{
			delete pdu;
			}
		else
			{
			// Will delete itself on HCI completion.
			pdu->DeregisterPduOwner();
			}
		}
	}


CL2CapErtmDataTransmitter* CL2CapErtmDataTransmitter::NewL(CL2CapEnhancedReTxController& aController)
	{
	LOG_STATIC_FUNC
	return new (ELeave) CL2CapErtmDataTransmitter(aController);
	}

CL2CapErtmDataTransmitter::~CL2CapErtmDataTransmitter()
	{
	LOG_FUNC
	iUnackedIFrames.Close();
	}

CL2CapErtmDataTransmitter::CL2CapErtmDataTransmitter(CL2CapEnhancedReTxController& aController)
 :	iController(aController),
	iNextTxSeq(0),
	iExpectedAckSeq(0),
	iRemoteBusy(EFalse),
	iSRejActioned(EFalse),
	iSRejSaveReqSeq(0),
	iWaitAckStatePending(EFalse),
	iInWaitAckState(EFalse)
	{
	LOG_FUNC
	}

void CL2CapErtmDataTransmitter::HandleIncomingIFrame(RMBufChain& aIFrame)
	{
	LOG_FUNC
	LOG2(_L("NextTxSeq=%d, ExpectedAckSeq = %d"), iNextTxSeq, iExpectedAckSeq)

	const TUint8 reqSeq = HIFramePDU::ReqSeqNumber(aIFrame);
	const TBool final = HIFramePDU::FinalBit(aIFrame);

	HandleReqSeqAck(reqSeq);
	if (final)
		{
		HandleFinalAck();
		}
	}

void CL2CapErtmDataTransmitter::HandleIncomingSFrameL(RMBufChain& aSFrame)
	{
	LOG_FUNC
	LOG2(_L("NextTxSeq=%d, ExpectedAckSeq = %d"), iNextTxSeq, iExpectedAckSeq)

	const TUint8 reqSeq = HSFramePDU::ReqSeqNumber(aSFrame);
	const TSupervisoryFunction function = HSFramePDU::SupervisoryFunction(aSFrame);
	const TBool final = HSFramePDU::FinalBit(aSFrame);
	const TBool poll = HSFramePDU::PollBit(aSFrame);

	// An S-Frame [SREJ] with the POLL=0 does NOT acknowledge any I-Frames.
	if (!(function == ESelectiveReject && !poll))
		{
		HandleReqSeqAck(reqSeq);
		}
	else // SREJ [P=0]
		{
		// The remote detected packet loss itself, so restart the retransmission timer -
		// - SREJ -> I-Frame -> RR roundtrip time is too long and would almost always
		// cause it to expire if we didn't (most probably unnecessarily).
		if (TimerMan().IsAckTimerRunning())
			{
			TimerMan().StartAckTimer();
			}
		}

	switch (function)
		{
		case EReceiverNotReady:
			iRemoteBusy = ETrue;
			TimerMan().StopAckTimer();
			if (final && iInWaitAckState)
				{
				iInWaitAckState = EFalse;
				// We'll start retransmitting when we get RR from the peer. 
				}
			break;
	
		case EReceiverReady:
			if (final)
				{
				HandleFinalAck();
				}
			// iRemoteBusy cleared at the bottom of the function.
			break;
	
		case EReject:
			// A REJ should be ignored if we're in WAIT_ACK and the Final bit in the
			// frame is 0. We'll receive a Final packet in a while anyway, and that
			// packet is guaranteed to contain the same ReqSeq, because the remote
			// can't acknowledge anything new until we retransmit, and we won't
			// retransmit until we receive the Final ack.
			if (!(iInWaitAckState && !final))
				{
				RetransmitUnackedIFrames();
	
				TimerMan().StopAckTimer();
				// The timer will be started again when the first retransmitted I-Frame
				// is completed.
				}
	
			if (final)
				{
				HandleFinalAck();
				}
			// iRemoteBusy cleared at the bottom of the function.
			break;
			
		case ESelectiveReject:
			HandleIncomingSRejL(aSFrame);
			// iRemoteBusy cleared at the bottom of the function.
			break;
		}

	// Anything other than RNR means remote isn't busy anymore.
	if (iRemoteBusy && function != EReceiverNotReady)
		{
		__ASSERT_DEBUG(!TimerMan().IsAckTimerRunning(), Panic(EL2CAPAckTimerRunningWhenRemoteBusy));
		iRemoteBusy = EFalse;
		if (!iUnackedIFrames.IsEmpty())
			{
			TimerMan().StartAckTimer();
			}
		LOG(_L("Exitted RemoteBusy condition"))
		}
	}

void CL2CapErtmDataTransmitter::HandleIncomingSRejL(RMBufChain& aSRejFrame)
	{
	LOG_FUNC

	const TUint8 reqSeq = HSFramePDU::ReqSeqNumber(aSRejFrame);
	const TSupervisoryFunction function = HSFramePDU::SupervisoryFunction(aSRejFrame);
	const TBool final = HSFramePDU::FinalBit(aSRejFrame);
	const TBool poll = HSFramePDU::PollBit(aSRejFrame);

	HIFramePDU* requestedIFrame = iUnackedIFrames[reqSeq];
	// If the ReqSeq is invalid then it should've been caught earlier.
	__ASSERT_ALWAYS(requestedIFrame != NULL, Panic(EL2CAPSRejReqSeqNotOnUnackedList));

	if (poll)
		{
		// Set the Final bit directly here to make sure it's in the right frame.
		requestedIFrame->SetFinalBit(ETrue);
		SendOrPendL(*requestedIFrame);

		if (iController.IsPollOutstanding())
			{
			iSRejActioned = ETrue;
			iSRejSaveReqSeq = reqSeq;
			// If Poll is set, then Final must be 0, so we don't have to check for it.
			}
		} // Poll = 1
	else
		{
		// If peer is in SREJ_SENT when it receives the Poll, then it needs to respond
		// with an SREJ[F=1]. This SREJ may contain a ReqSeq it has already sent in the
		// previous SREJ[F=0], and we need to guard against this on our side, so that we
		// don't retransmit a packet twice - it could cause the channel to be closed, as
		// the second copy would be outside of peer's receive window and thus invalid.
		TBool duplicateWithFinal = EFalse;

		if (iController.IsPollOutstanding())
			{
			if (final)
				{
				if (iSRejActioned && iSRejSaveReqSeq == reqSeq)
					{
					// Peer has sent us a SREJ F=0 for the same frame before, need to
					// ignore this SREJ.
					duplicateWithFinal = ETrue;
					LOG1(_L("Received a duplicate SREJ with ReqSeq=%d with Final=1, will drop it"),
						 reqSeq)
					}

				if (!iUnackedIFrames.IsEmpty() && !iRemoteBusy)
					{
					TimerMan().StartAckTimer();
					}

				iSRejActioned = EFalse;
				iInWaitAckState = EFalse;
				// Don't go into ERetransmitAllUnackedFrames, even in WAIT_ACK:
				// - an SREJ[P=0,F=1] frame doesn't carry an acking ReqSeq, so we
				//   wouldn't know at which frame we should start the retransmission;
				// - the peer has just SREJed some I-Frames so there's a chance
				//   we're already retransmitting the stuff we timed out on.
				//   If we're not (which is possible if acknowledgments from the
				//   peer have been lost), then we'll need to wait for the
				//   next Retransmission Timeout and then retransmit (unless we
				//   we hit remote being in SREJ_SENT again, rinse-and-repeat then).
				OutgoingQ().SendPendingRetransmitIFramesL();
				}
			else
				{
				iSRejActioned = ETrue;
				iSRejSaveReqSeq = reqSeq;
				}
			} // waiting for final ack

		if (!duplicateWithFinal)
			{
			// Clean the F-bit in case it was set during previous transmission.
			requestedIFrame->SetFinalBit(EFalse);
			SendOrPendL(*requestedIFrame);
			}
		} // Poll bit = 0
	}

void CL2CapErtmDataTransmitter::HciCompletedIFrame(HIFramePDU& aIFrame)
	{
	LOG_FUNC
	if (aIFrame.Acked())
		{
		__ASSERT_DEBUG(iUnackedIFrames[aIFrame.TxSeqNumber()] == NULL,
					   Panic(EL2CAPAckedTxSeqFoundOnUnackedList));

		// This condition will happen extremely rarely or none at all on real hardware.
		// It does happen on the emulator with USB HCTL.
		LOG1(_L("I-Frame %d has been acknowledged before HCI completion"), aIFrame.TxSeqNumber())
		// Frame has been already sent and acked - skip putting it on the Send queue, just delete.
		delete &aIFrame;
		// We'll start the retransmission timer the first time a frame that hasn't been acked is
		// completed (the else branch below). 
		}
	else
		{
		__ASSERT_DEBUG(iUnackedIFrames[aIFrame.TxSeqNumber()] != NULL,
					   Panic(EL2CAPUnackedTxSeqNotFoundOnUnackedList));

		if (!TimerMan().IsAckTimerRunning() && !iRemoteBusy)
			{
			// It's the first frame sent out after a period of silence.
			// ... or it's the first frame of a retransmission.
			// ... or we can has a bug (LOL that's impossible, I coded this myself).
			TimerMan().StartAckTimer();
			}
		}
	}

void CL2CapErtmDataTransmitter::AckTimerExpired()
	{
	LOG_FUNC
	__ASSERT_DEBUG(!iUnackedIFrames.IsEmpty(), Panic(EL2CAPAckTimerExpiryWithoutPDUToSupervise));
	// Signal to GetPdu that we need to enter WAIT_ACK.
	iWaitAckStatePending = ETrue;
	iController.NotifyMuxerOfPdusToSendIfHaveSome();
	}

HIFramePDU* CL2CapErtmDataTransmitter::GetIFrameToSendL()
	{
	LOG_FUNC
	HIFramePDU* pduToSend = NULL;

	// Can't send data in WAIT_ACK.
	// We send data in RemoteBusy until the window fills up, but with the ack timer stopped.
	if (!iInWaitAckState)
		{
		if (iUnackedIFrames[iNextTxSeq] != NULL)
			{
			// Rreceived a REJ or Ack timer expired.
			pduToSend = iUnackedIFrames[iNextTxSeq];
			if (pduToSend->IsAwaitingHciCompletion())
				{
				pduToSend = NULL;
				}
			else
				{
				// In case it was set for the previous transmission. 
				pduToSend->SetFinalBit(EFalse);
				}
			}
		else if (HaveSpaceInOutgoingWindow())
			{
			pduToSend = static_cast<HIFramePDU*>(iController.SDUQueue().GetPDU());
			if (pduToSend != NULL)
				{
				pduToSend->SetTxSeqNumber(iNextTxSeq);
				// It's a new frame that hasn't been sent before, so we need to append it to the
				// unacked list. Once put on the list, it should never get delinked from it until
				// acknowledged, even during retransmissions.
				__ASSERT_DEBUG(iUnackedIFrames[iNextTxSeq] == NULL, Panic(EL2CAPNewTxSeqFoundOnUnackedList));
				iUnackedIFrames.Append(*pduToSend);
				}
			}

		if (pduToSend != NULL)
			{
			iNextTxSeq = Mod64(iNextTxSeq + 1);
			}
		} // state != WAIT_ACK (can transmit data)
	return pduToSend;
	}

TBool CL2CapErtmDataTransmitter::HaveSpaceInOutgoingWindow() const
	{
	LOG_FUNC
	TBool canSend = ETrue;
	if (Mod64(iNextTxSeq - iExpectedAckSeq) >= iController.Config().TXWindowSize())
		{
		canSend = EFalse;
		}
	return canSend;
	}

void CL2CapErtmDataTransmitter::HandleFinalAck()
	{
	LOG_FUNC

	if (iInWaitAckState)
		{
		iInWaitAckState = EFalse;
		RetransmitUnackedIFrames();
		}

	// Whether it was WAIT_ACK or WAIT_F, Ack timer was stopped because Monitor timer
	// was running.
	if (!iUnackedIFrames.IsEmpty() && !iRemoteBusy)
		{
		TimerMan().StartAckTimer();
		}
	}

void CL2CapErtmDataTransmitter::HandleReqSeqAck(TUint8 aReqSeq)
	{
	LOG_FUNC

	__ASSERT_DEBUG((aReqSeq == iExpectedAckSeq) || !iUnackedIFrames.IsEmpty(),
				   Panic(EL2CAPUnacknowledgedPdusMissingFromList));

	// See if any I-Frames have been acknowledged by the peer, slide the send window,
	// send new data if window space is now available, restart ack timer.

	if (!iUnackedIFrames.IsEmpty() && aReqSeq != iExpectedAckSeq)
		{
		const TBool wasWindowFull = !HaveSpaceInOutgoingWindow();

		TDblQueIter<HIFramePDU> iter(iUnackedIFrames.Iterator());
		HIFramePDU* pduPtr;
		TBool ackedPDUs = EFalse;

		__ASSERT_DEBUG(iUnackedIFrames.First()->TxSeqNumber() == iExpectedAckSeq,
					   Panic(EL2CAPOldestUnackedPduTxSeqNotMatchingExpectedAck));

		while ((pduPtr = iter++) != NULL)
			{
			if (InWindow(pduPtr->TxSeqNumber(), iExpectedAckSeq, Mod64(aReqSeq - 1)))
				{
				iUnackedIFrames.Remove(*pduPtr);
				pduPtr->SetAcked(ETrue);
				ackedPDUs = ETrue;

				if (!pduPtr->IsAwaitingHciCompletion())
					{
					delete pduPtr;
					}
				else
					{
					LOG1(_L("I-Frame %d has been acknowledged before HCI completion!"),
						 pduPtr->TxSeqNumber())
					// Will delete itself on HCI completion.
					pduPtr->DeregisterPduOwner();
					}
				} // pdu in acked window
			else
				{
				// The queue is sorted chronologically - the head is always the oldest PDU.
				// First PDU outside of the acknowledged window means we can stop the loop.
				break;
				}
			}

		if (iUnackedIFrames.IsEmpty())
			{
			TimerMan().StopAckTimer();
			}
		else if (ackedPDUs && !iRemoteBusy)
			{
			// This will actually REstart the timer.
			TimerMan().StartAckTimer();
			}

		iExpectedAckSeq = aReqSeq;
		LOG1(_L("New ExpectedAckSeq=%d"), aReqSeq)

		if (iController.IsOutgoingDataPathClosing() && iUnackedIFrames.IsEmpty())
			{
			iController.SignalOutgoingDataDeliveredToSduQ();
			}
		}
	}

void CL2CapErtmDataTransmitter::RetransmitUnackedIFrames()
	{
	LOG_FUNC
	LOG1(_L("Setting NextTxSeq to ExpectedAckSeq = %d"), iExpectedAckSeq)
	iNextTxSeq = iExpectedAckSeq;
	// This is important when exiting WAIT_ACK: forget about all I-Frames SREJ-requested
	// by the peer. We'll retransmit them as part of the whole bunch. And we can't transmit
	// the same frame twice, which would happen had we left these hanging around.
	OutgoingQ().CancelPendingRetransmitIFrames();
	}


TL2CapErtmMissingTxSeqs::TL2CapErtmMissingTxSeqs()
 :	iNumMissingTxSeqs(0),
	iExpectedRecvIdx(0),
	iResendIdx(0)
	{}

TBool TL2CapErtmMissingTxSeqs::IsTxSeqOnTheList(TUint8 aTxSeq) const
	{
	TBool found = EFalse;
	for (TInt i = 0; i < iNumMissingTxSeqs && !found; i++)
		{
		if (iMissingTxSeqs[i] == aTxSeq)
			{
			found = ETrue;
			}
		}
	return found;
	}

TBool TL2CapErtmMissingTxSeqs::ReceivedTxSeq(TUint8 aTxSeq)
	{
	LOG_FUNC
	TBool resendNeeded = EFalse;
	if (iMissingTxSeqs[iExpectedRecvIdx] == aTxSeq)
		{
		// This is the only path possible with the default of max. 1 SREJ at a time.
		iExpectedRecvIdx++;
		}
	else
		{
		// Should only fall here with the experimental KMaxSRejsInFlight > 1
		__ASSERT_DEBUG(KMaxSRejsInFlight > 1, Panic(EL2CAPMultiSRejPathHitWhenNotSoConfigured));

		LOG1(_L("SREJ resend path hit, TxSeq=%d"), aTxSeq);
#ifdef __FLOG_ACTIVE
		Log();
#endif
		resendNeeded = ETrue;

		// General idea: reshuffle the array to maintain chronological ordering, in which
		// we expect the requested I-Frames to come through. All I-Frames between iExpectedRecvIdx
		// and the one we're processing now are considered to have been lost and will be
		// re-requested, so we need to move them to the very end of the list.
		//
		// E.g.:
		// 0. Current list is 6 elements (iNumMissingTxSeqs == 6):
		//     32 33 | 20 21 45 46
		//   32 and 33 have been received (iExpectedRecvIdx == 2), we're still waiting for the
		//   rest.
		// 1. 45 comes through, we fall here. We need to move it to right after 33 and increase
		//   iExpectedRecvIdx:
		//     32 33 45 | 20 21 46
		// 2. But the order in the array implied that 20 & 21 were requested before 45 and
		//   haven't come through, so they must have been lost (remote has to respond to SREJs
		//   in order they are sent). Ergo, we need to resend the SREJs for 20 and 21, which in
		//   turn means we're now expecting them after all other frames, so finally:
		//     32 33 45 | 46 20 21

		// Find the index of the received TxSeq.
		TInt receivedIdx = iExpectedRecvIdx;
		while (iMissingTxSeqs[receivedIdx] != aTxSeq && receivedIdx < iNumMissingTxSeqs)
			{
			receivedIdx++;
			}

		const TInt numToResend = receivedIdx - iExpectedRecvIdx;
		LOG2(_L("Last received = %d, num SREJs to resend = %d"),
			 (iExpectedRecvIdx > 0 ? iMissingTxSeqs[iExpectedRecvIdx] : -1), numToResend);

		// To maintain the partitioning of the array we need to move the received
		// TxSeq towards the < iExpectedRecvIdx half, over the to-be-SREJed again TxSeqs.
		// (step 1 in the example)
		while (receivedIdx > iExpectedRecvIdx)
			{
			TUint8 tmp = iMissingTxSeqs[receivedIdx - 1];
			iMissingTxSeqs[receivedIdx - 1] = iMissingTxSeqs[receivedIdx];
			iMissingTxSeqs[receivedIdx] = tmp;
			receivedIdx--;
			}
		iExpectedRecvIdx++;

		// Now move the TxSeqs to be SREJed again onto the end of the list to maintain correct
		// ordering (step 2 in the example).
		// Note: this could be optimized a bit at least theoretically down to O(1) by
		// copying the TxSeqs to re-SREJ out to a temp array, then moving the ones currently
		// at the end, and then copying the ones from the temp array back in at the end.
		// Note 2: this is the first time I've used bubble sort in my carreer. Honest.
		for (TInt i = 0; i < numToResend; i++)
			{
			for (TInt bubble = iExpectedRecvIdx; bubble < iNumMissingTxSeqs - 1; bubble++)
				{
				TUint8 tmp = iMissingTxSeqs[bubble];
				iMissingTxSeqs[bubble] = iMissingTxSeqs[bubble + 1];
				iMissingTxSeqs[bubble + 1] = tmp;
				}
			}

		// Store the index of the beginning of the TxSeqs to re-request, they will be pulled
		// in an outer loop until the index reaches iNumMissingTxSeqs.
		iResendIdx = iNumMissingTxSeqs - numToResend;
#ifdef __FLOG_ACTIVE
		Log();
#endif
		}
	return resendNeeded;
	}

#ifdef __FLOG_ACTIVE
void TL2CapErtmMissingTxSeqs::Log()
	{
	LOG3(_L("ExpectedRecvIdx=%d, ResendIdx=%d, NumMissingTxSeqs=%d"),
		 iExpectedRecvIdx, iResendIdx, iNumMissingTxSeqs);

	TBuf<256> buf;
	for (TInt i = 0; i < iNumMissingTxSeqs; i++)
		{
		buf.AppendFormat(_L("%d "), iMissingTxSeqs[i]);
		}
	LOG1(_L("%S"), &buf)
	}
#endif


TL2CapErtmReceiverStateBase::TL2CapErtmReceiverStateBase(CL2CapErtmDataReceiver& aReceiver)
 : iReceiver(aReceiver)
	{
	LOG_FUNC
	}

void TL2CapErtmReceiverStateBase::EnterL(RMBufChain* /*aIFrame*/)
	{
	LOG_FUNC
	}

void TL2CapErtmReceiverStateBase::ExitL()
	{
	LOG_FUNC
	}

TBool TL2CapErtmReceiverStateBase::IsLocalBusySupported() const
	{
	LOG_FUNC
	return EFalse;
	}


TL2CapErtmReceiverStateRecv::TL2CapErtmReceiverStateRecv(CL2CapErtmDataReceiver& aReceiver)
 : TL2CapErtmReceiverStateBase(aReceiver)
	{
	LOG_FUNC
	}

void TL2CapErtmReceiverStateRecv::EnterL(RMBufChain* /*aIFrame*/)
	{
	LOG_FUNC

	if (iReceiver.IsIncomingSduQFull())
		{
		// We may have transitioned from REJ_SENT or SREJ_SENT, which don't allow LocalBusy.
		// We can enter it now.
		iReceiver.TimerMan().StopLocalBusyDelayTimer();
		iReceiver.UpdateLocalBusyStatusL();
		}

	__ASSERT_DEBUG((iReceiver.IsIncomingSduQFull() && iReceiver.LocalBusy()) ||
				   (!iReceiver.IsIncomingSduQFull() && !iReceiver.LocalBusy()) ||
				   (iReceiver.IsIncomingSduQFull() && iReceiver.TimerMan().IsLocalBusyDelayTimerRunning()) ||
				   (!iReceiver.IsIncomingSduQFull() && !iReceiver.TimerMan().IsLocalBusyDelayTimerRunning()),
				   Panic(EL2CapSduQAndLocalBusyStateInconsistent));
	}

void TL2CapErtmReceiverStateRecv::HandleIncomingIFrameL(RMBufChain& aIFrame)
	{
	LOG_FUNC
	__ASSERT_DEBUG((iReceiver.IsIncomingSduQFull() && iReceiver.LocalBusy()) ||
				   (!iReceiver.IsIncomingSduQFull() && !iReceiver.LocalBusy()) ||
				   (iReceiver.IsIncomingSduQFull() && iReceiver.TimerMan().IsLocalBusyDelayTimerRunning()) ||
				   (!iReceiver.IsIncomingSduQFull() && !iReceiver.TimerMan().IsLocalBusyDelayTimerRunning()),
				   Panic(EL2CapSduQAndLocalBusyStateInconsistent));

	__ASSERT_DEBUG(!iReceiver.IsIncomingSduQFull() ||
				   InWindow(iReceiver.TxSeqExpectedBySduQ(), iReceiver.BufferSeq(), iReceiver.ExpectedTxSeq()),
				   Panic(EL2CAPTxSeqExpectedBySduQNotWithinBufferSeqAndExpectedTxSeq));

	__ASSERT_DEBUG(iReceiver.IsIncomingSduQFull() ||
				   (iReceiver.ExpectedTxSeq() == iReceiver.BufferSeq() &&
					iReceiver.BufferSeq() == iReceiver.TxSeqExpectedBySduQ()),
				   Panic(EL2CAPExpectedTxSeqAndBufferSeqAndTxSeqExpectedBySduQNotEqualWhenIncomingQEmptyAndInRecvState));

	const TUint8 txSeq = HIFramePDU::TxSeqNumber(aIFrame);

	TBool freeFrame = ETrue;

	if (iReceiver.ExpectedTxSeq() == txSeq)
		// With-Expected-TxSeq
		{
		iReceiver.IncExpectedTxSeq();
		if (!iReceiver.IsIncomingSduQFull())
			{
			iReceiver.SetBufferSeq(iReceiver.ExpectedTxSeq());
			}
		iReceiver.PassToIncomingQL(aIFrame);
		freeFrame = EFalse;
		}
	else if (!iReceiver.LocalBusy())
		{
		if (iReceiver.IsTxSeqUnexpected(txSeq))
			// With-Unexpected-TxSeq
			{
			if (iReceiver.IsSRejPreferredToRej(iReceiver.ExpectedTxSeq(), txSeq))
				{
				iReceiver.SetStateSRejSentL(aIFrame);
				freeFrame = EFalse;
				}
			else
				{
				if (iReceiver.BufferSeq() == iReceiver.ExpectedTxSeq())
					{
					iReceiver.SetStateRejSentL();
					}
				else
					{
					// BufferSeq != ExpectedTxSeq only when SDU Q is full or in SREJ_SENT.
					__ASSERT_DEBUG(iReceiver.IsIncomingSduQFull(),
								   Panic(EL2CAPBufferSeqNotEqualToExpectedTxSeqWhenInRecvAndSduQNotFull));

					__ASSERT_DEBUG(iReceiver.TimerMan().IsLocalBusyDelayTimerRunning(),
								   Panic(EL2CAPLocalBusyDelayTimerNotRunningWhenSduQFullButNotInLB));

					iReceiver.TimerMan().StopLocalBusyDelayTimer();

					iReceiver.UpdateLocalBusyStatusL();
					}
				}
			} // Unexpected TxSeq
			// else must be duplicate (With-Duplicate-TxSeq), in which case we just drop it
		} // !LocalBusy
	// LocalBusy and !ExpectedTxSeq - drop it - the remote will initiate a retransmission
	// when we exit LocalBusy anyway.

	if (freeFrame)
		{
		aIFrame.Free();
		}
	}

void TL2CapErtmReceiverStateRecv::HandlePollL()
	{
	LOG_FUNC
	if (iReceiver.IsIncomingSduQFull())
		{
		// If the Poll is a result of the peer's retransmission timer expiry, then the peer will
		// start retransmitting I-Frames from the sequence number given here by us (our current
		// BufferSeq). Basically, the point of a Poll in WAIT_ACK is the peer asking "what have
		// you received, need to know where to start retransmitting from", and we're supposed
		// to be honest.
		// Otherwise, if our upper layer Read()s and we process buffered I-Frames between now
		// and the moment the first retransmitted packet comes through, we will have moved
		// BufferSeq and render that packet invalid (a duplicate effectively). Therefore we
		// need to drop the buffered I-Frames to prevent duplicates and guarantee that BufferSeq
		// won't move.
		iReceiver.FlushBufferedIncomingIFrames();

		// Incoming I-Frame buffer empty and state = RECV:
		__ASSERT_DEBUG(// no buffered I-Frames
					   iReceiver.ExpectedTxSeq() == iReceiver.BufferSeq() &&
					   // most recent ReqSeq
					   iReceiver.BufferSeq() == iReceiver.TxSeqExpectedBySduQ(),
					   Panic(EL2CAPExpectedTxSeqAndBufferSeqAndTxSeqExpectedBySduQNotEqualWhenIncomingQEmptyAndInRecvState));
		}
	// Now respond with a data frame or an ack frame with the Final bit set.
	iReceiver.Controller().SendIOrRrOrRnrL(ETrue);
	}

TBool TL2CapErtmReceiverStateRecv::IsLocalBusySupported() const
	{
	LOG_FUNC
	return ETrue;
	}

void TL2CapErtmReceiverStateRecv::TxSeqExpectedBySduQChanged(TUint8 aTxSeq)
	{
	LOG_FUNC
	LOG1(_L("Slipping BufferSeq to %d"), aTxSeq)
	iReceiver.SetBufferSeq(aTxSeq);
	__ASSERT_DEBUG(iReceiver.IsIncomingSduQFull() || iReceiver.BufferSeq() == iReceiver.ExpectedTxSeq(),
				   Panic(EL2CAPBufferSeqNotEqualToExpectedTxSeqWhenInRecvAndSduQNotFull));
	}


TL2CapErtmReceiverStateRejSent::TL2CapErtmReceiverStateRejSent(CL2CapErtmDataReceiver& aReceiver)
 :	TL2CapErtmReceiverStateBase(aReceiver)
	{
	LOG_FUNC
	}

void TL2CapErtmReceiverStateRejSent::EnterL(RMBufChain* /*aIFrame*/)
	{
	LOG_FUNC

	// LocalBusy is only allowed in RECV.
	__ASSERT_DEBUG(!iReceiver.LocalBusy(), Panic(EL2CAPInRejSentAndLocalBusy));

	// Otherwise by sending the REJ we'd acknowledge frames between BufferSeq and ExpectedTxSeq
	// (and if BufferSeq != ExpectedTxSeq and we're not in SREJ_SENT then incoming SDU Q is full
	// and we can not acknowledge anything new).
	__ASSERT_DEBUG(iReceiver.BufferSeq() == iReceiver.ExpectedTxSeq(),
				   Panic(EL2CAPRejSentEnteredWithBufferedFrames));
	
	iReceiver.Controller().OutgoingQ().QueueNonAckingSFrame(*HSFramePDU::NewL(EReject), iReceiver.ExpectedTxSeq());
	}

void TL2CapErtmReceiverStateRejSent::HandleIncomingIFrameL(RMBufChain& aIFrame)
	{
	LOG_FUNC

	__ASSERT_DEBUG(iReceiver.TxSeqExpectedBySduQ() == iReceiver.BufferSeq(),
				   Panic(EL2CAPNextTxSeqExpectedBySduQNotEqualToBufferSeqWhenNotInSRejSent));

	__ASSERT_DEBUG(InWindow(iReceiver.TxSeqExpectedBySduQ(), iReceiver.BufferSeq(), iReceiver.ExpectedTxSeq()),
				   Panic(EL2CAPTxSeqExpectedBySduQNotWithinBufferSeqAndExpectedTxSeq));

	__ASSERT_DEBUG(!iReceiver.LocalBusy(), Panic(EL2CAPInRejSentAndLocalBusy));

	const TUint8 txSeq = HIFramePDU::TxSeqNumber(aIFrame);

	if (iReceiver.ExpectedTxSeq() == txSeq)
		// With-Expected-TxSeq
		{
		iReceiver.IncExpectedTxSeq();
		if (!iReceiver.IsIncomingSduQFull())
			{
			iReceiver.SetBufferSeq(iReceiver.ExpectedTxSeq());
			}

		// Received the missing frame, clear REJ exception condition.
		iReceiver.SetStateRecvL();

		iReceiver.PassToIncomingQL(aIFrame);
		}
	else
		{
		// else it's a duplicate or unexpected frame:
		// - drop the duplicate as usual,
		// - drop the unexpected frame as we're already in a REJ exception condition
		//   and aren't allowed to send a second REJ until the first one is cleared.
		aIFrame.Free();
		}
	}

void TL2CapErtmReceiverStateRejSent::HandlePollL()
	{
	LOG_FUNC
	// General-case Poll handling: simply respond with a data frame or an ack frame with
	// the Final bit set.
	iReceiver.Controller().SendIOrRrOrRnrL(ETrue);
	}

void TL2CapErtmReceiverStateRejSent::TxSeqExpectedBySduQChanged(TUint8 aTxSeq)
	{
	LOG_FUNC
	LOG1(_L("NextConsumedTxSeq moved: slipping BufferSeq to %d"), aTxSeq)
	iReceiver.SetBufferSeq(aTxSeq);
	__ASSERT_DEBUG(iReceiver.IsIncomingSduQFull() || iReceiver.BufferSeq() == iReceiver.ExpectedTxSeq(),
				   Panic(EL2CAPBufferSeqNotEqualToExpectedTxSeqWhenInRecvAndSduQNotFull));
	}


TL2CapErtmReceiverStateSRejSent::TL2CapErtmReceiverStateSRejSent(CL2CapErtmDataReceiver& aReceiver)
 :	TL2CapErtmReceiverStateBase(aReceiver),
 	iGoToRej(EFalse)
	{
	LOG_FUNC
	}

void TL2CapErtmReceiverStateSRejSent::EnterL(RMBufChain* aIFrame)
	{
	LOG_FUNC

	__ASSERT_DEBUG(!iReceiver.LocalBusy(), Panic(EL2CAPInSRejSentAndLocalBusy));
	__ASSERT_DEBUG(iMissingTxSeqs.IsEmpty(), Panic(EL2CAPNormalReceiveStateWhenMissingSRejFramesListIsNotEmpty));
	__ASSERT_DEBUG(aIFrame != NULL, Panic(EL2CAPDataFrameNotPassedOnEntryToSRejSent));

	iGoToRej = EFalse;
	SendSRejsUpToReceivedIFrameL(*aIFrame);
	}

void TL2CapErtmReceiverStateSRejSent::ExitL()
	{
	LOG_FUNC
	TL2CapErtmReceiverStateBase::ExitL();
	
	iMissingTxSeqs.Reset();

	// BufferSeq was frozen once SREJ_SENT was entered. Now we can move it forward.
	if (iReceiver.IsIncomingSduQFull())
		{
		// If incoming SDU Q filled up during SREJ_SENT, then we can only move BufferSeq up to
		// the point at which that happened, not ExpectedTxSeq.
		iReceiver.SetBufferSeq(iReceiver.TxSeqExpectedBySduQ());
		}
	else
		{
		// Bring BufferSeq back in sync with ExpectedTxSeq.
		iReceiver.SetBufferSeq(iReceiver.ExpectedTxSeq());
		}
	LOG2(_L("On exit from SREJ_SENT[IsIncomingSduQFull = %d]: slipped BufferSeq to %d"),
		 iReceiver.IsIncomingSduQFull(), iReceiver.BufferSeq())
	}

void TL2CapErtmReceiverStateSRejSent::HandleIncomingIFrameL(RMBufChain& aIFrame)
	{
	LOG_FUNC

	__ASSERT_DEBUG(InWindow(iReceiver.TxSeqExpectedBySduQ(), iReceiver.BufferSeq(), iReceiver.ExpectedTxSeq()),
				   Panic(EL2CAPTxSeqExpectedBySduQNotWithinBufferSeqAndExpectedTxSeq));

	__ASSERT_DEBUG(!iReceiver.LocalBusy(), Panic(EL2CAPInSRejSentAndLocalBusy));

	const TUint8 txSeq = HIFramePDU::TxSeqNumber(aIFrame);

	TBool freeFrame = ETrue;
	
	if (iMissingTxSeqs.ExpectedSRejTxSeq() == txSeq)
		// With-Expected-TxSeq-Srej
		{
		LOG1(_L("With-Expected-TxSeq-Srej: TxSeq=%d"), txSeq)
		iReceiver.PassToIncomingQL(aIFrame);
		freeFrame = EFalse;

		TBool resendPreviousSRej = iMissingTxSeqs.ReceivedTxSeq(txSeq);
		__ASSERT_ALWAYS(!resendPreviousSRej, Panic(EL2CAPHaveSRejsToResendEvenThoughReceivedFirstInOrder));

		if (iMissingTxSeqs.AllRequestedFramesReceived())
			{
			if (iGoToRej)
				{
				if (iReceiver.IsIncomingSduQFull())
					{
					// We couldn't have entered LocalBusy in SREJ_SENT, but now we can transition
					// to RECV with LocalBusy instead of REJ_SENT. The retransmission will be
					// started when we exit LocalBusy anyway.
					iReceiver.SetStateRecvL();
					// We rely on this...
					__ASSERT_DEBUG(iReceiver.LocalBusy(),
								   Panic(EL2CAPNotInLocalBusyAfterTransitionToRecvFromSrejSentWithSduQFull));
					}
				else
					{
					iReceiver.SetStateRejSentL();
					}
				}
			else // !iGoToRej
				{
				iReceiver.SetStateRecvL();
				}
			} // all SREJed frames received
		} // With-Expected-TxSeq-Srej
	else
		{
		if (iReceiver.ExpectedTxSeq() == txSeq)
			{
			// With-Expected-TxSeq
			if (!iGoToRej)
				{
				LOG1(_L("With-Expected-TxSeq: TxSeq=%d"), txSeq)
				iReceiver.IncExpectedTxSeq();
				iReceiver.PassToIncomingQL(aIFrame);
				freeFrame = EFalse;
				}
			}
		else if (InWindow(txSeq, iReceiver.BufferSeq(), iReceiver.ExpectedTxSeq()))
			{
			if (iMissingTxSeqs.IsTxSeqOnTheList(txSeq))
				// With-Unexpected-TxSeq-Srej
				{
				__ASSERT_DEBUG(TL2CapErtmMissingTxSeqs::KMaxSRejsInFlight > 1,
							   Panic(EL2CAPMultiSRejPathHitWhenNotSoConfigured));

				// Multiple SRej requests have been made and some of the retransmitted I-Frames
				// were lost again. Send new SREJs for those frames.

				__ASSERT_DEBUG(!iMissingTxSeqs.IsEmpty(), Panic(EL2CAPOldestSRejedFrameNotOnMissingList));
				LOG2(_L("With-Unexpected-TxSeq-Srej: TxSeq=%d. Expected was=%d"),
					 txSeq, iMissingTxSeqs.ExpectedSRejTxSeq())

				TBool resendSRej = iMissingTxSeqs.ReceivedTxSeq(txSeq);
				while (resendSRej)
					{
					TUint8 resendTxSeq;
					resendSRej = iMissingTxSeqs.GetNextTxSeqForResend(resendTxSeq);
					iReceiver.Controller().OutgoingQ().QueueNonAckingSFrame(*HSFramePDU::NewL(ESelectiveReject), resendTxSeq);
					LOG1(_L("Resending SREJ for TxSeq=%d"), resendTxSeq)
					}
				iReceiver.PassToIncomingQL(aIFrame);
				freeFrame = EFalse;
				}
			else // else must be a duplicate: With-duplicate-TxSeq-Srej
				{
				LOG1(_L("With-duplicate-TxSeq-Srej: TxSeq=%d"), txSeq)
				}
			}
		else if (!iGoToRej) // With-Unexpected-TxSeq
			{
			LOG1(_L("With-Unexpected-TxSeq: TxSeq=%d."), txSeq)
			if (iReceiver.IsSRejPreferredToRej(iReceiver.ExpectedTxSeq(), txSeq) &&
				iMissingTxSeqs.HaveSpaceForNewTxSeqs(Mod64(txSeq - iReceiver.ExpectedTxSeq())))
				{
				__ASSERT_DEBUG(TL2CapErtmMissingTxSeqs::KMaxSRejsInFlight > 1,
							   Panic(EL2CAPMultiSRejPathHitWhenNotSoConfigured));
				SendSRejsUpToReceivedIFrameL(aIFrame);
				freeFrame = EFalse;
				}
			else
				{
				// Don't send more SREJs. Wait for I-Frames requested so far to be
				// retransmitted and go to REJ mode.
				iGoToRej = ETrue;
				LOG1(_L("NumMissingTxSeqs=%d, GoToRej=1"), iMissingTxSeqs.NumMissingTxSeqs())
				}
			}
		} // !With-Expected-TxSeq-Srej

	if (freeFrame)
		{
		aIFrame.Free();
		}
	}

void TL2CapErtmReceiverStateSRejSent::HandlePollL()
	{
	LOG_FUNC
	__ASSERT_DEBUG(!iMissingTxSeqs.IsEmpty(), Panic(EL2CAPCaughtInSRejSentWithNoMissingTxSeqs));

	HSFramePDU* frame = HSFramePDU::NewL(ESelectiveReject);
	frame->SetFinalBit(ETrue);
	iReceiver.OutgoingQ().QueueNonAckingSFrame(*frame, iMissingTxSeqs.LastTxSeq());
	}

void TL2CapErtmReceiverStateSRejSent::TxSeqExpectedBySduQChanged(TUint8 /*aTxSeq*/)
	{
	LOG_FUNC
	// Can't slip BufferSeq until SREJ_SENT is left.
	}

void TL2CapErtmReceiverStateSRejSent::SendSRejsUpToReceivedIFrameL(RMBufChain& aIFrame)
	{
	LOG_FUNC

	const TUint8 receivedTxSeq = HIFramePDU::TxSeqNumber(aIFrame);

	for (TUint8 sRejSeq = iReceiver.ExpectedTxSeq(); sRejSeq != receivedTxSeq; sRejSeq = Mod64(sRejSeq + 1))
		{
		iMissingTxSeqs.AppendTxSeq(sRejSeq);
		iReceiver.OutgoingQ().QueueNonAckingSFrame(*HSFramePDU::NewL(ESelectiveReject), sRejSeq);
		}

	iReceiver.SetExpectedTxSeq(Mod64(receivedTxSeq + 1));
	iReceiver.PassToIncomingQL(aIFrame);

	LOG3(_L("ExpectedTxSeq = %d, First Expected SREJed TxSeq = %d, Last Expected SREJed TxSeq = %d"),
		 iReceiver.ExpectedTxSeq(), iMissingTxSeqs.ExpectedSRejTxSeq(), iMissingTxSeqs.LastTxSeq())
	}

RL2CapErtmIncomingIFrameQueue::RL2CapErtmIncomingIFrameQueue()
 : iTxSeqExpectedBySduQ(0)
	{
	LOG_FUNC
	}

void RL2CapErtmIncomingIFrameQueue::Close()
	{
	LOG_FUNC
	if (!iQueue.IsEmpty())
		{
		LOG(_L("Incoming I-Frame queue not empty, freeing mbufs."))
		iQueue.Free();
		}
	}


void RL2CapErtmIncomingIFrameQueue::HandleIncomingIFrameL(RMBufChain& aIFrame, const CL2CapErtmDataReceiver& aReceiver)
	{
	LOG_FUNC
	Insert(aIFrame);
	if (!aReceiver.IsIncomingSduQFull())
		{
		ConsumeUpToFirstGapL(aReceiver);
		}
	}

void RL2CapErtmIncomingIFrameQueue::Insert(RMBufChain& aIFrame)
	{
	LOG_FUNC
	const TUint8 txSeq = HIFramePDU::TxSeqNumber(aIFrame);
	LOG2(_L("TxSeq=%d, NextConsumedTxSeq=%d"), txSeq, iTxSeqExpectedBySduQ)

	if (iQueue.IsEmpty() || !InWindow(txSeq, iTxSeqExpectedBySduQ, HIFramePDU::TxSeqNumber(iQueue.Last())))
		// Fast path for the most common cases.
		{
		__ASSERT_DEBUG(iQueue.IsEmpty() || txSeq != HIFramePDU::TxSeqNumber(iQueue.Last()),
					   Panic(EL2CapDuplicateIFramePassedToIncomingQ));
		iQueue.Append(aIFrame);
		LOG(_L("Appending on fast path"))
		}
	else
		// Generic insertion algorithm.
		// Depends on there not being duplicate TxSeqs on the queue, i.e. the queue mustn't contain
		// acknowledged frames. It can only contain frames within the current receive window.
		{
		TBool inserted = EFalse;
		for (TMBufPktQIter iter(iQueue); !inserted && iter.More(); iter++)
			{
			TUint8 currentTxSeq = HIFramePDU::TxSeqNumber(iter.Current());
			__ASSERT_DEBUG(txSeq != currentTxSeq, Panic(EL2CapDuplicateIFramePassedToIncomingQ));

			if (InWindow(txSeq, iTxSeqExpectedBySduQ, currentTxSeq))
				{
				LOG1(_L("Inserting before %d"), currentTxSeq)
				iter.Insert(aIFrame);
				inserted = ETrue;
				}
			}
		if (!inserted)
			{
			// It's actually impossible to get here with the fast path in the beginning
			// of the function, but it's here for testing of the loop. Might change to
			// __ASSERT_DEBUG when the testing dust settles.
			LOG(_L("Gone through the Q, appending"))
			iQueue.Append(aIFrame);
			}
		}
#ifdef _DEBUG
	LogQ();
#endif
	}

#ifdef _DEBUG
void RL2CapErtmIncomingIFrameQueue::LogQ()
	{
	LOG_FUNC
	TMBufPktQIter i(iQueue);
	TBuf<512> buf;
	while (i.More())
		{
		buf.AppendFormat(_L("%d "), HIFramePDU::TxSeqNumber(i++));
		}
	LOG1(_L("Q: %S"), &buf)
	}
#endif

void RL2CapErtmIncomingIFrameQueue::ConsumeUpToFirstGapL(const CL2CapErtmDataReceiver& aReceiver)
	{
	LOG_FUNC
	while (!iQueue.IsEmpty() && iTxSeqExpectedBySduQ == HIFramePDU::TxSeqNumber(iQueue.First())
		   && !aReceiver.IsIncomingSduQFull())
		{
		RMBufChain frame;
		//Check on return value of iQueue.Remove() is unnecessary, since we've checked iQueue.IsEmpty()
		static_cast<void>(iQueue.Remove(frame));
		iTxSeqExpectedBySduQ = Mod64(iTxSeqExpectedBySduQ + 1);
		// This feeds the frame to the SDU Queue. This is also the place where we get notified
		// if the queue gets full. If that happens, SetIncomingSduQFull is synchronously
		// called by SDU Q here in the same stack frame - hence the guard in the loop condition.
		aReceiver.PassToSduQL(frame);
		}
	LOG3(_L("On return from ConsumeUpToFirstGap: Queue empty=%d, NextConsumedTxSeq=%d, IsIncomingSduQFull=%d"),
		 iQueue.IsEmpty(), iTxSeqExpectedBySduQ, aReceiver.IsIncomingSduQFull())

#ifdef _DEBUG
	LogQ();
#endif
	}


CL2CapErtmDataReceiver* CL2CapErtmDataReceiver::NewL(CL2CapEnhancedReTxController& aController)
	{
	LOG_STATIC_FUNC
	CL2CapErtmDataReceiver* receiver = new (ELeave) CL2CapErtmDataReceiver(aController);
	CleanupStack::PushL(receiver);
	receiver->ConstructL();
	CleanupStack::Pop(receiver);
	return receiver;
	}

void CL2CapErtmDataReceiver::ConstructL()
	{
	LOG_FUNC
	iReceiveState = &iStateRecv;
	iReceiveState->EnterL(NULL);
	}

CL2CapErtmDataReceiver::CL2CapErtmDataReceiver(CL2CapEnhancedReTxController& aController)
 :	iController(aController),
	iExpectedTxSeq(0),
	iBufferSeq(0),
	iLastAckReqSeq(0),
	iIncomingSduQFull(EFalse),
	iLocalBusy(EFalse),
	iInWaitFState(EFalse),
	iWaitFStatePending(EFalse),
	iSendAck(EFalse),
	iStateRecv(*this),
	iStateRejSent(*this),
	iStateSRejSent(*this)
	{
	LOG_FUNC
	}

CL2CapErtmDataReceiver::~CL2CapErtmDataReceiver()
	{
	LOG_FUNC
	iIncomingIFrameQ.Close();
	}

void CL2CapErtmDataReceiver::HandleIncomingIFrameL(RMBufChain& aIFrame)
	{
	LOG_FUNC
	LOG3(_L("ExpectedTxSeq=%d, BufferSeq=%d, LastAckReqSeq=%d"),
		 iExpectedTxSeq, iBufferSeq, iLastAckReqSeq)

	if (HIFramePDU::FinalBit(aIFrame))
		{
		// It's important that this is executed first, we wouldn't like to drop an I-Frame that
		// carries the Final Ack if we're in WAIT_F. If the remote has set F=1 then it must have
		// received the Poll and is therefore aware of the ReqSeq we've sent in RR[P=1].
		// So if the remote is a correct implementation, then this I-Frame is the one we're
		// expecting and we shouldn't drop it.
		HandleFinalAckL();
		}

	if (// While we're in WAIT_F, peer may be still sending us I-Frames that are newer than those
		// acknowledged by the RR[P=1] frame. They will be retransmitted when it receives the RR,
		// so we can't accept the original transmissions, as that would move ExpectedAckSeq and
		// thus cause the retransmissions to be invalid - we'd close the connection.
		!iInWaitFState &&
		// ... also need to drop I-Frames when waiting to enter WAIT_F - if we're in WAIT_ACK,
		// then iWaitFStatePending persists for an extended period of time during which we may
		// have sent an RR due to some other unrelated reason. The peer started retransmitting
		// when it got that RR, which makes the situation identical to being in WAIT_F.
		// Ideally the spec would say that RR[P=1] makes the peer start retransmitting from
		// the given ReqSeq and RR[P=0] just makes it pick up where it left it, but it's not
		// the case - both restart the transmission, so both must be treated equally.
		!iWaitFStatePending)
		{
		const TUint8 bufferSeqBefore = iBufferSeq;

		iReceiveState->HandleIncomingIFrameL(aIFrame);

		if (iBufferSeq != bufferSeqBefore	// anything new to ack ?
			&& !iSendAck)					// an ack already pending ?
			{
			iSendAck = IsEndOfReceiveWindowApproaching();
			if (!iSendAck)
				{
				// Note that we'll never fall here if PeerTxWin <= KReceiveWinFreeSpaceLeftToTriggerAck
				if (!TimerMan().IsSendPeerAckTimerRunning())
					{
					if (!TimerMan().StartSendPeerAckTimer())
						{
						// The timer could not be started. Send an ack immediately.
						iSendAck = ETrue;
						}
					}
				}
			}
		} // !WAIT_F
	else
		{
		aIFrame.Free();
		}
	}

void CL2CapErtmDataReceiver::HandleIncomingSFrameL(RMBufChain& aSFrame)
	{
	LOG_FUNC
	LOG3(_L("ExpectedTxSeq=%d, BufferSeq=%d, LastAckReqSeq=%d"),
		 iExpectedTxSeq, iBufferSeq, iLastAckReqSeq)

	const TBool poll = HSFramePDU::PollBit(aSFrame);
	const TBool final = HSFramePDU::FinalBit(aSFrame);
	const TSupervisoryFunction function = HSFramePDU::SupervisoryFunction(aSFrame);

	if (poll && final)
		{
		LOG(_L("Incoming S-Frame has both P and F bits set!"))
		LEAVEL(KErrL2CAPIllegalRemoteBehavior);
		}

	if (final)
		{
		HandleFinalAckL();
		}
	
	if (poll && function != ESelectiveReject) // SREJ[P=1] is handled by the transmitter.
		{
		iReceiveState->HandlePollL();
		}
	}

void CL2CapErtmDataReceiver::HandleFinalAckL()
	{
	LOG_FUNC
	if (iInWaitFState)
		{
		iInWaitFState = EFalse;
		if (iIncomingSduQFull)
			{
			// Incoming SDU Q filled up again while we were exiting the previous LB condition.
			UpdateLocalBusyStatusL();
			}
		}
	}

void CL2CapErtmDataReceiver::SetIncomingSduQFullL(TBool aIncomingSduQFull)
	{
	LOG_FUNC
	LOG2(_L("Incoming SDU Q full %d -> %d"), iIncomingSduQFull, aIncomingSduQFull)

	if (!iIncomingSduQFull && aIncomingSduQFull)
		{
		// Don't enter LocalBusy even if we can, start the delay timer instead.
		// If the queue remains full until its expiry, we'll enter LocalBusy then.
		// LocalBusy means we inform the peer of our busy condition by sending RNRs
		// instead of RRs.
		// We refrain from entering LocalBusy right off because exiting it means
		// that the peer will have to restart the transmission from a seqnum given by us
		// in the RR exiting the LocalBusy condition, which in turn means some frames
		// which are already in transit (if there are any) will be lost. So if the full
		// SDU Q is just a momentary hiccup, we hide this from the peer and just buffer
		// the incoming frames and anchor BufferSeq at its current value to prevent the
		// receive window from moving.
		TimerMan().StartLocalBusyDelayTimer();
		}
	else if (iIncomingSduQFull && !aIncomingSduQFull)
		{
		TimerMan().StopLocalBusyDelayTimer();
		}

	iIncomingSduQFull = aIncomingSduQFull;
	UpdateLocalBusyStatusL();
	}

// This routine manages the LocalBusy status by evaluating the necessary conditions:
// - whether the incoming SDU queue is full at the moment,
// - whether LocalBusy delay timer is still running,
// - whether we're exiting a previous LocalBusy condition,
// - whether we're able to enter LB in current receive state (LB is only allowed in RECV).
void CL2CapErtmDataReceiver::UpdateLocalBusyStatusL()
	{
	LOG_FUNC
	LOG5(_L("iIncomingSduQFull=%d, iLocalBusy=%d, iWaitF=%d, iWaitFPending=%d, recv state = 0x%08x"),
		 iIncomingSduQFull, iLocalBusy, iInWaitFState, iWaitFStatePending, iReceiveState)

	if (iIncomingSduQFull)
		{
		if (!iLocalBusy && !TimerMan().IsLocalBusyDelayTimerRunning())
			{
			if (!iInWaitFState && !iWaitFStatePending // have to wait until previous LB is properly finished
				&& iReceiveState->IsLocalBusySupported()) // only RECV
				{
				EnterLocalBusyL();
				}
			}
		// Once we enter LB the receive state is locked onto RECV so don't have to check whether
		// it's changed.
		}
	else // !iIncomingSduQFull
		{
		// First we need to consume the frames on the incoming Q, which may cause the SDU Q to
		// fill up again.
		iIncomingIFrameQ.ConsumeUpToFirstGapL(*this);

		if (!iIncomingSduQFull)
			// Incoming SDU Q didn't fill up again in ConsumeUpToFirstGapL().
			{
			if (iLocalBusy)
				{
				// iLocalBusy is only possible when iInWaitFState && iWaitFStatePending are false
				// and the only receive state in LB is RECV, so we can exit it anytime.
				__ASSERT_DEBUG(iReceiveState == &iStateRecv && !iInWaitFState && !iWaitFStatePending,
							   Panic(EL2CAPLocalBusyUnderIllegalConditions));
				ExitLocalBusy();
				}
			}
		else // Incoming SDU Q filled up again in ConsumeUpToFirstGapL().
			{
			// Some frames have been consumed and we've restarted the LB delay timer,
			// so it's a good idea to ack now. 
			iSendAck = ETrue;
			}

		// Give the receive state a chance to sync BufferSeq with TxSeqExpectedBySduQ.
		// Must keep them in sync with if we can - there're reasons for that - see
		// TL2CapErtmReceiverStateRecv::HandlePollL. We don't do that in SREJ_SENT though
		// as BufferSeq can't change in that state but it's cool because our response to
		// a Poll in SREJ_SENT will be SREJ which doesn't trigger retransmission of all
		// unacked I-Frames and there's a separate mechanism for the peer to protect
		// itself against a duplicate SREJ[F=1].
		// This is asserted through the code.
		iReceiveState->TxSeqExpectedBySduQChanged(iIncomingIFrameQ.TxSeqExpectedBySduQ());

		__ASSERT_DEBUG(iIncomingSduQFull ||
					   ((iReceiveState != &iStateSRejSent &&
						 iIncomingIFrameQ.TxSeqExpectedBySduQ() == iBufferSeq &&
						 iBufferSeq == iExpectedTxSeq) ||
					    (iReceiveState == &iStateSRejSent)),
					   Panic(EL2CAPWindowInformationInconsistentWhenExitingSduQFull));

		__ASSERT_DEBUG(!iIncomingSduQFull ||
					   ((iReceiveState != &iStateSRejSent &&
						 iIncomingIFrameQ.TxSeqExpectedBySduQ() == iBufferSeq) ||
						(iReceiveState == &iStateSRejSent)),
					   Panic(EL2CAPWindowInformationInconsistentAfterMovingBufferSeqWhenSduQFull));
		} // !iIncomingSduQFull
	}

void CL2CapErtmDataReceiver::EnterLocalBusyL()
	{
	LOG_FUNC
	__ASSERT_DEBUG(!TimerMan().IsLocalBusyDelayTimerRunning(),
				   Panic(EL2CAPEnterLocalBusyCalledWhileDelayTimerStillRunning));

	iLocalBusy = ETrue;
	// Send an RNR immediately.
	iSendAck = ETrue;
	iController.NotifyMuxerOfPdusToSendIfHaveSome();
	}

void CL2CapErtmDataReceiver::ExitLocalBusy()
	{
	LOG_FUNC
	iLocalBusy = EFalse;
	// Signal to GetPdu that we need to enter WAIT_F.
	iWaitFStatePending = ETrue;
	iController.NotifyMuxerOfPdusToSendIfHaveSome();
	}

template<typename FrameType>
void CL2CapErtmDataReceiver::StampWithReqSeq(FrameType& aFrame)
	{
	LOG_FUNC
	__ASSERT_DEBUG(!aFrame.IsQueuedForSend(), Panic(EL2CAPReqSeqSetOnFrameAlreadyQueuedForSend));

	aFrame.SetReqSeqNumber(iLastAckReqSeq = iBufferSeq);
	TimerMan().StopSendPeerAckTimer();
	iSendAck = EFalse;
	}

TBool CL2CapErtmDataReceiver::IsSRejPreferredToRej(TUint8 aExpectedTxSeq, TUint8 aReceivedTxSeq)
	{
	LOG_FUNC
	return InWindow(aReceivedTxSeq, aExpectedTxSeq, Mod64(aExpectedTxSeq + KSRejMissingFrameThreshold));
	}

void CL2CapErtmDataReceiver::LocalBusyDelayTimerExpired()
	{
	LOG_FUNC

	// This guards from the classic state-changed-between-timer-expiry-and-handler-entry
	// race condition.
	if (iIncomingSduQFull)
		{
		TRAPD(err, UpdateLocalBusyStatusL());
		if (err != KErrNone)
			{
			iController.ErrorD(err);
			// Just joined the majority.
			}
		}
	}

TBool CL2CapErtmDataReceiver::IsEndOfReceiveWindowApproaching() const
	{
	LOG_FUNC
	TBool ackNeeded = EFalse;

	const TUint8 numFramesReceivedSinceLastAck = Mod64(iExpectedTxSeq - iLastAckReqSeq);
	const TInt numFramesReceivedBeforeWantToSendAck = iController.Config().PeerTXWindowSize() - KReceiveWinFreeSpaceLeftToTriggerAck; 
	if (numFramesReceivedSinceLastAck >= numFramesReceivedBeforeWantToSendAck)
		{
		LOG2(_L("LastAckReqSeq = %d, ExpectedTxSeq = %d. Reaching end of window, will send an ack."),
			 iLastAckReqSeq, iExpectedTxSeq)
		ackNeeded = ETrue;
		}
	return ackNeeded;
	}

void CL2CapErtmDataReceiver::SendPeerAckTimerExpiredL()
	{
	LOG_FUNC
	if (iBufferSeq != iLastAckReqSeq)
		{
		iSendAck = ETrue;
		iController.NotifyMuxerOfPdusToSendIfHaveSome();
		}
	}

HSFramePDU* CL2CapErtmDataReceiver::GetAckFrameL(TBool aFinal)
	{
	HSFramePDU* frame = HSFramePDU::NewL(iLocalBusy ? EReceiverNotReady : EReceiverReady);
	frame->SetFinalBit(aFinal);
	return frame;
	}

void CL2CapErtmDataReceiver::SetStateL(TL2CapErtmReceiverStateBase& aState, RMBufChain* aIFrame)
	{
	LOG_FUNC
	iReceiveState->ExitL();
	iReceiveState = &aState;
	iReceiveState->EnterL(aIFrame);
	}


RL2CapErtmOutgoingQueue::RL2CapErtmOutgoingQueue(CL2CapEnhancedReTxController& aController)
 :	iController(aController),
	iOutgoingQ(_FOFF(HL2CapPDU, iDataControllerInternalQLink)),
	iPendRetransmitIFrameQ(_FOFF(HL2CapPDU, iDataControllerInternalQLink))
	{
	LOG_FUNC
	}

void RL2CapErtmOutgoingQueue::Close()
	{
	LOG_FUNC
	DeleteAllFrames();
	}

void RL2CapErtmOutgoingQueue::QueueIFrameL(HIFramePDU& aIFrame)
	{
	LOG_FUNC
	if (!TRetransmissionAndFlowControlOption::EnhancedMaxTransmitLessOrEqual(aIFrame.TransmissionCount() + 1,
																			 iController.Config().MaxTransmit()))
		{
		LOG2(_L("MaxTransmit=%d exceeded by I-Frame no %d"),
			 iController.Config().MaxTransmit(), aIFrame.TxSeqNumber())
		LEAVEL(KErrL2CAPMaxTransmitExceeded);
		}
	else if (aIFrame.IsQueuedForSend())
		{
		// This is either due to the I-Frame being SREJ-requested twice or a bug
		// in our code. We don't have enough information to tell between the two,
		// so have to assume it's the former.
		LOG1(_L("I-Frame no %d already queued for send"), aIFrame.TxSeqNumber())
		LEAVEL(KErrL2CAPIllegalRemoteBehavior);
		}

	iController.StampWithReqSeq(aIFrame);
	iOutgoingQ.AddLast(aIFrame);
	aIFrame.SetQueuedForSend(ETrue);
	LOG3(_L("Queued I-Frame for send, TxSeq=%d, ReqSeq=%d, F=%d"),
		 aIFrame.TxSeqNumber(), aIFrame.ReqSeqNumber(), aIFrame.FinalBit())
	}

void RL2CapErtmOutgoingQueue::QueueAckingSFrame(HSFramePDU& aSFrame)
	{
	LOG_FUNC
	// This method is meant only for those S-Frames which carry an acknowledgement number.
	__ASSERT_DEBUG(aSFrame.SupervisoryFunction() != ESelectiveReject || aSFrame.PollBit(),
				   Panic(EL2CAPSendSFrameWithAckCalledForSRejP0));
	// S-Frames are fire-n-forget, no healthy S-Frame gets queued twice!
	__ASSERT_DEBUG(!aSFrame.IsQueuedForSend(), Panic(EL2CAPSFrameQueuedForSendTwice));

	iController.StampWithReqSeq(aSFrame);
	iOutgoingQ.AddLast(aSFrame);
	aSFrame.SetQueuedForSend(ETrue);
	LOG4(_L("Queued S-Frame for send, S=%d, ReqSeq=%d, P=%d, F=%d"),
		 aSFrame.SupervisoryFunction(), aSFrame.ReqSeqNumber(), aSFrame.PollBit(), aSFrame.FinalBit())
	}

void RL2CapErtmOutgoingQueue::QueueNonAckingSFrame(HSFramePDU& aSFrame, TUint8 aReqSeq)
	{
	LOG_FUNC
	// S-Frames are fire-n-forget, no healthy S-Frame gets queued twice!
	__ASSERT_DEBUG(!aSFrame.IsQueuedForSend(), Panic(EL2CAPSFrameQueuedForSendTwice));

	aSFrame.SetReqSeqNumber(aReqSeq);
	iOutgoingQ.AddLast(aSFrame);
	aSFrame.SetQueuedForSend(ETrue);
	LOG4(_L("Queued S-Frame for send, S=%d, ReqSeq=%d, P=%d, F=%d"),
		 aSFrame.SupervisoryFunction(), aSFrame.ReqSeqNumber(), aSFrame.PollBit(), aSFrame.FinalBit())
	}

void RL2CapErtmOutgoingQueue::PendRetransmitIFrameL(HIFramePDU& aIFrame)
	{
	LOG_FUNC
	if (aIFrame.IsQueuedForSend())
		{
		// This is either due to the I-Frame being SREJ-requested twice or a bug
		// in our code. We don't have enough information to tell between the two,
		// so have to assume it's the former.
		LOG1(_L("I-Frame no %d already queued for send!"), aIFrame.TxSeqNumber())
		LEAVEL(KErrL2CAPIllegalRemoteBehavior);
		}
	iPendRetransmitIFrameQ.AddLast(aIFrame);
	aIFrame.SetQueuedForSend(ETrue);
	LOG3(_L("Queued I-Frame pending retransmission, TxSeq=%d, ReqSeq=%d, F=%d"),
		 aIFrame.TxSeqNumber(), aIFrame.ReqSeqNumber(), aIFrame.FinalBit())
	}

void RL2CapErtmOutgoingQueue::SendPendingRetransmitIFramesL()
	{
	LOG_FUNC
	if (!iPendRetransmitIFrameQ.IsEmpty())
		{
		TSglQueIter<HIFramePDU> iter(iPendRetransmitIFrameQ);
		while (HIFramePDU* eyeFrame = iter++)
			{
			eyeFrame->SetQueuedForSend(EFalse);	// QueueIFrameL would leave if this flag was set.
			QueueIFrameL(*eyeFrame);
			}
		iPendRetransmitIFrameQ.Reset();
		}
	}

void RL2CapErtmOutgoingQueue::CancelPendingRetransmitIFrames()
	{
	LOG_FUNC
	if (!iPendRetransmitIFrameQ.IsEmpty())
		{
		TSglQueIter<HIFramePDU> iter(iPendRetransmitIFrameQ);
		while (HIFramePDU* eyeFrame = iter++)
			{
			LOG1(_L("Cancelling SREJ-requested pending retransmission of I-Frame %d"),
				 eyeFrame->TxSeqNumber())
			eyeFrame->SetQueuedForSend(EFalse);
			}
		iPendRetransmitIFrameQ.Reset();
		}
	}

void RL2CapErtmOutgoingQueue::DeleteAllFrames()
	{
	LOG_FUNC
	if (!iPendRetransmitIFrameQ.IsEmpty())
		{
		TSglQueIter<HIFramePDU> iter(iPendRetransmitIFrameQ);
		while (HIFramePDU* eyeFrame = iter++)
			{
			delete eyeFrame;
			}
		iPendRetransmitIFrameQ.Reset();
		}

	if (!iOutgoingQ.IsEmpty())
		{
		TSglQueIter<HL2CapPDU> iter(iOutgoingQ);
		while (HL2CapPDU* pdu = iter++)
			{
			delete pdu;
			}
		iOutgoingQ.Reset();
		}	
	}

HL2CapPDU* RL2CapErtmOutgoingQueue::DequeueNextToSend()
	{
	LOG_FUNC
	HL2CapPDU* pdu = NULL;
	if (!iOutgoingQ.IsEmpty())
		{
		pdu = iOutgoingQ.First();
		if (!pdu->IsAwaitingHciCompletion())
			{
			iOutgoingQ.Remove(*pdu);
			pdu->SetQueuedForSend(EFalse);
			}
		else
			{
			LOG(_L("OutgoingQ stalled pending HCI Completion of the previous transmission of the next PDU"))
			pdu = NULL;
			}
		}
	return pdu;
	}


CL2CapEnhancedReTxController* CL2CapEnhancedReTxController::NewL(TL2CAPPort aLocalCID,
																 TL2CAPPort aRemoteCID,
																 CL2CAPMux& aMuxer,
																 CL2CapSDUQueue& aSDUQueue,
																		TL2CapDataControllerConfig* aConfig)
	{
	LOG_STATIC_FUNC
	CL2CapEnhancedReTxController* controller = new (ELeave) CL2CapEnhancedReTxController(aLocalCID, aRemoteCID, aMuxer, aSDUQueue, aConfig);
	CleanupStack::PushL(controller);
	controller->ConstructL();
	CleanupStack::Pop(controller);
	return controller;
	}

void CL2CapEnhancedReTxController::ConstructL()
	{
	LOG_FUNC
	iTransmitter = CL2CapErtmDataTransmitter::NewL(*this);
	iReceiver = CL2CapErtmDataReceiver::NewL(*this);
	}

// ***** CL2CapEnhancedReTxController Implementation
CL2CapEnhancedReTxController::CL2CapEnhancedReTxController(TL2CAPPort aLocalCID,
														   TL2CAPPort aRemoteCID,
														   CL2CAPMux& aMuxer,
														   CL2CapSDUQueue& aSDUQueue,
														   TL2CapDataControllerConfig* aConfig)
 :	CL2CapBasicDataController(aLocalCID, aRemoteCID, aMuxer, aSDUQueue, aConfig, EFalse),
	iDeliverOutgoingDataAndSignalToSduQWhenDone(EFalse),
	iOutgoingQ(*this),
 	iTimerMan(*this)
	{
	LOG_FUNC
	LOG2(_L("CL2CapEnhancedReTxController: local CID = %d, remote CID = %d"), aLocalCID, aRemoteCID)
	}

CL2CapEnhancedReTxController::~CL2CapEnhancedReTxController()
	{
	LOG_FUNC
	iTimerMan.Close();
	iOutgoingQ.Close();
	delete iTransmitter;
	delete iReceiver;
	}

void CL2CapEnhancedReTxController::HandleIncomingIFrameL(RMBufChain& aIFrame)
	{
	LOG_FUNC
	const TUint8 txSeq = HIFramePDU::TxSeqNumber(aIFrame);
	const TUint8 reqSeq = HIFramePDU::ReqSeqNumber(aIFrame);
	const TBool final = HIFramePDU::FinalBit(aIFrame);

	LOG3(_L("Incoming I-Frame: TxSeq = %d, F = %d, ReqSeq = %d"), 
		 txSeq, final, reqSeq)
	
	if (iReceiver->IsTxSeqInvalid(txSeq))
		{
		LOG3(_L("Received invalid TxSeq num %d, [BufferSeq = %d, Receive TxWin = %d]"),
			 txSeq, iReceiver->BufferSeq(), iConfig->PeerTXWindowSize())
		LEAVEL(KErrL2CAPInvalidPacketSequenceNumber);
		}
	else if (!iTransmitter->IsReqSeqValid(reqSeq))
		{
		LOG3(_L("Received invalid ReqSeq num %d, [ExpectedAckSeq = %d, NextTxSeq = %d]"),
			 reqSeq, iTransmitter->ExpectedAckSeq(), iTransmitter->NextTxSeq())
		LEAVEL(KErrL2CAPInvalidAcknowledgementNumber);
		}
	else if (IsFBitValid(final)) // ... and TxSeq and ReqSeq are valid
		{
		// ReqSeq & FBit valid: use this information whatever TxSeq value is, as long as it's
		// within window.

		if (final)
			{
			HandleFinalAck();
			}

		iTransmitter->HandleIncomingIFrame(aIFrame);
		iReceiver->HandleIncomingIFrameL(aIFrame);

		NotifyMuxerOfPdusToSendIfHaveSome();
		} // ReqSeq & Final Valid, TxSeq within window
	else
		{
		LOG(_L("F-bit invalid, dropping the I-Frame"))
		aIFrame.Free();
		}
	}

void CL2CapEnhancedReTxController::HandleIncomingSFrameL(RMBufChain& aSFrame)
	{
	LOG_FUNC

	const TUint8 reqSeq = HSFramePDU::ReqSeqNumber(aSFrame);
	const TBool poll = HSFramePDU::PollBit(aSFrame);
	const TBool final = HSFramePDU::FinalBit(aSFrame);

	LOG4(_L("Incoming S-Frame: S = %d, P = %d, F = %d, ReqSeq = %d"),
		 HSFramePDU::SupervisoryFunction(aSFrame), poll, final, reqSeq)

	if (!iTransmitter->IsReqSeqValid(reqSeq))
		{
		LOG3(_L("Received invalid ReqSeq num %d, [ExpectedAckSeq = %d, NextTxSeq = %d]"),
			 reqSeq, iTransmitter->ExpectedAckSeq(), iTransmitter->NextTxSeq())
		LEAVEL(KErrL2CAPInvalidAcknowledgementNumber);
		}
	else if (IsFBitValid(final)) // ... and TxSeq and ReqSeq are valid
		{
		// ReqSeq & FBit valid: use this information whatever TxSeq value is, as long as it's
		// within window.

		if (final)
			{
			HandleFinalAck();
			}

		iTransmitter->HandleIncomingSFrameL(aSFrame);
		iReceiver->HandleIncomingSFrameL(aSFrame);

		NotifyMuxerOfPdusToSendIfHaveSome();
		}
	else
		{
		LOG(_L("F-bit invalid, dropping the S-Frame"))
		aSFrame.Free();
		}
	}

void CL2CapEnhancedReTxController::HandleFinalAck()
	{
	LOG_FUNC
	// Just do housekeeping here. Transmitter/Receiver maintain their respective WAIT_* states
	// and know what to do with the final ack.
	iTimerMan.StopMonitorTimer();

	iPollSFrameTransmitCount = 0;
	}

void CL2CapEnhancedReTxController::SendIOrRrOrRnrL(TBool aFinal)
	{
	LOG_FUNC
	// Try piggybacking on an I-Frame first...
	HIFramePDU* eyeFrame = iTransmitter->GetIFrameToSendL();
	if (eyeFrame)
		{
		eyeFrame->SetFinalBit(aFinal);
		iOutgoingQ.QueueIFrameL(*eyeFrame);
		}
	else
		{
		// ... no I-Frame to send or can't send one.
		iOutgoingQ.QueueAckingSFrame(*iReceiver->GetAckFrameL(aFinal));
		}
	}

HL2CapPDU* CL2CapEnhancedReTxController::GetPduL()
	{
	LOG_FUNC
	// Note that:
	// - the queue contains S-Frames (naturally) and I-Frames (if they're SREJ-requested by
	// the peer or a Poll request was received and we had a data frame to piggyback the Final
	// response on);
	// - once a packet is put on the Q, it should be virtually considered to be on the air from
	// a logical POV.
	if (!iOutgoingQ.HaveFramesToTransmit())
		{
		// Check for async events to handle.
		// Note: polls are only sent when the OutgoingQ is empty.
		// Reason:
		// 1. The frames on the queue may be I-Frames queued in response to SREJs from the peer.
		// Sending the Poll before responding to the SREJs would lead to a race condition
		// (in WAIT_F) - the SREJs were received when XMIT state = Normal and so SRejActioned was
		// not set to True. So if they're not responded to before the Poll, we'll receive a
		// duplicate SREJ with the Final ack and respond to it with a duplicate I-Frame, which
		// causes disconnect.
		//
		// 2. Even though this may increase the latency of handling an Ack timer expiry if
		// there're packets on the queue, it actually bets on the assumption that these packets are
		// I-Frames SREJ-requested by the peer (same one(s) that caused us to time out) and
		// sending them before the Poll packet still gives the SREJ chance to work and hence
		// minimizes the amount of stuff we'll have to retransmit once we receive the final ack.]
		if ((iReceiver->IsWaitFStatePending() || iTransmitter->IsWaitAckStatePending()) &&
			!IsPollOutstanding())
			// ^- this serializes poll cycles - only one poll can be outstanding at a time
			// but the conditions for WAIT_F and WAIT_ACK are independent and can occur
			// simultaneously.
			{
			// Start a new Poll cycle.
			__ASSERT_DEBUG(iPollSFrameTransmitCount == 0, Panic(EL2CAPPollFrameNumberTransmitIsNotZero));

			HSFramePDU* sFrame = iReceiver->GetAckFrameL();
			sFrame->SetPollBit(ETrue);

			// Give WAIT_ACK preference in case both are outstanding.
			if (iTransmitter->IsWaitAckStatePending())
				{
				LOG(_L("Entering WaitAck"))
				iTransmitter->EnterWaitAckState();
				}
			else
				{
				LOG(_L("Entering WaitF"))
				iReceiver->EnterWaitFState();
				}

			iPollSFrameTransmitCount++;
			iTimerMan.StartMonitorTimer();

			iOutgoingQ.QueueAckingSFrame(*sFrame);
			}
		else // outgoing Q empty & no async events outstanding, can send data
			{
			HIFramePDU* eyeFrame = iTransmitter->GetIFrameToSendL();
			if (eyeFrame)
				{
				iOutgoingQ.QueueIFrameL(*eyeFrame);
				}
			}
		}

	// If an acking frame was queued in the previous step, this flag has been cleared.
	if (iReceiver->SendAck())
		{
		SendIOrRrOrRnrL(EFalse);
		}

	// If there's anything ready to send it's on the queue by now.
	HL2CapPDU* pduToSend = iOutgoingQ.DequeueNextToSend();
	if (pduToSend)
		{
		pduToSend->DeliverOutgoingPDU(*this);
		NotifyMuxerOfPdusToSendIfHaveSome();
		}

	return pduToSend;
	}

void CL2CapEnhancedReTxController::HandlePduSendComplete(HL2CapPDU& aPdu)
	{
	LOG_FUNC
	// We only claim ownership of I-Frames.
	iTransmitter->HciCompletedIFrame(static_cast<HIFramePDU&>(aPdu));
    // May be waiting for the current I-Frame to complete if it's already been
    // requested for retransmission.
    NotifyMuxerOfPdusToSendIfHaveSome();
	}

void CL2CapEnhancedReTxController::HandlePduSendError(HL2CapPDU& /*aPdu*/)
	{
	LOG_FUNC
	// We use protocol-level retransmissions for I-Frames.
	}

TInt CL2CapEnhancedReTxController::HandleOutgoingIFrame(HIFramePDU* aIFrame)
	{
	LOG_FUNC
	__ASSERT_DEBUG(!iTransmitter->InWaitAckState(), Panic(EL2CAPIFrameSentInWaitAck));

	aIFrame->SetPDUCID(iRemoteCID);
	aIFrame->CalculateAndSetFCS();

	aIFrame->SetPduOwner(this);

	LOG3(_L("Outgoing I-Frame: TxSeq = %d, F = %d, ReqSeq = %d"), 
		 aIFrame->TxSeqNumber(), aIFrame->FinalBit(), aIFrame->ReqSeqNumber())

	return KErrNone;
	}

TInt CL2CapEnhancedReTxController::HandleOutgoingSFrame(HSFramePDU* aSFrame)
	{
	LOG_FUNC
	aSFrame->SetPDUCID(iRemoteCID);
	aSFrame->CalculateAndSetFCS();

	LOG4(_L("Outgoing S-Frame: S = %d, P = %d, F = %d, ReqSeq = %d"),
		 aSFrame->SupervisoryFunction(), aSFrame->PollBit(), aSFrame->FinalBit(), aSFrame->ReqSeqNumber())

	return KErrNone;
	}

void CL2CapEnhancedReTxController::SendPeerAckTimerExpired()
	{
	LOG_FUNC
	TRAPD(err, iReceiver->SendPeerAckTimerExpiredL());
	if (err != KErrNone)
		{
		ErrorD(err);
		// RIP
		}
	}

void CL2CapEnhancedReTxController::AckTimerExpired()
	{
	LOG_FUNC
	// Reset the POLL transmit count (it will be incremented when the SFrame[POLL] is pulled for sending
	iPollSFrameTransmitCount = 0;
	iTransmitter->AckTimerExpired();
	}

void CL2CapEnhancedReTxController::LocalBusyDelayTimerExpired()
	{
	LOG_FUNC
	iReceiver->LocalBusyDelayTimerExpired();
	}

void CL2CapEnhancedReTxController::MonitorTimerExpired()
	{
	LOG_FUNC
	__ASSERT_DEBUG(IsPollOutstanding(), Panic(EL2CAPUnexpectedMonitorTimeout));
	__ASSERT_DEBUG(iPollSFrameTransmitCount > 0, Panic(EL2CAPPollFrameNumberTransmitIsZero));

	TInt err = KErrNone;

	if (!TRetransmissionAndFlowControlOption::EnhancedMaxTransmitLessOrEqual(iPollSFrameTransmitCount + 1,
																			 iConfig->MaxTransmit()))
		{
		LOG1(_L("MaxTransmit=%d exceeded by a Poll S-Frame"), iConfig->MaxTransmit())
		err = KErrL2CAPMaxTransmitExceeded;
		}
	else
		{
		HSFramePDU* frame = NULL;
		TRAP(err, frame = iReceiver->GetAckFrameL());
		if (err == KErrNone)
			{
			frame->SetPollBit(ETrue);
			iOutgoingQ.QueueAckingSFrame(*frame);

			iPollSFrameTransmitCount++;
			iTimerMan.StartMonitorTimer();

			NotifyMuxerOfPdusToSendIfHaveSome();
			}
		}
	if (err != KErrNone)
		{
		ErrorD(err);
		// RIP
		}
	}

TUint16 CL2CapEnhancedReTxController::MonitorTimeout()
	{
	return iConfig->MonitorTimeout();
	}

TUint16 CL2CapEnhancedReTxController::RetransmissionTimeout()
	{
	return iConfig->RetransmissionTimeout();
	}

TUint16 CL2CapEnhancedReTxController::PeerRetransmissionTimeout()
	{
	return iConfig->PeerRetransmissionTimeout();
	}

void CL2CapEnhancedReTxController::OutgoingPduAvailableOnSduQ()
	{
	LOG_FUNC
	NotifyMuxerOfPdusToSendIfHaveSome();
	}

void CL2CapEnhancedReTxController::SetIncomingSduQFull(TBool aIncomingSduQFull)
	{
	LOG_FUNC
	TRAPD(err, iReceiver->SetIncomingSduQFullL(aIncomingSduQFull));
	if (err != KErrNone)
		{
		ErrorD(err);
		// RIP
		}
	}

TBool CL2CapEnhancedReTxController::DeliverOutgoingDataAndSignalToSduQWhenDone()
	{
	LOG_FUNC
	iDeliverOutgoingDataAndSignalToSduQWhenDone = ETrue;
	// Returning true means we don't have any outstanding data to deliver and
	// hence can be deleted immediately.
	return !iTransmitter->HaveUnackedIFrames();
	}

inline void CL2CapEnhancedReTxController::NotifyMuxerOfPdusToSendIfHaveSome()
    {
    LOG_FUNC
    // This is intended to be your one-stop kick-the-muxer routine.
    // It should evaluate all conditions that may cause us to want to send
    // a PDU and should be called at the end of handling of every event which
    // may cause us to want to send stuff.
    //
    // The conditions are:
    // - if there's something on the OutgoingQ, then always transmit it ASAP,
    //   Outgoing Q is "the air" from the protocols point of view;
    // - if we're not in WAIT_ACK (now called WAIT_F in the spec), then send
    //   data:
    //      - I-Frames requested for retransmission (with REJ or ack timer expiry,
    //        SREJ-requested ones are put on the OutgoingQ),
    //      - new I-Frames from SDUs on the SDU Q.
    // - if there's some signalling to be send that hasn't been directly put
    //   on the OutgoingQ - send it:
    //      - need to go through WAIT_ACK and can enter it (WAIT_F not in progress),
    //      - need to go through WAIT_F and can enter it (WAIT_ACK not in progress);
    //      - time to send an acknowledgement to the peer.

    if (iOutgoingQ.HaveFramesToTransmit()  ||  // various stuff already on the transmit Q,
        // ... or data:
        (!iTransmitter->InWaitAckState() &&
            // ... in need of retransmission ...
            ((iTransmitter->IsRetransmittingUnackedIFrames() && !iTransmitter->IsNextUnackedIFrameAwaitingHciCompletion()) ||
            // ... or waiting to be pulled from SDU Q,
            (!iTransmitter->IsRetransmittingUnackedIFrames() && iSDUQueue.HavePDUToSend() && iTransmitter->HaveSpaceInOutgoingWindow()))
        ) ||
        // or some boring signalling:
        // ... want to enter WAIT_ACK and not already in WAIT_F ...
        (iTransmitter->IsWaitAckStatePending() && !iReceiver->InWaitFState()) ||
        // ... want to enter WAIT_F and not already in WAIT_ACK ...
        (iReceiver->IsWaitFStatePending()      && !iTransmitter->InWaitAckState()) ||
        // ... time to send an acknowledgement.
        iReceiver->SendAck())
        {
        iMuxer.PDUAvailable();
        }

    #ifdef __L2CAP_ERTM_GETPDU_DEBUG
    LOG1(_L("iOutgoingQ.HaveFramesToTransmit(): %d"), iOutgoingQ.HaveFramesToTransmit())
    LOG1(_L("iTransmitter->HaveSpaceInOutgoingWindow(): %d"), iTransmitter->HaveSpaceInOutgoingWindow())
    LOG1(_L("iTransmitter->InWaitAckState(): %d"), iTransmitter->InWaitAckState())
    LOG1(_L("iTransmitter->IsRetransmittingUnackedIFrames(): %d"), iTransmitter->IsRetransmittingUnackedIFrames())
    if (iTransmitter->IsRetransmittingUnackedIFrames())
        {
        LOG1(_L("iTransmitter->IsNextUnackedIFrameAwaitingHciCompletion(): %d"), iTransmitter->IsNextUnackedIFrameAwaitingHciCompletion())
        }
    LOG1(_L("iChannel.DataQueue().HavePDUToSend(): %d"), iSDUQueue.HavePDUToSend())
    LOG1(_L("iTransmitter->IsWaitAckStatePending(): %d"), iTransmitter->IsWaitAckStatePending())
    LOG1(_L("iReceiver->InWaitFState(): %d"), iReceiver->InWaitFState())
    LOG1(_L("iReceiver->IsWaitFStatePending(): %d"), iReceiver->IsWaitFStatePending())
    LOG1(_L("iTransmitter->InWaitAckState(): %d"), iTransmitter->InWaitAckState())
    LOG1(_L("iReceiver->SendAck(): %d"), iReceiver->SendAck())
    #endif
    }