bluetooth/btstack/linkmgr/AclDataQController.cpp
changeset 0 29b1cd4cb562
child 22 9f17f914e828
child 32 f72906e669b4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btstack/linkmgr/AclDataQController.cpp	Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,624 @@
+// 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:
+// Link Command Q Controlling internal methods 
+// The command Q is used to store commands used by the stack
+// The HCI is at liberty to use its own command Q for any other HC signalling 
+// it requires.
+// The HCI in such cases would then signal the stack with not all credits etc.
+// 
+//
+
+#include <bluetooth/logger.h>
+#include "AclDataQController.h"
+#include "linkmgr.h"
+#include "AclDataQ.h"
+#include "hcifacade.h"
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_LINKMGR);
+#endif
+
+const TInt KACLDataQConnectionRecordGranularity = 2;
+
+/**
+	Factory function.
+
+	@return Ownership of a new CACLDataQController.
+ */
+CACLDataQController* CACLDataQController::NewL(CLinkMgrProtocol& aProtocol,
+											   CLinkMuxer& aMuxer,
+											   TUint16 aBufSize,
+											   TUint16 aFrameOverhead,
+											   TUint aNumBufs)
+	{
+	LOG_STATIC_FUNC
+	LOG3(_L("CACLDataQController::NewL aBufSize = %d, aFramingOverhead = %d, aNumBufs = %d"),
+		aBufSize, aFrameOverhead, aNumBufs);
+
+	CACLDataQController* self = new(ELeave) CACLDataQController(aProtocol.HCIFacade(), aMuxer);
+	CleanupStack::PushL(self);
+	self->ConstructL(aProtocol, aBufSize, aFrameOverhead, aNumBufs);
+	CleanupStack::Pop(self);
+
+	LOG1(_L("CACLDataQController::NewL self = 0x%08x"), self);
+	return self;
+	}
+
+CACLDataQController::CACLDataQController(CHCIFacade& aHCIFacade,
+										 CLinkMuxer& aMuxer)
+ :	iAclConns(KACLDataQConnectionRecordGranularity),
+	iLinkMuxer(aMuxer),
+	iHCIFacade(aHCIFacade)
+	{
+	LOG_FUNC
+	}
+
+CACLDataQController::~CACLDataQController()
+	{
+	LOG_FUNC
+
+	delete iDataQ;
+	iAclConns.Reset();
+	iAclConns.Close();
+	}
+
+void CACLDataQController::ConstructL(CLinkMgrProtocol& aProtocol,
+									 TUint16 aBufSize,
+									 TUint16 aFrameOverhead,
+									 TUint aNumBufs)
+	{
+	LOG_FUNC
+
+	iDataQ = CAclDataQ::NewL(aProtocol, aNumBufs, aBufSize, aFrameOverhead);
+
+#ifdef PROXY_COMMUNICATES
+	LOG(_L("\tPROXY_COMMUNICATES defined- reserving slots for broadcast channel"));
+
+	// Reserve BC one now
+	User::LeaveIfError(ACLLogicalLinkUp(KHCIBroadcastHandle, EFalse));
+#endif
+	}
+
+void CACLDataQController::InitialDataCredits(TUint16 aCredits)
+/**
+	Set the initial data credits to whatever number of buffers the HC has.
+*/
+	{
+	LOG_FUNC
+	LOG1(_L("CACLDataQController::InitialDataCredits aCredits = %d"), aCredits);
+
+	iDataCredits = aCredits;
+	iNumControllerBufs = aCredits;
+	}
+
+CACLDataItem* CACLDataQController::GetFreeItem()
+/**
+	Returns ownership of a data item, or NULL if there are none free.
+ */
+	{
+	LOG_FUNC
+
+	__ASSERT_DEBUG(iDataQ, Panic(EHCIBufferMgrBadState));
+	CACLDataItem* ret = iDataQ->RemoveFirstSpareItem();
+	
+	LOG1(_L("CACLDataQController::GetFreeItem ret = 0x%08x"), ret);
+	return ret;
+	}
+
+void CACLDataQController::AddItem(CACLDataItem& aAclItem)
+	{
+	LOG_FUNC
+	LOG1(_L("CACLDataQController::AddItem &aAclItem = 0x%08x"), &aAclItem);
+
+	// what Connection Handle is this frame for?
+	__ASSERT_DEBUG(aAclItem.Frame(), Panic(EHCIBufferMgrBadState));
+	THCIConnHandle connh = (*(aAclItem.Frame())).ConnectionHandle();
+	
+	// Get the connection record
+	const TInt index = FindConnection(connh);
+	__ASSERT_ALWAYS(index>=0, Panic(EHCIACLDataControllerConnectionListConnectionNotFound));
+
+	++(iAclConns[index].iPacketsQueued);
+	LOG3(_L("\tadded an item for connection handle %d: iPacketsQueued = %d, iPacketsPending = %d"), 
+		connh, iAclConns[index].iPacketsQueued, iAclConns[index].iPacketsPending);
+
+	__ASSERT_DEBUG(iDataQ, Panic(EHCIBufferMgrBadState));
+	iDataQ->AddItem(aAclItem);
+	iLinkMuxer.TryToSend();
+	}
+
+THCIConnHandle CACLDataQController::HighestPriority()
+/**
+ Get the highest priority connection, starting to look from the connection 
+ which sent last, and ignoring connections with no queued data to send.
+ If there's no clear highest priority connection, the connection handle of the 
+ 0th connection in our array will be returned, whether it has anything to send 
+ or not (i.e. you always get a valid connection handle). 
+ Assumes that there is at least 1 connection handle in our array.
+ */
+	{
+	LOG_FUNC
+	LOG1(_L("CACLDataQController::HighestPriority iIndexOfLastSendingConn = %d"), 
+		iIndexOfLastSendingConn);
+
+	const TUint count = iAclConns.Count();
+	LOG1(_L("\tcount = %d"), count);
+	// Should only be called if we have connections to judge the highest 
+	// priority of.
+	__ASSERT_DEBUG(count != 0, Panic(EHCIBufferMgrBadState));
+
+	// Deal with the most likely case first, for speed...
+	if ( count == 1 )
+		{
+		LOG1(_L("CACLDataQController::HighestPriority returning %d"), 
+			iAclConns[0].iConnH);
+		return iAclConns[0].iConnH;
+		}
+
+	// Now look through iAclConns from the one which sent last...
+	__ASSERT_DEBUG(iIndexOfLastSendingConn < count, Panic(EHCIBufferMgrBadState));
+	TInt highestpriority = -KMaxTInt;
+	// NB If no connections have anything to send, connection index 0 will be 
+	// indicated by the return value.
+	TInt highestindex = 0;
+	TUint index;
+	if ( iIndexOfLastSendingConn + 1 == count )
+		{
+		index = 0;
+		}
+	else
+		{
+		index = iIndexOfLastSendingConn + 1;
+		}
+
+	FOREVER
+		{
+		__ASSERT_DEBUG(index < static_cast<TUint>(iAclConns.Count()), Panic(EHCIBufferMgrBadState));
+
+		const TInt priority = -(iAclConns[index].iPacketsPending);
+		const TInt queued = iAclConns[index].iPacketsQueued;
+
+#ifdef __FLOG_ACTIVE
+		const THCIConnHandle connh = iAclConns[index].iConnH;
+		LOG3(_L("\tconnection handle %d: iPacketsQueued = %d, iPacketsPending = %d"), 
+			connh, queued, -priority);
+#endif
+
+		// Only consider connections with some data queued.
+		if ( queued > 0 && priority > highestpriority )
+			{
+			highestpriority = priority;
+			highestindex = index;
+			}
+
+		LOG3(_L("\tindex = %d, highestpriority = %d, highestindex = %d"),
+			index, highestpriority, highestindex);
+		
+		++index;
+		// Have we gone all the way round?
+		if ( index == iIndexOfLastSendingConn + 1 )
+			{
+			break;
+			}
+		// If we're at the end of the array, continue looking from the 
+		// beginning.
+		if ( index == count )
+			{
+			index = 0;
+			}
+		}
+
+	LOG1(_L("CACLDataQController::HighestPriority connection handle = %d"), 
+		iAclConns[highestindex].iConnH);
+	return iAclConns[highestindex].iConnH;
+	}
+
+TBool CACLDataQController::AnotherPacketAllowed(TDataQConnectionInfo& aRecord)
+/**	
+	A small strategy to see if another packet is allowed on a given connection 
+	handle.
+	Always try to keep some spare host controller buffers for another 
+	connection handle that might arise. This maxes out at 8.
+*/
+	{
+	LOG_FUNC
+
+	TBool allowAnotherPacket;
+	
+	if ( aRecord.iParked && aRecord.iConnH != KHCIBroadcastHandle )
+		{
+		// not allowed to send on non-broadcast handle when parked
+		allowAnotherPacket = EFalse;
+		}
+	else
+		{
+		// the rest of the handles are in principle sendable, but need to 
+		// check for room in HC. NB The '- 1' here is to make sure that one 
+		// connection can't result in the whole device being locked up if the 
+		// remote device stops responding- there'll still be one slot left 
+		// over. 
+		allowAnotherPacket = aRecord.iPacketsPending < iNumControllerBufs - 1 ? ETrue : EFalse;
+		}
+
+	LOG1(_L("CACLDataQController::AnotherPacketAllowed allowAnotherPacket = %d"), 
+		allowAnotherPacket);
+	return allowAnotherPacket;
+	}
+
+/**
+ Called by the link muxer to do an actual send.
+ Get the link which is entitled to send (on the basis of priority (which is 
+ simply (0-number of packets waiting to be sent)) and recent history (if it 
+ was the last to send, we check all the other links first for a higher or 
+ equal priority one).
+ Finally, get a packet belonging to the winning link and attempt to send it. 
+ 
+ @return EFalse if no send occurred, ETrue otherwise.
+ */
+TBool CACLDataQController::IssueNextACLDataFragment()
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(iDataCredits>0,Panic(EHCIACLDataControllerDataCreditsZeroBeforeIssue));
+
+	if ( !iAclConns.Count() )
+		{
+		LOG(_L("CACLDataQController::IssueNextACLDataFragment returning EFalse"));
+		return EFalse;
+		}
+
+	THCIConnHandle connh = HighestPriority(); // find suggested connection handle to send on
+	//ask the DataQ for the first item on this connection handle
+	CACLDataItem* item = NULL;
+	__ASSERT_DEBUG(iDataQ, Panic(EHCIBufferMgrBadState));
+	item = iDataQ->FirstItemByConnectionHandle(iHCIFacade, connh);
+	//if still NULL then there is no longer stuff on that connH on the Q so get the head item
+	if ( !item )
+		{
+		item = iDataQ->FirstItem(iHCIFacade, connh); // connh will be set
+		}
+
+	TBool sent = EFalse;
+
+	const TInt index = FindConnection(connh);
+	LOG1(_L("\tFindConnection returned %d"), index);
+	if ( index == KErrNotFound )
+		{
+		// Connection not found, because it went down between the link muxer 
+		// realising it could try to send and it (asynchronously) actually 
+		// asking us to send.
+		LOG1(_L("CACLDataQController::IssueNextACLDataFragment sent = %d"), 
+			sent);
+		return sent;
+		}
+
+	if ( !AnotherPacketAllowed(iAclConns[index]) )
+		{
+		LOG1(_L("CACLDataQController::IssueNextACLDataFragment sent = %d"), 
+			sent);
+		return sent; // no more allowed, otherwise HC liable to clogging
+		}
+
+	__ASSERT_DEBUG(item, Panic(EHCIBufferMgrBadState));
+	sent = SendItem(*item);
+
+	// Regardless of whether the send succeeded, make a note that this 
+	// connection handle was the last to 'have a go'...
+	iIndexOfLastSendingConn = index;
+
+	if ( sent )
+		{
+		--iDataCredits;
+		--(iAclConns[index].iPacketsQueued);
+		++(iAclConns[index].iPacketsPending);
+		LOG3(_L("\tsent on connection handle %d: iPacketsQueued = %d, iPacketsPending = %d"),
+			connh, iAclConns[index].iPacketsQueued, iAclConns[index].iPacketsPending);
+		}
+	
+	LOG1(_L("CACLDataQController::IssueNextACLDataFragment sent = %d"), sent);
+	return sent;
+	}
+
+TBool CACLDataQController::SendItem(CACLDataItem& aItem) 
+/**
+	Actually do the send - return true if successful
+**/
+	{
+	LOG_FUNC
+
+	TBool ret = EFalse;
+	__ASSERT_DEBUG(aItem.Frame(), Panic(EHCIBufferMgrBadState));
+	if ( iHCIFacade.WriteACLData(*aItem.Frame()) == KErrNone )
+		{
+		__ASSERT_DEBUG(iDataQ, Panic(EHCIBufferMgrBadState));
+		iDataQ->PendingItem(aItem);
+		ret = ETrue;
+		}
+	
+	LOG1(_L("CACLDataQController::SendItem ret = %d"), ret);
+	return ret;
+	}
+
+void CACLDataQController::GetBufferInfo(TUint16& aBufSize, TUint& aNumBufs)
+/**
+	Retrieves information about what the capabilities of the Data Q are.
+	This information will be used in order to inform the HCI client about
+	our MTU and maximum buffers so flow control can be possible.
+*/
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(iDataQ, Panic(EHCIBufferMgrBadState));
+	aBufSize = iDataQ->ItemSize();
+	aNumBufs = iDataQ->Ceiling();
+
+	LOG2(_L("CACLDataQController::GetBufferInfo aBufSize = %d, aNumBufs = %d"), 
+		aBufSize, aNumBufs);
+	}
+
+void CACLDataQController::GetDataQRecords(TUint& aQFillLevel, TUint16& aCredits)
+/**
+	Returns the number of things ready to send, and the number of credits we 
+	have to send
+**/
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(iDataQ, Panic(EHCIBufferMgrBadState));
+	aQFillLevel = iDataQ->FillLevel(); // items in the Q
+	aCredits = iDataCredits;
+	LOG2(_L("LL: CACLDataQController: Model: FillLevel %d, FillCeiling %d"),
+		aQFillLevel, iDataQ->Ceiling());
+
+	LOG2(_L("CACLDataQController::GetDataQRecords aQFillLevel = %d, aCredits = %d"), 
+		aQFillLevel, aCredits);
+	}
+
+void CACLDataQController::FlushOccurred(THCIConnHandle /*aConnH*/)
+	{
+	LOG_FUNC
+	// do nothing
+	// we will receive a num_completed_packets event
+	}
+
+TInt CACLDataQController::ACLLogicalLinkUp(THCIConnHandle aConnH, TBool aIsParked)
+/**
+	This object cares more about *ACL* links
+	Whether or not ACL comes up for free with physical channel, it
+	is worth drawing the distinction
+
+	If an error occurs we tell the caller - it's likely the ACL will need to 
+	be torn down
+**/
+	{
+	LOG_FUNC
+	LOG2(_L("CACLDataQController::ACLLogicalLinkUp aConnH = %d, aIsParked = %d"), 
+		aConnH, aIsParked);
+
+	TDataQConnectionInfo tmpConnRecord;
+	tmpConnRecord.iConnH = aConnH;
+	tmpConnRecord.iPacketsQueued = 0;
+	tmpConnRecord.iPacketsPending = 0;
+	tmpConnRecord.iParked = aIsParked;
+
+	TInt err = iAclConns.Append(tmpConnRecord);
+
+	// We don't need to update iIndexOfLastSendingConn because the new 
+	// connection will have been added 'after' its current position.
+
+	LOG1(_L("CACLDataQController::ACLLogicalLinkUp err = %d"), err);
+	return err;
+	}
+
+void CACLDataQController::ACLLogicalLinkDown(THCIConnHandle aConnH)
+/**
+	Updates the Q.
+ */
+	{
+	LOG_FUNC
+	LOG1(_L("CACLDataQController::ACLLogicalLinkDown aConnH = %d"), 
+		aConnH);
+
+	TInt connection = FindConnection(aConnH);
+		
+	if ( connection == KErrNotFound )
+		{
+		LOG(_L("CACLDataQController::ACLLogicalLinkDown (Connection not found)."));
+		return;
+		}
+
+	const TUint index = static_cast<TUint>(connection);
+
+	const TUint16 dropped = iAclConns[index].iPacketsPending;
+	LOG1(_L("\treusing %d credits"), dropped);
+
+	// Increase iDataCredits by how many packets this link had on the air (we 
+	// won't receive a num_completed_packets event to do it the normal way)
+	iDataCredits = static_cast<TUint16>(iDataCredits + dropped);
+
+	__ASSERT_DEBUG(iDataQ, Panic(EHCIBufferMgrBadState));
+	iDataQ->InvalidateByConnH(aConnH, iHCIFacade);
+	iAclConns.Remove(index);
+
+	// The array of connections has changed- if the removed connection is 
+	// 'after' iIndexOfLastSendingConn, we don't need to bother changing it. 
+	// If the removed connection IS the connection that sent last, reset it to 
+	// zero. If the removed connection is before the one that sent last, just 
+	// decrement iIndexOfLastSendingConn. 
+	if ( index == iIndexOfLastSendingConn )
+		{
+		iIndexOfLastSendingConn = 0;
+		}
+	else if ( index < iIndexOfLastSendingConn )
+		{
+		--iIndexOfLastSendingConn;
+		}
+
+	__ASSERT_DEBUG(iDataCredits<=iNumControllerBufs, Panic(EHCIACLDataControllerMaxAllowedDataCreditsExceeded));
+	}
+
+void CACLDataQController::SetParked(THCIConnHandle aConnH, TBool aParked)
+	{
+	LOG_FUNC
+	LOG2(_L("CACLDataQController::SetParked aConnH = %d, aParked = %d"), 
+		aConnH, aParked);
+
+	// Check if there are any data connections.
+	if ( iAclConns.Count() > 1 )
+		{
+		const TInt index = FindConnection(aConnH);
+		if(index != KErrNotFound)
+			{
+			iAclConns[index].iParked = aParked;
+			}
+		else
+			{
+			LOG(_L("\tConnection handle not found."));
+			}
+		}
+	else
+		{
+		LOG(_L("\tcurrently no ACL connections"));
+		}
+
+	if ( aParked == EFalse )
+		{
+		// kick off draining of queue
+		iLinkMuxer.TryToSend();
+		}
+	}
+
+void CACLDataQController::CompletedPackets(THCIConnHandle aConnH, TUint16 aNo)
+/**
+	Called when some buffer slots become free in our HC (a 
+	NumberOfCompletedPackets event)- in other words, we can now send aNo more 
+	packets down if we wish.
+ */
+	{
+	LOG_FUNC
+	LOG2(_L("CACLDataQController::CompletedPackets aConnH = %d, aNo = %d"), 
+		aConnH, aNo);
+
+	// Adjust the connection that's just had packets put on the air
+	const TInt index = FindConnection(aConnH);
+
+	//We have to check pending packets because it is possible this completed packets event comes 
+	//from a previous link and current link reuses the same connection handle
+	if (index!=KErrNotFound && iAclConns[index].iPacketsPending > 0)
+		{
+		iAclConns[index].iPacketsPending = static_cast<TUint16>(iAclConns[index].iPacketsPending - aNo);
+		
+		LOG4(_L("\t%d completed packets for connection handle %d: iPacketsQueued = %d, iPacketsPending = %d"),
+			aNo, aConnH, iAclConns[index].iPacketsQueued, iAclConns[index].iPacketsPending);
+
+		iDataCredits = static_cast<TUint16>(iDataCredits + aNo);
+		__ASSERT_DEBUG(iDataCredits<=iNumControllerBufs, Panic(EHCIACLDataControllerMaxAllowedDataCreditsExceeded));
+
+		LOG3(_L("LinkMgr: CACLDataQController: Got %d DataCredits on ConnH 0x%04x: total %d DataCredts"), 
+			aNo, aConnH, iDataCredits);
+		LOG2(_L("LL: CACLDataQController: Got %d DataCredits; total %d DataCredts"),
+			aNo,iDataCredits);
+			
+		// Notify sent items.
+		iDataQ->ItemsSent(aNo);
+		
+		// now we have credits - have a go at using them
+		iLinkMuxer.TryToSend();
+		}
+	else
+		{
+		LOG1(_L("LinkMgr: CACLDataQController: Got Completed Packets on lost ConnH(%d) [dropped?]"), aConnH);
+		//The Hardware has sent num_completed_packets for a ConnH which we don't know about,
+		//if this was a race with the connection being dropped we will already have reused the
+		//credits.
+		}
+	}
+
+#pragma warning (default:4244)// "conversion from 'int' to 'unsigned short', possible loss of data"
+
+TInt CACLDataQController::FindConnection(THCIConnHandle aConnH)
+/**
+ Returns the index (in iAclConns) of the connection record with THCIConnHandle 
+ aConnH, or error.
+ */
+	{
+	LOG_FUNC
+	LOG2(_L("CACLDataQController::FindConnection aConnH = %d, iAclConns.Count() = %d"), 
+		aConnH, iAclConns.Count());
+
+	TDataQConnectionInfo tmpInfo;
+	tmpInfo.iConnH = aConnH;
+
+	TIdentityRelation<TDataQConnectionInfo> relation(LinkMatch);
+
+	const TInt position = iAclConns.Find(tmpInfo, relation);
+
+	LOG1(_L("CACLDataQController::FindConnection position = %d"), position);
+	return position;
+	}
+
+/*static*/ TBool CACLDataQController::LinkMatch(const TDataQConnectionInfo& aA,
+									 const TDataQConnectionInfo& aB)
+	{
+	LOG_STATIC_FUNC
+	return (aA.iConnH == aB.iConnH);
+	}
+
+void CACLDataQController::FlushComplete(TInt __DEBUG_ONLY(aErr), THCIConnHandle aConnH)
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(aErr == KErrNone || aErr == ECommandDisallowed, Panic(EHCIACLDataControllerFlushCompleteError));
+		 
+	TInt ix = FindConnection(aConnH);
+	__ASSERT_DEBUG(ix != KErrNotFound, Panic(EHCIACLDataControllerFlushCompleteError));
+	if(ix != KErrNotFound)
+		{
+		__ASSERT_DEBUG(iAclConns[ix].iFlushInProgress, Panic(EHCIACLDataControllerFlushCompleteError));
+		iAclConns[ix].iFlushInProgress = EFalse;
+
+		iLinkMuxer.TryToSend();
+		}
+	}
+	
+	
+TInt CACLDataQController::SetFlushInProgress(THCIConnHandle aConnH)
+	{
+	LOG_FUNC
+	// Check a flush is not currently outstanding against this
+	// Connection handle.
+	TInt rerr = KErrNone;
+	TInt ix = FindConnection(aConnH);
+	if(ix != KErrNotFound)
+		{
+		if(!iAclConns[ix].iFlushInProgress)
+			{
+			TRAP(rerr, iHCIFacade.FlushL(aConnH));
+			if(rerr == KErrNone)
+				{
+				iAclConns[ix].iFlushInProgress = ETrue;
+				}
+			}
+			
+		if(rerr == KErrNone)
+			{
+			iDataQ->ProcessFlush(iHCIFacade, aConnH);
+			}
+		}
+	else
+		{
+		rerr = ix;
+		}
+
+	return rerr;
+	}
+
+//
+// End of file