bluetooth/btstack/linkmgr/linkmuxer.cpp
changeset 0 29b1cd4cb562
child 16 9f17f914e828
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btstack/linkmgr/linkmuxer.cpp	Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,314 @@
+// 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:
+// HCIChannelMux.cpp
+// HCI Channel Multiplexer implementation. 
+// 
+//
+
+#include <bluetooth/logger.h>
+#include <bluetooth/hcicommandqueue.h>
+#include "linkmuxer.h"
+#include "AclDataQController.h"
+#include "linkconsts.h"
+#include "hcifacade.h"
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_LINKMGR);
+#endif
+
+CLinkMuxer* CLinkMuxer::NewL(CLinkMgrProtocol& aLinkMgrProtocol, CHCIFacade& aHCIFacade)
+	{
+	LOG_STATIC_FUNC
+	CLinkMuxer* self = new (ELeave) CLinkMuxer(aLinkMgrProtocol, aHCIFacade);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop();
+	return self;
+	}
+
+void CLinkMuxer::ConstructL()
+	{
+	LOG_FUNC
+	// Get pointer to CommandQ
+	iCommandController = &iHCIFacade.CommandQController();
+
+#ifdef PROXY_COMMUNICATES
+	const TUint16 noBufs = iHCIFacade.ReadACLReportingInterval();
+#else
+	const TUint16 noBufs = ++iHCIFacade.ReadACLReportingInterval(); // another slot for bc handle
+#endif
+	const TUint16 framingOverhead = iHCIFacade.ReadACLFramingOverhead();
+
+	iDataController= CACLDataQController::NewL(iLinkMgrProtocol, 
+			                                   *this,
+											   KHCIACLMinDataBufferSize,
+											   framingOverhead,
+			                                   noBufs);
+
+	// Tell the limited data Q controller our data credits from HC is 0 
+	// when we establish how many HC really has we will notify again
+	// that will happen on the reception of the first ReadBufferSize result
+	iDataController->InitialDataCredits(0);
+
+	iChannelsFree = iHCIFacade.HCTLState();
+
+	TCallBack cb(TryToSendCallBackStatic, this);
+	iSendCallBack = new (ELeave)CAsyncCallBack(cb, EActiveMedPriority);
+	}
+
+CLinkMuxer::CLinkMuxer(CLinkMgrProtocol& aLinkMgrProtocol, CHCIFacade& aHCIFacade)
+	: iHCIFacade(aHCIFacade), iChannelsFree(KHCITransportNoChannels), iLinkMgrProtocol(aLinkMgrProtocol) 
+/**
+	We expect the transport to notify us when the transport channels are ready
+**/
+	{
+	LOG_FUNC
+	}
+
+CLinkMuxer::~CLinkMuxer()
+	{
+	LOG_FUNC
+    delete iSendCallBack;
+	delete iDataController;
+	}
+
+TInt CLinkMuxer::ACLPacketMTU() const
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(iACLPacketMTU, Panic(ELinkMgrPacketMTUUseBeforeSet));
+	return iACLPacketMTU;
+	}
+
+
+TInt CLinkMuxer::TryToSendCallBackStatic(TAny* aCLinkMuxer)
+	{
+	LOG_STATIC_FUNC
+	static_cast<CLinkMuxer*>(aCLinkMuxer)->DoSend();
+	return EFalse;
+	}
+
+void CLinkMuxer::TryToSend()
+	{
+	LOG_FUNC
+	// fireup async callback
+	iSendCallBack->CallBack();
+	}
+
+void CLinkMuxer::DoSend()
+/**
+	This is the method where it is decided whether a command or data packet will be issued next.
+	A send may not occur - the signal to send may have occurred through the addition to a Q
+	whereas the destination channel for the relevant packet may not be free
+
+	There could have been a 'Strategy' pattern here, for applying different
+	scheduling in the future. The likely hood of this is very low, hence the 
+	simpler implementation.
+
+	The scheduling is very simple:
+	If we have command packets to send and credits to do so ,send one
+	otherwise send a data packet if possible/available.
+*/
+	{
+	LOG_FUNC
+	LOG1(_L("LinkMuxer: Dosend- free channels 0x%04x"), iChannelsFree);
+
+	if (!iChannelsFree)
+		return;  // no channel to send on available
+
+	TUint   theDataQLevel=0;
+	TUint16 theDataCredits=0;
+
+	iDataController->GetDataQRecords(theDataQLevel,theDataCredits);
+	
+	// the assumption here is that a channel is free
+	// note the implicit precedence of commands over data
+	
+	if (iChannelsFree & KHCITransportCommandChannel)
+		{
+		// have commands and command channel is free
+		iCommandController->DoSend();
+		}
+
+	if((iChannelsFree & KHCITransportACLDataChannel)
+		&& theDataQLevel && theDataCredits)
+		{
+		// have data, and credits, and data channel is free, try to send some data
+		iDataController->IssueNextACLDataFragment(); // may or may not issue!
+		}
+	}
+
+
+/**
+	When channels become free this method gets called to tell the muxer
+	The muxer takes the opportunity to attempt a send
+*/
+
+void CLinkMuxer::ChannelsFree(THCITransportChannel aChannel)
+	{
+	LOG_FUNC
+	iChannelsFree |= aChannel;
+
+	if (iChannelsFree != KHCITransportNoChannels)
+		{
+		// only call async callback - don't call synchronous
+		TryToSend();
+		}
+	}
+/**
+	When channels become closed this method is called to remove them
+	from the "free" list.
+	
+	Note: The channels that require closing have their respective bits set
+		hence the reason for the bit inversion within this method.
+*/
+
+void CLinkMuxer::ChannelsClosed(THCITransportChannel aChannel)
+	{
+	LOG_FUNC
+	iChannelsFree &= (~aChannel);
+	iChannelsFree &= KHCITransportAllChannels;
+	}
+
+
+#ifdef STACK_SCO_DATA
+TBool CLinkMuxer::CanWriteSCOData()
+	{
+	LOG_FUNC
+	return (iChannelsFree & KHCITransportSCODataChannel);
+	}
+#endif
+
+void CLinkMuxer::RecordHostControllerToHostFlowControl(TBool aFlowFlag)
+/**
+	Called when HCIFacade receives a Command Complete event to the SetHostControllerToHostFlowControl command
+	@param	aFlowFlag - true is command succeeded, false otherwise
+
+**/
+    {
+	LOG_FUNC
+     // check our current mode
+    switch (iFlowControlMode)
+        {
+        case ENoFlowControl:
+            {
+#ifdef _DEBUG
+            if(aFlowFlag) 
+                {iFlowControlMode=EFlowControlFromHostControllerOnly;}
+#else
+            Panic(ELinkMgrNoFlowControlSetInReleaseBuild);
+#endif
+            break;
+        }
+        case EFlowControlToHostControllerOnly:
+            {
+            if(aFlowFlag)
+                {iFlowControlMode=ETwoWayFlowControlEnabled;}
+            break;
+            }
+        case EFlowControlFromHostControllerOnly:
+            {
+#ifdef _DEBUG
+            if(aFlowFlag==EFalse)
+                {iFlowControlMode=ENoFlowControl;}
+#else
+            Panic(ELinkMgrNoFlowControlSetInReleaseBuild);
+#endif
+            break;
+            }
+        case ETwoWayFlowControlEnabled:
+            {
+            if(aFlowFlag==EFalse)
+				{
+				// tried to do two-way but the HC can't to HC->H FC
+				iFlowControlMode=EFlowControlToHostControllerOnly;
+				}
+            break;
+            }
+		default:
+			Panic(ELinkMgrNoSuchFlowControlMode);
+        } //switch
+	}    
+
+
+CACLDataQController* CLinkMuxer::HandleLocalReadBufferSizeResult(
+			TUint16 aAclMaxLen,
+			TUint8 /*aScoMaxLen*/,
+			TUint16 aNoACL,
+			TUint16 /*aNoSCO*/)
+/**
+	This method handles the results of the local (intra HCI) inquiry of the HC 
+	buffer capabilities.
+	The results are then used for setting the HCI MTU (=HC MTU) and for 
+	modeling the HC's pool usage.
+	This method must be called very early upon start-up in order to set up the 
+	data Q and its controller, for the flow control between L2CAP -> HCI -> 
+	HC. 
+
+	@param aAclMaxLen Maximum length of each ACLDataPacket.
+	@param aScoMaxLen Maximum length of each SCODataPacket.
+	@param aNoACL Total no. of ACL Data Packets.
+	@param aNoSCO Total no. of SCO Data Packets.
+	@return Return address (not ownership) of new/same ACL Data Controller.
+*/
+    {
+	LOG_FUNC
+	LOG2(_L("CLinkMuxer::HandleLocalReadBufferSizeResult aAclMaxLen = %d, aNoACL = %d"), 
+		aAclMaxLen, aNoACL);
+
+	const TUint preferredNumBuffers = Max(
+			static_cast<TInt>(KHCIPreferedNumberOfHCIACLDataBuffers),
+			static_cast<TInt>(aNoACL)
+			);
+
+	// If the HC supports more buffers than we already allocated (in 
+	// ConstructL) then make a new (bigger!) Q controller. NB This may fail, 
+	// in which case just go with the earlier one- we always return from this 
+	// (non-leaving) function leaving behind a valid iDataController.
+	TUint16 bufSize;
+	TUint numBufs;
+	iDataController->GetBufferInfo(bufSize, numBufs);
+	if ( preferredNumBuffers >= numBufs )
+		{
+		const TUint16 framingOverhead = iHCIFacade.ReadACLFramingOverhead();
+
+		CACLDataQController* tmpDataQController = NULL;
+		TRAP_IGNORE(tmpDataQController = 
+ 					CACLDataQController::NewL(iLinkMgrProtocol,
+									*this, 
+									aAclMaxLen,
+									framingOverhead,
+									preferredNumBuffers));
+		if ( tmpDataQController )
+			{
+			delete iDataController;
+			iDataController = tmpDataQController;
+			}
+		}
+
+	// Tell the data Q controller our HC data credits 
+	iDataController->InitialDataCredits(aNoACL); 
+
+	iDataController->GetBufferInfo(bufSize, numBufs);
+	iACLPacketMTU = bufSize;
+ 	__ASSERT_DEBUG(numBufs >= iHCIFacade.ReadACLReportingInterval(), 
+		Panic(ELinkMgrDataQBufferIsNotSufficient));
+
+	LOG1(_L("CLinkMuxer::HandleLocalReadBufferSizeResult iDataController = 0x%08x"), 
+		iDataController);
+
+	return iDataController;
+	}
+
+//
+// End of file