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