bluetooth/btstack/l2cap/L2CapSDUQueue.cpp
changeset 0 29b1cd4cb562
child 51 20ac952a623c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btstack/l2cap/L2CapSDUQueue.cpp	Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,784 @@
+// 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 "L2CapSDUQueue.h"
+
+#include "L2CapDataController.h"
+#include "l2util.h"
+#include "l2sap.h"
+
+#include "L2CapDebugControlInterface.h"
+#include "btsockettimer.h"
+#include <es_prot_internal.h>
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_L2CAP_SDU_Q);
+#endif
+
+CL2CapSDUQueue* CL2CapSDUQueue::NewLC(CL2CAPConnectionSAP& aL2CapSap,
+		                              TL2CAPPort aLocalCID,
+		                              TL2CAPPort aRemoteCID,
+                                      CL2CAPMux& aMuxer,
+	                                  TUint16 aPDUSize,
+									  TUint8 aOutboundQueueSize,
+                                      TL2CapDataControllerConfig* aConfig,
+	                                  TUint16 aFlushTimeout, 
+		                              TUint16 aMaxOutgoingMTU,
+                               		  TUint16 aMaxIncomingMTU,
+                               		  TBool aCanDropSdus)
+	{
+	LOG_STATIC_FUNC
+	CL2CapSDUQueue* self = new(ELeave) CL2CapSDUQueue(aL2CapSap, aOutboundQueueSize, aPDUSize, aFlushTimeout, aMaxOutgoingMTU, aMaxIncomingMTU, aCanDropSdus);
+	CleanupStack::PushL(self);
+	self->ConstructL(aLocalCID, aRemoteCID, aMuxer, aConfig);
+	return self;
+	}
+		                                  
+CL2CapSDUQueue* CL2CapSDUQueue::NewL(CL2CAPConnectionSAP& aL2CapSap,
+		                             TL2CAPPort aLocalCID,
+		                             TL2CAPPort aRemoteCID,
+                                     CL2CAPMux& aMuxer,
+	                                 TUint16 aPDUSize,
+									 TUint8 aOutboundQueueSize,
+                                     TL2CapDataControllerConfig* aConfig,
+	                                 TUint16 aFlushTimeout, 
+		                             TUint16 aMaxOutgoingMTU,
+                               		 TUint16 aMaxIncomingMTU,
+                               		 TBool aCanDropSdus)
+	{
+	LOG_STATIC_FUNC
+	CL2CapSDUQueue* self = NewLC(aL2CapSap, aLocalCID, aRemoteCID, aMuxer, aPDUSize, aOutboundQueueSize, aConfig, aFlushTimeout, aMaxOutgoingMTU, aMaxIncomingMTU, aCanDropSdus);
+	CleanupStack::Pop();
+	return self;
+	}	                                 
+ 
+void CL2CapSDUQueue::ConstructL(TL2CAPPort aLocalCID, TL2CAPPort aRemoteCID, CL2CAPMux& aMuxer, TL2CapDataControllerConfig* aConfig)
+	{
+	LOG_FUNC
+	iDataController = CL2CapBasicDataController::NewL(aLocalCID, aRemoteCID, aMuxer, *this, aConfig);
+
+	TCallBack canSendCB(CanSendAsyncCallBack, this);
+	iCanSendAsyncCallBack = new (ELeave)CAsyncCallBack(canSendCB, EActiveMedPriority);
+	TCallBack sdusSentCB(SDUsSentAsyncCallBack, this);
+	iSDUSentAsyncCallBack = new (ELeave)CAsyncCallBack(sdusSentCB, EActiveLowPriority);
+	TCallBack qClosingCB(QueueClosingAsyncCallBack, this);
+	iQueueClosingCallBack = new (ELeave)CAsyncCallBack(qClosingCB, EActiveHighPriority);
+	}
+	
+
+CL2CapSDUQueue::CL2CapSDUQueue(CL2CAPConnectionSAP& aL2CapSap,
+                               TUint8 aOutboundQueueSize,
+                               TUint16 aPDUSize,
+                               TUint16 aFlushTimeout, 
+                               TUint16 aMaxOutgoingMTU,
+                               TUint16 aMaxIncomingMTU,
+                               TBool aCanDropSdus)
+ : iL2CapSap(aL2CapSap),
+   iOutboundQueueSize(aOutboundQueueSize),
+   iOutgoingSDUs(_FOFF(CL2CapSDU, iLink)),
+   iOutgoingPulledSDUs(_FOFF(CL2CapSDU, iLink)),
+   iFlushTimeout(aFlushTimeout),
+   iCurrentPDUSize(aPDUSize),
+   iMaximumPDUSize(aPDUSize),
+   iNegotiatedPDUSize(aPDUSize),
+   iMaxOutgoingMTU(aMaxOutgoingMTU),
+   iMaxIncomingMTU(aMaxIncomingMTU),
+   iCanDropSdus(aCanDropSdus)
+	{
+	LOG_FUNC
+	}
+	
+CL2CapSDUQueue::~CL2CapSDUQueue()
+	{
+	LOG_FUNC
+	TDblQueIter<CL2CapSDU> ogSDUIter(iOutgoingSDUs);
+	CL2CapSDU* sduPtr;
+	while((sduPtr = ogSDUIter++) != NULL)
+		{
+		delete sduPtr;
+		}
+
+	TDblQueIter<CL2CapSDU> ogPulledSDUIter(iOutgoingPulledSDUs);
+	while((sduPtr = ogPulledSDUIter++) != NULL)
+		{
+		delete sduPtr;
+		}
+
+	// Free any incoming SDU's in the list.
+	iIncomingSDUs.Free();
+	iCurrentIncomingSDU.Free();
+	
+	// If the outgoing queue has not been closed then the
+	// data controller needs to be deregistered. iDataController
+	// can be NULL if CL2CapSDUQueue is created and deleted immediately
+	if(iDataController)
+		{
+		iDataController->DeregisterFromMuxer();
+		}
+		
+	delete iDataController;
+	delete iCanSendAsyncCallBack;
+	delete iSDUSentAsyncCallBack;
+	delete iQueueClosingCallBack;
+	
+	CancelOutgoingQueueClosingTimer();
+	}
+
+void CL2CapSDUQueue::ProcessFlushTimerExpiry(CL2CapSDU& aL2CapSDU)
+	{
+	LOG_FUNC
+	CL2CapSDU* sduPtr;
+	TBool done = EFalse;
+	TBool dataPlaneFlushRequired = EFalse;
+
+	if(!iOutgoingSDUs.IsEmpty())
+		{
+		// Check the SDU that is first in line to be sent.  It may
+		// have already been partially sent in which case it must be
+		// flushed.  If it has not been sent at all then just delete it. 
+		sduPtr = iOutgoingSDUs.First();
+		
+		if(sduPtr == &aL2CapSDU)
+			{
+			// The flushed SDU is first in line to be sent.
+			if(sduPtr->IsSDUEmpty())
+				{
+				// None of the SDU has been pulled yet.
+				delete sduPtr;
+				L2CAP_DEBUG(UpdateFlushCounters(L2capDebugInfo::ESDUFlushQueuedSDUFlushed));
+				}
+			else
+				{
+				// Some of the SDU has been sent.  Remove it from
+				// the OG queue so no more is sent.
+				sduPtr->iLink.Deque();
+				iOutgoingPulledSDUs.AddLast(*sduPtr);
+				dataPlaneFlushRequired = ETrue;
+				L2CAP_DEBUG(UpdateFlushCounters(L2capDebugInfo::ESDUFlushPartialSentSDUFlushed));
+				}
+			done = ETrue;
+			}
+
+		// If its not the first SDU check the rest of the outbound queue.
+		TDblQueIter<CL2CapSDU> ogSDUIter(iOutgoingSDUs);
+		// Start from 2nd element in the queue.
+		ogSDUIter++;
+		
+		while(!done && ((sduPtr = ogSDUIter++) != NULL))
+			{
+			if(sduPtr == &aL2CapSDU)
+				{
+				// Flushed SDU found.  None of this SDU has been
+				// sent yet so it can just be deleted from the
+				// outbound queue.
+				delete sduPtr;
+				done = ETrue;
+				L2CAP_DEBUG(UpdateFlushCounters(L2capDebugInfo::ESDUFlushQueuedSDUFlushed));
+				}
+			}
+		}
+		
+	if(done)
+		{
+		// The SDU has been found and removed from the outgoing
+		// queue.			
+		SDURemovedFromOutboundQueue();
+		}
+	else
+		{
+		// If the SDU is not found in the outgoing queue it must have already 
+		// been sent
+		dataPlaneFlushRequired = ETrue;
+		L2CAP_DEBUG(UpdateFlushCounters(L2capDebugInfo::ESDUFlushSentSDUFlushed));
+		}
+
+	if(iDataController && dataPlaneFlushRequired)
+		{
+		iDataController->ProcessFlushTimerExpiry();
+		}
+	}
+
+
+HL2CapPDU* CL2CapSDUQueue::GetPDU()
+	{
+	LOG_FUNC
+	HL2CapPDU* pduPtr = NULL;
+	if(!iOutgoingSDUs.IsEmpty())
+		{
+		CL2CapSDU* sduPtr = iOutgoingSDUs.First();
+		// If the outbound queue is suspended, and this is the start
+		// of a new SDU.  Then don't send a PDU.
+		if(!(iOutboundQueueSuspended && sduPtr->CurrentPDUIsFirstPDU()))
+			{
+			TBool lastPDU = sduPtr->GetPDU(pduPtr);
+			if(lastPDU)
+				{
+				sduPtr->iLink.Deque();
+				iOutgoingPulledSDUs.AddLast(*sduPtr);
+				SDURemovedFromOutboundQueue();
+				}
+			}
+		}
+	return pduPtr;
+	}
+	
+void CL2CapSDUQueue::PutBFramePDU(RMBufChain& dataFrame)
+	{
+	LOG_FUNC
+	__ASSERT_ALWAYS(iDataController->IsBasicDataVersion(), 
+	                Panic(EL2CapPDUInvalidFrameType));
+
+	// Trim off the PDU header and store the SDU data.
+	HBFramePDU::RemoveHeaderBytes(dataFrame);
+	iIncomingSDUs.Append(dataFrame);
+	iCurrentInboundQueueLength++;
+	iL2CapSap.NewData();
+	}
+	
+TInt CL2CapSDUQueue::PutIFramePDU(RMBufChain& aDataFrame)
+	{
+	LOG_FUNC
+	__ASSERT_ALWAYS(iDataController->IsBasicDataVersion() == EFalse, 
+	                Panic(EL2CapPDUInvalidFrameType));
+
+	TL2CapSAR sar = HIFramePDU::SAR(aDataFrame);
+
+	if (sar == EStartOfL2CapSDU)
+		{
+		// SDU length and minimal StartOfSdu I-Frame length as per 2.1 Core Spec Addendum 1:
+		// 3.3.7 Invalid Frame Detection Algorithm steps 4a&c.
+		TInt err = HIFramePDU::CheckStartSduLength(aDataFrame, iMaxIncomingMTU);
+		if (err != KErrNone)
+			{
+			aDataFrame.Free();
+			return err;
+			}
+		}
+	
+	if(HIFramePDU::IsStartOfSDU(aDataFrame))
+		{
+		// TODO: Commented out until RTM and FC are fixed not to call this method
+		// once they're notified we're full.
+//		__ASSERT_DEBUG(iCurrentInboundQueueLength < KInboundQueueFlowCtrlThreshold,
+//					   Panic(EL2CAPPutIFrameCalledWhenSDUQFull));
+
+		if(!iCurrentIncomingSDU.IsEmpty())
+			{
+			aDataFrame.Free();
+			// The previous SDU has not been completed.  Take the
+			// required action, and remove the SDU from the queue.
+			return HandleIncompleteSDU(iCurrentIncomingSDU);
+			}
+
+		// A new SDU needs to be started.
+		if(sar == EStartOfL2CapSDU)
+			{
+			iCurrentIncomingSDULength = HIFramePDU::SDUSize(aDataFrame);
+			}
+		else
+			{
+			// Unsegmented PDU.
+			iCurrentIncomingSDULength = static_cast<TUint16>(HIFramePDU::PDUPayloadLength(aDataFrame) - (HL2CapPDU::KControlFieldLength + HL2CapPDU::KFCSFieldLength));
+			}
+			
+		HIFramePDU::RemoveHeaderAndFCSBytes(aDataFrame);
+		iCurrentIncomingSDU.Assign(aDataFrame);
+		}
+	else
+		{
+		HIFramePDU::RemoveHeaderAndFCSBytes(aDataFrame);
+
+		// The existing SDU should not be empty.
+		if(iCurrentIncomingSDU.IsEmpty())
+			{
+			aDataFrame.Free();
+			return HandleIncompleteSDU(aDataFrame);
+			}
+		else
+			{
+			// Add to the existing SDU.
+			iCurrentIncomingSDU.Append(aDataFrame);
+
+			if(sar == EEndOfL2CapSDU)
+				{
+				// Check the expected length matches the actual length.
+				if(iCurrentIncomingSDU.Length() != iCurrentIncomingSDULength)
+					{
+					return HandleIncompleteSDU(iCurrentIncomingSDU);
+					}
+				}
+			}
+		}
+
+	if(iCurrentIncomingSDU.Length() == iCurrentIncomingSDULength)
+		{
+		// Note: the two lines below should sit next to each other to be atomic wrt.
+		// a call to Read(), in whose context we may be executing right now.
+		// (Read() frees up space in the queue, calls iDataController->SetIncomingSduQFull,
+		// which in turn calls PutIFramePDU for buffered frames).
+		iIncomingSDUs.Append(iCurrentIncomingSDU);
+		iCurrentInboundQueueLength++;
+		
+		if(iCurrentInboundQueueLength >= KInboundQueueFlowCtrlThreshold)
+			{
+			iDataController->SetIncomingSduQFull(ETrue);
+			}
+			
+		// SDU is complete, signal new data.
+		iL2CapSap.NewData();		
+		}
+	else
+		{
+		if(iCurrentIncomingSDU.Length() > iCurrentIncomingSDULength)	
+			{
+			// The incoming SDU has exceeded the length.
+			return HandleIncompleteSDU(iCurrentIncomingSDU);
+			}
+		}
+	return KErrNone;
+	}
+
+TInt CL2CapSDUQueue::Write(RMBufChain& aSDUData)
+	{
+	LOG_FUNC
+	TInt rerr = KErrNone;
+	if(aSDUData.Length() <= iMaxOutgoingMTU)
+		{
+		CL2CapSDU* sduPtr = NULL;
+
+		if(!iOutboundSendingBlocked && 
+		   (iCurrentOutboundQueueLength < iOutboundQueueSize))
+			{
+			TRAP(rerr, sduPtr = CL2CapSDU::NewL(aSDUData, *this, iCurrentPDUSize, iDataController->IsBasicDataVersion(), iFlushTimeout));   
+
+			if(rerr == KErrNone)
+				{
+				iOutgoingSDUs.AddLast(*sduPtr);
+				iCurrentOutboundQueueLength++;
+				
+				iDataController->OutgoingPduAvailableOnSduQ();
+				}
+			}
+		else
+			{
+			iOutboundSendingBlocked = ETrue;
+			rerr = KErrNotReady;
+			}
+		}
+	else
+		{
+		// Attempt to send a SDU larger than the negotiated MTU size.
+		rerr = KErrTooBig;
+		}
+		
+	return rerr;
+	}
+
+void CL2CapSDUQueue::FlushCurrentlyAssembledSdu()
+	{
+	LOG_FUNC
+	iCurrentIncomingSDU.Free();
+	iCurrentIncomingSDULength = 0;
+	}
+
+void CL2CapSDUQueue::FlushOldestIncomingSdu()
+	{
+	LOG_FUNC
+	if (!iIncomingSDUs.IsEmpty())
+		{
+		RMBufChain sdu;
+		//Check on return value of iIncomingSDUs.Remove() is unnecessary, since we've checked iIncomingSDUs.IsEmpty()
+		static_cast<void>(iIncomingSDUs.Remove(sdu));
+		sdu.Free();
+		iCurrentInboundQueueLength--;
+		}
+	}
+
+void CL2CapSDUQueue::Read(RMBufChain& readData)
+	{
+	LOG_FUNC
+	if(!iIncomingSDUs.IsEmpty())
+		{
+		//Check on return value of iIncomingSDUs.Remove() is unnecessary, since we've checked iIncomingSDUs.IsEmpty()
+		static_cast<void>(iIncomingSDUs.Remove(readData));
+		iCurrentInboundQueueLength--;
+
+		if (iDataController && iCurrentInboundQueueLength < KInboundQueueFlowCtrlThreshold)
+			{
+			// Note: ERTM will call PutIFramePDU() for each buffered frame, in the context of the
+			// call below.
+			// Conclusion 1: flow control may be on again when returning from this call.
+			// Conclusion 2: this method (Read) and PutIFramePDU need to be reentrant with respect
+			// to each other: iIncomingSDUs.Append/Remove and corresponding iCurrentInboundQueueLength++/--
+			// need to be "atomic".
+			iDataController->SetIncomingSduQFull(EFalse);
+			}
+
+		// Check if this is the last SDU in the incoming queue.
+		if(iSDUQueuesClosing && iIncomingSDUs.IsEmpty())
+			// Note: see comment in CloseSDUQueue for why we don't need to check for incoming data
+			// in the data controller.
+			{
+			iIncomingQueueClosed = ETrue;
+			TryToCloseQueue();
+			}
+		}
+	}
+					
+void CL2CapSDUQueue::CloseSDUQueue(TIncomingSDUQueueCloseAction aIncomingQueueAction, 
+	                               TOutgoingSDUQueueCloseAction aOutgoingQueueAction)
+	{
+	LOG_FUNC
+	iSDUQueuesClosing = ETrue;
+	if (aIncomingQueueAction == EImmediatelyCloseIncomingQueue ||
+		iIncomingSDUs.IsEmpty())
+		// ^- note it's the remote's responsibility to only initiate disconnection when
+		// all it's outgoing data has been delivered, which on receiveing side means
+		// we don't have to check the data controller for pending incoming data -
+		// everything that's been acknowledged is always in the SDU Q.
+		{
+		iIncomingQueueClosed = ETrue;
+		}
+		
+	if (aOutgoingQueueAction == EImmediatelyCloseOutgoingQueue)
+		{
+		iOutgoingQueueClosed = ETrue;
+		}
+	else if (iOutgoingSDUs.IsEmpty() &&
+			 !iOutgoingQueueClosed)
+		//   ^- iDataController may be NULL if the outgoing Q has been closed.
+		//   It'll happen if the remote initiates the disconnection, we close the outgoing
+		//   Q and delete the data controller but still have unread SDUs in the incoming
+		//   Q, and then our application calls Shutdown without reading the SDUs.
+		{
+		// Some of the data may still be waiting for transmission/acknowledgement
+		// in the data controller's outgoing queues. If it's a reliable mode data controller
+		// then we need to make sure all data has been delivered.
+		iOutgoingQueueClosed = iDataController->DeliverOutgoingDataAndSignalToSduQWhenDone();
+		}
+	else // draining & have outgoing SDUs
+		{
+		// Start a guard timer to ensure the shutdown of the outgoing queue.
+		StartOutgoingQueueClosingTimer();
+		}
+		
+	TryToCloseQueue();
+	}
+
+void CL2CapSDUQueue::DataControllerDeliveredOutgoingData()
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(iSDUQueuesClosing && iOutgoingSDUs.IsEmpty(),
+				   Panic(EL2CAPStrayCallToDataControllerDeliveredOutgoingData));
+
+	// Use the async callback instead of deleting directly - spares us having to inform
+	// the data controller that it's been deleted and thus having a special exit path
+	// inside it.
+	iSDUSentAsyncCallBack->CallBack();
+	}
+
+void CL2CapSDUQueue::TryToCloseQueue()
+	{
+	LOG_FUNC
+	// If the outgoing queue is not closed nothing can
+	// be done.
+	if(iOutgoingQueueClosed)
+		{
+		// The outgoing queue is closed.  Remove the
+		// data controller from the muxer list, it is no longer required.
+		CancelOutgoingQueueClosingTimer();
+
+		if(iDataController)
+			{
+			// If we initiated the Shutdown, then we don't care about incoming data.
+			// If the remote initiated the shutdown, then it has delivered the data
+			// it wants us to read and it's now in the incoming SDU Q (because everything that
+			// we've acknowledged is). In both cases we can delete the data controller now.
+			iDataController->DeregisterFromMuxer();
+			delete iDataController;
+			iDataController = NULL;
+			}
+			
+		// If both queues are closed signal back to the SAP.
+		if(iIncomingQueueClosed)
+			{
+			iQueueClosingCallBack->CallBack();
+			}
+		}
+	}
+
+/*static*/ TInt CL2CapSDUQueue::QueueClosingAsyncCallBack(TAny* aSDUQueue)
+	{
+	LOG_STATIC_FUNC
+	CL2CapSDUQueue* sduQueue = static_cast<CL2CapSDUQueue*>(aSDUQueue);
+	sduQueue->SDUQueueClosed();
+	return EFalse;
+	}
+	
+void CL2CapSDUQueue::SDUQueueClosed()
+	{
+	LOG_FUNC
+	iL2CapSap.SDUQueueClosed();
+	}	
+
+void CL2CapSDUQueue::SDURemovedFromOutboundQueue()
+	{
+	LOG_FUNC
+	iCurrentOutboundQueueLength--;
+	
+	if(iOutboundSendingBlocked &&
+	   (iCurrentOutboundQueueLength < iOutboundQueueSize))
+	   	{
+		iOutboundSendingBlocked = EFalse;
+		iCanSendAsyncCallBack->CallBack();
+	   	}
+
+	// Check if any sent SDUs need deleting and if we can close the outgoing queue
+	// now (if we're in shutdown at all).
+	iSDUSentAsyncCallBack->CallBack();
+	}
+
+
+/*static*/ TInt CL2CapSDUQueue::CanSendAsyncCallBack(TAny* aSDUQueue)
+	{
+	LOG_STATIC_FUNC
+	CL2CapSDUQueue* sduQueue = static_cast<CL2CapSDUQueue*>(aSDUQueue);
+	sduQueue->CanSend();
+	return EFalse;
+	}
+
+void CL2CapSDUQueue::CanSend()
+	{
+	LOG_FUNC
+	iL2CapSap.CanSend();
+	}
+
+/*static*/ TInt CL2CapSDUQueue::SDUsSentAsyncCallBack(TAny* aSDUQueue)
+	{
+	LOG_STATIC_FUNC
+	CL2CapSDUQueue* sduQueue = static_cast<CL2CapSDUQueue*>(aSDUQueue);
+	sduQueue->SDUsSent();
+	return EFalse;
+	}
+	
+void CL2CapSDUQueue::SDUsSent()
+	{
+	LOG_FUNC
+	// Check the pulled SDU queue and remove all fully sent SDU's. 
+	CL2CapSDU* sdu = NULL;
+	while(!iOutgoingPulledSDUs.IsEmpty())
+		{		
+		sdu = iOutgoingPulledSDUs.First();
+		if(sdu->IsSDUEmpty())
+			{
+			delete sdu;
+			}
+		else
+			{
+			break;
+			}
+		}
+
+	// If the queue is waiting to close and nothing is left to be done close it.	
+	if(iSDUQueuesClosing && iOutgoingSDUs.IsEmpty())
+		{
+		// Take the data controller's outgoing PDU queues into account.
+		// In reliable modes all data needs to be acknowledged by the peer before we can close.
+		iOutgoingQueueClosed = iDataController->DeliverOutgoingDataAndSignalToSduQWhenDone();
+		TryToCloseQueue();
+		}
+	}
+
+/*static*/ TInt CL2CapSDUQueue::OutgoingQueueClosingTimerExpired(TAny *aSDUQueue)
+	{
+	LOG_STATIC_FUNC
+	CL2CapSDUQueue* sduQueue = reinterpret_cast<CL2CapSDUQueue*>(aSDUQueue);
+	sduQueue->HandleOutgoingQueueClosingTimerExpired();
+	return EFalse;
+	}
+
+void CL2CapSDUQueue::StartOutgoingQueueClosingTimer()
+	{
+	LOG_FUNC
+	if(!iOutgoingQueueClosingTimerRunning)
+		{
+		TCallBack cb(OutgoingQueueClosingTimerExpired, this);
+		iOutgoingQueueClosingTimer.Set(cb);
+		BTSocketTimer::Queue(KOutgoingQueueClosingTimer * KL2ProtocolSecondTimerMultiplier, iOutgoingQueueClosingTimer);
+		iOutgoingQueueClosingTimerRunning = ETrue;
+		}
+	}
+	
+void CL2CapSDUQueue::CancelOutgoingQueueClosingTimer()
+	{
+	LOG_FUNC
+	if(iOutgoingQueueClosingTimerRunning)
+		{
+		BTSocketTimer::Remove(iOutgoingQueueClosingTimer);
+		iOutgoingQueueClosingTimerRunning = EFalse;
+		}	
+	}
+	
+void CL2CapSDUQueue::HandleOutgoingQueueClosingTimerExpired()
+	{
+	LOG_FUNC
+	iOutgoingQueueClosingTimerRunning = EFalse;
+	
+	// The outgoing queue closing timer has expired.  Force the outgoing
+	// queue to close.  Pending data will NOT be sent.
+	iOutgoingQueueClosed = ETrue;
+	TryToCloseQueue();		
+	}
+
+TInt CL2CapSDUQueue::HandleIncompleteSDU(RMBufChain& aIncompleteSDU)
+	{
+	LOG_FUNC
+	// This method is the starting point for reporting errored / incomplete SDU's to
+	// the socket.  At present this functionality is not implemented and the buffer
+	// is just freed.
+	aIncompleteSDU.Free();
+	iCurrentIncomingSDULength = 0;
+
+	L2CAP_DEBUG(UpdateFlushCounters(L2capDebugInfo::EIncompleteErroredSDUReceived));
+
+	// This function is always called when SAR bits in the received I-Frame are unexpected.
+	// Need to close the connection as per 2.1 Core Spec Addendum 1: 3.3.7 Invalid Frame
+	// Detection Algorithm point 4b.
+	return iCanDropSdus ? KErrNone : KErrL2CAPIncomingSduSegmentationError;
+	}
+
+void CL2CapSDUQueue::ResumeSDUQueue(TL2CapDataControllerConfig* aConfig,
+                    				TUint16 aFlushTimeout,
+                    				TUint16 aPDUSize,
+                    				TUint16 aMaxOutgoingMTU,
+                    				TUint16 aMaxIncomingMTU)
+	{
+	LOG_FUNC
+	// Update the data controller.
+	iDataController->UpdateConfig(aConfig);
+	
+	// Update internal members.
+	iFlushTimeout = aFlushTimeout;
+	iMaxOutgoingMTU = aMaxOutgoingMTU;
+	iMaxIncomingMTU = aMaxIncomingMTU;	
+	iMaximumPDUSize = aPDUSize;
+	iNegotiatedPDUSize = aPDUSize;
+
+	TInt err = KErrNone;
+
+	// If the max PDU size has been increased or remained the same
+	// none of the existing SDU's need to be modified.
+	if(iNegotiatedPDUSize < iCurrentPDUSize)
+		{
+		// For each completely unsent SDU.  Adjust the PDU size.
+		// Check if there are any un-sent SDU's
+		CL2CapSDU* sduPtr = NULL;
+		if(!iOutgoingSDUs.IsEmpty())
+			{
+			// The first SDU on the outgoing queue might be partially sent.  If it is
+			// it can't be re-segmented.
+			TDblQueIter<CL2CapSDU> ogSDUIter(iOutgoingSDUs);
+			while((sduPtr = ogSDUIter++) != NULL)
+				{
+				if(sduPtr->CurrentPDUIsFirstPDU())
+					{
+					err = sduPtr->ChangeSDUSegmentation(iDataController->IsBasicDataVersion(), iNegotiatedPDUSize);
+					if(err != KErrNone)
+						{
+						ErrorD(err);
+						delete sduPtr;
+						break;
+						}
+					}
+				}
+			}
+		}
+	if (err == KErrNone)
+		{
+		iCurrentPDUSize = iNegotiatedPDUSize;
+	
+		// Allow the outbound queue to send again, and tell the data
+		// plane if SDU's are available.
+		iOutboundQueueSuspended = EFalse;
+		if(iCurrentOutboundQueueLength)
+			{
+			iDataController->OutgoingPduAvailableOnSduQ();
+			}
+		} // ... else we're a zombie.
+	}
+	
+void CL2CapSDUQueue::ErrorD(TInt aErrorCode)
+	{
+	LOG_FUNC
+	// We'll get deleted down the call chain. 
+	iL2CapSap.DataPlaneError(aErrorCode, MSocketNotify::TOperationBitmasks(MSocketNotify::EErrorSend | MSocketNotify::EErrorRecv));
+	}
+
+void CL2CapSDUQueue::UpdateChannelPriority(TUint8 aNewPriority)
+	{
+	LOG_FUNC
+	if (iDataController)
+		{
+		iDataController->UpdateChannelPriority(aNewPriority);	
+		}
+	}
+
+TBool CL2CapSDUQueue::IsBasicDataVersion() const
+	{
+	LOG_FUNC
+	TBool rValue = EFalse;
+	if (iDataController)
+		{
+		rValue = iDataController->IsBasicDataVersion();
+		}
+	return rValue;
+	}
+
+#ifdef _DEBUG
+TInt CL2CapSDUQueue::GetDataPlaneConfig(TL2DataPlaneConfig& conf) const
+	{
+	LOG_FUNC
+	TInt rerr = KErrNone;
+	// Populate the debug structure.
+	if(iDataController)
+		{
+		rerr = iDataController->GetDataPlaneConfig(conf);
+		if(rerr == KErrNone)
+			{
+			conf.iOutboundQueueSize = iOutboundQueueSize;
+			conf.iFlushTimeout = iFlushTimeout;
+			conf.iMaximumPDUSize = iCurrentPDUSize;
+			conf.iMaxOutgoingMTU = iMaxOutgoingMTU;
+			conf.iMaxIncomingMTU = iMaxIncomingMTU;
+			}
+		}
+	else
+		{
+		rerr = KErrNotReady;
+		}
+	return rerr;
+	}
+
+void CL2CapSDUQueue::PretendIncomingSduQFull(TBool aIncomingSduQFull)
+	{
+	LOG_FUNC
+	if (iDataController)
+		{
+		iDataController->SetIncomingSduQFull(aIncomingSduQFull);
+		}
+	}
+#endif