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