// 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:// Implements the Protocol object// //#include <bluetooth/logger.h>#include <e32def.h>#include "rfcomm.h"#include "rfcommstates.h"#include "rfcommmuxer.h"#include "rfcommmuxchannel.h"#include "rfcommconsts.h" #include "bt.h"#include "l2cap.h"#include "IncomingConnListener.h"#ifdef __FLOG_ACTIVE_LIT8(KLogComponent, LOG_COMPONENT_RFCOMM);#endifCRfcommProtocol::CRfcommProtocol(CBTSecMan& aSecMan, RBTControlPlane& aControlPlane, CBTCodServiceMan& aCodMan): CBluetoothProtocolBase(aSecMan, aControlPlane, aCodMan), iMuxes(_FOFF(CRfcommMuxer, iLink)), iListeningSAPs(_FOFF(CRfcommSAP, iListeningLink)), iBoundSAPs(_FOFF(CRfcommSAP,iBoundLink)), iCBFCDisallowed(EFalse) { LOG_FUNC TCallBack cb(TryToClose, this); iIdleTimerEntry.Set(cb); }CRfcommProtocol::~CRfcommProtocol() /** Destructor. We don't clear up the SAPs as they are owned by ESOCK and it is up to it to destroy them. **/ { LOG_FUNC RemoveIdleTimerEntry(); if(LowerProtocol()) LowerProtocol()->Close(); // Matches the bind delete iStateFactory; delete iFlowStrategyFactory; delete iMuxChannelStateFactory;#ifdef __FLOG_ACTIVE CLOSE_LOGGER#endif }CRfcommProtocol* CRfcommProtocol::NewL(CBTSecMan& aSecMan, RBTControlPlane& aControlPlane, CBTCodServiceMan& aCodMan) {#ifdef __FLOG_ACTIVE CONNECT_LOGGER#endif LOG_STATIC_FUNC CRfcommProtocol* i=new (ELeave) CRfcommProtocol(aSecMan, aControlPlane, aCodMan); CleanupStack::PushL(i); i->ConstructL(); CleanupStack::Pop(i); return i; }void CRfcommProtocol::ConstructL() { LOG_FUNC }void CRfcommProtocol::InitL(TDesC& /*aTag*/)/** Pre-binding initialise. Alloc any stuff we need. This will only ever be called once during the lifetime of this protocol.**/ { LOG_FUNC // Create all the things we need now iStateFactory=CRfcommStateFactory::NewL(); iFlowStrategyFactory=CRfcommFlowStrategyFactory::NewL(); iMuxChannelStateFactory=CMuxChannelStateFactory::NewL(); }void CRfcommProtocol::StartL()/** Binding complete.**/ { LOG_FUNC // Should check that we're bound now. if(!LowerProtocol()) { User::Leave(KErrRfcommNotBound); } }void CRfcommProtocol::BindToL(CProtocolBase* aProtocol) /*** Request by Protocol Mgr to bind to the specified protocol. We can only be bound to one lower layer protocol, so the function leaves if we are already bound. @param aProtocol The protocol we need to bind to. **/ { LOG_FUNC if(!LowerProtocol()) {#ifdef _DEBUG TServerProtocolDesc prtDesc; aProtocol->Identify(&prtDesc); if(prtDesc.iAddrFamily!=KBTAddrFamily || prtDesc.iProtocol!=KL2CAP) { User::Leave(KErrBtEskError); }#endif iLowerProtocol=static_cast<CBluetoothProtocolBase*>(aProtocol); LowerProtocol()->BindL(this, 0); // id not used LowerProtocol()->Open(); } else { User::Leave(KErrRfcommAlreadyBound); } }// Factory functionsCServProviderBase* CRfcommProtocol::NewSAPL(TUint aSockType) /** Create a new SAP. The SAP returned is owned by the caller -- this protocol will not clean it up. esock uses this function to create a new SAP, and esock will delete when it is finished with it. **/ { LOG_FUNC CRfcommSAP* sap=0; switch(aSockType) { case KSockStream: // the connected type sap=CRfcommSAP::NewL(*this); break; default: User::Leave(KErrNotSupported); break; } LOG1(_L("RFCOMM: NewSAPL created SAP %08x"), sap); return sap; }TInt CRfcommProtocol::StartProtocolListening()/** A SAP has wanted to start us listening...we look after the values to do this**/ { LOG_FUNC TInt rerr = CBluetoothProtocolBase::StartListening(KRFCOMMPSM, KSockSeqPacket, KRFCOMMIncomingConnQueueSize, KUidServiceRfcomm); if(rerr == KErrNone) // We should now have an L2Cap listener. // Use it to setup L2Cap config for RFComm. { TL2CapConfigPkg configPkg; SetL2CapConfig(configPkg); rerr = Listener().SetOption(KSolBtL2CAP, KL2CAPUpdateChannelConfig, configPkg); if(rerr!=KErrNone) { StopProtocolListening(); //clean up } } return rerr; }// Query functionsvoid CRfcommProtocol::SetL2CapConfig(TL2CapConfigPkg& aConfigPkg)/* L2Cap configuration required by RFComm*/ { LOG_FUNC aConfigPkg().ConfigureReliableChannel(TL2CapConfig::EDefaultRetransmission); aConfigPkg().SetMaxReceiveUnitSize(KMaxL2CAPMTUAllowed); }void CRfcommProtocol::Identify(TServerProtocolDesc *aDesc)const//// Identify request from SOCKET server// { LOG_FUNC CRfcommProtocol::ProtocolIdentity(aDesc); }void CRfcommProtocol::ProtocolIdentity(TServerProtocolDesc* aDesc) { LOG_STATIC_FUNC _LIT(name,"RFCOMM"); aDesc->iProtocol=KRFCOMM; aDesc->iName=name; aDesc->iAddrFamily=KBTAddrFamily; aDesc->iSockType=KSockStream; aDesc->iVersion=TVersion(KBTMajor,KBTMinor,KBTBuild); aDesc->iByteOrder=ELittleEndian; aDesc->iServiceInfo=KRFCOMMStreamServiceInfo; aDesc->iNamingServices=0; aDesc->iSecurity=KSocketNoSecurity; aDesc->iMessageSize=KSocketMessageSizeIsStream; aDesc->iServiceTypeInfo=ESocketSupport|ECantProcessMBufChains|EPreferDescriptors|EUseCanSend; aDesc->iNumSockets=100; }void CRfcommProtocol::CloseNow() /** Our reference is now zero, so start to close. We don't actually close, merely Q a timer for a later close down. This close can be prempted by another open. **/ { LOG_FUNC iClosePending = ETrue; QueIdleTimerEntry(); }void CRfcommProtocol::Open() /** Request to open the protocol. The protocol may be repeatedly opened and closed. The order of calls is InitL, [Open *n , Close * n, CloseNow] * m etc. **/ { LOG_FUNC iClosePending = EFalse; RemoveIdleTimerEntry(); CProtocolBase::Open(); }void CRfcommProtocol::Close() /** This is one session closing. **/ { LOG_FUNC CProtocolBase::Close(); }CRfcommFlowStrategyFactory* CRfcommProtocol::FlowStrategyFactory() const { LOG_FUNC return iFlowStrategyFactory; }CRfcommStateFactory* CRfcommProtocol::StateFactory() const { LOG_FUNC return iStateFactory; }TInt CRfcommProtocol::GetMux(TBTSockAddr& aRemoteAddr, CRfcommMuxer*& aMuxer) /** Gets a muxer for the given BT device address. If it finds the muxer or successfully creates a new muxer, it returns with KErrNone and a valid pointer to the correct muxer. On a failure, it will return with a standard error, without a pointer to a muxer. This flavour of GetMux is used when a muxer is being created in response to a local request to connect to a remote device (i.e. no existing L2CAP connection exists). **/ { LOG_FUNC TBTDevAddr devAddr=aRemoteAddr.BTAddr(); aMuxer=FindMux(devAddr); if(!aMuxer) { // Need to create one TRAPD(err, aMuxer=CRfcommMuxer::NewL(*this, *LowerProtocol(), *iMuxChannelStateFactory)); if(err != KErrNone) { return err; } LOG1(_L("RFCOMM: (Outgoing) Creating new mux %08x"), aMuxer); LOG6(_L(" for BT Device %02x %02x %02x %02x %02x %02x"), TUint8(devAddr[0]), TUint8(devAddr[1]), TUint8(devAddr[2]), TUint8(devAddr[3]), TUint8(devAddr[4]), TUint8(devAddr[5])); // Add mux to the Q iMuxes.AddFirst(*aMuxer); aMuxer->Bind(devAddr); // Sets the remote addr } return KErrNone; }TInt CRfcommProtocol::BearerConnectComplete(const TBTDevAddr& aAddr, CServProviderBase* aSAP) { LOG_FUNC CRfcommMuxer* muxer = NULL; // don't care about getting pointer to muxer return GetMux(aAddr, aSAP, muxer); // NB If no error occurs, ownership of aSSP passes to the muxer. }const TBTDevAddr& CRfcommProtocol::LocalBTAddr() const { LOG_FUNC return static_cast<const CL2CAPProtocol*>(iLowerProtocol)->LocalBTAddr(); }TInt CRfcommProtocol::GetMux(const TBTDevAddr& aRemoteAddr, CServProviderBase* aL2CAPConSAP, CRfcommMuxer*& aMuxer) /** Gets a muxer for the given BT device address, using the L2CAP connection provided. If it is found that a muxer for the specified BT device already exists, KErrAlreadyExists will be returned. If a new muxer is successfully created, KErrNone will be returned along with a valid pointer to the correct muxer in aMuxer This flavour of GetMux is used when a muxer is being created in response to a remote request to connect to the local device (i.e. an L2CAP SAP has been created due to an incoming connection). **/ { LOG_FUNC#ifdef _DEBUG // Check that the device address and the device of the L2CAP remote address match!! TBTSockAddr addr; aL2CAPConSAP->RemName(addr); __ASSERT_DEBUG(addr.BTAddr()==aRemoteAddr,Panic(ERfcommMismatchedAddressAndSAP));#endif//_DEBUG aMuxer=FindMux(aRemoteAddr); TInt err=KErrNone; if(aMuxer!=NULL) // i.e. a muxer already exists err=KErrAlreadyExists; else { TRAP(err, aMuxer=CRfcommMuxer::NewL(*this, aL2CAPConSAP, *iMuxChannelStateFactory)); LOG1(_L("RFCOMM: (Incoming) Creating new mux %08x"), aMuxer); LOG6(_L(" for BT Device %02x %02x %02x %02x %02x %02x"), TUint8(aRemoteAddr[0]), TUint8(aRemoteAddr[1]), TUint8(aRemoteAddr[2]), TUint8(aRemoteAddr[3]), TUint8(aRemoteAddr[4]), TUint8(aRemoteAddr[5])); if(!err) // Add mux to the Q iMuxes.AddFirst(*aMuxer); } return err; }CRfcommMuxer* CRfcommProtocol::FindMux(const TBTDevAddr& aAddr) /** Find the existing muxer for this address. @return Pointer to a muxer for this address, or NULL if it doesn't exist. **/ { LOG_STATIC_FUNC TDblQueIter<CRfcommMuxer> iter(iMuxes); CRfcommMuxer* mux; while(iter) { mux=iter++; // Find a mux aimed at the same addr, then check it's not on its way down if((mux->iRemoteAddr == aAddr) && mux->iMuxChannel->CanAttachSAP()) { // We have a mux, so return it return mux; } } // No mux return NULL; }void CRfcommProtocol::MuxDown(CRfcommMuxer& aMux) /** Called by the mux when it's dying. We clean up and delete it. **/ { LOG_FUNC LOG1(_L("RFCOMM: Mux %08x down"), &aMux); aMux.iLink.Deque(); delete &aMux; if(CheckForClose()) QueIdleTimerEntry(); }void CRfcommProtocol::AddIdleSAP(CRfcommSAP& aSAP) /** Used to inform the protocol of a SAP which has been told to listen **/ { LOG_FUNC#if defined(_DEBUG) || defined(__FLOG_ACTIVE) TDblQueIter<CRfcommSAP> iter(iListeningSAPs);#endif#ifdef _DEBUG // Check that the SAP about to be added is not already on the listening list CRfcommSAP* mySAP=NULL; TBool found(EFalse); while(iter && !found) { mySAP=iter++; if(mySAP==&aSAP) found=ETrue; } if(found) Panic(ERfcommRequeuingListeningSAP);#endif iListeningSAPs.AddLast(aSAP);#ifdef __FLOG_ACTIVE iter.SetToFirst(); while(iter) { LOG1(_L("RFCOMM: Listening on channel %d"),(iter++)->ServerChannel()); };#endif //__FLOG_ACTIVE }void CRfcommProtocol::AddBoundSAP(CRfcommSAP& aSAP) /** Used to inform the protocol of a SAP which has been told to bind **/ { LOG_FUNC #ifdef _DEBUG TDblQueIter<CRfcommSAP> iter(iBoundSAPs); // Check that the SAP about to be added is not already on the Bound list CRfcommSAP* mySAP=NULL; TBool found(EFalse); while(iter && !found) { mySAP=iter++; if(mySAP==&aSAP) found=ETrue; } if(found) Panic(ERfcommRequeuingBoundSAP); #endif iBoundSAPs.AddLast(aSAP); }void CRfcommProtocol::RemoveIdleSAP(CRfcommSAP& aSAP) /** Used to inform the protocol of a listening SAP which is about to be destroyed **/ { LOG_FUNC#ifdef _DEBUG // Should check that the SAP about to be dequed is actually on the listening list CRfcommSAP* SAP=NULL; TBool found(EFalse); TDblQueIter<CRfcommSAP> iter(iListeningSAPs); while(iter && !found) { SAP=iter++; if(SAP==&aSAP) found=ETrue; } if(!found) Panic(ERfcommDequeingBadListeningSAP);#endif//_DEBUG aSAP.iListeningLink.Deque(); }void CRfcommProtocol::RemoveBoundSAP(CRfcommSAP& aSAP) /** Used to inform the protocol of a bound SAP which is about to be destroyed **/ { LOG_FUNC // check that the SAP about to be dequeued is actually on the bound list __ASSERT_DEBUG(SAPIsBound(aSAP),Panic(ERfcommDequeuingBadBoundSAP)); aSAP.iBoundLink.Deque(); }TBool CRfcommProtocol::SAPIsBound(const CRfcommSAP& aSAP) /** Used to inform the protocol if a SAP is bound **/ { LOG_FUNC CRfcommSAP* SAP=NULL; TBool found(EFalse); TDblQueIter<CRfcommSAP> iter(iBoundSAPs); while(iter && !found) { SAP=iter++; if(SAP==&aSAP) found=ETrue; } return found; }CRfcommSAP* CRfcommProtocol::FindInboundConnectedSAP(const TBTSockAddr& aSockAddr) /** Used to enquire of the protocol whether there is a cloned SAP created from a SAP told to listen on the specified address. This can be used to determine if a local Server Channel is in use even though the original listening SAP no longer exists. Returns NULL if no such SAP exists. **/ { LOG_FUNC CRfcommSAP* SAP=NULL; CRfcommMuxer* mux=NULL; TUint8 dlci=0; // Search for matching SAP across all Muxes TDblQueIter<CRfcommMuxer> iter(iMuxes); while(iter && !SAP) { mux=iter++; dlci=mux->MakeInboundDLCI(aSockAddr.Port()); SAP=mux->FindSAP(dlci); } return SAP; }CRfcommSAP* CRfcommProtocol::FindIdleSAP(const TBTSockAddr& aSockAddr) /** Used to enquire of the protocol whether there is a SAP which has been told to listen on the specified address. Returns NULL if no such SAP exists. **/ { LOG_FUNC CRfcommSAP* SAP=NULL; TDblQueIter<CRfcommSAP> iter(iListeningSAPs); while(iter) { SAP=iter++; if(SAP->ListeningTo(aSockAddr)) // Found a SAP which is interested... return SAP; } return NULL; }CRfcommSAP* CRfcommProtocol::FindBoundSAP(const TBTSockAddr& aSockAddr) { LOG_FUNC CRfcommSAP* SAP=NULL; TDblQueIter<CRfcommSAP> iter(iBoundSAPs); while(iter) { SAP=iter++; if(SAP->BoundTo(aSockAddr)) // Found a SAP which is interested... return SAP; } return NULL; }TInt CRfcommProtocol::FindFreeServerChannel() /** Returns a server channel on which this device is not currently bound or listening **/ { LOG_FUNC // Array indexed from 0 to 29 but valid server channels are 1 to 30 so // valid server channel = index + 1 TFixedArray<TBool, KMaxRfcommServerChannel> channelInUse; TDblQueIter<CRfcommSAP> iterListening(iListeningSAPs); TDblQueIter<CRfcommSAP> iterBound(iBoundSAPs); TDblQueIter<CRfcommMuxer> iterMuxes(iMuxes); for(TInt i = 0; i < KMaxRfcommServerChannel; i++) { channelInUse[i] = EFalse; } while(iterListening) { channelInUse[(*iterListening++).ServerChannel() - 1] = ETrue; } while(iterBound) { channelInUse[(*iterBound++).ServerChannel() - 1] = ETrue; } // Need to check Muxes for cloned SAPs that are not taken account of already // because the associated listening SAP has been removed while(iterMuxes) { (*iterMuxes++).GetInboundServerChannelsInUse(channelInUse); } TInt index = 0; while(index < KMaxRfcommServerChannel && channelInUse[index++]) { // Do nothing, index will be set appropriately when loop exits } return index < KMaxRfcommServerChannel ? index : KErrInUse; }void CRfcommProtocol::DisallowCBFC() { LOG_FUNC iCBFCDisallowed = ETrue; }void CRfcommProtocol::AllowCBFC() { LOG_FUNC iCBFCDisallowed = EFalse; }TBool CRfcommProtocol::CBFCDisallowed() { LOG_FUNC return iCBFCDisallowed; }TBool CRfcommProtocol::CheckForClose() /** Called to check whether we can close down **/ { LOG_FUNC return (iClosePending && iMuxes.IsEmpty()); }TInt CRfcommProtocol::TryToClose(TAny* aProtocol) { LOG_STATIC_FUNC CRfcommProtocol* p=static_cast<CRfcommProtocol*>(aProtocol); p->iIdleEntryQueued = EFalse; if (p->iClosePending && p->iMuxes.IsEmpty()) { LOG(_L("RFCOMM: protocol can close now")); p->CanClose(); } return EFalse; }void CRfcommProtocol::RemoveIdleTimerEntry() /** Takes us off the idle timer Q, if we're on it. **/ { LOG_FUNC if(iIdleEntryQueued) { LOG(_L("RFCOMM: Protocol removing timer entry")); BTSocketTimer::Remove(iIdleTimerEntry); iIdleEntryQueued=EFalse; } }void CRfcommProtocol::QueIdleTimerEntry() /** Q a timer to delete us. **/ { LOG_FUNC if(!iIdleEntryQueued) { LOG(_L("RFCOMM: Protocol adding timer entry")); BTSocketTimer::Queue(KRfcommIdleTimeout, iIdleTimerEntry); iIdleEntryQueued = ETrue; } }