bluetooth/btstack/l2cap/L2CapDataController.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) 2004-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 <e32base.h>

#include "L2CapDataController.h"
#include "L2CapEnhancedDataController.h"

#include "L2CapSDUQueue.h"
#include "l2signalmgr.h"

#include "btsockettimer.h"
 
#include "l2util.h"
#include "L2CapDebugControlInterface.h"

#include "l2constants.h"	

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

using namespace L2CapDataUtils;

// ***** CL2CapBasicDataController Implementation
/*static*/ CL2CapBasicDataController* CL2CapBasicDataController::NewL(TL2CAPPort aLocalCID, TL2CAPPort aRemoteCID, CL2CAPMux& aMuxer, CL2CapSDUQueue& aSDUQueue, TL2CapDataControllerConfig* aConfig)
	{
	LOG_STATIC_FUNC
	// Create the required data controller. 
	CL2CapBasicDataController* controller = NULL;

	switch(aConfig->LinkMode())
		{
		case EL2CAPBasicMode:
			LOG(_L("FEC L2CapBasicDataController Basic Mode"))
			controller = new(ELeave) CL2CapBasicDataController(aLocalCID, aRemoteCID, aMuxer, aSDUQueue, aConfig, ETrue);
			break;
			
		case EL2CAPRetransmissionMode:
			LOG(_L("FEC L2CapBasicDataController Retransmission Mode"))
			controller = new(ELeave) CL2CapDataReTxController(aLocalCID, aRemoteCID, aMuxer, aSDUQueue, aConfig);		
			break;
			
		case EL2CAPFlowControlMode:
			LOG(_L("FEC L2CapBasicDataController Flow Control Mode"))
			controller = new(ELeave) CL2CapDataFlowController(aLocalCID, aRemoteCID, aMuxer, aSDUQueue, aConfig);		
			break;

		case EL2CAPStreamingMode:
			LOG(_L("FEC L2CapBasicDataController Streaming Mode"))
			controller = new(ELeave) CL2CapStreamingController(aLocalCID, aRemoteCID, aMuxer, aSDUQueue, aConfig);		
			break;

		case EL2CAPEnhancedRetransmissionMode:
			LOG(_L("FEC L2CapBasicDataController Enhnaced Retransmission Mode"))
			controller = CL2CapEnhancedReTxController::NewL(aLocalCID, aRemoteCID, aMuxer, aSDUQueue, aConfig);		
			break;
		
		default:
			Panic(EL2CAPInvalidLinkMode);
			break;
		};
	
	return controller;
	}

CL2CapBasicDataController::CL2CapBasicDataController(TL2CAPPort aLocalCID, TL2CAPPort aRemoteCID, CL2CAPMux& aMuxer, CL2CapSDUQueue& aSDUQueue, TL2CapDataControllerConfig* aConfig, TBool aIsBasicDataVersion)
 : iSDUQueue(aSDUQueue),
   iMuxer(aMuxer),
   iLocalCID(aLocalCID),
   iRemoteCID(aRemoteCID),
   iConfig(aConfig),
   iIsBasicDataVersion(aIsBasicDataVersion)
	{
	LOG_FUNC
	// Register with the Mux.
	iMuxer.RegisterDataPDUHandler(*this);

	L2CAP_DEBUG(ObjectAllocation(L2capDebugInfo::EDataPlanes,
	                             L2capDebugInfo::EAllocated));	
	}

CL2CapBasicDataController::~CL2CapBasicDataController()
	{
	LOG_FUNC
	delete iConfig;

	L2CAP_DEBUG(ObjectAllocation(L2capDebugInfo::EDataPlanes,
	                             L2capDebugInfo::EDeleted));	
	}	

HL2CapPDU* CL2CapBasicDataController::GetPdu()
	{
	LOG_FUNC
	HL2CapPDU* pdu = NULL;
	TRAPD(err, pdu = GetPduL());
	if (err != KErrNone)
		{
		// The protocol here is that all leaves propagated up to here cause
		// us to ErrorD() ourselves, which will destruct data plane objects
		// (the data controller, SDU queue and the whole shebang) and error
		// the socket with the leave error code.
		ErrorD(err);
		}
	return pdu;
	}

HL2CapPDU* CL2CapBasicDataController::GetPduL()
	{
	LOG_FUNC
	HL2CapPDU* pduToSend = NULL;
	if((pduToSend = iSDUQueue.GetPDU()) != NULL)
		{
		pduToSend->SetPDUCID(iRemoteCID);
		}
	return pduToSend;
	}

void CL2CapBasicDataController::ProcessFlushTimerExpiry()
	{
	LOG_FUNC
	iMuxer.ProcessFlushTimerExpiry();
	}

void CL2CapBasicDataController::OutgoingPduAvailableOnSduQ()
	{
	LOG_FUNC
	iMuxer.PDUAvailable();
	}

void CL2CapBasicDataController::UpdateConfig(TL2CapDataControllerConfig* aConfig)
	{
	LOG_FUNC
	TBool channelPriorityUpdated = (iConfig->ChannelPriority() != aConfig->ChannelPriority());
	// Delete the existing config, and replace it with the new one.
	delete iConfig;
	iConfig = aConfig;

	if(channelPriorityUpdated)
		{
		iMuxer.ChannelPriorityUpdated(*this);
		}
	}

void CL2CapBasicDataController::UpdateChannelPriority(TUint8 aNewPriority)
	{
	LOG_FUNC
	if(iConfig->ChannelPriority() != aNewPriority)
		{
		iConfig->SetChannelPriority(aNewPriority);
		iMuxer.ChannelPriorityUpdated(*this);
		}
	}
	
	
