bluetooth/btstack/linkmgr/hostmbufpool.cpp
changeset 33 4e80e1b997a8
child 47 a1b8f5cc021e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btstack/linkmgr/hostmbufpool.cpp	Mon Jul 12 07:03:47 2010 +0300
@@ -0,0 +1,382 @@
+// Copyright (c) 1999-2010 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:
+// 
+
+#include "hostmbufpool.h"
+
+#include <bluetooth/hcicommandqueue.h>
+#include <bluetooth/hci/hostnumberofcompletedpacketscommand.h>
+#include <bluetooth/hci/hcievents.h>
+#include <bluetooth/hci/commandcompleteevent.h>
+#include <bluetooth/hci/hciutil.h>
+
+#include "linkconsts.h"
+#include "linkutil.h"
+
+#include <bluetooth/logger.h>
+
+#ifdef HOSTCONTROLLER_TO_HOST_FLOW_CONTROL
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_LINKMGR);
+#endif
+
+__DEBUG_ONLY(PANICCATEGORY("mbufpool");)
+
+
+CHostMBufPool* CHostMBufPool::NewL(MHCICommandQueue& aCommandQueue)
+	{
+	LOG_STATIC_FUNC
+	CHostMBufPool* self = new (ELeave) CHostMBufPool(aCommandQueue);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+	
+void CHostMBufPool::DeletePool(TSglQue<TPoolBuffer>& aQueue)
+	{
+	LOG_FUNC
+	TPoolBuffer* buffer = NULL;
+	TSglQueIter<TPoolBuffer> iter(aQueue);
+	while(buffer=iter++, buffer)
+		{
+		aQueue.Remove(*buffer);
+		DeleteBuffer(buffer);
+		}
+	}
+
+void CHostMBufPool::DeleteBuffer(TPoolBuffer*& aBuffer)
+	{
+	LOG_FUNC
+	if(aBuffer)
+		{
+		aBuffer->iMBufChain.Free();
+		delete aBuffer;
+		aBuffer = NULL;
+		}
+	}
+
+CHostMBufPool::~CHostMBufPool()
+	{
+	LOG_FUNC
+	Cancel();
+	// iMBufRequester is cleaned in it's destructor (not much of an R-class...but it is what it is)
+	DeletePool(iBufferPool);
+	DeletePool(iWaitingAllocPool);
+	DeleteBuffer(iBufferBeingAllocd);
+	}
+	
+CHostMBufPool::CHostMBufPool(MHCICommandQueue& aCommandQueue)
+	: CActive(EPriorityHigh) 
+	// High priority so that buffers are allocated occur before more data is read, this prevents
+	// the cases the data floods the device and exhausts the buffers before any more can be allocated.
+	// This maximises throughput since we will ensure we send packet completion notifications in a
+	// timely manner.
+	, iCmdQ(aCommandQueue)
+	, iBufferPool(_FOFF(TPoolBuffer,iLink))
+	, iWaitingAllocPool(_FOFF(TPoolBuffer,iLink))
+	, iCurrAckHandle(KErrNotFound)
+	{
+	LOG_FUNC
+	}
+	
+void CHostMBufPool::ConstructL()
+/**
+2nd phase constructor for the Host MBuf Pool.
+
+This method will attempt to reserve enough MBufs from the global pool
+for bluetooth use.
+@leave KErrNoMemory If the required number of MBufs couldn't be reserved
+*/
+	{
+	LOG_FUNC
+	LOG2(_L("CHostMBufPool: now reserving %d size %d MBufChains"), KStackACLBuffersNum, KLinkMgrIncomingBufferSize);
+	
+	for (TInt i=0; i<=KStackACLBuffersNum-1; i++)
+		{
+		TPoolBuffer* thisBuffer = CreatePoolBufferL();
+		AddToBufferPool(*thisBuffer);
+		}
+		
+	CActiveScheduler::Add(this);
+	}
+	
+CHostMBufPool::TPoolBuffer* CHostMBufPool::CreatePoolBufferL()
+	{
+	LOG_FUNC
+	TPoolBuffer* newBuffer = new(ELeave) TPoolBuffer();
+	CleanupStack::PushL(newBuffer);
+	newBuffer->iCurrentHandle = KInvalidConnectionHandle; // we assert on this later
+	newBuffer->iMBufChain.AllocL(KLinkMgrIncomingBufferSize);
+	CleanupStack::Pop(newBuffer);
+	return newBuffer;
+	}
+	
+void CHostMBufPool::DoCancel()
+	{
+	LOG_FUNC
+	iMBufRequester.Cancel();
+	}
+	
+RMBufChain CHostMBufPool::TakeBufferL(THCIConnHandle aConnHandle)
+/**
+Takes a buffer from the pool and schedules an asynchronous allocation
+of the next buffer.	 Only when that allocation has succeeded will the host
+controller be signalled with a host_number_of_completed_packets.  Hence,
+if we cannot allocate a buffer from the global MBuf pool, the host controller
+will be flowed off and no data will be lost.
+*/
+	{
+	LOG_FUNC
+	ASSERT_DEBUG(aConnHandle != KInvalidConnectionHandle);
+	
+	// Speculatively attempt to allocate any queued allocations that may have previously failed.
+	TryToAllocQueuedBuffer();
+	
+	TPoolBuffer* ready = iBufferPool.First();
+	
+	if(!ready)
+		{
+		// Whoops run out of buffers - even though we were trying to prevent this with
+		// flow control, in the case of disconnection the controller will assume all the
+		// data for a connection handle will be flushed and therefore the buffers associated
+		// with that connection handle will be free.  Unfortunately for us we don't have
+		// that much control with the MBuf pool (since flow control is for asynchronous
+		// buffer allocation rather than waiting for the given MBufs to be relinquished
+		// by a higher layer).
+		// So the controller could think we have more buffers than we actually have...
+		LOG(_L8("CHostMBufPool: Ran out of buffers!!!!"));
+		LEAVEL(KErrOverflow);
+		}
+	
+	// If here then we should have a valid pool buffer to use
+	__ASSERT_DEBUG(!ready->iMBufChain.IsEmpty(), Panic(ELinkMgrHostControllerHasOverflowedHost));
+	__ASSERT_DEBUG(ready->iCurrentHandle == KInvalidConnectionHandle, Panic(ELinkMgrHostControllerHasOverflowedHost));
+	
+	RemoveFromBufferPool(*ready);
+	ready->iCurrentHandle = aConnHandle;
+	
+	RMBufChain retChain;
+	retChain.Assign(ready->iMBufChain);
+	
+	if (IsActive())
+		{
+		//This buffer will be reclaimed from the global pool
+		//after the one(s) we're currently trying to reclaim
+		LOG(_L8("CHostMBufPool: TakeBuffer, buffer taken while alloc outstanding: queued alloc"));
+		iWaitingAllocPool.AddLast(*ready);
+		}
+	else
+		{
+		LOG(_L8("CHostMBufPool: TakeBuffer, buffer taken"));
+		AllocNewBuffer(*ready);
+		}
+		
+	return retChain;
+	}
+
+void CHostMBufPool::InvalidateByConnH(THCIConnHandle aConnHandle)
+	{
+	LOG_FUNC
+	ASSERT_DEBUG(aConnHandle != KInvalidConnectionHandle);
+	
+	// We need to scan through the two pending "lists" to remove the
+	// connection handle from record so we don't try to provide a 
+	// packet completion notification (the controller already assumes
+	// the buffers are free as they are entitled to by the spec).
+	
+	// The current buffer being allocated
+	if(iBufferBeingAllocd && iBufferBeingAllocd->iCurrentHandle == aConnHandle)
+		{
+		iBufferBeingAllocd->iCurrentHandle = KInvalidConnectionHandle;
+		}
+	
+	// The list of buffers waiting to be allocted
+	TPoolBuffer* buffer = NULL;
+	TSglQueIter<TPoolBuffer> iter(iWaitingAllocPool);
+	while(buffer=iter++, buffer)
+		{
+		if(buffer->iCurrentHandle == aConnHandle)
+			{
+			buffer->iCurrentHandle = KInvalidConnectionHandle;
+			}
+		}
+	
+	// Finally we need to purge any batched up completions if they
+	// are for this connection handle
+	if(iCurrAckHandle == aConnHandle)
+		{
+		iCurrAckHandle = KErrNotFound;
+		iCurrCompletedPackets = 0;
+		}
+	}
+
+void CHostMBufPool::RunL()
+	{
+	LOG_FUNC
+	LEAVEIFERRORL(iStatus.Int());
+	
+	// We've successfully allocated a new MBufChain
+	TPoolBuffer* justAllocd = iBufferBeingAllocd;
+	iBufferBeingAllocd = NULL;
+	THCIConnHandle justAllocdHandle = justAllocd->iCurrentHandle;
+	
+	// Return buffer to pool for re-use
+	AddToBufferPool(*justAllocd);
+	justAllocd->iCurrentHandle = KInvalidConnectionHandle;
+	
+	// If connection handle is still valid then we need to provide a completion
+	// notification for the packet to the connection handle it was from.
+	if(justAllocdHandle != KInvalidConnectionHandle)
+		{
+		if (iCurrAckHandle == KErrNotFound)
+			{
+			// This is the first completion we have seen
+			iCurrAckHandle = justAllocdHandle;
+			}
+		ASSERT_DEBUG(iCurrAckHandle != KInvalidConnectionHandle); // just to be sure
+		
+		TBool ackNow = (justAllocdHandle != iCurrAckHandle);
+		
+		if (!ackNow)
+			{
+			iCurrCompletedPackets++;
+			LOG2(_L8("CHostMBufPool: CompletedPackets++ for conn: %d [->%d]"), justAllocdHandle, iCurrCompletedPackets);
+			ackNow = (iCurrCompletedPackets >= KStackACLBuffersTideMarkNum);
+			}
+			
+		if (ackNow)
+			{
+			TInt err = KErrNone;
+			
+			if (iCurrCompletedPackets > 0)
+				{
+				LOG2(_L8("CHostMBufPool: Sending HostNumberOfCompletedPackets for conn: %d [%d completed]"), iCurrAckHandle, iCurrCompletedPackets);
+				//Acknowledge the completed packets
+				TRAP(err, HostNumberOfCompletedPacketsL(iCurrAckHandle, iCurrCompletedPackets));
+				//if this failed we probably couldn't alloc the memory for the command frame,
+				//the HC is still flowed off.
+				__ASSERT_DEBUG(err == KErrNone, Panic(ELinkMgrCouldNotSendHostNumberOfCompletedPackets));
+				LEAVEIFERRORL(err);
+				}
+			
+			iCurrCompletedPackets = (justAllocdHandle != iCurrAckHandle) ? 1 : 0;
+			iCurrAckHandle = justAllocdHandle;
+			}
+		}
+	
+	TryToAllocQueuedBuffer();
+	}
+	
+void CHostMBufPool::TryToAllocQueuedBuffer()
+	{
+	LOG_FUNC
+	if (!iWaitingAllocPool.IsEmpty() && !IsActive())
+		{
+		TPoolBuffer* needsAlloc = iWaitingAllocPool.First();
+		iWaitingAllocPool.Remove(*needsAlloc);
+		AllocNewBuffer(*needsAlloc);
+		}
+	}
+	
+void CHostMBufPool::AllocNewBuffer(TPoolBuffer& aBuffer)
+	{
+	LOG_FUNC
+	ASSERT_DEBUG(!iBufferBeingAllocd);
+	iBufferBeingAllocd = &aBuffer;
+	iMBufRequester.Alloc(aBuffer.iMBufChain, KLinkMgrIncomingBufferSize, iStatus);
+	SetActive();
+	}
+	
+void CHostMBufPool::HostNumberOfCompletedPacketsL(THCIConnHandle aConnH, TUint16 aNumPackets)
+	{
+	RArray<THCIConnectionHandle> connHandles;
+	connHandles.AppendL(aConnH);
+	CleanupClosePushL(connHandles);
+
+	RArray<THCINumOfCompletedPackets> numPackets;
+	numPackets.AppendL(aNumPackets);
+	CleanupClosePushL(numPackets);
+	
+	CHostNumberOfCompletedPacketsCommand* cmd = CHostNumberOfCompletedPacketsCommand::NewL(1, connHandles, numPackets);
+	// Ownership of the arrays is taken by the command object.
+	CleanupStack::Pop(2, &connHandles); // &numPackets, &connHandles
+
+	// This is a priority command as we want to try to get this out as soon as possible (and not wait
+	// for all normal control aspects to be processed).  This command shouldn't normally consume any credits
+	// so as a priority command it has little impact.
+	// Ownership of cmd transfered even if MhcqAddPriorityCommandL leaves
+	iCmdQ.MhcqAddPriorityCommandL(cmd, *this);
+	}
+	
+void CHostMBufPool::MhcqcCommandEventReceived(const THCIEventBase& aEvent, const CHCICommandBase* /*aRelatedCommand*/)
+	{
+	LOG_FUNC
+	// We don't expect a non-error event back because we're only sending Host_Number_of_Completed_Packet commands
+	if(aEvent.EventCode() == ECommandCompleteEvent)
+		{
+		const THCICommandCompleteEvent& completeEvent = THCICommandCompleteEvent::Cast(aEvent);
+		if(completeEvent.CommandOpcode() == KHostNumberOfCompletedPacketsOpcode)
+			{
+			// a regular error for a Host_Number_Of_Completed_Packets command
+			TInt err = CHciUtil::SymbianErrorCode(completeEvent.ErrorCode());
+			if(err != KErrNone) // we shouldn't get a non-erroring event back, but just in case
+				{
+				Error(err);
+				}
+			}
+		else // an unexpected command complete event
+			{
+			LOG1(_L8("CHostMBufPool: Unexpected HCI command complete event; opcode = 0x%04x"), completeEvent.CommandOpcode());
+			DEBUG_PANIC_LINENUM;
+			}
+		}
+	else // some unexpected event
+		{
+		LOG1(_L8("CHostMBufPool: Unexpected HCI event received; code = 0x%02x"), aEvent.EventCode());
+		DEBUG_PANIC_LINENUM;
+		}
+	}
+
+void CHostMBufPool::MhcqcCommandErrored(TInt aErrorCode, const CHCICommandBase* /*aCommand*/)
+	{
+	LOG_FUNC
+	Error(aErrorCode);
+	}
+	
+TInt CHostMBufPool::RunError(TInt aError)
+	{
+	LOG_FUNC
+	if(iBufferBeingAllocd)
+		{
+		TPoolBuffer* justFailedToAlloc = iBufferBeingAllocd;
+		iBufferBeingAllocd = NULL;
+		// Add to wait for alloc queue - we may get another chance
+		iWaitingAllocPool.AddFirst(*justFailedToAlloc);
+		}
+	Error(aError);
+	return KErrNone;
+	}
+	
+void CHostMBufPool::Error(TInt IF_FLOGGING(aError))
+	{
+	LOG_FUNC
+	// So there has been some internal error when handling controller to host
+	// flow control.  Tough, we've done our best for now - the only real thing
+	// that might be worth doing is a hard reset to start-up clean.
+	LOG1(_L8("CHostMBufPool: ERROR (%d) - inbound data to the stack may stall soon"), aError);
+	}
+	
+#endif