// 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_ACTIVEconst TUint8 KBitsForNumberInTInt = 32 - 1; // -1 is because we don't count the sign bitCRfcommMuxer* 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(); }//CBFCCRfcommFlowStrategyFactory::TFlowStrategies CRfcommMuxer::FlowType() { return iFlowStrategy->FlowType(); }#ifdef NO_CBFCTBool CRfcommMuxer::FlowType(CRfcommFlowStrategyFactory::TFlowStrategies /*aFlow*/) { return EFalse; }#elseTBool 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; }#endifvoid 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 neededTInt 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_ACTIVEvoid 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_ACTIVEvoid 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; } }