void CL2CapBasicDataController::ErrorD(TInt aError)
	{
	LOG_FUNC
	LOG1(_L("FEC CL2CapBasicDataController::Error %d"), aError)
	iDataPlaneErrored = ETrue;
	iSDUQueue.ErrorD(aError);
	}

void CL2CapBasicDataController::SetIncomingSduQFull(TBool /*aIncomingSduQFull*/)
	{
	LOG_FUNC
	// No action can be taken in basic mode.
	}

TBool CL2CapBasicDataController::DeliverOutgoingDataAndSignalToSduQWhenDone()
	{
	LOG_FUNC
	// We can be deleted right away.
	return ETrue;
	}

void CL2CapBasicDataController::DeregisterFromMuxer()
	{
	LOG_FUNC
	iMuxer.DataChannelRemoved(this);
	}

#ifdef _DEBUG
TInt CL2CapBasicDataController::GetDataPlaneConfig(TL2DataPlaneConfig& conf) const
	{
	LOG_FUNC
	TInt rerr = KErrNone;
	// Populate the debug structure.
	if(iConfig)
		{
		conf.iPriority = iConfig->ChannelPriority();
		conf.iLinkMode = iConfig->LinkMode();
		conf.iTxWindowSize = iConfig->TXWindowSize();
		conf.iMaxTransmit = iConfig->MaxTransmit();
		conf.iRetransmissionTimeout = iConfig->RetransmissionTimeout();
		conf.iMonitorTimeout = iConfig->MonitorTimeout();
		}
	else
		{
		rerr = KErrNotReady;
		}
	return rerr;
	}
#endif

TBool CL2CapBasicDataController::HandleIncomingDataFrame(RMBufChain& aDataFrame)
	{
	LOG_FUNC
	TBool packetDelivered = EFalse;
	TRAPD(err, packetDelivered = HandleIncomingDataFrameL(aDataFrame));
	if (err != KErrNone)
		{
		// The protocol here is that all leaves propagated up to here cause
		// us to ErrorD() ourselves, which will destruct data plane objects
		// (the data controller, SDU queue and the whole shebang) and error
		// the socket with the leave error code.
		ErrorD(err);

		if (aDataFrame.IsEmpty())
			{
			packetDelivered = ETrue;
			}
		// else frame will be offered to other data controllers
		}
	return packetDelivered;
	}

void CL2CapBasicDataController::HandleIncomingIFrameL(RMBufChain& /*aDataFrame*/)
	{
	LOG_FUNC
	Panic(EL2CAPIFrameOrSFrameHandledByBasicController);
	}

void CL2CapBasicDataController::HandleIncomingBFrameL(RMBufChain& aDataFrame)
	{
	LOG_FUNC
	iSDUQueue.PutBFramePDU(aDataFrame);
	}

void CL2CapBasicDataController::HandleIncomingSFrameL(RMBufChain& /*aDataFrame*/)
	{
	LOG_FUNC
	Panic(EL2CAPIFrameOrSFrameHandledByBasicController);
	}

TBool CL2CapBasicDataController::HandleIncomingDataFrameL(RMBufChain& aDataFrame)
	{
	LOG_FUNC
	TBool PDUProcessed = EFalse;
	TInt err = KErrNone;
	
	if(HL2CapPDU::PDUCID(aDataFrame) == iLocalCID)
		{
		if(iIsBasicDataVersion)
			{
			// This can only be a B-Frame.
			if(HBFramePDU::CheckDecode(aDataFrame) == KErrNone)
			   	{
			   	HandleIncomingBFrameL(aDataFrame);
			   	}
			else
				{
				// Invalid frame.  Drop it.
				aDataFrame.Free();
				LOG(_L("FEC L2CapBasicDataController B-Frame Dropped"))
				}
			}
		else
			{
			if(HIFramePDU::IFrameIdentifier(aDataFrame))
				{
				// This is an I-Frame

				// Check stuff that causes us to drop frames first:
				// - FCS (2.1 Core Spec Addendum 1: 3.3.7 Invalid Frame Detection Algorithm pt. 2)
				// - length field matching the actual payload length (funnily 3.3.7 doesn't say
				//   what to do with it, pt. 7.2.2 Recombination Of L2CAP PDUs mentions it in
				//   passing).
				// Note: the frame is dropped when length is incorrect (instead of disconnecting)
				// because we don't know where FCS is (it's at the end of the packet and we don't
				// know where that is), so have to assume that the length field problem is due to
				// transmission corruption and not the remote misbehaving.
				if (HIFramePDU::CheckPayloadDecode(aDataFrame) != KErrNone ||
					HL2CapPDU::CheckDecode(aDataFrame) != KErrNone)
					{
					aDataFrame.Free();
					LOG(_L("FEC L2CapBasicDataController I-Frame Dropped"))
					}
				else
					{
					// Now check stuff that causes us to disconnect (per CSA1 3.3.7).
					err = HIFramePDU::CheckLengthWithinLimits(aDataFrame, iConfig->IncomingMps());
					if (err != KErrNone)
						{
						LOG(_L("FEC L2CapBasicDataController I-Frame invalid"))

						aDataFrame.Free();
						// This will cause the channel to be closed.
						LEAVEL(err);
						}
					else
						{
						HandleIncomingIFrameL(aDataFrame);
						}
					}
				}
			else
				{
				// S-Frame

				// Check stuff that causes the frame to be dropped first.
				if (HSFramePDU::CheckPayloadDecode(aDataFrame) != KErrNone ||
					HL2CapPDU::CheckDecode(aDataFrame) != KErrNone)
					{
					LOG(_L("FEC L2CapBasicDataController S-Frame Dropped"))
					}
				else
					{
					// Now check stuff that causes us to disconnect (per CSA1 3.3.7).
					err = HSFramePDU::CheckLengthField(aDataFrame);
					if (err != KErrNone)
						{
						LOG(_L("FEC L2CapBasicDataController S-Frame Invalid"))

						aDataFrame.Free();
						// This will cause the channel to be closed.
						LEAVEL(err);
						}
					else
						{
						HandleIncomingSFrameL(aDataFrame);
					  	}
					}
				// Finished with the S-Frame.
				aDataFrame.Free();
				}
			}
		PDUProcessed = ETrue;
		}
	return PDUProcessed;
	}

