bluetooth/btstack/avctp/avctp.cpp
changeset 0 29b1cd4cb562
child 13 20fda83a6398
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btstack/avctp/avctp.cpp	Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,1649 @@
+// Copyright (c) 2005-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 AVCTP protocol object
+// 
+//
+
+/**
+ @file
+ @internalTechnology
+*/
+
+#include <e32def.h>
+#include <bluetooth/logger.h>
+#include <bluetoothav.h>
+
+#include "Avctp.h"
+#include "avctpsap.h"
+#include "avctpmuxerstates.h"
+#include "avctpconstants.h" 
+#include "IncomingConnListener.h"
+#include "avctpPacketMgr.h"
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_AVCTP);
+#endif
+
+#ifdef _DEBUG
+PANICCATEGORY("avctp");
+#endif
+
+using namespace SymbianAvctp;
+
+#include "avctpmuxer.h"
+
+
+
+#ifdef __FLOG_ACTIVE
+#define LOG_SAPS LogSaps();
+#define LOG_MUXERS LogMuxers();
+#define LOG_SAPLINKSMGRS LogSapLinkMgrs();
+#else
+#define LOG_SAPS
+#define LOG_MUXERS
+#define LOG_SAPLINKSMGRS
+#endif
+
+/** 
+Protocol object constructor.
+
+@see CBTSecMan
+@param aSecMan The Bluetooth security manager
+@param aControlPlane the object which the protocol can use to pass control plane messages
+@param aCodMan the CoD manager for the protocol
+@return A pointer to the AVCTP protocol object
+@internalComponent
+*/
+CAvctpProtocol::CAvctpProtocol(CBTSecMan& aSecMan, RBTControlPlane& aControlPlane, CBTCodServiceMan& aCodMan)
+:	CBluetoothProtocolBase(aSecMan, aControlPlane, aCodMan),
+	iSaps(_FOFF(CAvctpSap, iQueLink)),
+	iTransports(_FOFF(CAvctpTransport, iQueLink)),
+	iIpidResponses(_FOFF(HAvctpOutgoingSdu, iQueLink))
+	{
+	LOG_FUNC
+	
+	TCallBack callBack(TryToClose, this);
+	iIdleTimerEntry.Set(callBack);
+	}
+
+/** Static protocol object factory function.
+
+Leaves the protocol object on the cleanup stack.
+
+@see CBTSecMan
+@param aSecMan The Bluetooth security manager
+@param aControlPlane the object which the protocol can use to pass control plane messages
+@param aCodMan the CoD manager for the protocol
+@return A pointer to the AVCTP protocol object
+@leave KErrNoMemory if the protocol object could not be created
+@internalComponent
+*/
+CAvctpProtocol* CAvctpProtocol::NewLC(CBTSecMan& aSecMan, RBTControlPlane& aControlPlane, CBTCodServiceMan& aCodMan)
+	{
+	CONNECT_LOGGER
+	LOG_STATIC_FUNC
+	CAvctpProtocol* self = new(ELeave) CAvctpProtocol(aSecMan, aControlPlane, aCodMan);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	return self;
+	}
+
+/** Static protocol object factory function.
+
+@see CBTSecMan
+@param aSecMan The Bluetooth security manager
+@param aControlPlane the object which the protocol can use to pass control plane messages
+@param aCodMan the CoD manager for the protocol
+@return A pointer to the AVCTP protocol object
+@leave KErrNoMemory if the protocol object could not be created
+@internalComponent
+*/
+CAvctpProtocol* CAvctpProtocol::NewL(CBTSecMan& aSecMan, RBTControlPlane& aControlPlane, CBTCodServiceMan& aCodMan)
+	{
+	LOG_STATIC_FUNC
+	CAvctpProtocol* self = CAvctpProtocol::NewLC(aSecMan, aControlPlane, aCodMan);
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+/** Protocol object second-phase construction.
+
+@internalComponent
+*/
+void CAvctpProtocol::ConstructL()
+	{
+	LOG_FUNC
+	iMuxerStateFactory = CAvctpMuxerStateFactory::NewL();
+
+	TCallBack cb(IpidAsyncCallBack, this);
+	iIpidResponseAsyncCallBack = new (ELeave)CAsyncCallBack(cb, EActiveHighPriority);
+	
+	}
+
+/** Protocol object destructor.
+
+We don't clear up the SAPs, or the Muxer Collections as they are owned by ESock
+and it is up to it to destroy them.
+
+ muxers are owned by the protocol and so should be destroyed by it.
+
+@internalComponent
+*/
+CAvctpProtocol::~CAvctpProtocol()
+	{
+	LOG_FUNC
+	
+	DequeIdleTimer();
+
+	if (LowerProtocol())
+		{
+		LowerProtocol()->Close();  // Matches the bind
+		}
+
+	CancelIpidAsyncCallBack();
+	delete iIpidResponseAsyncCallBack;
+
+	TDblQueIter<HAvctpOutgoingSdu> sduIter(iIpidResponses);
+	while(sduIter)
+		{
+		delete sduIter++; // deques sdu
+		}				
+	TDblQueIter<CAvctpTransport> muxerIter(iTransports);
+	while(muxerIter)
+		{
+		delete muxerIter++; // deques muxer
+		}	
+
+	delete iMuxerStateFactory;
+	// iListener is deleted through CBluetoothProtocolBase
+	delete iSecondChannelListener;
+	iClientItems.Close();
+	
+	
+	}
+
+/** Pre-binding initialise.
+
+Alloc any stuff we need. This will only ever be called once during the lifetime of this protocol.
+
+@internalAll
+@leave KErrNoMemory if the state factory cannot be created
+@param aTag The strin   g identifier for the protocol from the ESock ini file
+*/
+void CAvctpProtocol::InitL(TDesC& /*aTag*/)
+	{
+	LOG_FUNC
+	}
+
+/** Binding complete.
+
+@internalAll
+*/
+void CAvctpProtocol::StartL()
+	{
+	LOG_FUNC
+
+	// Should check that we're bound now.
+	if (!iLowerProtocol)
+		{
+		User::Leave(ENotBound);
+		}
+	User::LeaveIfError(IncrementListeners());
+	}
+
+/** Request by Protocol Mgr to bind to the specified protocol. 
+
+We can only be bound to one lower layer protocol, so the function leaves if we are already bound.
+
+@internalAll
+@leave KErrProtocolAlreadyBound if we are already bound, and any other leave from the lower protocol's BindL()
+@param aProtocol The protocol we need to bind to.
+*/
+void CAvctpProtocol::BindToL(CProtocolBase* aProtocol)
+	{
+	LOG_FUNC
+
+	if(!iLowerProtocol)
+		{
+#ifdef _DEBUG
+		TServerProtocolDesc prtDesc;
+		aProtocol->Identify(&prtDesc);
+
+		if(prtDesc.iAddrFamily != KBTAddrFamily ||
+		   prtDesc.iProtocol != KL2CAP)
+			{
+			User::Leave(KErrBtEskError);
+			}
+#endif
+
+		iLowerProtocol=static_cast<CBluetoothProtocolBase*>(aProtocol);
+		iLowerProtocol->BindL(this, 0); // id not used
+		iLowerProtocol->Open();
+		}
+	else
+		{
+	    User::Leave(KErrProtocolAlreadyBound);
+		}
+	}
+
+/** Create a new SAP.
+
+The SAP returned is owned by the caller -- this protocol will not clean it up.
+ESock uses this function to create a new SAP, and ESock will delete when it is
+finished with it.
+
+@internalAll
+@leave KErrNotSupported if aSockType is not KSockDatagram, KErrNoMemory if a
+     new SAP could not be created
+@param aSockType The socket type for the SAP: AVCTP only supports KSockDatagram
+@return A pointer to a new SAP
+*/
+CServProviderBase* CAvctpProtocol::NewSAPL(TUint aSockType)
+	{
+	LOG_FUNC
+
+	CBluetoothSAP* sap = NULL;
+	
+	switch(aSockType)
+		{
+	case KSockDatagram:    // AVCTP uses datagrams for data SAPs
+		sap = CAvctpSap::NewL(*this);
+		break;
+	case KSockRaw:    // AVCTP uses Raw for control SAPs
+		sap = CAvctpControlSAP::NewL(*this);
+		break;
+	default:
+		User::Leave(KErrNotSupported);
+		break;
+		}
+
+	return sap;
+	}
+	
+
+/** Identify the protocol.
+
+The descriptor is filled in to identify the protocol to ESock.
+
+@internalAll
+@param aDesc A pointer to the descriptor to be filled in
+*/
+void CAvctpProtocol::Identify(TServerProtocolDesc* aDesc) const
+	{
+	LOG_FUNC
+
+	ProtocolIdentity(aDesc);
+	}
+
+/** Fill in the protocol descriptor.
+
+This is a static utility function to fill in the protocol details.
+
+@internalComponent
+@param aDesc A pointer to the descriptor to be filled in
+*/
+void CAvctpProtocol::ProtocolIdentity(TServerProtocolDesc* aDesc)
+	{
+	LOG_STATIC_FUNC
+
+	_LIT(KAvctpProtocolName,"AVCTP"); 
+	aDesc->iName			= KAvctpProtocolName;
+	aDesc->iAddrFamily		= KBTAddrFamily;
+	aDesc->iSockType		= KUndefinedSockType; // can support datagram AND raw
+	aDesc->iProtocol		= KAVCTP;
+	aDesc->iVersion			= TVersion(KBTMajor,KBTMinor,KBTBuild);
+	aDesc->iByteOrder		= ELittleEndian;
+	aDesc->iServiceInfo		= KServiceInfo;
+	aDesc->iNamingServices	= NULL;
+	aDesc->iSecurity		= KSocketNoSecurity; // cause we do our own
+	aDesc->iMessageSize		= KSocketMessageSizeNoLimit; // we can fragment to our heart's content
+	aDesc->iServiceTypeInfo	= KServiceTypeInfo;
+	aDesc->iNumSockets		= KMaxNumSocketsInProtocol;
+	}
+
+/** Our protocol reference in esock is now zero, so start to close.
+
+We don't actually close, merely Q a timer for a later close down.
+This close can be pre-empted by another open.
+
+@internalAll
+*/
+void CAvctpProtocol::CloseNow()
+	{
+	LOG_FUNC
+
+	iClosePending = ETrue;
+	
+	// We can only assert there are no saps cause the sap links mgrs are asynchronously deleted
+	// and so may still be around at this point. However they should've been deleted by the
+	// time TryToClose is called so we assert IsIdle() there.
+	ASSERT_DEBUG(iSaps.IsEmpty());
+	
+	QueIdleTimer();
+	}
+
+/** Request to open the protocol.
+
+The protocol may be repeatedly opened and closed.  The order of calls is
+InitL, [Open *n , Close * n,  CloseNow] * m etc.
+
+@internalAll
+*/
+void CAvctpProtocol::Open()
+	{
+	LOG_FUNC
+
+	iClosePending = EFalse;
+	DequeIdleTimer();
+	CProtocolBase::Open();
+	}
+
+/**
+   This is one session closing.
+*/
+void CAvctpProtocol::Close()
+
+	{
+	LOG_FUNC
+	CProtocolBase::Close();
+	}
+
+
+/**
+Helper to actually start listening. This protocol doesn't use the base class
+implementation as it needs to do a bit more.
+@internalComponent
+*/
+void CAvctpProtocol::DoStartAvctpListeningL()
+	{	
+	LOG_FUNC
+
+	const TUint KDefaultMtu = 335;
+	
+	CServProviderBase* sap =iLowerProtocol->NewSAPL(KSockSeqPacket);
+	CleanupStack::PushL(sap);
+	
+	TBTSockAddr localAddr;
+	localAddr.SetPort(KAVCTP);
+
+	// the security settings are:	
+	// (though see :Preauthorise() for the authentication exceptions due to avdtp authentication)
+	TBTServiceSecurity sec;
+	sec.SetAuthentication(KInboundAuthenticationDefault);
+	sec.SetAuthorisation(KInboundAuthoristationDefault);
+	sec.SetEncryption(KInboundEncryptionDefault);
+	sec.SetDenied(EFalse);
+	sec.SetUid(KAvctpServiceUid);
+	localAddr.SetSecurity(sec);
+	
+	CleanupStack::Pop(sap);
+	iListener = CIncomingConnectionListener::NewL(*this, sap, localAddr, KProtocolListeningQueueSize);										
+	
+	CServProviderBase* sap2 =iLowerProtocol->NewSAPL(KSockSeqPacket);
+	CleanupStack::PushL(sap2);
+	
+	TPckgBuf<TL2CapConfig> config;
+	config().SetMaxTransmitUnitSize(KAvctpSecondaryChannelInboundMTU);
+	config().SetMinMTU(KDefaultMtu);
+	config().SetMaxReceiveUnitSize(KAvctpSecondaryChannelInboundMTU);
+	config().SetMinMRU(KDefaultMtu);
+	
+	__DEBUG_ONLY(TInt err =) sap2->SetOption(KSolBtL2CAP, KL2CAPUpdateChannelConfig, config); 
+	__ASSERT_DEBUG(err == KErrNone, Panic(ESetOptionError));
+	
+	// security for the secondary channel is not required
+	sec.SetAuthentication(KSecondaryChannelAuthenticationDefault);
+	sec.SetAuthorisation(KSecondaryChannelAuthoristationDefault);
+	sec.SetEncryption(KInboundEncryptionDefault);
+	sec.SetDenied(EFalse);
+	sec.SetUid(KAvctpServiceUid);	
+	localAddr.SetSecurity(sec);
+	localAddr.SetPort(KAvctpSecondChannelPSM);
+	
+	CleanupStack::Pop(sap2);
+	iSecondChannelListener  = CIncomingConnectionListener::NewL(*this, sap2, localAddr, KProtocolListeningQueueSize);										
+	}
+
+/**
+AVCTP connection listener connect complete function (cloned SAP overload).
+
+Part of the MSocketNotify interface.
+
+Called by the listening L2CAP SAP when an incoming connection occurs.
+
+This checks with the protocol to check for an existing muxer - if there
+is one, an error is returned and the new L2CAP SAP is shut down (the 
+remote device should be using the existing muxer). If not, the SAP is
+given to the muxer.
+
+@internalAll
+@param aSap The SAP for the new lower layer connection
+*/
+TInt CAvctpProtocol::BearerConnectComplete(const TBTDevAddr& aAddr, CServProviderBase* aSSP)
+	{
+	LOG_FUNC
+	
+	IF_FLOGGING
+		(
+		TBuf<KBTAddressLength> address; 
+		aAddr.GetReadable(address);
+		)
+		
+	LOG1(_L("from BT Device 0x%S"), &address);
+	
+	// NB: If no error occurs, ownership of aSSP passes to the muxer.
+	// If an error does occur then the CIncomingConnectionListener will take care of aSSP for us
+	TInt err = AttachInboundConnectionToTransport(aAddr, aSSP);
+	
+	return err;
+	}
+
+
+
+/**
+Control plane message delivery system between protocols
+@return error as a result of processing or not consuming the control message
+@param aMessage the message
+@param aParam arbitrary data for message - knowledge of aMessage allows casting
+@see CBTProtocolFamily
+@internalComponent
+*/
+TInt CAvctpProtocol::ControlPlaneMessage(TBTControlPlaneMessage aMessage, TAny* aParam)
+	{
+	LOG_FUNC
+
+	// only ones applicable to this protocol at present are 
+	// for preauthorising a device - must have come from AVCTP, with PID=RCP
+	TInt ret = KErrNotSupported;
+	
+	switch (aMessage)
+		{
+		case EPreauthoriseDevice:
+			{
+			__ASSERT_DEBUG(aParam, Panic(EProtocolReceivingBadlyFormedControlMessage));
+			const TOverrideAuthorise& override = *reinterpret_cast<const TOverrideAuthorise*>(aParam);
+			__ASSERT_DEBUG(override.iAuthorisingProtocol == KAVDTP, Panic(EProtocolReceivingControlFromUnexpectedProtocol));
+			
+			SetPreauthorisation(override.iPreauthorisedRemoteAddress,
+								override.iPreauthorise);
+			ret = KErrNone;							
+			break;
+			}
+		default:
+			__ASSERT_DEBUG(aParam, Panic(EProtocolReceivingBadlyFormedControlMessage));
+		}
+	return ret;
+	}
+
+/**
+Helper to hide the need to supply the socket level to which preauthorisation pertains
+@param aPreauthoriseAddress the address of the device to preauthorise
+@param aSetPreauthorisation ETrue if the device is allowed to be authorised for other AVDTP/AVCTP connection, EFalse to cancel
+@internalComponent
+*/
+TInt CAvctpProtocol::SetPreauthorisation(const TBTDevAddr& aPreauthoriseAddress, TBool aSetPreauthorisation)
+	{
+	LOG_FUNC
+
+	TInt ret = KErrNone;
+	if (IsListening())
+		{
+		if (aSetPreauthorisation && !Listener().IsPreauthorised(KSolBtL2CAP, aPreauthoriseAddress))
+			{
+			Listener().SetPreauthorisation(KSolBtL2CAP, aPreauthoriseAddress, ETrue);
+
+			// tell AVDTP
+			TOverrideAuthorise override;
+			override.iAuthorisingProtocol = KAVCTP;
+			override.iAuthorisingPort = KAvrcpPid;			
+			override.iPreauthorise = aSetPreauthorisation;
+			override.iPreauthorisedRemoteAddress = aPreauthoriseAddress;
+			
+			ControlPlane().Preauthorise(KAVDTP, override);
+
+			}
+		else if (!aSetPreauthorisation)
+			{
+			Listener().SetPreauthorisation(KSolBtL2CAP, aPreauthoriseAddress, EFalse);			
+			}
+		// else do nothing
+		}
+	else
+		{
+		ret = KErrNotReady;
+		}
+	return ret;
+	}
+
+
+
+//
+// Interface to all Saps provided for the muxers
+
+/**
+  Used by a muxer to tell the saps that it is now in a position to 
+  send data
+	
+  @param aRemoteAddr the remote address which they an now send to.
+  @internalComponent
+*/
+void CAvctpProtocol::SignalCanSendToSaps(CAvctpPacketMgr& IF_FLOGGING(aPacketMgr))
+	{
+	LOG_FUNC
+	LOG1(_L("from Packetgr 0x%08x"), &aPacketMgr);
+	
+	// kick off the ipid sending process.
+	StartSendIpidAsyncCallBack();
+	
+	TDblQueIter<CAvctpSap> sapIter(iSaps);
+	CAvctpSap* sap;
+	while(sapIter)
+		{
+		sap = sapIter++;
+		sap->CanSend(); 
+		}	
+	}
+	 
+/**
+  Find out whether there are any saps with data for the given remote address
+  @param aRemoteAddr the remote device which might have data for it
+  @return ETrue if there is such a sap, EFalse otherwise
+  @internalComponent
+**/	
+TBool CAvctpProtocol::SapsHaveDataFor(const TBTDevAddr& aRemoteAddr)
+	{
+	LOG_FUNC
+	IF_FLOGGING
+		(
+		TBuf<KBTAddressLength> address;
+		aRemoteAddr.GetReadable(address);
+		)
+
+	LOG1(_L("from BT Device 0x%S"), &address);
+
+	TDblQueIter<CAvctpSap> iter(iSaps);
+	CAvctpSap* sap;
+	TBool ans = EFalse;
+	
+	while(iter)
+		{
+		sap = iter++;
+		if(sap->HasDataFor(aRemoteAddr))
+			{
+			ans = ETrue;
+			break;
+			}
+		}
+
+	LOG1(_L("result %d"), ans);
+	return ans;
+	}
+	
+/**
+Note the a TAvctpHeaderInfo could be provided as a parameter to this function
+but since the HAvctpIncomingSdu data contains this info too, there's a danger
+that the TAvctpHeaderInfo would not match the actual sdu. Hence the header
+info class isn't provided. This isn't a big cost cause it's quick to pull 
+the transaction label, PID etc out of the sdu itself.
+*/
+void CAvctpProtocol::SignalNewDataToSaps(HAvctpIncomingSdu* aSdu, TInt aChannel)
+	{
+	LOG_FUNC
+
+	// Give data to each sap in turn to see if they want the PDU
+	TDblQueIter<CAvctpSap> iter(iSaps);
+	CAvctpSap* sap;
+	TBool sapAcceptedData = EFalse;
+	while(iter)
+		{
+		sap = iter++;
+		sapAcceptedData = sap->NewData(aSdu, aChannel);
+		if (sapAcceptedData)
+			{
+			break; // Once one accepts it, there's no point giving the data to the others
+			}
+		}					  	 
+
+	if (!sapAcceptedData)
+		{
+		// This is not for a registered PID.  If this is a command we should
+		// return an IPID response, if not we just dump it.  Crazy remote!
+		TAvctpStartHeaderInfo headerInfo;
+		TInt err = CAvctpPacket::ParseHeader(aSdu->Data(), headerInfo);
+		
+		if(!err && headerInfo.iMsgType == SymbianAvctp::ECommand)
+			{
+			HAvctpOutgoingSdu* ipidSdu = NULL;
+			TRAPD(ret, ipidSdu = HAvctpOutgoingSdu::NewIpidResponseL(*aSdu, aChannel));
+			if (ret == KErrNone)
+				{
+				iIpidResponses.AddLast(*ipidSdu);
+				StartSendIpidAsyncCallBack();
+				}
+			}
+		// else - we've OOM & we'll have to break the AVCTP spec & not send an IPID response :(
+		// or it's a response that we can't re-respond to.
+
+		// No one took the data so we need to clean it up
+		delete aSdu;
+		}
+	}
+
+/**
+This function is called when a muxer goes down and is used to inform Saps
+of this fact so they can get rid of any packets that are now stale.
+*/
+void CAvctpProtocol::SignalMuxerDownToSaps(const TBTDevAddr& aRemoteAddr)
+	{
+	LOG_FUNC
+
+	IF_FLOGGING
+		(
+		TBuf<KBTAddressLength> address;
+		aRemoteAddr.GetReadable(address);
+		)
+
+	LOG1(_L("from BT Device 0x%S"), &address);
+	
+	TDblQueIter<CAvctpSap> iter(iSaps);
+	CAvctpSap* sap;
+	
+	while(iter)
+		{
+		sap = iter++;
+		sap->MuxerDown(aRemoteAddr);
+		}					  	 
+	}
+
+//
+// Que Management Functions	 
+
+/** 
+Adds a SAP to their queue.
+Ensures that there will only ever be one sap on the Q for each PID
+
+@internalComponent
+@param aSap The SAP to be added
+@return KErrInUse if there is already a Sap on aSap's PID, otherwise KErrNone
+*/   
+TInt CAvctpProtocol::AddSap(CAvctpSap& aSap)
+	{
+	LOG_FUNC
+	LOG2(_L("Adding 0x%08x on Pid 0x%x"), &aSap, aSap.Pid());
+	LOG_SAPS
+	
+	// The given sap must have a valid Pid to be on the protocol's Q
+	__ASSERT_DEBUG(aSap.Pid() != 0, Panic(ENullPid));
+	
+	TInt ret = KErrNone;
+	
+	CAvctpSap* sap = FindSap(aSap.Pid());
+	
+	if (!sap && aSap.iChannel == KAvctpSecondaryChannel)
+		{
+		ret = KErrNotReady;
+		}
+	else if (sap && aSap.iChannel == KAvctpPrimaryChannel)
+		{
+		// Bit of a hack to support two SAPs doing one PID
+		ret = KErrAlreadyExists;
+		}
+	else
+		{
+		// tell the sap that has been inserted in the list. Infact a sap that is not inserted in the
+		// list is shutdown from esock. In that process CAvctpProtocol::RemoveSap() is called and 
+		// would remove the "PID's client" from the iClientItems list. But it has to remove it only if that
+		// sap has been inserted in the list.
+		aSap.iIsInList = ETrue;
+		iSaps.AddFirst(aSap);
+		DequeIdleTimer();
+		}
+	
+	return ret;
+	}
+	
+/** 
+Removes a SAP from their queue.
+
+@internalComponent
+@param aSap The SAP to be removed
+*/   	
+void CAvctpProtocol::RemoveSap(CAvctpSap& aSap)
+	{
+	LOG_FUNC
+	LOG2(_L("Removing 0x%08x from Pid 0x%x"), &aSap, aSap.Pid());
+	LOG_SAPS
+
+	if (aSap.IsInList())
+		{
+		aSap.iQueLink.Deque(); // safe to Deque even if it's already been done
+		
+		TDblQueIter<CAvctpTransport> muxerIter(iTransports);
+		CAvctpTransport* transport = NULL;
+		
+		
+		while(muxerIter)
+			{
+			transport = muxerIter++;
+			//Sap pid is used as clientId key to uniquely identify the client RAvctp
+			if (transport->HasClient(aSap.Pid()))
+				{
+				if (aSap.Channel() == KAvctpPrimaryChannel)
+					{
+					transport->RemovePrimaryChannelRef(aSap.Pid());
+					}
+				else
+					{
+					transport->RemoveSecondaryChannelRef(aSap.Pid());
+					}
+				}
+			}
+	
+		// as we are removing the sap it means the RAvctp for this Pid is being closing
+		// so we must remove the TClientItem from the protocol. To avoid removing it twice
+		// (it should be safe though) we only remove it for the primary channel sap
+				
+		if (aSap.Channel() == KAvctpPrimaryChannel)
+			{
+			__DEBUG_ONLY(TInt err = ) iClientItems.Remove(aSap.Pid());
+			__ASSERT_DEBUG(err == KErrNone, Panic(EAvctpClientNotFound));
+			}
+		
+		CheckForIdle();
+		}
+	}
+
+/** Find the SAP that is on this PID.
+
+If no such SAP exists, return NULL. Note this function doesn't transfer ownership.
+
+There should be no Sap with a zero PID on the Q
+
+@internalComponent
+@param aClientId The client Id to look for
+@return The SAP connected on the specified PID, if any, otherwise NULL. 
+*/
+CAvctpSap* CAvctpProtocol::FindSap(TUint16 aClientId)
+	{
+	LOG_FUNC
+
+	TDblQueIter<CAvctpSap> iter(iSaps);
+	CAvctpSap* sap = NULL;
+	CAvctpSap* foundSap = NULL;
+
+	while (iter)
+		{
+		sap = iter++;
+		__ASSERT_DEBUG(sap->Pid() != 0, Panic(ENullPid));
+		if(sap->Pid() == aClientId)
+			{
+			foundSap = sap;
+			break;
+			}
+		}
+
+	return foundSap;
+	}
+	
+/** 
+Adds a Muxer to their queue which has been assigned a remote address. 
+Does the transfer from the blank muxer Q for the muxer.
+
+@internalComponent
+@param aMuxer The Muxer to be added
+@return KErrInUse if there is already a Muxer on aMuxer's BTDevAddr, otherwise KErrNone
+*/   
+TInt CAvctpProtocol::AddTransport(CAvctpTransport& aTransport)
+	{
+	LOG_FUNC
+
+	LOG_MUXERS
+	
+	// The given muxer must have a valid remote addr to be on the protocol's Q
+	__ASSERT_DEBUG(aTransport.DevAddr() != TBTDevAddr(0), Panic(ENullTBTDevAddr));
+	
+	TInt ret = KErrNone;
+	CAvctpTransport* transport = FindTransport(aTransport.DevAddr());
+	if (transport)
+		{
+		ret = KErrInUse;
+		}
+	else
+		{
+		iTransports.AddFirst(aTransport);
+		}
+	
+	return ret;
+	}
+
+/** 
+Removes a Muxer from their queue.
+
+@internalComponent
+@param aMuxer The Muxer to be removed
+*/   	 
+void CAvctpProtocol::RemoveTransport(CAvctpTransport& aTransport)
+	{
+	LOG_FUNC
+	LOG_MUXERS
+	  
+	aTransport.iQueLink.Deque(); // safe to Deque even if it's already been done
+	}
+
+/** Find the Muxer that is on this remote address.
+If no such Muxer exists, return NULL. Note this function doesn't transfer ownership.
+
+There should be no Muxer with a zero BTDevAddr on the Q
+@internalComponent
+@param aDevAddr The remote address to look for
+@return The Muxer connected on the specified remote address, if any, otherwise NULL.
+*/
+CAvctpTransport* CAvctpProtocol::FindTransport(const TBTDevAddr& aDevAddr)
+	{
+	LOG_FUNC
+
+	TDblQueIter<CAvctpTransport> iter(iTransports); 
+	CAvctpTransport* transport = NULL;
+	CAvctpTransport* foundTransport = NULL;
+
+	while (iter)
+		{
+		transport = iter++;
+
+		if(transport->DevAddr() == aDevAddr)
+			{
+			foundTransport = transport;
+			break;
+			}
+		}
+
+	return foundTransport;
+	}
+
+CAvctpTransport* CAvctpProtocol::FindOrCreateTransport(const TBTDevAddr& aDevAddr, TInt aChannel, CServProviderBase* aL2CAPConSAP)
+	{
+	LOG_FUNC
+	TInt err = KErrNone;
+	CAvctpTransport* transport = FindTransport(aDevAddr);
+	
+	if (!transport)
+		{
+		// we create the transport but, in this case, we don't have PIDs attached and 
+		// we cannot notify them
+		TRAP(err, transport = CAvctpTransport::NewL(*this));
+		if (err == KErrNone)
+			{
+			iTransports.AddLast(*transport);
+			}
+		
+		if (err == KErrNone)
+			{
+			if (aL2CAPConSAP)
+				{
+				err = transport->StartIncoming(aDevAddr, aL2CAPConSAP);
+				}
+			else
+				{
+				err = transport->Start(aDevAddr, KUndefinedPid);
+				}
+			
+			if (err == KErrNone)
+				{
+				// It's possible that the muxer had to synchronously delete itself
+				// and wasn't able to return a synchronous error.
+				// In this case the muxer will have removed itself from the protocol's Q
+				if (!FindTransport(aDevAddr))
+					{
+					transport = NULL; // since it's already dead
+					}
+				}
+			}
+		}
+	else if (aChannel == KAvctpSecondaryChannel)	// secondary channel
+		{
+		if (!transport->HasSecondChannel())
+			{
+			CServProviderBase* sap = NULL;
+			TRAP(err, sap = iLowerProtocol->NewSAPL(KSockSeqPacket));
+			if (err == KErrNone)
+				{
+				err = transport->AddSecondChannel(*sap);
+				if (err!=KErrNone)
+					{
+					delete sap;
+					sap = NULL;
+					}
+				}
+			}
+		}
+	return transport;
+	}
+
+void CAvctpProtocol::AwaitTransport(TUint16 aClientId, MSocketNotify& aControlSocket)
+	{
+	LOG_FUNC
+	AddClientItem(aClientId, aControlSocket);
+	}
+
+/**
+ It is submitted by the client saying it is happy to accept an incoming indication (for the primary channel).
+ When the client receive an attach indication it can decide to accept or refuse it. 
+ If accepts it sends back an agree attachment ioctl that results in this call.
+ @param aClientId is the RAvctp pid value
+ @param aRemoteAddr is the remote device's address which has been connected
+ */
+TInt CAvctpProtocol::PrimaryChannelAgreeAttachment(TUint16 aClientId, const TBTDevAddr& aRemoteAddr)
+	{
+	LOG_FUNC
+	TInt err = KErrMuxerNotFound;
+	CAvctpTransport* transport = FindTransport(aRemoteAddr);
+	if (transport)
+		{
+		TClientItem* item = iClientItems.Find(aClientId);
+		__ASSERT_DEBUG(item, Panic(EAvctpClientNotFound));
+		err = transport->AddPrimaryChannelRef(item); // it returns KErrNone or KErrNoMemory
+		}
+	return err;
+	}
+
+/**
+ It is submitted by the client saying it is happy to accept an incoming indication (for the secondary channel).
+ When the client receive an attach indication it can decide to accept or refuse it. 
+ If accepts it sends back an agree attachment ioctl that results in this call.
+ @param aClientId is the RAvctp pid value
+ @param aRemoteAddr is the remote device's address which has been connected
+ */
+TInt CAvctpProtocol::SecondaryChannelAgreeAttachment(TUint16 aClientId, const TBTDevAddr& aRemoteAddr)
+	{
+	LOG_FUNC
+	TInt err = KErrMuxerNotFound;
+	CAvctpTransport* transport = FindTransport(aRemoteAddr);
+	if (transport)
+		{
+		TClientItem* item = iClientItems.Find(aClientId);
+		__ASSERT_DEBUG(item, Panic(EAvctpClientNotFound));
+		transport->AddSecondaryChannelRef();
+		err = KErrNone;
+		}
+	return err;
+	}
+
+/**
+ If not already present it adds a TClientItem to the hashtable. Despite it was already present or not
+ it always calls AttachPrimaryChannel to update the MSocketNotify.
+ That is because once the TClientItem is added to the hastable it remains alive until the protocol 
+ is alive. So we need to update the socket observer because it can be changed. i.e an RAvctp is created
+ and used, then it is destroyed, and created again. The "clientId" remains the same, but the 
+ MSocketNofity passed through it is probably changed. The item will be found (already added to the
+ protocol hashtable) but the MSocketNotify& must be updated.
+ @param aClientId is the unique identifier of the RAvctp client
+ @param aNotify the socket observer for the ioctl messages (the primary channel CAvctpControlSAp)
+ */
+const TClientItem* CAvctpProtocol::AddClientItem(TUint16 aClientId, MSocketNotify& aNotify) // it doesn't pass the ownership
+	{
+	LOG_FUNC
+	TClientItem* pitem = iClientItems.Find(aClientId);
+	if (!pitem)
+		{
+		// pitem is null so, proceed creating it and inserting in the hashtable
+		TClientItem newItem(aClientId);
+		newItem.AttachPrimaryChannel(aNotify);
+		if (iClientItems.Insert(aClientId, newItem) == KErrNone)
+			{
+			// insertion succesfully, so we assign the inserted item to pitem
+			pitem = iClientItems.Find(aClientId);
+			}
+		// if an error occurred (out of memory) pitem is still null
+		}
+	
+	if (pitem)	// pitem can be null if the insertion failed 
+		{
+		// we update aNotify anyway, so it is always updated to the correct reference
+		pitem->AttachPrimaryChannel(aNotify);
+		}
+	
+	return const_cast<const TClientItem*>(pitem);	// it doesn't pass the ownership
+	}
+
+const TClientItem* CAvctpProtocol::ClientItem(TUint16 aClientId) const // it doesn't pass ownership
+	{
+	LOG_FUNC
+	const TClientItem* pitem = iClientItems.Find(aClientId);
+	return pitem;
+	}
+
+void CAvctpProtocol::NotifyLinkUp(const TBTDevAddr& aAddr, TBool aIsSecondChannel)
+	{
+	LOG_FUNC
+	TControlIoctlMessage msg(ELinkUp, aAddr);
+	TPckgC<TControlIoctlMessage> pck(msg);
+	
+	THashMapIter<TInt, TClientItem> iter(iClientItems);
+	while(iter.NextKey())
+		{
+		TClientItem* pitem = iter.CurrentValue();
+		MSocketNotify* socket = aIsSecondChannel ? pitem->SecondaryChannel() : pitem->PrimaryChannel();
+
+		__DEBUG_ONLY
+			(
+			if (!aIsSecondChannel)
+				{
+				__ASSERT_DEBUG(socket, Panic(EAvctpInvalidChannelNotify));
+				}
+			)
+
+		if (socket)
+			{
+			socket->IoctlComplete(&pck);
+			}
+		}
+	}
+
+/** Gets a muxer for the given BT device address, using the L2CAP connection provided.
+@internalComponent
+*/	
+TInt CAvctpProtocol::StartProtocolListening()
+	{
+	LOG_FUNC
+
+	// because we do non-default security we override the base class
+	// and new the incoming listener ourselves
+	TRAPD(err, DoStartAvctpListeningL());
+	return err;
+	}
+	
+/** Gets a muxer for the given BT device address.
+	
+If it finds the muxer or successfully creates a new muxer, it returns 
+with KErrNone and a valid pointer to the correct muxer. On a failure, 
+it will return with a standard error, without a pointer to  a muxer.
+This flavour of GetMuxer is used when a muxer is being created in response 
+to a local request to connect to a remote device (i.e. no existing 
+L2CAP connection exists) or when someone just wants a muxer on the given 
+address whether or not one exists.
+
+Note that because a synchronous error could occur during the Start call, 
+aMuxer might be NULL at the end of this function. Note thought this
+depends on whether aMuxer was able to asynchronously delete itself or not.
+Note that there could then be two error paths, one via the muxer sending
+a ConnectComplete (with error) & the return from this function. If this is
+the case, KErrSynchronousMuxerError will be returned which should be voided
+so that the client doesn't get two errors.
+
+@internalComponent
+@param aRemoteAddr The remote address for which we want a muxer
+@param aMuxer A muxer will be created by this function if one doesn't exist and a connection attempt start. 
+			  If there is an existing muxer it is returned aMuxer will be not be owned by the caller
+@param aLinksMgr a possible control client for the newly created muxer
+@return KErrNone if the muxer is successfully returned, 
+		KErrSynchronousMuxerError if the muxer failed immediately on the address given, or
+		KErrNoMemory or
+		any other error code from CAvctpTransport::NewL
+*/ 
+TInt CAvctpProtocol::ProvideTransport(const TBTDevAddr& aRemoteAddr, TUint16 aClientId,
+									  MSocketNotify& aControlSocket)
+	{
+	LOG_FUNC
+	IF_FLOGGING
+		(
+		TBuf<KBTAddressLength> address;
+		aRemoteAddr.GetReadable(address);
+		)
+
+	LOG1(_L("from BT Device 0x%S"), &address);
+	
+	TInt err = KErrNone;
+	
+	// first of all I add the Client Id to my list and get a pointer to it.
+	const TClientItem* item = AddClientItem(aClientId, aControlSocket);
+	
+	if (item)	// we were able to allocate the memory for it
+		{
+		CAvctpTransport* transport = FindTransport(aRemoteAddr);
+	
+		if (!transport)
+			{
+			TRAP(err,transport = CAvctpTransport::NewL(*this));
+			if (err == KErrNone)
+				{
+				err = transport->AddPrimaryChannelRef(item);
+				if (err == KErrNone)
+					{
+					iTransports.AddLast(*transport);
+					}
+				else
+					{
+					// it is safe to call it on a non-attached clientId and it is the only
+					// way we have to ask the transport to destroying itself
+					// as we just created the transport we know calling this method will destroy it
+					transport->RemovePrimaryChannelRef(aClientId);
+					}
+				}
+			}
+		else
+			{
+			err = transport->AddPrimaryChannelRef(item);
+			// in this case we already had the transport, probably with some clients attached.
+			// we can't destroy it, and it is working for the attached clients. So, we simply
+			// return an error to the client is asking to be attached.
+			}
+		
+		if (err == KErrNone)
+			{
+			err = transport->Start(aRemoteAddr, aClientId);
+	
+			if (err == KErrNone)
+				{
+				// It's possible that the muxer had to synchronously delete itself
+				// and wasn't able to return a synchronous error (e.g. if the client
+				// tries to connect to itself and the async deletion attempt OOMs)
+				// In this case the muxer will have removed itself from the protocol's Q
+				if (!FindTransport(aRemoteAddr))
+					{
+					transport = NULL; // since it's already dead
+					err = KErrSynchronousMuxerError;
+					}
+				}
+			}
+		}
+	else
+		{
+		err = KErrNoMemory;
+		}
+	
+	return err;
+	}
+
+TInt CAvctpProtocol::ActiveExtendTransport(const TBTDevAddr& aRemoteAddr, TUint16 aClientId, MSocketNotify& aSecondChannelControlSocket)
+	{
+	LOG_FUNC
+	CAvctpTransport* transport = FindTransport(aRemoteAddr);
+	TInt err = KErrMuxerNotFound;
+	if (transport)
+		{
+		if (!transport->HasSecondChannel())
+			{
+			// get a new SAP and ask transport to connect it up
+			CServProviderBase* sap = NULL;
+			TRAP(err, sap = iLowerProtocol->NewSAPL(KSockSeqPacket));
+			err = transport->AddSecondChannel(*sap);
+			if (err!=KErrNone)
+				{
+				delete sap;
+				sap = NULL;
+				}
+			else
+				{
+				transport->SetSecondChannelCtrlNotify(aClientId, aSecondChannelControlSocket);
+				transport->AddSecondaryChannelRef();
+				}
+			}
+		else
+			{
+			transport->SetSecondChannelCtrlNotify(aClientId, aSecondChannelControlSocket);
+			transport->AddSecondaryChannelRef();
+			transport->NotifyAttachConfirm(aClientId, KErrNone, ETrue);
+			}
+		}
+	return err;
+	}
+
+void CAvctpProtocol::SetSecondChannelCtrlNotify(TUint16 aClientId, MSocketNotify& aSecondChannelControlSocket)
+	{
+	LOG_FUNC
+	TClientItem* item = iClientItems.Find(aClientId);
+	__ASSERT_DEBUG(item, Panic(EAvctpClientNotFound));
+	item->AttachSecondaryChannel(aSecondChannelControlSocket);
+	}
+
+void CAvctpProtocol::AwaitForExtendedTransport(TUint16 aClientId, MSocketNotify& aSecondChannelControlSocket)
+	{
+	LOG_FUNC
+	SetSecondChannelCtrlNotify(aClientId, aSecondChannelControlSocket);
+	}
+
+void CAvctpProtocol::PrimaryChannelRefuseAttach(const TBTDevAddr& aRemoteAddr, TUint16 aClientId)
+	{
+	LOG_FUNC
+	
+	CAvctpTransport* transport = FindTransport(aRemoteAddr);
+	
+	// when this method is called it means the client refused to be attached to the transport
+	// and didn't call the AgreeAttachment. It means that we don't have the TClientItem attached to
+	// the transport.
+	// However we call transport->RemovePrimaryChannelRef anyway. It is safe to call it even if the
+	// aClientId item is not attached, and it does the reference count control so that if there
+	// are no more clients attached it destroys the transport.
+	// this method can also be called on the PrimaryChannelCancelAttach invocation client side.
+	// In this case the client will be found and removed
+	if(transport)
+		{
+		// NOTE: if transport has no more clients attached it deletes itself.
+		// so, don't rely on transport after this call because it could have been deleted
+		transport->RemovePrimaryChannelRef(aClientId);
+		}
+	}
+
+void CAvctpProtocol::SecondaryChannelRefuseAttach(const TBTDevAddr& aRemoteAddr, TUint16 aClientId)
+	{
+	LOG_FUNC
+
+	CAvctpTransport* transport = FindTransport(aRemoteAddr);
+	
+	// when this method is called it means the client refused to be attached to the transport
+	// and didn't call the AgreeAttachment. It means that we don't have the TClientItem attached to
+	// the transport.
+	// However we call transport->RemovePrimaryChannelRef anyway. It is safe to call it even if the
+	// aClientId item is not attached, and it does the reference count control so that if there
+	// are no more clients attached it destroys the transport.
+	
+	if(transport)
+		{
+		// we must call AddSecondaryChannelRef() first, that increments the reference count
+		// because then we call RemoveSecondaryChannelRef() that decrements it.
+		// we want to call RemoveSecondaryChannelRef() because it manages the zero reference which
+		// drives the secondary l2cap channel destruction 
+		transport->AddSecondaryChannelRef();
+		transport->RemoveSecondaryChannelRef(aClientId);
+		}
+	}
+
+TInt CAvctpProtocol::GetChannelMtu(TInt aChannel, const TBTDevAddr& aAddr, TInt& aMtu)
+	{
+	LOG_FUNC
+	TInt err = KErrNotReady;
+	CAvctpTransport* t = FindTransport(aAddr);
+	if(t)
+		{
+		err = t->GetChannelMtu(aChannel, aMtu);
+		}
+	return err;
+	}
+
+TInt CAvctpProtocol::ReleaseTransport(const TBTDevAddr& aRemoteAddr, TUint16 aClientId)
+	{
+	LOG_FUNC
+	TInt err = KErrMuxerNotFound;
+	CAvctpTransport* transport = FindTransport(aRemoteAddr);
+	if(transport)
+		{
+		err = KErrNone;
+		transport->NotifyDetachConfirm(aClientId, err);
+		// NOTE: if there are no more pid attached to the transport it will delete itself.
+		// so, don't rely on the transport after this call because it could have been deleted
+		transport->RemovePrimaryChannelRef(aClientId);
+		}
+	return err;
+	}
+
+TInt CAvctpProtocol::ReleaseExtendedTransport(const TBTDevAddr& aRemoteAddr, TUint16 aClientId)
+	{
+	LOG_FUNC
+	TInt err = KErrMuxerNotFound;
+	CAvctpTransport* transport = FindTransport(aRemoteAddr);
+	if(transport)
+		{
+		err = KErrNone;
+		transport->NotifyDetachConfirm(aClientId, err, ETrue);
+		transport->RemoveSecondaryChannelRef(aClientId);
+		}
+	return err;
+	}
+
+void CAvctpProtocol::PrimaryChannelIncomingRemoteDisconnection(const TBTDevAddr& aRemoteAddr)
+	{
+	LOG_FUNC
+	CAvctpTransport* transport = FindTransport(aRemoteAddr);
+	if(transport)
+		{
+		// the secondary channel, if present, has been already destroyed.
+		transport->NotifyLinkDown(aRemoteAddr, KAvctpPrimaryChannel);	// we are destroying the transport 
+		}
+	}
+
+/**
+Gets a muxer for the given BT device address, using the L2CAP connection provided.
+
+If it is found that a muxer for the specified BT device already exists, 
+KErrAlreadyExists will be returned. If a new muxer is successfully created, 
+KErrNone will be returned along with a valid pointer to the correct muxer in aMuxer 
+
+This flavour of GetMuxer is used when a muxer is being created in response to a 
+remote request to connect to the local device (i.e. an L2CAP SAP has been created 
+due to an incoming connection).
+
+Note that because a synchronous error could occur during the Start call, 
+aMuxer might be NULL at the end of this function. Note thought this
+depends on whether aMuxer was able to asynchronously delete itself or not.
+Note that there could then be two error paths, one via the muxer sending
+a ConnectComplete (with error) & the return from this function. If this is
+the case, KErrSynchronousMuxerError will be returned which should be voided
+so that the client doesn't get two errors.
+
+@internalComponent
+@param aRemoteAddr The remote address for which we want a muxer
+@param aL2CAPConSAP This is a non null, L2CAP sap for the incoming connection from aRemoteAddr
+@param aMuxer A muxer created by this function to hold aL2CAPConSAP (assuming there wasn't one already for aRemoteAddr). aMuxer will be not be owned by the caller
+@return KErrNone if the muxer is successfully returned, 
+		KErrSynchronousMuxerError if the muxer failed immediately on the address give, or
+		any other error code from CAvctpTransport::NewL
+*/
+TInt CAvctpProtocol::AttachInboundConnectionToTransport(const TBTDevAddr& aRemoteAddr,
+														CServProviderBase* aL2CAPConSAP)
+	{
+	LOG_FUNC
+	__DEBUG_ONLY
+		(
+		TBuf<KBTAddressLength> address;
+		aRemoteAddr.GetReadable(address);
+		LOG1(_L("from BT Device 0x%S"), &address);
+		
+		//	Check that the device address and the device of the L2CAP remote address match!!
+		TBTSockAddr addr;
+		aL2CAPConSAP->RemName(addr);
+		__ASSERT_DEBUG(addr.BTAddr()==aRemoteAddr,Panic(EMismatchedAddressAndSap));
+		)
+
+	TL2CAPPort localPSM;
+	TPckg<TL2CAPPort> localPSMBuf(localPSM);
+	TInt err = aL2CAPConSAP->GetOption(KSolBtL2CAP, KL2CAPLocalPSM, localPSMBuf);
+
+	__ASSERT_DEBUG(err == KErrNone, Panic(EGetOptionError));
+	
+	//first try to find the transport with a given address: if it exists then this inbound link could be the second channel
+	CAvctpTransport* transport = FindTransport(aRemoteAddr);
+
+	if (transport)
+		{
+		// ok so try finding a listener transport for this link (which should be control)
+		if (localPSM == KAvctpSecondChannelPSM)
+			{
+			transport->AddSecondChannel(*aL2CAPConSAP);
+			}
+		else
+			{
+			// remote was being daft
+			return KErrGeneral;
+			}
+		}
+	else
+		{
+		
+		if (localPSM != KAVCTP)
+			{
+			return KErrGeneral; // the other type of daft remote
+			}
+				
+		// else remote sensibly connected first channel to the right PSM, for which we should have a listening transport on bdaddr==0
+		transport = FindOrCreateTransport(aRemoteAddr, KAvctpPrimaryChannel, aL2CAPConSAP); 
+		if (transport)
+			{
+			// It's possible that the muxer had to synchronously delete itself
+			// and wasn't able to return a synchronous error (e.g. if the client
+			// tries to connect to itself and the async deletion attempt OOMs)
+			// In this case the muxer will have removed itself from the protocol's Q
+			if (!FindTransport(aRemoteAddr))
+				{
+				transport = NULL; // since it's already dead
+				err = KErrSynchronousMuxerError;
+				}
+			else
+				{	// notify link up
+				NotifyLinkUp(aRemoteAddr, EFalse);
+				}
+			}
+		else
+			{
+			err = KErrNotReady;
+			}
+		}
+
+	return err;
+	}
+
+/** 
+Provide interface to class to send AVCTP SDUs (if one for the needed address exists
+*/
+MAvctpSDUSender* CAvctpProtocol::GetSDUSender(const TBTDevAddr& aRemoteAddr, TInt aChannel)
+	{
+	LOG_FUNC
+
+	CAvctpTransport* transport = FindOrCreateTransport(aRemoteAddr, aChannel);
+	return transport ? &static_cast<MAvctpSDUSender&>(transport->PacketMgr()) : NULL;
+	}
+
+/**
+Check to see if we're still needed.  If not, Q a delayed delete.
+
+  @internalComponent
+*/
+void CAvctpProtocol::CheckForIdle()
+	{
+	LOG_FUNC
+
+	if (IsIdle())
+		{
+		QueIdleTimer();
+		}
+	}
+
+/** 
+Called to check whether we can close down. I.e. are there any clients remaining on the
+other side of Esock.
+
+We could be idle even if there are Avctp muxers alive since Esock won't know about these
+
+@internalComponent
+@return ETrue if the protocol can close, EFalse if not
+*/
+TBool CAvctpProtocol::IsIdle()
+	{
+	LOG_FUNC
+	LOG_SAPS
+	
+	return (iClosePending && 
+			iSaps.IsEmpty());
+	}
+	
+/**
+Queues the idle timer if necessary
+
+  @internalComponent
+*/
+void CAvctpProtocol::QueIdleTimer()
+	{
+	LOG_FUNC
+
+	if (!iIdleTimerQueued)
+		{
+		LOG(_L("Queued idle timer"));
+
+		iIdleTimerQueued = ETrue;
+		BTSocketTimer::Queue(KProtocolIdleTimeout, iIdleTimerEntry);
+		}
+	}
+
+/**
+Deques idle timer if necessary
+
+  @internalComponent
+*/
+void CAvctpProtocol::DequeIdleTimer()
+	{
+	LOG_FUNC
+
+	if (iIdleTimerQueued)
+		{
+		LOG(_L("Dequeued idle timer"));
+
+		iIdleTimerQueued = EFalse;
+		BTSocketTimer::Remove(iIdleTimerEntry);
+		}
+	}
+
+	
+/** Asynchronous callback function.
+
+We check if should close then close.
+
+@internalComponent
+@param aProtocol The protocol object
+@return EFalse to indicate the callback does not need to be reissued
+*/
+TInt CAvctpProtocol::TryToClose(TAny* aProtocol)
+	{
+	LOG_STATIC_FUNC
+
+	CAvctpProtocol* protocol	= static_cast<CAvctpProtocol*>(aProtocol);
+	protocol->iIdleTimerQueued	= EFalse;
+	__ASSERT_DEBUG(protocol->IsIdle(), Panic(EIdleTimeoutWhenNotIdle));
+	protocol->CanClose();
+
+	return EFalse; // don't try to callback again
+	}
+
+TInt CAvctpProtocol::IpidAsyncCallBack(TAny* aProtocol)
+	{
+	LOG_STATIC_FUNC
+
+	__ASSERT_DEBUG(aProtocol, Panic(ENullAvctpProtocol));
+	
+	CAvctpProtocol& protocol = *static_cast<CAvctpProtocol*>(aProtocol);
+
+	TDblQueIter<HAvctpOutgoingSdu> iter(protocol.iIpidResponses);
+	HAvctpOutgoingSdu* sdu = NULL;
+	MAvctpSDUSender* sender = NULL;
+	
+	TInt err;
+	
+	while(iter)
+		{
+		sdu = iter++;
+		sender = protocol.GetSDUSender(sdu->BTAddr(), sdu->Channel());
+
+		if (sender)
+			{
+			LOG1(_L("Attempt to write IPID response 0x%08x in IpidAsyncCallBack"), &sdu);
+			if ((err = sender->WriteIpid(sdu)) != KErrNone)
+				{
+				if (err == KErrRemoteSentTooManyIpidSdus)
+					{
+					CAvctpTransport* transport = protocol.FindTransport(sdu->BTAddr());
+					if (transport)
+						{
+						transport->Shutdown(KErrRemoteSentTooManyIpidSdus);
+						}
+					}
+				else if (err == KErrMuxerBlocked)
+					{
+					//got blocked
+					//it will be called again asyncronously
+					}
+				}
+			// else send completed and sdu ownership transferred to the sender
+			}
+		else
+			{
+			// can't send the IPID since we don't have a data path to send it over
+			delete sdu; // will deque the sdu
+			}
+		}	
+	return EFalse;	
+	}
+	
+#ifdef __FLOG_ACTIVE	
+void CAvctpProtocol::LogSaps()
+	{
+	LOG_FUNC
+	TDblQueIter<CAvctpSap> iter(iSaps);
+	CAvctpSap* sap = NULL;
+	while (iter)
+		{
+		sap = iter++;
+		LOG2(_L("0x%08x is a sap on PID %d"), sap, sap->Pid());
+		}	
+	}
+	
+void CAvctpProtocol::LogMuxers()
+	{
+	LOG_FUNC
+	TDblQueIter<CAvctpTransport> iter(iTransports);
+	CAvctpTransport* transport = NULL;
+	TBuf<KBTAddressLength> address;
+	while (iter)
+		{
+		transport= iter++;
+		transport->DevAddr().GetReadable(address);
+		LOG2(_L("0x%08x is a transport on BT Device %S"), transport, &address);
+		}	
+	}
+
+#endif
+
+// TClientItem
+
+TClientItem::TClientItem(TUint16 aClientId) : 
+	iClientId(aClientId),
+	iPrimaryChannel(NULL),
+	iSecondaryChannel(NULL),
+	iIsSecondaryChannelAttached(EFalse),
+	iRefCount(0)
+	{
+	LOG_FUNC
+	}
+
+void TClientItem::AttachPrimaryChannel(MSocketNotify& aNotify)
+	{
+	LOG_FUNC
+	iPrimaryChannel = &aNotify;
+	iIsSecondaryChannelAttached = EFalse;
+	}
+
+void TClientItem::AttachSecondaryChannel(MSocketNotify& aNotify)
+	{
+	LOG_FUNC
+	iSecondaryChannel = &aNotify;
+	iIsSecondaryChannelAttached = ETrue;
+	}
+
+TUint16 TClientItem::ClientId() const
+	{
+	LOG_FUNC
+	return iClientId;
+	}
+
+MSocketNotify* TClientItem::PrimaryChannel() const
+	{
+	LOG_FUNC
+	return iPrimaryChannel;
+	}
+
+MSocketNotify* TClientItem::SecondaryChannel() const
+	{
+	LOG_FUNC
+	return iSecondaryChannel;
+	}
+
+TBool TClientItem::IsSecondaryChannelAttached() const
+	{
+	LOG_FUNC
+	return iIsSecondaryChannelAttached;
+	}
+
+void TClientItem::DetachSecondaryChannel()
+	{
+	LOG_FUNC
+	iSecondaryChannel = NULL;
+	iIsSecondaryChannelAttached = EFalse;
+	}