diff -r 000000000000 -r 29b1cd4cb562 bluetooth/btstack/rfcomm/rfcommmuxchannel.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bluetooth/btstack/rfcomm/rfcommmuxchannel.cpp Fri Jan 15 08:13:17 2010 +0200 @@ -0,0 +1,902 @@ +// Copyright (c) 1999-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: +// Runs the mux control channel for the Rfcomm muxer +// +// + +#include +#include "rfcommmuxchannel.h" +#include "rfcommconsts.h" +#include "rfcommutil.h" + +#ifdef __FLOG_ACTIVE +_LIT8(KLogComponent, LOG_COMPONENT_RFCOMM); +#endif + +CRfcommMuxChannel::CRfcommMuxChannel(CMuxChannelStateFactory& aFact, CRfcommMuxer& aMux, + CServProviderBase& aSAP, CMuxChannelStateFactory::TChannelState aInitialState) + : iSAP(aSAP), + iMux(aMux) + { + iState=aFact.GetState(aInitialState); + iState->Enter(*this, EFalse); + TCallBack cb(IdleTimerCallback, this); + iIdleTimer.Set(cb); + } + +CRfcommMuxChannel::~CRfcommMuxChannel() + { + DequeIdleTimer(); + } + +void CRfcommMuxChannel::SetAddress(TBTDevAddr& aAddr) + { + TL2CAPSockAddr remote; + iSAP.AutoBind(); + remote.SetBTAddr(aAddr); + remote.SetPort(KRFCOMMPSM); // 0x03 normally... + if(iSAP.SetRemName(remote)!=KErrNone) + { + Panic(ERfcommErrorSettingAddress); + } + } + +void CRfcommMuxChannel::QueIdleTimer(TInt aDelay) + { + if(!iIdleTimerQueued) + { + BTSocketTimer::Queue(aDelay, iIdleTimer); + iIdleTimerQueued=ETrue; + } + } + +void CRfcommMuxChannel::DequeIdleTimer() + { + if(iIdleTimerQueued) + { + BTSocketTimer::Remove(iIdleTimer); + iIdleTimerQueued=EFalse; + } + } + +TInt CRfcommMuxChannel::IdleTimerCallback(TAny* aChannel) + { + CRfcommMuxChannel* channel=static_cast(aChannel); + channel->iIdleTimerQueued=EFalse; + LOG(_L("RFCOMM: Mux channel idle timer callback")); + channel->iState->IdleTimeout(*channel); + return EFalse; + } + +/** + This lets the protocol know whether this mux is able to be attached to. + Sometimes it will be irrevocably committed to going down and so is not + valid to be attached to. + @return TBool Whether it's ok to attach a SAP + */ +TBool CRfcommMuxChannel::CanAttachSAP() + { + // Let the state use its context to work out if we're going down + return iState->CanAttachSAP(); + } + +/* + State factory +*/ + +CMuxChannelStateFactory* CMuxChannelStateFactory::NewL() + { + CMuxChannelStateFactory* ret=new (ELeave) CMuxChannelStateFactory(); + CleanupStack::PushL(ret); + ret->ConstructL(); + CleanupStack::Pop(); + return ret; + } + +void CMuxChannelStateFactory::ConstructL() + { + iStates[EClosed]=new (ELeave) TMuxChannelStateClosed(*this); + iStates[EWaitForLink]=new (ELeave) TMuxChannelStateWaitForLink(*this); + iStates[EError]= new (ELeave) TMuxChannelStateError(*this); + iStates[ELinkUp]=new (ELeave) TMuxChannelStateLinkUp(*this); + iStates[EWaitForSABMResp]=new (ELeave) TMuxChannelStateWaitForSABMResp(*this); + iStates[EOpen]=new (ELeave) TMuxChannelStateOpen(*this); + iStates[EClosing]=new (ELeave) TMuxChannelStateClosing(*this); + } + +CMuxChannelStateFactory::~CMuxChannelStateFactory() + { + iStates.DeleteAll(); + } + +TMuxChannelState* CMuxChannelStateFactory::GetState(TChannelState aState) + { + __ASSERT_DEBUG(aState != ERfcommChannelMaxState, Panic(ERfCommMuxerStateOutOfBounds)); + return iStates[aState]; + } + +TInt CMuxChannelStateFactory::StateIndex(const TMuxChannelState* aState) const + { + TInt state; + for (state = 0; state < ERfcommChannelMaxState; state++) + { + if (iStates[state] == aState) + { + return state; + } + } + + return KUnknownState; + } + + +/* + The base state class +*/ + +void TMuxChannelState::SetState(CRfcommMuxChannel& aContext, CMuxChannelStateFactory::TChannelState aState) + { +#ifdef __FLOG_ACTIVE + TMuxChannelState* state=iFactory.GetState(aState); + LOG2(_L("RFCOMM: MuxChannel : State %S -> %S"), + &aContext.iState->iName, &state->iName); +#endif //__FLOG_ACTIVE +#ifdef _DEBUG + TMuxChannelState* st=iFactory.GetState(aState); + __ASSERT_DEBUG(st!=aContext.iState, PanicInState(ERfcommMuxChannelStateChangeToSelf)); +#endif + aContext.iState=iFactory.GetState(aState); + } + +TMuxChannelState::TMuxChannelState(CMuxChannelStateFactory& aFactory) + : iFactory(aFactory) + { + } + +void TMuxChannelState::PanicInState(TRFCOMMPanic aPanic) const + { + Panic(aPanic, iFactory.StateIndex(this)); + } + +#ifdef _DEBUG +void TMuxChannelState::DebugPanicInState(TRFCOMMPanic aPanic) const +#else +void TMuxChannelState::DebugPanicInState(TRFCOMMPanic /*aPanic*/) const +#endif + { + #ifdef _DEBUG + PanicInState(aPanic); + #endif + } + +void TMuxChannelState::Enter(CRfcommMuxChannel& aContext, TBool /*aDisconnectingIdleTimer*/) + /** + Called when entering a state + **/ + { + aContext.DequeIdleTimer(); //ENTERED NEW STATE => NOT IDLE + } + +void TMuxChannelState::Open(CRfcommMuxChannel& aContext) + /** + A request to bring up the mux channel + **/ + { + aContext.iOpenPending=ETrue; + aContext.iClosePending=EFalse; + } + +void TMuxChannelState::Close(CRfcommMuxChannel& aContext) + /** + Close the mux channel down + **/ + { + aContext.iClosePending=ETrue; + aContext.iOpenPending=EFalse; + } + +TBool TMuxChannelState::IsOpen(CRfcommMuxChannel& /*aContext*/) + { + return EFalse; + } + +void TMuxChannelState::UA(CRfcommMuxChannel& /*aContext*/) + /** + A UA frame for DLCI zero has been received + **/ + { + // Do nothing + } + +void TMuxChannelState::DISC(CRfcommMuxChannel& /*aContext*/) + /** + A DISC frame for DLCI zero has been received + **/ + { + // Do nothing + } + +void TMuxChannelState::DM(CRfcommMuxChannel& /*aContext*/) + /** + A DM frame for DLCI zero has been received + **/ + { + // Do nothing + } + +void TMuxChannelState::PN(CRfcommMuxChannel& aContext, + TBool aCommand, TRfcommPortParams& /*aParams*/) + /** + A PN command or response for DLCI zero has been received + **/ + { + if(aCommand) + { + // We're at liberty to ignore this, since PNs are not allowed + // on DLCI 0. However, we can send back a PN response with a + // MTU of 127 (the default, which we can guarantee). + // + // NB. Default TRfcommPortParams constructor sets default MTU (127) + // + TRfcommPortParams muxParams; + muxParams.iInitialCredit=0; //DS improved by making 0 default? + aContext.TransmitPN(EFalse, muxParams); + } + else + { + // Should never happen + LOG(_L("RFCOMM: Received a PN response on Mux channel!")); + } + + } + +void TMuxChannelState::SABM(CRfcommMuxChannel& /*aContext*/) + /** + A SABM has been received for DLCI 0 + **/ + { + //do nothing + } + +void TMuxChannelState::FrameTimeout(CRfcommMuxChannel& /*aContext*/, + CRfcommFrame* /*aFrm*/) + /** + A ctrl or mux frame has timed out before a response was received. + + Generally this is not a good thing. + **/ + { + DebugPanicInState(ERfcommChannelIdleTimeout); + } + +void TMuxChannelState::Disconnect(CRfcommMuxChannel& /*aContext*/) + /** + A disconnect indication has come in from L2CAP + **/ + { + // Do nothing + } + +void TMuxChannelState::CanClose(CRfcommMuxChannel& /*aContext*/) + /** + L2CAP is happy for us to delete the sap + **/ + { + // Do nothing + } + +void TMuxChannelState::ConnectComplete(CRfcommMuxChannel& /*aContext*/) + /** + The L2CAP connect has completed + **/ + { + // Do nothing + } + +void TMuxChannelState::Error(CRfcommMuxChannel& /*aContext*/, TInt /*aError*/, + TUint /*anOperationMask*/) + /** + An error on the L2CAP sap has occurred + **/ + { + // Do nothing + } + +void TMuxChannelState::IdleTimeout(CRfcommMuxChannel& /*aContext*/) + /** + The idle timer has gone off. + **/ + { + DebugPanicInState(ERfcommChannelIdleTimeout); + } + +TBool TMuxChannelState::CanAttachSAP() + { + return ETrue; + } + +/* + Closed +*/ + +TMuxChannelStateClosed::TMuxChannelStateClosed(CMuxChannelStateFactory& aFactory) + : TMuxChannelState(aFactory) + { + STATENAME("Closed"); + } + +void TMuxChannelStateClosed::Enter(CRfcommMuxChannel& aContext, TBool /*aDisconnectingIdleTimer*/) + /** + Reset everything + **/ + { + aContext.iOpenPending=EFalse; + aContext.iClosePending=EFalse; + aContext.DequeIdleTimer(); + } + +void TMuxChannelStateClosed::Open(CRfcommMuxChannel& aContext) + /** + Open up the L2CAP link + **/ + { + // Set opening\closing flags + TMuxChannelState::Open(aContext); + // Make sure the PSM is in place, rather than an old CID + TL2CAPSockAddr remote; + aContext.iSAP.RemName(remote); + remote.SetPort(KRFCOMMPSM); + + TBTServiceSecurity ssec; + ssec.SetAuthentication(EMitmNotRequired); + ssec.SetAuthorisation(EFalse); + ssec.SetEncryption(EFalse); + ssec.SetDenied(EFalse); + + remote.SetSecurity(ssec); + + aContext.iSAP.SetRemName(remote); + SetState(aContext,CMuxChannelStateFactory::EWaitForLink); + aContext.iSAP.ActiveOpen(); + aContext.iState->Enter(aContext); + } + +void TMuxChannelStateClosed::Close(CRfcommMuxChannel& aContext) + { + // No need to do anything - call back synchronously + aContext.iMux.MuxChannelClosed(); + } + +/* + WaitForLink - waiting for the link to come up +*/ + +TMuxChannelStateWaitForLink::TMuxChannelStateWaitForLink(CMuxChannelStateFactory& aFactory) + : TMuxChannelState(aFactory) + { + STATENAME("WaitForLink"); + } + +void TMuxChannelStateWaitForLink::ConnectComplete(CRfcommMuxChannel& aContext) + /** + The link has come up + + Move to the start state in the connected superstate (LinkUp) + **/ + { + SetState(aContext,CMuxChannelStateFactory::ELinkUp); + aContext.iState->Enter(aContext); + } + +void TMuxChannelStateWaitForLink::Close(CRfcommMuxChannel& aContext) + /** + The mux is no longer wanted. + + Cancel the pending opening so we'll just timeout and die in + state LinkUp, or the link won't come up so we'll drop to + closed. + **/ + { + aContext.iOpenPending=EFalse; + } + +void TMuxChannelStateWaitForLink::Error(CRfcommMuxChannel& aContext, TInt aError, + TUint aOperationMask) + /** + The connection failed to come up + **/ + { + if(aOperationMask | MSocketNotify::EErrorFatal) + { + // Things are very bad, so drop to the error state + SetState(aContext,CMuxChannelStateFactory::EError); + aContext.iMux.MuxChannelError(ETrue, aError); + } + else if(aOperationMask | MSocketNotify::EErrorConnect) + { + // This connect failed, so we'd better go to the Closed state + SetState(aContext,CMuxChannelStateFactory::EClosed); + aContext.iMux.MuxChannelError(EFalse, aError); + } + else + { + // We have an error that probably should be ignored + LOG(_L("RFCOMM: **** Muxchannel wait for link, ignoring error ****")); + } + aContext.iState->Enter(aContext); + } + +/* + Error +*/ + +TMuxChannelStateError::TMuxChannelStateError(CMuxChannelStateFactory& aFactory) + : TMuxChannelState(aFactory) + { + STATENAME("Error"); + } + +void TMuxChannelStateError::Open(CRfcommMuxChannel& /*aContext*/) + { + PanicInState(ERfcommChannelError); + } + +void TMuxChannelStateError::Close(CRfcommMuxChannel& aContext) + { + // tell the muxer now since there'll be no transition from this state + aContext.iMux.MuxChannelClosed(); + } + +TBool TMuxChannelStateError::CanAttachSAP() + { + return EFalse; + } + +/* + Connected. Super state for several states +*/ + + +TMuxChannelStateConnected::TMuxChannelStateConnected(CMuxChannelStateFactory& aFactory) + : TMuxChannelState(aFactory) + { + STATENAME("Connected"); + } + +void TMuxChannelStateConnected::SABM(CRfcommMuxChannel& aContext) + /** + A SABM has been received for DLCI 0 + We are not in one of the connected states that expects a SABM + Attempt to tell the other side that we are not in a fit state. + **/ + { + LOG(_L("RFCOMM: sending NACK on unexpected SABM")); + aContext.TransmitDM(); + } + +void TMuxChannelStateConnected::FrameTimeout(CRfcommMuxChannel& aContext, CRfcommFrame* /*aFrm*/) + /** + A frame has failed to elicit a response + + This is bad for the mux channel, so we go to LinkUp. The muxer + will take care of the frame.. + **/ + { + // Do the basics... + FrameTimeoutHelper(aContext); + + //... and then go to ELinkUp + SetState(aContext,CMuxChannelStateFactory::ELinkUp); + aContext.iState->Enter(aContext); + } + +void TMuxChannelStateConnected::FrameTimeoutHelper(CRfcommMuxChannel& aContext) +/** + Helper function for code re-use in FrameTimeout methods +**/ + { + aContext.iOpenPending=EFalse; + aContext.iClosePending=EFalse; + aContext.iMux.MuxChannelError(EFalse, KErrRfcommFrameResponseTimeout); + } + +void TMuxChannelStateConnected::Disconnect(CRfcommMuxChannel& aContext) + /** + The other end has disconnected the L2CAP link + **/ + { + aContext.DequeIdleTimer(); + SetState(aContext,CMuxChannelStateFactory::EClosed); + aContext.iState->Enter(aContext); + aContext.iMux.MuxChannelDown(); + aContext.iMux.MuxChannelClosed(); + } + +void TMuxChannelStateConnected::Error(CRfcommMuxChannel& aContext, TInt aError, + TUint aOperationMask) + /** + Summ'ts up at t'mill! + + Check the op mask - if it's only Ioctl then we ignore it, else + something bad is wrong. + + Clear the link timer as well in case we are in LinkUp. + **/ + { + if(aOperationMask != MSocketNotify::EErrorIoctl) + { + aContext.DequeIdleTimer(); + SetState(aContext,CMuxChannelStateFactory::EError); + aContext.iMux.MuxChannelError(ETrue, aError); + aContext.iState->Enter(aContext); + } + } + +/* + Now the meaty ones... + + LinkUp - the l2cap link is up, and maybe we need to bring up the mux channel... +*/ + +TMuxChannelStateLinkUp::TMuxChannelStateLinkUp(CMuxChannelStateFactory& aFactory) + : TMuxChannelStateConnected(aFactory) + { + STATENAME("LinkUp"); + } + +void TMuxChannelStateLinkUp::Enter(CRfcommMuxChannel& aContext, TBool aDisconnectingIdleTimer) + /** + Entered the state + + Start to bring up the link if a Open is pending, or bring it + down if a Close is pending, or just remain link up and kick + off a timer if neither is true. + + Find out the L2CAP MTU as this is now important. + **/ + { + __ASSERT_DEBUG(!(aContext.iOpenPending && aContext.iClosePending), + PanicInState(ERfcommMuxChannelOpeningAndClosing)); + + aContext.DequeIdleTimer(); //ENTERED NEW STATE => NOT IDLE + + TPckgBuf buf; + + // Find out what the max data size is. + aContext.iSAP.GetOption(KSolBtL2CAP, KL2CAPInboundMTU, buf); + TInt t = buf(); + aContext.iSAP.GetOption(KSolBtL2CAP, KL2CAPOutboundMTUForBestPerformance, buf); + // Max size is the lower of incoming and outgoing + // L2CAP MTUs since it's symmetrical + aContext.iMaxDataSize=Min(buf(), t); + + if(aContext.iOpenPending) + { + aContext.iOpenPending=EFalse; + TInt err=aContext.TransmitSABM(); + if(err != KErrNone) + { + SetState(aContext,CMuxChannelStateFactory::EError); + aContext.iMux.MuxChannelError(ETrue, err); + } + else + { + aContext.iState= + iFactory.GetState(CMuxChannelStateFactory::EWaitForSABMResp); + } + aContext.iState->Enter(aContext); + } + else if(aContext.iClosePending) + { + SetState(aContext,CMuxChannelStateFactory::EClosing); + aContext.iSAP.Shutdown(CServProviderBase::ENormal); + aContext.iState->Enter(aContext); + } + else + { + if(aDisconnectingIdleTimer) + { + // Timeout (nominally 1 sec) to prevent a DoS attack from an out-of-sequence PN + aContext.QueIdleTimer(KRfcommMuxDisconnectingChannelTimeout); + } + else + { + // We start the idle timer (nominally 10 secs). This is a countdown + // which will be cancelled when we receive a SABM from the remote. + // Otherwise the connection is closed to avoid sapping our battery. + aContext.QueIdleTimer(KRfcommMuxConnectingChannelTimeout); + } + } + } + +void TMuxChannelStateLinkUp::Close(CRfcommMuxChannel& aContext) + /** + We've been explicitly asked to close down. + + We should now bring down the link. We can signal that the mux + channel is closed immediately. + **/ + { + aContext.iMux.MuxChannelDown(); + SetState(aContext,CMuxChannelStateFactory::EClosing); + aContext.iSAP.Shutdown(CServProviderBase::ENormal); + aContext.iState->Enter(aContext); + } + +void TMuxChannelStateLinkUp::Open(CRfcommMuxChannel& aContext) + /** + Time to bring up that channel + **/ + { + aContext.DequeIdleTimer(); + aContext.iOpenPending=EFalse; + TInt err=aContext.TransmitSABM(); + if(err != KErrNone) + { + SetState(aContext,CMuxChannelStateFactory::EError); + aContext.iMux.MuxChannelError(ETrue, err); + } + else + { + SetState(aContext,CMuxChannelStateFactory::EWaitForSABMResp); + } + aContext.iState->Enter(aContext); + } + +void TMuxChannelStateLinkUp::SABM(CRfcommMuxChannel& aContext) + /** + We've received a SABM on DLCI 0. + + Respond with a UA, and move to the Open state. + **/ + { + TInt err=aContext.TransmitUA(); + if(err!=KErrNone) + { + // We're unable to respond, so we need to error stuff + SetState(aContext,CMuxChannelStateFactory::EError); + aContext.iMux.MuxChannelError(ETrue, err); + } + else + { + aContext.DequeIdleTimer(); + SetState(aContext,CMuxChannelStateFactory::EOpen); + } + aContext.iState->Enter(aContext); + } + +void TMuxChannelStateLinkUp::DISC(CRfcommMuxChannel& /*aContext*/) + /** + We've received a DISC on DLCI 0. + **/ + { + // Do nothing + } + +void TMuxChannelStateLinkUp::IdleTimeout(CRfcommMuxChannel& aContext) + /** + We've been idle long enough. Bring it down... + **/ + { + LOG(_L("RFCOMM: Shutting down mux channel link")); + SetState(aContext,CMuxChannelStateFactory::EClosing); + aContext.iSAP.Shutdown(CServProviderBase::ENormal); + aContext.iState->Enter(aContext); + } + +void TMuxChannelStateLinkUp::FrameTimeout(CRfcommMuxChannel& aContext, CRfcommFrame* /*aFrm*/) +/** + Needs to be over-ridden as parent 'Connected' state does too much! + + FIXME - Yeah, I know it's horrible but short of re-writing most of the + mux channel states, what else can we do? +**/ + { + // Do the basics... + FrameTimeoutHelper(aContext); + + //... and don't go to ELinkUp 'cos we're already there. + } + +/* + Wait for UA to the SABM +*/ + +TMuxChannelStateWaitForSABMResp::TMuxChannelStateWaitForSABMResp(CMuxChannelStateFactory& aFactory) + : TMuxChannelStateConnected(aFactory) + { + STATENAME("WaitForSABMResp"); + } + +void TMuxChannelStateWaitForSABMResp::UA(CRfcommMuxChannel& aContext) + /** + The mux channel is up, we're done + **/ + { + SetState(aContext,CMuxChannelStateFactory::EOpen); + aContext.iState->Enter(aContext); + } + +void TMuxChannelStateWaitForSABMResp::DM(CRfcommMuxChannel& aContext) + /** + Our SABM has failed. + + It would be a good idea to rip up this muxer now, since the other end + is very unhappy. + **/ + { + SetState(aContext,CMuxChannelStateFactory::EError); + aContext.iMux.MuxChannelError(ETrue, KErrCouldNotConnect); + aContext.iState->Enter(aContext); + } + +void TMuxChannelStateWaitForSABMResp::SABM(CRfcommMuxChannel& aContext) + /** + Our SABMs have passed in the post. + + It's not clear whether the sender of the SABM gets to be + initiator or not, we assume that it's the creator of the L2CAP + channel which gets that honour. Since others may assume + otherwise, the safest thing to do here is to rip up this mux + and wait for it to go round again. + **/ + { + SetState(aContext,CMuxChannelStateFactory::EError); + aContext.iMux.MuxChannelError(ETrue, KErrCouldNotConnect); + aContext.iState->Enter(aContext); + } + +void TMuxChannelStateWaitForSABMResp::Close(CRfcommMuxChannel& aContext) + { + // We need to give up...pretend we never sent the SABM + aContext.iClosePending = ETrue; + aContext.iOpenPending = EFalse; + SetState(aContext,CMuxChannelStateFactory::ELinkUp); + aContext.iState->Enter(aContext); + } + +void TMuxChannelStateWaitForSABMResp::DISC(CRfcommMuxChannel& aContext) + /** + DISC received. + **/ + { + SetState(aContext,CMuxChannelStateFactory::EError); + aContext.iMux.MuxChannelError(ETrue, KErrCouldNotConnect); + aContext.iState->Enter(aContext); + } + +/* + Open - all systems are go. +*/ + +TMuxChannelStateOpen::TMuxChannelStateOpen(CMuxChannelStateFactory& aFactory) + : TMuxChannelStateConnected(aFactory) + { + STATENAME("Open"); + } + +void TMuxChannelStateOpen::Enter(CRfcommMuxChannel& aContext, TBool /*aDisconnectingIdleTimer*/) + /** + Entering the Open state. + + Let the mux know, unless we're supposed to be closing. + **/ + { + aContext.iOpenPending=EFalse; // Since we're open now! + if(!aContext.iClosePending) + { + aContext.iMux.MuxChannelUp(); + } + else + { + // We're going down again! + Close(aContext); + } + } + +void TMuxChannelStateOpen::Close(CRfcommMuxChannel& aContext) + /** + Time's up. Go on down. + **/ + { + aContext.iClosePending=ETrue; + SetState(aContext,CMuxChannelStateFactory::ELinkUp); + aContext.iState->Enter(aContext); + } + +/** +A SABM has been received for DLCI 0. +We're already in Open state- the remote may have sent another SABM because we +replied to the first SABM later than they expected. +All we can reasonably do is reply with a UA. Just dropping the SABM might +result in a timeout on the other end. +We need to override the SABM method to avoid the base class implementation's +panic. +*/ +void TMuxChannelStateOpen::SABM(CRfcommMuxChannel& aContext) + { + static_cast(aContext.TransmitUA()); + // We don't care about the error here. + // If the UA succeeded, we're already in the right state so no further + // action is required. + // If the UA failed, it doesn't matter as we're already in the right state + // and the connection already exists from the original SABM. If the remote + // times out the SABM it sent and consequently wants to pull down the + // connection, that's their business. Anyway, there's nothing we can do + // about it. + } + +void TMuxChannelStateOpen::DISC(CRfcommMuxChannel& aContext) + /** + Remote end wants us to shut down + **/ + { + aContext.TransmitUA(); // May fail + SetState(aContext,CMuxChannelStateFactory::ELinkUp); + aContext.iMux.CloseSAPs(); + aContext.iState->Enter(aContext); + } + +TBool TMuxChannelStateOpen::IsOpen(CRfcommMuxChannel& /*aContext*/) + { + return ETrue; + } + + + + +/* + Closing down the channel +*/ + +TMuxChannelStateClosing::TMuxChannelStateClosing(CMuxChannelStateFactory& aFactory) + : TMuxChannelStateConnected(aFactory) + { + STATENAME("Closing"); + } + +void TMuxChannelStateClosing::FrameTimeout(CRfcommMuxChannel& aContext, CRfcommFrame* /*aFrm*/) +/** + Needs to be over-ridden as parent 'Connected' state does too much! + + FIXME - Yeah, I know it's horrible but short of re-writing most of the mux channel + states, what else can we do? +**/ + { + // Do the basics... + FrameTimeoutHelper(aContext); + + //... and don't go to ELinkUp ('cos we're taking the link down!) + } + +void TMuxChannelStateClosing::CanClose(CRfcommMuxChannel& aContext) + /** + The socket is closed down now - we can move back to closed. + + We tear up the muxer at this point, since the creator of the l2cap + link is the one who acts as initiator for RFCOMM dlci assignment. + This is configured on mux creation, so we can't go back from here to + link up without changing it. + **/ + { + SetState(aContext,CMuxChannelStateFactory::EClosed); + aContext.iState->Enter(aContext); + aContext.iMux.MuxChannelClosed(); + } + +TBool TMuxChannelStateClosing::CanAttachSAP() + { + return EFalse; + } +