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