bluetooth/btstack/avctp/avctpsap.cpp
changeset 0 29b1cd4cb562
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btstack/avctp/avctpsap.cpp	Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,1605 @@
+// 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 code for AVCTP SAPs
+// 
+//
+
+/**
+ @file
+ @internalTechnology
+*/
+
+#include <bluetooth/logger.h>
+#include <es_mbuf.h>
+#include "Avctp.h"
+#include "avctpsap.h"
+#include "avctpmuxer.h"
+#include "avctpcommon.h"
+#include "avctputils.h"
+#include "avctppacket.h"
+#include "avctpPacketMgr.h"
+#include "AvctpMessageParameters.h"
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_AVCTP);
+#endif
+
+#ifdef _DEBUG
+PANICCATEGORY("avctpsap");
+#endif
+
+using namespace SymbianAvctp;
+
+/** 
+Avctp Sap Security policy  
+We don't need to check LocalServices as BluetoothSAP does that for us
+*/
+_LIT_SECURITY_POLICY_S0(KAvctpSapSecurityPolicy, KDefaultSidforPids);
+
+/** Diagnostic string identifying this module when calling security policy checking methods,
+in builds without platsec diagnostics this will be NULL */
+const char * const KAvctpSapSecurityPolicyDiagnostic = __PLATSEC_DIAGNOSTIC_STRING("Avctp Sap");
+
+/**
+Static AVCTP SAP factory function
+
+  @internalComponent
+  @leave KErrNoMemory if the SAP object could not be allocated
+  @param aProt Lower protocol object
+  @return A pointer to the new SAP
+*/
+CAvctpSap* CAvctpSap::NewL(CAvctpProtocol& aProt)
+	{
+	LOG_STATIC_FUNC
+
+	CAvctpSap* sap = new(ELeave) CAvctpSap(aProt);
+	CleanupStack::PushL(sap);
+	sap->ConstructL();
+	CleanupStack::Pop(sap);
+	return sap;
+	}
+
+/**
+Second-phase construction of a SAP
+
+Set up the async. callback.
+
+  @internalComponent
+*/
+void CAvctpSap::ConstructL()
+	{
+	LOG_FUNC
+
+	CBluetoothSAP::ConstructL();
+
+	TCallBack cb(SendAsyncCallBack, this);
+	iSendAsyncCallBack = new(ELeave) CAsyncCallBack(cb, EActiveHighPriority);
+	}
+
+/**
+Constructor for an AVCTP SAP
+
+  @internalComponent
+  @param aProt The AVCTP protocol object
+*/
+CAvctpSap::CAvctpSap(CAvctpProtocol& aProtocol)
+	: CBluetoothSAP(aProtocol.SecMan(), aProtocol.CodMan()),
+	  iProtocol(aProtocol),
+	  iOutgoingSdus(),
+	  iIncomingSdus(_FOFF(HAvctpIncomingSdu, iQueLink)),
+	  iChannel(KAvctpInvalidChannel),
+	  iIsInList(EFalse)
+	{
+	LOG_FUNC
+	// we set the channel because "0" is a real channel, so we want to know that iChannel is unset at time of construction
+	}
+
+
+/**
+Destructor for an AVCTP SAP
+
+Called when ESock deletes the SAP, when user app calls RSocket::Close()
+
+  @internalAll
+*/
+CAvctpSap::~CAvctpSap()
+	{
+	LOG_FUNC
+	
+	// Delete any remaining packets that the sap owns
+	TDblQueIter<HAvctpOutgoingSdu> outgoingIter = iOutgoingSdus.Iter();
+	while (outgoingIter)
+		{
+		delete outgoingIter++; // deques sdu
+		}
+	TDblQueIter<HAvctpIncomingSdu> incomingIter(iIncomingSdus);
+	while (incomingIter)
+		{
+		delete incomingIter++; // deques sdu
+		}	
+	
+	iSendAsyncCallBack->Cancel();
+	delete iSendAsyncCallBack;
+	}
+
+MSocketNotify& CAvctpSap::Socket() const
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(iSocket, Panic(EAvctpDataSAPNullSocket));
+	return *iSocket;
+	}
+
+/****************************************************************************/
+/* Implementation of CControlledServProvider methods */
+/****************************************************************************/
+
+
+/**
+Set the local name to anAddr.
+
+@internalAll
+@param anAddr The address with the PID
+@return KErrNone if successful
+*/
+TInt CAvctpSap::SetLocalName(TSockAddr& aAddr)
+	{
+	LOG_FUNC
+
+	TAvctpSockAddr avctpAddr = TAvctpSockAddr::Cast(aAddr);
+	iChannel = avctpAddr.Channel(); 
+	__ASSERT_DEBUG(iChannel == KAvctpPrimaryChannel || iChannel == KAvctpSecondaryChannel, Panic(EAvctpInvalidChannel));
+	
+	TInt err = KErrNone;
+	if (iChannel != KAvctpPrimaryChannel && iChannel != KAvctpSecondaryChannel)
+		{
+		err = KErrInvalidChannel;
+		}
+	if (err == KErrNone)
+		{
+		err = SetPid(avctpAddr.Pid());
+		}
+	if (err == KErrNone)
+		{
+		err = iProtocol.AddSap(*this);
+		}
+	return err;
+	}
+	
+/**
+Read the Local Name into anAddr.
+
+AVCTP knows only the PID on which the device is
+registered but this is OK since that's all they want.
+
+  @internalAll
+  @param anAddr The address to read into
+*/
+void CAvctpSap::LocalName(TSockAddr& aAddr) const
+	{
+	LOG_FUNC
+
+	// Copy iPid into TSockAddr and return
+	TAvctpSockAddr avctpAddr(aAddr);
+	avctpAddr.SetChannel(iChannel);
+	avctpAddr.SetPid(iPid);
+	aAddr = avctpAddr;
+	}
+
+/**
+Read the remote name into anAddr.
+
+AVCTP knows only the PID on which the device is
+registered but this is OK since that's all they want.
+
+  @internalAll
+  @param anAddr The address to read into
+*/
+void CAvctpSap::RemName(TSockAddr& aAddr) const
+	{
+	LOG_FUNC
+	// no BT Addr to fill on for data saps :-)
+	TAvctpSockAddr avctpAddr(aAddr);
+	avctpAddr.SetPid(iPid);
+	aAddr = avctpAddr;   // Convert back
+	}
+
+/**
+Set the remote name to anAddr.
+
+  @internalAll
+  @param anAddr The address to set
+*/
+TInt CAvctpSap::SetRemName(TSockAddr& aAddr)
+	{
+	LOG_FUNC
+
+	// Copy this over
+	TAvctpSockAddr avctpAddr = TAvctpSockAddr::Cast(aAddr);
+
+	iChannel = avctpAddr.Channel();
+	__ASSERT_DEBUG(iChannel == KAvctpPrimaryChannel || iChannel == KAvctpSecondaryChannel, Panic(EAvctpInvalidChannel));
+	
+	TInt err = KErrNone;
+	if (iChannel != KAvctpPrimaryChannel && iChannel != KAvctpSecondaryChannel)
+		{
+		err = KErrInvalidChannel;
+		}
+	
+	if (err == KErrNone)
+		{
+		err = SetPid(avctpAddr.Pid());
+		}
+
+	return err;
+	}
+
+/**
+Auto bind from ESock.
+
+We're not expecting this as a connectionless protocol so panic
+
+  @internalAll
+*/
+void CAvctpSap::AutoBind()
+	{
+	LOG_FUNC
+	__PANIC_UNEXPECTED_CALL
+	}
+
+/**
+Get a socket option.
+
+AVCTP doesn't support socket options, so fail it if it's for AVCTP.
+
+  @internalAll
+  @param aLevel The socket option level
+  @param aName The socket option name
+  @param aOption The socket option data
+*/
+TInt CAvctpSap::GetOption(TUint /*aLevel*/, TUint /*aName*/, TDes8& /*aOption*/) const
+	{
+	LOG_FUNC
+	
+	return KErrNotSupported;
+	}
+
+/**
+Set a socket option. (BluetoothSAP version)
+
+AVCTP doesn't support socket options, so fail it if it's for AVCTP.
+
+  @internalAll
+  @param aLevel The socket option level
+  @param aName The socket option name
+  @param aOption The socket option data
+*/
+TInt CAvctpSap::SAPSetOption(TUint /*aLevel*/, TUint /*aName*/, const TDesC8& /*aOption*/)
+	{
+	LOG_FUNC
+	return KErrNotSupported;
+	}
+
+
+// start-up/Shutdown
+
+// NB: All ActiveOpen / PassiveOpen / Start calls require a connection-
+//     oriented socket, so we just panic!
+
+/**
+Active open an AVCTP socket...
+
+Wait a minute!  AVCTP is connectionless, so panic!
+
+  @internalAll
+*/
+void CAvctpSap::ActiveOpen()
+	{
+	LOG_FUNC
+	Panic(EActiveOpenNotSupported); // control sap does this
+	}
+
+/**
+Active open an AVCTP socket (data overload)...
+
+Wait a minute!  AVCTP is connectionless, so panic!
+
+  @internalAll
+  @param aConnectionData Data to send on connection
+*/
+void CAvctpSap::ActiveOpen(const TDesC8& /*aConnectionData*/)
+	{
+	LOG_FUNC
+	Panic(EActiveOpenNotSupported);
+	}
+
+/**
+Passive open an AVCTP socket...
+
+Wait a minute, passive opens are only for connection-oriented protocols too! Panic.
+
+  @internalAll
+  @param aQueSize How many connections to complete before they are Accept()ed...
+  @return KErrNotSupported, obviously...
+*/
+TInt CAvctpSap::PassiveOpen(TUint /*aQueSize*/)
+	{
+	LOG_FUNC
+	Panic(EPassiveOpenNotSupported);
+	return KErrNotSupported;
+	}
+
+/**
+Passive open an AVCTP socket (data overload)...
+
+Wait a minute, passive opens are only for connection-oriented protocols too! Panic.
+
+  @internalAll
+  @param aQueSize How many connections to complete before they are Accept()ed...
+  @param aConnectionData Data to send on connection
+  @return KErrNotSupported, obviously...
+*/
+TInt CAvctpSap::PassiveOpen(TUint /*aQueSize*/, const TDesC8& /*aConnectionData*/)
+	{
+	LOG_FUNC
+	Panic(EPassiveOpenNotSupported);
+	return KErrNotSupported;
+	}
+
+/**
+We ignore this.
+
+  @internalAll
+*/
+void CAvctpSap::Start()
+	{
+	LOG_FUNC
+	}
+
+/**
+Close the SAP down.
+
+  @internalAll
+  @param aCloseType How fast we're going down
+*/
+void CAvctpSap::Shutdown(TCloseType aCloseType)
+	{
+	LOG_FUNC
+	LOG1(_L("option = %d"), aCloseType);
+
+	if (aCloseType != EImmediate)
+		{
+		// We need to remember we've been asked to close so:
+		iIsClosing = ETrue;
+		
+		// Normal Shutdown - ESOCK will wait for us to say we can end		
+		// Esock is no longer interested in our incoming packets so delete them
+		TDblQueIter<HAvctpIncomingSdu> incomingIter(iIncomingSdus);
+		while (incomingIter)
+			{
+			delete incomingIter++; // deques sdu
+			}	
+			
+		CheckForCanClose();	
+		}
+	else
+		{
+		// Note: Esock doesn't require us to call CanClose on an EImmediate Shutdown
+		iClosed = ETrue;
+		
+		iProtocol.RemoveSap(*this);
+		}
+	}
+
+/**
+Close the SAP down (data overload).
+
+  @internalAll
+  @param aCloseType How fast we're going down
+  @param aDisconnectionData Data to send on disconnect
+*/
+void CAvctpSap::Shutdown(TCloseType /*aCloseType*/, const TDesC8& /*aDisconnectionData*/)
+	{
+	LOG_FUNC
+	Panic(EDisconnectDataNotSupported);
+	}
+
+/**
+Send some data.
+
+Just pass this over to our state.
+
+  @internalAll
+  @param aDesc The data to send
+  @param aOptions The message parameters
+  @param aAddr The address to send to
+  @return 0 if the write failed or 1 if we took ownership of the data
+*/
+TInt CAvctpSap::Write(RMBufChain& aData, TUint aOptions, TSockAddr* aAddr)
+	{
+	LOG_FUNC
+
+	// We've been asked to close so esock shouldn't be giving us any more data
+	__ASSERT_ALWAYS(!iIsClosing, Panic(EUnexpectedEsockEvent));
+	
+	TUint ret = 0;
+	
+	if (iOutgoingSdus.Count() < KSapOutboundQHighMark)
+		{
+		TAvctpMessageParameters parameters(aOptions);
+		TAvctpNormalHeaderInfo headerInfo = TAvctpNormalHeaderInfo(
+												parameters.iTransaction, 
+												ENormalPkt, 
+												parameters.Type(),
+												ETrue, // hasValidPid
+												Pid());
+												
+		HAvctpOutgoingSdu* sdu = NULL;
+		
+		TRAPD(err,sdu = HAvctpOutgoingSdu::NewL(headerInfo, 
+												TAvctpSockAddr(*aAddr).BTAddr(),
+												aData));
+		if (err == KErrNone)
+			{
+			iOutgoingSdus.Insert(*sdu);
+			StartSendAsyncCallBack();
+			ret = 1; //one sdu
+			}
+		}
+
+	if (!ret)
+		{
+		iSendBlocked = ETrue;
+		iPendingSendAddress = TAvctpSockAddr::Cast(*aAddr).BTAddr();
+		}
+	return ret;
+	}
+	
+/**
+This function is called on the Sap by Esock when it wants to receive the
+data we told it about in NewData upcalls from us.
+
+Rather than letting Esock call the descriptor overload, this function
+deals with the request directly for two reasons. The intention is to 
+implement AVCTP in terms of MBufs in future but also because the descriptor 
+overload doesn't get called correctly by esock since it doesn't know how
+long our datagrams will be and so presents us with a TDes8 which is too 
+short.
+
+  @internalAll
+  @param aData The data to be received
+  @param aOptions The message parameters
+  @param aAddr The address the data is received from
+  @return the error associated with the read. Could be KErrNoMBufs (a likely case) in which case esock 
+  		   would attempt an allocation for us and would then try again. If it's some other error then it's up to us 
+  		   to tell esock when to retry (by signalling with NewData(0)). If it was some error that we couldn't
+  		   handle at all then we should have set the error upon the socket  	
+*/
+TInt CAvctpSap::GetData(RMBufChain& aData, TUint /*aLength*/, TUint /*aOptions*/, TSockAddr* aAddr)
+	{
+	LOG_FUNC
+
+	// We've been asked to close so esock shouldn't be asking us for more data
+	__ASSERT_ALWAYS(!iIsClosing, Panic(EUnexpectedEsockEvent));
+	__ASSERT_DEBUG(!iClosed, Panic(EAvctpSapClosed));
+	
+	HAvctpIncomingSdu* sdu = iIncomingSdus.First();
+	__ASSERT_ALWAYS(sdu, Panic(ENullPacket));
+	
+	aData.Assign(const_cast<RMBufChain&>(sdu->Data()));
+	
+	if (aAddr)
+		{
+		TAvctpSockAddr avctpAddr = TAvctpSockAddr::Cast(*aAddr);
+		avctpAddr.SetPid(iPid);
+		avctpAddr.SetBTAddr(sdu->BTAddr());
+		avctpAddr.SetChannel(iChannel);
+		(*aAddr) = avctpAddr;   // Convert back
+		}	
+	delete sdu; // deques pkt in ~HAvctpIncomingSdu				
+	
+	return KErrNone;	
+	}
+
+/**
+Perform an Ioctl.
+
+  @internalAll
+  @param aLevel The Ioctl level
+  @param aName The Ioctl name
+  @param aOption The Ioctl data
+*/
+void CAvctpSap::Ioctl(TUint /*aLevel*/, TUint /*aName*/, TDes8* /*aOption*/)
+	{
+	LOG_FUNC
+	Socket().Error(KErrNotSupported, MSocketNotify::EErrorIoctl);
+	__ASSERT_DEBUG(NULL, Panic(EIoctlsNotSupported));
+	}
+
+/**
+Cancel an Ioctl.
+
+Just pass it to the state machine.
+
+  @internalAll
+  @param aLevel The Ioctl level
+  @param aName The Ioctl name
+*/
+void CAvctpSap::CancelIoctl(TUint /*aLevel*/, TUint /*aName*/)
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(NULL, Panic(EIoctlsNotSupported));
+	}
+	
+TInt CAvctpSap::SecurityCheck(MProvdSecurityChecker* aSecurityChecker)
+	{
+	LOG_FUNC
+
+	// Store for later use when asked to bind to a particular PID.
+	iSecurityChecker = aSecurityChecker;
+	return KErrNone;
+	}
+
+/************************************************************************
+
+  Events interface (between muxer and Sap via Protocol)
+
+ ************************************************************************/
+																			
+/**
+There is new data from the muxer for us.
+We take ownership if the packet is suitable for us otherwise ignore
+
+  @internalComponent
+  @param aData The new data from the muxer
+  @param aMux The mux this data came from
+  @return ETrue if we took ownership of the data, or EFalse otherwise
+*/
+
+TBool CAvctpSap::NewData(HAvctpIncomingSdu* aSdu, TInt aChannel)
+	{
+	LOG_FUNC
+	LOG1(_L("New Sdu 0x%08x"), aSdu);	
+	
+	__ASSERT_DEBUG(!iClosed, Panic(EAvctpSapClosed));
+	
+	TBool acceptsPdu = EFalse;
+
+	if (SymbianAvctp::Pid(CAvctpPacket::GetHeader(aSdu->Data())) == Pid()
+					&& aChannel == iChannel)
+				{
+				if (!iIsClosing)
+					{
+					iIncomingSdus.AddLast(*aSdu);
+					Socket().NewData(1);
+					acceptsPdu = ETrue;
+					}
+				}
+
+	return acceptsPdu;
+	}
+	
+
+
+
+TBool CAvctpSap::HasDataFor(const TBTDevAddr& aRemoteAddr)
+	{
+	LOG_FUNC
+
+	TBool ans = EFalse;
+	
+	TDblQueIter<HAvctpOutgoingSdu> iter = iOutgoingSdus.Iter();
+	HAvctpOutgoingSdu* sdu;
+
+	while (iter)
+		{
+		sdu = iter++;
+		if (sdu->BTAddr() == aRemoteAddr)
+			{
+			ans = ETrue;
+			break;
+			}
+		}
+
+	LOG1(_L("result %d"), ans);
+	return ans;
+	}
+
+/**
+This function is called when a muxer goes down and is used to get rid of
+any packets that are now stale. If a packet remains in the system after 
+the muxer to it's remote device goes down, it'll cause a reconnection
+attempt which we don't want.
+
+We also need to delete any partial incoming packets from this muxer since
+they obviously won't be competed.
+*/
+void CAvctpSap::MuxerDown(const TBTDevAddr& aRemoteAddr)
+	{
+	LOG_FUNC
+
+	HAvctpOutgoingSdu* sdu;
+
+	TDblQueIter<HAvctpOutgoingSdu> iter = iOutgoingSdus.Iter();
+	while (iter)
+		{
+		sdu = iter++;
+		if (sdu->BTAddr() == aRemoteAddr)
+			{
+			delete sdu;
+			}
+		}
+		
+	if (!iIsClosing && iSendBlocked && iPendingSendAddress == aRemoteAddr)
+		{
+		Socket().Error(KErrDisconnected, MSocketNotify::EErrorSend);
+		}
+		
+	CheckForCanSend();
+	CheckForCanClose();
+	}
+
+/****************************************************************************/
+/* Implementation of private methods */
+/****************************************************************************/
+
+/**
+This function checks the Secure ID of the client process that is trying to 
+use an RAvctp object on aPid. Currently all PIDs are checked against the 
+default SID provided in avctpconstants.h
+
+If a licensee wanted to change behaviour, they would have to branch the stack
+to do so. This is obviously undesirable though they should question why they
+aren't using the RemCon server in the first place.
+
+This function should only be called once when the Pid of the Sap is first set.
+
+TODO: provide a resource / SPD file matching PIDs <-> SIDs, using KRemConSrvUid as the default SID
+*/
+TInt CAvctpSap::CheckPidAllowed(TPid /*aPid*/)
+	{
+	LOG_FUNC
+	// iSecurityChecker should've been provided in SecurityCheck by now
+	ASSERT_DEBUG(iSecurityChecker);
+	TBool ret = iSecurityChecker->CheckPolicy(KAvctpSapSecurityPolicy, KAvctpSapSecurityPolicyDiagnostic);
+	iSecurityChecker = NULL; // we're finished with it and it's not owned by us
+	return ret;
+	}
+
+void CAvctpSap::CheckForCanSend()
+	{
+	LOG_FUNC
+
+	if (!iIsClosing && iOutgoingSdus.Count() < KSapOutboundQLowMark)
+		{
+		Socket().CanSend(); // Okay to call at any time from Esock's point of view
+		iSendBlocked = EFalse;
+		}	
+	}
+
+void CAvctpSap::CheckForCanClose()
+	{
+	LOG_FUNC
+
+	if ((iIsClosing && iOutgoingSdus.Count() == 0) && !iClosed)
+		{
+		iClosed = ETrue;	// Ensure CanClose() is only called once, otherwise it hits an assert
+		iProtocol.RemoveSap(*this);
+		Socket().CanClose();
+		}
+	}
+		
+/**
+Asynchronous callback function to deal with actually sending stuff to 
+the muxers for delivery.
+
+Attempts to send all packets that it can from the iOutgoingSdus Q
+
+  @internalComponent
+  @param aSap The SAP being called back
+  @return EFalse - i.e. the callback should not be called again
+*/
+/*static*/ TInt CAvctpSap::SendAsyncCallBack(TAny* aSap)
+	{
+	LOG_STATIC_FUNC
+	LOG1(_L("for Sap: 0x%08x"), aSap);
+	
+	CAvctpSap& sap = *static_cast<CAvctpSap*>(aSap);
+	
+	TDblQueIter<HAvctpOutgoingSdu> iter = sap.iOutgoingSdus.Iter();
+	HAvctpOutgoingSdu* sdu = NULL;
+	MAvctpSDUSender* sduSender = NULL;
+	
+	while (iter)
+		{
+		sdu = iter++;
+		const TBTDevAddr& addr = sdu->BTAddr();
+		// Get a SDU Sender (tied to transport) if we don't have one yet
+		if (!sduSender)
+			{
+			sduSender = sap.iProtocol.GetSDUSender(addr, sap.iChannel);
+			}
+		
+		if (!sduSender)	// means the muxer has not been created 
+			{
+			delete sdu;	// I cannot send it due to a muxer creation failure
+			continue;
+			}
+		
+		// Try to send the sdu
+		if (sduSender->IsClearToSend(sap.iChannel))
+			{
+			// Note if the write succeeds the ownership of the sdu is transferred and the sdu will
+			// be deleted which will deque the sdu from the sap's outboundQ. If the write fails
+			// the sap retains ownership and the packet will remain on the outboundQ.
+			// Hence we don't need to check the return value
+			
+			(void)sduSender->Write(sdu, sap.iChannel);
+			}
+		else // may as well skip all sdus that are for the same transport
+			{
+			while (iter)
+				{
+				sdu = iter++;
+				if (sdu->BTAddr() != addr)
+					{
+					iter--; // don't want to skip this sdu when we go round the outer loop again
+					sduSender = NULL; // we're finished with this muxer
+					break; // inner while
+					}
+				}
+			}
+		}
+
+	sap.CheckForCanSend();
+	sap.CheckForCanClose();
+
+	return EFalse;
+	}
+
+/**
+Set the PID for this SAP & add us to the Protocol's Q
+
+  @internalComponent
+  @param aPid The SAP's PID
+  @return KErrInUse if there is already a Sap on aPid, otherwise KErrNone
+*/
+TInt CAvctpSap::SetPid(TPid aPid)
+	{
+	LOG_FUNC
+	LOG1(_L("to PID: 0x%x"), aPid);
+
+	__ASSERT_DEBUG(iPid == 0 || iPid == aPid, Panic(ESapAlreadyBound));
+	
+	TInt err = KErrNone;
+	if (iPid == 0 || iPid == aPid)
+		{
+		err = CheckPidAllowed(aPid);
+		if (err == KErrNone)
+			{
+			iPid = aPid;
+			}
+		}
+	else
+		{
+		err = KErrSapAlreadyBound;
+		}
+
+	return err;
+	}	
+
+
+// Pure virtuals From MSocketNotify
+// It's not used in favour of the public overloaded NewData()
+void CAvctpSap::NewData(TUint /*aCount*/)
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(NULL, Panic(ENewDataNotSupported));
+	}
+
+/**
+Notification from a muxer that we can send again.
+*/
+void CAvctpSap::CanSend()
+	{
+	LOG_FUNC
+	StartSendAsyncCallBack();
+	}
+
+// Not implemented because CAvctpSap is a connectionless sap
+void CAvctpSap::ConnectComplete()
+	{
+	LOG_FUNC
+	}
+
+// Not implemented because CAvctpSap is a connectionless sap
+void CAvctpSap::ConnectComplete(const TDesC8& /*aConnectData*/)
+	{
+	LOG_FUNC
+	}
+
+// Not implemented because CAvctpSap is a connectionless sap
+void CAvctpSap::ConnectComplete(CServProviderBase& /*aSSP*/)
+	{
+	LOG_FUNC
+	}
+	
+// Not implemented because CAvctpSap is a connectionless sap
+void CAvctpSap::ConnectComplete(CServProviderBase& /*aSSP*/,const TDesC8& /*aConnectData*/)
+	{
+	LOG_FUNC
+	}
+	
+// Not implemented because CAvctpSap is a connectionless sap
+void CAvctpSap::CanClose(TDelete /*aDelete*/)
+	{
+	LOG_FUNC
+	}
+
+// Not implemented because CAvctpSap is a connectionless sap
+void CAvctpSap::CanClose(const TDesC8& /*aDisconnectData*/,TDelete /*aDelete*/)
+	{
+	LOG_FUNC
+	}
+	
+void CAvctpSap::Error(TInt aError, TUint aOperationMask)
+	{
+	iSocket->Error(aError, aOperationMask);
+	}
+
+// Not implemented because CAvctpSap is a connectionless sap	
+void CAvctpSap::Disconnect()
+	{
+	LOG_FUNC
+	}
+
+// Not implemented because CAvctpSap is a connectionless sap
+void CAvctpSap::Disconnect(TDesC8& /*aDisconnectData*/)
+	{
+	LOG_FUNC
+	}
+
+// Not implemented because CAvctpSap is a connectionless sap
+void CAvctpSap::IoctlComplete(TDesC8* /*aBuf*/)
+	{
+	LOG_FUNC
+	// if we forward ioctls (NO!) then we'd expect this
+	}
+
+void CAvctpSap::NoBearer(const TDesC8& /*aConnectionInfo*/) {}
+void CAvctpSap::Bearer(const TDesC8& /*aConnectionInfo*/) {}
+
+CAvctpControlSAP* CAvctpControlSAP::NewL(CAvctpProtocol& aProtocol)
+	{
+	LOG_STATIC_FUNC
+	return new (ELeave) CAvctpControlSAP(aProtocol);
+	}
+
+CAvctpControlSAP::CAvctpControlSAP(CAvctpProtocol& aProtocol)
+:CBluetoothSAP(aProtocol.SecMan(), aProtocol.CodMan())
+,iProtocol(aProtocol),
+iIndicationQueue(_FOFF(HQueuedIndication, iLink))
+	{
+	LOG_FUNC
+	}
+
+/**
+ This method is called by CAvctpControlSAP::Ioctl() when the aName is an active request
+ (i.e. an attach request, a detach request, for both channels). In this case we want to set
+ an oustanding ioctl to be completed later. And it has priority on other incoming events, so we
+ want it to be served first. We can have only one oustanding ioctl. This method creates it and put 
+ it at first in the queue.
+ **/
+void CAvctpControlSAP::SetOutstandingIndication(const TControlIoctlMessage& aMessage)
+	{
+	LOG_FUNC
+	HQueuedIndication* ind = HQueuedIndication::New(aMessage);
+	if (ind)
+		{
+		iIndicationQueue.AddFirst(*ind);
+		}
+	else
+		{
+		Error(KErrNoMemory);
+		}
+	}
+
+void CAvctpControlSAP::CompleteIoctlWithError(const TControlIoctlMessage& aMessage)
+	{
+	LOG_FUNC
+	TPckgC<TControlIoctlMessage> pck(aMessage);
+	iSocket->IoctlComplete(&pck);
+	ClearIoctl();
+	}
+
+/**
+ In some cases the Ioctl (i.e. an AttachAgreement) does not wait for a reply. So, 
+ AvctpRemoteDevices, after sending an AgreeAttachment, cancels the ioctl and submit a new Listen one.
+ Hence, we need to queued the error completion, and when the listening ioctl comes we complete it with
+ the queued error. It is queued with priority as we want it to be the first completed.
+ */
+void CAvctpControlSAP::QueueErrorIoctlComplete(const TControlIoctlMessage& aMessage)
+	{
+	SetOutstandingIndication(aMessage);
+	}
+
+/**
+ Dequeues an iocl and if it is an oustanding iocl (and not an incoming indication) then it 
+ completes it with the error code passed on.
+ In some cases the outstanding ioctl will be already completed and hence not queued anymore.
+ for example, if we are trying to attach to ourself (so the address passed in the attach request
+ is the local device's address) the transport immediately notify an error up that completes the
+ outstanding ioctl. Then the transport is destroyed, and so iProtocol.ProvideTransport returns an
+ error. So this method is called but it won't find the outstanding ioctl because already completed
+ but NotifyError(). In this case we do nothing as the client has been already notified.
+ */
+void CAvctpControlSAP::CompleteQueuedIoctlWithError(TInt aError)
+	{
+	LOG_FUNC
+	if (!iIndicationQueue.IsEmpty())
+		{
+		HQueuedIndication* ind = iIndicationQueue.First();
+		SymbianAvctp::TControlIoctls ioctl = ind->Indication().iIoctl;
+		
+		if (ioctl == EPrimaryChannelAttachToTransport || 
+			ioctl == ESecondaryChannelAttachToTransport ||
+			ioctl == EPrimaryChannelDetachFromTransport ||
+			ioctl == ESecondaryChannelDetachFromTransport)
+			{
+			iIndicationQueue.Remove(*ind);
+			ind->Indication().iError = aError;
+			CompleteIoctlWithError(ind->Indication());
+			delete ind;
+			ind = NULL;
+			}
+			
+		}
+	}
+
+/**
+ This method process the queue and it complete the ioctl.
+ In some cases it changes the indication type (i.e. from ELinkUp to EAttachIndicate).
+ If the ioctl type is EError it doesn't do anything because we want the error to be propagated to 
+ the client.
+ */
+void CAvctpControlSAP::ProcessIoctlQueue()
+	{
+	LOG_FUNC
+	if (!iIndicationQueue.IsEmpty())
+		{
+		HQueuedIndication* ind = iIndicationQueue.First();
+		iIndicationQueue.Remove(*ind);
+		
+		switch(ind->Indication().iIoctl)
+			{
+			case ELinkUp:
+				ind->Indication().iIoctl = EAttachIndicate;
+				break;
+			case ELinkDown:
+				ind->Indication().iIoctl = EDetachIndicate;
+				break;
+			default:
+				break;	// propagate it
+			}
+		
+		
+		TPckgC<TControlIoctlMessage> pck(ind->Indication());
+		iSocket->IoctlComplete(&pck);
+		delete ind;
+		ClearIoctl();
+		}
+	}
+
+TBool CAvctpControlSAP::IsActiveRequest(TUint aName)
+	{
+	LOG_FUNC
+	return (aName == EPrimaryChannelAttachToTransport || 
+			aName == ESecondaryChannelAttachToTransport ||
+			aName == EPrimaryChannelDetachFromTransport || 
+			aName == ESecondaryChannelDetachFromTransport);
+	}
+
+void CAvctpControlSAP::Ioctl(TUint aLevel,TUint aName, TDes8* aOption)
+	{
+	LOG_FUNC
+	
+	TInt err = KErrNone;
+
+	TControlIoctlMessage msg;
+	TPckg<TControlIoctlMessage> pck(msg);
+	pck.Copy(*aOption);
+	
+	TBTDevAddr addr = pck().iAddr.BTAddr();
+	TPid pid = pck().iAddr.Pid();
+	TInt channel = pck().iAddr.Channel();
+	
+	if (aLevel == KSolBtAVCTP)
+		{
+		iIoctlLevel = aLevel;
+		iIoctlName = aName;
+		
+		if (IsActiveRequest(aName))
+			{
+			SetOutstandingIndication(pck());
+			}
+		
+		switch (aName)
+			{
+			case EPrimaryChannelAttachToTransport:
+				{
+				__ASSERT_DEBUG(channel == KAvctpPrimaryChannel, Panic(EAvctpInvalidChannelIoctl));
+				// we should complete the ioctl with an error if this function returns an error
+				if ((err = iProtocol.ProvideTransport(addr, pid, *this)) != KErrNone)
+					{
+					CompleteQueuedIoctlWithError(err);
+					}
+				}
+			break;
+			case EAwaitProvidedTransport:
+				{
+				__ASSERT_DEBUG(channel == KAvctpPrimaryChannel, Panic(EAvctpInvalidChannelIoctl));
+				iProtocol.AwaitTransport(pid, *this);
+				}
+			break;
+			case EPrimaryChannelAgreeAttachment:
+				{
+				__ASSERT_DEBUG(channel == KAvctpPrimaryChannel, Panic(EAvctpInvalidChannelIoctl));
+				LOG(_L("EPrimaryChannelAgreeAttachment, then reset aName"))
+				if ((err = iProtocol.PrimaryChannelAgreeAttachment(pid, addr)) == KErrNone)
+					{
+					ClearIoctl();				// this ioctl is not expecting an answer
+					iIoctlName = EUndefinedIoctl;	// just used to not execute the iIoctlName update
+					}
+				else
+					{
+					pck().iError = err;
+					QueueErrorIoctlComplete(pck());
+					}
+				}
+			break;
+			case ESecondaryChannelAttachToTransport:
+				{
+				__ASSERT_DEBUG(channel == KAvctpSecondaryChannel, Panic(EAvctpInvalidChannelIoctl));
+				// note this SAP is only managing the transport: the demuxing of control and second AVCTP data is handled
+				// by having different data SAPs for each traffic flow
+				// rare that we'd actively connect the second channel, but using at least for test purposes
+				
+				// should return the error through the confirm completion than via Error()?
+				if ((err = iProtocol.ActiveExtendTransport(addr, pid, *this)) != KErrNone)
+					{
+					CompleteQueuedIoctlWithError(err);
+					}
+				}
+			break;
+			case ESecondaryChannelAgreeAttachment:
+				{
+				__ASSERT_DEBUG(channel == KAvctpSecondaryChannel, Panic(EAvctpInvalidChannelIoctl));
+				if ((err = iProtocol.SecondaryChannelAgreeAttachment(pid, addr)) == KErrNone)
+					{
+					ClearIoctl();				// this ioctl is not expecting an answer
+					iIoctlName = EUndefinedIoctl;	// just used to not execute the iIoctlName update
+					}
+				else
+					{
+					pck().iError = err;
+					QueueErrorIoctlComplete(pck());
+					}
+				}
+			break;
+			case EAwaitExtendedTransport:
+				{
+				__ASSERT_DEBUG(channel == KAvctpSecondaryChannel, Panic(EAvctpInvalidChannelIoctl));
+				iProtocol.AwaitForExtendedTransport(pid, *this);
+				}
+			break;
+			case EPrimaryChannelRefuseAttach:
+				{
+				__ASSERT_DEBUG(channel == KAvctpPrimaryChannel, Panic(EAvctpInvalidChannelIoctl));
+				iProtocol.PrimaryChannelRefuseAttach(addr, pid);
+				}
+			break;
+			case ESecondaryChannelRefuseAttach:
+				{
+				__ASSERT_DEBUG(channel == KAvctpSecondaryChannel, Panic(EAvctpInvalidChannelIoctl));
+				iProtocol.SecondaryChannelRefuseAttach(addr, pid);
+				}
+			break;
+			case EPrimaryChannelDetachFromTransport:
+				{
+				__ASSERT_DEBUG(channel == KAvctpPrimaryChannel, Panic(EAvctpInvalidChannelIoctl));
+				if ((err = iProtocol.ReleaseTransport(addr, pid)) != KErrNone)
+					{
+					CompleteQueuedIoctlWithError(err);		
+					} 
+				}
+			break;
+			case ESecondaryChannelDetachFromTransport:
+				{
+				__ASSERT_DEBUG(channel == KAvctpSecondaryChannel, Panic(EAvctpInvalidChannelIoctl));
+				if ((err = iProtocol.ReleaseExtendedTransport(addr, pid)) != KErrNone)
+					{
+					CompleteQueuedIoctlWithError(err);		
+					} 
+				}
+			break;
+			default:
+				{
+				iSocket->Error(KErrNotSupported, MSocketNotify::EErrorIoctl);		
+				}
+			break;
+			}
+		
+		if (err == KErrNone && iIoctlName != EUndefinedIoctl)
+			{
+			if (!IsActiveRequest(aName))
+				{
+				ProcessIoctlQueue();
+				}
+			}
+		else
+			{
+			LOG(_L("No outstanding ioctl"));
+			}
+		}
+	}
+
+void CAvctpControlSAP::CancelIoctl(TUint/* aLevel*/,TUint /*aName*/)
+	{
+	LOG_FUNC
+	// do nothing for now, though should in future inform protocol that a transport is not needed
+	HQueuedIndication* ind = iIndicationQueue.First();
+	if (ind && IsActiveRequest(ind->Indication().iIoctl))
+		{
+		iIndicationQueue.Remove(*ind);
+		delete ind;
+		ind = NULL;
+		}
+	ClearIoctl();
+	}
+
+TInt CAvctpControlSAP::SetRemName(TSockAddr& /*aAddr*/)
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(EFalse, Panic(EAvctpControlSAPOpetationNotSupported));
+	return KErrNone;
+	}
+
+// CAvctpControlSAP inherited from CBluetoothSAP to take advantage of the socket paradigm, 
+// but it is used just as control channel between the client side and the server side.
+// so, many socket functions are not implemented
+void CAvctpControlSAP::ActiveOpen()
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(EFalse, Panic(EAvctpControlSAPOpetationNotSupported));
+	}
+
+// CAvctpControlSAP inherited from CBluetoothSAP to take advantage of the socket paradigm, 
+// but it is used just as control channel between the client side and the server side.
+// so, many socket functions are not implemented
+TInt CAvctpControlSAP::PassiveOpen(TUint /*aQueueSize*/)
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(EFalse, Panic(EAvctpControlSAPOpetationNotSupported));
+	return KErrNotSupported;
+	}
+
+// CAvctpControlSAP inherited from CBluetoothSAP to take advantage of the socket paradigm, 
+// but it is used just as control channel between the client side and the server side.
+// so, many socket functions are not implemented
+void CAvctpControlSAP::ActiveOpen(const TDesC8& /*aConnectionData*/)
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(EFalse, Panic(EAvctpControlSAPOpetationNotSupported));
+	}
+
+// CAvctpControlSAP inherited from CBluetoothSAP to take advantage of the socket paradigm, 
+// but it is used just as control channel between the client side and the server side.
+// so, many socket functions are not implemented
+TInt CAvctpControlSAP::PassiveOpen(TUint /*aQueueSize*/,const TDesC8& /*aConnectionData*/)
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(EFalse, Panic(EAvctpControlSAPOpetationNotSupported));
+	return KErrNotSupported;
+	}
+
+
+// CAvctpControlSAP inherited from CBluetoothSAP to take advantage of the socket paradigm, 
+// but it is used just as control channel between the client side and the server side.
+// so, many socket functions are not implemented or left empty cause called by the framework
+void CAvctpControlSAP::Start()
+	{
+	LOG_FUNC
+	}
+
+// CAvctpControlSAP inherited from CBluetoothSAP to take advantage of the socket paradigm, 
+// but it is used just as control channel between the client side and the server side.
+// so, many socket functions are not implemented
+void CAvctpControlSAP::LocalName(TSockAddr& /*aAddr*/) const
+	{
+	LOG_FUNC
+	}
+
+// CAvctpControlSAP inherited from CBluetoothSAP to take advantage of the socket paradigm, 
+// but it is used just as control channel between the client side and the server side.
+// so, many socket functions are not implemented
+TInt CAvctpControlSAP::SetLocalName(TSockAddr& /*aAddr*/)
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(EFalse, Panic(EAvctpControlSAPOpetationNotSupported));
+	return KErrNotSupported;
+	}
+
+// CAvctpControlSAP inherited from CBluetoothSAP to take advantage of the socket paradigm, 
+// but it is used just as control channel between the client side and the server side.
+// so, many socket functions are not implemented
+void CAvctpControlSAP::RemName(TSockAddr& /*aAddr*/) const
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(EFalse, Panic(EAvctpControlSAPOpetationNotSupported));
+	}
+
+TInt CAvctpControlSAP::GetOption(TUint aLevel,TUint aName, TDes8& aOption) const
+	{
+	LOG_FUNC
+	TInt err = KErrNotSupported;
+	
+	TOptionMessage msg;
+	TPckg<TOptionMessage> pck(msg);
+	pck.Copy(aOption);
+	
+	TInt mtu;
+	if (aLevel == KSolBtAVCTP)
+		{
+		switch(aName)
+			{
+			case KAvctpBaseOutboundMTU:
+				{
+				err = iProtocol.GetChannelMtu(KAvctpPrimaryChannel, pck().iAddr, mtu);
+				break;
+				}
+			case KAvctpExtendOutboundMTU:
+				{
+				err = iProtocol.GetChannelMtu(KAvctpSecondaryChannel, pck().iAddr, mtu);
+				break;
+				}
+			default:
+				break;
+			};
+		}
+
+	if (err == KErrNone)
+		{
+		pck().iMtu = mtu;
+		}
+	aOption = pck;
+	return err;
+	}
+
+void CAvctpControlSAP::Shutdown(CServProviderBase::TCloseType aOption)
+	{
+	LOG_FUNC
+	
+	if (aOption != EImmediate)
+		{
+		
+		iSocket->CanClose(); 
+		}
+	else
+		{
+		// If immediate don't need to call back
+		}
+	}
+
+// CAvctpControlSAP inherited from CBluetoothSAP to take advantage of the socket paradigm, 
+// but it is used just as control channel between the client side and the server side.
+// so, many socket functions are not implemented
+void CAvctpControlSAP::Shutdown(CServProviderBase::TCloseType /*aOption*/, const TDesC8& /*aDisconnectionData*/)
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(EFalse, Panic(EAvctpControlSAPOpetationNotSupported));
+	}
+
+// CAvctpControlSAP inherited from CBluetoothSAP to take advantage of the socket paradigm, 
+// but it is used just as control channel between the client side and the server side.
+// so, many socket functions are not implemented
+void CAvctpControlSAP::AutoBind()
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(EFalse, Panic(EAvctpControlSAPOpetationNotSupported));
+	}
+
+// CAvctpControlSAP inherited from CBluetoothSAP to take advantage of the socket paradigm, 
+// but it is used just as control channel between the client side and the server side.
+// so, many socket functions are not implemented
+TUint CAvctpControlSAP::Write(const TDesC8& /*aDesc*/,TUint /*aOptions*/, TSockAddr* /*aAddr*/)
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(EFalse, Panic(EAvctpControlSAPOpetationNotSupported));
+	return 0;
+	}
+
+// CAvctpControlSAP inherited from CBluetoothSAP to take advantage of the socket paradigm, 
+// but it is used just as control channel between the client side and the server side.
+// so, many socket functions are not implemented
+TInt CAvctpControlSAP::GetData(RMBufChain& /*aData*/, TUint /*aLength*/, TUint /*aOptions*/, TSockAddr* /*aAddr*/)
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(EFalse, Panic(EAvctpControlSAPOpetationNotSupported));
+	return KErrNotSupported;
+	}
+
+// CAvctpControlSAP inherited from CBluetoothSAP to take advantage of the socket paradigm, 
+// but it is used just as control channel between the client side and the server side.
+// so, many socket functions are not implemented
+TInt CAvctpControlSAP::SecurityCheck(MProvdSecurityChecker* /*aSecurityChecker*/)
+	{
+	LOG_FUNC
+	return KErrNone;
+	}
+
+// CAvctpControlSAP inherited from CBluetoothSAP to take advantage of the socket paradigm, 
+// but it is used just as control channel between the client side and the server side.
+// so, many socket functions are not implemented
+TInt CAvctpControlSAP::SAPSetOption(TUint /*aLevel*/,TUint /*aName*/,const TDesC8& /*aOption*/)
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(EFalse, Panic(EAvctpControlSAPOpetationNotSupported));
+	return KErrNotSupported;
+	}
+
+// Pure virtuals From MSocketNotify
+
+// CAvctpControlSAP inherited from MSocketNotify to take advantage of the socket paradigm, 
+// but it is used just as control channel between the client side and the server side.
+// so, many socket functions are not implemented
+void CAvctpControlSAP::NewData(TUint /*aCount*/)
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(EFalse, Panic(EAvctpControlSAPOpetationNotSupported));
+	}
+
+// CAvctpControlSAP inherited from MSocketNotify to take advantage of the socket paradigm, 
+// but it is used just as control channel between the client side and the server side.
+// so, many socket functions are not implemented
+void CAvctpControlSAP::CanSend()
+	{
+	LOG_FUNC
+	// the AVCTP Transport should not have given us tghis signal
+	// only data saps for this
+	__ASSERT_DEBUG(EFalse, Panic(EAvctpControlSAPOpetationNotSupported));
+	}
+
+// CAvctpControlSAP inherited from MSocketNotify to take advantage of the socket paradigm, 
+// but it is used just as control channel between the client side and the server side.
+// so, many socket functions are not implemented
+void CAvctpControlSAP::ConnectComplete()
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(EFalse, Panic(EAvctpControlSAPOpetationNotSupported));
+	}
+
+// CAvctpControlSAP inherited from MSocketNotify to take advantage of the socket paradigm, 
+// but it is used just as control channel between the client side and the server side.
+// so, many socket functions are not implemented
+void CAvctpControlSAP::ConnectComplete(const TDesC8& /*aConnectData*/)
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(EFalse, Panic(EAvctpControlSAPOpetationNotSupported));
+	}
+
+// CAvctpControlSAP inherited from MSocketNotify to take advantage of the socket paradigm, 
+// but it is used just as control channel between the client side and the server side.
+// so, many socket functions are not implemented
+void CAvctpControlSAP::ConnectComplete(CServProviderBase& /*aSSP*/)
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(EFalse, Panic(EAvctpControlSAPOpetationNotSupported));
+	}
+	
+// CAvctpControlSAP inherited from MSocketNotify to take advantage of the socket paradigm, 
+// but it is used just as control channel between the client side and the server side.
+// so, many socket functions are not implemented
+void CAvctpControlSAP::ConnectComplete(CServProviderBase& /*aSSP*/,const TDesC8& /*aConnectData*/)
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(EFalse, Panic(EAvctpControlSAPOpetationNotSupported));
+	}
+	
+// CAvctpControlSAP inherited from MSocketNotify to take advantage of the socket paradigm, 
+// but it is used just as control channel between the client side and the server side.
+// so, many socket functions are not implemented
+void CAvctpControlSAP::CanClose(TDelete /*aDelete*/)
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(EFalse, Panic(EAvctpControlSAPOpetationNotSupported));
+	}
+	
+// CAvctpControlSAP inherited from MSocketNotify to take advantage of the socket paradigm, 
+// but it is used just as control channel between the client side and the server side.
+// so, many socket functions are not implemented
+void CAvctpControlSAP::CanClose(const TDesC8& /*aDisconnectData*/,TDelete /*aDelete*/)
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(EFalse, Panic(EAvctpControlSAPOpetationNotSupported));
+	}
+	
+void CAvctpControlSAP::Error(TInt aError, TUint aOperationMask)
+	{
+	LOG_FUNC
+	iSocket->Error(aError, aOperationMask);
+	}
+	
+// CAvctpControlSAP inherited from MSocketNotify to take advantage of the socket paradigm, 
+// but it is used just as control channel between the client side and the server side.
+// so, many socket functions are not implemented
+void CAvctpControlSAP::Disconnect()
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(EFalse, Panic(EAvctpControlSAPOpetationNotSupported));
+	}
+
+// CAvctpControlSAP inherited from MSocketNotify to take advantage of the socket paradigm, 
+// but it is used just as control channel between the client side and the server side.
+// so, many socket functions are not implemented
+void CAvctpControlSAP::Disconnect(TDesC8& /*aDisconnectData*/)
+	{	
+	LOG_FUNC
+	__ASSERT_DEBUG(EFalse, Panic(EAvctpControlSAPOpetationNotSupported));
+	}
+
+/*inline*/ void CAvctpControlSAP::ClearIoctl()
+	{
+	LOG_FUNC
+	iIoctlLevel = 0;
+	iIoctlName = 0;
+	}
+
+/**
+ Create an HQueuedIndication object with the aInd argument (incoming device address)
+ and put it at the end of the queue. It is used just for the incoming Ioctl complete 
+ **/
+void CAvctpControlSAP::QueueIncomingIndication(const TControlIoctlMessage& aMessage)
+	{
+	LOG_FUNC
+	HQueuedIndication* ind = HQueuedIndication::New(aMessage);
+	if (ind)
+		{
+		LOG1(_L("Adding Indication to queue: "), aMessage.iIoctl);
+		iIndicationQueue.AddLast(*ind);
+		}
+	else
+		{
+		// OOM'd - better tell client...they've lost an indication
+		Error(KErrNoMemory);
+		}
+	}
+
+/**
+ This method is called to notify back something to the client. both remote indications and 
+ confirmation for actions requested from the client
+ */
+void CAvctpControlSAP::IoctlComplete(TDesC8* aBuf)
+	{
+	LOG_FUNC
+	
+	TControlIoctlMessage msg;
+	TPckg<TControlIoctlMessage> pck(msg);
+	
+	pck.Copy(*aBuf);
+	
+	TBTDevAddr addr = pck().iAddr.BTAddr();
+	
+	// we can have the following cases:
+	// 1) We are not waiting for a completion
+	//	  Action: queue the indication to be served later
+	// 2) The queue is not empty and there is an outstanding ioctl 
+	//	  a) we had asked for a connection to a remote device
+	//		I)  the ioctl complete is from the device we had requested.
+	//		II) this ioctl complete is from another device
+	//	  b) we were listening on an incoming connection
+	// 3) we were listening on an incoming connection
+	if (!iIoctlName)	// case 1
+		{
+		LOG(_L("iIoctlName is empty"));
+		QueueIncomingIndication(msg);
+		}
+	else if (!iIndicationQueue.IsEmpty() )	// case 2
+		{
+		HQueuedIndication* ind = iIndicationQueue.First();
+		
+		__ASSERT_DEBUG(addr != TBTDevAddr(0), Panic(EAvctpInvalidAddress));
+		
+		if (ind->Indication().iAddr.BTAddr() == addr)	// case 2aI
+			{
+			iIndicationQueue.Remove(*ind);
+			switch(ind->Indication().iIoctl)
+				{
+				case EPrimaryChannelAttachToTransport:
+				case ESecondaryChannelAttachToTransport:
+					__ASSERT_DEBUG(pck().iIoctl == ELinkUp || pck().iIoctl == EAttachConfirm || pck().iIoctl == EError, Panic(EAvctpUnexpectedIoctlCompletion));
+					pck().iIoctl = EAttachConfirm;
+					break;
+				case EPrimaryChannelDetachFromTransport:
+				case ESecondaryChannelDetachFromTransport:
+					__ASSERT_DEBUG(pck().iIoctl == EDetachConfirm, Panic(EAvctpUnexpectedIoctlCompletion));
+					break;	// do nothing the event is the same that goes back
+				default:
+					__ASSERT_DEBUG(EFalse, Panic(EAvctpUnexpectedIoctlCompletion));
+					break;
+				}
+			iSocket->IoctlComplete(&pck);
+			delete ind;
+			}
+		else	// case 2aII
+			{
+			LOG(_L("iIoctlName complete is for another device"));
+			QueueIncomingIndication(msg);
+			}	
+		}
+	else	// case 3
+		{
+		// tell client straight away
+        if (iSocket)
+            {
+            TControlIoctls ioctlValue = pck().iIoctl;
+            
+            switch(pck().iIoctl)
+            	{
+            	case ELinkUp:
+            		pck().iIoctl = EAttachIndicate;	// notify an indication
+            		break;
+            	case ELinkDown:
+            		pck().iIoctl = EDetachIndicate;	// notify an indication
+            		break;
+            	default:
+            		break;	// pass it as it is
+            	}
+            iSocket->IoctlComplete(&pck);
+            }
+		}
+	ClearIoctl();
+	}
+
+HQueuedIndication* HQueuedIndication::New(const TControlIoctlMessage& aMessage)
+    {
+	LOG_STATIC_FUNC
+    HQueuedIndication* ind = new HQueuedIndication;
+    if (ind)
+    	{
+    	TRAPD(err, ind->ConstructL(aMessage));
+    	if (err)
+    	    {
+    	    delete ind;
+    	    ind=NULL;
+    	    }
+    	}
+    return ind;
+    }
+	
+void HQueuedIndication::ConstructL(const TControlIoctlMessage& aMessage)
+    {
+	LOG_FUNC
+	iMessage = new (ELeave) TControlIoctlMessage(aMessage);
+    }
+    
+HQueuedIndication::~HQueuedIndication()
+    {
+	LOG_FUNC
+	delete iMessage;
+    }
+
+// Not used for avctp
+void CAvctpControlSAP::NoBearer(const TDesC8& /*aConnectionInfo*/) {}
+// Not used for avctp
+void CAvctpControlSAP::Bearer(const TDesC8& /*aConnectionInfo*/) {}
+