bthci/bthci2/hcicmdq/interface/HciCmdQController.h
changeset 0 29b1cd4cb562
child 51 20ac952a623c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bthci/bthci2/hcicmdq/interface/HciCmdQController.h	Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,405 @@
+// 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
+ @internalTechnology
+*/
+
+#ifndef HCICMDQCONTROLLER_H
+#define HCICMDQCONTROLLER_H
+
+#include <e32base.h>
+
+#include <bluetooth/hcicommandeventobserver.h>
+#include <bluetooth/hcicommandqueue.h>
+#include <bluetooth/hci/hciopcodes.h>
+#include <bttypes.h> // For THCIConnHandle
+#include <bluetooth/hci/hcicmdqueuedecisioninterface.h>
+
+class CHCICmdQueueDecisionPlugin;
+class CHCICommandQItem;
+class MHCICmdQueueDecisionInterface;
+class MHCICmdQueueEventModifierInterface;
+class MHCICommandAllocator;
+class MHCICommandQueueClient;
+class MHardResetInitiator;
+class MHCTLInterface;
+class MLinkMuxNotifier;
+class MPhysicalLinksState;
+class CHCICmdQStarvationTimer;
+class CHciUtil;
+
+// Callback interface for the timer.
+NONSHARABLE_CLASS(MHCITimerClient)
+	{
+public:
+	virtual void CompletionTimeoutFired(TUint aUncompletedCmdId) = 0;
+	virtual void StarvationTimeoutFired(TUint aNextPendingCmdId) = 0;
+	};
+
+NONSHARABLE_CLASS(CHCICmdQController) : public CBase,
+										public MHCICommandEventObserver, 
+										public MHCICommandQueue,
+										public MHCITimerClient,
+										public MHCICmdQueueUtilities
+	{
+public: // exported
+	IMPORT_C static CHCICmdQController* NewL();
+	IMPORT_C ~CHCICmdQController();
+
+	IMPORT_C void SetHCTLInterface(MHCTLInterface& aHctlInterface);
+	IMPORT_C void SetHCICommandAllocator(MHCICommandAllocator& aCommandAllocator);
+	IMPORT_C void SetLinkMuxNotifier(MLinkMuxNotifier& aLinkMuxer);
+	IMPORT_C void SetHCIUnmatchedEventObserver(MHCICommandQueueClient& aUnmatchedEventObserver);
+	IMPORT_C void SetHardResetInitiator(MHardResetInitiator& aHardResetInitiator);
+	IMPORT_C void SetPhysicalLinksState(const MPhysicalLinksState& aStackInfo);
+											 
+	IMPORT_C void Initialise();
+	IMPORT_C void Reset();
+	IMPORT_C void Start();
+	IMPORT_C void DoSend();
+
+public: // un-exported virtuals from MHCITimerClient
+	virtual void CompletionTimeoutFired(TUint aUncompletedCmdId);
+	virtual void StarvationTimeoutFired(TUint aWasNextPendingCmdId);
+	
+public: // virtuals from MHCICommandQueue
+	virtual TUint MhcqAddCommandL(CHCICommandQItem* aQueItem);
+	virtual TUint MhcqAddCommandL(CHCICommandBase* aCommandData, MHCICommandQueueClient& aCmdProgressRecipient);
+	virtual TUint MhcqAddPriorityCommandL(CHCICommandQItem* aQueItem);
+	virtual TUint MhcqAddPriorityCommandL(CHCICommandBase* aCommandData, MHCICommandQueueClient& aCmdProgressRecipient);
+	virtual TUint MhcqAddInitCommandL(CHCICommandQItem* aQueItem);
+	virtual TUint MhcqAddInitCommandL(CHCICommandBase* aCommandData, MHCICommandQueueClient& aCmdProgressRecipient);
+	virtual TInt  MhcqRemoveCommand(TUint aCommandId, const MHCICommandQueueClient& aCmdOriginator);
+	virtual void  MhcqRemoveAllCommands(const MHCICommandQueueClient& aCmdOriginator);
+	virtual TUint MhcqMaxHciCommandTimeout() const;
+	virtual TAny* MhcqQdpPluginInterface(TUid aUid) const;
+		
+private: // virtuals from MHCICommandEventObserver	
+	virtual void MhceoEventNotification(const THCIEventBase& aEvent);
+
+	// Helpers
+	void RemoveAllCommands(TDblQueIter<CHCICommandQItem> aIter,
+						   const MHCICommandQueueClient& aCmdOriginator,
+						   TBool aCanDelete);
+	void HandleCommandRemoval(CHCICommandQItem& aCmd, TBool aCanDelete);
+	void HandleCommandRemoval(CHCICommandQItem*& aCmd, TBool aCanDelete);
+	void CleanUpQueue(TDblQueIter<CHCICommandQItem> aIter);
+
+private: // virtuals from MHCICmdQueueUtilities
+	void MhcquiInjectEvent(const THCIEventBase& aEvent);
+	CHCICommandQItem* MhcquiFindOutstandingCommand(THCIOpcode aOpcode);
+	
+private:
+	/**
+	Command queue block bit flags.
+	*/
+	enum
+		{
+		ENoBlocks					= 0x00000000,
+		/**
+		Indicates that there is a workaround command on the sent
+		queue, so another command cannot be sent
+		*/
+		EWorkaroundBlock			= 0x00000001,
+
+		/**
+		Indicates the current head of the non-priority queues has been
+		blocked from MHCICmdQueueDecisionInterface::MhcqdiCanSend()
+		*/
+		ECanSendBlock				= 0x00000002,
+
+		/**
+		Indicates that the current head of the non-priority queues has the
+		same opcode as one on the sent queue.
+		*/
+		EDuplicatedOpcodeBlock		= 0x00000004,
+
+		/**
+		Indicates that the current head of the non-priority queues does not have
+		enough HCI credits.
+		*/
+		EInsufficientCreditBlock	= 0x00000008,
+
+		/**
+		Indicates that there is a resent command on the sent queue, so
+		another command cannot be resent.
+		*/
+		EResendBlock				= 0x00000010,
+
+		/**
+		Indicates that an HCTL send request
+		(MLinkMuxNotifier::TryToSend) is in progress, so another
+		command cannot be scheduled until we get a DoSend() callback.
+		*/
+		ETryToSendBlock				= 0x00000020,
+
+		EAllBlocks					= EWorkaroundBlock |
+									  ECanSendBlock |
+									  EDuplicatedOpcodeBlock |
+									  EInsufficientCreditBlock |
+									  EResendBlock | ETryToSendBlock,
+
+		/**
+		These three are different from the TryToSend, Resend and Workaround
+		blocks in that they only cache the effect of the last call to OkToSendCommand
+		for the most recently processed non-priority command. This means that
+		when they're all 0, OkToSendCommand needs to be called to re-evaluate
+		corresponding conditions.
+		*/
+		ECachingCommandBlocks		= ECanSendBlock |
+									  EDuplicatedOpcodeBlock |
+									  EInsufficientCreditBlock,
+
+		/**
+		Do not include the Resend block for any of the queues waiting to send a command
+		for the first time. Regardless of whether the Resend block is set we would need
+		a credit to send a command and if we get more than one credit (the first 
+		potentially being used by a resent command) then we should use them.
+		*/
+		ESendQueueBlocks			= EAllBlocks & ~EResendBlock,
+
+		/**
+		For the Resend queue we do not want to include the Workaround block as it maybe
+		a workaround command being resent.
+		*/
+		EResendQueueBlocks			= EAllBlocks & ~EWorkaroundBlock,
+		};
+
+	/**
+	Command queue controller state
+	*/
+	enum TCmdQControllerStates
+		{
+		/**
+		Power down state state. In this state no commands will be sent or queued.
+		Any attempt to queue a new command will fail.
+		
+		Reached on completion of async Reset() if in EResetting state.
+		*/
+		EUninitialised	= 0,
+		
+		/**
+		Asynchronous reset pending state.
+		
+		Reached by calling Reset().
+		*/
+		EResetting		= 1,
+
+		/**
+		Asynchronous reset pending state.
+		
+		Reached by calling Initialise() while async Reset() outstanding.
+		*/
+		EResetInit		= 2,
+		
+		/**
+		Initialisation state. In this state only initilisation commands will be 
+		sent, normal and priority commands are held on their queues.
+		
+		Reached by Initialise() or completion of async Reset() if in 
+		EResetInit state.
+		*/
+		EInitialising	= 3,
+		
+		/**
+		Operational state.
+		
+		Reached by calling Start().
+		*/
+		EStarted		= 4,
+		};
+
+	CHCICmdQController();
+	void ConstructL();
+	
+	inline TBool Blocked(TUint aBlocksToCheck, TUint aBlocksToBypass);
+	inline TBool Blocked(TUint aBlockStatus, TUint aBlocksToCheck, TUint aBlocksToBypass);
+	inline void	ClearBlock(TUint aBlocks);
+	inline void	SetBlock(TUint aBlocks);
+	inline TUint ValidBlocks(TUint aBlocks);
+	inline TUint InvalidBlocks(TUint aBlocks);
+	inline TUint CmdBypassBlocks(const CHCICommandQItem& aCmd);
+	
+	inline void UpdateCommandCredits(TUint8 aCommandCredits);
+
+	inline TUint NextCommandQueueItemId();
+	
+	inline TDblQue<CHCICommandQItem>* ActiveRegularQueue();
+	inline CHCICommandQItem* FirstQueueItem(TDblQue<CHCICommandQItem>& aQueue);
+	inline CHCICommandQItem* LastQueueItem(TDblQue<CHCICommandQItem>& aQueue);
+
+	inline void StorePurgeMarks();
+	inline void PurgeAllQueues();
+	void PurgeQueue(TDblQue<CHCICommandQItem>& aQueue, CHCICommandQItem* aMark);
+	
+	inline CHCICommandQItem* ScanQueueByOpcode(TDblQue<CHCICommandQItem>& aQueue, THCIOpcode aOpcode);
+	inline CHCICommandQItem* ScanQueueByQId(TDblQue<CHCICommandQItem>& aQueue, TUint aCmdId);
+#ifdef _DEBUG
+	void LogQueue(TDblQue<CHCICommandQItem>& aQueue);
+#endif // _DEBUG
+
+	inline TBool CanAddCommands();
+	inline TBool CmdsQueued(TDblQue<CHCICommandQItem>* aQueue);
+	inline TBool AnyCmdsToSend();
+	inline TDblQue<CHCICommandQItem>* QueueSendingNext();
+	inline TBool NextCommandChanged(const TDblQue<CHCICommandQItem> &aQueue);
+
+	void DoReset();
+	void TryToSend();
+	TBool ProcessResendQueue();
+	TBool ProcessWorkaroundQueue();
+	TBool ProcessPriorityQueue();
+	void ProcessRegularQueue(TDblQue<CHCICommandQItem>& aQueue);
+		
+	void SetUpNextWorkaroundCmd(CHCICommandQItem* aPreviousCmd, const THCIEventBase* aPreviousCmdResult);
+	void UpdateStarvationTimer();
+
+	TUint DoAddCommandL(CHCICommandQItem& aQueItem,	TDblQue<CHCICommandQItem> &aQueue, TCmdQControllerStates aActiveState);
+	void DeleteCommand(CHCICommandQItem* &aItem);
+	TBool OkToSendCommand(CHCICommandQItem*& aCmdQItem, TUint aBypassBlocks);
+	void SendCommand(CHCICommandQItem& aCmdQItem);
+		
+	void ProcessMatchedEvent(const THCIEventBase& aEvent, CHCICommandQItem* aCmd, TBool aConcludesCmd);
+	void ProcessMatchedErrorEvent(const THCIEventBase& aEvent, CHCICommandQItem* aCmd, TBool aConcludesCmd, TBool aSendToQdp);
+	void ProcessUnmatchedEvent(const THCIEventBase& aEvent, TBool aSendToQdp);
+	void ProcessCommandCompletionTimeout(CHCICommandQItem* aCmd);
+	void ProcessEvent(const THCIEventBase& aEvent, TBool aSendToQdp);
+
+	// Static async CallBack methods.
+	static TInt AsyncCallBackForReset(TAny* aCmdQController);
+	static TInt AsyncCallBackForSend(TAny* aCmdQController);
+
+private:
+	TUint iMaxHciCommandTimeout;
+	TUint iQueueStarvationTimeout;
+
+	const static TUint KDefaultMaxHciCommandTimeout = 60000; // 60 seconds.
+	
+	MLinkMuxNotifier*				iLinkMuxer;
+	MHCICommandQueueClient*			iUnmatchedEventObserver;
+	MHCTLInterface*					iHctl;
+	MHardResetInitiator*			iHardResetInitiator;
+	MHCICommandAllocator*			iCommandAllocator;
+	MHCICmdQueueDecisionInterface*	iQdp;
+	MHCICmdQueueEventModifierInterface*	iQdpEventModifier;
+	
+	/**
+	This timer gets reset every time the next command to send changes.
+	If that command stays the same for too long, we probably have a deadlock of some sorts.
+	*/
+	CHCICmdQStarvationTimer*		iQStarvationTimer;
+	
+	/**
+	The queue of initialisation commands that have not yet been processed.
+	*/
+	TDblQue<CHCICommandQItem>		iInitCommandQ;
+	
+	/**
+	The queue of normal commands that have not yet been processed.
+	*/
+	TDblQue<CHCICommandQItem>		iNormalCommandQ;
+
+	/**
+	The queue of priority commands that have not yet been processed.
+	Note: it's not FIFO - the order of execution of commands is determined
+	dynamically.
+	*/
+	TDblQue<CHCICommandQItem>		iPriorityCommandQ;
+
+	/**
+	The queue of workarounds for commands on the other queues.
+	A command is moved here when time comes to execute it but QDP deems it to
+	be in need of workaround.
+	*/
+	TDblQue<CHCICommandQItem>		iWorkaroundCommandQ;
+
+	/**
+	The queue of commands that have been sent to the controller but have not
+	yet had a response.
+	*/
+	TDblQue<CHCICommandQItem>		iSentCommandQ;
+	
+	/**
+	The queue of commands that are waiting to be re-sent to the controller.
+	*/
+	TDblQue<CHCICommandQItem>		iResendCommandQ;
+
+
+	/**
+	Recorded tails of the queues for Reset processing.
+	*/
+	CHCICommandQItem*				iInitQPurgeMark;
+	CHCICommandQItem*				iNormalQPurgeMark;
+	CHCICommandQItem*				iPriorityQPurgeMark;
+	CHCICommandQItem*				iWorkaroundQPurgeMark;
+	
+	/**
+	Currently processing send command. This is maintained to ensure both that the 
+	correct command is processed by DoSend, and that there is only ever one outstanding 
+	call to MLinkMuxNotifier::TryToSend at any given time
+	*/
+	CHCICommandQItem*				iSendingCommand;
+
+	/**
+	Currently processing parent command. This is maintained to simplify priority
+	command processing while a workaround is in progress, and to ensure that 
+	the parent's iQdpData is available to the QDP until the workaround completes.
+	*/
+	CHCICommandQItem*				iParentCommand;
+	
+	/**
+	Number of HCI command packets the queue controller can send to the HCI. This
+	corresponds to the Num_HCI_Command_Packets event parameter. This is decremented
+	whenever a command is sent to the HCTL, and is updated on receipt of Command Status
+	and Command Complete (including NOP) events.
+	*/
+	TUint8							iCommandCredits;
+	
+	/**
+	The CHCICmdQController state variable
+	*/
+	TCmdQControllerStates			iCmdQControllerState;
+	
+	/**
+	This is a bit mask of the possible TQueueStateBits states
+	*/
+	TUint							iCommandQState;
+	
+	/**
+	The QDP that the Command Queue will use.
+	*/
+	CHCICmdQueueDecisionPlugin*		iQdpPlugin;
+
+	/**
+	A counter generating IDs to uniquely identify commands.
+	*/
+	TUint							iNextCommandQItemId;
+	
+	/**
+	HCI Utility library
+	*/
+	CHciUtil* iHciUtil;
+	
+	/**
+	Async Callbacks
+	*/
+	CAsyncCallBack* iAsyncCallBackForReset;
+	CAsyncCallBack* iAsyncCallBackForSend;
+
+	};
+
+#endif // HCICMDQCONTROLLER_H