diff -r 000000000000 -r 29b1cd4cb562 bluetooth/btstack/avctp/avctpmuxer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bluetooth/btstack/avctp/avctpmuxer.cpp Fri Jan 15 08:13:17 2010 +0200 @@ -0,0 +1,1017 @@ +// 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: +// AVCTP muxer +// +// + +/** + @file + @internalComponent +*/ + +#include +#include +#include + +#include "avctpmuxer.h" +#include "avctpcommon.h" +#include "avctpmuxerstates.h" +#include "Avctp.h" +#include "avctputils.h" +#include "avctppacket.h" +#include "avctpsap.h" +#include "avctpPacketMgr.h" + +#ifdef __FLOG_ACTIVE +_LIT8(KLogComponent, LOG_COMPONENT_AVCTP); +#endif + +#ifdef _DEBUG +PANICCATEGORY("avctpmux"); +#endif + +using namespace SymbianAvctp; + +#ifdef __FLOG_ACTIVE +#define LOG_CLIENTS LogClients(); +#else +#define LOG_CLIENTS +#endif + +/** +Factory function for CAvctpTransport - called when local device initiates +the connection. + +Note the CProtocolBase passed in so that the muxer can create its own L2CAP SAP + + @internalComponent + @leave KErrNoMemory if the muxer could not be allocated + @param aProtocol The AVCTP protocol object, used to make callbacks on the protocol + @return A new muxer in the right state +*/ +CAvctpTransport* CAvctpTransport::NewL(CAvctpProtocol& aProtocol) + { + LOG_STATIC_FUNC + + CAvctpTransport* transport = new(ELeave) CAvctpTransport(aProtocol); + CleanupStack::PushL(transport); + transport->ConstructL(); + CleanupStack::Pop(transport); + return transport; + } + +/** +AVCTP SubConnection Provider Constructor + +Set up the async. callback. + + @internalComponent + @param aProt The protocol object +*/ +CAvctpTransport::CAvctpTransport(CAvctpProtocol& aProt) : + iProtocol(aProt), + iSecondChannelSocket(*this), + iState(&(aProt.MuxerStateFactory().GetState(CAvctpMuxerStateFactory::EClosed))), + iAddress(TBTDevAddr(0)) + { + LOG_FUNC + + TCallBack cb(IdleTimerExpired, this); + iIdleTimerEntry.Set(cb); + }; + +/** +Second phase muxer construction. + + @internalComponent +*/ +void CAvctpTransport::ConstructL() + { + LOG_FUNC + + TCallBack cb(SecondChannelNewDataAsyncCallBack, this); + iNewDataAsyncCallBack = new (ELeave)CAsyncCallBack(cb, EActiveHighPriority); + iPacketMgr = CAvctpPacketMgr::NewL(*this, iProtocol); + + SetSendBlocked(KAvctpPrimaryChannel); + SetSendBlocked(KAvctpSecondaryChannel); + CheckForIdle(); + } + +/** +Muxer d'tor + +We only Shutdown the lower protocol sap in an immediate way because in normal +circumstances we will have decided to die after idling for a while which means +no-one had data to send via this muxer. Hence there should be little chance +of losing data that could otherwise have been left in the L2CAP sap. If we did a +graceful close we would then have had to wait for the CanClose response which is +an unnecessary overhead if we've already used a timeout to decide no-one wants +the link. + +If the muxer died gracefully, there will be no data in the system that needs +to be sent over this muxer, however if the muxer goes down suddenly, e.g. the +remote disconnects, a connection attempt fails or a Shutdown is called there +will be data left in the protocol as a whole that would attempt a reconnection +to the remote device of this muxer. Hence we tell the saps we've gone down to +let them decide whether or not their packets have become stale or not. + +@internalComponent +*/ +CAvctpTransport::~CAvctpTransport() + { + LOG_FUNC + + DequeIdleTimer(); + + iProtocol.SignalMuxerDownToSaps(DevAddr()); + iProtocol.RemoveTransport(*this); // it's okay to deque the link even if that's already been done + + // the first channel (index 0) is the most important, so better closing from the last one + + TInt index; + for(index = iChannelSAPs.Count()-1;index>=0;index--) + { + if (iChannelSAPs[index]) + { + iChannelSAPs[index]->Shutdown(CServProviderBase::EImmediate); + delete iChannelSAPs[index]; + } + } + + CancelSecondChannelNewDataAsyncCallBack(); + delete iNewDataAsyncCallBack; + + delete iPacketMgr; + iClientItems.Close(); + } + + +TInt CAvctpTransport::AddSecondChannel(CServProviderBase& aSAP) + { + LOG_FUNC + return iState->AddSecondChannel(*this, aSAP); + } + +TBool CAvctpTransport::HasSecondChannel() const + { + LOG_FUNC + return iChannelSAPs[KAvctpSecondaryChannel] != NULL ? ETrue : EFalse; + } + +void CAvctpTransport::RemoveSecondChannel() + { + LOG_FUNC + iState->RemoveSecondChannel(*this); + } + +TInt CAvctpTransport::GetChannelMtu(TInt aChannel, TInt& aMtu) const + { + LOG_FUNC + + TInt err = KErrNotReady; + if(iChannelSAPs[aChannel]) + { + TPckgBuf mtuBuf; + err = iChannelSAPs[aChannel]->GetOption(KSolBtL2CAP, KL2CAPNegotiatedOutboundMTU, mtuBuf); + if (err == KErrNone) + { + // MTU for AVCTP clients is L2CAP MTU - AVCTP Header + aMtu = mtuBuf() - SymbianAvctp::ENormalHeaderLength; + } + } + + return err; + } + + +/****************************************************************************/ +/* + Notifications from the MSocketNotify interface + I.e calls from iChannelSAPs[KAvctpPrimaryChannel] +*/ + +/** +Called when new data is available. + +This is called each time a new packet of data arrives from L2CAP. +We get to the data by calling GetData on the L2CAP SAP. + +This assumes a packet interface from L2CAP. + + @internalComponent + @param aCount Number of new packets waiting +*/ +void CAvctpTransport::NewData(TUint aCount) + { + LOG_FUNC + + LOG1(_L("%d packets available"), aCount); + + if (aCount == KNewDataEndofData) + { + Disconnect(); + } + else + { + // for latency reasons, we process channel 1 data synchronously + // Only need to do this once rather than for each NewData call + // because these calls are all synchronous and hence the MTU + // of L2CAP can't change in-between GetData calls (changing + // the MTU requires negotiation with a remote device + // which is naturally asynchronous) + TInt mtu = GetCurrentInboundMtu(KAvctpPrimaryChannel); + + if(mtu >= 0) + { + __DEBUG_ONLY(TInt err;) + while (aCount--) + { + if ((__DEBUG_ONLY(err =) iState->NewData(*this, mtu, 0)) != KErrNone) + { + __ASSERT_DEBUG(err == KErrMuxerShutDown, Panic(EUnexpectedErrorCode)); + break; + } + } + } + } + } + + +/** +Notification that we can now send data to the lower layer. + + @internalComponent +*/ +void CAvctpTransport::CanSend() + { + LOG_FUNC + iState->CanSend(*this, KAvctpPrimaryChannel); //channel is implicitly primary one because this upcall from the sap1 + } + +/** +Signal from the lower protocol SAP that a connection has occurred. + +Part of the MSocketNotify interface. This is called when L2CAP +has brought up the lower layer link to the remote device. + + @internalComponent +*/ +void CAvctpTransport::ConnectComplete() + { + LOG_FUNC + + IF_FLOGGING + ( + TBuf address; + iRemoteAddr.GetReadable(address); + ) + + LOG1(_L("from BT Device 0x%S"), &address); + + iState->ConnectComplete(*this); + } + +/** +Version with connection data. + +Ignore the data (since L2CAP should never provide this!) + + @internalComponent + @param aConnectData The data received on connection +*/ +void CAvctpTransport::ConnectComplete(const TDesC8& /*aConnectData*/) + { + LOG_FUNC + LOG(_L("ConnectComplete with aConnectData")); + ConnectComplete(); + } + +/** +Incoming connection completed on listening socket. + + @internalComponent + @param aSSP The new SAP for the completed connection +*/ +void CAvctpTransport::ConnectComplete(CServProviderBase& /*aSSP*/) + { + LOG_FUNC + LOG(_L("ConnectComplete with aSSP")); + ConnectComplete(); + } + +/** +Incoming connection completed on listening socket with connection data. + + @internalComponent + @param aSSP The new SAP for the completed connection +*/ +void CAvctpTransport::ConnectComplete(CServProviderBase& /*aSSP*/,const TDesC8& /*aConnectData*/) + { + LOG_FUNC + LOG(_L("ConnectComplete with aSSP and aConnectData")); + ConnectComplete(); + } + +/** +We must have asked the lower protocol socket to shutdown, which means we're dying. +However we only ask for an immediate shutdown so we shouldn't get a CanClose callback + + @internalComponent + @param aDelete How to shutdown +*/ +void CAvctpTransport::CanClose(TDelete /*aDelete*/) + { + LOG_FUNC + Panic(ELowerProtocolSapCanClose); + } + +/** +The lower socket can close, with disconnection data. + +We don't support this so panic + + @internalComponent + @param aDisconnectData Data from the disconnecting socket + @param aDelete How to shutdown +*/ +void CAvctpTransport::CanClose(const TDesC8& /*aDisconnectData*/, TDelete /*aDelete*/) + { + LOG_FUNC + Panic(ELowerProtocolSapCanClose); + } + +/** +Something has gone wrong. + +We get told whether it's just this operation or others that are +affected. We use this to decide what to do. + + @internalComponent + @param aError The error code + @param aOperationMask The operation(s) affected +*/ +void CAvctpTransport::Error(TInt aError, TUint aOperationMask) + { + LOG_FUNC + + LOG2(_L("Error %d, OperationMask 0b%b"), aError, aOperationMask); + iState->Error(*this, aError, aOperationMask, KAvctpPrimaryChannel); + } + +/** +The L2CAP link has disconnected. This is the result of a remote device disconnecting + + @internalComponent +*/ +void CAvctpTransport::Disconnect() + { + LOG_FUNC + iState->Disconnect(*this); + } + +/** +Disconnect with disconnection data. + +Ignore the data (since L2CAP should never provide this!) + + @internalComponent + @param aDisconnectData Data from the disconnecting socket +*/ +void CAvctpTransport::Disconnect(TDesC8& /*aDisconnectData*/) + { + LOG_FUNC + LOG(_L("Disconnect with aDisconnectData")); + Disconnect(); + } + +/** +IOCTL forwarding not support, and none initiated by AVCTP so NOP +*/ +void CAvctpTransport::IoctlComplete(TDesC8* /*aBuf*/) + { + LOG_FUNC + __PANIC_UNEXPECTED_CALL + } + +/** +This function allows the muxer to be shutdown irrespective of whether +it is idle. This is intended to be used to punish a remote device that +has done something naughty. + + @internalComponent +*/ +void CAvctpTransport::Shutdown(TInt aError) + { + LOG_FUNC + NotifyLinkDown(iAddress, KAvctpPrimaryChannel, aError); + iState->Shutdown(*this, aError); + } + +TBool CAvctpTransport::HasDataToSend() + { + LOG_FUNC + + TBool ret = PacketMgr().WouldLikeToSend(); + if (ret == EFalse) + { + ret = iProtocol.SapsHaveDataFor(DevAddr()); + } + return ret; + } + +/**********************************************************************/ +/* + Commands from the AVCTP SAP. +*/ + + +TInt CAvctpTransport::Start(const TBTDevAddr& aAddr, TUint16 aClientId) + { + LOG_FUNC + + IF_FLOGGING + ( + TBuf address; + aAddr.GetReadable(address); + ) + + LOG1(_L("from BT Device 0x%S"), &address); + + iAddress = aAddr; + + return iState->Start(*this, aAddr, aClientId); + } + +TInt CAvctpTransport::StartIncoming(const TBTDevAddr& aAddr, CServProviderBase* aL2CAPConSAP) + { + LOG_FUNC + LOG1(_L("CServProviderBase* aL2CAPConSAP 0x%08x"), aL2CAPConSAP); + + iAddress = aAddr; + + TInt err = iState->StartIncoming(*this, aAddr, aL2CAPConSAP); + return err; + } + + +/**********************************************************************/ +/* + Internal functions. +*/ + +/** +Set the BTDevAddr for this Muxer & add us to the Protocol's Q + + @internalComponent + @param aDevAddr The Muxer's new remote address + @return KErrInUse if there is already a Muxer on aRemoteAddr, otherwise KErrNone +*/ +void CAvctpTransport::AssignToDevice(const TBTDevAddr& aRemoteAddr) + { + LOG_FUNC + + // Note the below assumes that there is at most one data client (or they're all on the same PID) + __ASSERT_DEBUG(iRemoteAddr == TBTDevAddr(0) || iRemoteAddr == aRemoteAddr, Panic(EMuxerAlreadyBound)); + + if (iRemoteAddr == TBTDevAddr(0)) + { + iRemoteAddr = aRemoteAddr; + } + } + +/** + New data on the secondary channel is async because we want to be responsive while processing a + potentially big amount of data + */ +/*static*/ TInt CAvctpTransport::SecondChannelNewDataAsyncCallBack(TAny* aTransport) + { + LOG_STATIC_FUNC + + CAvctpTransport* t = static_cast(aTransport); + + // Only need to do this once rather than for each NewData call + // because these calls are all synchronous and hence the MTU + // of L2CAP can't change in-between GetData calls (changing + // the MTU requires negotiation with a remote device + // which is naturally asynchronous) + TInt mtu = t->GetCurrentInboundMtu(1); + __DEBUG_ONLY(TInt err;) + if(mtu >= 0) + { + while (t->iSecondChannelPacketsWaiting) + { + if ((__DEBUG_ONLY(err =) t->iState->NewData(*t, mtu, KAvctpSecondaryChannel)) != KErrNone) + { + __ASSERT_DEBUG(err == KErrMuxerShutDown, Panic(EUnexpectedErrorCode)); + break; + } + --(t->iSecondChannelPacketsWaiting); + } + } + // else we have an error getting the MTU, this is probably because the + // L2CAP channel is in the process of shutting down. We leave the data + // there where it'll be cleared up later. If not we'll pick this data + // up next time we get signalled NewData from the lower protocol. + + return EFalse; + } + +/** +Check to see if we're still needed. If not, Q a delayed delete. + + @internalComponent +*/ +void CAvctpTransport::CheckForIdle() + { + LOG_FUNC + + if ( IsIdle()) + { + QueIdleTimer(); + } + } + +/** +@return System wide error code if the value could not be retrieved, otherwise the current + inbound MTU. +*/ +TInt CAvctpTransport::GetCurrentInboundMtu(TInt aChannel) + { + LOG_FUNC + TPckgBuf buf; + + __ASSERT_DEBUG(iChannelSAPs[aChannel], Panic(ENullLowerProtocolSap)); + + TInt err = iChannelSAPs[aChannel]->GetOption(KSolBtL2CAP, KL2CAPInboundMTU, buf); + if(!err) + { + return buf(); + } + else + { + return err; + } + } + +/** +@return System wide error code if the value could not be retrieved, otherwise the current + outbound MTU. +*/ +TInt CAvctpTransport::GetCurrentOutboundMtu(TInt aChannel) + { + LOG_FUNC + TPckgBuf buf; + + __ASSERT_DEBUG(iChannelSAPs[aChannel], Panic(ENullLowerProtocolSap)); + TInt err = iChannelSAPs[aChannel]->GetOption(KSolBtL2CAP, KL2CAPOutboundMTUForBestPerformance, buf); + if(!err) + { + return buf(); + } + else + { + return err; + } + } + +TInt CAvctpTransport::DoWrite(RMBufChain& aPDU, TInt aChannel) + { + LOG_FUNC + + __ASSERT_DEBUG(iChannelSAPs[aChannel], Panic(ENullLowerProtocolSap)); + return iChannelSAPs[aChannel]->Write(aPDU,0); + } + +/** +Check to see if the Muxer is idle. +Note this subConProvider shouldn't have any direct data clients. +It's only important if there are control clients or there are saps that have data to send +through the muxer. So a data client could effectively keep us non idle by queueing data +for us. + + @internalComponent +*/ +TBool CAvctpTransport::IsIdle() + { + LOG_FUNC + return iState->IsIdle(*this); + } + +/** +Queues the idle timer if necessary + + @internalComponent +*/ +void CAvctpTransport::QueIdleTimer() + { + LOG_FUNC + + if (!iIdleTimerQueued) + { + LOG(_L("Queued idle timer")); + + iIdleTimerQueued = ETrue; + BTSocketTimer::Queue(KTransportIdleTimeout, iIdleTimerEntry); + } + } + +/** +Deques idle timer if necessary + + @internalComponent +*/ +void CAvctpTransport::DequeIdleTimer() + { + LOG_FUNC + + if (iIdleTimerQueued) + { + LOG(_L("Dequeued idle timer")); + + iIdleTimerQueued = EFalse; + BTSocketTimer::Remove(iIdleTimerEntry); + } + } + +/** +Static idle callback. + +This is entered if we remain idle (as defined by IsIdle()) for the duration of the timer. + @internalComponent + @param aMux The muxer that is idle + @return EFalse - do no reissue callback +*/ +TInt CAvctpTransport::IdleTimerExpired(TAny* aTransport) + { + LOG_STATIC_FUNC + LOG1(_L("on Transport 0x%08x"), aTransport); + + CAvctpTransport* transport = static_cast(aTransport); + + __ASSERT_DEBUG(transport, Panic(ENullMuxer)); + // If the following panics it means we've not cancelled our IdleTimer at some previous point + __ASSERT_DEBUG(transport->IsIdle(), Panic(EIdleTimeoutWhenNotIdle)); + + transport->iIdleTimerQueued = EFalse; // Obviously... + + transport->iState->IdleTimerExpired(*transport); + + return EFalse; + } + +void CAvctpTransport::NotifyLinkState(const TBTDevAddr& aAddr, TControlIoctls aIotcl, TInt aChannel, TInt aError) + { + LOG_FUNC + TControlIoctlMessage msg(aIotcl, aAddr, aError); + TPckgC pck(msg); + + TClientItemIter iter(iClientItems); + while(iter.NextKey()) + { + const TClientItem** pitem = iter.CurrentValue(); + MSocketNotify* socket = aChannel == KAvctpSecondaryChannel ? (*pitem)->SecondaryChannel() : (*pitem)->PrimaryChannel(); + if(socket) + { + socket->IoctlComplete(&pck); + } + } + } + +void CAvctpTransport::NotifyLinkUp(const TBTDevAddr& aAddr, TBool aIsSecondChannel) + { + LOG_FUNC + iProtocol.NotifyLinkUp(aAddr, aIsSecondChannel); + } + +void CAvctpTransport::NotifyLinkDown(const TBTDevAddr& aAddr, TInt aChannel, TInt aError) + { + LOG_FUNC + NotifyLinkState(aAddr, ELinkDown, aChannel, aError); + } + +void CAvctpTransport::NotifyAttachConfirm(TInt aError, TBool aIsSecondChannel) + { + LOG_FUNC + TControlIoctlMessage msg(SymbianAvctp::EAttachConfirm, iRemoteAddr, aError); + TPckgC pck(msg); + + TClientItemIter iter(iClientItems); + while(iter.NextKey()) + { + const TClientItem** pitem = iter.CurrentValue(); + MSocketNotify* socket = aIsSecondChannel ? (*pitem)->SecondaryChannel() : (*pitem)->PrimaryChannel(); + __ASSERT_DEBUG(socket, Panic(EAvctpInvalidChannelNotify)); + socket->IoctlComplete(&pck); + } + } + +void CAvctpTransport::NotifyAttachConfirm(TUint16 aClientId, TInt aError, TBool aIsSecondChannel) + { + LOG_FUNC + TPtrC8 addr(iRemoteAddr.Des()); + TControlIoctlMessage msg(SymbianAvctp::EAttachConfirm, iRemoteAddr, aError); + TPckgC pck(msg); + + const TClientItem** pitem = iClientItems.Find(aClientId); + if (pitem) + { + MSocketNotify* socket = aIsSecondChannel ? (*pitem)->SecondaryChannel() : (*pitem)->PrimaryChannel(); + if (aIsSecondChannel && !(*pitem)->IsSecondaryChannelAttached()) + return; + + __ASSERT_DEBUG(socket, Panic(EAvctpInvalidChannelNotify)); + if (socket) + { + socket->IoctlComplete(&pck); + } + } + } + +void CAvctpTransport::NotifyDetachConfirm(TUint16 aClientId, TInt aError, TBool aIsSecondChannel) + { + LOG_FUNC + TPtrC8 addr(iRemoteAddr.Des()); + TControlIoctlMessage msg(SymbianAvctp::EDetachConfirm, iRemoteAddr, aError); + TPckgC pck(msg); + + const TClientItem** pitem = iClientItems.Find(aClientId); + if (*pitem) + { + MSocketNotify* socket = aIsSecondChannel ? (*pitem)->SecondaryChannel() : (*pitem)->PrimaryChannel(); + __ASSERT_DEBUG(socket, Panic(EAvctpInvalidChannelNotify)); + socket->IoctlComplete(&pck); + } + } + +void CAvctpTransport::NotifyLinkError(TInt aError, TBool aIsSecondChannel) + { + LOG_FUNC + TPtrC8 addr(iRemoteAddr.Des()); + TControlIoctlMessage msg(SymbianAvctp::EError, iRemoteAddr, aError); + TPckgC pck(msg); + + TClientItemIter iter(iClientItems); + while(iter.NextKey()) + { + const TClientItem** pitem = iter.CurrentValue(); + MSocketNotify* notify = aIsSecondChannel ? (*pitem)->SecondaryChannel() : (*pitem)->PrimaryChannel(); + if (notify) + { + notify->IoctlComplete(&pck); + } + } + } + +void CAvctpTransport::MbpsnNewData(TUint aCount) + { + // new data from the second channel + + LOG_FUNC + + LOG1(_L("%d packets available"), aCount); + + if (aCount == KNewDataEndofData) + { + this->MbpsnDisconnect(); + } + else + { + iSecondChannelPacketsWaiting += aCount; + StartSecondChannelNewDataAsyncCallBack(); + } + + } + +void CAvctpTransport::MbpsnCanSend() + { + LOG_FUNC + iState->CanSend(*this, KAvctpSecondaryChannel); //channel is implicitly the second channel with this upcall + } + +void CAvctpTransport::MbpsnConnectComplete() + { + LOG_FUNC + + IF_FLOGGING + ( + TBuf address; + iRemoteAddr.GetReadable(address); + ) + + LOG1(_L("from BT Device 0x%S"), &address); + + // state machine should be able to resolve the difference in saps completing + iState->ConnectComplete(*this); + } + +void CAvctpTransport::MbpsnConnectComplete(CServProviderBase& aSAP) + { + LOG_FUNC + // it is an incoming connection and we don't know the pid. + // but that's fine because it will be ignored in the state machine. + iState->AddSecondChannel(*this, aSAP); + } + +//only initiate immediate shutdowns so there should be no canclose callback +void CAvctpTransport::MbpsnCanClose(MSocketNotify::TDelete /*aDelete*/) + { + LOG_FUNC + Panic(ELowerProtocolSapCanClose); + } + +void CAvctpTransport::MbpsnError(TInt aError, TUint aOperationMask) + { + LOG_FUNC + + LOG2(_L("Error %d, OperationMask 0b%b"), aError, aOperationMask); + + iState->Error(*this, aError, aOperationMask, KAvctpSecondaryChannel); + } + +void CAvctpTransport::MbpsnDisconnect() + { + LOG_FUNC + iState->SecondChannelRemoved(*this); + } + +void CAvctpTransport::SetSecondChannelCtrlNotify(TUint16 aClientId, MSocketNotify& aSecondChannelControlSocket) + { + LOG_FUNC + iProtocol.SetSecondChannelCtrlNotify(aClientId, aSecondChannelControlSocket); + } + + +TInt CAvctpTransport::AddPrimaryChannelRef(const TClientItem* item) + { + LOG_FUNC + DequeIdleTimer(); // adding a client reference means not to be idle anymore + TUint16 clientId = item->ClientId(); + return iClientItems.Insert(clientId, item); + } + +void CAvctpTransport::AddSecondaryChannelRef() + { + iSecondaryChannelClientRefCount++; + } + +void CAvctpTransport::RemovePrimaryChannelRef(TUint16 aClientId) + { + LOG_FUNC + + TClientItem** item = const_cast(iClientItems.Find(aClientId)); + if (item && *item) + { + iClientItems.Remove(aClientId); + } + if (iClientItems.Count() == 0 && IsIdle()) + { + iProtocol.RemoveTransport(*this); + delete this; + } + } + +/** + This method is called from ReleaseExtendedTransport() and RemoveSap(). + When called from RemoveSap (that is called when the sap is shutdown) the secondary channel + may or may not exist. If the muxer state is, for example, Open then the secondary channel + does not exist. In this case, HasSecondChannel() return false and the method exits. + If the muxer is in SecondChannelPending state HasSecondChannel returns true. we can have a + situation where + */ +void CAvctpTransport::RemoveSecondaryChannelRef(TUint16 aClientId) + { + LOG_FUNC + + if (HasSecondChannel()) + { + TClientItem** item = const_cast(iClientItems.Find(aClientId)); + __ASSERT_DEBUG(item, Panic(EAvctpClientNotFound)); + + if (iSecondaryChannelClientRefCount > 0) + { + iSecondaryChannelClientRefCount--; + } + + if (iSecondaryChannelClientRefCount == 0) + { + RemoveSecondChannel(); + } + } + } + +TInt CAvctpTransport::ClientCount() + { + return iClientItems.Count(); + } + +TInt CAvctpTransport::ClientSecondaryChannelCount() + { + TInt count = 0; + TClientItemIter iter(iClientItems); + while(iter.NextKey()) + { + const TClientItem** pitem = iter.CurrentValue(); + if ((*pitem)->IsSecondaryChannelAttached()) + { + count++; + } + } + return count; + } + +TBool CAvctpTransport::HasClient(TUint16 aClientId) + { + LOG_FUNC + return (iClientItems.Find(aClientId) != NULL) ? ETrue : EFalse; + } + +void CAvctpTransport::BindSecondaryChannelSap(CServProviderBase& aSAP) + { + iChannelSAPs[KAvctpSecondaryChannel] = &aSAP; + iChannelSAPs[KAvctpSecondaryChannel]->SetNotify(&iSecondChannelSocket); + } + +//pseudosocket stuff +TAvctpSecondChannelPseudoSocket::TAvctpSecondChannelPseudoSocket(MSecondChannelPseudoSocketNotify& aNotify) +:iNotify(aNotify) + { + LOG_FUNC + } + + +void TAvctpSecondChannelPseudoSocket::NewData(TUint aCount) + { + LOG_FUNC + iNotify.MbpsnNewData(aCount); + } + +void TAvctpSecondChannelPseudoSocket::CanSend() + { + LOG_FUNC + iNotify.MbpsnCanSend(); + } + +void TAvctpSecondChannelPseudoSocket::ConnectComplete() + { + LOG_FUNC + iNotify.MbpsnConnectComplete(); + } + +void TAvctpSecondChannelPseudoSocket::ConnectComplete(const TDesC8& /*aConnectData*/) + { + LOG_FUNC + /*drop*/ + } + +void TAvctpSecondChannelPseudoSocket::ConnectComplete(CServProviderBase& aSSP) + { + LOG_FUNC + iNotify.MbpsnConnectComplete(aSSP); + } + +void TAvctpSecondChannelPseudoSocket::ConnectComplete(CServProviderBase& /*aSSP*/,const TDesC8& /*aConnectData*/) + { + LOG_FUNC + /*drop*/ + } + +void TAvctpSecondChannelPseudoSocket::CanClose(TDelete aDelete) + { + LOG_FUNC + iNotify.MbpsnCanClose(aDelete); + } + +void TAvctpSecondChannelPseudoSocket::CanClose(const TDesC8& /*aDisconnectData*/,TDelete /*aDelete*/) + { + LOG_FUNC + /*drop*/ + } + +void TAvctpSecondChannelPseudoSocket::Error(TInt aError, TUint aOperationMask) + { + LOG_FUNC + iNotify.MbpsnError(aError, aOperationMask); + } + +void TAvctpSecondChannelPseudoSocket::Disconnect() + { + LOG_FUNC + iNotify.MbpsnDisconnect(); + } + +void TAvctpSecondChannelPseudoSocket::IoctlComplete(TDesC8* /*aBuf*/) + { + LOG_FUNC + }