bluetooth/btstack/rfcomm/rfcommmuxchannel.cpp
changeset 0 29b1cd4cb562
child 23 32ba20339036
--- /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 <bluetooth/logger.h>
+#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<CRfcommMuxChannel*>(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<TInt> 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<void>(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;
+	}
+