bluetooth/btstack/rfcomm/rfcomm.cpp
changeset 0 29b1cd4cb562
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btstack/rfcomm/rfcomm.cpp	Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,699 @@
+// 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);
+#endif
+
+CRfcommProtocol::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 functions
+
+CServProviderBase* 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 functions
+
+void 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;
+		}
+	}
+