bthci/bthci2/hcicmdq/src/HciCmdQController.cpp
changeset 0 29b1cd4cb562
child 16 9f17f914e828
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bthci/bthci2/hcicmdq/src/HciCmdQController.cpp	Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,2107 @@
+// Copyright (c) 2006-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:
+//
+
+/**
+ @file
+ @internalComponent
+*/
+
+#include <bluetooth/hcicmdqcontroller.h>
+#include <bluetooth/hci/hciconsts.h>
+
+#include "HciCmdQTimer.h"
+#include "HciCmdQUtil.h"
+
+#include <bluetooth/hardresetinitiator.h>
+#include <bluetooth/hcicommandqitem.h>
+#include <bluetooth/hcicommandqueueclient.h>
+#include <bluetooth/linkmuxnotifier.h>
+#include <bluetooth/hci/commandstatusevent.h>
+#include <bluetooth/hci/event.h>
+#include <bluetooth/hci/commandcompleteevent.h>
+#include <bluetooth/hci/hcicmdqueuedecisionplugin.h>
+#include <bluetooth/hci/hcicmdqueuedecisioninterface.h>
+#include <bluetooth/hci/hciframe.h>
+#include <bluetooth/hci/hctlinterface.h>
+#include <bluetooth/hci/hciutil.h>
+#include <bluetooth/hci/command.h>
+#include <bluetooth/hci/hcievents.h>
+#include <bluetooth/hci/completingeventquery.h>
+
+#include <bluetooth/logger.h>
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_HCICMDQ);
+#endif
+
+#ifdef _DEBUG
+PANICCATEGORY("hcicmdqcon");
+#endif
+
+_LIT(KHciCommandQueueComponentName, "cmdq");
+
+EXPORT_C void MHCICmdQueueUtilities::InjectEvent(const THCIEventBase& aEvent)
+	{
+	MhcquiInjectEvent(aEvent);
+	}
+
+EXPORT_C CHCICommandQItem* MHCICmdQueueUtilities::FindOutstandingCommand(THCIOpcode aOpcode)
+	{
+	return MhcquiFindOutstandingCommand(aOpcode);
+	}
+
+EXPORT_C /*static*/ CHCICmdQController* CHCICmdQController::NewL()
+	{
+	LOG_STATIC_FUNC
+
+	CHCICmdQController* self = new (ELeave) CHCICmdQController();
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+/**
+Destructor.
+*/
+EXPORT_C CHCICmdQController::~CHCICmdQController()
+	{
+	LOG_FUNC
+
+	// All (entry point) command queue clients should have removed any commands they added.
+#if defined(_DEBUG) && defined(__FLOG_ACTIVE)
+	// In debug to help debugging by logging the commands left on the queues that should be empty.
+	if(!iInitCommandQ.IsEmpty())
+		{
+		LOG(_L("Initialisation Queue is not empty..."))
+		LogQueue(iInitCommandQ);
+		}
+	if(!iNormalCommandQ.IsEmpty())
+		{
+		LOG(_L("Normal Queue is not empty..."))
+		LogQueue(iNormalCommandQ);
+		}
+	if(!iPriorityCommandQ.IsEmpty())
+		{
+		LOG(_L("Priority Queue is not empty..."))
+		LogQueue(iPriorityCommandQ);
+		}
+	if(!iWorkaroundCommandQ.IsEmpty())
+		{
+		LOG(_L("Workaround Queue is not empty..."))
+		LogQueue(iWorkaroundCommandQ);
+		}
+#endif // _DEBUG && __FLOG_ACTIVE
+	__ASSERT_ALWAYS(iInitCommandQ.IsEmpty(), PANIC(KHCICmdQPanic, EInitCommandQNotEmptyInDestructor));
+	__ASSERT_ALWAYS(iNormalCommandQ.IsEmpty(), PANIC(KHCICmdQPanic, ENormalCommandQNotEmptyInDestructor));
+	__ASSERT_ALWAYS(iPriorityCommandQ.IsEmpty(), PANIC(KHCICmdQPanic, EPriorityCommandQNotEmptyInDestructor));
+	__ASSERT_ALWAYS(iWorkaroundCommandQ.IsEmpty(), PANIC(KHCICmdQPanic, EWorkaroundCommandQNotEmptyInDestructor));
+	
+	// Clean up internally managed queues
+	TDblQueIter<CHCICommandQItem> sentIter(iSentCommandQ);
+	CleanUpQueue(sentIter);
+	TDblQueIter<CHCICommandQItem> resendIter(iResendCommandQ);
+	CleanUpQueue(resendIter);
+
+	__ASSERT_ALWAYS(iSentCommandQ.IsEmpty(), PANIC(KHCICmdQPanic, ESentCommandQNotEmptyInDestructor));
+	__ASSERT_ALWAYS(iResendCommandQ.IsEmpty(), PANIC(KHCICmdQPanic, EResendCommandQNotEmptyInDestructor));
+		
+	delete iQStarvationTimer;
+	delete iSendingCommand;
+	delete iQdpPlugin;
+
+	// Delete async CallBacks.	If running, these should be cancelled by the
+	// d'tor of CAsyncOneShot.
+	delete iAsyncCallBackForReset;
+	delete iAsyncCallBackForSend;
+
+	delete iHciUtil;
+	CLOSE_LOGGER
+	}
+
+/**
+Sets the HCTL interface instance to be used by the Command Queue Controller to
+send command frames over the HCTL.
+
+@param aHctlInterface An instance implementing the interface to the HCTL.
+*/
+EXPORT_C void CHCICmdQController::SetHCTLInterface(MHCTLInterface& aHctlInterface)
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(!iHctl, PANIC(KHCICmdQPanic, EHctlInterfaceInitialisedTwice));
+	iHctl = &aHctlInterface;
+	}
+
+/**
+Sets the HCI Command Allocator interface instance to be used by the Command Queue 
+Controller to create HCI command frames.
+
+@param aCommandAllocator Implementation of command allocator interface.
+*/
+EXPORT_C void CHCICmdQController::SetHCICommandAllocator(MHCICommandAllocator& aCommandAllocator)
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(!iCommandAllocator, PANIC(KHCICmdQPanic, EHciCommandAllocatorInterfaceInitialisedTwice));
+	iCommandAllocator = &aCommandAllocator;
+	}
+
+/**
+Sets the Link Mux Notifier interface instance to be used by the Command Queue 
+Controller to provide co-ordinated sending to the HCTL.
+
+@param aLinkMuxer An instance implementing the interface to the Link Muxer.
+*/
+EXPORT_C void CHCICmdQController::SetLinkMuxNotifier(MLinkMuxNotifier& aLinkMuxer)
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(!iLinkMuxer, PANIC(KHCICmdQPanic, ELinkMuxNotifierInitialisedTwice));	
+	iLinkMuxer = &aLinkMuxer;
+	}
+
+/**
+Sets the command queue client that will subsequently receive unmatched events.
+	
+@param aUnmatchedEventObserver An instance implementing the command queue client interface.
+*/
+EXPORT_C void CHCICmdQController::SetHCIUnmatchedEventObserver(MHCICommandQueueClient& aUnmatchedEventObserver)
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(!iUnmatchedEventObserver, PANIC(KHCICmdQPanic, EUnmatchedEventObserverInitialisedTwice));	
+	iUnmatchedEventObserver = &aUnmatchedEventObserver;
+	}
+
+/**
+Sets the Hard Reset Initiator interface instance to be used by the QDP. 
+This will allow the QDP to request a Hard Reset.
+
+@param aHardResetInitiator An instance implementing the interface to the Hard Reset Initiator.
+*/
+EXPORT_C void CHCICmdQController::SetHardResetInitiator(MHardResetInitiator& aHardResetInitiator)
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(!iHardResetInitiator, PANIC(KHCICmdQPanic, EHardResetInitiatorInitialisedTwice));	
+	iHardResetInitiator = &aHardResetInitiator;
+	iQdp->MhcqdiSetHardResetInitiator(aHardResetInitiator);
+	}
+
+/**
+Sets the physical links state interface instance to be used by the QDP to query the state of
+physical links in the stack..
+
+@param aStackInfo An instance implementing the interface to the physical links state information.
+*/
+EXPORT_C void CHCICmdQController::SetPhysicalLinksState(const MPhysicalLinksState& aStackInfo)
+	{
+	LOG_FUNC
+	iQdp->MhcqdiSetPhysicalLinksState(aStackInfo);
+	}
+
+/**
+This allows the Command Queue to start accepting commands to be queued. Whilst
+in the Initialise state only Initialisation commands will be sent but other
+commands will be added to the Normal and Priority queues so they can be sent
+when we transition to the Started state.
+@see CHCICmdQController::Start
+*/
+EXPORT_C void CHCICmdQController::Initialise()
+	{
+	LOG_FUNC
+	
+	// Can only enter the Initialising state from either the Uninitialised
+	// state or the Resetting state
+	switch(iCmdQControllerState)
+		{
+		case EUninitialised:
+			iCmdQControllerState = EInitialising;
+			break;
+			
+		case EResetting:
+			iCmdQControllerState = EResetInit;
+			break;
+		
+		default:
+			PANIC(KHCICmdQPanic, EInvalidStateTransition);
+			break;
+		};
+	}
+	
+/**
+Initiates an asynchronous request for reset processing on the command queue. 
+Asynchronous reset processing is performed by CHCICmdQController::DoReset(), to
+empty the command queue and perform any other tasks on the Command Queue when 
+the stack requests a sub-system reset. This will put the Command Queue into a 
+state where no commands can be sent until CHCICmdQController::Initialise() is 
+called.
+@see CHCICmdQController::Initialise
+*/
+EXPORT_C void CHCICmdQController::Reset()
+	{
+	LOG_FUNC
+	
+	// Mark the tails of the queues.
+	StorePurgeMarks();
+	
+	// Stop the queue starvation timer and reset the priority insert point.
+	iQStarvationTimer->Cancel();
+
+	iCmdQControllerState = EResetting;
+
+	// Cancel the sending callback if it is currently active.
+	iAsyncCallBackForSend->Cancel();
+	
+	// Call async CallBack to initiate Reset processing.
+	iAsyncCallBackForReset->CallBack();
+	}
+
+/**
+Signals that the Stack has finished intialising the HCI, and that it is
+safe to send normal (non-initialisation) commands.
+Trying to add intialisation commands after this call will result in a panic.
+@see CHCICmdQController::Reset
+@panic EInvalidStateTransition if not in the initialising state.
+@panic EStartCalledWhenInitQNotEmpty if the method is called when the initialisation queue is not empty.
+@panic EObjectNotInitialised if not all inteface references have been populated.
+*/
+EXPORT_C void CHCICmdQController::Start()
+	{
+	LOG_FUNC
+
+	// Can only enter the Started state from the Initialising state
+	__ASSERT_ALWAYS((iCmdQControllerState == EInitialising), PANIC(KHCICmdQPanic, EInvalidStateTransition));
+	__ASSERT_ALWAYS((iInitCommandQ.IsEmpty()), PANIC(KHCICmdQPanic, EStartCalledWhenInitQNotEmpty));
+	__ASSERT_ALWAYS(iHctl &&
+					iCommandAllocator &&
+					iLinkMuxer &&
+					iUnmatchedEventObserver &&
+					iHardResetInitiator, PANIC(KHCICmdQPanic, EObjectNotInitialised));
+
+	iCmdQControllerState = EStarted;
+	if(AnyCmdsToSend())
+		{
+		iAsyncCallBackForSend->CallBack();
+		}
+	}
+
+/**
+Called by CLinkMuxer only when the command sub-channel is open.
+This shall be as a result of this class calling MLinkMuxNotifier::TryToSend.
+Only one send should be sent during this function. If another command is suitable
+for sending then MLinkMuxNotifier::TryToSend should be called again to get 
+permission to do the sending.
+*/
+EXPORT_C void CHCICmdQController::DoSend()
+	{
+	LOG_FUNC;
+
+	LOG1(_L("CHCICmdQController::DoSend() - iSendingCommand = 0x%08x"), iSendingCommand);
+
+	if(iSendingCommand)
+		{
+		// Format and send the command frame.  Operation cannot fail.
+		iSendingCommand->FormatFrame();
+		TInt err = iHctl->MhiWriteCommand(iSendingCommand->Frame().HCTLPayload());
+		LOG1(_L("CHCICmdQController::DoSend() - MhiWriteCommand, Err = %d"), err);
+
+		if(err == KErrNone)
+			{
+			// Increment the commands sent count.
+			iSendingCommand->CommandSent();
+
+			TUint consumed = iSendingCommand->Command().CreditsConsumed();
+			// Check if the vendor command has a completion event
+			MHCICompletingEventQuery* completingEventInterface = NULL;
+			err = iSendingCommand->Command().Extension_(KCompletingEventExpectUid, reinterpret_cast<TAny*&>(completingEventInterface), NULL);
+			if( (err == KErrNone && !completingEventInterface->MhceqCompletingEventExpected()) ||
+				(err == KErrNotSupported && consumed == 0))
+				{
+				// Certain commands (e.g. the Number of Host Complete Packets
+				// command) use no credits and do not normally return any
+				// event. In this case the command is not moved to the sent
+				// queue to prevent it from continually growing.
+				// This call will set iSendingCommand to NULL.
+				DeleteCommand(iSendingCommand);
+				}
+			else
+				{
+				// Decrement command credits, move the command to the sent queue and clear the send buffer.
+				__ASSERT_ALWAYS((consumed <= iCommandCredits),
+							    PANIC(KHCICmdQPanic, ECommandCreditsCountLessThanZero));
+				LOG2(_L("CHCICmdQController::DoSend() - iCommandCredits = %d,  consumed = %d"), iCommandCredits, consumed);
+
+				// If necessary start completion timer.
+				TUint timeout(iQdp->MhcqdiTimeoutRequired(*iSendingCommand));
+				__ASSERT_ALWAYS((timeout <= MhcqMaxHciCommandTimeout()),
+								PANIC(KHCICmdQPanic, ECommandTimeoutTooBig));
+
+				// If a timeout has been specified from the QDP start it.
+				if(timeout != MHCICmdQueueDecisionInterface::KNoTimeoutRequired)
+					{
+					iSendingCommand->StartCompletionTimer(timeout, *this);
+					}
+				
+				iCommandCredits -= consumed;
+				iSentCommandQ.AddLast(*iSendingCommand);
+				iSendingCommand = NULL;
+				}
+			}
+		else
+			{
+			// Failed to write the command.  Error the client.
+			if(iSendingCommand->Client())
+				{
+				iSendingCommand->Client()->MhcqcCommandErrored(err, &iSendingCommand->Command());
+				}
+			DeleteCommand(iSendingCommand); // This will also NULL iSendingCommand
+			}
+		}
+	// Clear the TryToSendBlock and reschedule if required.
+	ClearBlock(ETryToSendBlock);
+	if(AnyCmdsToSend())
+		{
+		iAsyncCallBackForSend->CallBack();
+		}
+	}
+
+/**
+This function is called when the timer for aUncompletedCmdId fires.
+@panic ETimedOutCommandError if aUncompletedCmdId does not exist on
+the sent queue.
+@param aUncompletedCmdId the ID of the uncompleted command.
+*/
+void CHCICmdQController::CompletionTimeoutFired(TUint aUncompletedCmdId)
+	{
+	LOG_FUNC
+	
+	CHCICommandQItem* cmd = ScanQueueByQId(iSentCommandQ, aUncompletedCmdId);
+	__ASSERT_ALWAYS(cmd != NULL, PANIC(KHCICmdQPanic, ETimedOutCommandError));
+	
+	ProcessCommandCompletionTimeout(cmd);
+ 
+	if (AnyCmdsToSend())
+		{
+		iAsyncCallBackForSend->CallBack();
+		}
+	}
+
+/**
+@panic ECanSendDeadlock as a guard against a CanSend block deadlock in
+	   UDEB builds. In UREL builds,
+	   MHardResetInitiator::MhriStartHardReset will be called.
+@panic EResendDeadlock as a guard against a Resend block deadlock in
+	   UDEB builds. In UREL builds,
+	   MHardResetInitiator::MhriStartHardReset will be called.
+@panic EUnknownDeadlock as a guard against priority queue deadlock in
+	   UDEB builds, i.e. the priority queue hasn't changed at all.
+	   In UREL builds, MHardResetInitiator::MhriStartHardReset will be called.
+@param aNextPendingCmdId this was the ID of the next pending
+	   command when the timer was started. If this hasn't changed then
+	   panic / ResetCycle as above.
+*/
+void CHCICmdQController::StarvationTimeoutFired(TUint __DEBUG_ONLY(aNextPendingCmdId))
+	{
+	LOG_FUNC
+
+#ifdef _DEBUG
+	// Check for possible dead-lock situation that can occur.
+	TDblQue<CHCICommandQItem>* queue = QueueSendingNext();
+	
+	if (queue)
+		{
+		if (queue != &iPriorityCommandQ)
+			{
+			CHCICommandQItem* cmd = queue->First();
+	
+			if (cmd->CommandQId() == aNextPendingCmdId)
+				{
+				if (Blocked(ECanSendBlock, ENoBlocks))
+					{
+					PANIC(KHCICmdQPanic, ECanSendDeadlock);
+					}
+				else if (Blocked(EResendBlock, ENoBlocks))
+					{
+					PANIC(KHCICmdQPanic, EResendDeadlock);
+					}
+				}
+			}
+		else if (aNextPendingCmdId == KInvalidCommandQueueItemId)
+			{
+			// This is priority queue, so we can't point to a single culprit.
+			// Apparently none of the commands on the queue could be sent.
+			PANIC(KHCICmdQPanic, EUnknownDeadlock);
+			}
+		}
+	
+	// Always assert in debug builds for starvation timer conditions.
+	// This may be removed when more is known about these scenarios.
+	__ASSERT_DEBUG(EFalse, PANIC(KHCICmdQPanic, EStarvationTimerFired));
+#endif
+
+	// Force possibly stale caching blocks to be recomputed.
+	ClearBlock(ECachingCommandBlocks);
+	
+	iHardResetInitiator->MhriStartHardReset();
+	}
+
+/** 
+Virtuals from MHCICommandQueue
+*/
+
+/*virtual*/ TUint CHCICmdQController::MhcqAddCommandL(CHCICommandQItem* aQueItem)
+	{
+	LOG_FUNC	
+	__ASSERT_ALWAYS(aQueItem != NULL, PANIC(KHCICmdQPanic, EAttemptToAddNullCommand));
+
+	return DoAddCommandL(*aQueItem, iNormalCommandQ, EStarted);
+	}
+
+/*virtual*/ TUint CHCICmdQController::MhcqAddCommandL(CHCICommandBase* aCommandData, MHCICommandQueueClient& aCmdProgressRecipient)
+	{
+	LOG_FUNC
+	__ASSERT_ALWAYS(aCommandData != NULL, PANIC(KHCICmdQPanic, EAttemptToAddNullCommand));
+	__ASSERT_ALWAYS(iCommandAllocator != NULL, PANIC(KHCICmdQPanic, EObjectNotInitialised));
+
+	CHCICommandQItem* aQueItem = CHCICommandQItem::NewL(*iCommandAllocator, 
+														aCmdProgressRecipient, 
+														aCommandData);
+	return MhcqAddCommandL(aQueItem);
+	}
+
+/*virtual*/ TUint CHCICmdQController::MhcqAddPriorityCommandL(CHCICommandQItem* aQueItem)
+	{
+	LOG_FUNC
+	__ASSERT_ALWAYS(aQueItem != NULL, PANIC(KHCICmdQPanic, EAttemptToAddNullCommand));
+
+	return DoAddCommandL(*aQueItem, iPriorityCommandQ, EStarted);
+	}
+
+/*virtual*/ TUint CHCICmdQController::MhcqAddPriorityCommandL(CHCICommandBase* aCommandData, MHCICommandQueueClient& aCmdProgressRecipient)
+	{
+	LOG_FUNC
+	__ASSERT_ALWAYS(aCommandData != NULL, PANIC(KHCICmdQPanic, EAttemptToAddNullCommand));
+	__ASSERT_ALWAYS(iCommandAllocator != NULL, PANIC(KHCICmdQPanic, EObjectNotInitialised));
+	
+	CHCICommandQItem* aQueItem = CHCICommandQItem::NewL(*iCommandAllocator, 
+														aCmdProgressRecipient, 
+														aCommandData);
+	return MhcqAddPriorityCommandL(aQueItem);
+	}
+
+/*virtual*/ TUint CHCICmdQController::MhcqAddInitCommandL(CHCICommandQItem* aQueItem)
+	{
+	LOG_FUNC
+	__ASSERT_ALWAYS(aQueItem != NULL, PANIC(KHCICmdQPanic, EAttemptToAddNullCommand));
+	__ASSERT_ALWAYS((iCmdQControllerState != EStarted), PANIC(KHCICmdQPanic, EInitCmdAfterStart));
+
+	TUint ret = DoAddCommandL(*aQueItem, iInitCommandQ, EInitialising);
+	// Mark as an Initialisation command.
+	aQueItem->SetInitialisationCmd();
+	return ret;
+	}
+
+/*virtual*/ TUint CHCICmdQController::MhcqAddInitCommandL(CHCICommandBase* aCommandData, MHCICommandQueueClient& aCmdProgressRecipient)
+	{
+	LOG_FUNC
+	__ASSERT_ALWAYS(aCommandData != NULL, PANIC(KHCICmdQPanic, EAttemptToAddNullCommand));
+	__ASSERT_ALWAYS(iCommandAllocator != NULL, PANIC(KHCICmdQPanic, EObjectNotInitialised));
+
+	CHCICommandQItem* aQueItem = CHCICommandQItem::NewL(*iCommandAllocator, 
+														aCmdProgressRecipient, 
+														aCommandData);
+	return MhcqAddInitCommandL(aQueItem);
+	}
+
+/*virtual*/ TInt CHCICmdQController::MhcqRemoveCommand(TUint aCommandId,
+													   const MHCICommandQueueClient& aCmdOriginator)
+	{
+	LOG_FUNC
+
+	CHCICommandQItem* pendingCmd = NULL;
+	CHCICommandQItem* lockedCmd = NULL;
+	TInt retVal = KErrNotFound;
+	
+	pendingCmd = ScanQueueByQId(iNormalCommandQ, aCommandId);
+	if(!pendingCmd)
+		{
+		pendingCmd = ScanQueueByQId(iInitCommandQ, aCommandId);
+		if (!pendingCmd)
+			{
+			pendingCmd = ScanQueueByQId(iPriorityCommandQ, aCommandId);
+			if (!pendingCmd)
+				{
+				pendingCmd = ScanQueueByQId(iWorkaroundCommandQ, aCommandId);
+				if(!pendingCmd)
+					{
+					lockedCmd = ScanQueueByQId(iSentCommandQ, aCommandId);
+					if(!lockedCmd)
+						{
+						lockedCmd = ScanQueueByQId(iResendCommandQ, aCommandId);
+						}
+					}
+				}
+			}
+		}
+	
+	if(lockedCmd)
+		{
+		__ASSERT_ALWAYS(lockedCmd->Client() == &aCmdOriginator, PANIC(KHCICmdQPanic, ETryingToDeleteCommandNotOwned));
+		retVal = KErrLocked;
+		}
+	else if(pendingCmd && pendingCmd->IsParent())
+		{
+		__ASSERT_ALWAYS(pendingCmd->Client() == &aCmdOriginator, PANIC(KHCICmdQPanic, ETryingToDeleteCommandNotOwned));
+		retVal = KErrLocked;
+		}
+	else if(pendingCmd)
+		{
+		__ASSERT_ALWAYS(pendingCmd->Client() == &aCmdOriginator, PANIC(KHCICmdQPanic, ETryingToDeleteCommandNotOwned));
+		__ASSERT_ALWAYS(!pendingCmd->IsPreChild(), PANIC(KHCICmdQPanic, ETryingToDeletePreChildCommandById));
+		__ASSERT_ALWAYS(!pendingCmd->IsPostChild(), PANIC(KHCICmdQPanic, ETryingToDeletePostChildCommandById));		
+		DeleteCommand(pendingCmd);
+		retVal = KErrNone;
+		}
+
+	return retVal;
+	}
+
+void CHCICmdQController::HandleCommandRemoval(CHCICommandQItem*& aCmd, TBool aCanDelete)
+	{
+	LOG_FUNC
+	ASSERT_DEBUG(aCmd);
+	if(aCanDelete && !aCmd->IsParent())
+		{
+		// The client should never be the originator of child commands
+		__ASSERT_ALWAYS(!aCmd->IsPreChild(), PANIC(KHCICmdQPanic, ETryingToDeletePreChildCommand));
+		__ASSERT_ALWAYS(!aCmd->IsPostChild(), PANIC(KHCICmdQPanic, ETryingToDeletePostChildCommand));
+		DeleteCommand(aCmd);
+		}
+	else
+		{
+		aCmd->DetachClient();
+		}
+	}
+
+void CHCICmdQController::HandleCommandRemoval(CHCICommandQItem& aCmd, TBool aCanDelete)
+	{
+	LOG_FUNC
+	CHCICommandQItem* cmd = &aCmd;
+	HandleCommandRemoval(cmd, aCanDelete);
+	}
+
+void CHCICmdQController::RemoveAllCommands(TDblQueIter<CHCICommandQItem> aIter,
+											const MHCICommandQueueClient& aCmdOriginator,
+											TBool aCanDelete)
+	{
+	LOG_FUNC
+	
+	for (CHCICommandQItem* item(aIter++);item != NULL;item = aIter++)
+		{
+		if(item->Client() == &aCmdOriginator)
+			{
+			HandleCommandRemoval(*item, aCanDelete);
+			}
+		}
+	}
+
+/*virtual*/ void  CHCICmdQController::MhcqRemoveAllCommands(const MHCICommandQueueClient& aCmdOriginator)
+	{
+	LOG_FUNC
+
+	TDblQueIter<CHCICommandQItem> initIter(iInitCommandQ);
+	RemoveAllCommands(initIter, aCmdOriginator, ETrue);
+
+	TDblQueIter<CHCICommandQItem> normalIter(iNormalCommandQ);
+	RemoveAllCommands(normalIter, aCmdOriginator, ETrue);
+
+	TDblQueIter<CHCICommandQItem> priorityIter(iPriorityCommandQ);
+	RemoveAllCommands(priorityIter, aCmdOriginator, ETrue);
+
+	TDblQueIter<CHCICommandQItem> workaroundIter(iWorkaroundCommandQ);
+	RemoveAllCommands(workaroundIter, aCmdOriginator, ETrue);
+
+	TDblQueIter<CHCICommandQItem> sentIter(iSentCommandQ);
+	RemoveAllCommands(sentIter, aCmdOriginator, EFalse);
+
+	TDblQueIter<CHCICommandQItem> resendIter(iResendCommandQ);
+	RemoveAllCommands(resendIter, aCmdOriginator, EFalse);
+
+	if(iSendingCommand)
+		{
+		if(iSendingCommand->Client() == &aCmdOriginator)
+			{
+			HandleCommandRemoval(iSendingCommand, ETrue);
+			}
+		}
+	}
+
+void CHCICmdQController::CleanUpQueue(TDblQueIter<CHCICommandQItem> aIter)
+	{
+	LOG_FUNC
+	
+	for (CHCICommandQItem* item(aIter++);item != NULL;item = aIter++)
+		{
+		DeleteCommand(item);
+		}
+	}
+
+/*virtual*/ TUint CHCICmdQController::MhcqMaxHciCommandTimeout() const
+	{
+	LOG_FUNC
+	
+	return iMaxHciCommandTimeout;
+	}
+
+/*virtual*/ TAny* CHCICmdQController::MhcqQdpPluginInterface(TUid aUid) const
+	{
+	LOG_FUNC
+
+	__ASSERT_ALWAYS((aUid != TUid::Uid(KHCICmdQueueDecisionInterfaceUid))
+					&& (aUid != TUid::Uid(KHCICmdQueueDecisionEventModifierInterfaceUid)),
+					PANIC(KHCICmdQPanic, EIllegalRequestForQdpInterface));
+	return (iQdpPlugin) ? iQdpPlugin->Interface(aUid) : NULL;
+	}
+
+/** 
+Virtuals from MHCICommandEventObserver
+*/
+
+/*virtual*/ void CHCICmdQController::MhceoEventNotification(const THCIEventBase& aEvent)
+	{
+	LOG_FUNC
+	
+	ProcessEvent(aEvent, ETrue);
+	}
+
+void CHCICmdQController::ProcessEvent(const THCIEventBase& aEvent, TBool aSendToQdp)
+	{
+	LOG_FUNC
+	
+	// Process command credits.
+	THCIEventCode eventCode(aEvent.EventCode());
+	if (eventCode == ECommandCompleteEvent)
+		{
+
+		const THCICommandCompleteEvent& event(THCICommandCompleteEvent::Cast(aEvent));
+		UpdateCommandCredits(event.NumHCICommandPackets());
+
+		if (event.CommandOpcode() == KNopOpcode)
+			{
+			// This is a special command complete event that does not
+			// complete an event. Command_Opcode, 0x0000 is a NOP, and
+			// can be used to change the number of outstanding HCI
+			// command packets that the Host can send before waiting.
+
+			// Nothing else to do for this event, so throw it away
+			return;
+			}
+		}
+	else if (eventCode == ECommandStatusEvent)
+		{
+		TCommandStatusEvent& event = TCommandStatusEvent::Cast(aEvent);
+		UpdateCommandCredits(event.NumHCICommandPackets());
+
+		if (event.CommandOpcode() == KNopOpcode)
+			{
+			// Same as above for NOP command complete event.
+			
+			// Nothing else to do for this event, so throw it away
+			return;
+			}
+		}
+
+	// Iterate through sent queue to try and match the event to a sent command.
+	CHCICommandQItem* cmd(0);
+	TDblQueIter<CHCICommandQItem> qIter(iSentCommandQ);
+	TBool matchesCmd(EFalse);
+	TBool concludesCmd(EFalse);
+	TBool continueMatching(EFalse);
+	TBool eventMatched(EFalse);
+
+	while ((qIter != NULL) && (!eventMatched || continueMatching))
+		{
+		cmd = qIter++;		
+		cmd->Command().Match(aEvent, matchesCmd, concludesCmd, continueMatching);
+		if (matchesCmd)
+			{
+			eventMatched = ETrue;
+			
+			// If the event concludes the command then remove it from the sent queue
+			if (concludesCmd)
+				{
+				cmd->iLink.Deque();
+				}
+			
+			if (aSendToQdp)
+				{
+				// Notify QDP.
+				if (iQdpEventModifier)
+					{
+					THCIEventCode origEventCode = aEvent.EventCode();
+					iQdpEventModifier->MhcqemiMatchedEventReceived(const_cast<THCIEventBase&>(aEvent), *cmd);
+					__ASSERT_ALWAYS(origEventCode == aEvent.EventCode(), PANIC(KHCICmdQPanic, EQdpTryingToChangeEventCode));
+					}
+				else
+					{
+					iQdp->MhcqdiMatchedEventReceived(aEvent, *cmd);
+					}
+				}
+		
+			// Process the matched event.
+			if (aEvent.ErrorCode() == EOK)
+				{
+				ProcessMatchedEvent(aEvent, cmd, concludesCmd);
+				}
+			else
+				{
+				ProcessMatchedErrorEvent(aEvent, cmd, concludesCmd, aSendToQdp);
+				}
+			}
+		}
+
+	if (!eventMatched)
+		{
+		// Process the unmatched event.
+		ProcessUnmatchedEvent(aEvent, aSendToQdp);
+		}
+	
+	// Reschedule if there are commands queued.
+	if (AnyCmdsToSend())
+		{
+		iAsyncCallBackForSend->CallBack();
+		}
+
+	}
+
+void CHCICmdQController::MhcquiInjectEvent(const THCIEventBase& aEvent)
+	{
+	LOG_FUNC
+	
+	ProcessEvent(aEvent, EFalse);
+	}
+
+CHCICommandQItem* CHCICmdQController::MhcquiFindOutstandingCommand(THCIOpcode aOpcode)
+	{
+	LOG_FUNC
+	
+	return ScanQueueByOpcode(iSentCommandQ, aOpcode);
+	}
+
+
+/**
+Constructor.
+*/
+CHCICmdQController::CHCICmdQController()
+  : iMaxHciCommandTimeout(KDefaultMaxHciCommandTimeout),
+	iQueueStarvationTimeout(2 * iMaxHciCommandTimeout),
+	iInitCommandQ(_FOFF(CHCICommandQItem,iLink)),
+	iNormalCommandQ(_FOFF(CHCICommandQItem,iLink)),
+	iPriorityCommandQ(_FOFF(CHCICommandQItem,iLink)),
+	iWorkaroundCommandQ(_FOFF(CHCICommandQItem,iLink)),
+	iSentCommandQ(_FOFF(CHCICommandQItem,iLink)),
+	iResendCommandQ(_FOFF(CHCICommandQItem,iLink)),
+	iCmdQControllerState(EUninitialised),
+	iNextCommandQItemId(1)
+
+	{
+	CONNECT_LOGGER
+	LOG_FUNC
+	}
+
+void CHCICmdQController::ConstructL()
+	{
+	LOG_FUNC
+
+	// Add Async Callbacks
+	TCallBack resetCallBack(AsyncCallBackForReset, this);
+
+	iAsyncCallBackForReset =
+		new (ELeave)CAsyncCallBack(resetCallBack, CActive::EPriorityStandard);
+
+	TCallBack startCallBack(AsyncCallBackForSend, this);
+
+	iAsyncCallBackForSend =
+		new (ELeave)CAsyncCallBack(startCallBack, CActive::EPriorityStandard);
+
+	iQStarvationTimer = CHCICmdQStarvationTimer::NewL();
+
+	// Create HCI Utility library
+	iHciUtil = CHciUtil::NewL(KHciCommandQueueComponentName);
+
+	// If we can't open the ini file then this will be treated in the same way
+	// as not reading a valid UID from the ini file.
+	TRAP_IGNORE(iHciUtil->OpenIniFileL());
+	
+	_LIT(KSection, "QDP");
+	_LIT(KQdpUidTag, "EcomUid");
+
+	TUid qdpImplUid = TUid::Null();
+	TRAPD(err, qdpImplUid = iHciUtil->GetUidFromFileL(KSection, KQdpUidTag));
+	
+	if (err == KErrNone)
+		{
+		// Valid UID found, load it
+		iQdpPlugin = CHCICmdQueueDecisionPlugin::NewL(qdpImplUid);
+		}
+	else
+		{
+		// No UID found in ini file, attempt to load single instance of 
+		// implementation
+		iQdpPlugin = CHCICmdQueueDecisionPlugin::NewL();
+		}
+
+	iQdp = reinterpret_cast<MHCICmdQueueDecisionInterface*>(iQdpPlugin->Interface(TUid::Uid(KHCICmdQueueDecisionInterfaceUid)));
+	iQdpEventModifier = reinterpret_cast<MHCICmdQueueEventModifierInterface*>(iQdpPlugin->Interface(TUid::Uid(KHCICmdQueueDecisionEventModifierInterfaceUid)));
+
+	MHCICmdQueueUtilityUser* qdpUtilityUser = reinterpret_cast<MHCICmdQueueUtilityUser*>(iQdpPlugin->Interface(TUid::Uid(KHCICmdQueueUtilityUserUid)));
+	
+	if (qdpUtilityUser)
+		{
+		qdpUtilityUser->MhcquuSetUtilitiesProvider(*this);
+		}
+	
+	// Read timeout values
+	_LIT(KMaxHciCommandTimeoutTag, "MaxHciCommandTimeout");
+	_LIT(KQueueStarvationTimeoutTag, "QueueStarvationTimeout");
+
+	// Don't worry if we cannot read timeouts from file, already initialised
+	// to default values
+	TRAP_IGNORE(iMaxHciCommandTimeout = iHciUtil->GetValueFromFileL(KSection, KMaxHciCommandTimeoutTag));
+	TRAP_IGNORE(iQueueStarvationTimeout = iHciUtil->GetValueFromFileL(KSection, KQueueStarvationTimeoutTag));
+	
+	__ASSERT_DEBUG(iMaxHciCommandTimeout < iQueueStarvationTimeout, PANIC(KHCICmdQPanic, EQStarvationTimerNotGreaterThanMaxCmdTimer));
+	iQdp->MhcqdiSetTimeouts(iQueueStarvationTimeout, iMaxHciCommandTimeout);
+	iQdp->MhcqdiSetHCICommandQueue(*this);
+
+	// Reset the QDP.
+	iCommandCredits = iQdp->MhcqdiReset();
+	}
+
+/**
+Tests for the specified TQueueStateBits block(s) being set.
+*/		
+inline TBool CHCICmdQController::Blocked(TUint aBlocksToCheck, TUint aBlocksToBypass)
+	{
+	return Blocked(iCommandQState, aBlocksToCheck, aBlocksToBypass);
+	}
+
+/**
+Tests for the specified TQueueStateBits block(s) being set.
+*/		
+inline TBool CHCICmdQController::Blocked(TUint aBlockStatus, TUint aBlocksToCheck, TUint aBlocksToBypass)
+	{
+	return (aBlockStatus & (ValidBlocks(aBlocksToCheck) & ValidBlocks(~aBlocksToBypass)));
+	}
+
+/**
+Clears the specified TQueueStateBits block(s).
+*/
+inline void	CHCICmdQController::ClearBlock(TUint aBlocks)
+	{
+	__ASSERT_DEBUG(!InvalidBlocks(aBlocks), PANIC(KHCICmdQPanic, EIllegalBlock));
+	iCommandQState &= ValidBlocks(~aBlocks);
+	}
+	
+/**
+Sets the specified TQueueStateBits block(s).
+*/
+inline void	CHCICmdQController::SetBlock(TUint aBlocks)
+	{
+	__ASSERT_DEBUG(!InvalidBlocks(aBlocks), PANIC(KHCICmdQPanic, EIllegalBlock));
+	iCommandQState |= ValidBlocks(aBlocks);
+	}
+	
+/**
+Masks out invalid TQueueStateBits block bit(s).
+*/
+inline TUint CHCICmdQController::ValidBlocks(TUint aBlocks)
+	{
+	return (aBlocks & EAllBlocks);
+	}
+	
+/**
+Masks out valid TQueueStateBits block bit(s).
+*/
+inline TUint CHCICmdQController::InvalidBlocks(TUint aBlocks)
+	{
+	return (aBlocks & ~EAllBlocks);
+	}
+
+/**
+Check for queue block bypass conditions. Currently the only such condition
+is to allow a non-credit consuming command (e.g. the Number of Host Complete
+Packets command) to bypass the insufficient credits block.
+*/
+inline TUint CHCICmdQController::CmdBypassBlocks(const CHCICommandQItem& aCmd)
+	{
+	TUint bypassBlocks = ENoBlocks;
+	if (aCmd.Command().CreditsConsumed() == 0)
+		{
+		bypassBlocks |= EInsufficientCreditBlock;
+		}
+	return bypassBlocks;
+	}
+	
+/**
+Updates the command credits counter and maintains the EInsufficientCreditBlock
+*/
+inline void CHCICmdQController::UpdateCommandCredits(TUint8 aCommandCredits)
+	{
+	iCommandCredits = aCommandCredits;
+	
+	if (iCommandCredits > 0)
+		{
+		// We clear block flags when sending priority commands, so there may be some
+		// commands blocked on insufficient credits, but the block flag not set
+		// anymore. Hence we want to schedule the callback irrespectably of the status
+		// of EInsufficientCreditBlock.
+		if (AnyCmdsToSend())
+			{
+			iAsyncCallBackForSend->CallBack();
+			}
+		ClearBlock(EInsufficientCreditBlock);
+		}
+	else
+		{
+		SetBlock(EInsufficientCreditBlock);	
+		}
+	}
+	
+/**
+Selects the active command queue (iNormalCommandQ or iInitCommandQ) based on
+current state.
+*/
+inline TDblQue<CHCICommandQItem>* CHCICmdQController::ActiveRegularQueue()
+	{
+	if (iCmdQControllerState == EStarted)
+		{
+		return &iNormalCommandQ;
+		}
+	else if ((iCmdQControllerState == EResetInit) || (iCmdQControllerState == EInitialising))
+		{
+		return &iInitCommandQ;
+		}
+		
+	return NULL;
+	}
+
+/**
+Returns the pointer to the queue which contains the command that would be scheduled next.
+Does not take the Resend queue into account.
+*/
+TDblQue<CHCICommandQItem>* CHCICmdQController::QueueSendingNext()
+	{
+	if (!iWorkaroundCommandQ.IsEmpty())
+		{
+		// Workaround has highest priority (well, second to resend which we don't care about).
+		return &iWorkaroundCommandQ;
+		}
+	else if ((iCmdQControllerState == EStarted) && !iPriorityCommandQ.IsEmpty())
+		{
+		// Priority comes after Workaround, but we look at it only in EStarted.
+		return &iPriorityCommandQ;
+		}
+	else
+		{
+		// Normal or Initialisation or NULL, depending on the state we're in.
+		return ActiveRegularQueue();
+		}
+	}
+
+/**
+Returns the item at the head of the specified queue, or NULL if the queue is empty.
+*/	
+inline CHCICommandQItem* CHCICmdQController::FirstQueueItem(TDblQue<CHCICommandQItem>& aQueue)
+	{
+	return (aQueue.IsEmpty() ? NULL : aQueue.First());
+	}
+
+/**
+Returns the item at the tail of the specified queue, or NULL if the queue is empty.
+*/	
+inline CHCICommandQItem* CHCICmdQController::LastQueueItem(TDblQue<CHCICommandQItem>& aQueue)
+	{
+	return (aQueue.IsEmpty() ? NULL : aQueue.Last());
+	}
+	
+/**
+Saves the purge marks for the queues that need them. When a reset is performed,
+the queues will be purged up to those marks.
+*/
+void CHCICmdQController::StorePurgeMarks()
+	{
+	iInitQPurgeMark	 = LastQueueItem(iInitCommandQ);
+	iNormalQPurgeMark = LastQueueItem(iNormalCommandQ);
+	iPriorityQPurgeMark = LastQueueItem(iPriorityCommandQ);
+	iWorkaroundQPurgeMark = LastQueueItem(iWorkaroundCommandQ);
+	}
+
+/**
+Purge the queues up to previously saved purge marks.
+*/
+void CHCICmdQController::PurgeAllQueues()
+	{
+	// Purge the queues to the recorded tail markers.
+	PurgeQueue(iInitCommandQ, iInitQPurgeMark);
+	PurgeQueue(iNormalCommandQ, iNormalQPurgeMark);
+	PurgeQueue(iPriorityCommandQ, iPriorityQPurgeMark);
+	PurgeQueue(iWorkaroundCommandQ, iWorkaroundQPurgeMark);
+	
+	iInitQPurgeMark = iNormalQPurgeMark = iPriorityQPurgeMark = iWorkaroundQPurgeMark = NULL;
+	
+	// Purge the sent and resend queues entirely.
+	PurgeQueue(iSentCommandQ, LastQueueItem(iSentCommandQ));
+	PurgeQueue(iResendCommandQ, LastQueueItem(iResendCommandQ));
+	}
+
+
+/**
+Removes and deletes items from the head of the queue up to and including the marked
+item.
+*/	
+void CHCICmdQController::PurgeQueue(TDblQue<CHCICommandQItem>& aQueue,
+									CHCICommandQItem* aMark)
+	{
+	LOG_FUNC
+
+	// A null aMark implies that there are no commands to purge.
+	if(aMark)
+		{
+		TDblQueIter<CHCICommandQItem> qIter(aQueue);
+		TBool found = EFalse;
+
+		// Ensure that the mark is in the queue.
+		while(qIter && !found)
+			{
+			if(aMark == qIter++)
+				{
+				found = ETrue;
+				}
+			}
+
+		// If the mark was found remove comands upto and including
+		// the mark.
+		if(found)
+			{
+			// Reset the queue iterator.
+			qIter.SetToFirst();
+			found = EFalse;
+			CHCICommandQItem* item = NULL;
+
+			while(qIter && !found)
+				{
+				item = qIter++;
+				if(item == aMark)
+					{
+					// Mark found.  Exit the loop after this element has been removed.
+					found = ETrue;
+					}
+				DeleteCommand(item);
+				}
+			}
+		}
+	}
+
+/**
+Returns the first item with the specified opcode which is closest to the head of the 
+specified queue, or NULL if there is no such opcode on the queue.
+*/	
+inline CHCICommandQItem* CHCICmdQController::ScanQueueByOpcode(TDblQue<CHCICommandQItem>& aQueue, THCIOpcode aOpcode)
+	{
+	LOG_FUNC
+
+	CHCICommandQItem* item = NULL;		
+	TDblQueIter<CHCICommandQItem> qIter(aQueue);
+	
+	while (((item = qIter) != NULL) && (item->Command().Opcode() != aOpcode))
+		{
+		qIter++;
+		}
+	
+	return item;
+	}
+
+/**
+Returns the item with the specified command queue ID from the specified queue, or NULL 
+if there is no such command on the queue.
+*/
+inline CHCICommandQItem* CHCICmdQController::ScanQueueByQId(TDblQue<CHCICommandQItem>& aQueue, TUint aCmdId)
+	{
+	LOG_FUNC
+
+	CHCICommandQItem* item = NULL;		
+	TDblQueIter<CHCICommandQItem> qIter(aQueue);
+	
+	while (((item = qIter) != NULL) && (item->CommandQId() != aCmdId))
+		{
+		qIter++;
+		}
+	
+	return item;
+	}
+
+/**
+Performs command queue EResetting state processing. EResetting state is
+reached by a call to CHCICmdQController::Reset().
+*/	
+void CHCICmdQController::DoReset()
+	{
+	LOG_FUNC
+
+	// Should only be reached if Reset has been called
+	__ASSERT_ALWAYS((iCmdQControllerState == EResetInit) || 
+					(iCmdQControllerState == EResetting), 
+					PANIC(KHCICmdQPanic, EInvalidStateTransition));
+
+	// Delete the parent command of possibly ongoing workaround.
+	// Wee need to do it here separately, because the command
+	// is not reachable any other way if it's in post-workaround phase
+	// - it's already been removed from its queue.
+	DeleteCommand(iParentCommand);
+
+	PurgeAllQueues();
+
+	// Reset the QDP.
+	iCommandCredits = iQdp->MhcqdiReset();
+	
+	// Delete any command waiting to be sent.
+	DeleteCommand(iSendingCommand);
+	
+	// Clear the block flags and execute state transition.
+	iCommandQState = 0;
+	
+	// Initialise could have been called during async break so check which
+	// state we should move into
+	if (iCmdQControllerState == EResetInit)
+		{
+		iCmdQControllerState = EInitialising;
+		}
+	else
+		{
+		// Acording to the ASSERT this must be EResetting
+		iCmdQControllerState = EUninitialised;
+		}
+		
+	// Reschedule if there are commands queued and we're ready to send.
+	if ((iCmdQControllerState == EInitialising) && CmdsQueued(&iInitCommandQ))
+		{
+		iAsyncCallBackForSend->CallBack();
+		}
+	}
+
+/**
+The main queue processing routine. Schedules the next command and asks
+HCTL to send.
+*/
+void CHCICmdQController::TryToSend()
+	{
+	LOG_FUNC
+
+	__ASSERT_DEBUG((iCmdQControllerState == EInitialising) || (iCmdQControllerState == EStarted),
+				   PANIC(KHCICmdQPanic, ETryToSendWhileUnoperational));
+	
+	if ((iCmdQControllerState != EInitialising) && (iCmdQControllerState != EStarted))
+		{
+		// This is just a safety net. We shouldn't have been scheduled to run
+		// if we're not ready. But even if we've been mistakenly scheduled or
+		// the state changed in the meantime, this return makes it harmless.
+		return;
+		}
+	
+	TBool stopProcessing(ProcessResendQueue());
+	if (!stopProcessing)
+		{
+		stopProcessing = ProcessWorkaroundQueue();
+		if (!stopProcessing)
+			{
+			if (iCmdQControllerState == EStarted)
+				{
+				stopProcessing = ProcessPriorityQueue();
+				}
+			if (!stopProcessing)
+				{
+				TDblQue<CHCICommandQItem>* queue = ActiveRegularQueue();
+				if (queue != NULL)
+					{
+					ProcessRegularQueue(*queue);
+					}
+				}
+			}
+		}
+	}
+
+/**
+Returns ETrue if our current state allows commands to be queued.
+*/
+inline TBool CHCICmdQController::CanAddCommands()
+	{
+	switch (iCmdQControllerState)
+		{
+	case EStarted:
+	case EResetInit:
+	case EInitialising:
+		return ETrue;
+	default:
+		return EFalse;
+		}
+	}
+
+/**
+Tests if there are items queued on the specified queue.
+*/
+inline TBool CHCICmdQController::CmdsQueued(TDblQue<CHCICommandQItem>* aQueue)
+	{
+	if (aQueue != NULL)
+		{
+		return (!aQueue->IsEmpty());
+		}
+	return EFalse;
+	}
+
+/**
+Tests if there are items queued on any of the queues active in current state.
+*/
+inline TBool CHCICmdQController::AnyCmdsToSend()
+	{
+	TDblQue<CHCICommandQItem>* activeQ = ActiveRegularQueue();
+
+	// Need to check those two no matter which state we're in.
+	TBool commonQueuesEmpty = iWorkaroundCommandQ.IsEmpty() && iResendCommandQ.IsEmpty();
+
+	if (activeQ == &iInitCommandQ)
+		{
+		return !iInitCommandQ.IsEmpty() || !commonQueuesEmpty;
+		}
+	else
+		{
+		return !iNormalCommandQ.IsEmpty() || !iPriorityCommandQ.IsEmpty() || !commonQueuesEmpty;
+		}
+	}
+
+/**
+Returns the next Command Queue Item Id
+*/
+inline TUint CHCICmdQController::NextCommandQueueItemId()
+	{
+	__ASSERT_DEBUG(iNextCommandQItemId != KInvalidCommandQueueItemId,
+				   PANIC(KHCICmdQPanic, EInvalidCommandQueueItemId));
+	iNextCommandQItemId++;
+	if(iNextCommandQItemId == KInvalidCommandQueueItemId)
+		{
+		iNextCommandQItemId++;
+		}
+	return iNextCommandQItemId;
+	}
+	
+/**
+Processes the resend queue.
+Returns ETrue if the scheduling loop shouldn't process lower priority queues
+(which is when it schedules a command for resend) and EFalse if the loop should
+go on.
+*/
+TBool CHCICmdQController::ProcessResendQueue()
+	{
+	LOG_FUNC
+
+	if (!CmdsQueued(&iResendCommandQ))
+		{
+		return EFalse;
+		}
+	
+	CHCICommandQItem* cmd = iResendCommandQ.First();
+	TUint bypassBlocks = CmdBypassBlocks(*cmd);
+	if (Blocked(EResendQueueBlocks, bypassBlocks))
+		{
+		// We're the highest priority queue, but allow the other queues to try
+		// and send if we have commands queued but are blocked - "Only one
+		// active resend at a time" is still fulfilled.
+		return EFalse;
+		}
+
+	if (OkToSendCommand(cmd, bypassBlocks))
+		{
+		// Process command.
+		SendCommand(*cmd);
+		SetBlock(EResendBlock);
+		return ETrue;
+		}
+
+	return EFalse;
+	}
+
+/**
+Processes the workaround queue.
+Returns ETrue if the scheduling loop shouldn't process lower priority queues
+(which is when the queue is non-empty) and EFalse if the loop should go on.
+*/
+TBool CHCICmdQController::ProcessWorkaroundQueue()
+	{
+	LOG_FUNC
+
+	if (!CmdsQueued(&iWorkaroundCommandQ))
+		{
+		return EFalse;
+		}
+
+	CHCICommandQItem* cmd = iWorkaroundCommandQ.First();
+	switch (cmd->Type())
+		{
+	case CHCICommandQItem::EIndeterminate:
+		// Start of workaround sequence, set up the first command.
+		cmd->SetType(CHCICommandQItem::EParent);
+		SetUpNextWorkaroundCmd(NULL, NULL);
+		cmd = iWorkaroundCommandQ.First();
+		UpdateStarvationTimer();
+		break;
+		
+	case CHCICommandQItem::EPreChild:
+	case CHCICommandQItem::EPostChild:
+	case CHCICommandQItem::EParent:
+		// In the middle of a workaround.
+		// Nothing to do here really, command setup has been done by calling
+		// SetUpNextWorkaroundCmd() in Process*Event on completion of the previous
+		// command in the workaround sequence.
+		break;
+	
+	default:
+		__ASSERT_DEBUG(EFalse, PANIC(KHCICmdQPanic, EInvalidCmdQueueItemType));
+		break;
+		}
+
+#ifdef _DEBUG
+	// We should only ever have at maximum one parent and one child on the 
+	// workaround queue at any time.
+	TDblQueIter<CHCICommandQItem> iter(iWorkaroundCommandQ);
+	TUint count = 0;
+	while(iter++)
+		{
+		count++;
+		}
+	__ASSERT_DEBUG(count <= 2, PANIC(KHCICmdQPanic, ETooManyItemsOnWorkaroundQueue));
+#endif // _DEBUG
+	
+	// Now that we're sure we have pre-workaraound set up we can check blocks.
+	TUint bypassBlocks = CmdBypassBlocks(*cmd);
+	if (Blocked(ESendQueueBlocks, bypassBlocks))
+		{
+		// Don't allow lower priority queues to send until we're empty.
+		return ETrue;
+		}
+	
+	if (OkToSendCommand(cmd, bypassBlocks))
+		{
+		SendCommand(*cmd); 
+		UpdateStarvationTimer();
+		SetBlock(EWorkaroundBlock);
+		}
+
+	// Don't allow lower priority queues to send until we're empty.
+	return ETrue;
+	}
+
+/**
+Processes the priority queue.
+Returns ETrue if the scheduling loop shouldn't process lower priority queues
+(which is when the queue is non-empty) and EFalse if the loop should go on.
+*/
+TBool CHCICmdQController::ProcessPriorityQueue()
+	{
+	LOG_FUNC
+
+	if (!CmdsQueued(&iPriorityCommandQ))
+		{
+		return EFalse;
+		}
+	else if (Blocked(ESendQueueBlocks, ECachingCommandBlocks))
+		{
+		// Check blocks excluding ECachingCommandBlocks
+		// The blocks we're checking don't depend on particular commands, so we can
+		// safely return here.
+		// Don't allow lower priority queues to send until we're empty.
+		return ETrue;
+		}
+
+	// Search the queue for the first non-blocking command.
+	// Note that this is still just heurisitics, because even if we find a non-blocking
+	// command, it may still yield a blocking workaround.
+	CHCICommandQItem* cmd = NULL;
+	TDblQueIter<CHCICommandQItem> i(iPriorityCommandQ);
+	while ((cmd = i++) != NULL)
+		{
+		// For every command we clear the cached blocks as they apply to the last command
+		// we attempted to send, using OkToSendCommand. As the priority queue can send out
+		// of order, i.e. not just the head command, the cached blocks need to be
+		// re-evaluated for each command until we find the one we intend to send.
+		ClearBlock(ECachingCommandBlocks);
+		if (OkToSendCommand(cmd, CmdBypassBlocks(*cmd)))
+			{
+			// Found available command.
+			if (iQdp->MhcqdiDoesCommandRequireWorkaround(*cmd))
+				{
+				// Start of workaround sequence.
+				// Move onto the workaround queue and kick the scheduling loop.
+				cmd->iLink.Deque();
+				iWorkaroundCommandQ.AddLast(*cmd);
+				iAsyncCallBackForSend->CallBack();
+				}
+			else
+				{
+				// Non-workaround.
+				cmd->SetType(CHCICommandQItem::ENormal);
+				SendCommand(*cmd);
+				UpdateStarvationTimer();
+				}
+			break;
+			}
+		}
+
+	// At this point we are in one of the following states:
+	//  - All priority commands are either blocked or have errored and been deleted in 
+	//    OkToSendCommand. If any commands are errored then the scheduling loop will 
+	//    also have been kick. 
+	//  - A priority command has been added to the workaround queue and the scheduling 
+	//    loop has been kick.
+	//  - A priority command has been sent.
+	//
+	// In any one of these conditions we want to stop processing further queues so can
+	// always return true.
+	return ETrue;
+	}
+
+/**
+Processes a regular queue - normal or initialization command queue.
+*/
+void CHCICmdQController::ProcessRegularQueue(TDblQue<CHCICommandQItem>& aQueue)
+	{
+	LOG_FUNC
+
+	if (!CmdsQueued(&aQueue))
+		{
+		return;
+		}
+
+	CHCICommandQItem* cmd = aQueue.First();
+	TUint bypassBlocks = CmdBypassBlocks(*cmd);
+	if (Blocked(ESendQueueBlocks, bypassBlocks))
+		{
+		return;
+		}
+
+	if (cmd->Type() == CHCICommandQItem::EIndeterminate)
+		{
+		// It's the first time we're looking into cmd. See if it needs a workaround.
+		if (iQdp->MhcqdiDoesCommandRequireWorkaround(*cmd))
+			{
+			// Start of workaround sequence.
+			// Move it onto the workaround queue and kick the scheduling loop.
+			cmd->iLink.Deque();
+			iWorkaroundCommandQ.AddLast(*cmd);
+			iAsyncCallBackForSend->CallBack();
+			
+			return;
+			}
+		else
+			{
+			// Non-workaround.
+			cmd->SetType(CHCICommandQItem::ENormal);
+			}
+		}
+	if (OkToSendCommand(cmd, bypassBlocks))
+		{
+		SendCommand(*cmd); 
+		UpdateStarvationTimer();
+		}
+	}
+
+/**
+Process workaround related commands, and is invoked every time a
+workaround completes.  As the QDP interface needs to be passed the
+concluding event of each workaround command, and events are used
+synchronously and have a limited lifetime, this means workaround
+handling has to be done at the same time as the event processing.
+
+This processing ensures that the head of the workaround queue contains
+the next command in the workaround. It also tidies up and deletes
+commands as necessary. Child commands are deleted once their events
+have been reported to the QDP. The parent command lives until the end
+of the workaround.
+*/
+void CHCICmdQController::SetUpNextWorkaroundCmd(CHCICommandQItem* aPreviousCmd,
+												const THCIEventBase* aPreviousCmdResult)
+	{
+	LOG_FUNC
+
+	// Update blocks.
+	ClearBlock(EWorkaroundBlock);
+
+	// Get parent
+	if (iParentCommand == NULL)
+		{
+		iParentCommand = iWorkaroundCommandQ.First();
+		}
+
+	__ASSERT_ALWAYS(iParentCommand, PANIC(KHCICmdQPanic, EObjectNotInitialised));
+
+	// Determine next command in the workaround sequence.
+	CHCICommandQItem* child = NULL;
+	CHCICommandQItem::TType type;
+
+	if (iParentCommand->SentCount() == 0)
+		{
+		child = iQdp->MhcqdiGetPreChildCommand(*iParentCommand, aPreviousCmd, aPreviousCmdResult);
+		if (child != NULL)
+			{
+			// Pre-workaround.
+			type = CHCICommandQItem::EPreChild;
+			}
+		else
+			{
+			// Transitioning to post-workaround.
+			type = CHCICommandQItem::EParent;
+			}
+		}
+	else
+		{
+		__ASSERT_ALWAYS(aPreviousCmd, PANIC(KHCICmdQPanic, EObjectNotInitialised));
+		if (aPreviousCmd->Type() == CHCICommandQItem::EParent)
+			{
+			// Transitioning to post-workaround.
+			child = iQdp->MhcqdiGetPostChildCommand(*iParentCommand, NULL, aPreviousCmdResult);
+			}
+		else
+			{
+			// Post-workaround.
+			child = iQdp->MhcqdiGetPostChildCommand(*iParentCommand, aPreviousCmd, aPreviousCmdResult);
+			}
+			
+		type = CHCICommandQItem::EPostChild;
+		}
+	
+	// Delete previous command (if not parent).
+	if ((aPreviousCmd != NULL) &&
+		(aPreviousCmd->Type() != CHCICommandQItem::EParent))
+		{
+		DeleteCommand(aPreviousCmd);
+		}
+
+	if (child != NULL)
+		{
+		// Enqueue child to head of queue.
+		child->SetType(type);
+		iWorkaroundCommandQ.AddFirst(*child);
+		}
+	else if (type != CHCICommandQItem::EParent)
+		{
+		// Give the QDP a chance to update state within the stack if required
+		THCIEventBase* fakedEvent = NULL;
+		while ((fakedEvent = iQdp->MhcqdiGetFakedUnsolicitedEvent(*iParentCommand, fakedEvent)) != NULL)
+			{
+			iUnmatchedEventObserver->MhcqcCommandEventReceived(*fakedEvent, NULL);
+			}
+			
+		// End of workaround sequence, delete parent.
+		DeleteCommand(iParentCommand);
+		}
+	}
+
+/**
+A helper for restarting the Starvation Timer during command addition/sendout.
+Determines the next command that will be sent and restarts the timer with
+the ID of that command.
+Caveat: this always restarts (or cancels, hence Update rather than just Restart in the name)
+the timer, so use it only when you know that the command that will be sent next has really changed.
+*/
+void CHCICmdQController::UpdateStarvationTimer()
+	{
+	// Determine the id of the next command to be executed.
+	// Don't care about resend queue, the starvation timer shouldn't be restarted on resends.	
+
+	TUint cmdId = KInvalidCommandQueueItemId;
+
+	if (!iWorkaroundCommandQ.IsEmpty())
+		{
+		cmdId = iWorkaroundCommandQ.First()->CommandQId();
+		}
+	else if ((iCmdQControllerState == EStarted) && !iPriorityCommandQ.IsEmpty())
+		{
+		// Only send off priority queue in EStarted (i.e. not in EInitialising)
+		// Never know which priority command will be sent next.
+		cmdId = KInvalidCommandQueueItemId;
+		}
+	else
+		{
+		TDblQue<CHCICommandQItem>* queue = ActiveRegularQueue();
+		if (queue && !queue->IsEmpty())
+			{
+			cmdId = queue->First()->CommandQId();
+			}
+		else
+			{
+			// No command to send.
+			iQStarvationTimer->Cancel();
+			return;
+			}
+		}
+	iQStarvationTimer->Restart(iQueueStarvationTimeout, cmdId, *this);
+	}
+
+/**
+Dequeues and deletes aItem from its queue.
+*/
+void CHCICmdQController::DeleteCommand(CHCICommandQItem* &aItem)
+	{
+	LOG_FUNC
+
+	if (!aItem)
+		{
+		return;
+		}
+	aItem->iLink.Deque();
+	iQdp->MhcqdiCommandAboutToBeDeleted(*aItem);
+	delete aItem;
+	aItem = NULL;
+	UpdateStarvationTimer();
+	}
+
+/**
+Helper to be called on command addition.
+Returns ETrue if the command that will be sent next changed after we added a command to aQueue.
+Ignores the Resend queue.
+*/
+TBool CHCICmdQController::NextCommandChanged(const TDblQue<CHCICommandQItem> &aQueue)
+	{
+	LOG_FUNC
+
+	if (&aQueue != QueueSendingNext())
+		{
+		// We added a command to aQueue, but it's still another queue
+		// that sends next, so we didn't change the next command to be sent.
+		return EFalse;
+		}
+	else // aQueue is sending next.
+		{
+		if (&aQueue == &iPriorityCommandQ)
+			{
+			// PriorityQ is not FIFO, so any added command can potentially
+			// be sent next irrespectably of whether the queue was empty or not.
+			return ETrue;
+			}
+		else
+			{
+			// Rest of queues is FIFO, so the next command to be sent changed
+			// if the queue had been empty when we added the command.
+			return aQueue.First() == aQueue.Last();
+			}
+		}
+	}
+
+/**
+The general command addition routine. Adds aQueItem to aQueue and schedules
+the command processing loop if current state is equal to aActiveState.
+*/
+TUint CHCICmdQController::DoAddCommandL(CHCICommandQItem& aQueItem,
+		TDblQue<CHCICommandQItem> &aQueue, TCmdQControllerStates aActiveState)
+	{
+	LOG_FUNC	
+
+	// Ensure we are in a state that allows commands to be added.
+	if(!CanAddCommands())
+		{
+		delete &aQueItem;
+		User::Leave(KErrHardwareNotAvailable);
+		}
+		
+	// Assign a unique CommandQId.
+	aQueItem.SetCommandQId(NextCommandQueueItemId());
+
+	aQueue.AddLast(aQueItem);
+	// Restart queue starvation timer if this is the next command to be sent.
+	if (NextCommandChanged(aQueue))
+		{
+		// If this is priority queue, we don't really know which command should be executed
+		// next, so we feed the timer with KInvalidCommandQueueItemId in that case.
+		TUint cmdId = KInvalidCommandQueueItemId;
+		if (&aQueue != &iPriorityCommandQ)
+			{
+			cmdId = aQueItem.CommandQId();
+			}
+		iQStarvationTimer->Restart(iQueueStarvationTimeout, cmdId, *this);
+		}
+
+	// Only schedule if we're in the state in which given queue should be considered for scheduling.
+	if (iCmdQControllerState == aActiveState)
+		{
+		iAsyncCallBackForSend->CallBack();
+		}
+
+	return aQueItem.CommandQId();
+	}
+
+/**
+Evaluates various blocking conditions to determine if the command can be sent.
+aCmdQItem is passed by reference to pointer as it can be deleted and NULLified in case of error.
+@return whether the command can be sent.
+*/
+TBool CHCICmdQController::OkToSendCommand(CHCICommandQItem*& aCmdQItem, TUint aBypassBlocks)
+	{
+	TInt result = KErrNone;
+	LOG_FUNC
+	
+	__ASSERT_ALWAYS(aCmdQItem, PANIC(KHCICmdQPanic, ENullCmdQItemPtr));
+	
+	if((iCommandCredits < aCmdQItem->Command().CreditsConsumed()) && 
+	   (!(aBypassBlocks & EInsufficientCreditBlock))) 
+		{
+		// Insufficient HCI credits. Block and suspend processing queue.
+		SetBlock(EInsufficientCreditBlock);
+		}
+	else 
+		{
+		CHCICommandQItem* const duplicate = ScanQueueByOpcode(iSentCommandQ,
+															  aCmdQItem->Command().Opcode());
+		if (duplicate && !(aBypassBlocks & EDuplicatedOpcodeBlock))
+			{
+			// Duplicate command pending. Block and suspend processing queue.
+			SetBlock(EDuplicatedOpcodeBlock);
+
+			// Need to mark the duplicate so that EDuplicatedOpcodeBlock is cleared
+			// on its completion.
+			duplicate->SetDuplicatedOpcode(ETrue);
+			}
+		else
+			{
+			const TDblQue<const CHCICommandQItem>* sentQ = (reinterpret_cast<const TDblQue<const CHCICommandQItem>*>(&iSentCommandQ));
+
+			result = iQdp->MhcqdiCanSend(*aCmdQItem, *sentQ);
+			// KErrNone if aCommand is to be sent, EBlock if Command
+			// is to be delayed.
+			if(result < 0)
+				{
+				if(aCmdQItem->Client())
+					{
+					aCmdQItem->Client()->MhcqcCommandErrored(result, &aCmdQItem->Command());	
+					}
+				
+				// Dequeue, delete & set to NULL.
+				DeleteCommand(aCmdQItem);
+				
+				if(AnyCmdsToSend())
+					{
+					iAsyncCallBackForSend->CallBack();
+					}
+				}
+			else if(result == MHCICmdQueueDecisionInterface::EBlock && !(aBypassBlocks & ECanSendBlock))
+				{
+				// QDP should never block when the sent queue is empty (would permanently block the queue.
+				__ASSERT_ALWAYS((!iSentCommandQ.IsEmpty()), PANIC(KHCICmdQPanic, ECanSendBlockWhenEmpty));
+				SetBlock(ECanSendBlock);
+				}
+			}
+		}
+
+	return ((!Blocked(ECachingCommandBlocks, aBypassBlocks))&&(result==KErrNone));
+	}
+	
+/**
+Submits a request to the link muxer for permission to send a command. 
+*/
+void CHCICmdQController::SendCommand(CHCICommandQItem& aCmdQItem)
+	{
+	LOG_FUNC
+
+	__ASSERT_DEBUG(!iSendingCommand, PANIC(KHCICmdQPanic, ETryToSendWhilstSending));
+ 
+	iSendingCommand = &aCmdQItem;
+	// Dequeue command.
+	iSendingCommand->iLink.Deque();
+
+	// Initiate send request to HCTL, and block the queue.
+	iLinkMuxer->TryToSend();
+	SetBlock(ETryToSendBlock);
+	}
+
+/**
+Process matched non-error events.
+*/
+void CHCICmdQController::ProcessMatchedEvent(const THCIEventBase& aEvent, CHCICommandQItem* aCmd, TBool aConcludesCmd)
+	{
+	LOG_FUNC
+	
+	// If required by the command, a Command Status Event should always be received before any other event related to a command.
+	THCIEventCode eventCode(aEvent.EventCode());	
+	if (eventCode == ECommandStatusEvent)
+		{
+		if (aCmd->Command().ExpectsCommandStatusEvent())
+			{
+			aCmd->SetReceivedCmdStatusEvent(ETrue);
+			}
+		else
+			{
+			// assert in debug that we don't get here.
+			}
+		}
+	else 
+		{
+		// Non Command Status Event, clear possible CanSend block.
+		ClearBlock(ECanSendBlock);
+		}
+		
+	CHCICommandQItem::TType type = aCmd->Type();
+	if ((type != CHCICommandQItem::EPreChild) &&
+		(type != CHCICommandQItem::EPostChild))
+		{
+		// Command client is notified of non-workaround command events.
+		if(aCmd->Client())
+			{
+			aCmd->Client()->MhcqcCommandEventReceived(aEvent, &aCmd->Command());
+			}
+		}
+	
+	if (aConcludesCmd)
+		{
+		// Check that if a command status is expected then it has been got before being concluded.
+
+		// Cancel completion timer.
+		aCmd->CancelCompletionTimer();
+		
+		// Update blocks, cleared blocks will be re-evaluated on next Run Queue
+		if (aCmd->SentCount() > 1)
+			{
+			ClearBlock(EResendBlock);
+			}
+		if (aCmd->DuplicatedOpcode())
+			{
+			ClearBlock(EDuplicatedOpcodeBlock);
+			}
+						
+		// Process completed command.					
+		if (type == CHCICommandQItem::ENormal)
+			{
+			// Non-workaround command. Delete processed command.
+			DeleteCommand(aCmd);
+			}
+		else
+			{
+			// Workaround in progress. Set up next workaround command.
+			SetUpNextWorkaroundCmd(aCmd, &aEvent);
+			}
+		}
+	}
+
+/**
+Process matched error events.
+*/
+void CHCICmdQController::ProcessMatchedErrorEvent(const THCIEventBase& aEvent, CHCICommandQItem* aCmd, TBool /* aConcludesCmd */, TBool aSendToQdp)
+	{
+	LOG_FUNC
+	
+	// Cancel completion timer. 
+	aCmd->CancelCompletionTimer(); 
+		
+	/*
+	
+	Update blocks. The following blocks are not cleared:
+	
+	1. The Resend block untouched to avoid interfering with an in-progress resend and 
+	   to ensure that only a single resend is active at a time.	
+	2. The TryToSend block which is strictly managed by the HCTL flow control methods 
+	   (SendCommand and DoSend). 
+	3. The Workaround block to prevent possibly bypassing a valid workaround block
+	   on completion of a resend.
+	
+	All other cleared blocks (the caching blocks) will be re-evaluated before sending
+	the next command.
+	*/
+	ClearBlock(ECachingCommandBlocks);
+	
+	// Remove item from sent queue.
+	aCmd->iLink.Deque();
+
+	if (aSendToQdp && iQdp->MhcqdiMatchedErrorEventReceived(aEvent, *aCmd) == MHCICmdQueueDecisionInterface::EResendErroredCommand)
+		{
+		// Clear the command status event flag and enqueue to the resend queue
+		aCmd->SetReceivedCmdStatusEvent(EFalse);
+		if (aCmd->SentCount() > 1)
+			{
+			// Command was previously resent, enqueue to head of resend queue and clear Resend block
+			iResendCommandQ.AddFirst(*aCmd);
+			ClearBlock(EResendBlock);
+			}
+		else
+			{
+			// Command has not previously been resent, enqueue to tail of resend queue.
+			iResendCommandQ.AddLast(*aCmd);			
+			}
+		}
+	else
+		{
+		CHCICommandQItem::TType type = aCmd->Type();
+
+		if ((type != CHCICommandQItem::EPreChild) &&
+			(type != CHCICommandQItem::EPostChild))
+			{
+			// Command error event received, notify client if non-child command
+			if(aCmd->Client())
+				{
+				aCmd->Client()->MhcqcCommandEventReceived(aEvent, &aCmd->Command());	
+				}
+			}
+
+		// Process completed command.					
+		if (type == CHCICommandQItem::ENormal)
+			{
+			// Non-workaround command. Delete processed command.
+			DeleteCommand(aCmd);
+			}
+		else
+			{
+			// Workaround in progress. Set up next workaround command.
+			SetUpNextWorkaroundCmd(aCmd, &aEvent);
+			}											
+		}
+	}
+
+/**
+Processes unmatched events.
+*/
+void CHCICmdQController::ProcessUnmatchedEvent(const THCIEventBase& aEvent, TBool aSendToQdp)
+	{
+	LOG_FUNC
+	
+	if (aSendToQdp)
+		{
+		// Notify QDP.
+		if (iQdpEventModifier)
+			{
+			THCIEventCode origEventCode = aEvent.EventCode();
+			iQdpEventModifier->MhcqemiUnmatchedEventReceived(const_cast<THCIEventBase&>(aEvent));
+			__ASSERT_ALWAYS(origEventCode == aEvent.EventCode(), PANIC(KHCICmdQPanic, EQdpTryingToChangeEventCode));
+			}
+		else
+			{
+			iQdp->MhcqdiUnmatchedEventReceived(aEvent);
+			}
+		}
+
+	iUnmatchedEventObserver->MhcqcCommandEventReceived(aEvent, NULL);
+	}
+
+/**
+Processes timed out commands.
+*/
+void CHCICmdQController::ProcessCommandCompletionTimeout(CHCICommandQItem* aCmd)
+	{
+	LOG_FUNC
+		
+	/*
+	
+	Update blocks. The following blocks are not cleared:
+	
+	1. The Resend block untouched to avoid interfering with an in-progress resend and 
+	   to ensure that only a single resend is active at a time.	
+	2. The TryToSend block which is strictly managed by the HCTL flow control methods 
+	   (SendCommand and DoSend). 
+	3. The Workaround block to prevent possibly bypassing a valid workaround block
+	   on completion of a resend.
+	
+	All other cleared blocks (the caching blocks) will be re-evaluated before sending
+	the next command.
+	*/
+	ClearBlock(ECachingCommandBlocks);
+	
+	// Remove item from sent queue.
+	aCmd->iLink.Deque();
+
+	TUint refundedCredits = 0;
+	MHCICmdQueueDecisionInterface::TCommandTimedOutAction action;
+	const TDblQue<const CHCICommandQItem>* sentQ = (reinterpret_cast<const TDblQue<const CHCICommandQItem>*>(&iSentCommandQ));
+
+	action = iQdp->MhcqdiCommandTimedOut(*aCmd, *sentQ, iCommandCredits, refundedCredits);
+	
+	// Take back any refunded credits
+	iCommandCredits += refundedCredits;
+
+	if (action == MHCICmdQueueDecisionInterface::EContinueWithTimeoutEvent)
+		{
+		CHCICommandQItem::TType type = aCmd->Type();
+
+		if ((type != CHCICommandQItem::EPreChild) &&
+			(type != CHCICommandQItem::EPostChild))
+			{
+			// Command error event received, notify client if non-child command
+			if(aCmd->Client())
+				{
+				aCmd->Client()->MhcqcCommandErrored(CHciUtil::SymbianErrorCode(EHardwareFail), &aCmd->Command());	
+				}
+			}
+
+		// Process completed command.					
+		if (type == CHCICommandQItem::ENormal)
+			{
+			// Non-workaround command. Delete processed command.
+			DeleteCommand(aCmd);
+			}
+		else
+			{
+			SetUpNextWorkaroundCmd(aCmd, NULL);
+			}											
+		}
+	else if (action == MHCICmdQueueDecisionInterface::EResendTimedOutCommand)
+		{
+		// Clear the command status event flag and enqueue to the resend queue
+		aCmd->SetReceivedCmdStatusEvent(EFalse);
+		if (aCmd->SentCount() > 1)
+			{
+			// Command was previously resent, enqueue to head of resend queue and clear Resend block
+			iResendCommandQ.AddFirst(*aCmd);
+			ClearBlock(EResendBlock);
+			}
+		else
+			{
+			// Command has not previously been resent, enqueue to tail of resend queue.
+			iResendCommandQ.AddLast(*aCmd);
+			}
+		}
+	}
+
+// Static async CallBack methods.
+/*static*/ TInt CHCICmdQController::AsyncCallBackForReset(TAny* aCmdQController)
+	{
+	CHCICmdQController* cmdQController = static_cast<CHCICmdQController*>(aCmdQController);
+	cmdQController->DoReset();
+	return EFalse;	// Don't call back any more.
+	}
+	
+/*static*/ TInt CHCICmdQController::AsyncCallBackForSend(TAny* aCmdQController)
+	{
+	CHCICmdQController* cmdQController = static_cast<CHCICmdQController*>(aCmdQController);
+	cmdQController->TryToSend();
+	return EFalse;	// Don't call back any more.
+	}
+
+#ifdef _DEBUG
+void CHCICmdQController::LogQueue(TDblQue<CHCICommandQItem>& aQueue)
+	{
+	TDblQueIter<CHCICommandQItem> iter(aQueue);
+	while(CHCICommandQItem* item = iter++)
+		{
+		THCIOpcode opcode = item->Command().Opcode();
+		const TUint16 KOgfMask = 0xfc00;
+		TUint8 ogf = (opcode&KOgfMask) >> 10; // OGF starts at bit 10.
+		TUint16 ocf = (opcode&~KOgfMask);
+		LOG4(_L("Command OGF(0x%02x) OCF(0x%04x) item 0x%08x owned by 0x%08x"), ogf, ocf, item, item->Client());
+		}
+	}
+#endif // _DEBUG
+
+