bluetooth/btstack/linkmgr/hostmbufpool.cpp
changeset 32 f72906e669b4
child 47 a1b8f5cc021e
equal deleted inserted replaced
31:b9d1744dc449 32:f72906e669b4
       
     1 // Copyright (c) 1999-2010 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 // 
       
    15 
       
    16 #include "hostmbufpool.h"
       
    17 
       
    18 #include <bluetooth/hcicommandqueue.h>
       
    19 #include <bluetooth/hci/hostnumberofcompletedpacketscommand.h>
       
    20 #include <bluetooth/hci/hcievents.h>
       
    21 #include <bluetooth/hci/commandcompleteevent.h>
       
    22 #include <bluetooth/hci/hciutil.h>
       
    23 
       
    24 #include "linkconsts.h"
       
    25 #include "linkutil.h"
       
    26 
       
    27 #include <bluetooth/logger.h>
       
    28 
       
    29 #ifdef HOSTCONTROLLER_TO_HOST_FLOW_CONTROL
       
    30 
       
    31 #ifdef __FLOG_ACTIVE
       
    32 _LIT8(KLogComponent, LOG_COMPONENT_LINKMGR);
       
    33 #endif
       
    34 
       
    35 __DEBUG_ONLY(PANICCATEGORY("mbufpool");)
       
    36 
       
    37 
       
    38 CHostMBufPool* CHostMBufPool::NewL(MHCICommandQueue& aCommandQueue)
       
    39 	{
       
    40 	LOG_STATIC_FUNC
       
    41 	CHostMBufPool* self = new (ELeave) CHostMBufPool(aCommandQueue);
       
    42 	CleanupStack::PushL(self);
       
    43 	self->ConstructL();
       
    44 	CleanupStack::Pop(self);
       
    45 	return self;
       
    46 	}
       
    47 	
       
    48 void CHostMBufPool::DeletePool(TSglQue<TPoolBuffer>& aQueue)
       
    49 	{
       
    50 	LOG_FUNC
       
    51 	TPoolBuffer* buffer = NULL;
       
    52 	TSglQueIter<TPoolBuffer> iter(aQueue);
       
    53 	while(buffer=iter++, buffer)
       
    54 		{
       
    55 		aQueue.Remove(*buffer);
       
    56 		DeleteBuffer(buffer);
       
    57 		}
       
    58 	}
       
    59 
       
    60 void CHostMBufPool::DeleteBuffer(TPoolBuffer*& aBuffer)
       
    61 	{
       
    62 	LOG_FUNC
       
    63 	if(aBuffer)
       
    64 		{
       
    65 		aBuffer->iMBufChain.Free();
       
    66 		delete aBuffer;
       
    67 		aBuffer = NULL;
       
    68 		}
       
    69 	}
       
    70 
       
    71 CHostMBufPool::~CHostMBufPool()
       
    72 	{
       
    73 	LOG_FUNC
       
    74 	Cancel();
       
    75 	// iMBufRequester is cleaned in it's destructor (not much of an R-class...but it is what it is)
       
    76 	DeletePool(iBufferPool);
       
    77 	DeletePool(iWaitingAllocPool);
       
    78 	DeleteBuffer(iBufferBeingAllocd);
       
    79 	}
       
    80 	
       
    81 CHostMBufPool::CHostMBufPool(MHCICommandQueue& aCommandQueue)
       
    82 	: CActive(EPriorityHigh) 
       
    83 	// High priority so that buffers are allocated occur before more data is read, this prevents
       
    84 	// the cases the data floods the device and exhausts the buffers before any more can be allocated.
       
    85 	// This maximises throughput since we will ensure we send packet completion notifications in a
       
    86 	// timely manner.
       
    87 	, iCmdQ(aCommandQueue)
       
    88 	, iBufferPool(_FOFF(TPoolBuffer,iLink))
       
    89 	, iWaitingAllocPool(_FOFF(TPoolBuffer,iLink))
       
    90 	, iCurrAckHandle(KErrNotFound)
       
    91 	{
       
    92 	LOG_FUNC
       
    93 	}
       
    94 	
       
    95 void CHostMBufPool::ConstructL()
       
    96 /**
       
    97 2nd phase constructor for the Host MBuf Pool.
       
    98 
       
    99 This method will attempt to reserve enough MBufs from the global pool
       
   100 for bluetooth use.
       
   101 @leave KErrNoMemory If the required number of MBufs couldn't be reserved
       
   102 */
       
   103 	{
       
   104 	LOG_FUNC
       
   105 	LOG2(_L("CHostMBufPool: now reserving %d size %d MBufChains"), KStackACLBuffersNum, KLinkMgrIncomingBufferSize);
       
   106 	
       
   107 	for (TInt i=0; i<=KStackACLBuffersNum-1; i++)
       
   108 		{
       
   109 		TPoolBuffer* thisBuffer = CreatePoolBufferL();
       
   110 		AddToBufferPool(*thisBuffer);
       
   111 		}
       
   112 		
       
   113 	CActiveScheduler::Add(this);
       
   114 	}
       
   115 	
       
   116 CHostMBufPool::TPoolBuffer* CHostMBufPool::CreatePoolBufferL()
       
   117 	{
       
   118 	LOG_FUNC
       
   119 	TPoolBuffer* newBuffer = new(ELeave) TPoolBuffer();
       
   120 	CleanupStack::PushL(newBuffer);
       
   121 	newBuffer->iCurrentHandle = KInvalidConnectionHandle; // we assert on this later
       
   122 	newBuffer->iMBufChain.AllocL(KLinkMgrIncomingBufferSize);
       
   123 	CleanupStack::Pop(newBuffer);
       
   124 	return newBuffer;
       
   125 	}
       
   126 	
       
   127 void CHostMBufPool::DoCancel()
       
   128 	{
       
   129 	LOG_FUNC
       
   130 	iMBufRequester.Cancel();
       
   131 	}
       
   132 	
       
   133 RMBufChain CHostMBufPool::TakeBufferL(THCIConnHandle aConnHandle)
       
   134 /**
       
   135 Takes a buffer from the pool and schedules an asynchronous allocation
       
   136 of the next buffer.	 Only when that allocation has succeeded will the host
       
   137 controller be signalled with a host_number_of_completed_packets.  Hence,
       
   138 if we cannot allocate a buffer from the global MBuf pool, the host controller
       
   139 will be flowed off and no data will be lost.
       
   140 */
       
   141 	{
       
   142 	LOG_FUNC
       
   143 	ASSERT_DEBUG(aConnHandle != KInvalidConnectionHandle);
       
   144 	
       
   145 	// Speculatively attempt to allocate any queued allocations that may have previously failed.
       
   146 	TryToAllocQueuedBuffer();
       
   147 	
       
   148 	TPoolBuffer* ready = iBufferPool.First();
       
   149 	
       
   150 	if(!ready)
       
   151 		{
       
   152 		// Whoops run out of buffers - even though we were trying to prevent this with
       
   153 		// flow control, in the case of disconnection the controller will assume all the
       
   154 		// data for a connection handle will be flushed and therefore the buffers associated
       
   155 		// with that connection handle will be free.  Unfortunately for us we don't have
       
   156 		// that much control with the MBuf pool (since flow control is for asynchronous
       
   157 		// buffer allocation rather than waiting for the given MBufs to be relinquished
       
   158 		// by a higher layer).
       
   159 		// So the controller could think we have more buffers than we actually have...
       
   160 		LOG(_L8("CHostMBufPool: Ran out of buffers!!!!"));
       
   161 		LEAVEL(KErrOverflow);
       
   162 		}
       
   163 	
       
   164 	// If here then we should have a valid pool buffer to use
       
   165 	__ASSERT_DEBUG(!ready->iMBufChain.IsEmpty(), Panic(ELinkMgrHostControllerHasOverflowedHost));
       
   166 	__ASSERT_DEBUG(ready->iCurrentHandle == KInvalidConnectionHandle, Panic(ELinkMgrHostControllerHasOverflowedHost));
       
   167 	
       
   168 	RemoveFromBufferPool(*ready);
       
   169 	ready->iCurrentHandle = aConnHandle;
       
   170 	
       
   171 	RMBufChain retChain;
       
   172 	retChain.Assign(ready->iMBufChain);
       
   173 	
       
   174 	if (IsActive())
       
   175 		{
       
   176 		//This buffer will be reclaimed from the global pool
       
   177 		//after the one(s) we're currently trying to reclaim
       
   178 		LOG(_L8("CHostMBufPool: TakeBuffer, buffer taken while alloc outstanding: queued alloc"));
       
   179 		iWaitingAllocPool.AddLast(*ready);
       
   180 		}
       
   181 	else
       
   182 		{
       
   183 		LOG(_L8("CHostMBufPool: TakeBuffer, buffer taken"));
       
   184 		AllocNewBuffer(*ready);
       
   185 		}
       
   186 		
       
   187 	return retChain;
       
   188 	}
       
   189 
       
   190 void CHostMBufPool::InvalidateByConnH(THCIConnHandle aConnHandle)
       
   191 	{
       
   192 	LOG_FUNC
       
   193 	ASSERT_DEBUG(aConnHandle != KInvalidConnectionHandle);
       
   194 	
       
   195 	// We need to scan through the two pending "lists" to remove the
       
   196 	// connection handle from record so we don't try to provide a 
       
   197 	// packet completion notification (the controller already assumes
       
   198 	// the buffers are free as they are entitled to by the spec).
       
   199 	
       
   200 	// The current buffer being allocated
       
   201 	if(iBufferBeingAllocd && iBufferBeingAllocd->iCurrentHandle == aConnHandle)
       
   202 		{
       
   203 		iBufferBeingAllocd->iCurrentHandle = KInvalidConnectionHandle;
       
   204 		}
       
   205 	
       
   206 	// The list of buffers waiting to be allocted
       
   207 	TPoolBuffer* buffer = NULL;
       
   208 	TSglQueIter<TPoolBuffer> iter(iWaitingAllocPool);
       
   209 	while(buffer=iter++, buffer)
       
   210 		{
       
   211 		if(buffer->iCurrentHandle == aConnHandle)
       
   212 			{
       
   213 			buffer->iCurrentHandle = KInvalidConnectionHandle;
       
   214 			}
       
   215 		}
       
   216 	
       
   217 	// Finally we need to purge any batched up completions if they
       
   218 	// are for this connection handle
       
   219 	if(iCurrAckHandle == aConnHandle)
       
   220 		{
       
   221 		iCurrAckHandle = KErrNotFound;
       
   222 		iCurrCompletedPackets = 0;
       
   223 		}
       
   224 	}
       
   225 
       
   226 void CHostMBufPool::RunL()
       
   227 	{
       
   228 	LOG_FUNC
       
   229 	LEAVEIFERRORL(iStatus.Int());
       
   230 	
       
   231 	// We've successfully allocated a new MBufChain
       
   232 	TPoolBuffer* justAllocd = iBufferBeingAllocd;
       
   233 	iBufferBeingAllocd = NULL;
       
   234 	THCIConnHandle justAllocdHandle = justAllocd->iCurrentHandle;
       
   235 	
       
   236 	// Return buffer to pool for re-use
       
   237 	AddToBufferPool(*justAllocd);
       
   238 	justAllocd->iCurrentHandle = KInvalidConnectionHandle;
       
   239 	
       
   240 	// If connection handle is still valid then we need to provide a completion
       
   241 	// notification for the packet to the connection handle it was from.
       
   242 	if(justAllocdHandle != KInvalidConnectionHandle)
       
   243 		{
       
   244 		if (iCurrAckHandle == KErrNotFound)
       
   245 			{
       
   246 			// This is the first completion we have seen
       
   247 			iCurrAckHandle = justAllocdHandle;
       
   248 			}
       
   249 		ASSERT_DEBUG(iCurrAckHandle != KInvalidConnectionHandle); // just to be sure
       
   250 		
       
   251 		TBool ackNow = (justAllocdHandle != iCurrAckHandle);
       
   252 		
       
   253 		if (!ackNow)
       
   254 			{
       
   255 			iCurrCompletedPackets++;
       
   256 			LOG2(_L8("CHostMBufPool: CompletedPackets++ for conn: %d [->%d]"), justAllocdHandle, iCurrCompletedPackets);
       
   257 			ackNow = (iCurrCompletedPackets >= KStackACLBuffersTideMarkNum);
       
   258 			}
       
   259 			
       
   260 		if (ackNow)
       
   261 			{
       
   262 			TInt err = KErrNone;
       
   263 			
       
   264 			if (iCurrCompletedPackets > 0)
       
   265 				{
       
   266 				LOG2(_L8("CHostMBufPool: Sending HostNumberOfCompletedPackets for conn: %d [%d completed]"), iCurrAckHandle, iCurrCompletedPackets);
       
   267 				//Acknowledge the completed packets
       
   268 				TRAP(err, HostNumberOfCompletedPacketsL(iCurrAckHandle, iCurrCompletedPackets));
       
   269 				//if this failed we probably couldn't alloc the memory for the command frame,
       
   270 				//the HC is still flowed off.
       
   271 				__ASSERT_DEBUG(err == KErrNone, Panic(ELinkMgrCouldNotSendHostNumberOfCompletedPackets));
       
   272 				LEAVEIFERRORL(err);
       
   273 				}
       
   274 			
       
   275 			iCurrCompletedPackets = (justAllocdHandle != iCurrAckHandle) ? 1 : 0;
       
   276 			iCurrAckHandle = justAllocdHandle;
       
   277 			}
       
   278 		}
       
   279 	
       
   280 	TryToAllocQueuedBuffer();
       
   281 	}
       
   282 	
       
   283 void CHostMBufPool::TryToAllocQueuedBuffer()
       
   284 	{
       
   285 	LOG_FUNC
       
   286 	if (!iWaitingAllocPool.IsEmpty() && !IsActive())
       
   287 		{
       
   288 		TPoolBuffer* needsAlloc = iWaitingAllocPool.First();
       
   289 		iWaitingAllocPool.Remove(*needsAlloc);
       
   290 		AllocNewBuffer(*needsAlloc);
       
   291 		}
       
   292 	}
       
   293 	
       
   294 void CHostMBufPool::AllocNewBuffer(TPoolBuffer& aBuffer)
       
   295 	{
       
   296 	LOG_FUNC
       
   297 	ASSERT_DEBUG(!iBufferBeingAllocd);
       
   298 	iBufferBeingAllocd = &aBuffer;
       
   299 	iMBufRequester.Alloc(aBuffer.iMBufChain, KLinkMgrIncomingBufferSize, iStatus);
       
   300 	SetActive();
       
   301 	}
       
   302 	
       
   303 void CHostMBufPool::HostNumberOfCompletedPacketsL(THCIConnHandle aConnH, TUint16 aNumPackets)
       
   304 	{
       
   305 	RArray<THCIConnectionHandle> connHandles;
       
   306 	connHandles.AppendL(aConnH);
       
   307 	CleanupClosePushL(connHandles);
       
   308 
       
   309 	RArray<THCINumOfCompletedPackets> numPackets;
       
   310 	numPackets.AppendL(aNumPackets);
       
   311 	CleanupClosePushL(numPackets);
       
   312 	
       
   313 	CHostNumberOfCompletedPacketsCommand* cmd = CHostNumberOfCompletedPacketsCommand::NewL(1, connHandles, numPackets);
       
   314 	// Ownership of the arrays is taken by the command object.
       
   315 	CleanupStack::Pop(2, &connHandles); // &numPackets, &connHandles
       
   316 
       
   317 	// This is a priority command as we want to try to get this out as soon as possible (and not wait
       
   318 	// for all normal control aspects to be processed).  This command shouldn't normally consume any credits
       
   319 	// so as a priority command it has little impact.
       
   320 	// Ownership of cmd transfered even if MhcqAddPriorityCommandL leaves
       
   321 	iCmdQ.MhcqAddPriorityCommandL(cmd, *this);
       
   322 	}
       
   323 	
       
   324 void CHostMBufPool::MhcqcCommandEventReceived(const THCIEventBase& aEvent, const CHCICommandBase* /*aRelatedCommand*/)
       
   325 	{
       
   326 	LOG_FUNC
       
   327 	// We don't expect a non-error event back because we're only sending Host_Number_of_Completed_Packet commands
       
   328 	if(aEvent.EventCode() == ECommandCompleteEvent)
       
   329 		{
       
   330 		const THCICommandCompleteEvent& completeEvent = THCICommandCompleteEvent::Cast(aEvent);
       
   331 		if(completeEvent.CommandOpcode() == KHostNumberOfCompletedPacketsOpcode)
       
   332 			{
       
   333 			// a regular error for a Host_Number_Of_Completed_Packets command
       
   334 			TInt err = CHciUtil::SymbianErrorCode(completeEvent.ErrorCode());
       
   335 			if(err != KErrNone) // we shouldn't get a non-erroring event back, but just in case
       
   336 				{
       
   337 				Error(err);
       
   338 				}
       
   339 			}
       
   340 		else // an unexpected command complete event
       
   341 			{
       
   342 			LOG1(_L8("CHostMBufPool: Unexpected HCI command complete event; opcode = 0x%04x"), completeEvent.CommandOpcode());
       
   343 			DEBUG_PANIC_LINENUM;
       
   344 			}
       
   345 		}
       
   346 	else // some unexpected event
       
   347 		{
       
   348 		LOG1(_L8("CHostMBufPool: Unexpected HCI event received; code = 0x%02x"), aEvent.EventCode());
       
   349 		DEBUG_PANIC_LINENUM;
       
   350 		}
       
   351 	}
       
   352 
       
   353 void CHostMBufPool::MhcqcCommandErrored(TInt aErrorCode, const CHCICommandBase* /*aCommand*/)
       
   354 	{
       
   355 	LOG_FUNC
       
   356 	Error(aErrorCode);
       
   357 	}
       
   358 	
       
   359 TInt CHostMBufPool::RunError(TInt aError)
       
   360 	{
       
   361 	LOG_FUNC
       
   362 	if(iBufferBeingAllocd)
       
   363 		{
       
   364 		TPoolBuffer* justFailedToAlloc = iBufferBeingAllocd;
       
   365 		iBufferBeingAllocd = NULL;
       
   366 		// Add to wait for alloc queue - we may get another chance
       
   367 		iWaitingAllocPool.AddFirst(*justFailedToAlloc);
       
   368 		}
       
   369 	Error(aError);
       
   370 	return KErrNone;
       
   371 	}
       
   372 	
       
   373 void CHostMBufPool::Error(TInt IF_FLOGGING(aError))
       
   374 	{
       
   375 	LOG_FUNC
       
   376 	// So there has been some internal error when handling controller to host
       
   377 	// flow control.  Tough, we've done our best for now - the only real thing
       
   378 	// that might be worth doing is a hard reset to start-up clean.
       
   379 	LOG1(_L8("CHostMBufPool: ERROR (%d) - inbound data to the stack may stall soon"), aError);
       
   380 	}
       
   381 	
       
   382 #endif