diff -r 000000000000 -r 29b1cd4cb562 bluetooth/btstack/avctp/avctpmuxerstates.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bluetooth/btstack/avctp/avctpmuxerstates.cpp Fri Jan 15 08:13:17 2010 +0200 @@ -0,0 +1,1193 @@ +// 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: +// Implement the AVCTP Muxer state classes. +// These are CAvctpMuxerStateFactory, TAvctpMuxerState (abstract) and its derived +// classes +// Together, these classes and the Muxer implement the State pattern +// (GOF). The states themselves are implemented using the Flyweight +// pattern. Each state is a Flyweight object, and CAvctpMuxerStateFactory +// is manager of these objects. As a result of being a flyweight, no +// state object may have state that can't be shared between all +// possible users of the state (i.e. no per-Muxer state) +// +// + +/** + @file + @internalComponent +*/ +#include + +#include +#include + +#include "avctpmuxerstates.h" +#include "bt_subconn_levels.h" +#include "Avctp.h" +#include "avctpmuxer.h" +#include "avctputils.h" +#include "avctpconstants.h" +#include "avctpPacketMgr.h" + +#ifdef __FLOG_ACTIVE +_LIT8(KLogComponent, LOG_COMPONENT_AVCTP); +#endif + +using namespace SymbianAvctp; + +// +// // +// TAvctpMuxerState Implementation // +// // +// + +/** +Default constructor. + +@internalComponent +@param aFactory The Muxer state factory +*/ +TAvctpMuxerState::TAvctpMuxerState(CAvctpMuxerStateFactory& aFactory) + : iFactory(aFactory) + { + LOG_FUNC + } + +/** +Change a Muxer from one state to another. + +We Exit() the first state and then Enter() the second + +@internalComponent +@param aTransport The Muxer whose state is to be changed +@param aNewState The index for the new state the Muxer is to transition to +*/ +void TAvctpMuxerState::ChangeState(CAvctpTransport& aTransport, CAvctpMuxerStateFactory::TAvctpMuxerStates aNewState) const + { + LOG_FUNC + + aTransport.iState->Exit(aTransport); + +#ifdef __FLOG_ACTIVE + TAvctpMuxerState& state = iFactory.GetState(aNewState); +#endif + LOG3(_L("Muxer 0x%08x : State %S -> %S"), &aTransport, &(aTransport.iState->iName), &(state.iName)); + + aTransport.iState = &iFactory.GetState(aNewState); + aTransport.iState->Enter(aTransport); + } + +/** +Calls the appropriate panic function to encode the panic +code with the current state identifier. +@param aPanic The panic code that the state is panicking with. +*/ +void TAvctpMuxerState::PanicInState(SymbianAvctp::TPanic aPanic) const + { + Panic(aPanic, iFactory.StateIndex(this)); + } + +/** + Close the secondary l2cap channel with or without notifying clients. + @param aTransport the transport the state is related to + @param aNotifyClient if it's ETrue clients are notified. + */ +void TAvctpMuxerState::ShutDownSecondarySap(CAvctpTransport& aTransport, TBool aNotifyClient) const + { + LOG_FUNC + + CServProviderBase* sap = aTransport.iChannelSAPs[KAvctpSecondaryChannel]; + if (sap) + { + sap->Shutdown(CServProviderBase::EImmediate); + delete sap; + sap = NULL; + aTransport.iChannelSAPs[KAvctpSecondaryChannel] = NULL; + if (aNotifyClient) + { + aTransport.NotifyLinkDown(aTransport.iRemoteAddr, KAvctpSecondaryChannel); + } + ChangeState(aTransport, CAvctpMuxerStateFactory::EOpen); + } + } + +/** +Default state entry function. + +@internalComponent +@param aTransport The Muxer whose state is changing +*/ +void TAvctpMuxerState::Enter(CAvctpTransport& /*aTransport*/) const + { + LOG_FUNC + } + +/** +Default state exit function. + +@internalComponent +@param aTransport The Muxer whose state is changing +*/ +void TAvctpMuxerState::Exit(CAvctpTransport& /*aTransport*/) const + { + LOG_FUNC + } + + +/** +Default Muxer IsIdle + +The muxer is asking its state to determine whether it's idle. + +@internalComponent +@param aTransport The Muxer which is idle +*/ +TBool TAvctpMuxerState::IsIdle(CAvctpTransport& aTransport) const + { + LOG_FUNC + TBool hasData = aTransport.HasDataToSend(); + TBool pidCount = aTransport.ClientCount(); + LOG1(_L("Is Idle: %d"), (!hasData && pidCount == 0)); + return (!hasData && pidCount == 0); + } + +/** +Default Muxer IdleTimerExpired + +This is a confirmation that the muxer isn't wanted and so should go +to the closed state and die. + + @internalComponent + @param aTransport The Muxer which is idle +*/ +void TAvctpMuxerState::IdleTimerExpired(CAvctpTransport& aTransport) const + { + LOG_FUNC + + ChangeState(aTransport, CAvctpMuxerStateFactory::EClosed); + } + +/** + Default Muxer State Start (outgoing) function. + + We've been asked to start a connection to the specified address + The default is to assume that nothing needs to be done. + + @internalComponent + @param aTransport The Muxer which is to start up + @param aAddr The remote address to start up + @return System wide error code +*/ +TInt TAvctpMuxerState::Start(CAvctpTransport& aTransport, const TBTDevAddr& aAddr, TUint16 /*aClientId*/) const + { + LOG_FUNC + __ASSERT_DEBUG(aAddr != TBTDevAddr(0) && aTransport.DevAddr() == aAddr, PanicInState(EWrongBTAddress)); + (void)aAddr; + (void)aTransport; + return KErrNone; + } + +/** + Default Muxer State Start (incoming) function. + + We've been asked to start a connection using the given L2CAP sap + However, this is only valid when we're in the Closed state so + panic + + @internalComponent + @param aTransport The Muxer which is to start up + @param aL2CAPConSAP The existing connection + @return System wide error code +*/ +TInt TAvctpMuxerState::StartIncoming(CAvctpTransport& /*aTransport*/, const TBTDevAddr& /*aAddr*/, CServProviderBase* /*aL2CAPConSAP*/) const + { + LOG_FUNC + PanicInState(EUnexpectedMuxerEvent); + return KErrNone; + } + +TInt TAvctpMuxerState::AddSecondChannel(CAvctpTransport& /*aTransport*/, CServProviderBase& /*aSAP*/) const + { + LOG_FUNC + return KErrNotReady; + } + +void TAvctpMuxerState::RemoveSecondChannel(CAvctpTransport& /*aTransport*/) const + { + LOG_FUNC + } + +TInt TAvctpMuxerState::SecondChannelRemoved(CAvctpTransport& /*aTransport*/) const + { + LOG_FUNC + return KErrNotReady; + } + +/** + Default Muxer State CancelConnectRequest function. + + This is called when a control client wants to cancel a connection to a particular PID. + The connection will actually only be cancelled if there are no remaining control + clients for this connection. + + @internalComponent + @param aTransport The Muxer which is affected +*/ +TInt TAvctpMuxerState::CancelConnectRequest(CAvctpTransport& /*aTransport*/, TUint16 /*aClientId*/) const + { + LOG_FUNC + return KErrNotReady; + } + + +/** +Default Muxer Shutdown function. + + @internalComponent + @param aTransport The Muxer which is to shutdown +*/ +void TAvctpMuxerState::Shutdown(CAvctpTransport& /*aTransport*/, TInt /*aError*/) const + { + LOG_FUNC + PanicInState(EUnexpectedMuxerEvent); + } + +/** +Default Muxer Disconnect function. + + @internalComponent + @param aTransport The Muxer whose L2CAP link has just gone down +*/ +void TAvctpMuxerState::Disconnect(CAvctpTransport& /*aTransport*/) const + { + LOG_FUNC + PanicInState(EUnexpectedMuxerEvent); + } + +/** +Default Muxer ConnectComplete function. + + @internalComponent + @param aTransport The Muxer whose L2CAP connection has just come up +*/ +void TAvctpMuxerState::ConnectComplete(CAvctpTransport& /*aTransport*/) const + { + LOG_FUNC + PanicInState(EUnexpectedMuxerEvent); + } + +/** +Default Muxer Error function + + @internalComponent + @param aTransport The Muxer which has just received an error from it's channel sap + @param anError The error + @param anOperationMask The effected operation +*/ +void TAvctpMuxerState::Error(CAvctpTransport& /*aTransport*/, TInt /*aError*/,TUint /*aOperationMask*/, TInt /*aChannel*/) const + { + LOG_FUNC + PanicInState(EUnexpectedMuxerEvent); + } + +/** +Default Muxer new data function + + @internalComponent + @param aTransport The Muxer which has received the notification of new data. +*/ +TInt TAvctpMuxerState::NewData(CAvctpTransport& /*aTransport*/, TUint /*aMtu*/, TInt /*aChannel*/) const + { + LOG_FUNC + PanicInState(EUnexpectedMuxerEvent); + return KErrNone; + } + +/** +Default Muxer can send function + + @internalComponent + @param aTransport The Muxer which can now send +*/ +void TAvctpMuxerState::CanSend(CAvctpTransport& /*aTransport*/,TInt /*aChannel*/) const + { + LOG_FUNC + PanicInState(EUnexpectedMuxerEvent); + } + +// +// // +// Implementation of TAvctpStateClosed. // +// // +// + +/** +Default constructor + + @internalComponent + @param aFactory The Muxer state factory +*/ +TAvctpStateClosed::TAvctpStateClosed(CAvctpMuxerStateFactory& aFactory) + : TAvctpMuxerState(aFactory) + + { + LOG_FUNC + + STATENAME("Closed"); + } + +/** +Closed state entry function. + +This is only called on re-entering the Closed state, so we're not needed so delete ourselves + +@internalComponent +@param aTransport The Muxer whose state is changing +*/ +void TAvctpStateClosed::Enter(CAvctpTransport& aTransport) const + { + LOG_FUNC + + delete &aTransport; + } + +/** +Closed state exit function. + +@internalComponent +@param aTransport The Muxer whose state is changing +*/ +void TAvctpStateClosed::Exit(CAvctpTransport& aTransport) const + { + LOG_FUNC + __ASSERT_ALWAYS(aTransport.DevAddr() != TBTDevAddr(0), PanicInState(ENullTBTDevAddr)); + } + +/** +Closed Muxer IdleTimerExpired + +The idle timer should only expire in a non closed state so panic +if we get it in this state. + + @internalComponent + @param aTransport The Muxer which is idle +*/ +void TAvctpStateClosed::IdleTimerExpired(CAvctpTransport& /*aTransport*/) const + { + LOG_FUNC + + PanicInState(EUnexpectedMuxerEvent); + } + +/** + Closed Muxer State Start (outgoing) function. + + This function asks us to start a connection to the specified address + + @internalComponent + @param aTransport The Muxer which is to start up + @param aAddr The remote address to start up + @return System wide error code +*/ +TInt TAvctpStateClosed::Start(CAvctpTransport& aTransport, const TBTDevAddr& aAddr, TUint16 /*aClientId*/) const + { + LOG_FUNC + + __ASSERT_DEBUG(aTransport.DevAddr() == TBTDevAddr(0), PanicInState(ETBTDevAddrNotNull)); + + TInt ret = KErrNone; + if (aAddr != TBTDevAddr(0)) + { + // New this up just in time. + TRAP(ret, aTransport.iChannelSAPs[KAvctpPrimaryChannel] = aTransport.iProtocol.LowerProtocol()->NewSAPL(KSockSeqPacket)); + if (ret == KErrNone) + { + aTransport.AssignToDevice(aAddr); + ChangeState(aTransport, CAvctpMuxerStateFactory::ELinkPending); + } + } + else + { + // do nothing - stay closed + ret = KErrNone; + } + + if (ret != KErrNone) + { + delete &aTransport; + } + return ret; + } + +/** + Closed Muxer State Start (incoming) function. + + We've been asked to start a connection using the given L2CAP sap + + @internalComponent + @param aTransport The Muxer which is to start up + @param aL2CAPConSAP The existing connection + @return System wide error code +*/ +TInt TAvctpStateClosed::StartIncoming(CAvctpTransport& aTransport, const TBTDevAddr& aAddr, CServProviderBase* aL2CAPConSAP) const + { + LOG_FUNC + + TInt ret = SymbianAvctp::KErrBadAddress; + + // If the following is not true we'll Kern-Exec 3 anyway so assert always + __ASSERT_ALWAYS(aL2CAPConSAP, PanicInState(ENullLowerProtocolSap)); + __ASSERT_DEBUG(aTransport.DevAddr() == TBTDevAddr(0), PanicInState(ETBTDevAddrNotNull)); + + if (aAddr != TBTDevAddr(0)) + { + aTransport.AssignToDevice(aAddr); + aTransport.iChannelSAPs[KAvctpPrimaryChannel] = aL2CAPConSAP; + aTransport.iChannelSAPs[KAvctpPrimaryChannel]->SetNotify(&aTransport); + + aTransport.iChannelSAPs[KAvctpPrimaryChannel]->Start(); + ChangeState(aTransport, CAvctpMuxerStateFactory::EOpen); + ret = KErrNone; + } + // else covered by ret + + if (ret != KErrNone) + { + delete &aTransport; + } + return ret; + } + +// +// // +// Implementation of void TAvctpStateLinkPending // +// // +// Represents a muxer waiting for the L2CAP link to be created. // +// // +// + +/** +Default constructor + + @internalComponent + @param aFactory The Muxer state factory +*/ +TAvctpStateLinkPending::TAvctpStateLinkPending(CAvctpMuxerStateFactory& aFactory) + : TAvctpMuxerState(aFactory) + + { + LOG_FUNC + STATENAME("LinkPending"); + } + +/** +Link Pending state entry function. + +We request the lower protocol SAP to bring up the link. + +@internalComponent +@param aTransport The Muxer which is waiting for a link +*/ +void TAvctpStateLinkPending::Enter(CAvctpTransport& aTransport) const + { + LOG_FUNC + + __ASSERT_ALWAYS(aTransport.iChannelSAPs[KAvctpPrimaryChannel], PanicInState(ENullLowerProtocolSap)); + + aTransport.DequeIdleTimer(); + // we become the socket for iChannelsSAPs[0] + aTransport.iChannelSAPs[KAvctpPrimaryChannel]->SetNotify(&aTransport); + + TL2CAPSockAddr addr; + addr.SetBTAddr(aTransport.DevAddr()); + addr.SetPort(KAVCTP); + + // the security settings are: + // (though see :Preauthorise() for the authentication exceptions due to avdtp authentication) + TBTServiceSecurity sec; + sec.SetAuthentication(KOutboundAuthenticationDefault); + sec.SetAuthorisation(KOutboundAuthoristationDefault); + sec.SetEncryption(KOutboundEncryptionDefault); + sec.SetDenied(EFalse); + sec.SetUid(KAvctpServiceUid); + addr.SetSecurity(sec); + + TInt err = aTransport.iChannelSAPs[KAvctpPrimaryChannel]->SetRemName(addr); + __ASSERT_DEBUG(!err, PanicInState(EErrorSettingAddress)); + + // Bring up the L2CAP link + aTransport.iChannelSAPs[KAvctpPrimaryChannel]->ActiveOpen(); + } + +// TAvctpStateLinkPending::Exit - we'd lke to call aTransport.CheckForIdle(0) but cause +// in this state IsIdle always returns EFalse that's not possible. Hence do the +// CheckForIdle in the Enter of the next state currently Open or Closed. + + + + +/** +Link Pending Muxer IsIdle + +The muxer is asking it's state to determine whether it's idle. Cause the link is pending +we can't be idle whatever happens. + +Note that this means we must call CheckForIdle on aTransport on entering the next state. + + @internalComponent + @param aTransport The Muxer which is idle +*/ +TBool TAvctpStateLinkPending::IsIdle(CAvctpTransport& /*aTransport*/) const + { + LOG_FUNC + return EFalse; + } + +/** + Link Pending Muxer State CancelConnnectRequest function. + + This is called when a control client wants to cancel a connection to a particular PID. + The connection will actually only be cancelled if there are no remaining control + clients for this connection. + + This function just removes the saplinksmgr on aPid from the list of control + clients since it no longer is interested in this remote device and shouldn't + receive any more events related to it. + + @internalComponent + @param aTransport The Muxer which is affected + @param aPid The pid to cancel the connection too +*/ +TInt TAvctpStateLinkPending::CancelConnectRequest(CAvctpTransport& /*aTransport*/, TUint16 /*aClientId*/) const + { + LOG_FUNC + return KErrNotSupported; + } + + +/** +Link Pending Muxer ConnectComplete function. + +This is the successful result of a connection attempt to a remote device. +(A incoming connection wouldn't have resulted in a BearerConnectComplete on the +protocol instead) + + @internalComponent + @param aTransport The Muxer whose L2CAP connection has just come up +*/ +void TAvctpStateLinkPending::ConnectComplete(CAvctpTransport& aTransport) const + { + LOG_FUNC + + ChangeState(aTransport, CAvctpMuxerStateFactory::EOpen); + aTransport.NotifyLinkUp(aTransport.iRemoteAddr); + } + +/** +Link Pending Muxer Error function + +This is called after some kind of error in the lower protocol Sap. + + @internalComponent + @param aTransport The Muxer which has just received an error from it's iChannelsSAPs[0] + @param anError The error + @param anOperationMask The effected operation +*/ +void TAvctpStateLinkPending::Error(CAvctpTransport& aTransport, TInt aError,TUint aOperationMask, TInt aChannel) const + { + LOG_FUNC + + __ASSERT_DEBUG(aChannel == KAvctpPrimaryChannel, PanicInState(EAvctpInvalidChannel)); + + if (aOperationMask & (MSocketNotify::EErrorFatal | + MSocketNotify::EErrorConnect | + MSocketNotify::EErrorClose | + MSocketNotify::EErrorAllOperations)) + { + // The connect failed, so we'd better go to the Closed state + // and tell clients what's happened. + // Notifications are made before changing the state because the Enter() method of the Close state + // delete the transport, so we can't call anything after changing the state. + + aTransport.NotifyLinkError(aError, aChannel == KAvctpSecondaryChannel); + ChangeState(aTransport, CAvctpMuxerStateFactory::EClosed); + } + else if (aOperationMask & (MSocketNotify::EErrorSend | + MSocketNotify::EErrorRecv | + MSocketNotify::EErrorIoctl)) + { + // Since the connection isn't up, we shouldn't be getting a send or recv error + // nor do we currently support ioctls in the muxer so: + PanicInState(EUnexpectedMuxerEvent); + } + } + +// +// // +// Implementation of state TAvctpStateOpen // +// // +// This represents a fully connected Muxer that can send and receive data // +// // +// + +/** +Default constructor + + @internalComponent + @param aFactory The Muxer state factory +*/ +TAvctpStateOpen::TAvctpStateOpen(CAvctpMuxerStateFactory& aFactory) + : TAvctpMuxerState(aFactory) + + { + LOG_FUNC + STATENAME("Open"); + } + +/** +Open state entry function. + +In case a Avctp Sap asked to send data we need to signal them that they can now send + +@internalComponent +@param aTransport The Muxer whose state is changing +*/ +void TAvctpStateOpen::Enter(CAvctpTransport& aTransport) const + { + LOG_FUNC + + aTransport.SetClearToSend(KAvctpPrimaryChannel); //only first channel is open so far + aTransport.PacketMgr().CanSend(KAvctpPrimaryChannel); + + // for stereo headset usecase, pre-authorise device for AVDTP + aTransport.iProtocol.SetPreauthorisation(aTransport.iRemoteAddr, ETrue); + } + +/** +Open state exit function. + +@internalComponent +@param aTransport The Muxer whose state is changing +*/ +void TAvctpStateOpen::Exit(CAvctpTransport& aTransport) const + { + LOG_FUNC + + // for stereo headset usecase, de-pre-authorise device for AVDTP + aTransport.iProtocol.SetPreauthorisation(aTransport.iRemoteAddr, EFalse); + } + + +/** + Open Muxer State Start (outgoing) function. + + We've been asked to start a connection to the specified address + The connection is already up so immediately send a ConnectCfm event + + @internalComponent + @param aTransport The Muxer which is to start up + @param aAddr The remote address to start up + @return System wide error code +*/ +TInt TAvctpStateOpen::Start(CAvctpTransport& aTransport, const TBTDevAddr& __DEBUG_ONLY(aAddr), TUint16 aClientId) const + { + LOG_FUNC + __ASSERT_DEBUG(aAddr != TBTDevAddr(0) && aTransport.DevAddr() == aAddr, PanicInState(EWrongBTAddress)); + + aTransport.NotifyAttachConfirm(aClientId, KErrNone, EFalse); + return KErrNone; + } + + +/** +Open Muxer Shutdown function. + +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. + +Notify all clients of the problem and go to the closed state. + + @internalComponent + @param aTransport The Muxer which is sending data +*/ +void TAvctpStateOpen::Shutdown(CAvctpTransport& aTransport, TInt /*aError*/) const + { + LOG_FUNC + + ChangeState(aTransport, CAvctpMuxerStateFactory::EClosed); + } + + +/** +Open Muxer Disconnect function. + +This is the result of a remote device disconnecting from us. +Let all interested parties know. + + @internalComponent + @param aTransport The Muxer whose L2CAP link has just gone down +*/ +void TAvctpStateOpen::Disconnect(CAvctpTransport& aTransport) const + { + LOG_FUNC + + aTransport.iProtocol.PrimaryChannelIncomingRemoteDisconnection(aTransport.iRemoteAddr); + ChangeState(aTransport, CAvctpMuxerStateFactory::EClosed); + } + +/** +Open Muxer Error function + +This is called after some kind of error in the lower protocol Sap. + + @internalComponent + @param aTransport The Muxer which has just received an error from it's channel sap + @param anError The error + @param anOperationMask The affected operation +*/ +void TAvctpStateOpen::Error(CAvctpTransport& aTransport, TInt aError,TUint aOperationMask, TInt aChannel) const + { + LOG_FUNC + + __ASSERT_DEBUG(aChannel == KAvctpPrimaryChannel, PanicInState(EAvctpInvalidChannel)); + + if (aOperationMask & (MSocketNotify::EErrorRecv | + MSocketNotify::EErrorSend)) + { + // the packet mgr is only interested in data plane errors + aTransport.PacketMgr().SignalMuxerError(aError, aOperationMask); + } + + if (aOperationMask & (MSocketNotify::EErrorFatal | + MSocketNotify::EErrorClose | + MSocketNotify::EErrorAllOperations)) + { + // The connection has gone down so we inform our control clients of this fact + aTransport.NotifyLinkDown(aTransport.iAddress, aChannel, aError); + } + else if (aOperationMask & (MSocketNotify::EErrorConnect | + MSocketNotify::EErrorIoctl)) + { + // Since the connection is already up, we shouldn't be getting a connect error + // nor do we currently support ioctls + PanicInState(EUnexpectedMuxerEvent); + } + ChangeState(aTransport, CAvctpMuxerStateFactory::EClosed); + } + +TInt TAvctpStateOpen::AddSecondChannel(CAvctpTransport& aTransport, CServProviderBase& aSAP) const + { + LOG_FUNC + // can bind socket to SAP now + aTransport.BindSecondaryChannelSap(aSAP); + + // see if it is connected or not + TL2CAPSockAddr addr; + aSAP.RemName(addr); + + if (addr.BTAddr()==TBTDevAddr(0)) + { + addr.SetBTAddr(aTransport.iRemoteAddr); + addr.SetPort(KAvctpSecondChannelPSM); + + // the security settings are: + // (though see :Preauthorise() for the authentication exceptions due to avdtp authentication) + TBTServiceSecurity sec; + sec.SetAuthentication(KSecondaryChannelAuthenticationDefault); + sec.SetAuthorisation(KSecondaryChannelAuthoristationDefault); + sec.SetEncryption(KOutboundEncryptionDefault); + sec.SetDenied(EFalse); + sec.SetUid(KAvctpServiceUid); + addr.SetSecurity(sec); + + TInt err = aTransport.iChannelSAPs[KAvctpSecondaryChannel]->SetRemName(addr); + __ASSERT_DEBUG(err == KErrNone, PanicInState(ESetRemNameError)); + + const TUint KDefaultMtu = 335; + + TPckgBuf config; + config().SetMaxTransmitUnitSize(KAvctpSecondaryChannelInboundMTU); + config().SetMinMTU(KDefaultMtu); + config().SetMaxReceiveUnitSize(KAvctpSecondaryChannelInboundMTU); + config().SetMinMRU(KDefaultMtu); + + err = aTransport.iChannelSAPs[KAvctpSecondaryChannel]->SetOption(KSolBtL2CAP, KL2CAPUpdateChannelConfig, config); + __ASSERT_DEBUG(err == KErrNone, Panic(ESetOptionError)); + + aTransport.iChannelSAPs[KAvctpSecondaryChannel]->ActiveOpen(); + ChangeState(aTransport, CAvctpMuxerStateFactory::ESecondLinkPending); + } + else + { + aTransport.iChannelSAPs[KAvctpSecondaryChannel]->Start(); + aTransport.SetClearToSend(KAvctpSecondaryChannel); + ChangeState(aTransport, CAvctpMuxerStateFactory::EFullyOpen); + aTransport.NotifyLinkUp(addr.BTAddr(), ETrue); + } + return KErrNone; + } + +/** +Open Muxer new data function + +The lower protocol sap has notified the muxer of new data that is +waiting for retrieval. + +This is called once for each new packet of data that L2CAP has notified the muxer of. +We get to the data by calling GetData on the lower protocol SAP +and give the resulting data to the Avctp Protocol, letting it workout who wants it. + +This assumes a packet interface from L2CAP. + + @internalComponent + @param aTransport The Muxer which has received the notification of new data. + @param aMtu The length of the data packet to be read. +*/ +TInt TAvctpStateOpen::NewData(CAvctpTransport& aTransport, TUint aMtu, TInt aChannel) const + { + LOG_FUNC + __ASSERT_DEBUG(aChannel==KAvctpPrimaryChannel, PanicInState(EUnexpectedMuxerEvent)); + __ASSERT_DEBUG(aTransport.iChannelSAPs[aChannel], PanicInState(ENullLowerProtocolSap)); + + TInt err = KErrNone; + // Read data into the buffer + // the transport feeds us synchronously for first channel + RMBufChain inboundFragment; + TInt dataAvailable = aTransport.iChannelSAPs[KAvctpPrimaryChannel]->GetData(inboundFragment, aMtu, 0); + if (dataAvailable > 0) + { + err = aTransport.iPacketMgr->NewData(inboundFragment, aChannel); // ownership xferred; + + //NewData() only returns an error in the case where the data header gave a parse error. + //In this case the muxer is shutdown, so the remaining packets cannot be read. + //In the case where NewData() failed to pass on onwnership, an attempt should be made + //to read remaining packets, so no error is returned. + if(err != KErrNone) + { + inboundFragment.Free(); + } + } + else if (dataAvailable < KErrNone) // some error occurred reading the data. shutdown the muxer and report the error + { + aTransport.Shutdown(dataAvailable); + inboundFragment.Free(); + err = KErrMuxerShutDown; + } + // otherwise, no data available + return err; + } + +/** +Open Muxer can send function + +This is called after a blocked Write on the lower protocol SAP. This is a notification +that the lower protocol SAP is ready to receive more data. + + @internalComponent + @param aTransport The Muxer which can now send +*/ +void TAvctpStateOpen::CanSend(CAvctpTransport& aTransport, TInt aChannel) const + { + LOG_FUNC + + aTransport.SetClearToSend(aChannel); + aTransport.PacketMgr().CanSend(aChannel); + } + +TAvctpStateFullyOpen::TAvctpStateFullyOpen(CAvctpMuxerStateFactory& aFactory) +:TAvctpStateOpen(aFactory) + { + LOG_FUNC + STATENAME("FullyOpen"); + } + +void TAvctpStateFullyOpen::Enter(CAvctpTransport& aTransport) const + { + // should check the sap psm really + aTransport.SetClearToSend(KAvctpSecondaryChannel); + aTransport.PacketMgr().CanSend(KAvctpSecondaryChannel); + } + +/** + The method is defined and left empty on purpose (because this class derives from TAvctpStateOpen + but we don't wont its method to be called. + */ +void TAvctpStateFullyOpen::Exit(CAvctpTransport& /*aTransport*/) const + { + } + +void TAvctpStateFullyOpen::Error(CAvctpTransport& aTransport, TInt aError, TUint aErrorMask, TInt aChannel) const + { + LOG_FUNC + + if (aErrorMask & (MSocketNotify::EErrorRecv | + MSocketNotify::EErrorSend)) + { + // the packet mgr is only interested in data plane errors + aTransport.PacketMgr().SignalMuxerError(aError, aErrorMask); + } + + if (aErrorMask & (MSocketNotify::EErrorFatal | + MSocketNotify::EErrorClose | + MSocketNotify::EErrorAllOperations)) + { + // The connection has gone down so we inform our control clients of this fact + aTransport.NotifyLinkDown(aTransport.iAddress, aChannel, aError); + } + else if (aErrorMask & (MSocketNotify::EErrorConnect | + MSocketNotify::EErrorIoctl)) + { + // Since the connection is already up, we shouldn't be getting a connect error + // nor do we currently support ioctls + PanicInState(EUnexpectedMuxerEvent); + } + ChangeState(aTransport, aChannel == KAvctpSecondaryChannel ? CAvctpMuxerStateFactory::EOpen : CAvctpMuxerStateFactory::EClosed); + } + +void TAvctpStateFullyOpen::Disconnect(CAvctpTransport& aTransport) const + { + aTransport.iProtocol.PrimaryChannelIncomingRemoteDisconnection(aTransport.iRemoteAddr); + ChangeState(aTransport, CAvctpMuxerStateFactory::EClosed); + } + +void TAvctpStateFullyOpen::CanSend(CAvctpTransport& aTransport,TInt aChannel) const + { + aTransport.SetClearToSend(aChannel); + aTransport.PacketMgr().CanSend(aChannel); + } + +TInt TAvctpStateFullyOpen::NewData(CAvctpTransport& aTransport, TUint aMtu, TInt aChannel) const + { + RMBufChain inboundFragment; + __ASSERT_DEBUG(aTransport.iChannelSAPs[aChannel], PanicInState(ENullLowerProtocolSap)); + aTransport.iChannelSAPs[aChannel]->GetData(inboundFragment, aMtu, 0); + + TInt err = aTransport.iPacketMgr->NewData(inboundFragment, aChannel); // ownership xferred; + + if(err != KErrNone) + { + inboundFragment.Free(); + } + return err; + } + +TInt TAvctpStateFullyOpen::Start(CAvctpTransport& aTransport, const TBTDevAddr& /*aAddr*/, TUint16 aClientId) const + { + aTransport.NotifyAttachConfirm(aClientId, KErrNone, EFalse); + return KErrNone; + } + +void TAvctpStateFullyOpen::RemoveSecondChannel(CAvctpTransport& aTransport) const + { + LOG_FUNC + // shutdown secondary channel without notifying clients (second param is EFalse) + ShutDownSecondarySap(aTransport, EFalse); + } + +TInt TAvctpStateFullyOpen::SecondChannelRemoved(CAvctpTransport& aTransport) const + { + LOG_FUNC + // shutdown secondary channel notifying clients (second param is ETrue) + ShutDownSecondarySap(aTransport, ETrue); + return KErrNone; + } + +void TAvctpStateFullyOpen::Shutdown(CAvctpTransport& aTransport, TInt /*aError*/) const + { + ChangeState(aTransport, CAvctpMuxerStateFactory::EClosed); + } + + +/** + The method is declared and left empty on purpose (because this class derives from TAvctpStateOpen + but we don't wont its method to be called. + */ +void TAvctpStateSecondChannelPending::Enter(CAvctpTransport& /*aTransport*/) const + { + } + +/** + The method is declared and left empty on purpose (because this class derives from TAvctpStateOpen + but we don't wont its method to be called. + */ +void TAvctpStateSecondChannelPending::Exit(CAvctpTransport& /*aTransport*/) const + { + } + +void TAvctpStateSecondChannelPending::Error(CAvctpTransport& aTransport, TInt aError, TUint /*aErrorMask*/, TInt aChannel) const + { + aTransport.NotifyLinkError(aError, aChannel == KAvctpSecondaryChannel); + ChangeState(aTransport, CAvctpMuxerStateFactory::EOpen); + } + +TAvctpStateSecondChannelPending::TAvctpStateSecondChannelPending(CAvctpMuxerStateFactory& aFactory) +:TAvctpStateOpen(aFactory) + { + LOG_FUNC + STATENAME("SecondLinkPending"); + } + +void TAvctpStateSecondChannelPending::ConnectComplete(CAvctpTransport& aTransport) const + { + LOG_FUNC + aTransport.NotifyLinkUp(aTransport.iRemoteAddr, ETrue); + ChangeState(aTransport, CAvctpMuxerStateFactory::EFullyOpen); + } + +void TAvctpStateSecondChannelPending::Disconnect(CAvctpTransport& aTransport) const + { + LOG_FUNC + + // In this state there is at least one outstanding ioctl on the secondary ctrl sap that + // we must complete + + aTransport.NotifyAttachConfirm(KErrMuxerShutDown, ETrue); + aTransport.iProtocol.PrimaryChannelIncomingRemoteDisconnection(aTransport.iRemoteAddr); + ChangeState(aTransport, CAvctpMuxerStateFactory::EClosed); + } + +void TAvctpStateSecondChannelPending::CanSend(CAvctpTransport& aTransport, TInt __DEBUG_ONLY(aChannel)) const + { + LOG_FUNC + __ASSERT_DEBUG(aChannel == KAvctpPrimaryChannel, PanicInState(EAvctpInvalidChannel)); + aTransport.SetClearToSend(KAvctpPrimaryChannel); + aTransport.PacketMgr().CanSend(KAvctpPrimaryChannel); + } + +TInt TAvctpStateSecondChannelPending::NewData(CAvctpTransport& aTransport, TUint aMtu, TInt aChannel) const + { + LOG_FUNC + __ASSERT_DEBUG(aChannel==KAvctpPrimaryChannel, PanicInState(EUnexpectedMuxerEvent)); + __ASSERT_DEBUG(aTransport.iChannelSAPs[aChannel], PanicInState(ENullLowerProtocolSap)); + + // Read data into the buffer + // the transport feeds us synchronously for first channel + RMBufChain inboundFragment; + aTransport.iChannelSAPs[KAvctpPrimaryChannel]->GetData(inboundFragment, aMtu, 0); + + TInt err = aTransport.iPacketMgr->NewData(inboundFragment, aChannel); // ownership xferred; + + //NewData() only returns an error in the case where the data header gave a parse error. + //In this case the muxer is shutdown, so the remaining packets cannot be read. + //In the case where NewData() failed to pass on onwnership, an attempt should be made + //to read remaining packets, so no error is returned. + if(err != KErrNone) + { + inboundFragment.Free(); + } + return err; + } + +TInt TAvctpStateSecondChannelPending::Start(CAvctpTransport& /*aTransport*/, const TBTDevAddr& /*aAddr*/, TUint16 /*aClientId*/) const + { + return KErrNotReady; + } + +void TAvctpStateSecondChannelPending::Shutdown(CAvctpTransport& aTransport, TInt /*aError*/) const + { + LOG_FUNC + ChangeState(aTransport, CAvctpMuxerStateFactory::EClosed); + } + +void TAvctpStateSecondChannelPending::RemoveSecondChannel(CAvctpTransport& aTransport) const + { + LOG_FUNC + // shutdown secondary channel without notifying clients (second param is EFalse) + ShutDownSecondarySap(aTransport, EFalse); + } +// +// // +// CAvctpMuxerStateFactory implementation // +// // +// + +/** +Default constructor + + @internalComponent +*/ +CAvctpMuxerStateFactory::CAvctpMuxerStateFactory() + { + LOG_FUNC + } + +/** +Destructor to free resources. + +Delete all our lightweight states + + @internalComponent +*/ +CAvctpMuxerStateFactory::~CAvctpMuxerStateFactory() + { + LOG_FUNC + + iStates.DeleteAll(); + } + +/** +Static factory factory function (think about it... ;-) + +Creates an array of lightweight states for Muxers + + @internalComponent + @leave KErrNoMemory if the CAvctpMuxerStateFactory object could not be created + @return A pointer to the new state factory +*/ +CAvctpMuxerStateFactory* CAvctpMuxerStateFactory::NewL() + { + LOG_STATIC_FUNC + + CAvctpMuxerStateFactory* factory = new(ELeave) CAvctpMuxerStateFactory(); + CleanupStack::PushL(factory); + + // Create all the new states + factory->iStates[EClosed] = new(ELeave) TAvctpStateClosed(*factory); + factory->iStates[ELinkPending] = new(ELeave) TAvctpStateLinkPending(*factory); + factory->iStates[EOpen] = new(ELeave) TAvctpStateOpen(*factory); + factory->iStates[ESecondLinkPending] = new(ELeave) TAvctpStateSecondChannelPending(*factory); + factory->iStates[EFullyOpen] = new(ELeave) TAvctpStateFullyOpen(*factory); + + CleanupStack::Pop(factory); + return factory; + } + +/** +Utility function to get a lightweight state + + @internalComponent + @param aState Index to the state required + @return A lightweight Muxer state +*/ +TAvctpMuxerState& CAvctpMuxerStateFactory::GetState(const TAvctpMuxerStates aState) const + { + LOG_FUNC + __ASSERT_DEBUG(aState != EMaxStates, Panic(EAvctpMuxerStateOutOfBounds)); + return *iStates[aState]; + } + +TInt CAvctpMuxerStateFactory::StateIndex(const TAvctpMuxerState* aState) const + { + TInt state; + for (state = 0; state < EMaxStates; state++) + { + if (iStates[state] == aState) + { + return state; + } + } + + return KUnknownState; + }