diff -r 000000000000 -r 29b1cd4cb562 bluetooth/btstack/rfcomm/rfcommmuxer.cpp --- /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 + +#include + +#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 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& 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 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 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 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 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 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 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 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 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 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(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(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 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(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((*iNextPacket)[iCurrentHeaderLength+offset] >> 2); + LOG1(_L("RFCOMM: DLCI %d"), dlci); + // ...and the next is the V.24 signals + TUint8 signals = static_cast((*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(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(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 iter(iResponseQ); + CRfcommFrame* frm; + + while(iter) + { + frm=iter++; + if(frm->Type() == KMuxCtrlFrameType) + { + CRfcommMuxCtrlFrame* muxframe=static_cast(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 iter(iResponseQ); + CRfcommFrame* frm; + + while(iter) + { + frm=iter++; + if(frm->Type() == KMuxCtrlFrameType) + { + CRfcommMuxCtrlFrame* muxframe=static_cast(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 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 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(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(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(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(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(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 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(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 iter(iOutboundQ); + CRfcommFrame* frm; + + while(iter) + { + frm=iter++; + delete frm; + } + iOutboundQLength=0; + + iter=iResponseQ; + while(iter) + { + frm=iter++; + delete frm; + } + } +