// Outgoing PDU handler.
TInt CL2CapBasicDataController::HandleOutgoingIFrame(HIFramePDU* /*aIFrame*/)
	{
	LOG_FUNC
	return KErrNone;
	}

TInt CL2CapBasicDataController::HandleOutgoingBFrame(HBFramePDU* /*aBFrame*/)
	{
	LOG_FUNC
	return KErrNone;
	}

TInt CL2CapBasicDataController::HandleOutgoingGFrame(HGFramePDU* /*aGFrame*/)
	{
	LOG_FUNC
	return KErrNone;
	}

TInt CL2CapBasicDataController::HandleOutgoingCFrame(HCFramePDU* /*aCFrame*/)
	{
	LOG_FUNC
	return KErrNone;
	}

TInt CL2CapBasicDataController::HandleOutgoingSFrame(HSFramePDU* /*aSFrame*/)
	{
	LOG_FUNC
	return KErrNone;
	}

// ***** CL2CapDataFlowController Implementation
CL2CapDataFlowController::CL2CapDataFlowController(TL2CAPPort aLocalCID, TL2CAPPort aRemoteCID, CL2CAPMux& aMuxer, CL2CapSDUQueue& aSDUQueue, TL2CapDataControllerConfig* aConfig)
 : CL2CapBasicDataController(aLocalCID, aRemoteCID, aMuxer, aSDUQueue, aConfig, EFalse),
   iPendingSentPDUs(_FOFF(HIFramePDU, iLink)),
   iSentPDUs(_FOFF(HIFramePDU, iLink)),
   iTimerMan(*this),
   iIncomingSduQFull(EFalse)
	{
	LOG_FUNC
	}

CL2CapDataFlowController::~CL2CapDataFlowController()
	{
	LOG_FUNC
	// Some PDUs may hold a reference to this data controller.
	TDblQueIter<HIFramePDU> pendingIter(iPendingSentPDUs);
	TDblQueIter<HIFramePDU> sentIter(iSentPDUs);
	HIFramePDU* pduPtr;
	
	while((pduPtr = pendingIter++) != NULL)
		{
		LOG1(_L("Deregistering TxSeq = %d"), pduPtr->TxSeqNumber());
		pduPtr->DeregisterPduOwner();
		}

	// Delete any PDUs that have already been sent.
	while((pduPtr = sentIter++) != NULL)
		{
		delete pduPtr;
		}

	// Cancel any outstanding timers.
	iTimerMan.Close();
	}


void CL2CapDataFlowController::ProcessFlushTimerExpiry()
	{
	LOG_FUNC
	iMuxer.ProcessFlushTimerExpiry();
	PDUAvailable();

	if(!iSentPDUs.IsEmpty())
		{
		iNextTxSeq = Mod64(iSentPDUs.Last()->TxSeqNumber() + 1);
		}
	else
		{
		iNextTxSeq = iExpectedAckSeq;
		}
	}

void CL2CapDataFlowController::HandleIncomingIFrameL(RMBufChain& aDataFrame)
	{
	LOG_FUNC
	// Check if flow control is active for incoming data.
	if(iIncomingSduQFull)
		{
		// Check that the PDU sequence number is within the expected range.
		if(!iSenderTxWindowClosed && Mod64(HIFramePDU::TxSeqNumber(aDataFrame) - iLastAckSentRxSeqNum) >= iConfig->PeerTXWindowSize())
			{
			// The number is not in range.  No more I-Frames will be processed
			// until flow control is deactivated.
			iSenderTxWindowClosed = ETrue;
			}
		}

	LOG(_L("FEC CL2CapDataFlowController: HandleIncomingIFrame"))
	LOG1(_L("\tTxSeqNumber = %d"), HIFramePDU::TxSeqNumber(aDataFrame))
	LOG1(_L("\tLastAckSentRxSeqNum = %d"), iLastAckSentRxSeqNum)
	
	if(!iSenderTxWindowClosed)
		{
		ProcessIFrameL(aDataFrame);
		}
	else
		{
		// Drop the frame.
		aDataFrame.Free();
		LOG(_L("FEC CL2CapDataFlowController: Incoming PDU seq number of I-Frame is not in range!"))
		}
	}

void CL2CapDataFlowController::ProcessIFrameL(RMBufChain& aDataFrame)
	{
	LOG_FUNC
	iExpectedTxSeq = Mod64(iExpectedTxSeq + 1); 
	iExpectedAckSeq = HIFramePDU::ReqSeqNumber(aDataFrame);
	
	LEAVEIFERRORL(iSDUQueue.PutIFramePDU(aDataFrame));

	// Check if the acknowledged frame(s) has created
	// space in the window.	
	RemoveAckedPDUsFromSentQueue();

	// Only send an ack if incoming data is not currently flow controlled off.
	if(!iIncomingSduQFull)
		{
		// Check if the peer frames need to be acknowledged now.
		// Check the peer TxWindow.  This check is
		// (Number of I-Frames recv since last Ack was sent > (TxWindow - Constant)
		if(Mod64(iExpectedTxSeq - iLastAckSentRxSeqNum) > (iConfig->PeerTXWindowSize() - KTxWinAckThresholdOffset))
			{
			// Send an ack.
			iSendAckToPeer = ETrue;
			}
		else
			{
			// If the timer is not running, start it.
			if(!iTimerMan.IsSendPeerAckTimerRunning())
				{
				if(!iTimerMan.StartSendPeerAckTimer())
					{
					// A timer could not be started.  Send an ack immediately.
					iSendAckToPeer = ETrue;
					}
				}
			}
		}
	// Check if any outbound data can be sent.
	PDUAvailable();
	}

