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