|
1 // Copyright (c) 2002-2010 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0"" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // include/drivers/dma_v1.h |
|
15 // DMA Framework API v1 |
|
16 // |
|
17 // NB: DMA clients should never include this file directly, but only ever the |
|
18 // generic header file <drivers/dma.h>. |
|
19 // |
|
20 |
|
21 #ifndef __DMA_H__ |
|
22 #error "dma_v1.h must'n be included directly - use <drivers/dma.h> instead" |
|
23 #endif // #ifndef __DMA_H__ |
|
24 |
|
25 #ifndef __DMA_V1_H__ |
|
26 #define __DMA_V1_H__ |
|
27 |
|
28 #include <kernel/kern_priv.h> |
|
29 |
|
30 |
|
31 ////////////////////////////////////////////////////////////////////////////// |
|
32 // Debug Support - KDmaPanicCat is defined in each source file |
|
33 |
|
34 #define __DMA_ASSERTD(e) __ASSERT_DEBUG(e, Kern::Fault(KDmaPanicCat, __LINE__)) |
|
35 #define __DMA_ASSERTA(e) __ASSERT_ALWAYS(e, Kern::Fault(KDmaPanicCat, __LINE__)) |
|
36 #ifdef _DEBUG |
|
37 #define __DMA_CANT_HAPPEN() Kern::Fault(KDmaPanicCat, __LINE__) |
|
38 #define __DMA_DECLARE_INVARIANT public: void Invariant(); |
|
39 #define __DMA_INVARIANT() Invariant() |
|
40 #else |
|
41 #define __DMA_CANT_HAPPEN() |
|
42 #define __DMA_DECLARE_INVARIANT |
|
43 #define __DMA_INVARIANT() |
|
44 #endif |
|
45 |
|
46 |
|
47 ////////////////////////////////////////////////////////////////////////////// |
|
48 // INTERFACE EXPOSED TO DEVICE-DRIVERS |
|
49 ////////////////////////////////////////////////////////////////////////////// |
|
50 |
|
51 /** |
|
52 Bitmasks used for configuring a DMA request. |
|
53 |
|
54 In general, specify KDmaMemSrc|KDmaIncSrc (resp. KDmaMemDest|KDmaIncDest) if |
|
55 the source (resp. destination) is a memory buffer and clear |
|
56 KDmaMemSrc|KDmaIncSrc (resp. KDmaMemDest|KDmaIncDest) if the source |
|
57 (resp. destination) is a peripheral. |
|
58 |
|
59 If the location is given as a physical address (rather than a linear one) |
|
60 then also specify KDmaPhysAddrSrc and/or KDmaPhysAddrDest. |
|
61 |
|
62 The EKA1 "Fill Mode" can be implemented by omitting KDmaIncSrc. |
|
63 |
|
64 Some peripherals may require a post-increment address mode. |
|
65 |
|
66 @see DDmaRequest::Fragment |
|
67 @publishedPartner |
|
68 @released |
|
69 */ |
|
70 |
|
71 enum TDmaRequestFlags |
|
72 { |
|
73 /** Source is address of memory buffer */ |
|
74 KDmaMemSrc = 0x01, |
|
75 /** Destination is address of memory buffer */ |
|
76 KDmaMemDest = 0x02, |
|
77 /** Source address must be post-incremented during transfer */ |
|
78 KDmaIncSrc = 0x04, |
|
79 /** Destination address must be post-incremented during transfer */ |
|
80 KDmaIncDest = 0x08, |
|
81 /** Source address is a physical address (as opposed to a linear one) */ |
|
82 KDmaPhysAddrSrc = 0x10, |
|
83 /** Destination address is a physical address (as opposed to a linear one) */ |
|
84 KDmaPhysAddrDest = 0x20, |
|
85 /** Request a different max transfer size (for instance for test purposes) */ |
|
86 KDmaAltTransferLen = 0x40 |
|
87 }; |
|
88 |
|
89 |
|
90 ////////////////////////////////////////////////////////////////////////////// |
|
91 |
|
92 class TDmaChannel; |
|
93 struct SDmaDesHdr; |
|
94 |
|
95 /** A DMA request is a list of fragments small enough to be transferred in one go |
|
96 by the DMAC. |
|
97 |
|
98 In general, fragmentation is done in the framework by calling Fragment() but |
|
99 clients with special needs can allocate a blank descriptor list with |
|
100 ExpandDesList() and customise it to fit their needs. |
|
101 |
|
102 Clients should not set attributes directly, but should use the various functions |
|
103 instead. |
|
104 |
|
105 This class has not been designed to be called from several concurrent threads. |
|
106 Multithreaded clients must implement their own locking scheme (via DMutex). |
|
107 |
|
108 Fast mutexes are used internally to protect data structures accessed both |
|
109 by the client thread and the DFC thread. Therefore no fast mutex can be held |
|
110 when calling a request function. |
|
111 |
|
112 @publishedPartner |
|
113 @released |
|
114 */ |
|
115 class DDmaRequest : public DBase |
|
116 { |
|
117 friend class TDmaChannel; |
|
118 public: |
|
119 /** The outcome of the transfer */ |
|
120 enum TResult {EBadResult=0, EOk, EError}; |
|
121 /** The signature of the completion/failure callback function */ |
|
122 typedef void (*TCallback)(TResult, TAny*); |
|
123 public: |
|
124 |
|
125 /** |
|
126 Create a new transfer request. |
|
127 |
|
128 @param aChannel The channel this request is bound to. |
|
129 @param aCb Callback function called on transfer completion or failure (in channel |
|
130 DFC context). Can be NULL. |
|
131 @param aCbArg Argument passed to callback function. |
|
132 @param aMaxTransferSize Maximum fragment size. If not specified, defaults to the maximum size |
|
133 supported by the DMA controller for the type of transfer that is later scheduled. |
|
134 */ |
|
135 IMPORT_C DDmaRequest(TDmaChannel& aChannel, TCallback aCb=NULL, TAny* aCbArg=NULL, TInt aMaxTransferSize=0); |
|
136 |
|
137 |
|
138 /** |
|
139 Destructor. |
|
140 |
|
141 Assume the request is not being transferred or pending. |
|
142 */ |
|
143 IMPORT_C ~DDmaRequest(); |
|
144 |
|
145 |
|
146 /** |
|
147 Split request into a list of fragments small enough to be fed to the DMAC. |
|
148 |
|
149 The size of each fragment is smaller than or equal to the maximum transfer size |
|
150 supported by the DMAC. If the source and/or destination is memory, each |
|
151 fragment points to memory which is physically contiguous. |
|
152 |
|
153 The kind of transfer to perform is specified via a set of flags used by a PIL |
|
154 and a magic cookie passed to the PSL. If the source (resp. destination) is a |
|
155 peripheral, aSrc (resp. aDest) is treated as a magic cookie by the PIL and |
|
156 passed straight to the PSL. |
|
157 |
|
158 The request can be uninitialised or may have been fragmented previously. The |
|
159 previous configuration if any is lost whether or not the function succeeds. |
|
160 |
|
161 The client must ensure that any memory buffers involved in the transfer |
|
162 have been suitably prepared for DMA. For memory allocated on the kernel |
|
163 side or in a shared chunk this amounts to ensuring cache consistency |
|
164 before Queue() is called. However for memory that was allocated on the |
|
165 user side the client must also ensure that the memory is protected from |
|
166 both data paging and RAM defragmentation before Fragment() is called |
|
167 @see Kern::MapAndPinMemory(). Note however, that this function is only |
|
168 available if the flexible memory model (FMM) is in use. |
|
169 |
|
170 @param aSrc Source memory buffer linear address or peripheral magic cookie. |
|
171 @param aDest Destination memory buffer linear address or peripheral magic cookie. |
|
172 @param aCount Number of bytes to transfer. |
|
173 @param aFlags Bitmask characterising the transfer. |
|
174 @param aPslInfo Hardware-specific information passed to PSL. |
|
175 |
|
176 @return KErrNone if success. KErrArgument if aFlags and/or aPslInfo are invalid when finding |
|
177 the maximum transfer size. May also fail if running out of descriptors. |
|
178 |
|
179 @pre The request is not being transferred or pending. |
|
180 @pre The various parameters must be valid. The PIL or PSL will fault the |
|
181 kernel if not. |
|
182 |
|
183 @see TDmaRequestFlags |
|
184 */ |
|
185 IMPORT_C TInt Fragment(TUint32 aSrc, TUint32 aDest, TInt aCount, TUint aFlags, TUint32 aPslInfo); |
|
186 |
|
187 |
|
188 /** |
|
189 Transfer asynchronously this request. |
|
190 |
|
191 If this request's channel is idle, the request is transferred immediately. |
|
192 Otherwise, it is queued and transferred later. |
|
193 |
|
194 The client is responsible for ensuring cache consistency before and/or after the |
|
195 transfer if necessary. |
|
196 */ |
|
197 IMPORT_C void Queue(); |
|
198 |
|
199 |
|
200 /** |
|
201 Append new descriptor(s) to existing list. |
|
202 |
|
203 Clients needing to build a custom descriptor list should call this function to |
|
204 allocate the list and access the resulting list through iFirstHdr and iLastHdr. |
|
205 |
|
206 Clients should not change the value of iFirstHdr, iLastHdr and the iNext field |
|
207 of the descriptor headers to ensure descriptors can be deallocated. Clients |
|
208 are free to change hardware descriptors, including chaining, in whatever way |
|
209 suit them. |
|
210 |
|
211 Assume the request is not being transferred or pending. |
|
212 |
|
213 @param aCount Number of descriptors to append. |
|
214 |
|
215 @return KErrNone or KErrTooBig if not enough descriptors available. |
|
216 */ |
|
217 IMPORT_C TInt ExpandDesList(TInt aCount=1); |
|
218 |
|
219 |
|
220 /** |
|
221 Free resources associated with this request. |
|
222 |
|
223 Assume the request is not being transferred or pending. |
|
224 */ |
|
225 IMPORT_C void FreeDesList(); |
|
226 private: |
|
227 inline void OnDeque(); |
|
228 public: |
|
229 // WARNING: The following attributes are accessed both in client and DFC |
|
230 // context and so accesses must be protected with the channel lock. |
|
231 TDmaChannel& iChannel; /**< The channel this request is bound to */ |
|
232 volatile TCallback iCb; /**< Called on completion/failure (can be NULL) */ |
|
233 TAny* volatile iCbArg; /**< Callback argument */ |
|
234 TInt iDesCount; /**< The number of fragments in list */ |
|
235 SDmaDesHdr* iFirstHdr; /**< The first fragment in the list (or NULL) */ |
|
236 SDmaDesHdr* iLastHdr; /**< The last fragment in the list (or NULL) */ |
|
237 SDblQueLink iLink; /**< The link on channel queue of pending requests */ |
|
238 TBool iQueued; /**< Indicates whether request is pending or being transferred */ |
|
239 TInt iMaxTransferSize; /**< Defaults to DMA controller max. transfer size */ |
|
240 __DMA_DECLARE_INVARIANT |
|
241 }; |
|
242 |
|
243 |
|
244 ////////////////////////////////////////////////////////////////////////////// |
|
245 |
|
246 class TDmac; |
|
247 class DmaChannelMgr; |
|
248 |
|
249 /** DMA channel base class. |
|
250 |
|
251 This class has not been designed to be called from several concurrent |
|
252 client threads. Multithreaded clients must implement their own locking |
|
253 scheme (via DMutex). |
|
254 |
|
255 Fast mutexes are used internally to protect data structures accessed both |
|
256 by the client thread and the DFC one. Therefore no fast mutex can be held |
|
257 when calling a channel function. |
|
258 |
|
259 Must be allocated in BSS because it relies on being zeroed at |
|
260 creation-time. If the PSL really needs to allocate channels on the kernel |
|
261 heap, it must manually zero-initialises the instances. This can be |
|
262 achieved either by allocating raw memory and using placement new, or by |
|
263 wrapping channels into a DBase-derived wrapper. |
|
264 |
|
265 @publishedPartner |
|
266 @released |
|
267 */ |
|
268 class TDmaCancelInfo; |
|
269 class TDmaChannel |
|
270 { |
|
271 friend class DDmaRequest; |
|
272 friend class TDmac; |
|
273 friend class DmaChannelMgr; |
|
274 public: |
|
275 /** Information passed by client when opening channel */ |
|
276 struct SCreateInfo |
|
277 { |
|
278 /** Identifier used by PSL to select channel to open */ |
|
279 TUint32 iCookie; |
|
280 /** Number of descriptors this channel can use */ |
|
281 TInt iDesCount; |
|
282 /** DFC queue used to service DMA interrupts */ |
|
283 TDfcQue* iDfcQ; |
|
284 /** DFC priority */ |
|
285 TUint8 iDfcPriority; |
|
286 }; |
|
287 public: |
|
288 /** |
|
289 Opens the DMA channel. |
|
290 |
|
291 Channel selection is done by the hardware-specific layer using a cookie passed in |
|
292 via aInfo. |
|
293 |
|
294 The client should not delete the returned pointer as the framework owns |
|
295 channel objects. However, the client should explicitly close the channel when |
|
296 finished with it. |
|
297 |
|
298 @param aInfo Information passed by caller to select and configure channel. |
|
299 @param aChannel Point to open channel on successful return. NULL otherwise. |
|
300 |
|
301 @return KErrNone or standard error code. |
|
302 */ |
|
303 IMPORT_C static TInt Open(const SCreateInfo& aInfo, TDmaChannel*& aChannel); |
|
304 |
|
305 |
|
306 /** |
|
307 Closes a previously opened DMA channel. |
|
308 |
|
309 Assume the channel is idle and all requests have been deleted. |
|
310 */ |
|
311 IMPORT_C void Close(); |
|
312 |
|
313 |
|
314 /** |
|
315 Cancels the current request and all the pending ones. |
|
316 */ |
|
317 IMPORT_C void CancelAll(); |
|
318 inline TBool IsOpened() const; |
|
319 inline TBool IsQueueEmpty() const; |
|
320 inline TUint32 PslId() const; |
|
321 inline TInt FailNext(TInt aFragmentCount); |
|
322 inline TInt MissNextInterrupts(TInt aInterruptCount); |
|
323 inline TInt Extension(TInt aCmd, TAny* aArg); |
|
324 |
|
325 /** |
|
326 This is a function that allows the Platform Specific Layer (PSL) to extend the DMA API |
|
327 with new channel-independent operations. |
|
328 |
|
329 @param aCmd Command identifier. Negative values are reserved for Symbian use. |
|
330 @param aArg PSL-specific. |
|
331 |
|
332 @return KErrNotSupported if aCmd is not supported; a PSL specific value otherwise. |
|
333 */ |
|
334 IMPORT_C TInt StaticExtension(TInt aCmd, TAny* aArg); |
|
335 inline const TDmac* Controller() const; |
|
336 inline TInt MaxTransferSize(TUint aFlags, TUint32 aPslInfo); |
|
337 inline TUint MemAlignMask(TUint aFlags, TUint32 aPslInfo); |
|
338 protected: |
|
339 // Interface with state machines |
|
340 TDmaChannel(); |
|
341 virtual void DoQueue(DDmaRequest& aReq) = 0; |
|
342 virtual void DoCancelAll() = 0; |
|
343 virtual void DoUnlink(SDmaDesHdr& aHdr); |
|
344 virtual void DoDfc(DDmaRequest& aCurReq, SDmaDesHdr*& aCompletedHdr) = 0; |
|
345 /** |
|
346 This function allows the Platform Specific Layer (PSL) to control the |
|
347 power management of the channel or its controller by overriding the |
|
348 PIL's default implementation (which does nothing) and making |
|
349 appropriate use of the Power Resource Manager (PRM). |
|
350 |
|
351 The function gets called by the PIL whenever the channel's queued |
|
352 requests count has changed in a significant way, either before the |
|
353 channel's Transfer() method is invoked for a request on a previously |
|
354 empty request queue, or immediately after the request count has become |
|
355 zero because of request cancellation or completion. |
|
356 |
|
357 Depending on the current and previous observed values of |
|
358 iQueuedRequests, the PSL may power down or power up the channel. |
|
359 |
|
360 Note that iQueuedRequests gets accessed and changed by different |
|
361 threads, so the PSL needs to take the usual precautions when evaluating |
|
362 the variable's value. Also, due to the multithreaded framework |
|
363 architecture, there is no guarantee that the function calls always |
|
364 arrive at the PSL level in the strict chronological order of |
|
365 iQueuedRequests being incremented/decremented in the PIL, i.e. it might |
|
366 happen that the PSL finds iQueuedRequests to have the same value in two |
|
367 or more consecutive calls (that's why the previous observed value needs |
|
368 to be locally available and taken into account). It is however promised |
|
369 that before any actual transfer commences the PSL will find the request |
|
370 count to be greater than zero and that after the last request has |
|
371 finished it will be found to be zero. |
|
372 |
|
373 None of the internal DMA framework mutexes is being held by the PIL |
|
374 when calling this function. |
|
375 |
|
376 Here is an example implementation for a derived channel class: |
|
377 |
|
378 @code |
|
379 |
|
380 class TFooDmaChannel : public TDmaSgChannel |
|
381 { |
|
382 DMutex* iDmaMutex; |
|
383 TInt iPrevQueuedRequests; |
|
384 virtual void QueuedRequestCountChanged(); |
|
385 }; |
|
386 |
|
387 void TFooDmaChannel::QueuedRequestCountChanged() |
|
388 { |
|
389 Kern::MutexWait(*iDmaMutex); |
|
390 const TInt queued_now = __e32_atomic_load_acq32(&iQueuedRequests); |
|
391 if ((queued_now > 0) && (iPrevQueuedRequests == 0)) |
|
392 { |
|
393 IncreasePowerCount(); // Base port specific |
|
394 } |
|
395 else if ((queued_now == 0) && (iPrevQueuedRequests > 0)) |
|
396 { |
|
397 DecreasePowerCount(); // Base port specific |
|
398 } |
|
399 iPrevQueuedRequests = queued_now; |
|
400 Kern::MutexSignal(*iDmaMutex); |
|
401 } |
|
402 |
|
403 @endcode |
|
404 |
|
405 @see iQueuedRequests |
|
406 */ |
|
407 virtual void QueuedRequestCountChanged(); |
|
408 #if defined(__CPU_ARM) && !defined(__EABI__) |
|
409 inline virtual ~TDmaChannel() {} // kill really annoying warning |
|
410 #endif |
|
411 private: |
|
412 static void Dfc(TAny*); |
|
413 void DoDfc(); |
|
414 inline void Wait(); |
|
415 inline void Signal(); |
|
416 inline TBool Flash(); |
|
417 void ResetStateMachine(); |
|
418 protected: |
|
419 TDmac* iController; // DMAC this channel belongs to (NULL when closed) |
|
420 TUint32 iPslId; // unique identifier provided by PSL |
|
421 NFastMutex iLock; // for data accessed in both client & DFC context |
|
422 SDmaDesHdr* iCurHdr; // fragment being transferred or NULL |
|
423 SDmaDesHdr** iNullPtr; // Pointer to NULL pointer following last fragment |
|
424 TDfc iDfc; // transfer completion/failure DFC |
|
425 TInt iMaxDesCount; // maximum number of allocable descriptors |
|
426 TInt iAvailDesCount; // available number of descriptors |
|
427 volatile TUint32 iIsrDfc; // Interface between ISR and DFC: |
|
428 enum { KErrorFlagMask = 0x80000000 }; // bit 31 - error flag |
|
429 enum { KCancelFlagMask = 0x40000000 }; // bit 30 - cancel flag |
|
430 enum { KDfcCountMask = 0x3FFFFFFF }; // bits 0-29 - number of queued DFCs |
|
431 SDblQue iReqQ; // being/about to be transferred request queue |
|
432 TInt iReqCount; // number of requests attached to this channel |
|
433 TInt iQueuedRequests; // number of requests currently queued on this channel |
|
434 TBool iCallQueuedRequestFn; // call QueuedRequestCountChanged? (default: true) |
|
435 private: |
|
436 TDmaCancelInfo* iCancelInfo; |
|
437 __DMA_DECLARE_INVARIANT |
|
438 }; |
|
439 |
|
440 |
|
441 ////////////////////////////////////////////////////////////////////////////// |
|
442 // PIL-PSL INTERFACE |
|
443 ////////////////////////////////////////////////////////////////////////////// |
|
444 |
|
445 // Trace macros intended for use by the DMA PSL |
|
446 #define DMA_PRINTF(MSG) __KTRACE_OPT(KDMA, Kern::Printf((MSG))) |
|
447 #define DMA_PRINTF1(MSG, ARG1) __KTRACE_OPT(KDMA, Kern::Printf((MSG), (ARG1))) |
|
448 #define DMA_PRINTF2(MSG, ARG1, ARG2) __KTRACE_OPT(KDMA, Kern::Printf((MSG), (ARG1), (ARG2))) |
|
449 |
|
450 #define DMA_PSL_MESG "DMA PSL: " |
|
451 |
|
452 // General PSL tracing |
|
453 #define DMA_PSL_TRACE(MSG) DMA_PRINTF(DMA_PSL_MESG MSG) |
|
454 #define DMA_PSL_TRACE1(MSG, ARG1) DMA_PRINTF1(DMA_PSL_MESG MSG, (ARG1)) |
|
455 #define DMA_PSL_TRACE2(MSG, ARG1, ARG2) DMA_PRINTF2(DMA_PSL_MESG MSG, (ARG1), (ARG2)) |
|
456 |
|
457 |
|
458 #define DMA_PSL_CHAN_MESG DMA_PSL_MESG "ChanId %d: " |
|
459 #define DMA_PSL_CHAN_ARGS(CHAN) ((CHAN).PslId()) |
|
460 |
|
461 // For channel specific tracing (where CHAN is a TDmaChannel) |
|
462 #define DMA_PSL_CHAN_TRACE_STATIC(CHAN, MSG) DMA_PRINTF1(DMA_PSL_CHAN_MESG MSG, DMA_PSL_CHAN_ARGS(CHAN)) |
|
463 #define DMA_PSL_CHAN_TRACE_STATIC1(CHAN, MSG, ARG1) DMA_PRINTF2(DMA_PSL_CHAN_MESG MSG, DMA_PSL_CHAN_ARGS(CHAN), (ARG1)) |
|
464 |
|
465 // For channel specific tracing, for use within methods of TDmaChannel derived |
|
466 // class |
|
467 #define DMA_PSL_CHAN_TRACE(MSG) DMA_PSL_CHAN_TRACE_STATIC(*this, MSG) |
|
468 #define DMA_PSL_CHAN_TRACE1(MSG, ARG1) DMA_PSL_CHAN_TRACE_STATIC1(*this, MSG, (ARG1)) |
|
469 |
|
470 |
|
471 /** |
|
472 Generic DMA descriptor used if the DMAC does not have support for hardware |
|
473 descriptor. |
|
474 @see DDmaRequest::Fragment |
|
475 @publishedPartner |
|
476 @released |
|
477 */ |
|
478 |
|
479 struct SDmaPseudoDes |
|
480 { |
|
481 /** Source linear address or peripheral cookie */ |
|
482 TUint32 iSrc; |
|
483 /** Destination linear address or peripheral cookie */ |
|
484 TUint32 iDest; |
|
485 /** Number of bytes to transfer */ |
|
486 TInt iCount; |
|
487 /** @see TDmaRequestFlags */ |
|
488 TUint iFlags; |
|
489 /** PSL-specific information provided by client */ |
|
490 TUint32 iPslInfo; |
|
491 /** The same as TDmaChannel::SCreateInfo.iCookie */ |
|
492 TUint32 iCookie; |
|
493 }; |
|
494 |
|
495 |
|
496 /** |
|
497 Each hardware or pseudo descriptor is associated with a header. Headers are |
|
498 needed because hardware descriptors can not easily be extended to store |
|
499 additional information. |
|
500 @publishedPartner |
|
501 @released |
|
502 */ |
|
503 |
|
504 struct SDmaDesHdr |
|
505 { |
|
506 SDmaDesHdr* iNext; |
|
507 }; |
|
508 |
|
509 |
|
510 /** |
|
511 Interface used by PIL to open and close DMA channels. |
|
512 |
|
513 Must be implemented by PSL. |
|
514 @publishedPartner |
|
515 @released |
|
516 */ |
|
517 |
|
518 class DmaChannelMgr |
|
519 { |
|
520 public: |
|
521 /** Opens a channel using a client-provided identifier. |
|
522 This function must be implemented by the PSL. |
|
523 @param aOpenId Magic cookie passed by client |
|
524 This may identify the channel (if a static channel |
|
525 allocation scheme is used) or may indicate some |
|
526 properties which the channel must possess (if a dynamic |
|
527 channel allocation scheme is used). It may be set to |
|
528 zero always if dynamic allocation is used and all |
|
529 channels are equivalent. |
|
530 @return Pointer to channel if available, NULL otherwise. |
|
531 @pre The PIL calls this function with a global fast mutex held to |
|
532 avoid race conditions. |
|
533 @post If a non-NULL pointer is returned, the object pointed to has its |
|
534 iController and iPslId members set to valid states. |
|
535 iController should point to the controller handling that channel. |
|
536 iPslId should contain a value uniquely identifying the channel - |
|
537 it is used only for debug tracing by PIL. It can be given any |
|
538 convenient value by PSL (channel index, I/O port address, ...). |
|
539 */ |
|
540 static TDmaChannel* Open(TUint32 aOpenId); |
|
541 |
|
542 /** Performs platform-specific operations when a channel is closed. |
|
543 This function must be implemented by the PSL but the implementation can be |
|
544 a no-op. |
|
545 @param aChannel The channel to close |
|
546 @pre The PIL calls this function with a global fast mutex held to |
|
547 avoid race conditions. |
|
548 */ |
|
549 static void Close(TDmaChannel* aChannel); |
|
550 |
|
551 /** Function allowing PSL to extend DMA API with new channel-independent operations. |
|
552 This function must be implemented by the PSL. |
|
553 @param aCmd Command identifier. Negative values are reserved for Symbian use. |
|
554 @param aArg PSL-specific |
|
555 @return KErrNotSupported if aCmd is not supported. PSL-specific value otherwise. |
|
556 */ |
|
557 static TInt StaticExtension(TInt aCmd, TAny* aArg); |
|
558 |
|
559 static inline void Wait(); |
|
560 static inline void Signal(); |
|
561 private: |
|
562 static NFastMutex Lock; |
|
563 }; |
|
564 |
|
565 |
|
566 ////////////////////////////////////////////////////////////////////////////// |
|
567 |
|
568 /** |
|
569 Abstract base class representing a DMA controller. |
|
570 |
|
571 The class has two purposes. |
|
572 |
|
573 First, it is a container for channels, descriptors and descriptor headers. |
|
574 |
|
575 Second, it exposes a set of virtual functions implemented by |
|
576 the PSL (platform-specific layer). |
|
577 These functions are the main interfaces between |
|
578 the PIL (platform-independent layer) and PSL. |
|
579 |
|
580 Must be allocated in BSS because it relies on being zeroed at creation-time. |
|
581 |
|
582 @publishedPartner |
|
583 @released |
|
584 */ |
|
585 |
|
586 class TDmac |
|
587 { |
|
588 friend class DmaChannelMgr; |
|
589 // protected: VC++ complains when building PSL if following decl is protected |
|
590 public: |
|
591 /** Data required for creating a new instance */ |
|
592 struct SCreateInfo |
|
593 { |
|
594 /** Number of channels in controller */ |
|
595 TInt iChannelCount; |
|
596 /** Maximum number of descriptors (shared by all channels) */ |
|
597 TInt iDesCount; |
|
598 /** Bitmask. The only supported value is KCapsBitHwDes (hardware |
|
599 descriptors used). */ |
|
600 TUint32 iCaps; |
|
601 /** Size of individual descriptors. Use sizeof(SDmaPseudoDes) for |
|
602 single-buffer and double-buffer controllers. */ |
|
603 TInt iDesSize; |
|
604 /** Bitmask used when creating the hardware chunk storing the descriptor |
|
605 pool. Used only for hardware descriptors. The access part must be |
|
606 EMapAttrSupRw. If the chunk is cached and/or buffered, the PSL must |
|
607 flush the data cache and/or drain the write buffer in InitHwDes() |
|
608 and related functions. |
|
609 @see TMappingAttributes |
|
610 */ |
|
611 TUint iDesChunkAttribs; |
|
612 }; |
|
613 public: |
|
614 TInt Create(const SCreateInfo& aInfo); |
|
615 virtual ~TDmac(); |
|
616 TInt ReserveSetOfDes(TInt aCount); |
|
617 void ReleaseSetOfDes(TInt aCount); |
|
618 void InitDes(const SDmaDesHdr& aHdr, TUint32 aSrc, TUint32 aDest, TInt aCount, |
|
619 TUint aFlags, TUint32 aPslInfo, TUint32 aCookie); |
|
620 inline SDmaPseudoDes& HdrToDes(const SDmaDesHdr& aHdr) const; |
|
621 inline TAny* HdrToHwDes(const SDmaDesHdr& aHdr) const; |
|
622 inline TUint32 DesLinToPhys(TAny* aDes) const; |
|
623 inline void Wait(); |
|
624 inline void Signal(); |
|
625 protected: |
|
626 TDmac(const SCreateInfo& aInfo); |
|
627 |
|
628 public: |
|
629 /** |
|
630 Called by PIL when one fragment (single-buffer and double-buffer DMACs) or |
|
631 list of fragments (scatter/gather DMAC) is to be transferred. |
|
632 |
|
633 Called when initiating a new transfer and also, for double-buffer DMACs, for |
|
634 configuring the next fragment to transfer while the current one is |
|
635 ongoing. Must always be implemented by PSL. |
|
636 @param aChannel The channel to use |
|
637 @param aHdr Header associated with fragment to transfer |
|
638 */ |
|
639 virtual void Transfer(const TDmaChannel& aChannel, const SDmaDesHdr& aHdr) = 0; |
|
640 |
|
641 /** |
|
642 Called by PIL to suspend transfer on a given channel. |
|
643 |
|
644 The suspension must occur synchronously as the PSL assumes the channel |
|
645 is suspended after calling this function. Must always be implemented by PSL. |
|
646 @param aChannel The channel to suspend |
|
647 */ |
|
648 virtual void StopTransfer(const TDmaChannel& aChannel) = 0; |
|
649 |
|
650 /** |
|
651 Called by PIL to check whether a DMA channel is idle. |
|
652 @param aChannel The channel to test |
|
653 @return ETrue if channel idle, EFalse if transferring. |
|
654 */ |
|
655 virtual TBool IsIdle(const TDmaChannel& aChannel) = 0; |
|
656 |
|
657 /** |
|
658 Called by PIL to retrieve from the PSL the maximum transfer size based on the |
|
659 parameters passed. |
|
660 @param aChannel Channel to be used for the transfer |
|
661 @param aFlags Bitmask characterising transfer |
|
662 @param aPslInfo Cookie passed by client and used by PSL |
|
663 @return 0 if invalid argument(s), -1 if transfer size not limited, the maximum |
|
664 transfer size otherwise. |
|
665 */ |
|
666 virtual TInt MaxTransferSize(TDmaChannel& aChannel, TUint aFlags, TUint32 aPslInfo) = 0; |
|
667 |
|
668 /** |
|
669 Called by PIL to retrieve from the PSL the memory alignment mask based on the |
|
670 parameters passed. Some DMA controllers impose alignment constraints on the base |
|
671 address of memory buffers. This mask is AND'ed against memory addresses computed |
|
672 during fragmentation. |
|
673 @param aChannel Channel to be used for the transfer |
|
674 @param aFlags Bitmask characterising transfer |
|
675 @param aPslInfo Cookie passed by client and used by PSL |
|
676 @return A value representing the alignment mask (e.g. 3 if buffer must be 4-byte aligned) |
|
677 */ |
|
678 virtual TUint MemAlignMask(TDmaChannel& aChannel, TUint aFlags, TUint32 aPslInfo) = 0; |
|
679 |
|
680 /** |
|
681 Called by PIL during fragmentation to initialise a hardware descriptor. |
|
682 |
|
683 The PSL must assume the descriptor is the last in the chain and so set the |
|
684 interrupt bit and set the next descriptor field to an end of chain marker. |
|
685 Must be implemented by PSL if and only if the DMAC supports hardware |
|
686 descriptors. |
|
687 @param aHdr Header associated with hardware descriptor to initialise |
|
688 @param aSrc Transfer source |
|
689 @param aDest Transfer destination |
|
690 @param aCount Number of bytes to transfer (<= max. size supported by DMAC) |
|
691 @param aFlags Bitmask characterising transfer |
|
692 @param aPslInfo Cookie passed by client and used by PSL |
|
693 @param aCookie the channel selection cookie |
|
694 @see DDmaRequest::Fragment |
|
695 */ |
|
696 virtual void InitHwDes(const SDmaDesHdr& aHdr, TUint32 aSrc, TUint32 aDest, TInt aCount, |
|
697 TUint aFlags, TUint32 aPslInfo, TUint32 aCookie); |
|
698 |
|
699 /** |
|
700 Called by PIL, when fragmenting a request, to append a new hardware |
|
701 descriptor to an existing descriptor chain. |
|
702 |
|
703 Must clear the interrupt bit of the descriptor associated with aHdr. |
|
704 Must be implemented by PSL if and only if the DMAC supports hardware descriptors. |
|
705 @param aHdr Header associated with last fragment in chain |
|
706 @param aNextHdr Header associated with fragment to append |
|
707 */ |
|
708 virtual void ChainHwDes(const SDmaDesHdr& aHdr, const SDmaDesHdr& aNextHdr); |
|
709 |
|
710 /** |
|
711 Called by PIL when queuing a new request while the channel is running. |
|
712 |
|
713 Must append the first hardware descriptor of the new request to the last |
|
714 descriptor in the existing chain. Must be implemented by PSL if and only if |
|
715 the DMAC supports hardware descriptors. |
|
716 @param aChannel The channel where the transfer takes place |
|
717 @param aLastHdr Header associated with last hardware descriptor in chain |
|
718 @param aNewHdr Header associated with first hardware descriptor in new request |
|
719 */ |
|
720 virtual void AppendHwDes(const TDmaChannel& aChannel, const SDmaDesHdr& aLastHdr, |
|
721 const SDmaDesHdr& aNewHdr); |
|
722 |
|
723 /** |
|
724 Called by PIL when completing or cancelling a request to cause the PSL to unlink |
|
725 the last item in the h/w descriptor chain from a subsequent chain that it was |
|
726 possibly linked to. Must be implemented by the PSL if and only if the DMAC supports |
|
727 hardware descriptors. |
|
728 |
|
729 @param aChannel The channel where the request (and thus the descriptor) was queued |
|
730 @param aHdr Header associated with last h/w descriptor in completed/cancelled chain |
|
731 */ |
|
732 virtual void UnlinkHwDes(const TDmaChannel& aChannel, SDmaDesHdr& aHdr); |
|
733 |
|
734 /** |
|
735 Called by test harness to force an error when the next fragment is |
|
736 transferred. |
|
737 |
|
738 Must be implemented by the PSL only if possible. |
|
739 @param aChannel The channel where the error is to occur. |
|
740 @return KErrNone if implemented. The default PIL implementation returns |
|
741 KErrNotSupported and the test harness knows how to deal with that. |
|
742 */ |
|
743 virtual TInt FailNext(const TDmaChannel& aChannel); |
|
744 |
|
745 /** |
|
746 Called by test harness to force the DMA controller to miss one or |
|
747 more interrupts. |
|
748 |
|
749 Must be implemented by the PSL only if possible. |
|
750 @param aChannel The channel where the error is to occur |
|
751 @param aInterruptCount The number of interrupt to miss. |
|
752 @return KErrNone if implemented. The default PIL implementation returns |
|
753 KErrNotSupported and the test harness knows how to deal with that. |
|
754 */ |
|
755 virtual TInt MissNextInterrupts(const TDmaChannel& aChannel, TInt aInterruptCount); |
|
756 |
|
757 /** Function allowing platform-specific layer to extend channel API with |
|
758 new channel-specific operations. |
|
759 @param aChannel Channel to operate on |
|
760 @param aCmd Command identifier. Negative values are reserved for Symbian use. |
|
761 @param aArg PSL-specific |
|
762 @return KErrNotSupported if aCmd is not supported. PSL-specific value otherwise. |
|
763 @see TDmaChannel::Extension |
|
764 */ |
|
765 virtual TInt Extension(TDmaChannel& aChannel, TInt aCmd, TAny* aArg); |
|
766 |
|
767 protected: |
|
768 static void HandleIsr(TDmaChannel& aChannel, TBool aIsComplete); |
|
769 private: |
|
770 TInt AllocDesPool(TUint aAttribs); |
|
771 void FreeDesPool(); |
|
772 private: |
|
773 NFastMutex iLock; // protect descriptor reservation and allocation |
|
774 const TInt iMaxDesCount; // initial number of descriptors and headers |
|
775 TInt iAvailDesCount; // current available number of descriptors and headers |
|
776 SDmaDesHdr* iHdrPool; // descriptor header dynamic array |
|
777 #ifndef __WINS__ |
|
778 DPlatChunkHw* iHwDesChunk; // chunk for hardware descriptor pool |
|
779 #endif |
|
780 TAny* iDesPool; // hardware or pseudo descriptor dynamic array |
|
781 const TInt iDesSize; // descriptor size in bytes |
|
782 public: |
|
783 const TUint iCaps; /*< what is supported by DMA controller */ |
|
784 enum {KCapsBitHwDes = 1}; /*< hardware descriptors supported */ |
|
785 SDmaDesHdr* iFreeHdr; /*< head of unallocated descriptors linked list */ |
|
786 #ifdef _DEBUG |
|
787 TBool IsValidHdr(const SDmaDesHdr* aHdr); |
|
788 #endif |
|
789 __DMA_DECLARE_INVARIANT |
|
790 }; |
|
791 |
|
792 |
|
793 ////////////////////////////////////////////////////////////////////////////// |
|
794 |
|
795 /** |
|
796 Single-buffer DMA channel. |
|
797 |
|
798 Can be instantiated or further derived by PSL. Not |
|
799 intended to be instantiated by client device drivers. |
|
800 @publishedPartner |
|
801 @released |
|
802 */ |
|
803 |
|
804 class TDmaSbChannel : public TDmaChannel |
|
805 { |
|
806 private: |
|
807 virtual void DoQueue(DDmaRequest& aReq); |
|
808 virtual void DoCancelAll(); |
|
809 virtual void DoDfc(DDmaRequest& aCurReq, SDmaDesHdr*& aCompletedHdr); |
|
810 private: |
|
811 TBool iTransferring; |
|
812 }; |
|
813 |
|
814 |
|
815 /** |
|
816 Double-buffer DMA channel. |
|
817 |
|
818 Can be instantiated or further derived by PSL. Not |
|
819 intended to be instantiated by client device drivers. |
|
820 @publishedPartner |
|
821 @released |
|
822 */ |
|
823 |
|
824 class TDmaDbChannel : public TDmaChannel |
|
825 { |
|
826 private: |
|
827 virtual void DoQueue(DDmaRequest& aReq); |
|
828 virtual void DoCancelAll(); |
|
829 virtual void DoDfc(DDmaRequest& aCurReq, SDmaDesHdr*& aCompletedHdr); |
|
830 private: |
|
831 enum { EIdle = 0, ETransferring, ETransferringLast } iState; |
|
832 }; |
|
833 |
|
834 |
|
835 /** |
|
836 Scatter-gather DMA channel. |
|
837 |
|
838 Can be instantiated or further derived by PSL. |
|
839 Not intended to be instantiated by client device drivers. |
|
840 @publishedPartner |
|
841 @released |
|
842 */ |
|
843 |
|
844 class TDmaSgChannel : public TDmaChannel |
|
845 { |
|
846 private: |
|
847 virtual void DoQueue(DDmaRequest& aReq); |
|
848 virtual void DoCancelAll(); |
|
849 virtual void DoUnlink(SDmaDesHdr& aHdr); |
|
850 virtual void DoDfc(DDmaRequest& aCurReq, SDmaDesHdr*& aCompletedHdr); |
|
851 private: |
|
852 TBool iTransferring; |
|
853 }; |
|
854 |
|
855 |
|
856 ////////////////////////////////////////////////////////////////////////////// |
|
857 // INTERFACE WITH TEST HARNESS |
|
858 ////////////////////////////////////////////////////////////////////////////// |
|
859 |
|
860 /** |
|
861 Set of information used by test harness. |
|
862 @publishedPartner |
|
863 @released |
|
864 */ |
|
865 |
|
866 struct TDmaTestInfo |
|
867 { |
|
868 /** Maximum transfer size in bytes for all channels (ie. the minimum of all channels' maximum size)*/ |
|
869 TInt iMaxTransferSize; |
|
870 /** 3->Memory buffers must be 4-byte aligned, 7->8-byte aligned, ... */ |
|
871 TUint iMemAlignMask; |
|
872 /** Cookie to pass to DDmaRequest::Fragment for memory-memory transfer*/ |
|
873 TUint32 iMemMemPslInfo; |
|
874 /** Number of test single-buffer channels */ |
|
875 TInt iMaxSbChannels; |
|
876 /** Pointer to array containing single-buffer test channel ids */ |
|
877 TUint32* iSbChannels; |
|
878 /** Number of test double-buffer channels */ |
|
879 TInt iMaxDbChannels; |
|
880 /** Pointer to array containing double-buffer test channel ids */ |
|
881 TUint32* iDbChannels; |
|
882 /** Number of test scatter-gather channels */ |
|
883 TInt iMaxSgChannels; |
|
884 /** Pointer to array containing scatter-gather test channel ids */ |
|
885 TUint32* iSgChannels; |
|
886 }; |
|
887 |
|
888 |
|
889 /** |
|
890 Provides access to test information structure stored in the PSL. |
|
891 |
|
892 Must be implemented by the PSL. |
|
893 @publishedPartner |
|
894 @released |
|
895 */ |
|
896 |
|
897 IMPORT_C const TDmaTestInfo& DmaTestInfo(); |
|
898 |
|
899 |
|
900 ////////////////////////////////////////////////////////////////////////////// |
|
901 |
|
902 #include <drivers/dma_v1.inl> |
|
903 |
|
904 #endif |