void CL2CapDataFlowController::HandleIncomingSFrameL(RMBufChain& aDataFrame)
	{
	LOG_FUNC
	// Check that the function is RR.  If not this S-Frame is not valid for
	// flow control mode.
	if(HSFramePDU::SupervisoryFunction(aDataFrame) == EReceiverReady)
		{
		iExpectedAckSeq = HSFramePDU::ReqSeqNumber(aDataFrame);
		// Check if the acknowledged frame(s) has created
		// space in the window.	
		RemoveAckedPDUsFromSentQueue();
		PDUAvailable();				
		LOG(_L("FEC CL2CapDataFlowController: Incoming S-Frame: RR"))				
		LOG1(_L("\tiLastAckedFrameNum = %d"),  iExpectedAckSeq)
		}
	}

TInt CL2CapDataFlowController::HandleOutgoingIFrame(HIFramePDU* aIFrame)
	{
	LOG_FUNC
	aIFrame->SetPDUCID(iRemoteCID);
	aIFrame->SetTxSeqNumber(iNextTxSeq);
	LOG(_L("FEC CL2CapDataFlowController: HandleOutgoingIFrame"))
	LOG1(_L("\tTxSeqNumber = %d"), iNextTxSeq)
	iNextTxSeq = Mod64(iNextTxSeq + 1);

	// If flow controlled off - don't acknowledge any new I-Frames
	aIFrame->SetReqSeqNumber(iIncomingSduQFull ? iLastAckSentRxSeqNum : iExpectedTxSeq);

	// Set the retransmission disable bit if flow control is on.
	aIFrame->SetRetransmitDisable(iIncomingSduQFull);
	aIFrame->CalculateAndSetFCS();

	aIFrame->iLink.Deque(); // just in case, it shouldn't be on any list at this point
	aIFrame->SetPduOwner(this);
	iPendingSentPDUs.AddLast(*aIFrame);
	
	// Cancel the peer ack timer.
	iTimerMan.StopSendPeerAckTimer();
	iSendAckToPeer = EFalse;

	// Store the last acknowledged frame index.
	if(!iIncomingSduQFull)
		{
		iLastAckSentRxSeqNum = iExpectedTxSeq;
		}
	LOG1(_L("\tLastAckSentRxSeqNum = %d"), iLastAckSentRxSeqNum)
	
	return KErrNone;
	}

TInt CL2CapDataFlowController::HandleOutgoingSFrame(HSFramePDU* aSFrame)
	{
	LOG_FUNC
	aSFrame->SetPDUCID(iRemoteCID);
	// If flow controlled off - don't acknowledge any new I-Frames
	aSFrame->SetReqSeqNumber(iIncomingSduQFull ? iLastAckSentRxSeqNum : iExpectedTxSeq);
	LOG(_L("FEC CL2CapDataFlowController: HandleOutgoingSFrame"))		
	LOG1(_L("\tReqSeqNUmber = %d"), iIncomingSduQFull ? iLastAckSentRxSeqNum : iExpectedTxSeq)
	// Set the retransmission disable bit if flow control is on.
	aSFrame->SetRetransmitDisable(iIncomingSduQFull);
	aSFrame->CalculateAndSetFCS();

	// If the monitor timer is currently running.  Re-start it.
	if(!iTimerMan.IsAckTimerRunning())
		{
		iTimerMan.StartMonitorTimer();
		}

	// Store the last acknowledged frame index.
	if(!iIncomingSduQFull)
		{
		iLastAckSentRxSeqNum = iExpectedTxSeq;
		}
		
	// Cancel the peer ack timer.
	iTimerMan.StopSendPeerAckTimer();
	iSendAckToPeer = EFalse;
	LOG1(_L("\tLastAckSentRxSeqNum = %d"), iLastAckSentRxSeqNum)
	return KErrNone;
	}

TBool CL2CapDataFlowController::CanSendPDU()
	{
	LOG_FUNC
	TBool canSend = ETrue;

	// This condition is (V(S) - V(A))mod64 >= TxWindow
	if(Mod64(iNextTxSeq - iExpectedAckSeq) >= iConfig->TXWindowSize())
		{
		canSend = EFalse;
		}	
	return canSend;
	}

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

	if(CanSendPDU())
		{	
		pduToSend = iSDUQueue.GetPDU();
		}

	if(pduToSend)
		{
		pduToSend->DeliverOutgoingPDU(*this);
		}

	// Note:  If pduToSend has a valid pointer then the call to 
	// pduToSend->DeliverOutgoingPDU(*this) (above) will clear the 
	// iSendAckToPeer flag.
	if(iSendAckToPeer)
		{
		pduToSend = HSFramePDU::New(EReceiverReady);
		if(pduToSend)
			{
			pduToSend->DeliverOutgoingPDU(*this);
			}
		else
			{
			ErrorD(KErrNoMemory);
			// We've been deleted!
			}
		}
	return pduToSend;
	}


void CL2CapDataFlowController::PDUAvailable()
	{
	LOG_FUNC

	// Note that CanSend only checks whether there's available outgoing window space,
	// not whether there really is some outstanding data on SDU Q. GetPduL will check
	// that and do nothing if there isn't.
	if(CanSendPDU() || iSendAckToPeer)
		{
		iMuxer.PDUAvailable();
		}
	}

