--- /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;
+ }
+ }
+