Addition of the PDK content and example code for Documentation_content according to Feature bug 1607 and bug 1608
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) 2007-2010 Nokia Corporation and/or its subsidiary(-ies) All rights reserved. -->
<!-- This component and the accompanying materials are made available under the terms of the License
"Eclipse Public License v1.0" which accompanies this distribution,
and is available at the URL "http://www.eclipse.org/legal/epl-v10.html". -->
<!-- Initial Contributors:
Nokia Corporation - initial contribution.
Contributors:
-->
<!DOCTYPE concept
PUBLIC "-//OASIS//DTD DITA Concept//EN" "concept.dtd">
<concept id="GUID-A4C19890-2380-5498-A5F8-3B6D95CEFEF4" xml:lang="en"><title>PSL
Implementation</title><shortdesc>Describes how to create the PSL implementation.</shortdesc><prolog><metadata><keywords/></metadata></prolog><conbody>
<p>To create a port of the DMA Framework, you must create an implementation
of the PSL for the DMA controller hardware.</p>
<section id="GUID-8664FA8F-E53A-5DBA-999F-A0B800C1F54B"><title>Define and
implement PSL-specific channel classes</title> <p>The choice of class to be
used for implementing DMA channels is governed by the mode of operation to
be supported: </p> <ul>
<li id="GUID-C254C7CA-D010-5885-811D-05BEAF068EDD"><p>single-buffer channels
should use <xref href="GUID-A8B4AD1B-770C-363E-A0DE-C78A9786CBDC.dita"><apiname>TDmaSbChannel</apiname></xref> </p> </li>
<li id="GUID-91DDE5AD-16E5-5FCE-86E9-80069F2CCDDB"><p>double-buffer channels
should use <xref href="GUID-FD76AF08-6250-3054-8A06-16343E385B23.dita"><apiname>TDmaDbChannel</apiname></xref> </p> </li>
<li id="GUID-994E863D-2C32-5AE3-931A-B852855883CA"><p>scatter-gather channels
should use <xref href="GUID-2D4CFBB1-8D64-3CF5-B6F0-B24D16D5BAD4.dita"><apiname>TDmaSgChannel</apiname></xref> </p> </li>
</ul> <p>These three classes are defined in <filepath>dma.h</filepath>. </p> <p>In
addition, the PSL may need to store channel-specific data. One way of achieving
this is by deriving a PSL-specific class from one of the generic ones. For
example, the template scatter-gather implementation defines a <codeph>TTemplateSgChannel</codeph> class
derived from <codeph>TDmaSgChannel</codeph> for storing data when appending
new descriptors to a running channel: </p> <codeblock id="GUID-C4807321-01A3-5893-800A-6E22B8B5E131" xml:space="preserve">class TTemplateSgChannel : public TDmaSgChannel
{
public:
TDmaDesc* iTmpDes;
TPhysAddr iTmpDesPhysAddr;
};
</codeblock> </section>
<section id="GUID-0991CFB7-B37E-57B9-BC1F-4A69FEDAF5A5"><title>Define and
implement the controller classes</title> <p>A controller can either be a physical
controller, or a logical one depending on which <xref href="GUID-97F97D7B-D5A3-5471-A359-77B805ED198C.dita">DMA
mode you intend to support</xref>. </p> <p>The PSL (platform-specific layer)
must define one concrete controller class per supported controller. A controller
class is derived from <xref href="GUID-25398075-927B-36E4-B953-16785EC4086C.dita"><apiname>TDmac</apiname></xref>. The concrete class must implement
all the pure virtual functions defined in <codeph>TDmac</codeph>, and, optionally
the functions used to manipulate hardware descriptors. </p> <p>Taking the
template port as an example, the class is defined as: </p> <codeblock id="GUID-1C275111-6597-5616-822A-9EABF40BFE77" xml:space="preserve">class TTemplateDmac : public TDmac
{
public:
TTemplateDmac();
TInt Create();
private:
// from TDmac (PIL pure virtual)
virtual void Transfer(const TDmaChannel& aChannel, const SDmaDesHdr& aHdr);
virtual void StopTransfer(const TDmaChannel& aChannel);
virtual TBool IsIdle(const TDmaChannel& aChannel);
virtual TInt MaxTransferSize(TDmaChannel& aChannel, TUint aFlags, TUint32 aPslInfo);
virtual TUint MemAlignMask(TDmaChannel& aChannel, TUint aFlags, TUint32 aPslInfo);
// from TDmac (PIL virtual)
virtual void InitHwDes(const SDmaDesHdr& aHdr, TUint32 aSrc, TUint32 aDest, TInt aCount,
TUint aFlags, TUint32 aPslInfo, TUint32 aCookie);
virtual void ChainHwDes(const SDmaDesHdr& aHdr, const SDmaDesHdr& aNextHdr);
virtual void AppendHwDes(const TDmaChannel& aChannel, const SDmaDesHdr& aLastHdr,
const SDmaDesHdr& aNewHdr);
virtual void UnlinkHwDes(const TDmaChannel& aChannel, SDmaDesHdr& aHdr);
// other
static void Isr(TAny* aThis);
inline TDmaDesc* HdrToHwDes(const SDmaDesHdr& aHdr);
private:
static const SCreateInfo KInfo;
public:
TTemplateSgChannel iChannels[KChannelCount];
};
</codeblock> <p>Notes: </p> <ul>
<li id="GUID-4E8059FF-0BBC-5F65-9F66-3C96BC86FE55"><p>The array of channels
must be defined in the PSL controller class because only the PSL knows what
kind of channels are used. </p> </li>
<li id="GUID-66E7787B-E933-5E17-9169-E0D245B9D27A"><p>The PSL controller class
must be allocated in the BSS section of the DMA kernel extension. The second
phase constructor must be called from the extension entry point. </p> </li>
<li id="GUID-7306F4CB-5D0F-5E82-AC61-E068A48C9C9B"><p>The C++ constructor
must pass a <xref href="GUID-25398075-927B-36E4-B953-16785EC4086C.dita#GUID-25398075-927B-36E4-B953-16785EC4086C/GUID-BBD2153C-4B41-357C-9299-D710930AFCBE"><apiname>TDmac::SCreateInfo</apiname></xref> structure to the <xref href="GUID-25398075-927B-36E4-B953-16785EC4086C.dita"><apiname>TDmac</apiname></xref> constructor.
This structure defines what the PIL (platform-independent layer) needs to
know about the underlying DMA controller. The template <codeph>TTemplateDmac</codeph> constructor
just passes a <codeph>TDmac::ScreateInfo</codeph> structure, which defines
the layout of the (base class managed) descriptor pool to the base class constructor
through a ctor list: </p> <codeblock id="GUID-E00DD5C0-0976-58A0-B3C0-22DFD9216BC0" xml:space="preserve">TTemplateDmac::TTemplateDmac()
//
// Constructor.
//
: TDmac(KInfo)
{}
</codeblock> <p>where KInfo is a <codeph>TDmac::ScreateInfo</codeph> type. </p> </li>
<li id="GUID-4C6FB95D-BE6F-5938-8AC3-824B3BB508EE"><p>The PSL controller class
needs a second phase constructor. This is the <codeph>Create()</codeph> function,
which must: </p> <ol id="GUID-33DC912F-C5B4-5F33-A427-62700522D958">
<li id="GUID-F0E8A497-B09F-5DA0-A94A-3B08FC28707C"><p>call the second phase
constructor of the base class: <xref href="GUID-25398075-927B-36E4-B953-16785EC4086C.dita#GUID-25398075-927B-36E4-B953-16785EC4086C/GUID-9B0C8C35-F149-3358-90B8-B4670CFE888F"><apiname>TDmac::Create()</apiname></xref>. </p> </li>
<li id="GUID-1FD43FAD-4F43-557E-9EF0-19C725DD5FF5"><p>perform any hardware-specific
initialisation. </p> </li>
<li id="GUID-A522B8D4-744B-5C5B-B684-C82F34462171"><p>bind and enable the
DMA interrupt(s). </p> </li>
</ol> <p>In the template port, <codeph>Create()</codeph> calls the base class <xref href="GUID-25398075-927B-36E4-B953-16785EC4086C.dita#GUID-25398075-927B-36E4-B953-16785EC4086C/GUID-9B0C8C35-F149-3358-90B8-B4670CFE888F"><apiname>TDmac::Create()</apiname></xref> function
and then initialises the template specific channel object members (the temporary
descriptor fields used for appending requests to live channels), and performs
platform specific initialisation for the interrupt dispatch of DMA interrupt
events: </p> <codeblock id="GUID-FBCC8132-129C-53A4-99B7-290B56C3035D" xml:space="preserve">TInt TTemplateDmac::Create()
//
// Second phase construction.
//
{
TInt r = TDmac::Create(KInfo); // Base class Create()
if (r == KErrNone)
{
__DMA_ASSERTA(ReserveSetOfDes(KChannelCount) == KErrNone);
for (TInt i=0; i < KChannelCount; ++i)
{
TDmaDesc* pD = HdrToHwDes(*iFreeHdr);
iChannels[i].iTmpDes = pD;
iChannels[i].iTmpDesPhysAddr = DesLinToPhys(pD);
iFreeHdr = iFreeHdr->iNext;
}
r = Interrupt::Bind(EAsspIntIdDma, Isr, this);
if (r == KErrNone)
{
// TO DO: Map DMA clients (requests) to DMA channels here.
r = Interrupt::Enable(EAsspIntIdDma);
}
}
return r;
}
</codeblock> </li>
</ul> </section>
<section id="GUID-0DBA2E68-7759-5447-B34B-14F237369CB1"><title>Implement the
channel allocator</title> <p>Channel allocation is implemented in the PSL
(platform-specific layer) because this is a hardware-specific operation. There
are two basic options: </p> <ul>
<li id="GUID-A9246754-F21B-5BFC-9FDB-2CE5C92D1FA3"><p>Preallocate, at design
time, one channel per DMA-aware peripheral. This is the simplest approach,
and it should be acceptable for most Symbian platform devices because the
set of supported peripherals is closed. In this case, cookies passed by client
device drivers map uniquely to DMA channels. </p> </li>
<li id="GUID-C1164FA0-9B70-55C1-9C5A-2D02728FA67A"><p>Full dynamic allocation.
This is a simple approach too, but DMA channels are, in general, not completely
identical. For example, some channels may have greater priorities than others. </p> </li>
</ul> <p>Mixed schemes are also possible, for example, client device driver
cookies could be used to select a subset of channels, and dynamic allocation
used inside this subset. </p> <p>Whatever option is chosen, the PSL must provide
an implementation for the function <xref href="GUID-176B8E0D-0422-341B-A134-7C85432E1303.dita#GUID-176B8E0D-0422-341B-A134-7C85432E1303/GUID-934F46B3-1DF9-3870-87EE-A8E2DEF82810"><apiname>DmaChannelMgr::Open()</apiname></xref>.
The template implementation is: </p> <codeblock id="GUID-9C7F5850-F72B-5A16-9DA0-606FBABAB5F0" xml:space="preserve">TDmaChannel* DmaChannelMgr::Open(TUint32 aOpenId)
//
//
//
{
__KTRACE_OPT(KDMA, Kern::Printf(">DmaChannelMgr::Open aOpenId=%d", aOpenId));
__DMA_ASSERTA(aOpenId < static_cast<TUint32>(KChannelCount));
TDmaChannel* pC = Controller.iChannels + aOpenId;
if (pC->IsOpened())
pC = NULL;
else
{
pC->iController = &Controller;
pC->iPslId = aOpenId;
}
return pC;
}
</codeblock> <p>The template DMA channel manager returns a pointer to
a DMA channel if the channel has not been previously allocated. Note that
since channels possess preset priorities, the device drivers must be aware
of which channel(s) they require DMA service from and configure the DMA controller
to route sources to allocated channels accordingly. </p> <p>The platform-specific
cookies passed by client device drivers to the PSL must be defined somewhere
so that client device drivers can access them. </p> </section>
<section id="GUID-6B35E9A7-11D6-5B26-B562-934169503412"><title>[Optionally]
implement channel cleanup</title> <p>In the template PSL (platform-specific
layer), the function <xref href="GUID-176B8E0D-0422-341B-A134-7C85432E1303.dita#GUID-176B8E0D-0422-341B-A134-7C85432E1303/GUID-909D795A-7303-3A76-9C8E-3B07A97DD716"><apiname>DmaChannelMgr::Close()</apiname></xref> is a no-operation.
This function is called by the PIL (platform-independent layer) when client
device drivers call <xref href="GUID-83882548-FAC5-3EFF-92ED-14D1D9A85D37.dita#GUID-83882548-FAC5-3EFF-92ED-14D1D9A85D37/GUID-8204AFBD-2A60-372E-B626-35BD19851FF7"><apiname>TDmaChannel::Close()</apiname></xref>. If the PSL needs
to perform any hardware-specific operation when the channel is closed, then
the implementation of <codeph>DmaChannelMgr::Close()</codeph> should be updated
to reflect that. </p> </section>
<section id="GUID-C9439F04-0BCC-5BC7-B18B-5C1B88515340"><title>Implement the
mandatory controller virtual functions</title> <p>The <xref href="GUID-25398075-927B-36E4-B953-16785EC4086C.dita"><apiname>TDmac</apiname></xref> class
contains several pure virtual functions that must be implemented by the PSL
(platform-specific layer): </p> <ul>
<li id="GUID-FF533DDF-E4B1-5AD1-9CBD-19A1D94AB07D"><p> <xref href="GUID-25398075-927B-36E4-B953-16785EC4086C.dita#GUID-25398075-927B-36E4-B953-16785EC4086C/GUID-81921A9D-41F5-34BC-B882-60CC4CD807FB"><apiname>TDmac::Transfer()</apiname></xref> </p> </li>
<li id="GUID-F6D6524A-09E3-54C0-AEB6-1D6BAB29210C"><p> <xref href="GUID-25398075-927B-36E4-B953-16785EC4086C.dita#GUID-25398075-927B-36E4-B953-16785EC4086C/GUID-B738D1B9-80FA-334D-ABEB-DFFF093E0B9D"><apiname>TDmac::StopTransfer()</apiname></xref> </p> </li>
<li id="GUID-6209F3C7-9E3C-58BF-ABA4-6DF4BBF44A82"><p> <xref href="GUID-25398075-927B-36E4-B953-16785EC4086C.dita#GUID-25398075-927B-36E4-B953-16785EC4086C/GUID-7422834D-CE6B-32DB-A040-7762A8BAB7D7"><apiname>TDmac::IsIdle()</apiname></xref> </p> </li>
</ul> <p>These functions start and stop transfers on a DMA channel and are
the main interface between the PIL (platform-independent layer) and the PSL.
The implementation of these functions depends on the hardware available for
performing DMA, and on the characteristics used to specify a DMA transfer: </p> <ul>
<li id="GUID-81DA9FED-18F0-5205-9597-33542B0CB3CC"><p>the source and destination
addresses </p> </li>
<li id="GUID-D6684249-31CC-544B-8CEE-DD1D7FC8A8A1"><p>the burst size </p> </li>
<li id="GUID-91D910DB-E1D7-5993-9ADB-6823662249D0"><p>the maximum transfer
size </p> </li>
<li id="GUID-C1926D7D-8349-5BA1-8E21-AB5D90486443"><p>the transfer width,
i.e. number of bits per memory access </p> </li>
<li id="GUID-C091D505-1088-56CA-8630-F1099B0A2234"><p>the memory alignment
and endianness. </p> </li>
</ul> <p>The DMA Framework manages the transfer descriptors according to the
descriptor parameter passed into the <xref href="GUID-25398075-927B-36E4-B953-16785EC4086C.dita"><apiname>TDmac</apiname></xref> constructor
by the derived class constructor; the descriptor parameter is a <xref href="GUID-25398075-927B-36E4-B953-16785EC4086C.dita#GUID-25398075-927B-36E4-B953-16785EC4086C/GUID-BBD2153C-4B41-357C-9299-D710930AFCBE"><apiname>TDmac::SCreateInfo</apiname></xref> structure.
The per-request transfer parameters are passed into the descriptors for each
request issued by a driver. </p> <p><b>The
transfer function: Transfer()</b> </p> <p>This function initiates a previously
constructed request on a specific channel. This is the template implementation: </p> <codeblock id="GUID-80B95C0E-B30D-5A10-8512-68317D404DB3" xml:space="preserve">void TTemplateDmac::Transfer(const TDmaChannel& aChannel, const SDmaDesHdr& aHdr)
//
// Initiates a (previously constructed) request on a specific channel.
//
{
const TUint8 i = static_cast<TUint8>(aChannel.PslId());
TDmaDesc* pD = HdrToHwDes(aHdr);
__KTRACE_OPT(KDMA, Kern::Printf(">TTemplateDmac::Transfer channel=%d des=0x%08X", i, pD));
// TO DO (for instance): Load the first descriptor address into the DMAC and start it
// by setting the RUN bit.
(void) *pD, (void) i;
}
</codeblock> <p><b>The
stop transfer function: StopTransfer()</b> </p> <p>This function requires
that the RUN mode is cleared. This is the template implementation: </p> <codeblock id="GUID-1BF7A5B4-2723-5A18-98E0-1F79A4928121" xml:space="preserve">void TTemplateDmac::StopTransfer(const TDmaChannel& aChannel)
//
// Stops a running channel.
//
{
const TUint8 i = static_cast<TUint8>(aChannel.PslId());
__KTRACE_OPT(KDMA, Kern::Printf(">TTemplateDmac::StopTransfer channel=%d", i));
// TO DO (for instance): Clear the RUN bit of the channel.
(void) i;
}
</codeblock> <p><b>The
function: IsIdle()</b> </p> <p> <codeph>IsIdle()</codeph> returns the state
of a given channel. This is the template implementation: </p> <codeblock id="GUID-C7CC986E-986B-5C5E-AA3D-86851131BE93" xml:space="preserve">TBool TTemplateDmac::IsIdle(const TDmaChannel& aChannel)
//
// Returns the state of a given channel.
//
{
const TUint8 i = static_cast<TUint8>(aChannel.PslId());
__KTRACE_OPT(KDMA, Kern::Printf(">TTemplateDmac::IsIdle channel=%d", i));
// TO DO (for instance): Return the state of the RUN bit of the channel.
// The return value should reflect the actual state.
(void) i;
return ETrue;
}
</codeblock> </section>
<section id="GUID-A362A7CF-3674-525C-8114-485315E6594B"><title>Implement the
non-mandatory controller virtual functions</title> <p>The following auxiliary
functions are used to implement the scatter-gather transfer mode behaviour
by creating and manipulating the linked list of transfer fragment headers
that describe a given scatter-gather transaction. They are called by the <xref href="GUID-25398075-927B-36E4-B953-16785EC4086C.dita"><apiname>TDmac</apiname></xref> base
class functions when the instance of the <codeph>TDmac</codeph> derived class
reports itself as being capable of scatter-gather operations. </p> <ul>
<li id="GUID-D401D7C8-B086-56CD-BAE0-96EEB0AA7E38"><p> <xref href="GUID-25398075-927B-36E4-B953-16785EC4086C.dita#GUID-25398075-927B-36E4-B953-16785EC4086C/GUID-90AC3E58-E589-3F91-85F7-16A4ADFFFA69"><apiname>TDmac::InitHwDes()</apiname></xref> </p> </li>
<li id="GUID-58633367-FE56-53B7-A967-21828DA85F5C"><p> <xref href="GUID-25398075-927B-36E4-B953-16785EC4086C.dita#GUID-25398075-927B-36E4-B953-16785EC4086C/GUID-05021B05-75DE-3F75-92C6-8B9445EB86D3"><apiname>TDmac::ChainHwDes()</apiname></xref> </p> </li>
<li id="GUID-94F81366-77D4-5AF6-9933-53E964863301"><p> <xref href="GUID-25398075-927B-36E4-B953-16785EC4086C.dita#GUID-25398075-927B-36E4-B953-16785EC4086C/GUID-AFCDDA16-991D-3BDA-B90B-87BCAFF66E5C"><apiname>TDmac::AppendHwDes()</apiname></xref> </p> </li>
</ul> <p><b>First
scatter-gather support function: InitHwDes()</b> </p> <p>This is a function
for creating a scatter-gather list. From the information in the passed-in
request, the function sets up the descriptor with that fragment's: </p> <ul>
<li id="GUID-098E886D-D98D-5AC9-BB25-7FF8E6F0D05B"><p>source and destination
address </p> </li>
<li id="GUID-83440454-B955-55C8-BF10-DF86CDA0C466"><p>size </p> </li>
<li id="GUID-294D3B08-0A64-534D-858F-F43759A2DC59"><p>driver/DMA controller
specific transfer parameters: memory/peripheral, burst size, transfer width. </p> </li>
</ul> <p>This is the template implementation: </p> <codeblock id="GUID-EB6F9C76-EC23-5464-A6C5-AB0B36717D2B" xml:space="preserve">void TTemplateDmac::InitHwDes(const SDmaDesHdr& aHdr, TUint32 aSrc, TUint32 aDest, TInt aCount,
TUint aFlags, TUint32 aPslInfo, TUint32 /*aCookie*/)
//
// Sets up (from a passed in request) the descriptor with that fragment's source and destination address,
// the fragment size, and the (driver/DMA controller) specific transfer parameters (mem/peripheral,
// burst size, transfer width).
//
{
TDmaDesc* pD = HdrToHwDes(aHdr);
__KTRACE_OPT(KDMA, Kern::Printf("TTemplateDmac::InitHwDes 0x%08X", pD));
// Unaligned descriptor? Error in generic layer!
__DMA_ASSERTD(IsHwDesAligned(pD));
pD->iSrcAddr = (aFlags & KDmaPhysAddrSrc) ? aSrc : Epoc::LinearToPhysical(aSrc);
pD->iDestAddr = (aFlags & KDmaPhysAddrDest) ? aDest : Epoc::LinearToPhysical(aDest);
pD->iCmd = DcmdReg(aCount, aFlags, aPslInfo);
pD->iDescAddr = TDmaDesc::KStopBitMask;
}
</codeblock> <p><b>Second
scatter-gather support function: ChainHwDes()</b> </p> <p>If the framework
needs to fragment the client’s request, for transfer size or memory discontiguousness
reasons, then the framework calls this function. It chains hardware descriptors
together by setting the next pointer of the original descriptor to the physical
address of the descriptor to be chained. It assumes that the DMAC channel
is quiescent when called. </p> <p>This is the template implementation: </p> <codeblock id="GUID-7979A1FB-5FA1-5078-8762-3874A62E6C63" xml:space="preserve">void TTemplateDmac::ChainHwDes(const SDmaDesHdr& aHdr, const SDmaDesHdr& aNextHdr)
//
// Chains hardware descriptors together by setting the next pointer of the original descriptor
// to the physical address of the descriptor to be chained.
//
{
TDmaDesc* pD = HdrToHwDes(aHdr);
TDmaDesc* pN = HdrToHwDes(aNextHdr);
__KTRACE_OPT(KDMA, Kern::Printf("TTemplateDmac::ChainHwDes des=0x%08X next des=0x%08X", pD, pN));
// Unaligned descriptor? Error in generic layer!
__DMA_ASSERTD(IsHwDesAligned(pD) && IsHwDesAligned(pN));
// TO DO: Modify pD->iCmd so that no end-of-transfer interrupt gets raised any longer.
pD->iDescAddr = DesLinToPhys(pN);
}
</codeblock> <p><b>Third
scatter-gather support function: AppendHwDes()</b> </p> <p>This function is
called by the <xref href="GUID-25398075-927B-36E4-B953-16785EC4086C.dita"><apiname>TDmac</apiname></xref> base class when a driver request is
called for a channel that is still active, i.e. where the intent is to provide
data streaming so that the target device is constantly provided with data;
for example, an audio device playing a track. In this case, the function provided
by the derived class must: </p> <ul>
<li id="GUID-F94FBAA8-2A0D-5863-BA09-0EC1D61005A9"><p>stop the DMAC to prevent
any corruption of the scatter-gather list while appending the new fragment
descriptor </p> </li>
<li id="GUID-AEF81B78-73D8-5B70-AFE4-0AEA7862CB74"><p>append the new descriptor </p> </li>
<li id="GUID-2304F2D4-833E-5F75-B88E-C794DF9D984E"><p>re-enable the channel,
ideally before the target has detected the gap in service. </p> </li>
</ul> <p>This is the template implementation: </p> <codeblock id="GUID-18512439-C781-5267-ADBC-B9047942F800" xml:space="preserve">void TTemplateDmac::AppendHwDes(const TDmaChannel& aChannel, const SDmaDesHdr& aLastHdr,
const SDmaDesHdr& aNewHdr)
//
// Appends a descriptor to the chain while the channel is running.
//
{
const TUint8 i = static_cast<TUint8>(aChannel.PslId());
TDmaDesc* pL = HdrToHwDes(aLastHdr);
TDmaDesc* pN = HdrToHwDes(aNewHdr);
__KTRACE_OPT(KDMA, Kern::Printf(">TTemplateDmac::AppendHwDes channel=%d last des=0x%08X new des=0x%08X",
i, pL, pN));
// Unaligned descriptor? Error in generic layer!
__DMA_ASSERTD(IsHwDesAligned(pL) && IsHwDesAligned(pN));
TPhysAddr newPhys = DesLinToPhys(pN);
const TInt irq = NKern::DisableAllInterrupts();
StopTransfer(aChannel);
pL->iDescAddr = newPhys;
const TTemplateSgChannel& channel = static_cast<const TTemplateSgChannel&>(aChannel);
TDmaDesc* pD = channel.iTmpDes;
// TO DO: Implement the appropriate algorithm for appending a descriptor here.
(void) *pD, (void) i;
NKern::RestoreInterrupts(irq);
__KTRACE_OPT(KDMA, Kern::Printf("<TTemplateDmac::AppendHwDes"));
}
</codeblock> </section>
<section id="GUID-F5968000-0C3E-5E53-A2D5-120B81F3140F"><title>Implement an
interrupt service routine</title> <p>The interrupt service routine needs to
do the following: </p> <ul>
<li id="GUID-CE55E999-0886-5620-87BA-99A36D694FD4"><p>identify the channel
that raised the interrupt </p> </li>
<li id="GUID-02467F17-DC1E-552C-B1C2-D3C2CCE12347"><p>decide whether the interrupt
was raised because of a successful data transfer or because of an error </p> </li>
<li id="GUID-0D5FDA57-A45E-54E5-A69C-64D22B916AB4"><p>call the base class
function <codeph>TDmac::HandleIsr()</codeph>, which queues a DFC, or increments
the number of pending interrupts if a DFC is already queued. </p> </li>
</ul> <p>This is the template implementation: </p> <codeblock id="GUID-4CFCA5A1-9657-55F1-BE3C-AFF1F3974F80" xml:space="preserve">void TTemplateDmac::Isr(TAny* aThis)
//
// This ISR reads the interrupt identification and calls back into the base class
// interrupt service handler with the channel identifier and an indication whether the
// transfer completed correctly or with an error.
//
{
TTemplateDmac& me = *static_cast<TTemplateDmac*>(aThis);
// TO DO: Implement the behaviour described above, call HandleIsr().
HandleIsr(me.iChannels[5], 0); // Example
}
</codeblock> </section>
<section id="GUID-D7F410A1-DC70-515D-A00D-3955B534C62F"><title>Implement the
test support table and function</title> <p>The DMA Framework comes with a
test harness that can be used to validate the port if the underlying DMA controller
supports memory to memory transfers. </p> <p>The test harness needs to know
about the capabilities of the port being tested. The PSL provides the global
function <xref href="GUID-3D0A9A03-E210-30EE-A1A1-7DA06E668CAA.dita"><apiname>DmaTestInfo()</apiname></xref> that returns a <xref href="GUID-59EE15C1-19E7-3AA7-8339-A80CCC074D64.dita"><apiname>TDmaTestInfo</apiname></xref> object
that contains this information. In the template PSL, this structure is initialised
to binary zeroes. Before using the test harness, it must be initialised with
valid values. </p> <p>See <xref href="GUID-59EE15C1-19E7-3AA7-8339-A80CCC074D64.dita"><apiname>TDmaTestInfo</apiname></xref>, <xref href="GUID-3D0A9A03-E210-30EE-A1A1-7DA06E668CAA.dita"><apiname>DmaTestInfo()</apiname></xref> and <xref href="GUID-8552E66E-73D6-51DA-8F53-DDF437186CD6.dita">Validation</xref>. </p> </section>
<section id="GUID-D452D2CD-D251-50F4-818E-E7125501CE8F"><title>Optimise the
performance of critical functions</title> <p>You can optionally optimise critical
functions by writing them in assembler. The two candidates for an assembler
rewrite are: </p> <ul>
<li id="GUID-157E6F4A-0310-5923-B446-040F75E469D4"><p>The interrupt service
routine </p> </li>
<li id="GUID-74C9D4DB-9CA9-5C5E-A475-B6840EBC2245"><p>In scatter-gather mode,
the <xref href="GUID-25398075-927B-36E4-B953-16785EC4086C.dita#GUID-25398075-927B-36E4-B953-16785EC4086C/GUID-AFCDDA16-991D-3BDA-B90B-87BCAFF66E5C"><apiname>TDmac::AppendHwDes()</apiname></xref> function if it needs to suspend
a transfer when appending a new descriptor chain to an existing one. </p> </li>
</ul> </section>
<section id="GUID-A2AF293F-558A-5F69-821F-EC2B202A5A4F"><title>Extend the
framework with platform-specific functionality</title> <p>There are two ways
of extending the DMA Framework: </p> <ol id="GUID-D6C660FC-FF23-55B2-B91E-4BC04C592059">
<li id="GUID-4453F4C2-3444-5A3E-94C7-BE6320B1EF89"><p>to provide platform-specific
functionality on a per-channel basis. </p> </li>
<li id="GUID-C0C6A072-134A-5753-A6DC-185F3975C2A6"><p>to provide platform-specific
functionality on a <i>channel independent</i> basis. </p> </li>
</ol> <p>In the first case, the PSL provides an implementation of the virtual
function <xref href="GUID-25398075-927B-36E4-B953-16785EC4086C.dita#GUID-25398075-927B-36E4-B953-16785EC4086C/GUID-D6D011E9-9D75-3881-87D8-1986FFBBA106"><apiname>TDmac::Extension()</apiname></xref>. The default implementation
just returns <xref href="GUID-F89DA3F0-2A48-3F9B-8F08-29350E92D0E4.dita"><apiname>KErrNotSupported</apiname></xref>. </p> <p> <xref href="GUID-83882548-FAC5-3EFF-92ED-14D1D9A85D37.dita#GUID-83882548-FAC5-3EFF-92ED-14D1D9A85D37/GUID-2CF7498E-F5D2-3C4A-8E98-47504F9FC404"><apiname>TDmaChannel::Extension()</apiname></xref> calls <xref href="GUID-25398075-927B-36E4-B953-16785EC4086C.dita#GUID-25398075-927B-36E4-B953-16785EC4086C/GUID-D6D011E9-9D75-3881-87D8-1986FFBBA106"><apiname>TDmac::Extension()</apiname></xref>. </p> <p>In the second case, the PSL provides an implementation of the static
function <xref href="GUID-176B8E0D-0422-341B-A134-7C85432E1303.dita#GUID-176B8E0D-0422-341B-A134-7C85432E1303/GUID-C733B302-4269-3391-8ADE-617CFF198B56"><apiname>DmaChannelMgr::StaticExtension()</apiname></xref>. The template
PSL implementation just returns <xref href="GUID-F89DA3F0-2A48-3F9B-8F08-29350E92D0E4.dita"><apiname>KErrNotSupported</apiname></xref>. </p> <p> <xref href="GUID-83882548-FAC5-3EFF-92ED-14D1D9A85D37.dita#GUID-83882548-FAC5-3EFF-92ED-14D1D9A85D37/GUID-75562653-1F27-39D3-96D8-55A94BA9B68B"><apiname>TDmaChannel::StaticExtension()</apiname></xref> calls <xref href="GUID-176B8E0D-0422-341B-A134-7C85432E1303.dita#GUID-176B8E0D-0422-341B-A134-7C85432E1303/GUID-C733B302-4269-3391-8ADE-617CFF198B56"><apiname>DmaChannelMgr::StaticExtension()</apiname></xref>. </p> </section>
</conbody></concept>