void CL2CapDataFlowController::RemoveAckedPDUsFromSentQueue()
	{
	LOG_FUNC
	TDblQueIter<HIFramePDU> iter(iSentPDUs);
	HIFramePDU* pduPtr;
	TInt ackedPDUs = 0;
	TInt count = 0;

	TUint8 frameSeqNumber = Mod64(iExpectedAckSeq - 1);

	// Find out how many PDU's have been ack'd
	while((pduPtr = iter++) != NULL)
		{
		if(pduPtr->TxSeqNumber() == frameSeqNumber)
			{
			// Increment the count by one and exit the loop.
			ackedPDUs = count + 1;
			break;
			}
		count++;
		}

	// Remove all ack'd PDUs
	iter.SetToFirst();
	for(TInt i=0;i<ackedPDUs;i++)
		{
		pduPtr = iter++;
		delete pduPtr;
		}

	// Some PDUs have been ack'd.
	if(ackedPDUs)
		{
		if(iSentPDUs.IsEmpty())
			{
			iTimerMan.StartMonitorTimer();
			}
		else
			{
			iTimerMan.StopMonitorTimer();
			iTimerMan.StartAckTimer();
			}
		}
	}

void CL2CapDataFlowController::MonitorTimerExpired()
	{
	LOG_FUNC
	// The monitor timer has expired.  Send a S-Frame to the peer by setting
	// the ack outstanding flag and calling PDUAvailable.  This method will
	// call this back at which point the S-Frame will be created.
	LOG(_L("FEC CL2CapDataFlowController::MonitorTimerExpired"))
	iSendAckToPeer = ETrue;
	PDUAvailable();
	}
	
	
void CL2CapDataFlowController::AckTimerExpired()
	{
	LOG_FUNC
	// Remove the PDU that the timer is supervising.  This can
	// only be the first Sent PDU.
	LOG(_L("FEC CL2CapDataFlowController::AckTimerExpired"))
	__ASSERT_DEBUG(!iSentPDUs.IsEmpty(), Panic(EL2CAPAckTimerExpiryWithoutPDUToSupervise));
	
	HL2CapPDU* pdu = iSentPDUs.First();
	delete pdu;

	iExpectedAckSeq = Mod64(iExpectedAckSeq + 1);

	if(iSentPDUs.IsEmpty())
		{
		iTimerMan.StartMonitorTimer();
		}
	else
		{
		iTimerMan.StopMonitorTimer();
		iTimerMan.StartAckTimer();
		}
	PDUAvailable();	
	}

void CL2CapDataFlowController::SendPeerAckTimerExpired()
	{
	LOG_FUNC

	// Indicate that an ack should be sent to the peer to
	// acknowledge receipt of I-Frames.
	iSendAckToPeer = ETrue;
	PDUAvailable();	
	}

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

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

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


void CL2CapDataFlowController::HandlePduSendComplete(HL2CapPDU& aPdu)
	{
	LOG_FUNC

	// We only claim ownership of I-Frames.
	HIFramePDU& IFrame = static_cast<HIFramePDU&>(aPdu);

	// Remove the PDU from the pending send queue.
	IFrame.iLink.Deque();
	
	// If there are outstanding (i.e., un-ack'ed) PDU's, the
	// Ack timer will already be running.  Otherwise it should
	// be started.
	if(iSentPDUs.IsEmpty())
		{
		iTimerMan.StartAckTimer();
		}

	// It's waiting for acknowledgement now.
	iSentPDUs.AddLast(IFrame);
	PDUAvailable();
	}

void CL2CapDataFlowController::HandlePduSendError(HL2CapPDU& /*aPdu*/)
	{
	LOG_FUNC
	// In FC mode the frame will get deleted when we get an acknowledgement.
	// In RTM, protocol-level retransmissions will be run when peer rejects or
	// our ack timer expires.
	}


void CL2CapDataFlowController::SetIncomingSduQFull(TBool aIncomingSduQFull)
	{
	LOG_FUNC
	// Check that the FC status has changed
	if(iIncomingSduQFull != aIncomingSduQFull)
		{
		iIncomingSduQFull = aIncomingSduQFull;
		iSenderTxWindowClosed = EFalse;

		// Send an acknowledgement to the peer indicate the new
		// flow control status.
		iSendAckToPeer = ETrue;
		PDUAvailable();
		}
	}


// ***** CL2CapDataReTxController Implementation
CL2CapDataReTxController::CL2CapDataReTxController(TL2CAPPort aLocalCID, TL2CAPPort aRemoteCID, CL2CAPMux& aMuxer, CL2CapSDUQueue& aSDUQueue, TL2CapDataControllerConfig* aConfig)
 : CL2CapDataFlowController(aLocalCID, aRemoteCID, aMuxer, aSDUQueue, aConfig),
   iRejectPDU(0),
   iRetransmitSentPDUs(EFalse),
   iRejectSent(EFalse),
   iRetransmissionDisabled(EFalse),
   iRestartAckTimer(EFalse),
   iRetransTxVal(iNextTxSeq)
	{
	LOG_FUNC
	}
	
void CL2CapDataReTxController::HandleIncomingIFrameL(RMBufChain& aDataFrame)
	{
	LOG_FUNC
	TBool newRtxDisable = HIFramePDU::RetransmitDisable(aDataFrame);
	if(iExpectedTxSeq == HIFramePDU::TxSeqNumber(aDataFrame))
		{
		LOG(_L("FEC CL2CapDataReTxController: HandleIncomingIFrame"))
		LOG1(_L("\tTxSeqNumber = %d"), HIFramePDU::TxSeqNumber(aDataFrame))	
		ProcessIFrameL(aDataFrame);
		iRejectSent = EFalse;
		}
	else
		{
		// This I-Frame has an invalid sequence number.
		// Check that a Reject Exception is not already being
		// handled.
		if(!iRejectSent)
			{
			iRejectPDU = HSFramePDU::NewL(EReject);
			iRejectPDU->DeliverOutgoingPDU(*this);
			// Send the Reject PDU.
			iMuxer.PDUAvailable();
			iRejectSent = ETrue;
			}
		aDataFrame.Free();
		LOG(_L("FEC CL2CapDataReTxController: Incoming PDU seq number of I-Frame is not in range!"))
		}
	
	// Check if the re-transmission disable bit has transitioned.
	if(iRetransmissionDisabled != newRtxDisable)
		{
		if(newRtxDisable)
			{
			// Re-transmission has been disabled.
			iTimerMan.StartMonitorTimer();
			}
		else
			{
			// Re-transmission has been enabled.
			if(!iSentPDUs.IsEmpty())
				{
				iTimerMan.StopMonitorTimer();
				iTimerMan.StartAckTimer();
				}
			}
		iRetransmissionDisabled = newRtxDisable;
		}
	}

