diff -r 578be2adaf3e -r 307f4279f433 Adaptation/GUID-FA026EBD-7867-4A2C-9FA4-EC70A5243526.dita --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Adaptation/GUID-FA026EBD-7867-4A2C-9FA4-EC70A5243526.dita Fri Oct 15 14:32:18 2010 +0100 @@ -0,0 +1,193 @@ + + + + + +Transferring Data with DMAExplains how to transfer data using DMA. +

The client +will typically have a DMA class containing pointers to:

    +
  • a TDmaChannel object,

  • +
  • one or more DDmaRequest objects,

  • +
  • a TDfcQueue object, and

  • +
  • a callback which returns when the data transfer completes (successfully +or not).

  • +

These classes are discussed in DMA OS Interface.

+

To allow +a client application to receive or transmit a block of data by DMA +you set up a transfer between a source or a destination buffer and +a source or a destination peripheral. Data is copied between the client +process and kernel memory by IPC form data transfer between two buffers. +When a shared chunk or a shared data buffer is used, copying of data +to kernel memory is required. At the level of implementation there +is no difference between receiving and transmitting. Client drivers +using DMA sometimes have separate receive and transmit functions defined +from the driver point of view.

You transfer data in this sequence:

    +
  • Open a DMA channel.

  • +
  • Fragment the data if necessary.

  • +
  • Queue the transfer.

  • +
  • Close the channel.

  • +

Example code in this tutorial is taken from the DMemoryCardDma class which transfers data to and from MMC cards by DMA.

+ +Initialize +the client object by allocating a request and setting buffers. + //pre-allocate space for DDmaRequest + iDMARequest = (DDmaRequest*)Kern::AllocZ(sizeof(DDmaRequest)); + + // Check Buffer Length + if (aBuffLen) + { + // Set Buffer Start + iBufferStart = (TUint32)aBuffer; + // Set Buffer End + iBufferEnd = iBufferStart + aBuffLen; + // Set DFC Queue + iDfcQue = aDfcQue; + + return KErrNone; + } + +The client object is now initialized. + +Open the +channel by creating an SCreateInfo object and initializing +it as follows: + +Set the iCookie member to a platform specific channel identifier. + +Set the iDesCount member to the number of descriptors the channel +can use. +

The value should be set to a value based on number of simultaneous +transfers, transfer length and type of memory used for transfer. If +you select too high a value, no descriptors will be left for other +clients, and if you select too low a value the transfer may fail.

+
+Set the iDfcPriority member to the DFC priority. + +Set the iDfcQueue member to the client class queue: it will be +used to service transfer completion events. + +
+ // Open and configure the channel + if (!iDMAChannelOpen) + { + if (!iDfcQue) + return KErrGeneral; + + TDmaChannel::SCreateInfo info; + + info.iCookie = (TUint32)iChannelId; + info.iDesCount = 3; + info.iDfcPriority = 3; + info.iDfcQ = iDfcQue; + + +The channel is now initialized. +
+Pass the SCreateInfo object and a channel ID to the Open() function of TDmaChannel to open a DMA channel. +

This is not necessary if the channel is already open due +to a previous transfer request by the same application on the same +channel.

+ // Open DMA Channel + r = TDmaChannel::Open(info, iDMAChannel); + + if (r) + return r; + + // Set DMA Channel Open + iDMAChannelOpen = ETrue; + +At what point you open the channel depends on platform configuration. +If a channel is dedicated to a particular function such as USB, it +is typically opened by the channel constructor method and closed by +the destructor as there is never any contention for it. In other cases, +you must open the channel at the start of a transfer and close it +at the end. +The channel is now open. +
+Call the Fragment() function of DDmaRequest to +fragment the data. +:If your buffers are cacheable you must flush them before a +write transfer and both before and after a read transfer, using these +functions of the Cache class:
    +
  • Cache::SyncMemoryBeforeDmaWrite() (takes source +buffer as argument)

  • +
  • Cache::SyncMemoryBeforeDmaRead() (takes destination +buffer as argument)

  • +
  • Cache::SyncMemoryAfterDmaRead() (takes destination +buffer as argument)

  • +
+The data is now fragmented. +
+Call the Queue() function of TDmaChannel to queue +the data on the channel. + TInt retval = KErrNone; + + if (iDMABusy) + { + if (iDMAChannel) + { + iDMAChannel->CancelAll(); + } + } + + iDMABusy = ETrue; + + iDMAReadStarted = EFalse; + + if (!iDMAChannelOpen) + { + retval = Open(); + } + + if (iDMAChannelOpen) + { + iDMADestination = aDest; + iDMACount = aCount; + + // aDest cached, ivalidate RAM + Cache::SyncMemoryBeforeDmaRead(aDest, aCount); + retval = iDMARequest ? iDMARequest->Fragment(EDmaSDMMC, aDest, aCount, KDmaMemDest | KDmaIncDest, 0 /* no HW Flags*/) : KErrGeneral; + + if (retval) + { + return retval; + } + + iDMAReadStarted = ETrue; + if (iDMARequest) + { + iDMARequest->Queue(); + } + } + +

The data is now queued on the channel.

+
+Close the +channel. First check whether the channel is busy and take appropriate +action if it is (in the following example code you cancel all pending +transfers). Then call the Close() function of TDmaChannel. + if (iDMAChannelOpen && iDMAChannel && !iDMABusy) + { + iDMAChannel->CancelAll(); + iDMARequestsCount = 0; // All DMA requests have been now canceled! + + if (iDMARequest) + { + //just call destructor explicitly and the object can reused + iDMARequest->~DDmaRequest(); + } + ... + iDMAChannel->Close(); + +

The channel is now closed.

+
+
+

You have +now conducted a data transfer by DMA.

+
\ No newline at end of file