sl@0: // Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies).
sl@0: // All rights reserved.
sl@0: // This component and the accompanying materials are made available
sl@0: // under the terms of "Eclipse Public License v1.0""
sl@0: // which accompanies this distribution, and is available
sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html".
sl@0: //
sl@0: // Initial Contributors:
sl@0: // Nokia Corporation - initial contribution.
sl@0: //
sl@0: // Contributors:
sl@0: //
sl@0: // Description:
sl@0: // include/drivers/dma_v1.h
sl@0: // DMA Framework API v1
sl@0: //
sl@0: // NB: DMA clients should never include this file directly, but only ever the
sl@0: // generic header file <drivers/dma.h>.
sl@0: //
sl@0: 
sl@0: #ifndef __DMA_H__
sl@0: #error "dma_v1.h must'n be included directly - use <drivers/dma.h> instead"
sl@0: #endif	// #ifndef __DMA_H__
sl@0: 
sl@0: #ifndef __DMA_V1_H__
sl@0: #define __DMA_V1_H__
sl@0: 
sl@0: #include <kernel/kern_priv.h>
sl@0: 
sl@0: 
sl@0: //////////////////////////////////////////////////////////////////////////////
sl@0: // Debug Support - KDmaPanicCat is defined in each source file
sl@0: 
sl@0: #define __DMA_ASSERTD(e) __ASSERT_DEBUG(e, Kern::Fault(KDmaPanicCat, __LINE__))
sl@0: #define __DMA_ASSERTA(e) __ASSERT_ALWAYS(e, Kern::Fault(KDmaPanicCat, __LINE__))
sl@0: #ifdef _DEBUG
sl@0: #define __DMA_CANT_HAPPEN() Kern::Fault(KDmaPanicCat, __LINE__)
sl@0: #define __DMA_DECLARE_INVARIANT public: void Invariant();
sl@0: #define __DMA_INVARIANT() Invariant()
sl@0: #else
sl@0: #define __DMA_CANT_HAPPEN()
sl@0: #define __DMA_DECLARE_INVARIANT
sl@0: #define __DMA_INVARIANT()
sl@0: #endif
sl@0: 
sl@0: 
sl@0: //////////////////////////////////////////////////////////////////////////////
sl@0: // INTERFACE EXPOSED TO DEVICE-DRIVERS
sl@0: //////////////////////////////////////////////////////////////////////////////
sl@0: 
sl@0: /**
sl@0: Bitmasks used for configuring a DMA request.
sl@0: 
sl@0: In general, specify KDmaMemSrc|KDmaIncSrc (resp. KDmaMemDest|KDmaIncDest) if
sl@0: the source (resp. destination) is a memory buffer and clear
sl@0: KDmaMemSrc|KDmaIncSrc (resp. KDmaMemDest|KDmaIncDest) if the source
sl@0: (resp. destination) is a peripheral.
sl@0: 
sl@0: If the location is given as a physical address (rather than a linear one)
sl@0: then also specify KDmaPhysAddrSrc and/or KDmaPhysAddrDest.
sl@0: 
sl@0: The EKA1 "Fill Mode" can be implemented by omitting KDmaIncSrc.
sl@0: 
sl@0: Some peripherals may require a post-increment address mode.
sl@0: 
sl@0: @see DDmaRequest::Fragment
sl@0: @publishedPartner
sl@0: @released
sl@0: */
sl@0: 
sl@0: enum TDmaRequestFlags
sl@0: 	{
sl@0: 	/** Source is address of memory buffer */
sl@0: 	KDmaMemSrc       = 0x01,
sl@0: 	/** Destination is address of memory buffer */
sl@0: 	KDmaMemDest      = 0x02,
sl@0: 	/** Source address must be post-incremented during transfer */
sl@0: 	KDmaIncSrc       = 0x04,
sl@0: 	/** Destination address must be post-incremented during transfer */
sl@0: 	KDmaIncDest      = 0x08,
sl@0: 	/** Source address is a physical address (as opposed to a linear one) */
sl@0: 	KDmaPhysAddrSrc  = 0x10,
sl@0: 	/** Destination address is a physical address (as opposed to a linear one) */
sl@0: 	KDmaPhysAddrDest = 0x20,
sl@0: 	/** Request a different max transfer size (for instance for test purposes) */
sl@0: 	KDmaAltTransferLen = 0x40
sl@0: 	};
sl@0: 
sl@0: 
sl@0: //////////////////////////////////////////////////////////////////////////////
sl@0: 
sl@0: class TDmaChannel;
sl@0: struct SDmaDesHdr;
sl@0: 
sl@0: /** A DMA request is a list of fragments small enough to be transferred in one go
sl@0: 	by the DMAC.
sl@0: 
sl@0: 	In general, fragmentation is done in the framework by calling Fragment() but
sl@0: 	clients with special needs can allocate a blank descriptor list with
sl@0: 	ExpandDesList() and customise it to fit their needs.
sl@0: 
sl@0: 	Clients should not set attributes directly, but should use the various functions
sl@0: 	instead.
sl@0: 
sl@0: 	This class has not been designed to be called from several concurrent threads.
sl@0: 	Multithreaded clients must implement their own locking scheme (via DMutex).
sl@0: 
sl@0: 	Fast mutexes are used internally to protect data structures accessed both
sl@0: 	by the client thread and the DFC thread.  Therefore no fast mutex can be held
sl@0: 	when calling a request function.
sl@0: 
sl@0: 	@publishedPartner
sl@0: 	@released
sl@0:  */
sl@0: class DDmaRequest : public DBase
sl@0: 	{
sl@0: 	friend class TDmaChannel;
sl@0: public:
sl@0: 	/** The outcome of the transfer */
sl@0: 	enum TResult {EBadResult=0, EOk, EError};
sl@0: 	/** The signature of the completion/failure callback function */
sl@0: 	typedef void (*TCallback)(TResult, TAny*);
sl@0: public:
sl@0:    
sl@0:     /**
sl@0:     Create a new transfer request. 
sl@0: 
sl@0:     @param aChannel The channel this request is bound to.
sl@0:     @param aCb      Callback function called on transfer completion or failure (in channel
sl@0:                     DFC context).  Can be NULL.
sl@0:     @param aCbArg   Argument passed to callback function.
sl@0:     @param aMaxTransferSize Maximum fragment size.  If not specified, defaults to the maximum size
sl@0:            supported by the DMA controller for the type of transfer that is later scheduled.
sl@0:     */
sl@0: 	IMPORT_C DDmaRequest(TDmaChannel& aChannel, TCallback aCb=NULL, TAny* aCbArg=NULL, TInt aMaxTransferSize=0);
sl@0: 	
sl@0: 	
sl@0: 	/**
sl@0:     Destructor.
sl@0: 
sl@0:     Assume the request is not being transferred or pending.
sl@0:     */
sl@0: 	IMPORT_C ~DDmaRequest();
sl@0: 	
sl@0: 	
sl@0: 	/**
sl@0:     Split request into a list of fragments small enough to be fed to the DMAC.
sl@0: 
sl@0:     The size of each fragment is smaller than or equal to the maximum transfer size
sl@0:     supported by the DMAC.  If the source and/or destination is memory, each
sl@0:     fragment points to memory which is physically contiguous.
sl@0: 
sl@0:     The kind of transfer to perform is specified via a set of flags used by a PIL
sl@0:     and a magic cookie passed to the PSL.  If the source (resp. destination) is a
sl@0:     peripheral, aSrc (resp. aDest) is treated as a magic cookie by the PIL and
sl@0:     passed straight to the PSL.
sl@0: 
sl@0:     The request can be uninitialised or may have been fragmented previously.  The
sl@0:     previous configuration if any is lost whether or not the function succeeds.
sl@0: 
sl@0:     @param aSrc     Source memory buffer linear address or peripheral magic cookie.
sl@0:     @param aDest    Destination memory buffer linear address or peripheral magic cookie.
sl@0:     @param aCount   Number of bytes to transfer.
sl@0:     @param aFlags   Bitmask characterising the transfer.
sl@0:     @param aPslInfo Hardware-specific information passed to PSL.
sl@0: 
sl@0:     @return KErrNone if success. KErrArgument if aFlags and/or aPslInfo are invalid when finding
sl@0:     the maximum transfer size. May also fail if running out of descriptors.
sl@0: 
sl@0:     @pre The request is not being transferred or pending.
sl@0:     @pre The various parameters must be valid.  The PIL or PSL will fault the
sl@0:     kernel if not.
sl@0: 
sl@0:     @see TDmaRequestFlags
sl@0:     */
sl@0: 	IMPORT_C TInt Fragment(TUint32 aSrc, TUint32 aDest, TInt aCount, TUint aFlags, TUint32 aPslInfo);
sl@0: 	
sl@0: 	
sl@0: 	/**
sl@0:     Transfer asynchronously this request.
sl@0: 
sl@0:     If this request's channel is idle, the request is transferred immediately.
sl@0:     Otherwise, it is queued and transferred later.
sl@0: 
sl@0:     The client is responsible for ensuring cache consistency before and/or after the
sl@0:     transfer if necessary.
sl@0:     */
sl@0: 	IMPORT_C void Queue();
sl@0: 	
sl@0: 
sl@0:     /**
sl@0:     Append new descriptor(s) to existing list.
sl@0: 
sl@0:     Clients needing to build a custom descriptor list should call this function to
sl@0:     allocate the list and access the resulting list through iFirstHdr and iLastHdr.
sl@0: 
sl@0:     Clients should not change the value of iFirstHdr, iLastHdr and the iNext field
sl@0:     of the descriptor headers to ensure descriptors can be deallocated. Clients
sl@0:     are free to change hardware descriptors, including chaining, in whatever way
sl@0:     suit them.
sl@0: 
sl@0:     Assume the request is not being transferred or pending.
sl@0: 
sl@0:     @param aCount Number of descriptors to append.
sl@0: 
sl@0:     @return KErrNone or KErrTooBig if not enough descriptors available.
sl@0:     */
sl@0: 	IMPORT_C TInt ExpandDesList(TInt aCount=1);
sl@0: 	
sl@0: 	
sl@0: 	/**
sl@0:     Free resources associated with this request.
sl@0: 
sl@0:     Assume the request is not being transferred or pending.
sl@0:     */
sl@0: 	IMPORT_C void FreeDesList();
sl@0: private:
sl@0: 	inline void OnDeque();
sl@0: public:
sl@0: 	// WARNING: The following attributes are accessed both in client and DFC
sl@0: 	// context and so accesses must be protected with the channel lock.
sl@0: 	TDmaChannel& iChannel;		/**< The channel this request is bound to */
sl@0: 	volatile TCallback iCb;		/**< Called on completion/failure (can be NULL) */
sl@0: 	TAny* volatile iCbArg;		/**< Callback argument */
sl@0: 	TInt iDesCount;				/**< The number of fragments in list */
sl@0: 	SDmaDesHdr* iFirstHdr;		/**< The first fragment in the list (or NULL) */
sl@0: 	SDmaDesHdr* iLastHdr;		/**< The last fragment in the list (or NULL) */
sl@0: 	SDblQueLink iLink;			/**< The link on channel queue of pending requests */
sl@0: 	TBool iQueued;				/**< Indicates whether request is pending or being transferred */
sl@0: 	TInt iMaxTransferSize;		/**< Defaults to DMA controller max. transfer size */
sl@0: 	__DMA_DECLARE_INVARIANT
sl@0: 	};
sl@0: 
sl@0: 
sl@0: //////////////////////////////////////////////////////////////////////////////
sl@0: 
sl@0: class TDmac;
sl@0: class DmaChannelMgr;
sl@0: 
sl@0: /** DMA channel base class.
sl@0: 
sl@0: 	This class has not been designed to be called from several concurrent
sl@0: 	client threads.  Multithreaded clients must implement their own locking
sl@0: 	scheme (via DMutex).
sl@0: 
sl@0: 	Fast mutexes are used internally to protect data structures accessed both
sl@0: 	by the client thread and the DFC one.  Therefore no fast mutex can be held
sl@0: 	when calling a channel function.
sl@0: 
sl@0: 	Must be allocated in BSS because it relies on being zeroed at
sl@0: 	creation-time.  If the PSL really needs to allocate channels on the kernel
sl@0: 	heap, it must manually zero-initialises the instances.  This can be
sl@0: 	achieved either by allocating raw memory and using placement new, or by
sl@0: 	wrapping channels into a DBase-derived wrapper.
sl@0: 
sl@0: 	@publishedPartner
sl@0: 	@released
sl@0:  */
sl@0: class TDmaCancelInfo;
sl@0: class TDmaChannel
sl@0: 	{
sl@0: 	friend class DDmaRequest;
sl@0: 	friend class TDmac;
sl@0: 	friend class DmaChannelMgr;
sl@0: public:
sl@0: 	/**  Information passed by client when opening channel */
sl@0: 	struct SCreateInfo
sl@0: 		{
sl@0: 		/** Identifier used by PSL to select channel to open */
sl@0: 		TUint32 iCookie;
sl@0: 		/** Number of descriptors this channel can use */
sl@0: 		TInt iDesCount;
sl@0: 		/** DFC queue used to service DMA interrupts.  The DFC thread
sl@0: 			priority must be higher than any client thread priority to
sl@0: 			avoid a situation where a transfer completes while being
sl@0: 			cancelled and another transfer is started before the DFC
sl@0: 			thread gets a chance to run.  This would lead to a stray
sl@0: 			DFC.
sl@0: 		*/
sl@0: 		TDfcQue* iDfcQ;
sl@0: 		/** DFC priority */
sl@0: 		TUint8 iDfcPriority;
sl@0: 		};
sl@0: public:
sl@0:     /**
sl@0:  	Opens the DMA channel.
sl@0: 
sl@0:  	Channel selection is done by the hardware-specific layer using a cookie passed in
sl@0:  	via aInfo.
sl@0: 
sl@0:  	The client should not delete the returned pointer as the framework owns
sl@0:  	channel objects.  However, the client should explicitly close the channel when
sl@0:  	finished with it.
sl@0: 
sl@0: 	@param aInfo    Information passed by caller to select and configure channel.
sl@0:  	@param aChannel Point to open channel on successful return.  NULL otherwise.
sl@0: 
sl@0:  	@return KErrNone or standard error code.
sl@0:  	*/
sl@0: 	IMPORT_C static TInt Open(const SCreateInfo& aInfo, TDmaChannel*& aChannel);
sl@0: 	
sl@0: 	
sl@0: 	/**
sl@0:  	Closes a previously opened DMA channel.
sl@0: 
sl@0:  	Assume the channel is idle and all requests have been deleted.
sl@0:  	*/
sl@0: 	IMPORT_C void Close();
sl@0: 	
sl@0: 	
sl@0: 	/**
sl@0:  	Cancels the current request and all the pending ones.
sl@0:  	*/
sl@0: 	IMPORT_C void CancelAll();
sl@0: 	inline TBool IsOpened() const;
sl@0: 	inline TBool IsQueueEmpty() const;
sl@0: 	inline TUint32 PslId() const;
sl@0: 	inline TInt FailNext(TInt aFragmentCount);
sl@0: 	inline TInt MissNextInterrupts(TInt aInterruptCount);
sl@0: 	inline TInt Extension(TInt aCmd, TAny* aArg);
sl@0: 	
sl@0: 	/**
sl@0: 	This is a function that allows the Platform Specific Layer (PSL) to extend the DMA API
sl@0: 	with new channel-independent operations.
sl@0: 
sl@0: 	@param aCmd Command identifier.  Negative values are reserved for Symbian use.
sl@0: 	@param aArg PSL-specific.
sl@0: 	
sl@0: 	@return KErrNotSupported if aCmd is not supported; a  PSL specific value otherwise.
sl@0:  	*/
sl@0: 	IMPORT_C TInt StaticExtension(TInt aCmd, TAny* aArg);
sl@0: 	inline const TDmac* Controller() const;
sl@0: 	inline TInt MaxTransferSize(TUint aFlags, TUint32 aPslInfo);
sl@0: 	inline TUint MemAlignMask(TUint aFlags, TUint32 aPslInfo);
sl@0: protected:
sl@0: 	// Interface with state machines
sl@0: 	TDmaChannel();
sl@0: 	virtual void DoQueue(DDmaRequest& aReq) = 0;
sl@0: 	virtual void DoCancelAll() = 0;
sl@0: 	virtual void DoUnlink(SDmaDesHdr& aHdr);
sl@0: 	virtual void DoDfc(DDmaRequest& aCurReq, SDmaDesHdr*& aCompletedHdr) = 0;
sl@0: 	/**
sl@0: 	   This function allows the Platform Specific Layer (PSL) to control the
sl@0: 	   power management of the channel or its controller by overriding the
sl@0: 	   PIL's default implementation (which does nothing) and making appropriate
sl@0: 	   use of the Power Resource Manager (PRM).
sl@0: 
sl@0: 	   The function gets called by the PIL whenever the channel's queued
sl@0: 	   requests count has changed in a significant way, either before the
sl@0: 	   channel's Transfer() method is invoked for a request on a previously
sl@0: 	   empty request queue, or immediately after the request count has become
sl@0: 	   zero because of request cancellation or completion.
sl@0: 
sl@0: 	   Depending on the current value of iQueuedRequests, the PSL may power
sl@0: 	   down or power up the channel. Note that iQueuedRequests gets accessed
sl@0: 	   and changed by different threads, so the PSL needs to take the usual
sl@0: 	   precautions when evaluating the variable's value.
sl@0: 
sl@0: 	   None of the internal DMA framework mutexes is being held by the PIL when
sl@0: 	   calling this function.
sl@0: 
sl@0: 	   @see iQueuedRequests
sl@0: 	 */
sl@0: 	virtual void QueuedRequestCountChanged();
sl@0: #if defined(__CPU_ARM) && !defined(__EABI__)
sl@0: 	inline virtual ~TDmaChannel() {}	// kill really annoying warning
sl@0: #endif
sl@0: private:
sl@0: 	static void Dfc(TAny*);
sl@0: 	void DoDfc();
sl@0: 	inline void Wait();
sl@0: 	inline void Signal();
sl@0: 	inline TBool Flash();
sl@0: 	void ResetStateMachine();
sl@0: protected:
sl@0: 	TDmac* iController;										// DMAC this channel belongs to (NULL when closed)
sl@0: 	TUint32 iPslId;											// unique identifier provided by PSL
sl@0: 	NFastMutex iLock;										// for data accessed in both client & DFC context
sl@0: 	SDmaDesHdr* iCurHdr;									// fragment being transferred or NULL
sl@0: 	SDmaDesHdr** iNullPtr;									// Pointer to NULL pointer following last fragment
sl@0: 	TDfc iDfc;												// transfer completion/failure DFC
sl@0: 	TInt iMaxDesCount;										// maximum number of allocable descriptors
sl@0: 	TInt iAvailDesCount;									// available number of descriptors
sl@0: 	volatile TUint32 iIsrDfc;								// Interface between ISR and DFC:
sl@0: 	enum { KErrorFlagMask = 0x80000000 };					// bit 31 - error flag
sl@0: 	enum { KCancelFlagMask = 0x40000000 };					// bit 30 - cancel flag
sl@0: 	enum { KDfcCountMask = 0x3FFFFFFF };					// bits 0-29 - number of queued DFCs
sl@0: 	SDblQue iReqQ;											// being/about to be transferred request queue
sl@0: 	TInt iReqCount;											// number of requests attached to this channel
sl@0: 	TInt iQueuedRequests; 									// number of requests currently queued on this channel
sl@0: private:
sl@0: 	TDmaCancelInfo* iCancelInfo;
sl@0: 	__DMA_DECLARE_INVARIANT
sl@0: 	};
sl@0: 
sl@0: 
sl@0: //////////////////////////////////////////////////////////////////////////////
sl@0: // PIL-PSL INTERFACE
sl@0: //////////////////////////////////////////////////////////////////////////////
sl@0: 
sl@0: /**
sl@0: Generic DMA descriptor used if the DMAC does not have support for hardware
sl@0: descriptor.
sl@0: @see DDmaRequest::Fragment
sl@0: @publishedPartner
sl@0: @released
sl@0: */
sl@0: 
sl@0: struct SDmaPseudoDes
sl@0: 	{
sl@0: 	/** Source linear address or peripheral cookie */
sl@0: 	TUint32 iSrc;
sl@0: 	/** Destination linear address or peripheral cookie */
sl@0: 	TUint32 iDest;
sl@0: 	/** Number of bytes to transfer */
sl@0: 	TInt iCount;
sl@0: 	/** @see TDmaRequestFlags */
sl@0: 	TUint iFlags;
sl@0: 	/** PSL-specific information provided by client */
sl@0: 	TUint32 iPslInfo;
sl@0: 	/** The same as TDmaChannel::SCreateInfo.iCookie */
sl@0: 	TUint32 iCookie;
sl@0: 	};
sl@0: 
sl@0: 
sl@0: /**
sl@0: Each hardware or pseudo descriptor is associated with a header.  Headers are
sl@0: needed because hardware descriptors can not easily be extended to store
sl@0: additional information.
sl@0: @publishedPartner
sl@0: @released
sl@0: */
sl@0: 
sl@0: struct SDmaDesHdr
sl@0: 	{
sl@0: 	SDmaDesHdr* iNext;
sl@0: 	};
sl@0: 
sl@0: 
sl@0: /**
sl@0: Interface used by PIL to open and close DMA channels.
sl@0: 
sl@0: Must be implemented by PSL.
sl@0: @publishedPartner
sl@0: @released
sl@0: */
sl@0: 
sl@0: class DmaChannelMgr
sl@0: 	{
sl@0: public:
sl@0: 	/** Opens a channel using a client-provided identifier.
sl@0: 		This function must be implemented by the PSL.
sl@0: 		@param	aOpenId Magic cookie passed by client
sl@0: 				This may identify the channel (if a static channel
sl@0: 				allocation scheme is used) or may indicate some
sl@0: 				properties which the channel must possess (if a dynamic
sl@0: 				channel allocation scheme is used). It may be set to
sl@0: 				zero always if dynamic allocation is used and all
sl@0: 				channels are equivalent.
sl@0: 		@return	Pointer to channel if available, NULL otherwise.
sl@0: 		@pre	The PIL calls this function with a global fast mutex held to
sl@0: 				avoid race conditions.
sl@0: 		@post	If a non-NULL pointer is returned, the object pointed to has its
sl@0: 				iController and iPslId members set to valid states.
sl@0: 				iController should point to the controller handling that channel.
sl@0: 				iPslId should contain a value uniquely identifying the channel -
sl@0: 				it is used only for debug tracing by PIL. It can be given any
sl@0: 				convenient value by PSL	(channel index, I/O port address, ...).
sl@0: 	*/
sl@0: 	static TDmaChannel* Open(TUint32 aOpenId);
sl@0: 
sl@0: 	/** Performs platform-specific operations when a channel is closed.
sl@0: 		This function must be implemented by the PSL but the implementation can be
sl@0: 		a no-op.
sl@0: 		@param aChannel The channel to close
sl@0: 		@pre The PIL calls this function with a global fast mutex held to
sl@0: 			avoid race conditions.
sl@0: 	*/
sl@0: 	static void Close(TDmaChannel* aChannel);
sl@0: 
sl@0: 	/** Function allowing PSL to extend DMA API with new channel-independent operations.
sl@0: 		This function must be implemented by the PSL.
sl@0: 		@param aCmd Command identifier.  Negative values are reserved for Symbian use.
sl@0: 		@param aArg PSL-specific
sl@0: 		@return KErrNotSupported if aCmd is not supported.  PSL-specific value otherwise.
sl@0: 	 */
sl@0: 	static TInt StaticExtension(TInt aCmd, TAny* aArg);
sl@0: 
sl@0: 	static inline void Wait();
sl@0: 	static inline void Signal();
sl@0: private:
sl@0: 	static NFastMutex Lock;
sl@0: 	};
sl@0: 
sl@0: 
sl@0: //////////////////////////////////////////////////////////////////////////////
sl@0: 
sl@0: /**
sl@0:  Abstract base class representing a DMA controller.
sl@0: 
sl@0:  The class has two purposes.
sl@0: 
sl@0:  First, it is a container for channels, descriptors and descriptor headers.
sl@0: 
sl@0:  Second, it exposes a set of virtual functions implemented by
sl@0:  the PSL (platform-specific layer).
sl@0:  These functions are the main interfaces between
sl@0:  the PIL (platform-independent layer) and PSL.
sl@0: 
sl@0:  Must be allocated in BSS because it relies on being zeroed at creation-time.
sl@0: 
sl@0:  @publishedPartner
sl@0:  @released
sl@0:  */
sl@0: 
sl@0: class TDmac
sl@0: 	{
sl@0: 	friend class DmaChannelMgr;
sl@0: // protected: VC++ complains when building PSL if following decl is protected
sl@0: public:
sl@0: 	/** Data required for creating a new instance */
sl@0: 	struct SCreateInfo
sl@0: 		{
sl@0: 		/** Number of channels in controller */
sl@0: 		TInt iChannelCount;
sl@0:         /** Maximum number of descriptors (shared by all channels) */
sl@0: 		TInt iDesCount;
sl@0: 		/** Bitmask.  The only supported value is KCapsBitHwDes (hardware
sl@0: 			descriptors used). */
sl@0: 		TUint32 iCaps;
sl@0: 		/** Size of individual descriptors.  Use sizeof(SDmaPseudoDes) for
sl@0: 		 	single-buffer and double-buffer controllers. */
sl@0: 		TInt iDesSize;
sl@0: 		/** Bitmask used when creating the hardware chunk storing the descriptor
sl@0: 			pool. Used only for hardware descriptors. The access part must be
sl@0: 			EMapAttrSupRw.  If the chunk is cached and/or buffered, the PSL must
sl@0: 			flush the data cache and/or drain the write buffer in InitHwDes()
sl@0: 			and related functions.
sl@0: 		 	@see TMappingAttributes
sl@0: 		 */
sl@0: 		TUint iDesChunkAttribs;
sl@0: 		};
sl@0: public:
sl@0: 	TInt Create(const SCreateInfo& aInfo);
sl@0: 	virtual ~TDmac();
sl@0: 	TInt ReserveSetOfDes(TInt aCount);
sl@0: 	void ReleaseSetOfDes(TInt aCount);
sl@0: 	void InitDes(const SDmaDesHdr& aHdr, TUint32 aSrc, TUint32 aDest, TInt aCount,
sl@0: 				 TUint aFlags, TUint32 aPslInfo, TUint32 aCookie);
sl@0: 	inline SDmaPseudoDes& HdrToDes(const SDmaDesHdr& aHdr) const;
sl@0: 	inline TAny* HdrToHwDes(const SDmaDesHdr& aHdr) const;
sl@0: 	inline TUint32 DesLinToPhys(TAny* aDes) const;
sl@0: 	inline void Wait();
sl@0: 	inline void Signal();
sl@0: protected:
sl@0: 	TDmac(const SCreateInfo& aInfo);
sl@0: 
sl@0: public:
sl@0: 	/**
sl@0: 	Called by PIL when one fragment (single-buffer and double-buffer DMACs) or
sl@0: 	list of fragments (scatter/gather DMAC) is to be transferred.
sl@0: 
sl@0: 	Called when	initiating a new transfer and also, for double-buffer DMACs, for
sl@0: 	configuring the next fragment to transfer while the current one is
sl@0: 	ongoing. Must always be implemented by PSL.
sl@0: 	@param aChannel The channel to use
sl@0: 	@param aHdr Header associated with fragment to transfer
sl@0: 	*/
sl@0: 	virtual void Transfer(const TDmaChannel& aChannel, const SDmaDesHdr& aHdr) = 0;
sl@0: 
sl@0: 	/**
sl@0:     Called by PIL to suspend transfer on a given channel.
sl@0: 
sl@0:     The suspension must occur synchronously as the PSL assumes the channel
sl@0:     is suspended after calling this function. Must always be implemented by PSL.
sl@0: 	@param aChannel The channel to suspend
sl@0: 	*/
sl@0: 	virtual void StopTransfer(const TDmaChannel& aChannel) = 0;
sl@0: 
sl@0: 	/**
sl@0: 	Called by PIL to check whether a DMA channel is idle.
sl@0: 	@param aChannel The channel to test
sl@0: 	@return ETrue if channel idle, EFalse if transferring.
sl@0: 	 */
sl@0: 	virtual TBool IsIdle(const TDmaChannel& aChannel) = 0;
sl@0: 
sl@0: 	/**
sl@0: 	Called by PIL to retrieve from the PSL the maximum transfer size based on the
sl@0: 	parameters passed.
sl@0: 	@param aChannel Channel to be used for the transfer
sl@0: 	@param aFlags Bitmask characterising transfer
sl@0: 	@param aPslInfo Cookie passed by client and used by PSL
sl@0: 	@return 0 if invalid argument(s), -1 if transfer size not limited, the maximum
sl@0: 	transfer size otherwise.
sl@0: 	*/
sl@0: 	virtual TInt MaxTransferSize(TDmaChannel& aChannel, TUint aFlags, TUint32 aPslInfo) = 0;
sl@0: 
sl@0: 	/**
sl@0: 	Called by PIL to retrieve from the PSL the memory alignment mask based on the
sl@0: 	parameters passed. Some DMA controllers impose alignment constraints on the base
sl@0: 	address of memory buffers. This mask is AND'ed against memory addresses computed
sl@0: 	during fragmentation.
sl@0: 	@param aChannel Channel to be used for the transfer
sl@0: 	@param aFlags Bitmask characterising transfer
sl@0: 	@param aPslInfo Cookie passed by client and used by PSL
sl@0: 	@return A value representing the alignment mask (e.g. 3 if buffer must be 4-byte aligned)
sl@0: 	*/
sl@0: 	virtual TUint MemAlignMask(TDmaChannel& aChannel, TUint aFlags, TUint32 aPslInfo) = 0;
sl@0: 
sl@0: 	/**
sl@0:     Called by PIL during fragmentation to initialise a hardware descriptor.
sl@0: 
sl@0:     The PSL must assume the descriptor is the last in the chain and so set the
sl@0: 	interrupt bit and set the next descriptor field to an end of chain marker.
sl@0: 	Must be implemented by PSL if and only if the DMAC supports hardware
sl@0: 	descriptors.
sl@0: 	@param aHdr Header associated with hardware descriptor to initialise
sl@0: 	@param aSrc Transfer source
sl@0: 	@param aDest Transfer destination
sl@0: 	@param aCount Number of bytes to transfer (<= max. size supported by DMAC)
sl@0: 	@param aFlags Bitmask characterising transfer
sl@0: 	@param aPslInfo Cookie passed by client and used by PSL
sl@0: 	@param aCookie the channel selection cookie
sl@0: 	@see DDmaRequest::Fragment
sl@0: 	*/
sl@0: 	virtual void InitHwDes(const SDmaDesHdr& aHdr, TUint32 aSrc, TUint32 aDest, TInt aCount,
sl@0: 						   TUint aFlags, TUint32 aPslInfo, TUint32 aCookie);
sl@0: 
sl@0: 	/**
sl@0: 	Called by PIL, when fragmenting a request, to append a new hardware
sl@0: 	descriptor to an existing descriptor chain.
sl@0: 
sl@0: 	Must clear the interrupt bit of	the descriptor associated with aHdr.
sl@0: 	Must be implemented by PSL if and only if the DMAC supports hardware descriptors.
sl@0: 	@param aHdr Header associated with last fragment in chain
sl@0: 	@param aNextHdr Header associated with fragment to append
sl@0: 	*/
sl@0: 	virtual void ChainHwDes(const SDmaDesHdr& aHdr, const SDmaDesHdr& aNextHdr);
sl@0: 
sl@0: 	/**
sl@0: 	Called by PIL when queuing a new request while the channel is running.
sl@0: 
sl@0: 	Must append the first hardware descriptor of the new request to the last
sl@0: 	descriptor in the existing chain. Must be implemented by PSL if and only if
sl@0: 	the DMAC supports hardware descriptors.
sl@0: 	@param aChannel The channel where the transfer takes place
sl@0: 	@param aLastHdr Header associated with last hardware descriptor in chain
sl@0: 	@param aNewHdr Header associated with first hardware descriptor in new request
sl@0: 	*/
sl@0: 	virtual void AppendHwDes(const TDmaChannel& aChannel, const SDmaDesHdr& aLastHdr,
sl@0: 							 const SDmaDesHdr& aNewHdr);
sl@0: 
sl@0: 	/**
sl@0: 	Called by PIL when completing or cancelling a request to cause the PSL to unlink
sl@0: 	the last item in the h/w descriptor chain from a subsequent chain that it was
sl@0: 	possibly linked to. Must be implemented by the PSL if and only if the DMAC supports
sl@0: 	hardware descriptors.
sl@0: 
sl@0: 	@param aChannel The channel where the request (and thus the descriptor) was queued
sl@0: 	@param aHdr Header associated with last h/w descriptor in completed/cancelled chain
sl@0: 	*/
sl@0: 	virtual void UnlinkHwDes(const TDmaChannel& aChannel, SDmaDesHdr& aHdr);
sl@0: 
sl@0: 	/**
sl@0: 	Called by test harness to force an error when the next fragment is
sl@0: 	transferred.
sl@0: 
sl@0: 	Must be implemented by the PSL only if possible.
sl@0: 	@param aChannel The channel where the error is to occur.
sl@0: 	@return KErrNone if implemented.  The default PIL implementation returns
sl@0: 	KErrNotSupported and the test harness knows how to deal with that.
sl@0: 	*/
sl@0: 	virtual TInt FailNext(const TDmaChannel& aChannel);
sl@0: 
sl@0: 	/**
sl@0: 	Called by test harness to force the DMA controller to miss one or
sl@0: 	more interrupts.
sl@0: 
sl@0: 	Must be implemented by the PSL only if possible.
sl@0: 	@param aChannel The channel where the error is to occur
sl@0: 	@param aInterruptCount The number of interrupt to miss.
sl@0: 	@return KErrNone if implemented.  The default PIL implementation returns
sl@0: 	KErrNotSupported and the test harness knows how to deal with that.
sl@0: 	*/
sl@0: 	virtual TInt MissNextInterrupts(const TDmaChannel& aChannel, TInt aInterruptCount);
sl@0: 
sl@0: 	/** Function allowing platform-specific layer to extend channel API with
sl@0: 		new channel-specific operations.
sl@0: 		@param aChannel Channel to operate on
sl@0: 		@param aCmd Command identifier.  Negative values are reserved for Symbian use.
sl@0: 		@param aArg PSL-specific
sl@0: 		@return KErrNotSupported if aCmd is not supported.  PSL-specific value otherwise.
sl@0: 		@see TDmaChannel::Extension
sl@0: 	*/
sl@0: 	virtual TInt Extension(TDmaChannel& aChannel, TInt aCmd, TAny* aArg);
sl@0: 
sl@0: protected:
sl@0: 	static void HandleIsr(TDmaChannel& aChannel, TBool aIsComplete);
sl@0: private:
sl@0: 	TInt AllocDesPool(TUint aAttribs);
sl@0: 	void FreeDesPool();
sl@0: private:
sl@0: 	NFastMutex iLock;			 // protect descriptor reservation and allocation
sl@0: 	const TInt iMaxDesCount;	 // initial number of descriptors and headers
sl@0: 	TInt iAvailDesCount;		 // current available number of descriptors and headers
sl@0: 	SDmaDesHdr* iHdrPool;		 // descriptor header dynamic array
sl@0: #ifndef __WINS__
sl@0: 	DPlatChunkHw* iHwDesChunk;	 // chunk for hardware descriptor pool
sl@0: #endif
sl@0: 	TAny* iDesPool;				 // hardware or pseudo descriptor dynamic array
sl@0: 	const TInt iDesSize;		 // descriptor size in bytes
sl@0: public:
sl@0: 	const TUint iCaps;  		 /*< what is supported by DMA controller */
sl@0: 	enum {KCapsBitHwDes = 1};	 /*< hardware descriptors supported */
sl@0: 	SDmaDesHdr* iFreeHdr;		 /*< head of unallocated descriptors linked list */
sl@0: #ifdef _DEBUG
sl@0: 	TBool IsValidHdr(const SDmaDesHdr* aHdr);
sl@0: #endif
sl@0: 	__DMA_DECLARE_INVARIANT
sl@0: 	};
sl@0: 
sl@0: 
sl@0: //////////////////////////////////////////////////////////////////////////////
sl@0: 
sl@0: /**
sl@0: Single-buffer DMA channel.
sl@0: 
sl@0: Can be instantiated or further derived by PSL.  Not
sl@0: intended to be instantiated by client device drivers.
sl@0: @publishedPartner
sl@0: @released
sl@0: */
sl@0: 
sl@0: class TDmaSbChannel : public TDmaChannel
sl@0: 	{
sl@0: private:
sl@0: 	virtual void DoQueue(DDmaRequest& aReq);
sl@0: 	virtual void DoCancelAll();
sl@0: 	virtual void DoDfc(DDmaRequest& aCurReq, SDmaDesHdr*& aCompletedHdr);
sl@0: private:
sl@0: 	TBool iTransferring;
sl@0: 	};
sl@0: 
sl@0: 
sl@0: /**
sl@0: Double-buffer DMA channel.
sl@0: 
sl@0: Can be instantiated or further derived by PSL.  Not
sl@0: intended to be instantiated by client device drivers.
sl@0: @publishedPartner
sl@0: @released
sl@0: */
sl@0: 
sl@0: class TDmaDbChannel : public TDmaChannel
sl@0: 	{
sl@0: private:
sl@0: 	virtual void DoQueue(DDmaRequest& aReq);
sl@0: 	virtual void DoCancelAll();
sl@0: 	virtual void DoDfc(DDmaRequest& aCurReq, SDmaDesHdr*& aCompletedHdr);
sl@0: private:
sl@0: 	enum { EIdle = 0, ETransferring, ETransferringLast } iState;
sl@0: 	};
sl@0: 
sl@0: 
sl@0: /**
sl@0: Scatter-gather DMA channel.
sl@0: 
sl@0: Can be instantiated or further derived by PSL.
sl@0: Not intended to be instantiated by client device drivers.
sl@0: @publishedPartner
sl@0: @released
sl@0: */
sl@0: 
sl@0: class TDmaSgChannel : public TDmaChannel
sl@0: 	{
sl@0: private:
sl@0: 	virtual void DoQueue(DDmaRequest& aReq);
sl@0: 	virtual void DoCancelAll();
sl@0: 	virtual void DoUnlink(SDmaDesHdr& aHdr);
sl@0: 	virtual void DoDfc(DDmaRequest& aCurReq, SDmaDesHdr*& aCompletedHdr);
sl@0: private:
sl@0: 	TBool iTransferring;
sl@0: 	};
sl@0: 
sl@0: 
sl@0: //////////////////////////////////////////////////////////////////////////////
sl@0: // INTERFACE WITH TEST HARNESS
sl@0: //////////////////////////////////////////////////////////////////////////////
sl@0: 
sl@0: /**
sl@0: Set of information used by test harness.
sl@0: @publishedPartner
sl@0: @released
sl@0: */
sl@0: 
sl@0: struct TDmaTestInfo
sl@0: 	{
sl@0: 	/** Maximum transfer size in bytes for all channels (ie. the minimum of all channels' maximum size)*/
sl@0: 	TInt iMaxTransferSize;
sl@0: 	/** 3->Memory buffers must be 4-byte aligned, 7->8-byte aligned, ... */
sl@0: 	TUint iMemAlignMask;
sl@0: 	/** Cookie to pass to DDmaRequest::Fragment for memory-memory transfer*/
sl@0: 	TUint32 iMemMemPslInfo;
sl@0: 	/** Number of test single-buffer channels */
sl@0: 	TInt iMaxSbChannels;
sl@0: 	/** Pointer to array containing single-buffer test channel ids */
sl@0: 	TUint32* iSbChannels;
sl@0: 	/** Number of test double-buffer channels */
sl@0: 	TInt iMaxDbChannels;
sl@0: 	/** Pointer to array containing double-buffer test channel ids */
sl@0: 	TUint32* iDbChannels;
sl@0: 	/** Number of test scatter-gather channels */
sl@0: 	TInt iMaxSgChannels;
sl@0: 	/** Pointer to array containing scatter-gather test channel ids */
sl@0: 	TUint32* iSgChannels;
sl@0: 	};
sl@0: 
sl@0: 
sl@0: /**
sl@0: Provides access to test information structure stored in the PSL.
sl@0: 
sl@0: Must be implemented by the PSL.
sl@0: @publishedPartner
sl@0: @released
sl@0: */
sl@0: 
sl@0: IMPORT_C const TDmaTestInfo& DmaTestInfo();
sl@0: 
sl@0: 
sl@0: //////////////////////////////////////////////////////////////////////////////
sl@0: 
sl@0: #include <drivers/dma_v1.inl>
sl@0: 
sl@0: #endif