void CL2CapDataReTxController::HandleIncomingSFrameL(RMBufChain& aDataFrame)
	{
	LOG_FUNC
	if(HSFramePDU::SupervisoryFunction(aDataFrame) == EReject)
		{
		iRetransmitSentPDUs = ETrue;
		iRetransTxVal = HSFramePDU::ReqSeqNumber(aDataFrame);
		LOG(_L("FEC CL2CapDataReTxController: Incoming S-Frame: Reject"))		
		}
	iExpectedAckSeq = HSFramePDU::ReqSeqNumber(aDataFrame);
	LOG(_L("FEC CL2CapDataReTxController: Incoming S-Frame"))
	LOG1(_L("\tLastAckedFrameNum = %d"), iExpectedAckSeq)

	TBool newRtxDisable = HSFramePDU::RetransmitDisable(aDataFrame);

	// Check if the acknowledged frame(s) has created
	// space in the window.	
	RemoveAckedPDUsFromSentQueue();
	PDUAvailable();				
	
	// Check if the re-transmission disable bit has transitioned.
	if(iRetransmissionDisabled != newRtxDisable)
		{
		if(newRtxDisable)
			{
			// Re-transmission has been disabled.
			iTimerMan.StartMonitorTimer();
			}
		else
			{
			// Re-transmission has been enabled.
			if(!iSentPDUs.IsEmpty())
				{
				iTimerMan.StopMonitorTimer();
				iTimerMan.StartAckTimer();
				}
			}
		iRetransmissionDisabled = newRtxDisable;
		}
	}

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

	// Check the re-transmission disabled flag.  If its set
	// then only S-Frames can be sent.
	if(!iRetransmissionDisabled && !iDataPlaneErrored)
		{
		if(iRejectPDU)
			{
			pduToSend = iRejectPDU;
			iRejectPDU = NULL;
			}
		else
			{
			if(iRetransmitSentPDUs)
				{
				pduToSend = RetransmitSentPDU();
				}	

			if(!pduToSend && CanSendPDU())
				{	
				pduToSend = iSDUQueue.GetPDU();
				}
			}
		}

	if(pduToSend)
		{
		pduToSend->DeliverOutgoingPDU(*this);
		}

	// Note:  If pduToSend has a valid pointer then the call to 
	// pduToSend->DeliverOutgoingPDU(*this) (above) will clear the 
	// iSendAckToPeer flag.
	if(iSendAckToPeer)
		{
		pduToSend = HSFramePDU::New(EReceiverReady);
		if(pduToSend)
			{
			pduToSend->DeliverOutgoingPDU(*this);
			}
		else
			{
			ErrorD(KErrNoMemory);
			// We've been deleted!
			}
		}
	return pduToSend;
	}

void CL2CapDataReTxController::PDUAvailable()
	{
	LOG_FUNC

	// Note that CanSend only checks whether there's available outgoing window space,
	// not whether there really is some outstanding data on SDU Q. GetPduL will check
	// that and do nothing if there isn't.
	if(CanSendPDU() || iSendAckToPeer || iRetransmitSentPDUs)
		{
		iMuxer.PDUAvailable();
		}
	}

void CL2CapDataReTxController::AckTimerExpired()
	{
	LOG_FUNC
	__ASSERT_DEBUG(!iSentPDUs.IsEmpty(), Panic(EL2CAPAckTimerExpiryWithoutPDUToSupervise));

	// Check if the PDU can be re-sent (i.e., the Max.
	// transmit has not been exceeded.)
	
    HIFramePDU* pdu = iSentPDUs.First();
        
	if(pdu->CanTransmit(iConfig->MaxTransmit()))
		{
		// Set the next frame to be transmitted to the PDU 
		// whose timer expired.
		iRetransTxVal = pdu->TxSeqNumber();
		iRetransmitSentPDUs = ETrue;
		iRestartAckTimer = ETrue;

		// The frame will be re-transmitted when GetPDU is called.
		// This will also re-start the Retransmission timer.
		PDUAvailable();	
		}
	else
		{
		// The channel can not support the requested quality.
		// i.e., the number of retransmissions before the channel is
		// disconnected has been exceeded.
		// Error the SAP to disconnect the channel.
		ErrorD(KErrL2CAPMaxTransmitExceeded);
		LOG(_L("FEC CL2CapDataReTxController::AckTimerExpired - KErrL2CAPMaxTransmitExceeded"))	
		}
	}

HL2CapPDU* CL2CapDataReTxController::RetransmitSentPDU()
	{
	LOG_FUNC
	HIFramePDU* pduToSend = NULL;
	TDblQueIter<HIFramePDU> iter(iSentPDUs);
	HIFramePDU* pduPtr;
	
	while((pduPtr = iter++) != NULL)
		{
		if(pduPtr->TxSeqNumber() == iRetransTxVal)
			{
			pduToSend = pduPtr;
			break;
			}
		}

	if(!pduToSend)
		{
		iRetransmitSentPDUs = EFalse;
		}
	return pduToSend;
	}

