bluetooth/btstack/rfcomm/rfcommmuxer.cpp
changeset 0 29b1cd4cb562
child 23 32ba20339036
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btstack/rfcomm/rfcommmuxer.cpp	Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,3388 @@
+// 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:
+// Rfcomm muxer
+// 
+//
+
+#include <bluetooth/logger.h>
+
+#include <bt_sock.h>
+
+#include "rfcommmuxer.h"
+#include "rfcomm.h"
+#include "rfcommutil.h"
+#include "rfcommframe.h"
+#include "rfcommtypes.h"
+#include "rfcommfcs.h"
+#include "rfcommsap.h"
+
+#include "rfcommflow.h"
+#include "rfcommmuxchannel.h"
+#include "AsyncErrorKicker.h"
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_RFCOMM);
+#endif
+
+// Disable int to char warnings
+#ifdef __WINS__
+#pragma warning( disable : 4244 )
+#endif
+
+#ifdef __FLOG_ACTIVE
+_LIT(KCommandText, "command");
+_LIT(KResponseText, "response");
+#endif //__FLOG_ACTIVE
+
+const TUint8 KBitsForNumberInTInt = 32 - 1; // -1 is because we don't count the sign bit
+
+CRfcommMuxer* CRfcommMuxer::NewL(CRfcommProtocol& aProt, CProtocolBase& aL2CAP,
+								CMuxChannelStateFactory& aFactory)
+	/**
+	Factory function for CRfcommMuxer - called when local device initiates the connection.
+
+	Note the CProtocolBase passed in so that the muxer can create its own L2CAP SAP
+	**/
+	{
+	CRfcommMuxer* mux= new (ELeave) CRfcommMuxer(aProt,KInitiationDirectionOutgoing);
+	CleanupStack::PushL(mux);
+	mux->ConstructL(aFactory, aL2CAP);
+	CleanupStack::Pop();
+	return mux;
+	}
+
+CRfcommMuxer* CRfcommMuxer::NewL(CRfcommProtocol& aProt, CServProviderBase* aSAP,
+								CMuxChannelStateFactory& aFactory)
+	/**
+	Factory function for CRfcommMuxer - called when remote device initiates the connection.
+
+	Note the CServProviderBase passed in, which becomes the L2CAP SAP used by this muxer.
+
+	@param aSAP		 This is a connected L2CAP sap
+	@param aFactory  The Mux channel state factory
+	@return          A new Muxer in the right state
+	**/
+	{
+	CRfcommMuxer* mux= new (ELeave) CRfcommMuxer(aProt,KInitiationDirectionIncoming);
+	CleanupStack::PushL(mux);
+	mux->ConstructL(aFactory, aSAP);
+	CleanupStack::Pop();
+	return mux;
+	}
+
+
+CRfcommMuxer::CRfcommMuxer(CRfcommProtocol& aProt, TInitiationDirection aDirection)
+	: iSAPs(_FOFF(CRfcommSAP, iLink)),
+	  iOutboundQ(_FOFF(CRfcommFrame, iLink)),
+	  iResponseQ(_FOFF(CRfcommFrame, iLink)),
+	  iOutboundQLength(0),
+	  iProtocol(aProt),
+	  iDirection(aDirection),
+	  iMuxIdleTimeout(KRfcommMuxIdleTimeoutOpening),	  
+	  iCanSend(ETrue),
+	  iBlockedSAPs(_FOFF(CRfcommSAP, iLink)),
+	  iDataFramesSent(0),
+	  iL2CAPSendBlocked(EFalse),
+	  iTriedCBFC(EFalse),
+	  iCurrentCredit(0),
+	  iInitialTxCredit(0),
+	  iFlowStrategy(0)
+	{
+	TCallBack cb(IdleTimerExpired, this);
+	iIdleTimerEntry.Set(cb);
+	}
+
+void CRfcommMuxer::ConstructL(CMuxChannelStateFactory& aFactory, CProtocolBase& aL2CAP)
+	{ 
+	// Create a SAP for this to use
+	iBoundSAP=aL2CAP.NewSAPL(KSockSeqPacket);
+	iMuxChannel = new (ELeave) CRfcommMuxChannel(aFactory, *this, *iBoundSAP, CMuxChannelStateFactory::EClosed);
+
+	//	We now have a bound L2Cap SAP.
+	//  Use it to setup L2Cap config for RFComm.
+	TL2CapConfigPkg configPkg;
+	iProtocol.SetL2CapConfig(configPkg);
+	User::LeaveIfError(iBoundSAP->SetOption(KSolBtL2CAP, KL2CAPUpdateChannelConfig, configPkg));
+
+	CommonConstructL();
+	}
+
+void CRfcommMuxer::ConstructL(CMuxChannelStateFactory& aFactory, CServProviderBase* aSAP)
+	{ 
+	// SAP has been provided for this to use, so start it
+	__ASSERT_DEBUG(aSAP!=NULL, Panic(ERfcommNullSAPForMuxer));
+	iBoundSAP=aSAP;
+	iBoundSAP->Start();
+	TBTSockAddr btAddr;
+	aSAP->RemName(btAddr);
+	iRemoteAddr=btAddr.BTAddr();
+	iMuxChannel = new (ELeave) CRfcommMuxChannel(aFactory, *this, *iBoundSAP, CMuxChannelStateFactory::ELinkUp);
+	CommonConstructL();
+	}
+
+void CRfcommMuxer::CommonConstructL()
+	{
+	iBoundSAP->SetNotify(this);
+	TCallBack cb(TryToSendCallbackStatic,this);
+	iSendCallback=new (ELeave) CAsyncCallBack(cb, ECAsyncImmediatePriority);
+	iNextPacket=HBufC8::NewL(KRfcommDefaultMTU);  // This will do for now
+#ifdef NO_CBFC
+	iFlowStrategy = TRfcommFlowStrategyNonCreditBased::NewL(*this);
+#else
+	if(iProtocol.CBFCDisallowed())
+		{
+		SetFlowType(CRfcommFlowStrategyFactory::EFlowNonCreditBased);
+		iTriedCBFC = ETrue;
+		}
+	else
+		{
+		SetFlowType(CRfcommFlowStrategyFactory::EFlowInitial);
+		}
+#endif
+	}
+
+CRfcommMuxer::~CRfcommMuxer()
+	{
+	LOG(_L("CRfcommMuxer::~CRfcommMuxer"));
+
+	DequeIdleTimer();
+	DeleteQueuedFrames();
+	
+	// remove the references to this muxer in any SAPs it is/was linked to
+	TDblQueIter<CRfcommSAP> iter(iSAPs);
+	CRfcommSAP* sap;
+	while(iter)
+		{
+		sap=iter++;
+		if (sap)
+			{
+			sap->iMux = NULL;
+			sap->iLink.Deque();
+			}
+		}
+
+	
+	
+	delete iSendCallback;
+	delete iBoundSAP;
+	delete iNextPacket;
+	delete iMuxChannel;
+	}
+
+/***************************************************************************/
+/*
+  Commands from the protocol.
+  
+*/
+  
+
+void CRfcommMuxer::Bind(TBTDevAddr& aAddr)
+	/**
+	   Bind this mux to the given remote address
+	   
+	   Calling this function implies that this end is to be the
+	   initiator (direction bit = 1).  The bound sap will be used to
+	   create an L2CAP link to the remote end over which the RFCOMM
+	   frames will flow.  The muxchannel will then run this channel.
+	**/
+	{
+	iRemoteAddr=aAddr;
+	iMuxChannel->SetAddress(aAddr);
+	}
+
+void CRfcommMuxer::AddSAP(CRfcommSAP& aSAP)
+	/**
+	   Adds a sap to the Q for this mux.
+	**/
+	{
+	DequeIdleTimer();
+	iSAPs.AddFirst(aSAP);
+	if(iMuxChannel->IsOpen())
+		aSAP.MuxUp();
+	else
+		iMuxChannel->Open();  // Eventually calls back
+	}
+
+void CRfcommMuxer::DetachSAP(CRfcommSAP& aSAP)
+	/**
+	   Detach this sap.
+
+	   If it's the last one, shut down.  We clean out the outbound and
+	   response queues as we no longer want these frames since the sap
+	   has gone.
+	**/
+	{
+	aSAP.iLink.Deque();
+	aSAP.iMux=0;
+	
+	ClearOutboundQueue(aSAP);
+	ClearResponseQueue(aSAP);
+	CheckForIdle(ETrue);
+	}
+
+void CRfcommMuxer::GetInboundServerChannelsInUse(TFixedArray<TBool, KMaxRfcommServerChannel>& aChannelInUse)
+	/**
+	   Identify inbound server channels in use for connected SAPs.
+	   
+	   aChannelInUse is an array indexed by (server channel - 1) and on return 
+	   aChannelInUse will be updated with any inbound server channels that
+	   are currently in use by connected SAPs.
+	**/
+	{
+	TDblQueIter<CRfcommSAP> iter(iSAPs);
+	CRfcommSAP* sap;
+
+	// Only interested in cloned SAPs as this identifies if the SAP is for 
+	// an inbound connection
+	while(iter)
+		{
+		sap=iter++;		
+		if (sap->IsCloned())
+			{
+			aChannelInUse[sap->ServerChannel() - 1] = ETrue;
+			}
+		}
+
+	iter=iBlockedSAPs;
+	while(iter)
+		{
+		sap=iter++;
+		if (sap->IsCloned())
+			{
+			aChannelInUse[sap->ServerChannel() - 1] = ETrue;
+			}
+		}	
+	}
+
+/****************************************************************************/
+/*
+  Notifications from the MSocketNotify interface
+	
+*/
+
+void CRfcommMuxer::NewData(TUint aCount)
+	/**
+	   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 SAP.
+
+	   Frames never span a L2CAP packet, and are one per packet.
+
+	   This assumes a packet interface from L2CAP
+
+	   @param aCount  Number of new packets waiting
+	**/
+	{
+ 	if (aCount == KNewDataEndofData)
+ 		{
+		LOG(_L("RFCOMM: L2CAP signalled EndOfData"));
+ 		Disconnect();
+ 		}
+	else
+		{
+		LOG1(_L("RFCOMM: New data, count %d"), aCount);
+		//BLOG: KBlogNewData
+		iPacketsWaiting+=aCount;
+		while(CanProcessNewData() && iPacketsWaiting)
+			{
+			// Get a packet into the buffer
+			TPtr8 data=iNextPacket->Des();
+			data.SetMax();
+			iBoundSAP->GetData(data, 0);
+			// process the new data
+			ProcessFrame();
+			iPacketsWaiting--;
+			}
+		}
+	}
+
+
+void CRfcommMuxer::CanSend()
+	/**
+	   Notification that we can now send data to the lower layer.
+	**/
+	{
+	L2CAPBlocked(EFalse);
+	TryToSend();
+	}
+
+//CBFC
+CRfcommFlowStrategyFactory::TFlowStrategies CRfcommMuxer::FlowType()
+	{
+	return iFlowStrategy->FlowType();
+	}
+
+#ifdef NO_CBFC
+TBool CRfcommMuxer::FlowType(CRfcommFlowStrategyFactory::TFlowStrategies /*aFlow*/)
+	{
+	return EFalse;
+	}
+#else
+TBool CRfcommMuxer::SetFlowType(CRfcommFlowStrategyFactory::TFlowStrategies aFlow)
+	{
+	if(iTriedCBFC)
+		{
+		LOG(_L("RFCOMM: Requesting new flow strategy again: DISALLOWED"));
+		return EFalse;
+		}
+
+	switch(aFlow)
+		{
+	case CRfcommFlowStrategyFactory::EFlowCreditBased:
+		LOG(_L("RFCOMM: Requesting CBFC at SAP level"));
+		iTriedCBFC = ETrue;				// stop the test being done again
+		iFlowStrategy = &(iProtocol.FlowStrategyFactory()->GetFlowStrategy(aFlow));
+		break;
+	case CRfcommFlowStrategyFactory::EFlowNonCreditBased:
+		LOG(_L("RFCOMM: Not requesting CBFC at SAP level"));
+		iTriedCBFC = ETrue;				// stop the test being done again
+		iFlowStrategy = &(iProtocol.FlowStrategyFactory()->GetFlowStrategy(aFlow));
+		break;
+	default:
+		iFlowStrategy = &(iProtocol.FlowStrategyFactory()->GetFlowStrategy(CRfcommFlowStrategyFactory::EFlowInitial));
+		break;
+		}
+	return ETrue;
+	}
+#endif
+
+void CRfcommMuxer::DisallowCBFC()
+	{
+	iCBFCDisallowed = ETrue;
+	}
+
+void CRfcommMuxer::AllowCBFC()
+	{
+	iCBFCDisallowed = EFalse;
+	}
+
+TRfcommFlowStrategy* CRfcommMuxer::FlowStrategy()
+	{
+	return iFlowStrategy;
+	}
+
+
+
+void CRfcommMuxer::ConnectComplete(const TDesC8& /*aConnectData*/)
+	/**
+	   Version with connection data.
+
+	   Ignore the data (since L2CAP should never provide this!)
+	**/
+	{
+	ConnectComplete();
+	}
+
+void CRfcommMuxer::ConnectComplete(CServProviderBase& /*aSSP*/)
+	/**
+	   Incoming connection completed on listen socket.
+	**/
+	{
+	Panic(ERfcommIncomingNotSupported);
+	}
+
+void CRfcommMuxer::ConnectComplete(CServProviderBase& /*aSSP*/,const TDesC8& /*aConnectData*/)
+	{
+	Panic(ERfcommIncomingNotSupported);
+	}
+
+void CRfcommMuxer::CanClose(TDelete /*aDelete*/)
+	/**
+	   We must have asked the socket to shutdown, which means we're dying.
+	**/
+	{
+	LOG1(_L("RFCOMM: CanClose from L2CAP for mux %08x"), this);
+	iMuxChannel->CanClose();
+	}
+
+void CRfcommMuxer::CanClose(const TDesC8& /*aDisconnectData*/,TDelete /*aDelete*/)
+	{
+	Panic(ERfcommDisconnectDataNotSupported);
+	}
+
+void CRfcommMuxer::Error(TInt aError,TUint aOperationMask)
+	/**
+	   Something's gone wrong.
+
+	   We get told whether it's just this operation or all that are
+	   affected, but we'll just assume that all of them are bust for
+	   now.
+	**/
+	{
+	LOG1(_L("RFCOMM: Error on L2CAP sap %d"), aError);
+	if(aOperationMask & MSocketNotify::EErrorIoctl)
+		{
+		// This was an ioctl problem, so pass up to the saps
+		PropagateIoctlCompletion(aError, iIoctlLevel, iIoctlName, NULL);
+		iIoctlName=0;
+		iIoctlLevel=0;
+		}
+
+	// Pass this on to the Mux channel as well
+	iMuxChannel->Error(aError, aOperationMask);
+	}
+
+void CRfcommMuxer::MuxChannelError(TBool aFatal, TInt aError)
+	/**
+	   Something's gone wrong on the mux channel
+
+	   If this is a fatal error it means we need a new muxchannel,
+	   else we can live with it.  Either way we need to error all the
+	   existing saps.
+	**/
+	{
+	LOG1(_L("RFCOMM: Error on Mux %d"), aError)
+    
+	TDblQueIter<CRfcommSAP> iter(iSAPs);
+	CRfcommSAP* sap;
+
+	// Erroring the saps will remove them from us when we signal a 
+	// general error
+	while(iter)
+		{
+		sap=iter++;
+		sap->Error(aError, CRfcommSAP::EErrorGeneral);
+		}
+	iter=iBlockedSAPs;
+	while(iter)
+		{
+		sap=iter++;
+		sap->Error(aError, CRfcommSAP::EErrorGeneral);
+		}
+
+	if(aFatal)
+		{
+		// The channel is now useless, and so is the mux
+		// Set the address to 0 to stop any more saps attaching
+		DeleteQueuedFrames();
+		iRemoteAddr=TInt64(0);
+		return;
+		}
+	CheckForIdle(ETrue);
+	}
+
+const TBTDevAddr& CRfcommMuxer::RemoteBTAddr() const
+	{
+	return iRemoteAddr;
+	}
+
+void CRfcommMuxer::MuxChannelDown()
+	/**
+	   The mux channel has gone down.
+	   Pass this on to the saps	
+	**/
+	{
+	CloseSAPs();
+	DeleteQueuedFrames(); // since we can't send or receive any more
+	CheckForIdle(ETrue);
+	}
+
+void CRfcommMuxer::CloseSAPs()
+	{
+	TDblQueIter<CRfcommSAP> iter(iSAPs);
+	CRfcommSAP* sap;
+
+	while(iter)
+		{
+		sap=iter++;
+		sap->LinkDown();
+		}
+	iter=iBlockedSAPs;
+	while(iter)
+		{
+		sap=iter++;
+		sap->LinkDown();
+		}
+	}
+
+void CRfcommMuxer::MuxChannelUp()
+	/**
+	   The mux channel has come up.
+
+	   We need to allocate the buffer for incoming packets.  Note this
+	   may not be the same as the RFCOMM MTU since we can't guarantee
+	   that the other end won't send us omore in an L2CAP packet.
+	**/
+	{
+
+	TPckgBuf<TInt> buf;
+	// Find out what the max data size is.
+	iBoundSAP->GetOption(KSolBtL2CAP, KL2CAPInboundMTU, buf);
+	TInt incoming = buf();
+
+	TRAPD(err, iNextPacket=iNextPacket->ReAllocL(incoming));
+	if(err != KErrNone)
+		{
+		// We can't do anything here, so close & error
+		iMuxChannel->Close();
+		MuxChannelError(ETrue, err);
+		}
+	else
+		{
+		TDblQueIter<CRfcommSAP> iter(iSAPs);
+		CRfcommSAP* sap;
+		
+		while(iter)
+			{
+			sap=iter++;
+			sap->MuxUp();
+			}
+		iter=iBlockedSAPs;
+		while(iter)
+			{
+			sap=iter++;
+			sap->MuxUp();
+			}
+		}
+	}
+ 
+void CRfcommMuxer::Disconnect()
+	/**
+	   The L2CAP link has disconnected.
+
+	   
+	**/
+	{
+	iMuxChannel->Disconnect();
+	}
+
+void CRfcommMuxer::Disconnect(TDesC8& /*aDisconnectData*/)
+	{
+	Disconnect();
+	}
+
+void CRfcommMuxer::IoctlComplete(TDesC8* aBuf)
+	/**
+	   An ioctl has completed.
+
+	   Broadcast it to all the saps that might be interested
+	**/
+	{
+	PropagateIoctlCompletion(KErrNone, iIoctlLevel, iIoctlName, aBuf);
+	iIoctlName=0;
+	iIoctlLevel=0;
+	}
+
+void CRfcommMuxer::PropagateIoctlCompletion(TInt aError, TUint aIoctlLevel, TUint aIoctlName, TDesC8* aBuf)
+	{
+	TDblQueIter<CRfcommSAP> iter(iSAPs);
+	CRfcommSAP* sap;
+	while(iter)
+		{
+		sap=iter++;
+		sap->IoctlComplete(aError, aIoctlLevel, aIoctlName, aBuf);
+		}
+	iter=iBlockedSAPs;
+	while(iter)
+		{
+		sap=iter++;
+		sap->IoctlComplete(aError, aIoctlLevel, aIoctlName, aBuf);
+		}
+	}
+
+void CRfcommMuxer::ConnectComplete()
+	/**
+	   Signal from bound sap that connection has occurred.
+	   
+	   Part of the MSocketNotify interface. This is called when L2CAP
+	   has brought up the lower layer link to the remote device.
+	   Pass this through to the muxchannel.
+	**/
+	{
+	FTRACE(TSockAddr addr;
+		   iBoundSAP->RemName(addr);
+		   LOG1(_L("RFCOMM: Connect complete rcid %d"),
+		   	  addr.Port());
+		   iBoundSAP->LocalName(addr);
+		   LOG1(_L("RFCOMM: Connect complete lcid %d"),
+		   	  addr.Port());
+		);
+	iMuxChannel->ConnectComplete();
+	}
+
+/**********************************************************************/
+/*
+  Commands from the SAP.
+*/
+
+TUint8 CRfcommMuxer::MakeDLCI(TUint8 aServerChannel, TUint8 aDirectionBit)
+	{
+	TUint8 addr = aServerChannel;
+	addr <<= 1;
+	addr |= aDirectionBit;
+	return addr;
+	}
+
+TUint8 CRfcommMuxer::MakeOutboundDLCI(TUint8 aServerChannel)
+	/**
+	 Given a server channel, add the direction bit for an outbound connection
+	 **/
+	{
+	// Use the inverse of our direction bit
+	return MakeDLCI(aServerChannel, ~iDirection & KDirectionMask);
+	}
+	
+TUint8 CRfcommMuxer::MakeInboundDLCI(TUint8 aServerChannel)
+	/**
+	 Given a server channel, add the direction bit for an inbound connection
+	 **/
+	{
+	return MakeDLCI(aServerChannel, iDirection);
+	}
+	
+TUint8 CRfcommMuxer::MakeServerChannel(TUint8 aDLCI)
+	/**
+	Given a DLCI, turn that back into a server channel
+	**/
+	{
+	return aDLCI>>1;	//	Lose the bottom (direction) bit.
+	}
+
+TInt CRfcommMuxer::GetMaxDataSize() const
+	/**
+	   Returns the current max data size allowed
+	**/
+	{
+	return iMuxChannel->MaxDataSize();
+	}
+
+
+void CRfcommMuxer::Donate(CRfcommSAP& aSAP, TUint8 aCredit)
+	/**
+	   Send an empty UIH data frame, cos the other end has been caught short
+	**/
+	{
+	//Try to find another "Donate" frame in the outbound Q and attach to that...
+	TDblQueIter<CRfcommFrame> iter(iOutboundQ);
+	CRfcommFrame* frmInQ;
+	
+	while(iter)
+		{
+		frmInQ=iter;
+ 		if(frmInQ->Ctrl()==KUIHCBFCCtrlField && !(frmInQ->DataLength()))
+ 		    {
+ 		    if (frmInQ->SAP() && 
+ 		       (frmInQ->SAP()->RemoteAddress() == aSAP.RemoteAddress() && 
+ 		       (frmInQ->SAP()->DLCI()) == aSAP.DLCI()))
+ 		        {
+                LOG1(_L("RFCOMM: Old credit in donate frame: %d"), frmInQ->Credit());
+                TUint8 revisedDonation = aCredit+frmInQ->Credit();
+                frmInQ->SetCredit(revisedDonation);
+                LOG1(_L("RFCOMM: New credit in donate frame: %d"), frmInQ->Credit());
+                break;
+ 			}
+		}
+		iter++;
+		}
+
+	if(!iter)
+		//If no other "Donate" frame in the outbound Q.....
+		{
+		CRfcommDataFrame* frm=0;
+		
+		frm=NewDataFrame(aSAP.DLCI(), 0, aCredit, &aSAP);
+		if(!frm)
+			{
+			LOG1(_L("RFCOMM: OOM when writing for SAP %08x, queueing SAP Error callback"), &aSAP);
+			aSAP.iErrorKicker->SetError(KErrNoMemory, CRfcommSAP::EErrorOperation);
+			aSAP.iErrorKicker->Call();
+			}
+		if(!frm)
+			{
+			SetSendBlocked(aSAP, ETrue);
+			return;
+			}
+		
+		EnqueFrame(frm);
+		}
+
+	FlowStrategy()->ReviseDonatedCredits(aSAP, aCredit); //Rx credit
+	}
+
+TInt CRfcommMuxer::Write(CRfcommSAP& aSAP, TUint8 aCredit, const TDesC8& aData)
+	/**
+	   Write some data.
+
+	   We attempt to create a datapacket on the Q for this packet.  If
+	   this fails or the data Q is too long then we return less than
+	   the length of the data.  In which case we mark the SAP as
+	   needing a CanSend, and provide one at a suitable later date.
+	**/
+	{
+	__ASSERT_DEBUG(aData.Length() <= iMuxChannel->MaxDataSize(), Panic(ERfcommDataTooLong));
+	CRfcommDataFrame* frm=0;
+
+	if(iOutboundQLength < KMaxOutboundQLength)
+		{
+		frm=NewDataFrame(aSAP.DLCI(), aData.Length(), aCredit, &aSAP);
+
+		if(!frm)
+			{
+			LOG1(_L("RFCOMM: OOM when writing for SAP %08x, queueing SAP Error callback"), &aSAP);
+			aSAP.iErrorKicker->SetError(KErrNoMemory, CRfcommSAP::EErrorOperation);
+			aSAP.iErrorKicker->Call();
+			}
+		}
+	
+
+	if(!frm)
+		{
+		SetSendBlocked(aSAP, ETrue);
+		return 0;
+		}
+	if(!aData.Length())
+		{
+		LOG(_L("RFCOMM: Write has sent ZERO length data."));
+		}
+
+	frm->PutData(aData);
+	LOG1(_L("RFCOMM: Writing 0x%x"),/*frm->Data()*/aData[0]);
+	//BLOG: (KBlogWriteData, aData[0])
+	
+	FlowStrategy()->ReviseTransmittedCredits(aSAP); //TxCredit
+	FlowStrategy()->ReviseDonatedCredits(aSAP, aCredit); //Rx credit
+	EnqueFrame(frm);
+	return aData.Length();
+	}
+
+TInt CRfcommMuxer::Ioctl(TUint aLevel, TUint aName, TDes8* aOption)
+	/**
+	   A request to do an ioctl onto our lower level sap.
+	   
+	   If one's already in progress, simply return KErrInUse
+	**/
+	{
+	if(iIoctlLevel)
+		{
+		return KErrInUse;
+		}
+	else
+		{
+		iIoctlLevel=aLevel;
+		iIoctlName=aName;
+		iBoundSAP->Ioctl(aLevel, aName, aOption);
+		}
+	return KErrNone;
+	}
+
+void CRfcommMuxer::CancelIoctl(TUint aLevel, TUint aName)
+	/**
+	One of the RFCOMM SAPs has been asked to cancel an Ioctl which
+	was being run at the L2CAP level.
+	**/
+	{
+	if(aLevel==iIoctlLevel && iIoctlName==aName)
+		{
+		iBoundSAP->CancelIoctl(aLevel, aName);
+		iIoctlLevel=0;
+		iIoctlName=0;
+		}
+	}
+
+TInt CRfcommMuxer::SetOption(TUint aLevel, TUint aName, const TDesC8 &aOption)
+	// Handle SetOption passed down from SAP
+	{
+	return iBoundSAP->SetOption(aLevel, aName, aOption);
+	}
+
+TInt CRfcommMuxer::GetOption(TUint aLevel, TUint aName, TDes8 &aOption)
+	// Handle SetOption passed down from SAP
+	{
+	return iBoundSAP->GetOption(aLevel, aName, aOption);
+	}
+		
+void CRfcommMuxer::SetCanHandleData(CRfcommSAP& aSAP, TBool aCanReceive)
+	/**
+	   Inform the muxer whether this sap can handle more data arriving.
+
+	   The mux uses this information to see if it is safe to read more
+	   data up from L2CAP.
+	   
+	   @param aCanReceive False if this sap can't handle any more
+	**/
+	{
+	LOG2(_L("RFCOMM: CanHandleData sap %08x, State %d"),
+				  &aSAP, aCanReceive);
+		
+	//FC
+	// Update the bitmask that controls whether we will take new data from L2CAP
+	SetNoFreeSpace(aSAP.DLCI(), !aCanReceive);
+	
+	if(CanProcessNewData() && iPacketsWaiting)
+		{
+		NewData(0);  // Tickle ourselves
+		}
+	}
+
+TInt CRfcommMuxer::SendSABM(CRfcommSAP& aSAP)
+	/**
+	   Send a SABM for this SAP
+	**/
+	{
+	LOG2(_L("RFCOMM: SendSABM for SAP %08x, Addr %d"), &aSAP, aSAP.DLCI());
+	return(TransmitSABM(aSAP.DLCI(), &aSAP));
+	}
+
+TInt CRfcommMuxer::SendMSC(CRfcommSAP& aSAP, TUint8 aFlags)
+	/**
+	   Send a MSC command for this SAP.
+	**/
+	{
+	//FC
+	LOG1(_L("RFCOMM: Sending MSC command for sap %08x"), &aSAP);
+	return TransmitMSC(aSAP.DLCI(), ETrue, aFlags, &aSAP);
+	}
+
+TInt CRfcommMuxer::SendMSCRsp(CRfcommSAP& aSAP, TUint8 aFlags)
+	/**
+	   Send a MSC response for this SAP.
+	**/
+	{
+	LOG1(_L("RFCOMM: Sending MSC response for sap %08x"), &aSAP);
+	return TransmitMSC(aSAP.DLCI(), EFalse, aFlags, &aSAP);
+	}
+
+TInt CRfcommMuxer::SendRLS(CRfcommSAP& aSAP, TUint8 aStatus)
+	/**
+	   Send a Remote Line Status command for this SAP.
+	**/
+	{
+	LOG1(_L("RFCOMM: Sending RLS cmd for sap %08x"), &aSAP);
+	return TransmitRLS(ETrue, aSAP.DLCI(), aStatus, &aSAP);
+	}
+
+TInt CRfcommMuxer::SendRLSRsp(CRfcommSAP& aSAP, TUint8 aStatus)
+	/**
+	   Send a Remote Line Status response for this SAP.
+	**/
+	{
+	LOG1(_L("RFCOMM: Sending RLS resp for sap %08x"), &aSAP);
+	return TransmitRLS(EFalse, aSAP.DLCI(), aStatus, &aSAP);
+	}
+
+TInt CRfcommMuxer::SendRPN(CRfcommSAP& aSAP, TBool aCommand, TUint8 aLength, 
+						   const TRfcommRPNTransaction& aRPNTransaction)
+	/**
+	   Send a RPN frame to L2CAP for this SAP.
+	**/
+	{
+	return TransmitRPN(aSAP.DLCI(), aCommand, aLength, aRPNTransaction, &aSAP);
+	}
+
+TInt CRfcommMuxer::SendUA(CRfcommSAP& aSAP)
+	/**
+	   Send a UA frame on behalf of the SAP
+	**/
+	{
+	return TransmitUA(aSAP.DLCI(), &aSAP);
+	}
+
+TInt CRfcommMuxer::SendUA(TUint8 aDLCI)
+	/**
+	   Send a UA frame on for the DLC
+	**/
+	{
+	return TransmitUA(aDLCI);
+	}
+
+TInt CRfcommMuxer::SendPN(CRfcommSAP& aSAP, const TRfcommPortParams& aParams)
+	/**
+	   Send a PN frame on behalf of the SAP
+	   This function takes the proposed maximum frame size parameter from the SAP itself.
+	**/
+	{
+	return TransmitPN(aSAP.DLCI(), ETrue, aParams, &aSAP);
+	}
+
+TInt CRfcommMuxer::SendPNResponse(CRfcommSAP& aSAP, const TRfcommPortParams& aParams)
+	/**
+		Send a PN Response frame on behalf of the SAP
+	**/
+	{
+	return TransmitPN(aSAP.DLCI(), EFalse, aParams, &aSAP);
+	}
+
+void CRfcommMuxer::SendDISC(CRfcommSAP& aSAP)
+	/**
+	   Disconnect this sap and associated DLC.
+	**/
+	{
+	TransmitDISC(aSAP.DLCI(), &aSAP);
+	}
+
+void CRfcommMuxer::SendDISC(TUint8 aDLCI)
+	/**
+	   Disconnect the associated DLC.
+	**/
+	{
+	TransmitDISC(aDLCI);
+	}
+
+void CRfcommMuxer::SendDM(TUint8 aDLCI)
+	{
+	TransmitDM(aDLCI,ETrue);
+	}
+
+TInt CRfcommMuxer::SendFCon()
+	/**
+	Sends a FCon command to the peer RFCOMM entity.
+
+	Used only for internal testing purposes.
+	**/
+	{
+	//FC
+	return TransmitFCon(ETrue,NULL);
+	}
+
+TInt CRfcommMuxer::SendFCoff()
+	/**
+	Sends a FCoff command to the peer RFCOMM entity.
+
+	Used only for internal testing purposes.
+	**/
+	{
+	//FC
+	return TransmitFCoff(ETrue,NULL);
+	}
+
+/*
+  Timeout from the frames
+*/
+
+void CRfcommMuxer::FrameResponseTimeout(CRfcommFrame* aFrm)
+	/*
+	  Called when a frame that needs a response times out
+
+	*/
+	{
+	//	I could code the next bit more efficiently, but don't like obfuscation!
+	TBool done=EFalse;
+	if(aFrm->SAP()!=NULL)
+		done=aFrm->SAP()->HandleFrameResponseTimeout();
+
+	if(!done)
+		iMuxChannel->FrameTimeout(aFrm);
+	//	The frame will be cleaned up either by the mux error handling or by the SAP 
+	//	going into the closed or error state.
+	}
+
+void CRfcommMuxer::ClearOutboundQueue(CRfcommSAP& aSAP)
+	/**
+	   Clear any frames on the outbound queue that point to this SAP.
+
+	   This prevents any frame timeout getting back to a deleted SAP.
+	   If a SAP wants to send a frame to the remote end after its
+	   death, it must make sure that the iSAP member of the frame does
+	   not point to it (ie is null).
+	**/
+	{
+	TDblQueIter<CRfcommFrame> iter(iOutboundQ);
+	CRfcommFrame* frm;
+	
+	while(iter)
+		{
+		frm=iter++;
+		if(frm->SAP() == &aSAP)
+			{
+			--iOutboundQLength;
+			delete frm;
+			}
+		}
+	}
+
+void CRfcommMuxer::ClearResponseQueue(CRfcommSAP& aSAP)	
+	/**
+	Clear the response queue of all frames associated with the SAP.
+
+	The association is based on the frame pointing to the sap, or
+	being for a DLCI which matches that of the SAP.  Frames on DLCI 0
+	are not matched by the DLCI matching.
+	
+	This is generally called in response to a SAP being closed or
+	entering an error state.  Clearly since the sap is no longer
+	around, it makes no sense to keep the frames that are awaiting a
+	response for timeout purposes.
+	**/
+	{
+	TDblQueIter<CRfcommFrame> iter(iResponseQ);
+	CRfcommFrame* frm=NULL;
+	TUint8 frmDLCI=KMuxDLCI;
+
+	TUint8 dlci=aSAP.iDLCI;
+	if(dlci == KMuxDLCI)
+		dlci=KMaxRfcommDLCI+1;  // This is an impossible DLCI that will never match
+	while(iter)
+		{
+		frm=iter++;
+		
+		//	determine the DLCI the frame is associated with
+		if(frm->Type() == KMuxCtrlFrameType)
+			frmDLCI=static_cast<CRfcommMuxCtrlFrame*>(frm)->DLCI();
+		else
+			frmDLCI=frm->Address();
+
+		if(frmDLCI==dlci || frm->SAP() == &aSAP)
+			{
+			LOG2(_L("RFCOMM: Clearing response for DLCI %d, sap 0x%08x"), frmDLCI, &aSAP);
+			delete frm;
+			break;
+			}
+		}
+	}
+
+/**********************************************************************/
+/*
+  Internal functions.
+*/
+
+void CRfcommMuxer::SetNoFreeSpace(TInt aDLCI, TBool aVal)
+	/**
+		Sets or resets this DLCI as having no space
+
+		@param aVal   True if no space
+	**/
+	{
+	__ASSERT_DEBUG(aDLCI <= KMaxRfcommDLCI, Panic(ERfcommInvalidDLCI));
+	if(aVal)
+		iSAPNoFreeSpaceMask[aDLCI >> 5] |= 1 << (aDLCI % 32);
+	else
+		iSAPNoFreeSpaceMask[aDLCI >> 5] &= ~(1 << (aDLCI % 32));
+	}
+
+TBool CRfcommMuxer::CanProcessNewData() const
+	{
+	return iFlowStrategy->CanProcessNewData(!iSAPNoFreeSpaceMask[0] && !iSAPNoFreeSpaceMask[1]);
+	}
+
+void CRfcommMuxer::SetSendBlocked(CRfcommSAP& aSAP, TBool aIsBlocked)
+	/**
+	   Mark the state of this SAP as able/unable to send
+
+	   This is done by moving the SAP from the active to the blocked
+	   Q.  To ensure all saps get a chance, we wake saps from the
+	   front of the blocked Q, but add them to the end, thus
+	   round-robining the chance to send.
+	**/
+	{
+	
+	aSAP.iLink.Deque();
+	if(aIsBlocked)
+		/*AddFirst - this helps avoid an infinite loop.
+		For example SignalSAPsCanSend() unblocks a SAP 
+		then can cause it to be reblocked when it calls 
+		sap->CanSend().
+		*/
+		iBlockedSAPs.AddFirst(aSAP); 
+	else
+		iSAPs.AddLast(aSAP);
+	}
+
+void CRfcommMuxer::ProcessFrame()
+	/**
+	   Process the frame that is at the start data buffer.
+
+	   When this is called, a frame is in the data buffer.  The
+	   contents should be verified and processed appropriately.
+	**/
+	{
+	if(iNextPacket->Length() < KMinFrameLength)
+		{
+		LOG(_L("RFCOMM:  ** ERROR ** received frame too short"));
+		return;
+		}
+	LOG(_L("1st five bytes of incoming frame...."));
+	LOGHEXRAW(&(*iNextPacket)[0], 5);
+	
+	TUint8  addr=DecodeDLCI((*iNextPacket)[KFrameAddrOffset]);
+	TUint8  ctrl=(*iNextPacket)[KFrameCtrlOffset];
+	// Is this really necessary ?
+	TBool   poll = ((ctrl & KPollFinalBitmask) != 0)?ETrue:EFalse;
+	//TBool   poll = ctrl & KPollFinalBitmask;
+	ctrl = static_cast<TUint8>(ctrl & ~KPollFinalBitmask); // zero the poll/final bit
+
+	//LOG1(_L("RFCOMM: Frame for dlci %d"), addr);
+	if(addr!=0 && !iMuxChannel->IsOpen())
+		{
+		LOG1(_L("RFCOMM: Error: Frame for dlci %d before mux channel up - junking"), addr);
+		return;
+		}
+	DecodeLengthAndCredit((poll && ctrl == KUIHCtrlField && addr != KMuxDLCI));  // get the length and the credit if correct packet
+
+	// Frame length is header+data+fcs (fcs is one byte)
+	if(iCurrentDataLength+iCurrentHeaderLength+1 != iNextPacket->Length())
+		{
+		if(iCurrentDataLength+iCurrentHeaderLength+1 < iNextPacket->Length())
+			{
+			LOG(_L("RFCOMM: Frame shorter than L2CAP packet, junking"));
+			}
+		else
+			{
+			LOG(_L("RFCOMM: Frame longer than L2CAP packet, junking"));
+			}
+		return;
+		}
+	
+	TUint8  fcs=(*iNextPacket)[iCurrentDataLength+iCurrentHeaderLength];
+		
+	if(CheckFCS(fcs,ctrl))
+		{
+	switch (ctrl)
+			{
+		case KSABMCtrlField:
+			if(poll)
+				{
+				LOG(_L("Rx: SABM"));
+				HandleSABM(addr);
+				}
+			else
+				{
+				// Must ignore SABM with poll bit 0, TS07.10 5.4.4.1
+				LOG(_L("RFCOMM: SABM with P/F = 0, ignoring"));
+				}
+			break;
+			
+		case KUACtrlField:
+			LOG(_L("Rx: UA"));
+			HandleUA(addr);
+			break;
+			
+		case KDMCtrlField:
+			LOG(_L("Rx: DM"));
+			HandleDM(addr);
+			break;
+			
+		case KDISCCtrlField:
+			if(poll)
+				{
+				LOG(_L("Rx: DISC"));
+				HandleDISC(addr);
+				}
+			else
+				{
+				// Must ignore DISC with poll bit 0, TS07.10 5.4.4.1
+				LOG(_L("RFCOMM: DISC with P/F = 0, ignoring"));
+				}
+			break;
+			
+		case KUIHCtrlField:
+			if(addr == KMuxDLCI)
+				{
+				LOG(_L("Rx: UIH Ctrl Frame"));
+				TRAPD(err, ParseCtrlMessageL());
+				if(err !=KErrNone)
+					{
+					LOG(_L("RFCOMM: Error in parsing the ctrl frames"));
+					}
+				}
+			else
+				{
+				LOG(_L("Rx: Simple UIH"));
+				ProcessDataFrame(addr, poll);
+				}
+			break;
+			
+		default:
+			{
+			LOG(_L("Error: RFCOMM: Unexpected frame ctrl field"));
+			}
+			}
+		}
+	else
+		{
+		LOG(_L("RFCOMM: Frame failed checksum"));
+		}
+	}
+
+TUint8 CRfcommMuxer::DecodeDLCI(TUint8 aAddr)
+	/**
+	   Strip off the EA and C/R bits and return an TS07.10 DLCI.
+
+	   Note that this is not the same as the RFCOMM server channel,
+	   which is 5 bits + 1 direction bit.
+	**/
+	{
+	return TUint8(aAddr >> 2);
+	}
+
+void CRfcommMuxer::DecodeLengthAndCredit(TBool aCBFC)
+	/**
+	   Decode the length of a frame.
+
+	   Return the overall length of the header.  This can be one or
+	   two bytes in length.
+	**/
+	{
+	if((*iNextPacket)[2] & 1)
+		{
+		// Single byte length
+		iCurrentDataLength=(*iNextPacket)[2] >> 1;
+		iCurrentHeaderLength= KShortFrameHeaderLength;
+		}
+	else 
+		{
+		iCurrentDataLength=((*iNextPacket)[2] >> 1) +
+			((*iNextPacket)[3] << 7);
+		iCurrentHeaderLength= KLongFrameHeaderLength;
+		}
+
+	//if doing CBFC updates credit buffer and revises header length buffer
+	//(iCurrentHeaderLength and iCurrentCredit)
+	iFlowStrategy->DecodeLength(aCBFC, *iNextPacket, 
+						iCurrentCredit, iCurrentHeaderLength);
+	}
+
+void CRfcommMuxer::HandleSABM(TUint8 aDLCI)
+	/**
+		Handles an incoming SABM command.
+		This function basically dispatches the SABM to either the
+		mux channel or an appropriate SAP.
+	**/
+	{
+	if(iCurrentDataLength != 0)
+		{
+		LOG1(_L("RFCOMM: SABM frame with length %d"), iCurrentDataLength);
+		}
+// want this for UPF test verification - need the whole of a SABM in log file	
+	LOG1(_L("RFCOMM: SABM command received, DLCI %d"), aDLCI);
+	LOGHEXRAW(&((iNextPacket->Des())[0]), (iCurrentHeaderLength+iCurrentDataLength));
+	
+	if(aDLCI == KMuxDLCI)
+		//	A SABM intended to initialise the Mux channel
+		iMuxChannel->SABM();
+	else
+		{
+		//	A SABM intended for a SAP
+		CRfcommSAP* mySAP=FindConnectedOrListeningSAP(aDLCI);
+		if(mySAP)
+			mySAP->SABM(*this, aDLCI);
+		else
+			{
+			//	There is nothing out there to handle this SABM.
+			//	Send a DM back as response.
+			SendDM(aDLCI);
+			}
+		}
+	}
+
+void CRfcommMuxer::HandleDISC(TUint8 aAddr)
+	/**
+	   Handles an incoming DISConnect request.
+
+	   If this is for the mux channel then we should shutdown totally.
+	   Otherwise we signal the appropriate SAP and then send a UA
+	   frame to acknowledge.
+	**/
+	{
+	LOG1(_L("RFCOMM: DISC frame for DLCI %d"), aAddr);
+	if(iCurrentDataLength !=0)
+		{
+		LOG(_L("RFCOMM: DISC frame length !=0"));
+		}
+
+	CRfcommSAP* sap;
+	
+	if(aAddr != KMuxDLCI)
+		{
+		sap=FindSAP(aAddr);
+		if(!sap)
+			{
+			LOG(_L("RFCOMM: DISC for non-existant sap"));
+			TransmitDM(aAddr, ETrue);
+			}
+		else
+			{
+			sap->DISC();
+			}
+		}
+	else
+		{
+		iMuxChannel->DISC();
+		}
+	}
+
+void CRfcommMuxer::HandleDM(TUint8 aAddr)
+	/**
+	   Handles an incoming DM frame.
+
+	   We must remove the equivalent SABM or DISC from the response Q
+	**/
+	{
+	LOG1(_L("RFCOMM: DM Frame for DLCI %d"), aAddr);
+	if(aAddr != KMuxDLCI)
+		{
+		CRfcommSAP* sap=FindSAP(aAddr);
+		if(!sap)
+			{
+			LOG(_L("RFCOMM: DM for unanticipated DLCI!"));
+			}
+		else
+			{
+			sap->DM();
+			}
+		}
+	else
+		{
+		iMuxChannel->DM();
+		}
+	if(!CtrlFrameResponse(aAddr))
+		MuxCtrlResponseDM(aAddr);
+	}
+
+void CRfcommMuxer::HandleUA(TUint8 aAddr)
+	/**
+	   Handles an incoming UA frame.
+
+	   This represents an acknowledgement of a command on this channel.
+	**/
+	{
+	LOG1(_L("RFCOMM: UA Frame for addr %d"), aAddr);
+	if(aAddr != KMuxDLCI)
+		{
+		CRfcommSAP* sap=FindSAP(aAddr);
+		if(!sap)
+			{
+			LOG(_L("RFCOMM: UA for invalid DLCI, ignoring"));
+			}
+		else
+			{
+			sap->UA();
+			}
+		}
+	else
+		{
+		iMuxChannel->UA();
+		}
+	//	Attempt to find the matching control frame - ignore errors.
+	(void)CtrlFrameResponse(aAddr);
+	}
+
+// TRY_CBFC is this used for PNresponses ? got to check if our CBFC overture has been
+// accepted. Appears only to be used for DM and UA responses where no action is needed
+TInt CRfcommMuxer::CtrlFrameResponse(TUint8 aAddr)
+	/**
+	   Remove the ctrl frame that caused this response from the response pending Q
+
+	   Responses are UA and DM, which may both be caused by SABM &
+	   DISC frames.  However, there should only ever be one SABM/DISC
+	   outstanding for a given address, so we simply look for a
+	   matching address.
+	**/
+	{
+	TDblQueIter<CRfcommFrame> iter(iResponseQ);
+	CRfcommFrame* frm;
+
+	while(iter)
+		{
+		frm=iter++;
+		if(frm->Type() == KCtrlFrameType && DecodeDLCI(frm->Address()) == aAddr)
+			{
+			LOG(_L("RFCOMM: Found matching Ctrl command for response"));
+			delete frm;
+			break;
+			}
+		}
+	return(iter?KErrNone:KErrNotFound);
+	}
+
+void CRfcommMuxer::ProcessDataFrame(TUint8 aAddr, TBool aPoll)
+	/**
+	   Send this data up to the sap.
+	**/
+	{
+	__ASSERT_DEBUG(iNextPacket, Panic(ERfcommNoPacket));
+	LOG(_L("CRfcommMuxer::ProcessDataFrame"));
+	CRfcommSAP* sap=FindSAP(aAddr);
+	if(sap)
+		{
+		if(iFlowStrategy->ProcessDataFrameReviseCredits(*sap, aPoll, iCurrentCredit))
+			//returning'ETrue' => sap needs unblocking
+			{
+			LOG(_L("RFCOMM: CBFC unblocking"));
+			SetSendBlocked(*sap, EFalse);
+			sap->CanSend();
+			}
+		if (iCurrentDataLength)
+			{
+			sap->Data(iNextPacket->Mid(iCurrentHeaderLength, iCurrentDataLength));
+			}
+		else
+			{
+			LOG(_L("RFCOMM: Data frame with no data - probably a CBFC credit"));
+			}
+		}
+	else
+		{
+		LOG1(_L("RFCOMM: Data for unknown sap %d"), aAddr);
+		}
+	}
+
+void CRfcommMuxer::ParseCtrlMessageL()
+	/**
+	   Parse the control messages for the mux.
+
+	   The data on the channel has the following format.
+
+	   | Type | Length | Value 1 | Value 2 | ... | Value n |
+	   unless this is PN frame then CL bits may indicate CBFC
+	   and the K bits indicate the initial credit
+
+	**/
+	{
+	TInt offset=0; // Number of bytes we've parsed.
+	TInt x=0;
+	TUint8 type=0;
+	TInt length;
+	TBool command;  // command or response
+
+	if(!iMuxChannel->IsOpen())
+		{
+		LOG(_L("RFCOMM: Error - mux ctrl message before muxchannel open! Junking."));
+//		return;  // MB - fixme
+		}
+	
+	while(offset+2 <= iCurrentDataLength)
+		{
+		command=(*iNextPacket)[iCurrentHeaderLength+offset] & KCRBitmask;
+		offset+=GetTypeFieldL(x, offset);
+		// Type must be 8 bits or less for us to deal with
+		if(x > 255 || x < 0)
+			{
+			User::Leave(KErrArgument);
+			}
+		type=static_cast<TUint8>(x);
+		offset+=GetLengthFieldL(length, offset);
+		if(offset+length > iCurrentDataLength)
+			{
+			User::Leave(KErrArgument);
+			}
+		
+		LOG2(_L("RFCOMM: Incoming Packet, %d, %d"),type, length);
+
+		// Now parse the individual types.  Offset points to the data
+		switch(type)
+			{
+		case KTestType:
+			HandleTest(command, iCurrentHeaderLength+offset, length);
+			break;
+		
+		case KFConType:
+			//FC
+			if(length!=0)
+				{
+				LOG(_L("RFCOMM: FCon with non-zero length!"));
+				}
+			HandleFCon(command);
+			//
+			break;
+		
+		case KFCoffType:
+			//FC
+			if(length!=0)
+				{
+				LOG(_L("RFCOMM: FCoff with non-zero length!"));
+				}
+			HandleFCoff(command);
+			//
+			break;
+
+		case KMSCType:
+			{
+			LOG(_L("RFCOMM: MSC:"));
+			LOGHEXRAW(&((iNextPacket->Des())[0]), (iCurrentHeaderLength+iCurrentDataLength));
+
+			LOG2(_L("RFCOMM: MSC Hex: Incoming Packet, %d, %d"), (*iNextPacket)[0], 80);
+			
+			// See TS 7.10, pg 29 for details
+			// Length must be exactly 2, or 3 if 'Break Signals' are present
+			if(length < 2 || length > 3)
+				{
+				LOG1(_L("RFCOMM: MSC with erroneous length %d"), length);
+				break;
+				}
+			// Next byte is DLCI...
+			TUint8 dlci = static_cast<TUint8>((*iNextPacket)[iCurrentHeaderLength+offset] >> 2);
+			LOG1(_L("RFCOMM: DLCI %d"), dlci);
+			// ...and the next is the V.24 signals
+			TUint8 signals = static_cast<TUint8>((*iNextPacket)[iCurrentHeaderLength+offset+1] >> 1);
+			LOG3(_L("RFCOMM: MSC %S received dlci %d signals 0x%02x"),
+				(command?&KCommandText:&KResponseText), dlci, signals);
+			
+			if(!command)
+				{
+				LOG(_L("RFCOMM: Response"));
+				}
+			else
+				{
+				LOG(_L("RFCOMM: Command"));
+				}
+			LOG1(_L("RFCOMM: Signals 0x%02x"), signals);
+
+			HandleMSC(command, dlci, signals);
+			break;
+			}
+
+		case KRPNType:
+		    {
+			// RPN frame received from L2CAP
+			LOG(_L("RFCOMM: RPN received"));
+			if(length != KRPNRequestLength && 
+			   length != KRPNCommandLength &&
+			   length != KRPNResponseLength)
+				{
+				LOG(_L("RFCOMM: Ignoring RPN with bad length"));
+				break;
+				}
+			TUint8 dlci=0;
+			if(length == KRPNRequestLength)
+				{
+				// There's no parameter data
+				ParseRPN(iCurrentHeaderLength+offset, length, dlci);
+				HandleRPN(command, dlci); 
+				}
+			else // There was data so need to parse this too
+				{
+				TRfcommRPNTransaction rpnTransaction;
+				ParseRPN(iCurrentHeaderLength+offset, length, dlci, 
+						 &rpnTransaction);
+				HandleRPN(command, dlci, &rpnTransaction);
+				}
+			break;
+			}
+
+		case KPNType:
+		    {
+			LOG(_L("RFCOMM: PN received"));
+			LOGHEXRAW(&((iNextPacket->Des())[0]), (iCurrentHeaderLength+iCurrentDataLength));
+
+			TRfcommPortParams portparams;
+			TUint8 dlci;
+			ParsePNL(iCurrentHeaderLength+offset, length, dlci, portparams);
+			HandlePN(command, dlci, portparams);
+			break;
+			}
+		
+		case KNSCType:
+			LOG(_L("RFCOMM: NSC received"));
+			break;
+
+		case KRLSType:
+			{
+			LOG(_L("RFCOMM: RLS received"));
+			// See TS 7.10, pgs 34/35 for details
+			const TUint8 KRLSLength = 2; // Length *must* be exactly 2
+			if(length != KRLSLength)
+				{
+				LOG1(_L("RFCOMM: RLS with erroneous length %d"),
+							  length);
+				break;
+				}
+			// First byte is DLCI...
+			TUint8 dlci = TUint8((*iNextPacket)[iCurrentHeaderLength+offset]) >> 2;
+			// ...and the next is the status octet
+			TUint8 status = TUint8((*iNextPacket)[iCurrentHeaderLength+offset+1]);
+			LOG3(_L("RFCOMM: RLS %S received dlci %d status 0x%02x"),
+					(command?&KCommandText:&KResponseText), dlci, status);
+			HandleRLS(command, dlci, status);
+			break;		
+			}
+		default:
+			LOG1(_L("RFCOMM: Unknown type 0x%02x"), type);
+			TransmitNSC(command, type);
+			break;
+			}
+		offset+=length;
+		}
+	}
+
+void CRfcommMuxer::ParsePNL(TInt aOffset, TInt aLen, TUint8& aDLCI,
+						    TRfcommPortParams& aParams)
+	/**
+	   Parse the PN field at the specified offset in the frame.
+	   Format is:
+	   @verbatim
+	   Value Octet Bit 1 Bit 2 Bit 3 Bit 4 Bit 5 Bit 6 Bit 7 Bit 8
+	       1        D1    D2    D3    D4    D5    D6    0     0
+    	   2        I1    I2    I3    I4    CL1   CL2   CL3   CL4
+	       3        P1    P2    P3    P4    P5    P6    0     0
+    	   4        T1    T2    T3    T4    T5    T6    T7    T8
+    	   5        N1    N2    N3    N4    N5    N6    N7    N8
+	       6        N9    N10   N11   N12   N13   N14   N15   N16
+    	   7        NA1   NA2   NA3   NA4   NA5   NA6   NA7   NA8
+     	   8        K1    K2    K3    0     0     0     0     0
+
+	   Where:
+	     D1-6  : DLCI
+		 I1-4  : frame type (must be zero)
+		 CL1-4 : Convergence layer (if CBFC 0xf for command, 0xe for response...
+									...otherwise must be zero)
+		 P1-6  : Priority
+		 T1-8  : Acknowledgement timer T1 (must be zero)
+		 N1-16 : Max frame size (default 127)
+		 NA1-8 : Max retransmissions (must be zero)
+		 K1-3  : either Initial CBFC credit or Error recovery window (must be zero)
+		 @endverbatim
+	**/
+	{
+	// Length must be exactly 8
+	const TUint8 KPNLength = 8;
+	if(aLen < KPNLength)
+		{
+		LOG(_L("RFCOMM: Invalid PN packet length"));
+		User::Leave(KErrGeneral);
+		}
+
+	HBufC8& packet = *iNextPacket;
+
+	// Constants for offsets into packet
+	// DLCI is at start of byte, (offset = 0)
+	// Ignore frame type		 (offset = 1)
+	const TUint8 KConvergenceLayerOffset = 1; //used by CBFC
+	const TUint8 KPriorityOffset		 = 2;
+	// Ignore Ack timer			 (offset = 3)
+	const TUint8 KMaxFrameLowerOffset	 = 4;
+	const TUint8 KMaxFrameUpperOffset	 = 5;
+	const TUint8 KErrorRecoveryWindow    = 7; //used by CBFC
+	// Ignore the rest
+
+	// DLCI - only want lowest 6 bits of byte
+	aDLCI = packet[aOffset] & KMaxRfcommDLCI;
+	// Priority - only want lowest 6 bits of byte
+	aParams.iPriority = packet[aOffset+KPriorityOffset] & 0x3f;
+	// Frame size is 2 bytes. Lower byte first...
+	aParams.iMaxFrameSize = TUint16(packet[aOffset+KMaxFrameLowerOffset]);
+	// ...then upper byte
+	aParams.iMaxFrameSize += TUint16(packet[aOffset+KMaxFrameUpperOffset] << 8);
+	aParams.iCreditIndicator = TUint8(packet[aOffset + KConvergenceLayerOffset] >> 4);
+	aParams.iInitialCredit = TUint8(packet[aOffset + KErrorRecoveryWindow] & 7);
+	LOG2(_L("RFCOMM: PN Rx(CBFC) CL 0x%02x, K 0x%02x"), aParams.iCreditIndicator, aParams.iInitialCredit);
+	}
+
+void CRfcommMuxer::ParseRPN(TInt aOffset, TInt aLen, TUint8& aDLCI, 
+							TRfcommRPNTransaction* aRPNTransactionPtr)
+	/**
+
+	Parse an incoming RPN frame.  
+	aOffset points to the start of the data octets on entry
+
+	@verbatim
+	Format is:
+	Value Octet Bit 1 Bit 2 Bit 3 Bit 4 Bit 5 Bit 6 Bit 7 Bit 8
+	     0       EA    1               DLCI
+		 1       B1    B2    B3    B4    B5    B6    B7    B8
+		 2       D1    D2    S     P     PT1   PT2   res   res
+		 3       FLC1  FLC2  FLC3  FLC4  FLC5  FLC6  res   res
+		 4       XON1  XON2  XON3  XON4  XON5  XON6  XON7  XON8
+		 5       XOF1  XOF2  XOF3  XOF4  XOF5  XOF6  XOF7  XOF8
+		 6       PM1   PM2   PM3   PM4   PM5   PM6   PM7   PM8
+		 7       PM9   PM10  PM11  PM12  PM13  PM14  PM15  PM16
+
+	Where:
+	   B1-8   :  Baud rate
+       D1-2   :  data bits
+	   S      :  Stop bits
+	   P      :  Parity
+	   PT1-2  :  Parity Type
+	   FLC1-6 :  Flow ctrl type
+	   XON1-8 :  Xon character
+	   XOFF1-8:  Xoff character
+	   PM1-16 :  Which values are to be set/accepted
+   @endverbatim
+	See TS07.10 5.4.6.3.9 for more details
+	**/
+	{
+	// Wrap a descriptor over the data we want
+	TPtrC8 valueOctets=iNextPacket->Des().Mid(aOffset, aLen);
+	
+	// Get the DLCI
+	aDLCI = valueOctets[0] >> 2;
+	LOG1(_L("RFCOMM: RPN frame, DLCI %d"), aDLCI);
+
+	// Now parse out the valid fields if necessary
+	if(aRPNTransactionPtr)
+		{
+		LOG(_L("Parsing full RPN data\n"));
+		// Local references into the transaction structure
+		TRfcommRemotePortParams& portParams = aRPNTransactionPtr->iPortParams;
+		TUint16& paramMask = aRPNTransactionPtr->iParamMask;
+		// construct the parameter mask out of the last two value octets
+		paramMask = TUint16(valueOctets[7]);
+		paramMask = TUint16((paramMask << 8) + valueOctets[6]);
+		
+		// Bit rate
+		if(paramMask & EPMBitRate)
+		{
+			TUint8 bitRate=valueOctets[1];
+			switch(bitRate)
+			{
+			// Magic numbers because enums are not the same 
+			// values as the ones specified in the spec and 
+			// adding even more enums would confuse matters
+			case 0:
+				portParams.SetBitRate(EBps2400);
+				break;
+			case 1:
+				portParams.SetBitRate(EBps4800);
+				break;
+			case 2:
+			case 3:
+			case 4:
+			case 5:
+			case 6:
+			case 7:
+			case 8:
+				portParams.SetBitRate(static_cast<TBps>(EBps7200+(bitRate-2)));
+				break;
+			default:
+				{
+				LOG(_L("RFCOMM: RPN with invalid bitrate field, ignoring"));
+				}
+			}
+		}
+		
+		// Value octet 2 is compound
+		// Masks for bit position within octet
+		//							Bit number:	   76543210
+		const TUint8 KDataBitsPos		= 0x03; // 000000XX
+		const TUint8 KStopBitPos		= 0x04;	// 00000X00
+		const TUint8 KParityBitPos		= 0x08;	// 0000X000
+		const TUint8 KParityTypeBitsPos = 0x30; // 00XX0000
+		// 2 most significant bits are reserved (always 0)
+
+		const TUint8 KStopBitOffset		= 2; // offset for 00000X00
+		const TUint8 KParityBitOffset	= 3; // offset for 0000X000
+		const TUint8 KParityTypeOffset	= 4; // offset for 00XX0000
+			
+		if(paramMask & EPMDataBits)
+			{
+			TUint8 dataBits = TUint8(valueOctets[2] & KDataBitsPos);
+			// Data bits
+			switch(dataBits)
+				{
+				// Magic numbers because enums are not the same 
+				// values as the ones specified in the spec and 
+				// adding even more enums would confuse matters
+				case 0:
+					portParams.SetDataBits(EData5);
+					break;
+				case 1:
+					portParams.SetDataBits(EData7);
+					break;
+				case 2:
+					portParams.SetDataBits(EData6);
+					break;
+				case 3:
+					portParams.SetDataBits(EData8);
+					break;
+				default:
+					__DEBUGGER()
+					LOG(_L("How did we get more than 3 with 2 bits?!\n"));
+				}
+			}
+
+		if(paramMask & EPMStopBit)
+			{
+			TUint8 stopBit = TUint8((valueOctets[2] & KStopBitPos)>>KStopBitOffset);
+			portParams.SetStopBit(static_cast<TStopBits>(stopBit));
+			}
+
+		// The Parity and ParityType bits have a combined effect..
+		
+		if(paramMask & EPMParity)
+			{
+			TUint8 parityBit = TUint8((valueOctets[2] & KParityBitPos)>>KParityBitOffset);
+			
+			if(!parityBit)
+				{ 
+				portParams.SetParity(EParityNone);
+				}
+			}
+
+		if(paramMask & EPMParityType)
+			{
+			TParity parityType = EParitySpace; //Just to stop the compiler warnings
+
+			//It is intended that this 'GetParity' will only update 'parityType' 
+			//if 'SetParity' was called in the previous 'if' clause.
+			portParams.GetParity(parityType);
+
+			if(parityType == EParityNone)
+				{
+				// Can't have a parity type if there's no parity set
+				LOG(_L("RFCOMM: WARNING!: Tried to set parity type when parity is none or invalid"));
+				}
+			else
+				{	
+				// Parity Type is in first two bits of upper nibble
+				// Need to move it down to the lower nibble
+				switch((valueOctets[2] & KParityTypeBitsPos)>>KParityTypeOffset)
+					{
+				case 0:
+					portParams.SetParity(EParityOdd);
+					break;
+				case 1:
+					portParams.SetParity(EParityMark);
+					break;
+				case 2:
+					portParams.SetParity(EParityEven);
+					break;
+				case 3:
+					portParams.SetParity(EParitySpace);
+					break;
+				default:
+					break;
+					}
+				}
+			}
+
+		if(paramMask & EPMXOnChar)
+			{
+			portParams.SetXOnChar(valueOctets[4]);
+			}
+
+		if(paramMask & EPMXOffChar)
+			{
+			portParams.SetXOffChar(valueOctets[5]);
+			}	
+
+		// Flow control is only in the first 6 bits of this value octet
+		TUint8 flowCtrl = TUint8(valueOctets[3] & 0x3f);
+		portParams.UpdateWholeFlowCtrl(paramMask, flowCtrl);
+		}
+	else
+		{	
+		LOG(_L("Query request received - package has not been filled out\n"));
+		}
+	}
+
+
+void CRfcommMuxer::HandleRLS(TBool aCommand, TUint8 aDLCI, TUint8 aStatus)
+	/**
+		Handle a RLS frame from remote dev
+
+		If it's a command, respond then pass the data up to the user
+		If it's a response, just ignore it - it'll just be what we sent
+	**/
+	{
+	CRfcommSAP* sap=FindSAP(aDLCI);
+	if(!sap)
+		{
+		LOG1(_L("RFCOMM: RLS for unknown dlci %d"), aDLCI);
+		if(aCommand)
+			SendDM(aDLCI);
+		return;
+		}
+
+	if(aCommand)
+		{
+		LOG(_L("RFCOMM: RLS command received"));
+		// Send a response to the remote device
+		SendRLSRsp(*sap, aStatus);
+		// Pass status up to the user
+		sap->RLS(aStatus); 
+		}
+	else
+		{
+		// Response to our command
+		LOG(_L("RFCOMM: RLS response received"));
+		MuxCtrlResponse(KRLSType);
+		}
+	}
+
+
+void CRfcommMuxer::HandleRPN(const TBool& aCommand, const TUint8& aDLCI, 
+							 const TRfcommRPNTransaction* aRPNTransactionPtr)
+	/**
+	   Handle an RPN frame from remote dev
+
+	   If it's valid, just pass it on to the SAP
+	**/
+	{	
+	LOG1(_L("RFCOMM: Handling RPN for dlci %d"), aDLCI);
+
+	if(aDLCI == KMuxDLCI)
+		{
+		LOG(_L("RFCOMM: RPN for Mux channel. Ignoring"));
+		return;
+		}
+
+	if(!aCommand && aRPNTransactionPtr == 0)
+		{
+		LOG(_L("RFCOMM: Corrupt RPN response. Ignoring"));
+		return;
+		}
+	
+	// Check to see if there's a sap to pass this onto
+	CRfcommSAP* sap=FindConnectedOrListeningSAP(aDLCI);
+	
+	if(sap == 0)// No listening or connected SAP available
+		{
+		if(aCommand)
+			{
+			LOG(_L("RFCOMM: RPN command for unknown sap - Sending DM"));
+			SendDM(aDLCI);
+			}
+		else
+			{
+			LOG(_L("RFCOMM: RPN response for unknown sap. Ignoring"));
+			}
+		return;
+		}
+
+	// Still here? Everything's OK to pass the RPN on to the SAP
+	if(aCommand)
+		sap->RPN(aRPNTransactionPtr, *this, aDLCI);
+	else // It's a valid response
+		{
+		// So we're no longer waiting for a response
+		MuxCtrlResponse(KRPNType);
+		sap->RPNRsp(*aRPNTransactionPtr);
+		}
+	}
+
+void CRfcommMuxer::HandlePN(TBool aCommand, TUint8 aDLCI, TRfcommPortParams& aParams)
+	/**
+	   Handles PN commands & responses.
+	   
+	   Hands over to the SAP, but checks the max length first and
+	   truncates if necessary before passing on.  We may also get a PN
+	   for the mux channel, which is setting the frame size.
+	**/
+	{
+	LOG2(_L("RFCOMM: Handling PN for dlci %d (MTU=%d)"), aDLCI, aParams.iMaxFrameSize);
+	if(aDLCI == KMuxDLCI)
+		{
+		iMuxChannel->PN(aCommand, aParams);
+		}
+	else
+		{
+		CRfcommSAP* sap=FindConnectedOrListeningSAP(aDLCI);
+		TBool useCBFC = aCommand?(aParams.iCreditIndicator == (KCBFCCommandFlag >> 4)):
+								 (aParams.iCreditIndicator == (KCBFCResponseFlag >> 4));
+		if(!sap)
+			{
+			if(aCommand)
+				{
+				//	Means no suitable listening SAP is around.
+				LOG(_L("RFCOMM: PN for unknown sap - incoming connection request with no listening SAP"));
+				TransmitDM(aDLCI, ETrue);	//	DM for specified DLCI with P/F bit set
+				}
+			else
+				{
+				// unexpected response
+				LOG(_L("RFCOMM: PN response for unknown sap, ignoring"));
+				}
+			}
+		else
+			{// Found a SAP for this PN
+			if (!iTriedCBFC)
+				{
+				if(useCBFC)
+					{
+					SetFlowType(CRfcommFlowStrategyFactory::EFlowCreditBased);
+					}
+				else
+					{
+					SetFlowType(CRfcommFlowStrategyFactory::EFlowNonCreditBased);
+					}
+				}
+
+			if(aParams.iCreditIndicator == (KCBFCCommandFlag >> 4))
+			//done for each PN
+				{
+				// need to set a one time initial credit in case
+				// subsequent DLCs don't have a PN frame
+				iInitialTxCredit = aParams.iInitialCredit;
+				}
+
+			if(aCommand)
+				{
+				sap->PN(aParams,*this,aDLCI);
+				}
+			else
+				{
+				sap->PNResp(aParams);
+				}
+			}
+		}
+	// FIXME not sure about this, 
+	// if this is a SAP PN frame will it be in the muxctrl queue ?
+	if(!aCommand)
+		MuxCtrlResponse(KPNType);
+	}
+
+
+void CRfcommMuxer::HandleMSC(TBool aCommand, TUint8 aDLCI, TUint8 aSignals)
+	/**
+		Handles a MSC command & response from the remote device
+	   
+		If it's a command, send response and filter up to SAP
+		If it's a response, we're no longer expecting a response (!)
+	**/
+	{
+	CRfcommSAP* sap=FindSAP(aDLCI);
+	if(aCommand)
+		{
+		LOG(_L("RFCOMM: MSC command received"));
+		if(sap)
+			{
+			// Send a response to remote device
+			SendMSCRsp(*sap, aSignals);
+			// Pass signals up to user
+			sap->MSC(aSignals); 
+			}
+		else
+			{
+			LOG1(_L("RFCOMM: MSC for unknown dlci %d"), aDLCI);
+			SendDM(aDLCI);
+			}
+		}
+	else
+		{ 
+		LOG(_L("RFCOMM: MSC response received"));
+		MuxCtrlResponse(KMSCType);
+		}
+	}
+
+void CRfcommMuxer::HandleTest(TBool aCommand, TInt aOffset, TInt aLen)
+	/**
+	   Handles a test command.
+
+	   If this is a command, then respond with the same data bytes as
+	   were sent to us.  Else ignore it.
+	**/
+	{
+	if(aCommand)
+		{
+		LOG(_L("RFCOMM: Test command received"));
+		__ASSERT_DEBUG(aLen < iMuxChannel->MaxDataSize(), Panic(ERfcommTestDataTooLong));
+		TransmitTest(!aCommand, TPtr8(&iNextPacket->Des()[aOffset], aLen, aLen));
+		}
+	else
+		{
+		MuxCtrlResponse(KTestType);
+		LOG(_L("RFCOMM: Test response received"));
+		}
+	}
+
+void CRfcommMuxer::HandleFCon(TBool aCommand)
+	/**
+	   Handle a FCon instruction.
+
+	   If this is a command, then we need to respond and set flow ctrl
+	   to OK.  Otherwise it's a response to our FCon command, and we
+	   can expect the remote end to start sending.
+	**/
+	{
+	if(aCommand)
+		{
+		LOG(_L("RFCOMM: FCon command received"));
+		if(ClearToSend())
+			{
+			LOG(_L("RFCOMM: Received FCon when state was ON"));
+			}
+		SetCanSend(ETrue);
+		if(TransmitFCon(EFalse) != KErrNone)
+			{
+			LOG(_L("RFCOMM: Failed to send FCon response!"));
+			}
+		TryToSend();  // Try to send even if we couldn't respond
+		}
+	else
+		{
+		LOG(_L("RFCOMM: FCon response received"));
+		MuxCtrlResponse(KFConType);
+		}
+	}
+
+void CRfcommMuxer::HandleFCoff(TBool aCommand)
+	/**
+	   Handles a FCoff command.
+
+	   If this is a command, then set our state to flow ctrl off, and
+	   send a response.  If it was a reply, then we should now not
+	   expect any more data packets till we send a FCon.
+	**/
+	{
+	//FC
+	if(aCommand)
+		{
+		LOG(_L("RFCOMM: FCoff command received"));
+		if(!ClearToSend())
+			{
+			LOG(_L("RFCOMM: Received FCoff when state was off"));
+			}
+		SetCanSend(EFalse);
+		TransmitFCoff(EFalse);
+		}
+	else
+		{
+		LOG(_L("RFCOMM: FCoff response received"));
+		MuxCtrlResponse(KFCoffType);
+		}
+	//
+	}
+
+TInt CRfcommMuxer::GetTypeFieldL(TInt& aType, TInt aPos)
+	/**
+	   Parse the type field.  Due to EA bits this is of unknown length.
+
+	   Type field looks like this :
+	   @verbatim
+       | EA | C/R | T1 | T2 | T3 | T4 | T5 | T6 |
+
+	   If EA is 0, then the following bytes are extensions:
+
+	   | EA | T7 | T8 | T9 | T10 | T11 | T12 | T13 |
+	   @endverbatim
+	   @param aType The type is returned in this
+	   @param aPos  The start position in frame's data
+	   @return The length of the type field in bytes.
+	**/
+	{
+	const TUint8 KBitsInFirstTypeFieldByte = 6;
+	const TUint8 KBitsInSubsequentTypeFieldBytes = 7;
+	
+	TInt readptr = iCurrentHeaderLength + aPos;
+	aType = (*iNextPacket)[readptr] >> 2; // drop the EA and C/R bit (any good reason the latter?)
+	TInt offset = 1; // in bytes
+	TUint8 bitshift = KBitsInFirstTypeFieldByte;
+	
+	if(!((*iNextPacket)[readptr] & KEABitmask))
+		{
+		// Multiple length type field
+		LOG(_L("RFCOMM: Multi-length type field in mux channel"));
+		do
+			{
+			if(((readptr + offset - iCurrentHeaderLength) > iCurrentDataLength)
+				|| ((bitshift + KBitsInSubsequentTypeFieldBytes) > KBitsForNumberInTInt))
+				{
+				// We enter here if the EA's are such that we cannot handle the field, by either
+				// 1) They indicated there was another type field byte which isn't present.
+				// 2) They indicate a type field value that cannot be represented in a TInt.
+				User::Leave(KErrArgument);
+				}
+			// we should also check that we don't have too extensions
+			// (i.e. we have a number larger than a TInt)
+			aType += ((*iNextPacket)[readptr+offset] >> 1) << bitshift;
+			offset++;
+			bitshift += KBitsInSubsequentTypeFieldBytes;
+			}
+		while (!((*iNextPacket)[readptr+offset-1] & KEABitmask));
+		}
+	
+	return offset;
+	}
+	
+TInt CRfcommMuxer::GetLengthFieldL(TInt& aLen, TInt aPos)
+	/**
+	   Parse the length field.  Due to EA bits this is of unknown length.
+
+	   Type field looks like this :
+
+       | EA | L1 | L2 | L3 | L4 | L5 | L6 | L7 |
+
+	   @param aLen The length is returned in this
+	   @param aPos The start position in the frame's data
+	   @return The length of the length field.
+	**/
+	{
+	const TUint8 KBitsInLengthFieldByte = 7;
+	
+	TInt readptr = iCurrentHeaderLength + aPos;
+	aLen = (*iNextPacket)[readptr] >> 1; // discard the EA bit
+	TInt offset = 1;
+	TUint8 bitshift = KBitsInLengthFieldByte;
+	
+	if(!((*iNextPacket)[readptr] & KEABitmask))
+		{
+		// Multiple length Len field
+		LOG(_L("RFCOMM: Multi-length length field in mux channel"));
+		do
+			{
+			// Initially we do some checking to ensure field isn't malformed.
+			// Part 1 of 2.
+			if((readptr + offset - iCurrentHeaderLength) > iCurrentDataLength)
+				{
+				// We enter here if the EAs indicate that there is another length field byte
+				// when there isn't.
+                User::Leave(KErrArgument);
+				}
+
+			// Now we know that there is sufficient data in the packet, we can get the next length value
+			TUint8 value((*iNextPacket)[readptr+offset] >> 1);
+			
+			// Do some more checking of the field
+			// Part 2 of 2.
+			if((bitshift + KBitsInLengthFieldByte) > KBitsForNumberInTInt)
+				{
+				// We enter here if the EA's indicate that the length value the field represents is
+				// potentially larger than a TInt.
+				// However it may be that the overflowed bits are just trailing zeros, meaning that
+				// we can represent it.
+				TUint8 trailingZeroMask(0xff);
+				trailingZeroMask <<= (KBitsForNumberInTInt - bitshift);
+				trailingZeroMask &= value;
+				if(trailingZeroMask != 0x00)
+					{
+					// We enter here if there are bits which would overflow a TInt
+					User::Leave(KErrArgument);
+					}
+				}
+
+			// If here the byte appears valid, so update the length as appropriate.
+			aLen += value << bitshift;
+			offset++;
+			bitshift += KBitsInLengthFieldByte;
+			}
+		while (!((*iNextPacket)[readptr+offset-1] & KEABitmask));
+		}
+	
+	return offset;
+	}
+
+void CRfcommMuxer::MuxCtrlResponse(TUint8 aType)
+	/**
+	   Remove the mux ctrl frame that caused this response from the
+	   response pending Q
+
+	   To fully match a response, one would need to look at all the
+	   parameters to the command.  However, we cheat by just matching
+	   the command type.  This may lead us to remove the wrong one,
+	   but since any lack of a match brings down the link, we go for
+	   the easy option.
+	**/
+	{
+	TDblQueIter<CRfcommFrame> iter(iResponseQ);
+	CRfcommFrame* frm;
+
+	while(iter)
+		{
+		frm=iter++;
+		if(frm->Type() == KMuxCtrlFrameType)
+			{
+			CRfcommMuxCtrlFrame* muxframe=static_cast<CRfcommMuxCtrlFrame*>(frm);
+			if(muxframe->CommandType() == aType)
+				{
+				LOG(_L("RFCOMM: Found matching muxctrl command for response"));
+				delete frm;
+				break;
+				}
+			}
+		}
+	}
+
+void CRfcommMuxer::MuxCtrlResponseDM(TUint8 aDLCI)
+	/**
+	   Remove the mux ctrl frame that caused this DM response from the
+	   response pending Q
+
+	   All we have to work on is the DLCI of the channel which issued the
+	   original mux control frame which elicited the DM response. We
+	   can search through the response queue until we find a frame sent 
+	   out for this DLCI.
+    **/
+	{
+	TDblQueIter<CRfcommFrame> iter(iResponseQ);
+	CRfcommFrame* frm;
+
+	while(iter)
+		{
+		frm=iter++;
+		if(frm->Type() == KMuxCtrlFrameType)
+			{
+			CRfcommMuxCtrlFrame* muxframe=static_cast<CRfcommMuxCtrlFrame*>(frm);
+			if(muxframe->DLCI() == aDLCI)
+				{
+				LOG(_L("RFCOMM: Found matching muxctrl command for DM response"));
+				delete frm;
+				break;
+				}
+			}
+		}
+	}
+
+TInt CRfcommMuxer::TransmitSABM(TUint8 aDLCI, CRfcommSAP* aSAP)
+	/**
+	   Send a SABM command for specified DLCI.
+	   This command is used to start the connection of a channel,
+	   including the control channel.
+	   @verbatim
+	   Frame format is:
+
+	   Address | Control | Length | FCS
+
+	   Address format is:
+
+	   | 1  |  2  | 3 | 4   5   6   7   8 |
+	   | EA | C/R | D |  Server channel   |
+
+	   Where aDLCI already includes the D & Server channel bits.
+	   @endverbatim
+
+	   We always set the P/F bit to one in a SABM frame.
+	   
+	   @param aDLCI This value is the server channel plus direction
+	   bit part of the DLCI.  This will have the E/A & C/R bits added.
+
+	   @return Error code
+	**/
+	{
+	LOG1(_L("RFCOMM: Sending SABM for DLCI %d"), aDLCI); 
+	CRfcommCtrlFrame* frm=NewFrame(aSAP);
+	
+	if(!frm)
+		return KErrNoMemory;
+
+	frm->SetResponseNeeded(ETrue);
+	frm->SetAddress(BuildAddr(aDLCI, ETrue));
+	frm->SetControl(KSABMCtrlField | KPollFinalBitmask);
+	/*
+	BLOG: The frame has now been prepared therefore can show all bits. 
+	//BLOG: Can show all the information about this frame in TrytoSend() just as it is physically about
+	//to go over the air - Bluetooth...wooohooooo!
+	//Mind you probably should blog DLCI here? 
+	//BLOG KBlogDLCI
+	BlogTransmitCtrlFrame(frm, aDLCI, aSAP)
+	*/	
+	EnqueFrame(frm);
+	
+	return KErrNone;
+	}
+
+TInt CRfcommMuxer::TransmitUA(TUint8 aDLCI, CRfcommSAP* aSAP)
+	{
+	LOG1(_L("RFCOMM: Sending UA for DLCI %d"), aDLCI); 
+	CRfcommCtrlFrame* frm=NewFrame(aSAP);
+	if(!frm)
+		return KErrNoMemory;
+
+	// UA always has Final set
+	frm->SetControl(KUACtrlField | KPollFinalBitmask);
+	frm->SetAddress(BuildAddr(aDLCI, EFalse));
+	EnqueFrame(frm);
+	return KErrNone;
+	}
+
+TInt CRfcommMuxer::TransmitDM(TUint8 aDLCI, TBool aPFBit, CRfcommSAP* aSAP)
+	{
+	LOG1(_L("RFCOMM: Sending DM for DLCI %d"), aDLCI); 
+	CRfcommCtrlFrame* frm=NewFrame(aSAP);
+	if(!frm)
+		return KErrNoMemory;
+
+	if(aPFBit)
+		frm->SetControl(KDMCtrlField | KPollFinalBitmask);
+	else
+		frm->SetControl(KDMCtrlField);
+	frm->SetAddress(BuildAddr(aDLCI, EFalse));
+	EnqueFrame(frm);
+	return KErrNone;
+	}
+
+TInt CRfcommMuxer::TransmitDISC(TUint8 aDLCI, CRfcommSAP* aSAP)
+	{
+	LOG1(_L("RFCOMM: Sending DISC for DLCI %d"), aDLCI); 
+	CRfcommCtrlFrame* frm=NewFrame(aSAP);
+	if(!frm)
+		return KErrNoMemory;
+
+	// DISC always has Final set
+	frm->SetResponseNeeded(ETrue);
+	frm->SetControl(KDISCCtrlField | KPollFinalBitmask);
+	frm->SetAddress(BuildAddr(aDLCI, ETrue));  // C/R set
+	EnqueFrame(frm);
+	return KErrNone;
+	}
+
+TInt CRfcommMuxer::TransmitRPN(TUint8 aDLCI, TBool aCommand, TUint8 aLen,
+							   const TRfcommRPNTransaction& aRPNTransaction, CRfcommSAP* aSAP)
+	/**
+
+	Transmit an RPN frame. 
+
+	@verbatim
+	Format is:
+	Value Octet Bit 1 Bit 2 Bit 3 Bit 4 Bit 5 Bit 6 Bit 7 Bit 8
+		 1		 E/A   C/R	---------------DLCI----------------
+		 2       B1    B2    B3    B4    B5    B6    B7    B8
+		 3       D1    D2    S     P     PT1   PT2   res   res
+		 4       FLC1  FLC2  FLC3  FLC4  FLC5  FLC6  res   res
+		 5       XON1  XON2  XON3  XON4  XON5  XON6  XON7  XON8
+		 6       XOF1  XOF2  XOF3  XOF4  XOF5  XOF6  XOF7  XOF8
+		 7       PM1   PM2   PM3   PM4   PM5   PM6   PM7   PM8
+		 8       PM9   PM10  PM11  PM12  PM13  PM14  PM15  PM16
+
+	Where:
+	   B1-8   :  Baud rate
+       D1-2   :  data bits
+	   S      :  Stop bits
+	   P      :  Parity
+	   PT1-2  :  Parity Type
+	   FLC1-6 :  Flow ctrl type
+	   XON1-8 :  Xon character
+	   XOFF1-8:  Xoff character
+	   PM1-16 :  Which values are to be set/accepted
+   
+	See TS07.10 5.4.6.3.9 for more details
+	@endverbatim
+	**/
+	{
+	CRfcommMuxCtrlFrame* frm = 0;
+	
+	__ASSERT_DEBUG(aLen == KRPNCommandLength || aLen == KRPNRequestLength || 
+				   aLen == KRPNResponseLength,	Panic(ERfcommInvalidRPNLength));
+	LOG(_L("RFCOMM: Creating RPN frame"));
+	frm=NewSignalFrame(aLen, aCommand, aSAP);
+	if(!frm)
+		return KErrNoMemory;
+	
+	frm->SetDLCI(aDLCI);	//	For use when matching DM responses to mux control command frames
+	frm->SetCommandType(KRPNType, aCommand);
+	frm->SetResponseNeeded(aCommand);
+
+	// Now the only parameter that's always present, the DLCI
+	// Make sure the DLCI is only 6 bits long
+	__ASSERT_DEBUG(aDLCI <= KMaxRfcommDLCI, Panic(ERfcommInvalidDLCI));
+	aDLCI &= KMaxRfcommDLCI;
+	frm->PutByte((aDLCI << 2) | 0x03); // Add E/A and C/R (both 1)
+
+	TUint8 valueOctet;
+
+	// If we're creating a negotiation request or a response of any kind,
+	// fill in the rest of the value octets 
+	if(aLen == KRPNCommandLength || aLen == KRPNResponseLength)
+		{
+		const TRfcommRemotePortParams& portParams = aRPNTransaction.iPortParams;
+	
+		// Second octet carries Baud Rate (AKA 'Bit Rate')
+		TBps bitRate = EBps9600; //Just to stop the compiler warnings
+		if(portParams.GetBitRate(bitRate)) // Sets bitRate if valid in portParams
+			{
+			valueOctet = 0;
+			switch(bitRate)
+				{
+			// Magic numbers because enums are not the same 
+			// values as the ones specified in the spec and 
+			// adding even more enums would confuse matters
+			case EBps2400:
+				valueOctet = 0;
+				break;
+			case EBps4800:
+				valueOctet = 1;
+				break;
+			case EBps7200:
+			case EBps9600:
+			case EBps19200:
+			case EBps38400:
+			case EBps57600:
+			case EBps115200:
+			case EBps230400:
+				valueOctet = (bitRate - EBps7200) + 2;
+				break;
+			default:
+				LOG(_L("RFCOMM:[rfcommmuxer.cpp] Invalid bitrate, ignoring"));
+				__DEBUGGER(	);
+				}
+			}
+		else
+			{
+			valueOctet = 0;
+			}
+		frm->PutByte(valueOctet);			
+		
+		// Third octet is compound. Each part is offset from 
+		// the beginning of the octet.  
+		//								Bit No. 76543210
+		// Data Bits are stored at start:		000000XX
+		const TUint8 KStopBitOffset		= 2; // 00000X00
+		const TUint8 KParityBitOffset	= 3; // 0000X000
+		const TUint8 KParityTypeOffset	= 4; // 00XX0000
+		
+		// Now it gets complicated - here we go...
+		// Start with the 2 data bits
+		TDataBits dataBits = EData5; //Just to stop the compiler warnings
+		if(portParams.GetDataBits(dataBits))
+			{
+			switch (dataBits)
+				{
+				// Magic numbers because enums are not the same 
+				// values as the ones specified in the spec and 
+				// adding even more enums would confuse matters
+				case EData5:	
+					valueOctet = 0;
+					break;
+				case EData6:
+					valueOctet = 2;
+					break;
+				case EData7:
+					valueOctet = 1;
+					break;
+				case EData8:
+					valueOctet = 3;
+					break;
+				default:
+					__DEBUGGER();
+					LOG(_L("TransmitRPN - Dodgy Data Bits. Assuming 0\n"));
+					valueOctet = 0;
+				}
+			}
+		else
+			valueOctet = 0;
+
+		// Set the stop bit	if necessary
+		// EStop1 corresponds to leaving the stop bit clear
+		// EStop2 corresponds to setting the stop bit
+		TStopBits stopBit = EStop1; //Just to stop the compiler warnings
+		if(portParams.GetStopBit(stopBit))
+			{
+			if(stopBit == EStop2)
+				valueOctet |=  (1 << KStopBitOffset);
+			}
+
+		TParity parity = EParityNone; //Just to stop the compiler warnings
+		if(portParams.GetParity(parity))
+			{
+			// Set the parity bit by default
+			valueOctet |= (1 << KParityBitOffset);
+			// Set Parity Type (and clear parity bit if necessary)
+			switch(parity)
+				{
+				// Magic numbers because enums are not the same 
+				// values as the ones specified in the spec and 
+				// adding even more enums would confuse matters
+				case EParityNone:
+					// Clear the parity bit
+					valueOctet &= ~(1 << KParityBitOffset); 
+					break;
+				case EParityOdd:
+					// Do nothing, this parity type is stored as 0
+					break;
+				case EParityEven:
+					valueOctet |= (2 << KParityTypeOffset);
+					break;
+				case EParityMark:
+					valueOctet |= (1 << KParityTypeOffset); 
+					break;
+				case EParitySpace:
+					valueOctet |= (3 << KParityTypeOffset); 
+					break;
+				}
+			}
+		// The reserved bits are already 0 so ignore them
+		// Finally finished building Octet 3, now store it
+		frm->PutByte(valueOctet);
+
+		// Octet 4 contains 6 flow control bits and 
+		// 2 reserved bits (both zero)
+		if(portParams.GetFlowCtrl(valueOctet))
+			{
+			// Make sure the Flow Control is only 6 bits
+			if (valueOctet >> 6 != 0)
+				{
+				LOG(_L("Invalid Flow Control\n"));
+				__DEBUGGER();
+				delete frm;
+				
+				return KErrArgument;
+				}
+			frm->PutByte(valueOctet);
+			}
+		else
+			{
+			frm->PutByte(0);
+			}
+
+
+		// Octet 5 contains 8 XON bits
+		if(portParams.GetXOnChar(valueOctet))
+			{
+			frm->PutByte(valueOctet);
+			}
+		else
+			{
+			frm->PutByte(0);
+			}
+		
+		// Octet 6 contains 8 XOFF bits
+		if(portParams.GetXOffChar(valueOctet))
+			{
+			frm->PutByte(valueOctet);
+			}
+		else
+			{
+			frm->PutByte(0);
+			}
+		
+		// Octets 7 and 8 are the Parameter Mask
+		// Lowest byte first...
+		valueOctet = TUint8(aRPNTransaction.iParamMask & 0xff);
+		frm->PutByte(valueOctet);
+		// ...then the upper byte
+		valueOctet = TUint8(aRPNTransaction.iParamMask >> 8);
+		frm->PutByte(valueOctet);
+		}
+
+	// Finished building frame, so send it	
+	EnqueFrame(frm);
+	
+	return KErrNone;
+	}
+
+
+TInt CRfcommMuxer::TransmitPN(TUint8 aDLCI, TBool aCommand,
+							  const TRfcommPortParams& aParams, CRfcommSAP* aSAP)
+	/**
+	   Format is:
+	   @verbatim
+	   Value Octet Bit 1 Bit 2 Bit 3 Bit 4 Bit 5 Bit 6 Bit 7 Bit 8
+	       1        D1    D2    D3    D4    D5    D6    0     0
+    	   2        I1    I2    I3    I4    CL1   CL2   CL3   CL4
+	       3        P1    P2    P3    P4    P5    P6    0     0
+    	   4        T1    T2    T3    T4    T5    T6    T7    T8
+    	   5        N1    N2    N3    N4    N5    N6    N7    N8
+	       6        N9    N10   N11   N12   N13   N14   N15   N16
+    	   7        NA1   NA2   NA3   NA4   NA5   NA6   NA7   NA8
+     	   8        K1    K2    K3    0     0     0     0     0
+	   Where:
+	     D1-6  : DLCI
+		 I1-4  : frame type (must be zero)
+		 CL1-4 : Convergence layer 
+				 (if CBFC, set to 0x0f in request, 0x0e in response....
+				  .....otherewise must be zero)
+		 P1-6  : Priority
+		 T1-8  : Acknowledgement timer T1 (must be zero)
+		 N1-16 : Max frame size (default 127)
+		 NA1-8 : Max retransmissions (must be zero)
+		 K1-3  : Error recovery window 
+				 (initial credit (0-7) for CBFC or set to 0 if fallback to FCon/off)
+	  @endverbatim
+	 **/
+	{
+	LOG3(_L("RFCOMM: Sending PN %S for dlci %d (MTU=%d)"), 
+		(aCommand?&KCommandText:&KResponseText) , aDLCI, aParams.iMaxFrameSize);
+	CRfcommMuxCtrlFrame* frm=NewSignalFrame(KRPNCommandLength, aCommand, aSAP);
+
+	if(!frm)
+		return KErrNoMemory;
+
+	frm->SetDLCI(aDLCI);	//	For use when matching DM responses to mux control command frames
+	frm->SetCommandType(KPNType, aCommand);
+	frm->SetResponseNeeded(aCommand);
+	// Now the 8 parameters
+	__ASSERT_DEBUG(aDLCI <= KMaxRfcommDLCI, Panic(ERfcommInvalidDLCI));
+	aDLCI &= KMaxRfcommDLCI;
+	frm->PutByte(aDLCI);
+	//TRY_CBFC ... NB used to do some negotiating here - but in current version all
+	//				   such negotiation is performed in HandlePN
+#ifdef NO_CBFC
+	frm->PutByte(0x00);
+#else
+	frm->PutByte(iFlowStrategy->PNConvergenceLayer(aCommand));
+#endif
+	LOG2(_L("RFCOMM: CBFC Tried 0x%x, Type 0x%x"), iTriedCBFC, FlowType());
+
+	__ASSERT_DEBUG(aParams.iPriority <= KMaxRfcommPriority, Panic(ERfcommInvalidDLCI));
+	TUint8 params = aParams.iPriority & KMaxRfcommPriority;
+	frm->PutByte(params);
+	frm->PutByte(0x00); // T bits
+	frm->PutLittleEndian16(aParams.iMaxFrameSize);
+	frm->PutByte(0x00); // NA bits
+#ifdef NO_CBFC
+	frm->PutByte(0x00);
+#else
+	frm->PutByte(aParams.iInitialCredit);
+#endif
+	EnqueFrame(frm);
+	
+	return KErrNone;
+	}
+
+
+TInt CRfcommMuxer::TransmitFCon(TBool aCommand, CRfcommSAP* aSAP)
+	/**
+	   Send a FCon command frame.
+
+	   If aCommand is True then this should be a command, else it is a
+	   response.
+	**/
+	{
+	LOG(_L("RFCOMM: Trying to send FCon"));
+	if(!(iFlowStrategy->AllowFCOnOff(aCommand)))
+		return KErrNotSupported; //Occurs if CBFC is on.
+
+	LOG1(_L("RFCOMM: Sending FCon %S"),aCommand?&KCommandText:&KResponseText);
+	//FC
+	CRfcommMuxCtrlFrame* frm=NewSignalFrame(KFConCommandLength, aCommand, aSAP);
+	if(!frm)
+		return KErrNoMemory;
+
+	frm->SetCommandType(KFConType, aCommand);
+	frm->SetResponseNeeded(aCommand);
+	EnqueFrame(frm);
+	//
+	return KErrNone;
+	}
+
+TInt CRfcommMuxer::TransmitNSC(TBool aCommand, TUint8 aType)
+	/**
+	   Send a NSC command frame.
+
+	**/
+	{
+	LOG(_L("RFCOMM: Sending NSC"));
+	
+	CRfcommMuxCtrlFrame* frm=NewSignalFrame(KNSCCommandLength, EFalse);
+	if(!frm)
+		return KErrNoMemory;
+
+	frm->SetCommandType(KNSCType, EFalse);
+	aCommand = (aCommand) ? 1 : 0;
+	frm->PutByte(aType | (aCommand << 1));
+	EnqueFrame(frm);
+	
+	return KErrNone;
+	}
+
+
+TInt CRfcommMuxer::TransmitFCoff(TBool aCommand, CRfcommSAP* aSAP)
+	/**
+	   Send a FCoff command frame.
+
+	   If aCommand is True then this should be a command, else it is a
+	   response.
+	**/
+	{
+	LOG(_L("RFCOMM: Trying to send FCoff"));
+	if(!(iFlowStrategy->AllowFCOnOff(aCommand)))
+		return KErrNotSupported; //Occurs if CBFC is on.
+
+	LOG1(_L("RFCOMM: Sending FCoff %S"),aCommand?&KCommandText:&KResponseText);
+	//FC
+	CRfcommMuxCtrlFrame* frm=NewSignalFrame(KFConCommandLength, aCommand, aSAP);
+	if(!frm)
+		return KErrNoMemory;
+
+	frm->SetCommandType(KFCoffType, aCommand);
+	frm->SetResponseNeeded(aCommand);
+	EnqueFrame(frm);
+	//
+	return KErrNone;
+	}
+
+
+TInt CRfcommMuxer::TransmitMSC(TUint8 aDLCI, TBool aCommand, TUint8 aSignals, CRfcommSAP* aSAP)
+	/**
+	   Send out a MSC signalling command (or response) which travels in a UIH frame
+
+	   Format is (TS07.10 5.4.6.3.7):
+
+	   | Command | Length | DLCI | Signals |
+	**/
+	{
+	LOG3(_L("RFCOMM: SendMSC %S DLCI %d, Signals %x"),
+		(aCommand?&KCommandText:&KResponseText), aDLCI, aSignals);
+	
+	CRfcommMuxCtrlFrame* frm=NewSignalFrame(KMSCCommandLength, aCommand, aSAP);
+	if(!frm)
+		return KErrNoMemory;
+
+	frm->SetDLCI(aDLCI);	//	For use when matching DM responses to mux control command frames
+	frm->SetCommandType(KMSCType, aCommand);
+	frm->SetResponseNeeded(aCommand);
+	// DLCI
+	frm->PutByte((aDLCI << 2) | 0x03); // dlci, 1, EA
+	// Signals
+	iFlowStrategy->OutgoingMSCReviseSignals(aCommand, aSignals); //FC-bit to zero if CBFC.
+	frm->PutByte((aSignals << 1) | 0x01); // signals, EA
+	EnqueFrame(frm);
+	
+	return KErrNone;
+	}
+
+TInt CRfcommMuxer::TransmitTest(TBool aCommand, const TDesC8& aData, CRfcommSAP* aSAP)
+	/**
+	   Send out a Test signalling command
+	**/
+	{
+	LOG1(_L("RFCOMM: Sending Test %S"),(aCommand?&KCommandText:&KResponseText));
+	
+	CRfcommMuxCtrlFrame* frm=NewSignalFrame(aData.Length(), aCommand, aSAP);
+	if(!frm)
+		return KErrNoMemory;
+
+	frm->SetCommandType(KTestType, aCommand);
+	frm->SetResponseNeeded(aCommand);
+	// Put the data in
+	frm->PutData(aData);
+	EnqueFrame(frm);
+	
+	return KErrNone;
+	}
+
+TInt CRfcommMuxer::TransmitRLS(TBool aCommand, TUint8 aDLCI, TUint8 aStatus,
+							   CRfcommSAP* aSAP)
+	/**
+	   Send out a RLS signalling command
+	**/
+	{
+	CRfcommMuxCtrlFrame* frm=NewSignalFrame(KRLSCommandLength, aCommand, aSAP);
+	if(!frm)
+		return KErrNoMemory;
+
+	frm->SetDLCI(aDLCI);	//	For use when matching DM responses to mux control command frames
+	frm->SetCommandType(KRLSType, aCommand);
+	frm->SetResponseNeeded(aCommand);
+	// Now the DLCI 
+	frm->PutByte(aDLCI << 2 | 0x03);  // C/R & EA both 1
+	// Now the status
+	frm->PutByte(aStatus);
+	EnqueFrame(frm);
+	
+	return KErrNone;
+	}
+
+
+TUint8 CRfcommMuxer::BuildAddr(TUint8 aDLCI, TBool aCommand)
+	/**
+	   Add C/R and EA bits to an existing DLCI to create an address field.
+
+	   Format of address is:
+
+	   EA | C/R | DLCI |
+
+	   where C/R depends on whether this is a command frame and what
+	   the direction bit is set to.
+	**/
+
+	{
+	aDLCI<<=2; // Shift up for C/R and EA bits
+	aDLCI |= 0x01; // EA is 1
+	if(aCommand)
+		{
+		aDLCI |= iDirection << 1; // C/R is same as direction for commands
+		}
+	else
+		{
+		aDLCI |= (~iDirection & 1) << 1;  // Response has C/R =!Direction
+		}
+	return aDLCI;
+	}
+
+CRfcommMuxCtrlFrame* CRfcommMuxer::NewSignalFrame(TInt aCommandLength, TBool aCommand, CRfcommSAP* aSAP)
+/**
+	Create a new UIH frame for a mux command
+
+	NB The command length excludes the Type octet and the Length octet(s)
+**/
+	{
+	CRfcommMuxCtrlFrame* frm=NULL;
+	TRAPD(err, frm=CRfcommMuxCtrlFrame::NewL(aCommandLength, *this, aSAP));
+	if(!err)
+		{
+		// The C/R bit for the UIH frame is always 1 for the initiator
+		// and 0 for the responder, which is the same as for commands.
+		frm->SetAddress(BuildAddr(KMuxDLCI, ETrue));
+		frm->SetControl(KUIHCtrlField);
+		frm->SetResponseNeeded(aCommand);  // Commands need responses
+		}
+	return frm;	//	NB frm will be NULL if NewL failed.
+	}
+
+CRfcommCtrlFrame* CRfcommMuxer::NewFrame(CRfcommSAP* aSAP)
+	/**
+	   Returns a ctrl frame (SABM, DISC, DM, UA)
+	**/
+	{
+	return new CRfcommCtrlFrame(*this, aSAP);
+	}
+
+CRfcommDataFrame* CRfcommMuxer::NewDataFrame(TUint8 aDLCI, TInt aLen, 
+											 TUint8 aCredit, CRfcommSAP* aSAP)
+	/**
+	   Returns a new data (UIH) frame for the specified DLCI
+
+	   The C/R bit for the UIH frame is always 1 for the initiator and
+	   0 for the responder, which is the same as for commands.
+	**/
+	{
+	//	NB should be NULL if CRfcommDataFrame was not created
+	return iFlowStrategy->NewDataFrame(*this, BuildAddr(aDLCI, ETrue), aLen, aCredit, aSAP);
+	}
+
+TBool CRfcommMuxer::CheckFCS(TUint8 aFCS, TUint8 aCtrl)
+	/**
+	   Check the FCS on the incoming frame.
+
+	   FCS calculated over addr & ctrl for UIH, and addr, ctrl & len
+	   for the others.
+	**/
+	{
+	TUint8* data=&iNextPacket->Des()[0];
+	TInt len;
+	
+	if(aCtrl == KUIHCtrlField)
+		len=2;  // addr & ctrl fields
+	else
+		len=iCurrentHeaderLength;
+
+	return ::CheckFCS(data, len, aFCS);
+	}
+
+CRfcommSAP* CRfcommMuxer::FindSAP(const TUint8 aDLCI)
+	/**
+	   Find the SAP that is on this channel.
+
+	   If no such sap exists, return 0
+	**/
+	{
+	TDblQueIter<CRfcommSAP> iter(iSAPs);
+	CRfcommSAP* sap;
+
+	while(iter)
+		{
+		sap=iter++;
+		if(sap->DLCI() == aDLCI)
+			return sap;
+		}
+
+	iter=iBlockedSAPs;
+	while(iter)
+		{
+		sap=iter++;
+		if(sap->DLCI() == aDLCI)
+			return sap;
+		}
+
+	return 0;
+	}
+
+CRfcommSAP* CRfcommMuxer::FindConnectedOrListeningSAP(const TUint8 aDLCI)
+	/**
+	Find a SAP that is on this channel, or get a listening SAP from the protocol.
+
+	If there is neither a SAP on this channel or a listening SAP then return NULL.
+	**/
+	{
+	CRfcommSAP* mySAP=FindSAP(aDLCI);
+	if(mySAP==NULL)
+		{
+		//	No SAP is currently in existance for this DLCI. 
+		//	Find a listening SAP if possible.
+		TBTSockAddr addr;
+		addr.SetBTAddr(iRemoteAddr);
+		addr.SetPort(aDLCI>>1);	//	i.e. convert from DLCI to server channel
+		mySAP=iProtocol.FindIdleSAP(addr);
+		}
+
+	return mySAP;
+	}
+
+void CRfcommMuxer::EnqueFrame(CRfcommFrame* aFrm)
+	/**
+	   Add this frame to the outbound Q.
+
+	   Exactly where the frame is added depends on the type of frame,
+	   frames where Priority==ETrue are added just behind any other 
+	   priority frames in the queue, others are added right at the end.
+	**/
+	{
+	//FC
+	if(!aFrm->Priority())
+		{
+		//	Not a high priority frame. To the back of the entire queue!
+		iOutboundQ.AddLast(*aFrm);
+		}
+	else
+		{
+		//	A priority frame. Jump to the back of the priority queue!
+		
+		//	First, we need to find the back of the priority queue...
+		TDblQueIter<CRfcommFrame> iter(iOutboundQ);
+		while(iter && (*iter).Priority())
+			{
+			iter++;
+			}
+
+		if(iter)
+			{
+			//	We found a non-priority frame - jump into queue before this one
+			aFrm->iLink.AddBefore(&(*iter).iLink);
+			}
+		else
+			{
+			//	We ran out of frames, so either entire Q is made up of priority
+			//	frames, or the Q is empty. Either way, we can add to the end
+			//	of the queue.
+			iOutboundQ.AddLast(*aFrm);
+			}
+		}
+	iOutboundQLength++;
+	TryToSend();
+	}
+
+void CRfcommMuxer::TryToSend()
+	/**
+		Just Q an async callback
+	**/
+	{
+	iSendCallback->CallBack();
+	}
+		
+TInt CRfcommMuxer::TryToSendCallbackStatic(TAny* aMux)
+	{
+	(static_cast<CRfcommMuxer*>(aMux))->TryToSendCallback();
+	return EFalse;
+	}
+
+#ifdef __FLOG_ACTIVE
+void CRfcommMuxer::LogMuxCommand(CRfcommSAP* aSAP, CRfcommMuxer* /*aMux*/, TUint8 aCommand)
+	{
+	TUint8 signals;
+
+	switch(aCommand)
+		{
+	case KTestType:
+			LOG(_L("TestCommand")); 
+			break;
+		case KPNType:
+			LOG(_L("PN"));//
+			break;
+		case KRPNType:
+			LOG(_L("RPN"));//
+			break;
+		case KFConType:
+			//may need length byte value
+			LOG(_L("FcOn"));//
+			break;
+		case KFCoffType:
+			LOG(_L("FcOff"));//
+			break;
+		case KMSCType:
+			if (aSAP)
+				{
+				signals = aSAP->Signals();//V.24 signals in MSC
+				LOG1(_L("MSC %d"),signals);//
+				}
+			else
+				{
+				LOG(_L("RFCOMM: Unable to get signals for KMSCType command, unknown SAP"));
+				}
+			break;
+		case KNSCType:
+			LOG(_L("NSC"));//
+			break;
+		case KRLSType:
+			LOG(_L("RLS"));//
+		};
+
+	}
+
+void CRfcommMuxer::ExplainOutgoingFrame(CRfcommFrame* aFrm, CRfcommMuxer* aMux )
+	{
+#ifndef TCI
+	//Show what type of frame we have sent and the various parts of the frame which are important.
+
+	TInt frametype = aFrm->Type(); //Is it a Ctrl Frame, Data Frame, CreditDataFrame or Mux Ctrl Frame ?
+	TUint8 ctrlfield = aFrm->Ctrl();
+	TUint8 ctrl = ctrlfield&~KPollFinalBitmask; //tells whether SABM, DISC, UA, DM
+	TUint8 addressfield = aFrm->Address(); //Contains EA, CR, DLCI
+//	TUint8 dlci = aMux->DecodeDLCI(addressfield); //extracts dlci
+	TBool EA = addressfield & KEABitmask; //Is the EA bit set? 
+	TBool CR = addressfield & KCRBitmask; //Is the CR bit set?
+	TBool poll = ctrlfield & KPollFinalBitmask; //Is the p/f bit set?
+	
+	switch(frametype)
+		{
+		case KCtrlFrameType:
+			{
+			//CRfcommCtrlFrame* ctrlfrm=static_cast<CRfcommCtrlFrame*>(aFrm);
+			//TUint16 ctrlframelength = ctrlfrm->DataLength();
+			LOG(_L("Tx:"));
+			
+			if(ctrl==KSABMCtrlField)
+				{
+				LOG(_L("Tx: SABM"));
+				}
+			if(ctrl==KUACtrlField)
+				{
+				LOG(_L("Tx: UA"));
+				}
+			if(ctrl==KDMCtrlField)
+				{
+				LOG(_L("Tx: DM"));
+				}
+			if(ctrl==KDISCCtrlField)
+				{
+				LOG(_L("Tx: DISC"));
+				}
+			}
+			break;
+		case KDataFrameType: //CRfCommUIHFrame
+			{
+			CRfcommUIHFrame* uihfrm=static_cast<CRfcommUIHFrame*>(aFrm);
+			TUint16 uihframelength = uihfrm->DataLength();
+
+			if(poll)
+				LOG(_L("Tx: UIH credit data frame"));
+			
+			if (uihframelength<=127)
+				{
+				LOG(_L("Tx: UIH simple data frame"));
+				}
+			else
+				{
+				LOG(_L("Tx: UIH simple data frame"));
+				}
+			}
+			break;
+		case KCreditDataFrameType: //CRfcommCreditDataFrame
+			{
+			CRfcommCreditDataFrame* creditfrm=static_cast<CRfcommCreditDataFrame*>(aFrm);
+			TUint8 credits = creditfrm->Credit();
+			//BLOG Credits
+			TUint16 length = creditfrm->DataLength();
+			
+			if (length<=127)
+				{
+				LOG1(_L("Tx: Short UIH credit data frame with %d credits"), credits);
+				}
+			else
+				{
+				LOG1(_L("Tx :Long UIH credit data frame with %d credits"), credits);
+				}
+			}
+			break;
+		case KMuxCtrlFrameType: //CRfcommMuxCtrlFrame containing muxer messages/commands
+			{
+			//#ifndef TCI
+			CRfcommMuxCtrlFrame* muxfrm=static_cast<CRfcommMuxCtrlFrame*>(aFrm);
+			TUint8 muxdlci = muxfrm->iDLCI;
+			CRfcommSAP* sap = aMux->FindSAP(muxdlci); //Find the SAP that is on this dlci for aMux 
+			TUint8 command = muxfrm->CommandType(); 
+			if (sap)
+				{
+				LogMuxCommand(sap, aMux, command);
+				}
+			else
+				{
+				LOG1(_L("RFCOMM: Outgoing frame for unknown SAP: command = %d"),command);
+				LogMuxCommand(NULL, aMux, command);
+				}
+			//#endif
+			}
+			break;
+		default: //CRfcommDataFrame
+			break;
+		};
+	LOG1(_L("         P/F bit = %d"), poll);
+	LOG1(_L("         EA  bit = %d"), EA);
+	LOG1(_L("         C/R bit = %d"), CR);
+
+	LOG1(_L("Frame of type %d"), frametype);
+#endif
+	}
+#endif	//FLOG_ACTIVE
+
+void CRfcommMuxer::TryToSendCallback()
+	/**
+	   Attempt to send a frame off the bottom of the Q.
+
+		This is called back via an Async callback to break 
+		"up to the top and down to the bottom from the RunL"	
+
+	   Currently assumes that a frame will always fit in an L2CAP
+	   packet.
+	**/
+	{
+	LOG1(_L("RFCOMM: Send Callback with Q len %d"), iOutboundQLength);
+	CRfcommFrame* frm;
+
+	__ASSERT_DEBUG(!(iOutboundQLength !=0 && iOutboundQ.IsEmpty()),User::Panic(_L("Debug"),1));
+
+	while	(	!iOutboundQ.IsEmpty() && !L2CAPBlocked() && 
+				((iOutboundQ.First()->Priority()) || ClearToSend())	)
+		//	i.e. We have frames to send, L2CAP isn't blocked and we are EITHER clear to send
+		//	OR we have priority frames to send.
+		{
+		LOG1(_L("RFCOMM: Sending @ Q len %d"), iOutboundQLength);
+		frm=iOutboundQ.First();
+		
+		FTRACE(ExplainOutgoingFrame(frm, this));
+#ifdef _DEBUG
+		//FOR DEBUG PURPOSES ONLY
+		if(frm->Address() != KMuxDLCI)
+			{
+			CRfcommSAP* sap = FindSAP(DecodeDLCI(frm->Address()));
+			if(sap)
+				{
+				switch(frm->Ctrl())
+					{
+				case KUIHCtrlField:
+				case KUIHCBFCCtrlField:
+					if(frm->DataLength())
+						{
+						iDataFramesSent++;
+						LOG1(_L("RFCOMM: Total data frames transmitted by mux (=> caused Tx decrement): %d"), iDataFramesSent);
+						}
+					break;
+				default:
+					break;
+					}
+				}
+			}
+#endif
+
+		// Datagram send, so 0 or length of packet returned
+
+		if(!iBoundSAP->Write(frm->Data(), 0))
+			{
+			LOG(_L("RFCOMM: L2CAP send failed, restoring frame"));
+			L2CAPBlocked(ETrue);
+			}
+		else
+			{
+			// Managed to send this packet, so either move to responseQ or delete
+			frm->iLink.Deque();
+			if(frm->ResponseNeeded())
+				{
+				iResponseQ.AddLast(*frm);
+				frm->QueResponseTimer();
+				}
+			else
+				{
+				delete frm;
+				}
+			iOutboundQLength--;
+			}
+		}
+	
+	// We may have made room for some new sap to send, so let them know
+	SignalSAPsCanSend();
+	
+	// Equally, we may have sent all the remaining queued frames, and have no SAPs
+	// left to service.
+	CheckForIdle();
+	}
+
+
+void CRfcommMuxer::SignalSAPsCanSend()
+	/**
+	   We may be able to let some saps send, so start signalling them
+	**/
+	{
+	CRfcommSAP* sap;
+	TDblQueIter<CRfcommSAP> iter(iBlockedSAPs);
+
+	while(iter && !L2CAPBlocked() && ClearToSend())
+		{
+		sap = iter++;
+		SetSendBlocked(*sap, EFalse);
+		sap->CanSend();
+		}
+	}
+
+void CRfcommMuxer::CheckForIdle(TBool aClosing)
+	/**
+	   Check to see if we're still needed.  If not, Q a delayed delete.
+	**/
+	{
+	if (aClosing)
+		{
+		// If we could be in the position where we have no SAPs then the next
+		// time we set the idle timer we should use the closing value.
+		iMuxIdleTimeout = KRfcommMuxIdleTimeoutClosing;
+		}
+		
+	if(iOutboundQLength==0 && iSAPs.IsEmpty() && iBlockedSAPs.IsEmpty())
+		{
+		QueIdleTimer();
+		}
+	}
+
+void CRfcommMuxer::QueIdleTimer()
+	/**
+	   Queues the idle timer if necessary
+	**/
+	{
+	if(!iIdleTimerQueued)
+		{
+		LOG2(_L("RFCOMM: Mux 0x%08x Q idle timer for %d microsecs"), this, iMuxIdleTimeout);
+		iIdleTimerQueued=ETrue;
+		BTSocketTimer::Queue(iMuxIdleTimeout, iIdleTimerEntry);
+		}
+	}
+
+void CRfcommMuxer::DequeIdleTimer()
+	/**
+	   Deques idle timer if necessary
+	**/
+	{
+	if(iIdleTimerQueued)
+		{
+		LOG1(_L("RFCOMM: Mux 0x%08x deQ idle timer"), this);
+		BTSocketTimer::Remove(iIdleTimerEntry);
+		iIdleTimerQueued=EFalse;
+		}
+	}
+
+TInt CRfcommMuxer::IdleTimerExpired(TAny* aMux)
+	/**
+	   Static idle callback.
+
+	   This is entered after all our saps have gone away.
+	   
+	**/
+	{
+ 	LOG1(_L("RFCOMM: Mux %08x idle timer expired"), aMux);
+	// Start the delete process
+	CRfcommMuxer* mux = static_cast<CRfcommMuxer*>(aMux);
+	mux->iIdleTimerQueued=EFalse;  // Obviously...
+
+	__ASSERT_DEBUG(mux->iSAPs.IsEmpty() && mux->iBlockedSAPs.IsEmpty(),
+		           Panic(ERfcommIdleTimeoutWhenNotIdle));
+	
+	mux->iMuxChannel->Close();	//	May call back syncronously or asynchronously
+								//	into MuxChannelClosed
+	return FALSE;
+	}
+
+void CRfcommMuxer::MuxChannelClosed()
+	{
+	// remove this muxer
+	iProtocol.MuxDown(*this);	
+	}
+
+void CRfcommMuxer::DeleteQueuedFrames()
+	/**
+		Delete all frames that are on either Q.
+	**/	
+	{
+	TDblQueIter<CRfcommFrame> iter(iOutboundQ);
+	CRfcommFrame* frm;
+	
+	while(iter)
+		{
+		frm=iter++;
+		delete frm;
+		}
+	iOutboundQLength=0;
+	
+	iter=iResponseQ;
+	while(iter)
+		{
+		frm=iter++;
+		delete frm;
+		}
+	}
+