bluetooth/btstack/l2cap/l2sap.cpp
changeset 0 29b1cd4cb562
child 8 2b6718f05bdb
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btstack/l2cap/l2sap.cpp	Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,1187 @@
+// Copyright (c) 1999-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:
+// Implements the code for the connected and group L2CAP saps
+// 
+//
+
+#include <bluetooth/logger.h>
+
+#include "l2sap.h"
+
+#include "l2sapstates.h"
+#include "l2cap.h"
+
+#include "l2util.h"
+
+#include "L2CapSDUQueue.h"
+#include "L2CapDataController.h"
+#include "l2capSAPSignalHandler.h"
+#include "L2types.h"
+
+#include "L2CapDebugControlInterface.h"
+#include "l2capCommand.h"
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_L2CAP);
+#endif
+
+CL2CAPConnectionSAP* CL2CAPConnectionSAP::NewL(CL2CAPProtocol& aProt)
+	{
+	LOG_STATIC_FUNC
+	// Create and return a new SAP
+	CL2CAPConnectionSAP* sap= new (ELeave) CL2CAPConnectionSAP(aProt);
+	CleanupStack::PushL(sap);
+	sap->ConstructL();
+	CleanupStack::Pop();
+	return sap;
+	}
+
+void CL2CAPConnectionSAP::ConstructL()
+	{
+	LOG_FUNC
+	CBluetoothSAP::ConstructL();
+	TCallBack cb(NewDataAsyncCallBack, this);
+	iNewDataAsyncCallBack = new (ELeave)CAsyncCallBack(cb, EActiveHighPriority);
+	iL2CapSAPSignalHandler = CL2CapSAPSignalHandler::NewL(*this);
+	LOG2(_L("CL2CAPConnectionSAP.iL2CapSAPSignalHandler = %X.%X"), (TAny*)this, (TAny*)iL2CapSAPSignalHandler)
+	}
+
+CL2CAPConnectionSAP::CL2CAPConnectionSAP(CL2CAPProtocol& aProt)
+ : CBluetoothSAP(aProt.SecMan(), aProt.CodMan()),
+   iProtocol(aProt),
+   iShutdownReceived(ESAPShutdownNone),
+   iSocketErrorCode(KErrNone),
+   iSocketErrorAction(MSocketNotify::TOperationBitmasks(0))
+	{
+	LOG_FUNC
+	iState=&aProt.StateFactory().GetState(CL2CAPSAPStateFactory::EClosed);
+
+	L2CAP_DEBUG(ObjectAllocation(L2capDebugInfo::ESAP,
+	                             L2capDebugInfo::EAllocated));
+	}
+
+
+
+CL2CAPConnectionSAP::~CL2CAPConnectionSAP()
+/**
+   SAP destructor.
+   Called when the socket deletes the SAP.
+*/
+	{
+	LOG_FUNC
+
+	__ASSERT_DEBUG(iClones.Count() == 0, Panic(EL2CAPClonesExistDuringSAPDestructor));
+
+	// Ensure that everything is completely shutdown.
+	iState->FastShutdown(*this);
+
+	// Inform the signal handler that the SAP is being destructed.
+	// The SH will normally destroy itself unless it has a message
+	// that still needs to be sent.
+	if(iL2CapSAPSignalHandler)
+		{
+		iL2CapSAPSignalHandler->SAPClosed();
+		}
+
+	__ASSERT_DEBUG(iL2CapDataQueue == NULL, Panic(EL2CAPSDUQueueStillExistsDuringSAPDestructor));
+		
+	delete iNewDataAsyncCallBack;
+	iClones.Close();
+
+	//We may be still attached to the listening SAP if the connection was not fully completed before shutdown
+	//No checking needed because iListeningSAP will be NULL if this is the listening SAP or we are already 
+	//detached from the listeningSAP 
+	DetachFromListeningSAP();
+
+	L2CAP_DEBUG(ObjectAllocation(L2capDebugInfo::ESAP,
+	                             L2capDebugInfo::EDeleted));
+	}
+
+
+TBool CL2CAPConnectionSAP::CanAcceptConnection() const
+	{
+	LOG_FUNC
+	return (TotalOutstandingCloneCount() < iMaxAcceptingQCount);
+	}
+
+TUint8 CL2CAPConnectionSAP::TotalOutstandingCloneCount() const
+   	{
+	LOG_FUNC
+   	return static_cast<TUint8>(iClones.Count());
+   	}
+
+CL2CAPConnectionSAP* CL2CAPConnectionSAP::CloneListeningSAP(const TBTDevAddr& aAddr)
+	{
+	LOG_FUNC
+	
+	TInt rerr = KErrNone;
+	CL2CAPConnectionSAP* clone = NULL;
+
+	TRAP(rerr, clone = NewL(iProtocol));
+	if(rerr == KErrNone)
+		{
+		if(!EnqueClone(clone))
+			{
+			delete clone;
+			clone = NULL;
+			}
+		else
+			{
+			clone->SetState(iState->iFactory.GetState(CL2CAPSAPStateFactory::EPassiveLinkPending));
+			clone->SetListeningSAP(*this);
+			
+			// Copy the details required for security.
+			clone->iSecurity = iSecurity;
+			clone->iRemoteDev = aAddr; 
+			}
+		}
+	return clone;
+	}
+
+
+TBool CL2CAPConnectionSAP::EnqueClone(CL2CAPConnectionSAP* aSAP)
+	{
+	LOG_FUNC
+	TInt rerr = iClones.Append(aSAP);
+	return (rerr == KErrNone);
+	}
+
+TBool CL2CAPConnectionSAP::DequeClone(CL2CAPConnectionSAP* aSAP)
+	{
+	LOG_FUNC
+	TBool rcode = EFalse;
+	TInt ix = iClones.Find(aSAP);
+
+	if(ix != KErrNotFound)
+		{
+		iClones.Remove(ix);
+		rcode = ETrue;
+		}
+	return rcode;
+	}
+
+void CL2CAPConnectionSAP::DeleteAllClones()
+/**
+   This function is called by TL2CAPSAPStateListening::Exit() to clear
+   the iClones queue and delete all the clones. However, any clones
+   with their iAcceptingPending flag raised are owned by a socket,
+   so they shouldn't be deleted. Instead their pointer to this SAP
+   should be nulled, then they should be removed from the queue.
+**/
+	{
+	LOG_FUNC
+	
+	for(TInt i=iClones.Count()-1;i>=0;i--)
+		{
+		if(iClones[i]->IsAcceptPending())
+			{
+			iClones[i]->DetachFromListeningSAP();
+			}
+		}
+	iClones.ResetAndDestroy();
+	}
+
+void CL2CAPConnectionSAP::DeleteSAP()
+	{
+	LOG_FUNC
+	delete this;
+	}
+	
+void CL2CAPConnectionSAP::SocketConnectComplete()
+/**
+   A Clone has completed a connection, so we tell our socket.
+   
+   Send the socket a ConnectComplete, the clone remains in the listening SAPs clone 
+   queue, but its iAcceptPending flag is raised. Logically ownership of the clone has
+   been transferred from the listening SAP to the socket, however the listening SAP 
+   keeps a pointer until the socket calls Start(). 
+**/
+	{
+	LOG_FUNC
+
+	// There was a mysterious problem with AVDTP signalling channel connections
+	// which suggested that iL2CapDataQueue was 0 when BearerConnectionComplete was
+	// being called. The problem has disappeared but added this just in case it comes
+	// back again.
+	__ASSERT_DEBUG(iL2CapDataQueue != NULL, Panic(EL2CAPSduQNotExisitantInConnectComplete));
+
+	if(iListeningSAP)
+		{
+		SetState(iState->iFactory.GetState(CL2CAPSAPStateFactory::EAccepting));
+		iAcceptPending = ETrue;
+		iListeningSAP->Socket()->ConnectComplete(*this);
+		}
+	else
+		{
+		SetState(iState->iFactory.GetState(CL2CAPSAPStateFactory::EAwaitingInitialData));
+		Socket()->ConnectComplete();
+		}
+	}
+
+/*static*/ TInt CL2CAPConnectionSAP::NewDataAsyncCallBack(TAny* aSAP)
+	{
+	LOG_STATIC_FUNC
+	CL2CAPConnectionSAP* sap = static_cast<CL2CAPConnectionSAP*>(aSAP);
+	sap->iState->NewDataAsyncCallBack(*sap);
+	return EFalse;
+	}
+
+void CL2CAPConnectionSAP::LocalName(TSockAddr& anAddr) const
+/**
+	Read the Local Name into aAddr.
+	Note: On protocol start-up, the protocol might not have received the
+	BTAddr back from the HW when this is called... :-(
+	So the user should use the read local address ioctl to be sure of 
+	getting the right result.
+
+**/
+	{
+	LOG_FUNC
+
+	// Copy iLocalPort into TSockAddr and return
+	TL2CAPSockAddr& addr = TL2CAPSockAddr::Cast(anAddr);
+	iL2CapSAPSignalHandler->GetLocalParameters(addr);
+	}
+
+
+TInt CL2CAPConnectionSAP::SetLocalName(TSockAddr& anAddr)
+	{
+	LOG_FUNC
+
+	TL2CAPSockAddr l2capAddr = TL2CAPSockAddr::Cast(anAddr);
+	TL2CAPPort psm = l2capAddr.Port();
+	TInt err = KErrNone;
+
+	if(psm == KL2CAPPassiveAutoBind)
+		{
+		// User wishes for us to choose free ServerChannel
+		err = Protocol().MuxController().FindFreeUserPSM(psm);
+		if(err == KErrNone)
+			{
+			l2capAddr.SetPort(psm);
+			}
+		}
+	else
+		{
+		// User supplied explicit PSM.  Check that the value is odd.
+		if(psm & 0x0001)
+			{
+			// Valid, so see if available
+			if(Protocol().MuxController().FindIdleSignalHandler(psm))
+				{//psm in use already
+				err = KErrInUse;
+				}
+			}
+		else
+			{
+			// PSM not valid (as per 1.0B spec)
+			err = KErrArgument;
+			}
+		}
+
+
+	if (err == KErrNone)
+		{
+		iL2CapSAPSignalHandler->SetLocalParameters(l2capAddr);	
+		iState->Bound(*this);
+		iSecurity = l2capAddr.BTSecurity();
+		}
+
+	return err;
+	}
+
+void CL2CAPConnectionSAP::RemName(TSockAddr& anAddr) const
+	{
+	LOG_FUNC
+	//Return the remote name
+
+	// Copy iRemoteDev and iRemotePort into TSockAddr and return
+	TL2CAPSockAddr& addr = TL2CAPSockAddr::Cast(anAddr);
+	iL2CapSAPSignalHandler->GetRemName(addr);
+	}
+
+TInt CL2CAPConnectionSAP::SetRemName(TSockAddr& anAddr)
+	{
+	LOG_FUNC
+	
+	// Copy this over
+	TL2CAPSockAddr& addr = TL2CAPSockAddr::Cast(anAddr);
+	iL2CapSAPSignalHandler->SetRemName(addr);
+	
+	// Set the security and remote address values in the base class.
+	// This is required for security handling.
+	iSecurity = addr.BTSecurity();	//outgoing security
+	iRemoteDev = addr.BTAddr();
+	
+	LOG1(_L("L2CAP [l2sap.cpp]: Set remname to port %d, Device...."), addr.Port());
+	LOGHEXDESC(addr.BTAddr().Des());
+	return KErrNone;
+	}
+
+const TBTDevAddr& CL2CAPConnectionSAP::RemoteDev() const
+	{
+	LOG_FUNC
+	return iRemoteDev;
+	}
+
+void CL2CAPConnectionSAP::SetRemoteDev(const TBTDevAddr& aAddr)
+	{
+	LOG_FUNC
+	iRemoteDev = aAddr;
+	LOGBTDEVADDR(iRemoteDev);
+	}
+
+TUint CL2CAPConnectionSAP::GetOptimalMTUSize(TUint aMTU, TUint aPduSize, TBool aBasicMode) const
+	{
+	LOG_FUNC
+
+	// If the negotiated MTU minus any overhead will fit into the optimal PDU then that
+	// is the optimal MTU. The overhead will differ for basic and non-basic mode, for basic mode
+	// we have to consider the SDU overhead as there is no fragment overhead and for non-basic we 
+	// consider the PDU overhead only (the additional SDU overhead is taken account of later if 
+	// more than one PDU is required for the optimal MTU).
+	TUint optimalMTU = aMTU;
+
+	// Calculate the size of the MTU + any overhead assuming that the MTU is not segmented
+	TUint singlePduSize = aBasicMode ? (aMTU + CL2CapSDU::GetSDUOverhead(aBasicMode)) : aMTU;
+
+	// If the unsegmented MTU + overhead can fit into the optimal PDU size then no 
+	// further calculation is required
+	if(singlePduSize > aPduSize)
+		{
+		// The MTU will need to be segmented / fragmented (depending on L2CAP mode).
+		// Calculate an MTU size that will be a factor of the PDU size.
+		optimalMTU = aMTU - ((aMTU + CL2CapSDU::GetSDUOverhead(aBasicMode)) % aPduSize); 
+		}
+
+	return optimalMTU;
+	}
+
+TInt CL2CAPConnectionSAP::SAPSetOption(TUint aLevel, TUint aName, const TDesC8& aOption)
+	{
+	LOG_FUNC
+	// Perform a setopt
+
+	if (aLevel!=KSolBtL2CAP)
+		{
+		// pass down
+
+		return iL2CapSAPSignalHandler->SetOption(aLevel, aName, aOption);
+		}
+
+	switch (aName)
+		{
+		case KL2CAPRTXTimer:
+			{
+			TInt val = *reinterpret_cast<const TInt*>(aOption.Ptr());
+			if (aOption.Length() != sizeof(TInt) || val < HL2CapCommand::KMinRTXTimerDuration || val > HL2CapCommand::KMaxRTXTimerDuration)
+				return KErrArgument;
+			return iL2CapSAPSignalHandler->SetRTXTimerDuration(static_cast<TUint8>(val));
+			}
+		case KL2CAPERTXTimer:
+			{
+			TInt val = *reinterpret_cast<const TInt*>(aOption.Ptr());
+			if (aOption.Length() != sizeof(TInt) || val < HL2CapCommand::KMinERTXTimerDuration || val > HL2CapCommand::KMaxERTXTimerDuration)
+				return KErrArgument;
+			return iL2CapSAPSignalHandler->SetERTXTimerDuration(static_cast<TUint8>(val));
+			}
+		case KL2CAPInboundMTU:
+			{
+			TUint16 val = *reinterpret_cast<const TUint16*>(aOption.Ptr());
+			if (aOption.Length() != sizeof(TUint16) || val < KL2MinMTU)
+				return KErrArgument;
+			TL2CapConfig conf;
+			conf.SetMaxReceiveUnitSize(val);
+			return iL2CapSAPSignalHandler->UpdateChannelConfig(conf);
+			}
+#ifdef _DEBUG
+		case KL2CAPDebugOptionMask:
+			{// Set the debug options mask
+			if (aOption.Length() != sizeof(TUint))
+				return KErrArgument;
+			Protocol().iDebugOptionMask = *reinterpret_cast<const TUint*>(aOption.Ptr());
+			return KErrNone;
+			}
+#endif
+		case KL2CAPNegotiatedOutboundMTU:
+	    case KL2CAPOutboundMTUForBestPerformance: //equals KL2CAPGetOutboundMTU here for legacy purposes
+	        {
+	        TUint16 val = *reinterpret_cast<const TUint16*>(aOption.Ptr());
+	        if (aOption.Length() != sizeof(TUint16) || val < KL2MinMTU)
+	            return KErrArgument;
+			TL2CapConfig conf;
+			conf.SetMaxTransmitUnitSize(val);
+			return iL2CapSAPSignalHandler->UpdateChannelConfig(conf);
+	        }
+
+		case KBTSecurityDeviceOverride:
+			{
+			return SetDeviceOverride(aOption); 
+			}
+
+		case KL2CAPUpdateChannelConfig:
+			{
+			const TL2CapConfig apiConf = *reinterpret_cast<const TL2CapConfig*>(aOption.Ptr());
+			return iL2CapSAPSignalHandler->UpdateChannelConfig(apiConf);
+			}
+			
+		// Can't set these
+		default:
+			return KErrNotSupported;
+		};
+	}
+
+TInt CL2CAPConnectionSAP::GetOption(TUint aLevel,TUint aName,TDes8& aOption) const
+	{
+	LOG_FUNC
+	if (aLevel!=KSolBtL2CAP)
+		{// it must be for the lower layers then
+		
+		return iL2CapSAPSignalHandler->GetOption(aLevel, aName, aOption);
+		}
+
+	TInt val = 0;
+	TInt rValue = KErrNone;
+	
+	switch (aName)
+		{
+		case KL2CAPGetMaxOutboundMTU:
+			if(aOption.Length() == sizeof(TInt))
+				{
+				val = KL2CapMaxMTUSize;
+				aOption = TPtrC8(reinterpret_cast<TUint8*>(&val), sizeof(TInt));
+				}
+			else
+				{
+				rValue = KErrArgument;
+				}
+			break;
+			
+		case KL2CAPGetMaxInboundMTU:
+			if(aOption.Length() == sizeof(TInt))
+				{
+				val = KL2CapMaxMTUSize;
+				aOption = TPtrC8(reinterpret_cast<TUint8*>(&val), sizeof(TInt));
+				}
+			else
+				{
+				rValue = KErrArgument;
+				}
+			break;
+			
+		case KL2CAPNegotiatedOutboundMTU:
+			if(aOption.Length() == sizeof(TInt))
+				{
+				if(iL2CapDataQueue)
+					{
+					val = iL2CapDataQueue->MaxOutgoingMTU();
+					aOption = TPtrC8(reinterpret_cast<TUint8*>(&val), sizeof(TInt));
+					}
+				else
+					{
+					rValue = KErrNotReady;
+					}
+				}
+			else
+				{
+				rValue = KErrArgument;
+				}
+			break;	
+
+		case KL2CAPOutboundMTUForBestPerformanceWithRestriction:
+			// get the restriction value
+			if((aOption.Length() == sizeof(TInt)) && iL2CapDataQueue && iL2CapSAPSignalHandler)
+				{
+				val = *reinterpret_cast<const TInt*>(aOption.Ptr());
+
+				// Ensure that the restriction is less then the current MTU.
+				if (val < iL2CapDataQueue->MaxOutgoingMTU())
+					{
+					// We now need to recalculate the optimal PDU size for the restricted MTU as
+					// this is used in the calculation of the optimal MTU
+					TPckgBuf<TInt> buf;	
+					TInt err = iL2CapSAPSignalHandler->GetOption(KSolBtACL, ELMOutboundACLSize, buf);
+
+					TInt optimalPduSize = HL2CapPDU::GetPDUOrFragmentSize(val, iL2CapDataQueue->MaximumPDUSize(), (err == KErrNone) ? buf() : 0, iL2CapDataQueue->IsBasicDataVersion());
+
+					// update the data queue to use the new optimal PDU size from now on
+					iL2CapDataQueue->SetOptimalPDUSize(optimalPduSize);
+					}
+				else
+					{
+					// can't increase the MTU at this stage so just use the existing MTU
+					val = iL2CapDataQueue->MaxOutgoingMTU();
+					}
+
+				// work out the optimal MTU
+				val = GetOptimalMTUSize(val, iL2CapDataQueue->OptimalPDUSize(), iL2CapDataQueue->IsBasicDataVersion());
+				aOption = TPtrC8(reinterpret_cast<TUint8*>(&val), sizeof(TInt));
+				}			
+			break;
+
+		case KL2CAPOutboundMTUForBestPerformance: //equals KL2CAPGetOutboundMTU
+			if(aOption.Length() == sizeof(TInt))
+				{
+				if(iL2CapDataQueue && iL2CapSAPSignalHandler)
+					{
+					// work out the optimal MTU
+					val = GetOptimalMTUSize(iL2CapDataQueue->MaxOutgoingMTU(), iL2CapDataQueue->OptimalPDUSize(), iL2CapDataQueue->IsBasicDataVersion());
+					aOption = TPtrC8(reinterpret_cast<TUint8*>(&val), sizeof(TInt));
+					}
+				else
+					{
+					rValue = KErrNotReady;
+					}
+				}
+			else
+				{
+				rValue = KErrArgument;
+				}
+			break;	
+			
+		case KL2CAPInboundMTU:
+			if(aOption.Length() == sizeof(TInt))
+				{
+				if(iL2CapDataQueue)
+					{
+					val = iL2CapDataQueue->MaxIncomingMTU();
+					aOption = TPtrC8(reinterpret_cast<TUint8*>(&val), sizeof(TInt));
+					}
+				else
+					{
+					rValue = KErrNotReady;
+					}
+				}
+			else
+				{
+				rValue = KErrArgument;
+				}
+			break;	
+
+		case KL2CAPRTXTimer:
+			if(aOption.Length() == sizeof(TInt))
+				{
+				val = iL2CapSAPSignalHandler->RTXTimerDuration();
+				aOption = TPtrC8(reinterpret_cast<TUint8*>(&val), sizeof(TInt));
+				}
+			else
+				{
+				rValue = KErrArgument;
+				}
+			break;
+
+		case KL2CAPERTXTimer:
+			if(aOption.Length() == sizeof(TInt))
+				{
+				val = iL2CapSAPSignalHandler->ERTXTimerDuration();
+				aOption = TPtrC8(reinterpret_cast<TUint8*>(&val), sizeof(TInt));
+				}
+			else
+				{
+				rValue = KErrArgument;
+				}
+			break;
+
+		case KBTSecurityDeviceOverride:
+			// aspect oriented programming would be nice.
+			rValue = GetDeviceOverride(aOption); 
+			break;
+			
+		case KL2CAPGetDebug1:
+			val = static_cast<TInt>((static_cast<SBtTls*>(Dll::Tls()))->iPort);
+			aOption = TPtrC8(reinterpret_cast<TUint8*>(&val), sizeof(TInt));
+			break;
+			
+		// returns the PSM for the accepted SAP (or listening SAP if clients forgot)
+		case KL2CAPLocalPSM:
+			{
+			if (aOption.Length() == sizeof(TL2CAPPort))
+				{
+				const CL2CAPConnectionSAP* listeningSAP = ListeningSAP();
+				if (!listeningSAP)
+					{
+					listeningSAP = this;
+					}
+				TL2CAPSockAddr locAddr;
+				listeningSAP->LocalName(locAddr);
+				TPckg<TL2CAPPort> pckg(locAddr.Port());
+				aOption = pckg;
+				}
+			else
+				{
+				rValue = KErrArgument;
+				}
+			}
+			break;
+
+		// Returns the negotiated channel mode.
+		// KErrNotReady if the channel hasn't undergone configuration yet. 
+		case KL2CAPNegotiatedChannelMode:
+			if(aOption.Length() == sizeof(TL2CapChannelMode))
+				{
+				TL2CapChannelMode mode;
+				rValue = iL2CapSAPSignalHandler->GetNegotiatedChannelMode(mode);
+				TPckgBuf<TL2CapChannelMode> pckg(mode);
+				aOption = pckg;
+				}
+			else
+				{
+				rValue = KErrArgument;
+				}
+			break;
+
+#ifdef _DEBUG
+
+		case KL2CAPVersion1_2:
+			// This debug option is used to check if the stack is a 1.2 version.
+			// For pre 1.2 versions the stack will respond with KErrNotSupported.
+			break;
+						
+		case KL2CAPHeapAlloc:
+			if(aOption.Length() == sizeof(TInt))
+				{
+				val = User::Heap().Count();
+				aOption = TPtrC8(reinterpret_cast<TUint8*>(&val), sizeof(TInt));
+				}
+			else
+				{
+				rValue = KErrArgument;
+				}
+			break;
+			
+		case KL2CAPDataPlaneConfig:
+			{	
+			if(iL2CapDataQueue)
+				{
+				TL2DataPlaneConfig conf;
+				iL2CapDataQueue->GetDataPlaneConfig(conf);
+				TL2DataPlaneConfigPkg pckg(conf);
+				aOption = pckg;
+				}
+			else
+				{
+				rValue = KErrNotReady;
+				}
+			}
+			break;
+		
+#endif
+			
+		default:
+			rValue = KErrNotSupported;
+			break;
+		};
+	return rValue;	
+	}
+
+
+void CL2CAPConnectionSAP::Ioctl(TUint aLevel,TUint aName,TDes8* aOption)
+/** 
+	Handle an Ioctl from above. 
+	
+	Some Ioctls are not for L2CAP but must be handled here anyway.
+	Other non-L2CAP Ioctls should just be passed downward.
+
+	If the Ioctl is for L2CAP and should be handled the same in all states, 
+	then handle it here, otherwise, pass it on to the current state.
+
+	@param aLevel		Level of Ioctl (should be KSolBtL2CAP here)
+	@param aName		Name of Ioctl
+	@param aOption		Ioctl data
+**/
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(iOutstandingIoctlName == 0, Panic(EL2SAPTwoIoctls));
+
+	// Must set this first, in case any synchronous errors come up. 	
+	iOutstandingIoctlName = aName;
+	TInt rerr = KErrNotSupported;
+
+	if(aLevel == KSolBtL2CAP)
+		{
+		switch(aName)
+			{
+			case KL2CAPEchoRequestIoctl:
+				rerr = iL2CapSAPSignalHandler->SendEchoRequest(aOption);
+				break;
+
+			case KL2CAPIncomingMTUIoctl:
+				{
+				const TUint8* optionPtr = aOption->Ptr();
+				TUint16 newSize = *((TUint16*)optionPtr);
+				TL2CapConfig conf;
+				conf.SetMaxReceiveUnitSize(newSize);
+				rerr = iL2CapSAPSignalHandler->UpdateChannelConfig(conf);
+			  	if(rerr == KErrNone)
+					{
+					// The operation has completed.   
+					IoctlComplete(KErrNone, KSolBtL2CAP, KL2CAPIncomingMTUIoctl, aOption);
+					rerr = KErrNone;
+					}
+				else
+					{
+					if(rerr == KErrL2CAPConfigPending)
+						{
+						// This indicates that configuration is pending.
+						rerr = KErrNone;
+						}
+					}
+				}
+				break;
+				
+			case KL2CAPOutgoingMTUIoctl:
+				{
+				const TUint8* optionPtr = aOption->Ptr();
+				TUint16 newSize = *((TUint16*)optionPtr);
+				TL2CapConfig conf;
+				conf.SetMaxTransmitUnitSize(newSize);
+				rerr = iL2CapSAPSignalHandler->UpdateChannelConfig(conf);
+			  	if(rerr == KErrNone)
+					{
+					// The operation has completed.   
+					IoctlComplete(KErrNone, KSolBtL2CAP, KL2CAPOutgoingMTUIoctl, aOption);
+					rerr = KErrNone;
+					}
+				else
+					{
+					if(rerr == KErrL2CAPConfigPending)
+						{
+						// This indicates that configuration is pending.
+						rerr = KErrNone;
+						}
+					}
+				}
+				break;
+
+			case KL2CAPUpdateChannelConfigIoctl:
+				{
+				const TL2CapConfig* conf = reinterpret_cast<const TL2CapConfig*>(aOption->Ptr());
+				rerr = UpdateChannelConfig(*conf);
+			  	if(rerr == KErrNone)
+					{
+					// The operation has completed.   
+					IoctlComplete(KErrNone, KSolBtL2CAP, KL2CAPUpdateChannelConfigIoctl, aOption);
+					rerr = KErrNone;
+					}
+				else
+					{
+					if(rerr == KErrL2CAPConfigPending)
+						{
+						// This indicates that configuration is pending.
+						rerr = KErrNone;
+						}
+					}
+				}
+				break;
+
+			case KL2CAPPretendIncomingSduQFull:
+				{
+#ifdef _DEBUG
+				if (aOption && aOption->Length() == sizeof(TBool))
+					{
+					iL2CapDataQueue->PretendIncomingSduQFull(*reinterpret_cast<const TBool*>(aOption->Ptr()));
+					IoctlComplete(KErrNone, KSolBtL2CAP, KL2CAPPretendIncomingSduQFull, aOption);
+					rerr = KErrNone;
+					}
+				else
+					{
+					rerr = KErrArgument;
+					}
+#endif
+				}
+				// Return not supported in UREL.
+				break;
+
+			default:
+				// Return not supported.
+				break;
+			}
+		}
+
+	if (rerr != KErrNone)
+		{
+		iOutstandingIoctlName = 0;
+		iSocket->Error(rerr, MSocketNotify::EErrorIoctl);
+		}
+	}
+
+#ifdef _DEBUG
+void CL2CAPConnectionSAP::CancelIoctl(TUint aLevel, TUint aName)
+#else
+void CL2CAPConnectionSAP::CancelIoctl(TUint /*aLevel*/, TUint /*aName*/)
+#endif
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG((aLevel == KSolBtL2CAP && aName == iOutstandingIoctlName), Panic(EL2SAPStrayIoctlCancel));
+	iOutstandingIoctlName = 0;
+	}
+
+void CL2CAPConnectionSAP::IoctlComplete(TInt aErr, TUint aLevel, TUint aName, TDesC8* aBuf)
+	{
+	LOG_FUNC
+	if(aLevel == KSolBtL2CAP && aName == iOutstandingIoctlName)
+		{
+		if(aErr == KErrNone)
+			{
+			if(iSocket)
+				{
+				iSocket->IoctlComplete(aBuf);
+				}
+			}
+		else
+			{
+			iSocket->Error(aErr, MSocketNotify::EErrorIoctl);
+			}
+		iOutstandingIoctlName = 0;
+		}
+	}
+
+void CL2CAPConnectionSAP::EchoResponseReceived(const TDesC8* aData)
+	{
+	LOG_FUNC
+	TDesC8* data = const_cast<TDesC8*>(aData);
+	IoctlComplete(KErrNone, KSolBtL2CAP, KL2CAPEchoRequestIoctl, data);
+	}
+
+
+void CL2CAPConnectionSAP::Start()
+	{
+	LOG_FUNC
+	// Called when data's about to flow
+
+	iAcceptPending = EFalse;
+	iState->Start(*this);
+	}
+
+
+void CL2CAPConnectionSAP::ActiveOpen()
+	{
+	LOG_FUNC
+	// Setup the link
+
+	iState->ActiveOpen(*this);
+	}
+
+void CL2CAPConnectionSAP::ActiveOpen(const TDesC8& /*aConnectionData*/)
+	{
+	LOG_FUNC
+	// This is an error
+	Panic(EL2CAPSAPOpenWithConnectionDataNotSupported);
+	}
+
+TInt CL2CAPConnectionSAP::PassiveOpen(TUint aQueSize)
+	{
+	LOG_FUNC
+	// A Listen request
+	// Now a listening SAP has been opened, start the underlying protocol listening.
+	return iState->PassiveOpen(*this, aQueSize);
+	}
+
+TInt CL2CAPConnectionSAP::PassiveOpen(TUint /*aQueSize*/, const TDesC8& /*aConnectionData*/)
+	{
+	LOG_FUNC
+	// This operation is not currently supported.
+	Panic(EL2CAPSAPOpenWithConnectionDataNotSupported);
+	return KErrNotSupported;
+	}
+
+void CL2CAPConnectionSAP::Shutdown(TCloseType aOption)
+	{
+	LOG_FUNC
+	LOG2(_L("L2CAP [l2sap.cpp]: CL2CAPConnectionSAP::Shutdown, option = %d, SAP %08x"), aOption, this);
+	// Shutdown
+	if(aOption != EImmediate)
+		{
+		// Normal shutdown, ESOCK will wait for the SAP to signal Can Close.
+		iShutdownReceived = ESAPShutdownNormal;
+		iState->Shutdown(*this);
+		}
+	else
+		{
+		iShutdownReceived = ESAPShutdownImmediate;
+		iState->FastShutdown(*this);
+		}
+	}
+
+void CL2CAPConnectionSAP::Shutdown(TCloseType /*option*/, 
+								   const TDesC8& /*aDisconnectionData*/)
+	{
+	LOG_FUNC
+	// this one's an error
+	Panic(EL2CAPShutdownWithDisconnectionDataNotSupported);
+	}
+
+void CL2CAPConnectionSAP::AutoBind()
+/**
+	Auto bind from esock.
+	Do nothing -- as we always allocate a local CID
+	in ActiveConnect anyway.
+**/
+	{
+	LOG_FUNC
+	}
+
+
+TInt CL2CAPConnectionSAP::Write(RMBufChain& aData, TUint aOptions, TSockAddr* /*anAddr*/)
+	{
+	LOG_FUNC
+	// If the write completes without error return 1.  
+	// If not the definition [of the return parameter
+	// of this method] in CServProviderBase indicates that zero
+	// should be returned.
+	TInt err = iState->Send(*this, aData, aOptions);
+	// If there is an error then simply returning 0 will just "flow off"
+	// esock (i.e. the write won't be completed).  We are reliant on
+	// some other event to either error the send or disconnect in order
+	// to complete the request.
+	return (err == KErrNone ? 1 : 0);
+	}
+
+TInt CL2CAPConnectionSAP::GetData(RMBufChain& aData, TUint /*aLength*/, TUint /*aOptions*/, TSockAddr* /*anAddr*/)
+	{
+	LOG_FUNC
+	return iState->Read(*this, aData);
+	}
+
+TUint CL2CAPConnectionSAP::Write(const TDesC8& aDesc, TUint aOptions, TSockAddr* /*anAddr*/)
+	{
+	LOG_FUNC
+	// Temporary Shim.  Should be remove when no longer used.
+	RMBufChain buf;
+	TInt err = KErrNone;
+	
+	// A zero return value indicates that no data was sent.
+	TUint rValue = 0;
+	
+	TRAP(err, buf.CreateL(aDesc));
+	if(err == KErrNone)
+		{
+		rValue = Write(buf, aOptions);
+		}
+
+	buf.Free();
+	return rValue;
+	}
+
+void CL2CAPConnectionSAP::GetData(TDes8& aDesc, TUint aOptions, TSockAddr* anAddr)
+	{
+	LOG_FUNC
+	// Temporary Shim.  Should be remove when no longer used.
+	RMBufChain buf;
+	GetData(buf, 0, aOptions, anAddr);
+	buf.CopyOut(aDesc);
+	buf.Free();
+	}
+	
+
+// Methods from SAP Signal Handler
+void CL2CAPConnectionSAP::LinkUp()
+	{
+	LOG_FUNC
+	iState->LinkUp(*this);
+	}
+
+void CL2CAPConnectionSAP::ChannelOpened()
+	{
+	LOG_FUNC
+	iState->ChannelOpened(*this);
+	}
+
+void CL2CAPConnectionSAP::ChannelConfigured(CL2CapChannelConfig& aConfig,
+                                            CL2CAPMux& aMuxer,
+                                            TL2CAPPort aLocalPort,
+                                            TL2CAPPort aRemotePort)
+	{
+	LOG_FUNC
+	iState->ChannelConfigured(*this, aConfig, aMuxer, aLocalPort, aRemotePort);
+	}
+
+void CL2CAPConnectionSAP::ReconfiguringChannel()
+	{
+	LOG_FUNC
+	iState->ReconfiguringChannel(*this);
+	}
+	
+void CL2CAPConnectionSAP::ChannelClosed()
+	{
+	LOG_FUNC
+	iState->ChannelClosed(*this);
+	}
+
+void CL2CAPConnectionSAP::DataPlaneError(TInt aErrorCode, MSocketNotify::TOperationBitmasks aErrorAction)
+	{
+	LOG_FUNC
+	iState->DataPlaneError(*this, aErrorCode, aErrorAction);
+	}
+
+void CL2CAPConnectionSAP::SignalHandlerError(TInt aErrorCode, MSocketNotify::TOperationBitmasks aErrorAction)
+	{
+	LOG_FUNC
+	iState->SignalHandlerError(*this, aErrorCode, aErrorAction);
+	}
+	
+
+// Methods from Data Controller
+void CL2CAPConnectionSAP::NewData()
+	{
+	LOG_FUNC
+	iState->NewData(*this);
+	}
+	
+void CL2CAPConnectionSAP::CanSend()
+	{
+	LOG_FUNC
+	iState->CanSend(*this);
+	}
+
+void CL2CAPConnectionSAP::CloseOutgoingSDUQueue()
+	{
+	LOG_FUNC
+	iState->CloseOutgoingSDUQueue(*this);
+	}
+	
+void CL2CAPConnectionSAP::SDUQueueClosed()
+	{
+	LOG_FUNC
+	// Delete the SDU queue.
+	delete iL2CapDataQueue;
+	iL2CapDataQueue = NULL;
+	
+	// Inform the state that the data plane is closed.
+	iState->SDUQueueClosed(*this);	
+	}	
+
+void CL2CAPConnectionSAP::SetState(TL2CAPSAPState& aState)
+	{
+	LOG_FUNC
+	iState->Exit(*this);
+	iState = &aState;
+	iState->Enter(*this);
+	}
+
+TInt CL2CAPConnectionSAP::CreateDataPlane(CL2CapChannelConfig& aConfig, 
+                                          CL2CAPMux& aMuxer,
+                                          TL2CAPPort aLocalPort,
+                                          TL2CAPPort aRemotePort)
+	{
+	LOG_FUNC
+	TInt rerr = KErrNone;
+	const TL2CapFecNegotiator& fec = aConfig.FecNegotiator();
+	TL2CapDataControllerConfig* dataConfig = new TL2CapDataControllerConfig(fec.DataControllerConfig());
+
+	if(dataConfig)
+		{
+		TRAP(rerr, iL2CapDataQueue = CL2CapSDUQueue::NewL(*this,
+		                                                  aLocalPort,
+		                                                  aRemotePort,
+		                                                  aMuxer,
+					                       				  fec.OutgoingMaximumPDUSize(),
+		                                                  CL2CapSDUQueue::KDefaultOutboundQueueSize,
+		                       			            	  dataConfig,
+											   			  aConfig.OutgoingFlushTimeout().Negotiated().FlushTimeoutDuration(),
+					                       				  aConfig.OutgoingMTU().Negotiated().MTU(),
+					                       				  aConfig.IncomingMTU().Negotiated().MTU(),
+					                       				  // Allow ourselves to drop SDUs we can't assemble if channel mode is not Reliable.
+					                       				  !TRetransmissionAndFlowControlOption::IsModeReliable(fec.IncomingLinkMode())));
+
+		// Set the optimal PDU size that the data controller should use.
+		TPckgBuf<TInt> buf;	
+		TInt err = iL2CapSAPSignalHandler->GetOption(KSolBtACL, ELMOutboundACLSize, buf);
+
+		TInt optimalPduSize = HL2CapPDU::GetPDUOrFragmentSize(iL2CapDataQueue->MaxOutgoingMTU(), iL2CapDataQueue->MaximumPDUSize(), (err == KErrNone) ? buf() : 0, iL2CapDataQueue->IsBasicDataVersion());
+		iL2CapDataQueue->SetOptimalPDUSize(optimalPduSize);
+		}
+	else
+		{
+		rerr = KErrNoMemory;
+		}
+
+	return rerr;
+	}
+	
+
+void CL2CAPConnectionSAP::PauseDataPlane()
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(iL2CapDataQueue, Panic(EL2CAPPauseDataPlaneCalledWhenDataPlaneDoesNotExist));
+	iL2CapDataQueue->SuspendSDUQueue();				
+	}
+
+void CL2CAPConnectionSAP::TryToCompleteConfigurationIoctl(TInt aError)
+	{
+	LOG_FUNC
+	switch(iOutstandingIoctlName)
+		{
+		case KL2CAPIncomingMTUIoctl:
+		case KL2CAPOutgoingMTUIoctl:
+		case KL2CAPUpdateChannelConfigIoctl:
+			if(aError == KErrNone)
+				{
+				IoctlComplete(KErrNone, KSolBtL2CAP, iOutstandingIoctlName, NULL);
+				}
+			else
+				{
+				iSocket->Error(aError, MSocketNotify::EErrorIoctl);
+				}
+			iOutstandingIoctlName = 0;
+			break;
+			
+		default:
+			break;
+		};
+	}
+	
+void CL2CAPConnectionSAP::AccessRequestComplete(TInt aResult)
+	{
+	LOG_FUNC
+	iState->AccessRequestComplete(*this, aResult);
+	}
+
+TInt CL2CAPConnectionSAP::UpdateChannelConfig(const TL2CapConfig& aAPIConfig)
+	{
+	LOG_FUNC
+	TInt rerr = KErrNone;
+	if(iL2CapSAPSignalHandler)
+		{
+		// Update the channel config.
+		rerr = iL2CapSAPSignalHandler->UpdateChannelConfig(aAPIConfig);
+
+		// Check if the a new priority has been specified.
+		TBool specified;
+		TUint8 newPriority = static_cast<TUint8>(aAPIConfig.ChannelPriority(specified));
+		if(specified && iChannelPriority != newPriority)
+			{
+			iChannelPriority = newPriority;
+			if(iL2CapDataQueue)
+				{
+				iL2CapDataQueue->UpdateChannelPriority(iChannelPriority);
+				}
+			}
+		}
+	else
+		{
+		rerr = KErrNotReady;
+		}
+	return rerr;
+	}
+	
+void CL2CAPConnectionSAP::DetachFromListeningSAP()
+	{
+	LOG_FUNC
+	if(iListeningSAP)
+		{
+		iListeningSAP->DequeClone(this);
+		iListeningSAP = NULL;
+		}
+	}