void CL2CapDataReTxController::HandlePduSendComplete(HL2CapPDU& aPdu)
	{
	LOG_FUNC

	// We only claim ownership of I-Frames.
	HIFramePDU& IFrame = static_cast<HIFramePDU&>(aPdu);

	// Remove the PDU from the pending send queue.
	IFrame.iLink.Deque();
	
	// If there are outstanding (i.e., un-ack'ed) PDU's, the
	// Ack timer will already be running.  Otherwise it should
	// be started.
	if(iSentPDUs.IsEmpty() || iRestartAckTimer)
		{
		iTimerMan.StopMonitorTimer();
		iRestartAckTimer = EFalse;
		iTimerMan.StartAckTimer();
		}

	iSentPDUs.AddLast(IFrame);
	
	// When operating in lossy conditions such as when running 
	// concurrently with a name inquiry it is possible for an 
	// acknowledgement to arrive before the sent completion code is called
	// for the relevant PDU.  As a result aIFrame may have already been 
	// acknowledged.  Now that we have ownership of the PDU we can 
	// remove it and cancel the Ack timer if necessary.
	
	// N.B. Here we have assumed that the delay in the notification 
	// that a PDU has been sent (thereby moving it from the pending sent
	// queue to the sent queue) is not so large that in the mean time we
	// have sent so many new PDUs that the transmission window has wrapped
	// so far around that an old I-Frame from the last cycle is 
	// considered an unacknowledged one because the Tx Seq value is once again 
	// in the valid transmission window.  This would require BT to be much
	// faster than it currently is (11/04/2007). 
	RemoveAckedPDUsFromSentQueue();
	PDUAvailable();
	}
	
void CL2CapDataReTxController::RemoveAckedPDUsFromSentQueue()
	{
	LOG_FUNC
	TDblQueIter<HIFramePDU> iter(iSentPDUs);
	HIFramePDU* pduPtr;
	TBool ackedPDUs = EFalse;

	// Remove PDUs which have been previously acknowledged.
	// The queue may be out of order due to retransmissions 
	// being appended at the end of the sent queue.
	while((pduPtr = iter++) != NULL)
		{
		// If the PDU's Tx Seq number is not in the current valid transmission window it is considered a previously acknowledged I-Frame and is removed.
		if (!(((pduPtr->TxSeqNumber()>= iExpectedAckSeq) && (pduPtr->TxSeqNumber()< (iExpectedAckSeq + iConfig->TXWindowSize()))) || (((iExpectedAckSeq + iConfig->TXWindowSize()) > KL2CapTxSeqValues) && (pduPtr->TxSeqNumber()< Mod64((iExpectedAckSeq + iConfig->TXWindowSize()))))))
			{
			ackedPDUs = ETrue;
			delete pduPtr;
			}
		}

	// Some PDUs have been ack'd.
	if(ackedPDUs)
		{
		if(iSentPDUs.IsEmpty())
			{
			if (iDeliverOutgoingDataAndSignalToSduQWhenDone && iPendingSentPDUs.IsEmpty())
				{
				iSDUQueue.DataControllerDeliveredOutgoingData();
				}
			else
				{
				iTimerMan.StartMonitorTimer();
				}
			}
		else
			{
			iTimerMan.StopMonitorTimer();
			iTimerMan.StartAckTimer();
			}
		}
	}

TInt CL2CapDataReTxController::HandleOutgoingIFrame(HIFramePDU* aIFrame)
	{
	LOG_FUNC
	aIFrame->SetPDUCID(iRemoteCID);
	
	// When sending a retransmission iRetransmitSentPDUs = ETrue.
	// When doing a retransmission the I-Frame has already had its Tx Seq
	// value set and iRetransTxVal is incremented so that we will continue
	// iterating through the sent PDUs for retransmission.
	// When sending a new I-Frame iNextTxSeq is used to get the next
	// brand new Tx Seq value (given the limitations of wrapping round the number
	// space).
	if (!iRetransmitSentPDUs)
		{
		aIFrame->SetTxSeqNumber(iNextTxSeq);
		iNextTxSeq = Mod64(iNextTxSeq + 1);
		}
	else
		{
		++iRetransTxVal;
		}
	
	LOG(_L("FEC CL2CapDataReTxController::HandleOutgoingIFrame"))		
	LOG1(_L("\tTxSeqNumber = %d"), (aIFrame->TxSeqNumber()))

	// If flow controlled off - don't acknowledge any new I-Frames
	aIFrame->SetReqSeqNumber(iIncomingSduQFull ? iLastAckSentRxSeqNum : iExpectedTxSeq);

	// Set the retransmission disable bit if flow control is on.
	aIFrame->SetRetransmitDisable(iIncomingSduQFull);
	aIFrame->CalculateAndSetFCS();

	aIFrame->iLink.Deque(); // may be on Sent PDU list if it's a retransmission
	aIFrame->SetPduOwner(this);
	iPendingSentPDUs.AddLast(*aIFrame);
	if ((iSentPDUs.IsEmpty()) && iTimerMan.IsAckTimerRunning())
		{
		iRestartAckTimer = EFalse;
		iTimerMan.StartMonitorTimer();
		}

	// Cancel the peer ack timer.
	iTimerMan.StopSendPeerAckTimer();
	iSendAckToPeer = EFalse;

	// Store the last acknowledged frame index.
	if(!iIncomingSduQFull)
		{
		iLastAckSentRxSeqNum = iExpectedTxSeq;
		}
	LOG1(_L("\tLastAckSentRxSeqNum = %d"), iLastAckSentRxSeqNum)

	return KErrNone;
	}

