bluetooth/btstack/avctp/avctpsap.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Fri, 15 Jan 2010 08:13:17 +0200
changeset 0 29b1cd4cb562
permissions -rw-r--r--
Revision: 200951_001

// 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*/) {}