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