TBool CL2CapDataReTxController::DeliverOutgoingDataAndSignalToSduQWhenDone()
	{
	LOG_FUNC
	iDeliverOutgoingDataAndSignalToSduQWhenDone = ETrue;
	// Returning true means we don't have any outstanding data to deliver and
	// hence can be deleted immediately.
	return iPendingSentPDUs.IsEmpty() && iSentPDUs.IsEmpty();
	}


RL2CapRetransmissionModeTimerManager::RL2CapRetransmissionModeTimerManager(MRetransmissionModeTimerClient& aClient)
 :	iClient(aClient) 
	{
	LOG_FUNC
	}

void RL2CapRetransmissionModeTimerManager::Close()
	{
	LOG_FUNC
	CancelFECTimer();
	StopSendPeerAckTimer();
	}

void RL2CapRetransmissionModeTimerManager::StartMonitorTimer()
	{
	LOG_FUNC
	// Monitor timer preempts the Ack timer, so we can stop whichever we have running.
	CancelFECTimer();
	
	TCallBack cb(FECTimerExpired, this);
	iFECTimerEntry.Set(cb);
	
	// Set the timeout.  The value is in Milliseconds.
	BTSocketTimer::Queue(iClient.MonitorTimeout()*1000, iFECTimerEntry);
	iFECTimerState = EMonitorTimerRunning;
	}

void RL2CapRetransmissionModeTimerManager::StartAckTimer()
	{
	LOG_FUNC

	if (iFECTimerState != EMonitorTimerRunning)
		// Ack timer can't be run when the Monitor timer is running.
		{
		CancelFECTimer();
	
		TCallBack cb(FECTimerExpired, this);
		iFECTimerEntry.Set(cb);
	
		// Set the timeout.  The value is in Milliseconds.
		BTSocketTimer::Queue(iClient.RetransmissionTimeout()*1000, iFECTimerEntry);
		iFECTimerState = EAckTimerRunning;
		}
	}

void RL2CapRetransmissionModeTimerManager::StopMonitorTimer()
	{
	LOG_FUNC
	if (iFECTimerState == EMonitorTimerRunning)
		{
		CancelFECTimer();
		}
	}

void RL2CapRetransmissionModeTimerManager::StopAckTimer()
	{
	LOG_FUNC
	if (iFECTimerState == EAckTimerRunning)
		{
		CancelFECTimer();
		}
	}

void RL2CapRetransmissionModeTimerManager::CancelFECTimer()
	{
	LOG_FUNC
	// Cancel either the monitor or ack timer.
	if(iFECTimerState != EFECTimerIdle)
		{
		// Cancel the current outgoing timer.
		BTSocketTimer::Remove(iFECTimerEntry);
		iFECTimerState = EFECTimerIdle;
		}
	}

void RL2CapRetransmissionModeTimerManager::HandleFECTimerExpired()
	{
	LOG_FUNC
	// Call the appropriate method to handle this expiry.
	switch(iFECTimerState)
		{
		case EFECTimerIdle:
			Panic(EL2CAPInvalidDataControllerTimerState);
			break;
			
		case EMonitorTimerRunning:
			iFECTimerState = EFECTimerIdle;
			iClient.MonitorTimerExpired();
			break;
			
		case EAckTimerRunning:
			iFECTimerState = EFECTimerIdle;
			iClient.AckTimerExpired();
			break;

		default:
			Panic(EL2CAPInvalidDataControllerTimerState);
			break;
		}
	}

void RL2CapRetransmissionModeTimerManager::HandleSendPeerAckTimerExpired()
	{
	LOG_FUNC
	__ASSERT_ALWAYS(iSendPeerAckTimerRunning, Panic(EL2CAPInvalidDataControllerTimerState));
	iSendPeerAckTimerRunning = EFalse;
	iClient.SendPeerAckTimerExpired();
	}

TBool RL2CapRetransmissionModeTimerManager::StartSendPeerAckTimer()
	{
	LOG_FUNC
	// This timer should only be started if the peer is using a long re-transmission timer.
	__ASSERT_ALWAYS(!iSendPeerAckTimerRunning, Panic(EL2CAPInvalidDataControllerTimerState));
	
	TInt peerAckTimer = iClient.PeerRetransmissionTimeout() - KAveTimeToTransmitAckToPeer;
	if(peerAckTimer > KMinimumPeerAckTimeout)
		{
		TCallBack cb(SendPeerAckTimerExpired, this);
		iSendPeerAckTimerEntry.Set(cb);

		// Set the timeout.  The value os in Milliseconds.
		BTSocketTimer::Queue(peerAckTimer*1000, iSendPeerAckTimerEntry);
		iSendPeerAckTimerRunning = ETrue;
		}
	return iSendPeerAckTimerRunning;
	}

void RL2CapRetransmissionModeTimerManager::StopSendPeerAckTimer()
	{
	LOG_FUNC
	if (iSendPeerAckTimerRunning)
		{
		BTSocketTimer::Remove(iSendPeerAckTimerEntry);
		iSendPeerAckTimerRunning = EFalse;
		}
	}

/*static*/ TInt RL2CapRetransmissionModeTimerManager::SendPeerAckTimerExpired(TAny* aTimerMan)
	{
	LOG_STATIC_FUNC
	RL2CapRetransmissionModeTimerManager* timerMan = reinterpret_cast<RL2CapRetransmissionModeTimerManager*>(aTimerMan);
	timerMan->HandleSendPeerAckTimerExpired();
	return EFalse;
	}

/*static*/ TInt RL2CapRetransmissionModeTimerManager::FECTimerExpired(TAny* aTimeMan)
	{
	LOG_STATIC_FUNC
	RL2CapRetransmissionModeTimerManager* timeMan = reinterpret_cast<RL2CapRetransmissionModeTimerManager*>(aTimeMan);
	timeMan->HandleFECTimerExpired();
	return EFalse;
	}