--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/drivers/dma/dma2_pil.cpp Tue Aug 31 16:34:26 2010 +0300
@@ -0,0 +1,3131 @@
+// Copyright (c) 2002-2010 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "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:
+//
+// Description:
+// e32/drivers/dma2_pil.cpp
+// DMA Platform Independent Layer (PIL)
+//
+//
+
+#include <drivers/dma.h>
+#include <drivers/dma_hai.h>
+
+#include <kernel/kern_priv.h>
+
+
+// Symbian _Min() & _Max() are broken, so we have to define them ourselves
+inline TUint _Min(TUint aLeft, TUint aRight)
+ {return(aLeft < aRight ? aLeft : aRight);}
+inline TUint _Max(TUint aLeft, TUint aRight)
+ {return(aLeft > aRight ? aLeft : aRight);}
+
+
+// The following section is used only when freezing the DMA2 export library
+/*
+TInt DmaChannelMgr::StaticExtension(TInt, TAny*) {return 0;}
+TDmaChannel* DmaChannelMgr::Open(TUint32, TBool, TUint) {return 0;}
+void DmaChannelMgr::Close(TDmaChannel*) {}
+EXPORT_C const TDmaTestInfo& DmaTestInfo() {static TDmaTestInfo a; return a;}
+EXPORT_C const TDmaV2TestInfo& DmaTestInfoV2() {static TDmaV2TestInfo a; return a;}
+*/
+
+static const char KDmaPanicCat[] = "DMA " __FILE__;
+
+//////////////////////////////////////////////////////////////////////
+// DmaChannelMgr
+//
+// Wait, Signal, and Initialise are defined here in the PIL.
+// Open, Close and Extension must be defined in the PSL.
+
+NFastMutex DmaChannelMgr::Lock;
+
+
+void DmaChannelMgr::Wait()
+ {
+ NKern::FMWait(&Lock);
+ }
+
+
+void DmaChannelMgr::Signal()
+ {
+ NKern::FMSignal(&Lock);
+ }
+
+
+TInt DmaChannelMgr::Initialise()
+ {
+ return KErrNone;
+ }
+
+
+class TDmaCancelInfo : public SDblQueLink
+ {
+public:
+ TDmaCancelInfo();
+ void Signal();
+public:
+ NFastSemaphore iSem;
+ };
+
+
+TDmaCancelInfo::TDmaCancelInfo()
+ : iSem(0)
+ {
+ iNext = this;
+ iPrev = this;
+ }
+
+
+void TDmaCancelInfo::Signal()
+ {
+ TDmaCancelInfo* p = this;
+ FOREVER
+ {
+ TDmaCancelInfo* next = (TDmaCancelInfo*)p->iNext;
+ if (p!=next)
+ p->Deque();
+ NKern::FSSignal(&p->iSem); // Don't dereference p after this
+ if (p==next)
+ break;
+ p = next;
+ }
+ }
+
+
+//////////////////////////////////////////////////////////////////////////////
+
+#ifdef __DMASIM__
+#ifdef __WINS__
+typedef TLinAddr TPhysAddr;
+#endif
+static inline TPhysAddr LinToPhys(TLinAddr aLin) {return aLin;}
+#else
+static inline TPhysAddr LinToPhys(TLinAddr aLin) {return Epoc::LinearToPhysical(aLin);}
+#endif
+
+//
+// Return minimum of aMaxSize and size of largest physically contiguous block
+// starting at aLinAddr.
+//
+static TUint MaxPhysSize(TLinAddr aLinAddr, const TUint aMaxSize)
+ {
+ const TPhysAddr physBase = LinToPhys(aLinAddr);
+ __DMA_ASSERTD(physBase != KPhysAddrInvalid);
+ TLinAddr lin = aLinAddr;
+ TUint size = 0;
+ for (;;)
+ {
+ // Round up the linear address to the next MMU page boundary
+ const TLinAddr linBoundary = Kern::RoundToPageSize(lin + 1);
+ size += linBoundary - lin;
+ if (size >= aMaxSize)
+ return aMaxSize;
+ if ((physBase + size) != LinToPhys(linBoundary))
+ return size;
+ lin = linBoundary;
+ }
+ }
+
+
+//////////////////////////////////////////////////////////////////////////////
+// TDmac
+
+TDmac::TDmac(const SCreateInfo& aInfo)
+ : iMaxDesCount(aInfo.iDesCount),
+ iAvailDesCount(aInfo.iDesCount),
+ iHdrPool(NULL),
+#ifndef __WINS__
+ iHwDesChunk(NULL),
+#endif
+ iDesPool(NULL),
+ iDesSize(aInfo.iDesSize),
+ iCapsHwDes(aInfo.iCapsHwDes),
+ iFreeHdr(NULL)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("TDmac::TDmac"));
+ __DMA_ASSERTD(iMaxDesCount > 0);
+ __DMA_ASSERTD(iDesSize > 0);
+ }
+
+
+//
+// Second-phase c'tor
+//
+TInt TDmac::Create(const SCreateInfo& aInfo)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("TDmac::Create"));
+ iHdrPool = new SDmaDesHdr[iMaxDesCount];
+ if (iHdrPool == NULL)
+ {
+ return KErrNoMemory;
+ }
+
+ TInt r = AllocDesPool(aInfo.iDesChunkAttribs);
+ if (r != KErrNone)
+ {
+ return KErrNoMemory;
+ }
+
+ // Link all descriptor headers together on the free list
+ iFreeHdr = iHdrPool;
+ for (TInt i = 0; i < iMaxDesCount - 1; i++)
+ iHdrPool[i].iNext = iHdrPool + i + 1;
+ iHdrPool[iMaxDesCount-1].iNext = NULL;
+
+ __DMA_INVARIANT();
+ return KErrNone;
+ }
+
+
+TDmac::~TDmac()
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("TDmac::~TDmac"));
+ __DMA_INVARIANT();
+
+ FreeDesPool();
+ delete[] iHdrPool;
+ }
+
+
+void TDmac::Transfer(const TDmaChannel& /*aChannel*/, const SDmaDesHdr& /*aHdr*/)
+ {
+ // TDmac needs to override this function if it has reported the channel
+ // type for which the PIL calls it.
+ __DMA_UNREACHABLE_DEFAULT();
+ }
+
+
+void TDmac::Transfer(const TDmaChannel& /*aChannel*/, const SDmaDesHdr& /*aSrcHdr*/,
+ const SDmaDesHdr& /*aDstHdr*/)
+ {
+ // TDmac needs to override this function if it has reported the channel
+ // type for which the PIL calls it.
+ __DMA_UNREACHABLE_DEFAULT();
+ }
+
+
+TInt TDmac::PauseTransfer(const TDmaChannel& /*aChannel*/)
+ {
+ // TDmac needs to override this function if it has reported support for
+ // channel pausing/resuming.
+ return KErrNotSupported;
+ }
+
+
+TInt TDmac::ResumeTransfer(const TDmaChannel& /*aChannel*/)
+ {
+ // TDmac needs to override this function if it has reported support for
+ // channel pausing/resuming.
+ return KErrNotSupported;
+ }
+
+
+TInt TDmac::AllocDesPool(TUint aAttribs)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("TDmac::AllocDesPool"));
+ // Calling thread must be in CS
+ __ASSERT_CRITICAL;
+ TInt r;
+ if (iCapsHwDes)
+ {
+ const TInt size = iMaxDesCount * iDesSize;
+#ifdef __WINS__
+ (void)aAttribs;
+ iDesPool = new TUint8[size];
+ r = iDesPool ? KErrNone : KErrNoMemory;
+#else
+ // Chunk not mapped as supervisor r/w user none? incorrect mask passed by PSL
+ __DMA_ASSERTD((aAttribs & EMapAttrAccessMask) == EMapAttrSupRw);
+ TPhysAddr phys;
+ r = Epoc::AllocPhysicalRam(size, phys);
+ if (r == KErrNone)
+ {
+ r = DPlatChunkHw::New(iHwDesChunk, phys, size, aAttribs);
+ if (r == KErrNone)
+ {
+ iDesPool = (TAny*)iHwDesChunk->LinearAddress();
+ __KTRACE_OPT(KDMA, Kern::Printf("descriptor hw chunk created lin=0x%08X phys=0x%08X, size=0x%X",
+ iHwDesChunk->iLinAddr, iHwDesChunk->iPhysAddr, size));
+ }
+ else
+ Epoc::FreePhysicalRam(phys, size);
+ }
+#endif
+ }
+ else
+ {
+ iDesPool = Kern::Alloc(iMaxDesCount * sizeof(TDmaTransferArgs));
+ r = iDesPool ? KErrNone : KErrNoMemory;
+ }
+ return r;
+ }
+
+
+void TDmac::FreeDesPool()
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("TDmac::FreeDesPool"));
+ // Calling thread must be in CS
+ __ASSERT_CRITICAL;
+ if (iCapsHwDes)
+ {
+#ifdef __WINS__
+ delete[] iDesPool;
+#else
+ if (iHwDesChunk)
+ {
+ const TPhysAddr phys = iHwDesChunk->PhysicalAddress();
+ const TInt size = iHwDesChunk->iSize;
+ iHwDesChunk->Close(NULL);
+ Epoc::FreePhysicalRam(phys, size);
+ }
+#endif
+ }
+ else
+ {
+ Kern::Free(iDesPool);
+ }
+ }
+
+
+//
+// Prealloc the given number of descriptors.
+//
+TInt TDmac::ReserveSetOfDes(TInt aCount)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("TDmac::ReserveSetOfDes count=%d", aCount));
+ __DMA_ASSERTD(aCount > 0);
+ TInt r = KErrTooBig;
+ Wait();
+ if (iAvailDesCount - aCount >= 0)
+ {
+ iAvailDesCount -= aCount;
+ r = KErrNone;
+ }
+ Signal();
+ __DMA_INVARIANT();
+ return r;
+ }
+
+
+//
+// Return the given number of preallocated descriptors to the free pool.
+//
+void TDmac::ReleaseSetOfDes(TInt aCount)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("TDmac::ReleaseSetOfDes count=%d", aCount));
+ __DMA_ASSERTD(aCount >= 0);
+ Wait();
+ iAvailDesCount += aCount;
+ Signal();
+ __DMA_INVARIANT();
+ }
+
+
+//
+// Queue DFC and update word used to communicate with channel DFC.
+//
+// Called in interrupt context by PSL.
+//
+void TDmac::HandleIsr(TDmaChannel& aChannel, TUint aEventMask, TBool aIsComplete)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("TDmac::HandleIsr"));
+
+ // Function needs to be called by PSL in ISR context
+ __DMA_ASSERTD(NKern::CurrentContext() == NKern::EInterrupt);
+
+ // First the ISR callback stuff
+
+ // Is this a transfer completion notification?
+ if (aEventMask & EDmaCallbackRequestCompletion)
+ {
+ // If so, has the client requested an ISR callback?
+ if (__e32_atomic_load_acq32(&aChannel.iIsrCbRequest))
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("ISR callback"));
+
+ // Since iIsrCbRequest was set no threads will be
+ // modifying the request queue.
+ const DDmaRequest* const req = _LOFF(aChannel.iReqQ.First(), DDmaRequest, iLink);
+
+ // We expect the request to have requested
+ // ISR callback
+ __NK_ASSERT_DEBUG(req->iIsrCb);
+
+ TDmaCallback const cb = req->iDmaCb;
+ TAny* const arg = req->iDmaCbArg;
+ // Execute the client callback
+ (*cb)(EDmaCallbackRequestCompletion,
+ (aIsComplete ? EDmaResultOK : EDmaResultError),
+ arg,
+ NULL);
+ // Now let's see if the callback rescheduled the transfer request
+ // (see TDmaChannel::IsrRedoRequest()).
+ const TBool redo = aChannel.iRedoRequest;
+ aChannel.iRedoRequest = EFalse;
+ const TBool stop = __e32_atomic_load_acq32(&aChannel.iIsrDfc) &
+ (TUint32)TDmaChannel::KCancelFlagMask;
+ // There won't be another ISR callback if this callback didn't
+ // reschedule the request, or the client cancelled all requests, or
+ // this callback rescheduled the request with a DFC callback.
+ if (!redo || stop || !req->iIsrCb)
+ {
+ __e32_atomic_store_rel32(&aChannel.iIsrCbRequest, EFalse);
+ }
+ if (redo && !stop)
+ {
+ // We won't queue the channel DFC in this case and just return.
+ __KTRACE_OPT(KDMA, Kern::Printf("CB rescheduled xfer -> no DFC"));
+ return;
+ }
+ // Not redoing or being cancelled means we've been calling the
+ // request's ISR callback for the last time. We're going to
+ // complete the request via the DFC in the usual way.
+ }
+ }
+ else
+ {
+ // The PIL doesn't support yet any completion types other than
+ // EDmaCallbackRequestCompletion.
+ __DMA_CANT_HAPPEN();
+ }
+
+ // Now queue a DFC if necessary. The possible scenarios are:
+ // a) DFC not queued (orig == 0) -> update iIsrDfc + queue DFC
+ // b) DFC queued, not running yet (orig != 0) -> just update iIsrDfc
+ // c) DFC running / iIsrDfc not reset yet (orig != 0) -> just update iIsrDfc
+ // d) DFC running / iIsrDfc already reset (orig == 0) -> update iIsrDfc + requeue DFC
+
+ // Set error flag if necessary.
+ const TUint32 inc = aIsComplete ? 1u : TUint32(TDmaChannel::KErrorFlagMask) | 1u;
+
+ // Add 'inc' (interrupt count increment + poss. error flag) to 'iIsrDfc' if
+ // cancel flag is not set, do nothing otherwise. Assign original value of
+ // 'iIsrDfc' to 'orig' in any case.
+ const TUint32 orig = __e32_atomic_tau_ord32(&aChannel.iIsrDfc,
+ TUint32(TDmaChannel::KCancelFlagMask),
+ 0,
+ inc);
+
+ // As transfer should be suspended when an error occurs, we
+ // should never get there with the error flag already set.
+ __DMA_ASSERTD((orig & inc & (TUint32)TDmaChannel::KErrorFlagMask) == 0);
+
+ if (orig == 0)
+ {
+ aChannel.iDfc.Add();
+ }
+ }
+
+
+TInt TDmac::InitDes(const SDmaDesHdr& aHdr, const TDmaTransferArgs& aTransferArgs)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("TDmac::InitDes"));
+ TInt r;
+ if (iCapsHwDes)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("iCaps.iHwDescriptors"));
+ r = InitHwDes(aHdr, aTransferArgs);
+ }
+ else
+ {
+ TDmaTransferArgs& args = HdrToDes(aHdr);
+ args = aTransferArgs;
+ r = KErrNone;
+ }
+ return r;
+ }
+
+
+TInt TDmac::InitHwDes(const SDmaDesHdr& /*aHdr*/, const TDmaTransferArgs& /*aTransferArgs*/)
+ {
+ // concrete controller must override if SDmacCaps::iHwDescriptors set
+ __DMA_UNREACHABLE_DEFAULT();
+ return KErrGeneral;
+ }
+
+
+TInt TDmac::InitSrcHwDes(const SDmaDesHdr& /*aHdr*/, const TDmaTransferArgs& /*aTransferArgs*/)
+ {
+ // concrete controller must override if SDmacCaps::iAsymHwDescriptors set
+ __DMA_UNREACHABLE_DEFAULT();
+ return KErrGeneral;
+ }
+
+
+TInt TDmac::InitDstHwDes(const SDmaDesHdr& /*aHdr*/, const TDmaTransferArgs& /*aTransferArgs*/)
+ {
+ // concrete controller must override if SDmacCaps::iAsymHwDescriptors set
+ __DMA_UNREACHABLE_DEFAULT();
+ return KErrGeneral;
+ }
+
+
+TInt TDmac::UpdateDes(const SDmaDesHdr& aHdr, TUint32 aSrcAddr, TUint32 aDstAddr,
+ TUint aTransferCount, TUint32 aPslRequestInfo)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("TDmac::UpdateDes"));
+ TInt r;
+ if (iCapsHwDes)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("iCaps.iHwDescriptors"));
+ r = UpdateHwDes(aHdr, aSrcAddr, aDstAddr, aTransferCount, aPslRequestInfo);
+ }
+ else
+ {
+ TDmaTransferArgs& args = HdrToDes(aHdr);
+ if (aSrcAddr != KPhysAddrInvalid)
+ args.iSrcConfig.iAddr = aSrcAddr;
+ if (aDstAddr != KPhysAddrInvalid)
+ args.iDstConfig.iAddr = aDstAddr;
+ if (aTransferCount)
+ args.iTransferCount = aTransferCount;
+ if (aPslRequestInfo)
+ args.iPslRequestInfo = aPslRequestInfo;
+ r = KErrNone;
+ }
+ return r;
+ }
+
+
+TInt TDmac::UpdateHwDes(const SDmaDesHdr& /*aHdr*/, TUint32 /*aSrcAddr*/, TUint32 /*aDstAddr*/,
+ TUint /*aTransferCount*/, TUint32 /*aPslRequestInfo*/)
+ {
+ // concrete controller must override if SDmacCaps::iHwDescriptors set
+ __DMA_UNREACHABLE_DEFAULT();
+ return KErrGeneral;
+ }
+
+
+TInt TDmac::UpdateSrcHwDes(const SDmaDesHdr& /*aHdr*/, TUint32 /*aSrcAddr*/,
+ TUint /*aTransferCount*/, TUint32 /*aPslRequestInfo*/)
+ {
+ // concrete controller must override if SDmacCaps::iAsymHwDescriptors set
+ __DMA_UNREACHABLE_DEFAULT();
+ return KErrGeneral;
+ }
+
+
+TInt TDmac::UpdateDstHwDes(const SDmaDesHdr& /*aHdr*/, TUint32 /*aDstAddr*/,
+ TUint /*aTransferCount*/, TUint32 /*aPslRequestInfo*/)
+ {
+ // concrete controller must override if SDmacCaps::iAsymHwDescriptors set
+ __DMA_UNREACHABLE_DEFAULT();
+ return KErrGeneral;
+ }
+
+
+void TDmac::ChainHwDes(const SDmaDesHdr& /*aHdr*/, const SDmaDesHdr& /*aNextHdr*/)
+ {
+ // concrete controller must override if SDmacCaps::iHwDescriptors set
+ __DMA_UNREACHABLE_DEFAULT();
+ }
+
+
+void TDmac::AppendHwDes(const TDmaChannel& /*aChannel*/, const SDmaDesHdr& /*aLastHdr*/,
+ const SDmaDesHdr& /*aNewHdr*/)
+ {
+ // concrete controller must override if SDmacCaps::iHwDescriptors set
+ __DMA_UNREACHABLE_DEFAULT();
+ }
+
+
+void TDmac::AppendHwDes(const TDmaChannel& /*aChannel*/,
+ const SDmaDesHdr& /*aSrcLastHdr*/, const SDmaDesHdr& /*aSrcNewHdr*/,
+ const SDmaDesHdr& /*aDstLastHdr*/, const SDmaDesHdr& /*aDstNewHdr*/)
+ {
+ // concrete controller must override if SDmacCaps::iAsymHwDescriptors set
+ __DMA_UNREACHABLE_DEFAULT();
+ }
+
+
+void TDmac::UnlinkHwDes(const TDmaChannel& /*aChannel*/, SDmaDesHdr& /*aHdr*/)
+ {
+ // concrete controller must override if SDmacCaps::iHwDescriptors set
+ __DMA_UNREACHABLE_DEFAULT();
+ }
+
+
+void TDmac::ClearHwDes(const SDmaDesHdr& /*aHdr*/)
+ {
+ // default implementation - NOP; concrete controller may override
+ return;
+ }
+
+
+TInt TDmac::LinkChannels(TDmaChannel& /*a1stChannel*/, TDmaChannel& /*a2ndChannel*/)
+ {
+ // default implementation - NOP; concrete controller may override
+ return KErrNotSupported;
+ }
+
+
+TInt TDmac::UnlinkChannel(TDmaChannel& /*aChannel*/)
+ {
+ // default implementation - NOP; concrete controller may override
+ return KErrNotSupported;
+ }
+
+
+TInt TDmac::FailNext(const TDmaChannel& /*aChannel*/)
+ {
+ // default implementation - NOP; concrete controller may override
+ return KErrNotSupported;
+ }
+
+
+TInt TDmac::MissNextInterrupts(const TDmaChannel& /*aChannel*/, TInt /*aInterruptCount*/)
+ {
+ // default implementation - NOP; concrete controller may override
+ return KErrNotSupported;
+ }
+
+
+TInt TDmac::Extension(TDmaChannel& /*aChannel*/, TInt /*aCmd*/, TAny* /*aArg*/)
+ {
+ // default implementation - NOP; concrete controller may override
+ return KErrNotSupported;
+ }
+
+
+TUint32 TDmac::HwDesNumDstElementsTransferred(const SDmaDesHdr& /*aHdr*/)
+ {
+ // Concrete controller must override if SDmacCaps::iHwDescriptors set.
+ __DMA_UNREACHABLE_DEFAULT();
+ return 0;
+ }
+
+
+TUint32 TDmac::HwDesNumSrcElementsTransferred(const SDmaDesHdr& /*aHdr*/)
+ {
+ // Concrete controller must override if SDmacCaps::iHwDescriptors set.
+ __DMA_UNREACHABLE_DEFAULT();
+ return 0;
+ }
+
+
+#ifdef _DEBUG
+
+void TDmac::Invariant()
+ {
+ Wait();
+ __DMA_ASSERTD(0 <= iAvailDesCount && iAvailDesCount <= iMaxDesCount);
+ __DMA_ASSERTD(!iFreeHdr || IsValidHdr(iFreeHdr));
+ for (TInt i = 0; i < iMaxDesCount; i++)
+ __DMA_ASSERTD(iHdrPool[i].iNext == NULL || IsValidHdr(iHdrPool[i].iNext));
+ Signal();
+ }
+
+
+TBool TDmac::IsValidHdr(const SDmaDesHdr* aHdr)
+ {
+ return (iHdrPool <= aHdr) && (aHdr < iHdrPool + iMaxDesCount);
+ }
+
+#endif
+
+
+//
+// Internal compat version, used by legacy Fragment()
+//
+TDmaTransferConfig::TDmaTransferConfig(TUint32 aAddr, TUint aFlags, TBool aAddrInc)
+ : iAddr(aAddr),
+ iAddrMode(aAddrInc ? KDmaAddrModePostIncrement : KDmaAddrModeConstant),
+ iElementSize(0),
+ iElementsPerFrame(0),
+ iElementsPerPacket(0),
+ iFramesPerTransfer(0),
+ iElementSkip(0),
+ iFrameSkip(0),
+ iBurstSize(KDmaBurstSizeAny),
+ iFlags(aFlags),
+ iSyncFlags(KDmaSyncAuto),
+ iPslTargetInfo(0),
+ iRepeatCount(0),
+ iDelta(~0u),
+ iReserved(0)
+ {
+ __KTRACE_OPT(KDMA,
+ Kern::Printf("TDmaTransferConfig::TDmaTransferConfig "
+ "aAddr=0x%08X aFlags=0x%08X aAddrInc=%d",
+ aAddr, aFlags, aAddrInc));
+ }
+
+
+//
+// Internal compat version, used by legacy Fragment()
+//
+TDmaTransferArgs::TDmaTransferArgs(TUint32 aSrc, TUint32 aDest, TInt aCount,
+ TUint aFlags, TUint32 aPslInfo)
+ : iSrcConfig(aSrc, RequestFlags2SrcConfigFlags(aFlags), (aFlags & KDmaIncSrc)),
+ iDstConfig(aDest, RequestFlags2DstConfigFlags(aFlags), (aFlags & KDmaIncDest)),
+ iTransferCount(aCount),
+ iGraphicsOps(KDmaGraphicsOpNone),
+ iColour(0),
+ iFlags(0),
+ iChannelPriority(KDmaPriorityNone),
+ iPslRequestInfo(aPslInfo),
+ iChannelCookie(0),
+ iDelta(~0u),
+ iReserved1(0),
+ iReserved2(0)
+ {
+ __KTRACE_OPT(KDMA,
+ Kern::Printf("TDmaTransferArgs::TDmaTransferArgs"));
+ __KTRACE_OPT(KDMA,
+ Kern::Printf(" aSrc=0x%08X aDest=0x%08X aCount=%d aFlags=0x%08X aPslInfo=0x%08X",
+ aSrc, aDest, aCount, aFlags, aPslInfo));
+ }
+
+
+//
+// As DDmaRequest is derived from DBase, the initializations with zero aren't
+// strictly necessary here, but this way it's nicer.
+//
+EXPORT_C DDmaRequest::DDmaRequest(TDmaChannel& aChannel, TCallback aCb,
+ TAny* aCbArg, TInt aMaxTransferSize)
+ : iChannel(aChannel),
+ iCb(aCb),
+ iCbArg(aCbArg),
+ iDmaCb(NULL),
+ iDmaCbArg(NULL),
+ iIsrCb(EFalse),
+ iDesCount(0),
+ iFirstHdr(NULL),
+ iLastHdr(NULL),
+ iSrcDesCount(0),
+ iSrcFirstHdr(NULL),
+ iSrcLastHdr(NULL),
+ iDstDesCount(0),
+ iDstFirstHdr(NULL),
+ iDstLastHdr(NULL),
+ iQueued(EFalse),
+ iMaxTransferSize(aMaxTransferSize),
+ iTotalNumSrcElementsTransferred(0),
+ iTotalNumDstElementsTransferred(0)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::DDmaRequest =0x%08X (old style)", this));
+ iChannel.iReqCount++;
+ __DMA_ASSERTD(0 <= aMaxTransferSize);
+ __DMA_INVARIANT();
+ }
+
+
+//
+// As DDmaRequest is derived from DBase, the initializations with zero aren't
+// strictly necessary here, but this way it's nicer.
+//
+EXPORT_C DDmaRequest::DDmaRequest(TDmaChannel& aChannel, TDmaCallback aDmaCb,
+ TAny* aCbArg, TUint aMaxTransferSize)
+ : iChannel(aChannel),
+ iCb(NULL),
+ iCbArg(NULL),
+ iDmaCb(aDmaCb),
+ iDmaCbArg(aCbArg),
+ iIsrCb(EFalse),
+ iDesCount(0),
+ iFirstHdr(NULL),
+ iLastHdr(NULL),
+ iSrcDesCount(0),
+ iSrcFirstHdr(NULL),
+ iSrcLastHdr(NULL),
+ iDstDesCount(0),
+ iDstFirstHdr(NULL),
+ iDstLastHdr(NULL),
+ iQueued(EFalse),
+ iMaxTransferSize(aMaxTransferSize),
+ iTotalNumSrcElementsTransferred(0),
+ iTotalNumDstElementsTransferred(0)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::DDmaRequest =0x%08X (new style)", this));
+ __e32_atomic_add_ord32(&iChannel.iReqCount, 1);
+ __DMA_INVARIANT();
+ }
+
+
+EXPORT_C DDmaRequest::~DDmaRequest()
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::~DDmaRequest"));
+ __DMA_ASSERTD(!iQueued);
+ __DMA_INVARIANT();
+ if (iChannel.iDmacCaps->iAsymHwDescriptors)
+ {
+ FreeSrcDesList();
+ FreeDstDesList();
+ }
+ else
+ {
+ FreeDesList();
+ }
+ __e32_atomic_add_ord32(&iChannel.iReqCount, TUint32(-1));
+ }
+
+
+EXPORT_C TInt DDmaRequest::Fragment(TUint32 aSrc, TUint32 aDest, TInt aCount,
+ TUint aFlags, TUint32 aPslInfo)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::Fragment thread %O (old style)",
+ &Kern::CurrentThread()));
+
+ __DMA_ASSERTD(aCount > 0);
+
+ TDmaTransferArgs args(aSrc, aDest, aCount, aFlags, aPslInfo);
+
+ return Frag(args);
+ }
+
+
+EXPORT_C TInt DDmaRequest::Fragment(const TDmaTransferArgs& aTransferArgs)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::Fragment thread %O (new style)",
+ &Kern::CurrentThread()));
+
+ // Writable temporary working copy of the transfer arguments.
+ // We need this because we may have to modify some fields before passing it
+ // to the PSL (for example iChannelCookie, iTransferCount,
+ // iDstConfig::iAddr, and iSrcConfig::iAddr).
+ TDmaTransferArgs args(aTransferArgs);
+
+ return Frag(args);
+ }
+
+
+TInt DDmaRequest::CheckTransferConfig(const TDmaTransferConfig& aTarget, TUint aCount) const
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::CheckTransferConfig"));
+
+ if (aTarget.iElementSize != 0)
+ {
+ if ((aCount % aTarget.iElementSize) != 0)
+ {
+ // 2, 7 (These strange numbers refer to some test cases documented
+ // elsewhere - they will be removed eventually.)
+ __KTRACE_OPT(KPANIC,
+ Kern::Printf("Error: ((aCount %% iElementSize) != 0)"));
+ return KErrArgument;
+ }
+ if (aTarget.iElementsPerFrame != 0)
+ {
+ if ((aTarget.iElementSize * aTarget.iElementsPerFrame *
+ aTarget.iFramesPerTransfer) != aCount)
+ {
+ // 3, 8
+ __KTRACE_OPT(KPANIC,
+ Kern::Printf("Error: ((iElementSize * "
+ "iElementsPerFrame * "
+ "iFramesPerTransfer) != aCount)"));
+ return KErrArgument;
+ }
+ }
+ }
+ else
+ {
+ if (aTarget.iElementsPerFrame != 0)
+ {
+ // 4, 9
+ __KTRACE_OPT(KPANIC,
+ Kern::Printf("Error: (iElementsPerFrame != 0)"));
+ return KErrArgument;
+ }
+ if (aTarget.iFramesPerTransfer != 0)
+ {
+ // 5, 10
+ __KTRACE_OPT(KPANIC,
+ Kern::Printf("Error: (iFramesPerTransfer != 0)"));
+ return KErrArgument;
+ }
+ if (aTarget.iElementsPerPacket != 0)
+ {
+ // 6, 11
+ __KTRACE_OPT(KPANIC,
+ Kern::Printf("Error: (iElementsPerPacket != 0)"));
+ return KErrArgument;
+ }
+ }
+ return KErrNone;
+ }
+
+
+TInt DDmaRequest::CheckMemFlags(const TDmaTransferConfig& aTarget) const
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::CheckMemFlags"));
+
+ const TBool mem_target = (aTarget.iFlags & KDmaMemAddr);
+
+ if (mem_target && (aTarget.iFlags & KDmaPhysAddr) && !(aTarget.iFlags & KDmaMemIsContiguous))
+ {
+ // Physical memory address implies contiguous range
+ // 13, 15
+ __KTRACE_OPT(KPANIC, Kern::Printf("Error: mem_target && KDmaPhysAddr && !KDmaMemIsContiguous"));
+ return KErrArgument;
+ }
+ else if ((aTarget.iFlags & KDmaMemIsContiguous) && !mem_target)
+ {
+ // Contiguous range implies memory address
+ // 14, 16
+ __KTRACE_OPT(KPANIC, Kern::Printf("Error: KDmaMemIsContiguous && !mem_target"));
+ return KErrArgument;
+ }
+ return KErrNone;
+ }
+
+
+// Makes sure an element or frame never straddles two DMA subtransfer
+// fragments. This would be a fragmentation error by the PIL.
+//
+TInt DDmaRequest::AdjustFragmentSize(TUint& aFragSize, TUint aElementSize,
+ TUint aFrameSize)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::AdjustFragmentSize FragSize=%d ES=%d FS=%d",
+ aFragSize, aElementSize, aFrameSize));
+
+ TUint rem = 0;
+ TInt r = KErrNone;
+
+ FOREVER
+ {
+ // If an element size is defined, make sure the fragment size is
+ // greater or equal.
+ if (aElementSize)
+ {
+ if (aFragSize < aElementSize)
+ {
+ __KTRACE_OPT(KPANIC, Kern::Printf("Error: aFragSize < aElementSize"));
+ r = KErrArgument;
+ break;
+ }
+ }
+ // If a frame size is defined, make sure the fragment size is greater
+ // or equal.
+ if (aFrameSize)
+ {
+ if (aFragSize < aFrameSize)
+ {
+ __KTRACE_OPT(KPANIC, Kern::Printf("Error: aFragSize < aFrameSize"));
+ r = KErrArgument;
+ break;
+ }
+ }
+ // If a frame size is defined, make sure the fragment ends on a frame
+ // boundary.
+ if (aFrameSize)
+ {
+ rem = aFragSize % aFrameSize;
+ if (rem != 0)
+ {
+ aFragSize -= rem;
+ // 20, 22
+ __KTRACE_OPT(KDMA, Kern::Printf("aFragSize %% aFrameSize != 0 --> aFragSize = %d",
+ aFragSize));
+ // aFragSize has changed, so we have to do all the checks
+ // again.
+ continue;
+ }
+ }
+ // If an element size is defined, make sure the fragment ends on an
+ // element boundary.
+ if (aElementSize)
+ {
+ rem = aFragSize % aElementSize;
+ if (rem != 0)
+ {
+ aFragSize -= rem;
+ // 21, 23
+ __KTRACE_OPT(KDMA, Kern::Printf("aFragSize %% aElementSize != 0 --> aFragSize = %d",
+ aFragSize));
+ // aFragSize has changed, so we have to do all the checks
+ // again.
+ continue;
+ }
+ }
+ // Done - all checks passed. Let's get out.
+ break;
+ }
+
+ return r;
+ }
+
+
+TUint DDmaRequest::GetTransferCount(const TDmaTransferArgs& aTransferArgs) const
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::GetTransferCount"));
+
+ const TDmaTransferConfig& src = aTransferArgs.iSrcConfig;
+#ifdef _DEBUG
+ const TDmaTransferConfig& dst = aTransferArgs.iDstConfig;
+#endif // #ifdef _DEBUG
+
+ TUint count = aTransferArgs.iTransferCount;
+ if (count == 0)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("iTransferCount == 0"));
+ count = src.iElementSize * src.iElementsPerFrame *
+ src.iFramesPerTransfer;
+#ifdef _DEBUG
+ const TUint dst_cnt = dst.iElementSize * dst.iElementsPerFrame *
+ dst.iFramesPerTransfer;
+ if (count != dst_cnt)
+ {
+ // 1
+ __KTRACE_OPT(KPANIC, Kern::Printf("Error: (count != dst_cnt)"));
+ return 0;
+ }
+#endif // #ifdef _DEBUG
+ }
+ else
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("iTransferCount == %d", count));
+#ifdef _DEBUG
+ // Client shouldn't specify contradictory or incomplete things
+ if (CheckTransferConfig(src, count) != KErrNone)
+ {
+ __KTRACE_OPT(KPANIC, Kern::Printf("Error: CheckTransferConfig(src)"));
+ return 0;
+ }
+ if (CheckTransferConfig(dst, count) != KErrNone)
+ {
+ __KTRACE_OPT(KPANIC, Kern::Printf("Error: CheckTransferConfig(dst)"));
+ return 0;
+ }
+#endif // #ifdef _DEBUG
+ }
+ return count;
+ }
+
+
+TUint DDmaRequest::GetMaxTransferlength(const TDmaTransferArgs& aTransferArgs, TUint aCount) const
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::GetMaxTransferlength"));
+
+ const TDmaTransferConfig& src = aTransferArgs.iSrcConfig;
+ const TDmaTransferConfig& dst = aTransferArgs.iDstConfig;
+
+ // Ask the PSL what the maximum length is for a single transfer
+ TUint max_xfer_len = iChannel.MaxTransferLength(src.iFlags, dst.iFlags,
+ aTransferArgs.iPslRequestInfo);
+ if (iMaxTransferSize)
+ {
+ // (User has set a transfer size cap)
+ __KTRACE_OPT(KDMA, Kern::Printf("iMaxTransferSize: %d", iMaxTransferSize));
+ if ((max_xfer_len != 0) && (iMaxTransferSize > max_xfer_len))
+ {
+ // Not really an error, but still...
+ __KTRACE_OPT(KPANIC, Kern::Printf("Warning: iMaxTransferSize > max_xfer_len"));
+ }
+ max_xfer_len = iMaxTransferSize;
+ }
+ else
+ {
+ // (User doesn't care about max transfer size)
+ if (max_xfer_len == 0)
+ {
+ // '0' = no maximum imposed by controller
+ max_xfer_len = aCount;
+ }
+ }
+ __KTRACE_OPT(KDMA, Kern::Printf("max_xfer_len: %d", max_xfer_len));
+
+ // Some sanity checks
+#ifdef _DEBUG
+ if ((max_xfer_len < src.iElementSize) || (max_xfer_len < dst.iElementSize))
+ {
+ // 18
+ __KTRACE_OPT(KPANIC, Kern::Printf("Error: max_xfer_len < iElementSize"));
+ return 0;
+ }
+ if ((max_xfer_len < (src.iElementSize * src.iElementsPerFrame)) ||
+ (max_xfer_len < (dst.iElementSize * dst.iElementsPerFrame)))
+ {
+ // 19
+ __KTRACE_OPT(KPANIC,
+ Kern::Printf("Error: max_xfer_len < (iElementSize * iElementsPerFrame)"));
+ return 0;
+ }
+#endif // #ifdef _DEBUG
+
+ return max_xfer_len;
+ }
+
+
+// Unified internal fragmentation routine, called by both the old and new
+// exported Fragment() functions.
+//
+// Depending on whether the DMAC uses a single or two separate descriptor
+// chains, this function branches into either FragSym() or FragAsym(), and the
+// latter function further into either FragAsymSrc()/FragAsymDst() or
+// FragBalancedAsym().
+//
+TInt DDmaRequest::Frag(TDmaTransferArgs& aTransferArgs)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::Frag"));
+ __DMA_ASSERTD(!iQueued);
+
+ // Transfer count + checks
+ const TUint count = GetTransferCount(aTransferArgs);
+ if (count == 0)
+ {
+ return KErrArgument;
+ }
+
+ // Max transfer length + checks
+ const TUint max_xfer_len = GetMaxTransferlength(aTransferArgs, count);
+ if (max_xfer_len == 0)
+ {
+ return KErrArgument;
+ }
+
+ // ISR callback requested?
+ const TBool isr_cb = (aTransferArgs.iFlags & KDmaRequestCallbackFromIsr);
+ if (isr_cb)
+ {
+ // Requesting an ISR callback w/o supplying one?
+ if (!iDmaCb)
+ {
+ // 12
+ __KTRACE_OPT(KPANIC, Kern::Printf("Error: !iDmaCb"));
+ return KErrArgument;
+ }
+ }
+
+ // Set the channel cookie for the PSL
+ aTransferArgs.iChannelCookie = iChannel.PslId();
+
+ // Client shouldn't specify contradictory or invalid things
+ TInt r = CheckMemFlags(aTransferArgs.iSrcConfig);
+ if (r != KErrNone)
+ {
+ __KTRACE_OPT(KPANIC, Kern::Printf("Error: CheckMemFlags(src)"));
+ return r;
+ }
+ r = CheckMemFlags(aTransferArgs.iDstConfig);
+ if (r != KErrNone)
+ {
+ __KTRACE_OPT(KPANIC, Kern::Printf("Error: CheckMemFlags(dst)"));
+ return r;
+ }
+
+ // Now the actual fragmentation
+ if (iChannel.iDmacCaps->iAsymHwDescriptors)
+ {
+ r = FragAsym(aTransferArgs, count, max_xfer_len);
+ }
+ else
+ {
+ r = FragSym(aTransferArgs, count, max_xfer_len);
+ }
+
+ if (r == KErrNone)
+ {
+ iIsrCb = isr_cb;
+ }
+
+ __DMA_INVARIANT();
+ return r;
+ };
+
+
+TInt DDmaRequest::FragSym(TDmaTransferArgs& aTransferArgs, TUint aCount,
+ TUint aMaxTransferLen)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::FragSym"));
+
+ TDmaTransferConfig& src = aTransferArgs.iSrcConfig;
+ TDmaTransferConfig& dst = aTransferArgs.iDstConfig;
+ const TBool mem_src = (src.iFlags & KDmaMemAddr);
+ const TBool mem_dst = (dst.iFlags & KDmaMemAddr);
+
+ const TUint align_mask_src = iChannel.AddressAlignMask(src.iFlags,
+ src.iElementSize,
+ aTransferArgs.iPslRequestInfo);
+ __KTRACE_OPT(KDMA, Kern::Printf("align_mask_src: 0x%x", align_mask_src));
+ const TUint align_mask_dst = iChannel.AddressAlignMask(dst.iFlags,
+ dst.iElementSize,
+ aTransferArgs.iPslRequestInfo);
+ __KTRACE_OPT(KDMA, Kern::Printf("align_mask_dst: 0x%x", align_mask_dst));
+
+ // Memory buffers must satisfy alignment constraint
+ __DMA_ASSERTD(!mem_src || ((src.iAddr & align_mask_src) == 0));
+ __DMA_ASSERTD(!mem_dst || ((dst.iAddr & align_mask_dst) == 0));
+
+ // Max aligned length is used to make sure the beginnings of subtransfers
+ // (i.e. fragments) are correctly aligned.
+ const TUint max_aligned_len = (aMaxTransferLen &
+ ~(_Max(align_mask_src, align_mask_dst)));
+ __KTRACE_OPT(KDMA, Kern::Printf("max_aligned_len: %d", max_aligned_len));
+ // Client and PSL sane?
+ __DMA_ASSERTD(max_aligned_len > 0);
+
+ if (mem_src && mem_dst &&
+ align_mask_src && align_mask_dst &&
+ (align_mask_src != align_mask_dst) &&
+ (!(src.iFlags & KDmaMemIsContiguous) || !(dst.iFlags & KDmaMemIsContiguous)))
+ {
+ // We don't support transfers which satisfy ALL of the following conditions:
+ // 1) from memory to memory,
+ // 2) both sides have address alignment requirements,
+ // 3) those alignment requirements are not the same,
+ // 4) the memory is non-contiguous on at least one end.
+ //
+ // [A 5th condition is that the channel doesn't support fully
+ // asymmetric h/w descriptor lists,
+ // i.e. TDmaChannel::DmacCaps::iAsymHwDescriptors is reported as EFalse
+ // or iBalancedAsymSegments as ETrue. Hence this check is done in
+ // FragSym() and FragBalancedAsym() but not in FragAsym().]
+ //
+ // The reason for this is that fragmentation could be impossible. The
+ // memory layout (page break) on the side with the less stringent
+ // alignment requirement can result in a misaligned target address on
+ // the other side.
+ //
+ // Here is an example:
+ //
+ // src.iAddr = 3964 (0x0F7C), non-contiguous,
+ // align_mask_src = 1 (alignment = 2 bytes)
+ // dst.iAddr = 16384 (0x4000), contiguous,
+ // align_mask_dst = 7 (alignment = 8 bytes)
+ // count = max_xfer_len = 135 bytes
+ // => max_aligned_len = 128 bytes
+ //
+ // Now, suppose MaxPhysSize() returns 132 bytes because src has 132
+ // contiguous bytes to the end of its current mem page.
+ // Trying to fragment this leads to:
+ //
+ // frag_1 = 128 bytes: src reads from 3964 (0x0F7C),
+ // dst writes to 16384 (0x4000).
+ // (Fragment 1 uses the max_aligned_len instead of 132 bytes because
+ // otherwise the next fragment would start for the destination at
+ // dst.iAddr + 132 = 16516 (0x4084), which is not 8-byte aligned.)
+ //
+ // frag_2 = 4 bytes: src reads from 4092 (0x0FFC),
+ // dst writes to 16512 (0x4080).
+ // (Fragment 2 uses just 4 bytes instead of the remaining 7 bytes
+ // because there is a memory page break on the source side after 4 bytes.)
+ //
+ // frag_3 = 3 bytes: src reads from 4096 (0x1000),
+ // dst writes to 16516 (0x4084).
+ //
+ // And there's the problem: the start address of frag_3 is going to be
+ // misaligned for the destination side - it's not 8-byte aligned!
+ //
+ // 17
+ __KTRACE_OPT(KPANIC, Kern::Printf("Error: Different alignments for src & dst"
+ " + non-contiguous target(s)"));
+ return KErrArgument;
+ }
+
+ TInt r;
+ // Revert any previous fragmentation attempt
+ FreeDesList();
+ do
+ {
+ // Allocate fragment
+ r = ExpandDesList(/*1*/);
+ if (r != KErrNone)
+ {
+ break;
+ }
+ // Compute fragment size
+ TUint c = _Min(aMaxTransferLen, aCount);
+ __KTRACE_OPT(KDMA, Kern::Printf("c = _Min(aMaxTransferLen, aCount) = %d", c));
+
+ // SRC
+ if (mem_src && !(src.iFlags & KDmaMemIsContiguous))
+ {
+ c = MaxPhysSize(src.iAddr, c);
+ __KTRACE_OPT(KDMA, Kern::Printf("c = MaxPhysSize(src.iAddr, c) = %d", c));
+ }
+
+ // DST
+ if (mem_dst && !(dst.iFlags & KDmaMemIsContiguous))
+ {
+ c = MaxPhysSize(dst.iAddr, c);
+ __KTRACE_OPT(KDMA, Kern::Printf("c = MaxPhysSize(dst.iAddr, c) = %d", c));
+ }
+
+ // SRC & DST
+ if ((mem_src || mem_dst) && (c < aCount) && (c > max_aligned_len))
+ {
+ // This is not the last fragment of a transfer to/from memory.
+ // We must round down the fragment size so the next one is
+ // correctly aligned.
+ c = max_aligned_len;
+ __KTRACE_OPT(KDMA, Kern::Printf("c = max_aligned_len = %d", c));
+ //
+ // But can this condition actually occur if src and dst are
+ // properly aligned to start with?
+ //
+ // If we disallow unequal alignment requirements in connection with
+ // non-contiguous memory buffers (see the long comment above in
+ // this function for why) and if both target addresses are
+ // correctly aligned at the beginning of the transfer then it
+ // doesn't seem possible to end up with a fragment which is not
+ // quite the total remaining size (c < aCount) but still larger
+ // than the greatest aligned length (c > max_aligned_len).
+ //
+ // That's because address alignment values are always a power of
+ // two (at least that's what we assume - otherwise
+ // AddressAlignMask() doesn't work), and memory page sizes are also
+ // always a power of two and hence a multiple of the alignment
+ // value (as long as the alignment is not greater than the page
+ // size, which seems a reasonable assumption regardless of the
+ // actual page size). So if we start properly aligned anywhere in a
+ // memory page then the number of bytes to the end of that page is
+ // always a multiple of the aligment value - there's no remainder.
+ //
+ // So let's see if we ever hit this assertion:
+ Kern::Printf("Unexpected: (mem_src || mem_dst) && (c < aCount) && (c > max_aligned_len)");
+ __DMA_ASSERTA(EFalse);
+ }
+
+ // If this is not the last fragment...
+ if (c < aCount)
+ {
+ const TUint es_src = src.iElementSize;
+ const TUint es_dst = dst.iElementSize;
+ const TUint fs_src = es_src * src.iElementsPerFrame;
+ const TUint fs_dst = es_dst * dst.iElementsPerFrame;
+ TUint c_prev;
+ do
+ {
+ c_prev = c;
+ // If fs_src is !0 then es_src must be !0 as well (see
+ // CheckTransferConfig).
+ if (es_src)
+ {
+ r = AdjustFragmentSize(c, es_src, fs_src);
+ if (r != KErrNone)
+ {
+ break; // while (c != c_prev);
+ }
+ }
+ // If fs_dst is !0 then es_dst must be !0 as well (see
+ // CheckTransferConfig).
+ if (es_dst)
+ {
+ r = AdjustFragmentSize(c, es_dst, fs_dst);
+ if (r != KErrNone)
+ {
+ break; // while (c != c_prev);
+ }
+ }
+ } while (c != c_prev);
+ if (r != KErrNone)
+ {
+ break; // while (aCount > 0);
+ }
+ }
+
+ // Set transfer count for the PSL
+ aTransferArgs.iTransferCount = c;
+ __KTRACE_OPT(KDMA, Kern::Printf("this fragm.: %d (0x%x) total remain.: %d (0x%x)",
+ c, c, aCount, aCount));
+ // Initialise fragment
+ r = iChannel.iController->InitDes(*iLastHdr, aTransferArgs);
+ if (r != KErrNone)
+ {
+ break;
+ }
+ // Update for next iteration
+ aCount -= c;
+ if (mem_src)
+ {
+ src.iAddr += c;
+ }
+ if (mem_dst)
+ {
+ dst.iAddr += c;
+ }
+ } while (aCount > 0);
+
+ if (r != KErrNone)
+ {
+ FreeDesList();
+ }
+ return r;
+ }
+
+
+TInt DDmaRequest::FragAsym(TDmaTransferArgs& aTransferArgs, TUint aCount,
+ TUint aMaxTransferLen)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::FragAsym"));
+
+ TInt r;
+ if (iChannel.iDmacCaps->iBalancedAsymSegments)
+ {
+ r = FragBalancedAsym(aTransferArgs, aCount, aMaxTransferLen);
+ if (r != KErrNone)
+ {
+ FreeSrcDesList();
+ FreeDstDesList();
+ }
+ return r;
+ }
+ r = FragAsymSrc(aTransferArgs, aCount, aMaxTransferLen);
+ if (r != KErrNone)
+ {
+ FreeSrcDesList();
+ return r;
+ }
+ r = FragAsymDst(aTransferArgs, aCount, aMaxTransferLen);
+ if (r != KErrNone)
+ {
+ FreeSrcDesList();
+ FreeDstDesList();
+ }
+ return r;
+ }
+
+
+TInt DDmaRequest::FragAsymSrc(TDmaTransferArgs& aTransferArgs, TUint aCount,
+ TUint aMaxTransferLen)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::FragAsymSrc"));
+
+ TDmaTransferConfig& src = aTransferArgs.iSrcConfig;
+ const TBool mem_src = (src.iFlags & KDmaMemAddr);
+
+ const TUint align_mask = iChannel.AddressAlignMask(src.iFlags,
+ src.iElementSize,
+ aTransferArgs.iPslRequestInfo);
+ __KTRACE_OPT(KDMA, Kern::Printf("align_mask: 0x%x", align_mask));
+
+ // Memory buffers must satisfy alignment constraint
+ __DMA_ASSERTD(!mem_src || ((src.iAddr & align_mask) == 0));
+
+ // Max aligned length is used to make sure the beginnings of subtransfers
+ // (i.e. fragments) are correctly aligned.
+ const TUint max_aligned_len = (aMaxTransferLen & ~align_mask);
+ __KTRACE_OPT(KDMA, Kern::Printf("max_aligned_len: %d", max_aligned_len));
+ // Client and PSL sane?
+ __DMA_ASSERTD(max_aligned_len > 0);
+
+ TInt r;
+ // Revert any previous fragmentation attempt
+ FreeSrcDesList();
+ do
+ {
+ // Allocate fragment
+ r = ExpandSrcDesList(/*1*/);
+ if (r != KErrNone)
+ {
+ break;
+ }
+ // Compute fragment size
+ TUint c = _Min(aMaxTransferLen, aCount);
+ __KTRACE_OPT(KDMA, Kern::Printf("c = _Min(aMaxTransferLen, aCount) = %d", c));
+
+ if (mem_src && !(src.iFlags & KDmaMemIsContiguous))
+ {
+ c = MaxPhysSize(src.iAddr, c);
+ __KTRACE_OPT(KDMA, Kern::Printf("c = MaxPhysSize(src.iAddr, c) = %d", c));
+ }
+
+ if (mem_src && (c < aCount) && (c > max_aligned_len))
+ {
+ // This is not the last fragment of a transfer from memory.
+ // We must round down the fragment size so the next one is
+ // correctly aligned.
+ __KTRACE_OPT(KDMA, Kern::Printf("c = max_aligned_len = %d", c));
+ //
+ // But can this condition actually occur if src is properly aligned
+ // to start with?
+ //
+ // If the target address is correctly aligned at the beginning of
+ // the transfer then it doesn't seem possible to end up with a
+ // fragment which is not quite the total remaining size (c <
+ // aCount) but still larger than the greatest aligned length (c >
+ // max_aligned_len).
+ //
+ // That's because address alignment values are always a power of
+ // two (at least that's what we assume - otherwise
+ // AddressAlignMask() doesn't work), and memory page sizes are also
+ // always a power of two and hence a multiple of the alignment
+ // value (as long as the alignment is not greater than the page
+ // size, which seems a reasonable assumption regardless of the
+ // actual page size). So if we start properly aligned anywhere in a
+ // memory page then the number of bytes to the end of that page is
+ // always a multiple of the aligment value - there's no remainder.
+ //
+ // So let's see if we ever hit this assertion:
+ Kern::Printf("Unexpected: mem_src && (c < aCount) && (c > max_aligned_len)");
+ __DMA_ASSERTA(EFalse);
+ }
+
+ // If this is not the last fragment...
+ if (c < aCount)
+ {
+ const TUint es = src.iElementSize;
+ const TUint fs = es * src.iElementsPerFrame;
+ // If fs is !0 then es must be !0 as well (see
+ // CheckTransferConfig).
+ if (es)
+ {
+ r = AdjustFragmentSize(c, es, fs);
+ if (r != KErrNone)
+ {
+ break; // while (aCount > 0);
+ }
+ }
+ }
+
+ // Set transfer count for the PSL
+ aTransferArgs.iTransferCount = c;
+ __KTRACE_OPT(KDMA, Kern::Printf("this fragm.: %d (0x%x) total remain.: %d (0x%x)",
+ c, c, aCount, aCount));
+ // Initialise fragment
+ r = iChannel.iController->InitSrcHwDes(*iSrcLastHdr, aTransferArgs);
+ if (r != KErrNone)
+ {
+ break;
+ }
+ // Update for next iteration
+ aCount -= c;
+ if (mem_src)
+ {
+ src.iAddr += c;
+ }
+ } while (aCount > 0);
+
+ return r;
+ }
+
+
+TInt DDmaRequest::FragAsymDst(TDmaTransferArgs& aTransferArgs, TUint aCount,
+ TUint aMaxTransferLen)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::FragAsymDst"));
+
+ TDmaTransferConfig& dst = aTransferArgs.iDstConfig;
+ const TBool mem_dst = (dst.iFlags & KDmaMemAddr);
+
+ const TUint align_mask = iChannel.AddressAlignMask(dst.iFlags,
+ dst.iElementSize,
+ aTransferArgs.iPslRequestInfo);
+ __KTRACE_OPT(KDMA, Kern::Printf("align_mask: 0x%x", align_mask));
+
+ // Memory buffers must satisfy alignment constraint
+ __DMA_ASSERTD(!mem_dst || ((dst.iAddr & align_mask) == 0));
+
+ // Max aligned length is used to make sure the beginnings of subtransfers
+ // (i.e. fragments) are correctly aligned.
+ const TUint max_aligned_len = (aMaxTransferLen & ~align_mask);
+ __KTRACE_OPT(KDMA, Kern::Printf("max_aligned_len: %d", max_aligned_len));
+ // Client and PSL sane?
+ __DMA_ASSERTD(max_aligned_len > 0);
+
+ TInt r;
+ // Revert any previous fragmentation attempt
+ FreeDstDesList();
+ do
+ {
+ // Allocate fragment
+ r = ExpandDstDesList(/*1*/);
+ if (r != KErrNone)
+ {
+ break;
+ }
+ // Compute fragment size
+ TUint c = _Min(aMaxTransferLen, aCount);
+ __KTRACE_OPT(KDMA, Kern::Printf("c = _Min(aMaxTransferLen, aCount) = %d", c));
+
+ if (mem_dst && !(dst.iFlags & KDmaMemIsContiguous))
+ {
+ c = MaxPhysSize(dst.iAddr, c);
+ __KTRACE_OPT(KDMA, Kern::Printf("c = MaxPhysSize(dst.iAddr, c) = %d", c));
+ }
+
+ if (mem_dst && (c < aCount) && (c > max_aligned_len))
+ {
+ // This is not the last fragment of a transfer to memory.
+ // We must round down the fragment size so the next one is
+ // correctly aligned.
+ __KTRACE_OPT(KDMA, Kern::Printf("c = max_aligned_len = %d", c));
+ //
+ // But can this condition actually occur if dst is properly aligned
+ // to start with?
+ //
+ // If the target address is correctly aligned at the beginning of
+ // the transfer then it doesn't seem possible to end up with a
+ // fragment which is not quite the total remaining size (c <
+ // aCount) but still larger than the greatest aligned length (c >
+ // max_aligned_len).
+ //
+ // That's because address alignment values are always a power of
+ // two (at least that's what we assume - otherwise
+ // AddressAlignMask() doesn't work), and memory page sizes are also
+ // always a power of two and hence a multiple of the alignment
+ // value (as long as the alignment is not greater than the page
+ // size, which seems a reasonable assumption regardless of the
+ // actual page size). So if we start properly aligned anywhere in a
+ // memory page then the number of bytes to the end of that page is
+ // always a multiple of the aligment value - there's no remainder.
+ //
+ // So let's see if we ever hit this assertion:
+ Kern::Printf("Unexpected: mem_dst && (c < aCount) && (c > max_aligned_len)");
+ __DMA_ASSERTA(EFalse);
+ }
+
+ // If this is not the last fragment...
+ if (c < aCount)
+ {
+ const TUint es = dst.iElementSize;
+ const TUint fs = es * dst.iElementsPerFrame;
+ // If fs is !0 then es must be !0 as well (see
+ // CheckTransferConfig).
+ if (es)
+ {
+ r = AdjustFragmentSize(c, es, fs);
+ if (r != KErrNone)
+ {
+ break; // while (aCount > 0);
+ }
+ }
+ }
+
+ // Set transfer count for the PSL
+ aTransferArgs.iTransferCount = c;
+ __KTRACE_OPT(KDMA, Kern::Printf("this fragm.: %d (0x%x) total remain.: %d (0x%x)",
+ c, c, aCount, aCount));
+ // Initialise fragment
+ r = iChannel.iController->InitDstHwDes(*iDstLastHdr, aTransferArgs);
+ if (r != KErrNone)
+ {
+ break;
+ }
+ // Update for next iteration
+ aCount -= c;
+ if (mem_dst)
+ {
+ dst.iAddr += c;
+ }
+ }
+ while (aCount > 0);
+
+ return r;
+ }
+
+
+TInt DDmaRequest::FragBalancedAsym(TDmaTransferArgs& aTransferArgs, TUint aCount,
+ TUint aMaxTransferLen)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::FragBalancedAsym"));
+
+ TDmaTransferConfig& src = aTransferArgs.iSrcConfig;
+ TDmaTransferConfig& dst = aTransferArgs.iDstConfig;
+ const TBool mem_src = (src.iFlags & KDmaMemAddr);
+ const TBool mem_dst = (dst.iFlags & KDmaMemAddr);
+
+ const TUint align_mask_src = iChannel.AddressAlignMask(src.iFlags,
+ src.iElementSize,
+ aTransferArgs.iPslRequestInfo);
+ __KTRACE_OPT(KDMA, Kern::Printf("align_mask_src: 0x%x", align_mask_src));
+ const TUint align_mask_dst = iChannel.AddressAlignMask(dst.iFlags,
+ dst.iElementSize,
+ aTransferArgs.iPslRequestInfo);
+ __KTRACE_OPT(KDMA, Kern::Printf("align_mask_dst: 0x%x", align_mask_dst));
+
+ // Memory buffers must satisfy alignment constraint
+ __DMA_ASSERTD(!mem_src || ((src.iAddr & align_mask_src) == 0));
+ __DMA_ASSERTD(!mem_dst || ((dst.iAddr & align_mask_dst) == 0));
+
+ // Max aligned length is used to make sure the beginnings of subtransfers
+ // (i.e. fragments) are correctly aligned.
+ const TUint max_aligned_len = (aMaxTransferLen &
+ ~(_Max(align_mask_src, align_mask_dst)));
+ __KTRACE_OPT(KDMA, Kern::Printf("max_aligned_len: %d", max_aligned_len));
+ // Client and PSL sane?
+ __DMA_ASSERTD(max_aligned_len > 0);
+
+ if (mem_src && mem_dst &&
+ align_mask_src && align_mask_dst &&
+ (align_mask_src != align_mask_dst) &&
+ (!(src.iFlags & KDmaMemIsContiguous) || !(dst.iFlags & KDmaMemIsContiguous)))
+ {
+ // We don't support transfers which satisfy ALL of the following conditions:
+ // 1) from memory to memory,
+ // 2) both sides have address alignment requirements,
+ // 3) those alignment requirements are not the same,
+ // 4) the memory is non-contiguous on at least one end.
+ //
+ // [A 5th condition is that the channel doesn't support fully
+ // asymmetric h/w descriptor lists,
+ // i.e. TDmaChannel::DmacCaps::iAsymHwDescriptors is reported as EFalse
+ // or iBalancedAsymSegments as ETrue. Hence this check is done in
+ // FragSym() and FragBalancedAsym() but not in FragAsym().]
+ //
+ // The reason for this is that fragmentation could be impossible. The
+ // memory layout (page break) on the side with the less stringent
+ // alignment requirement can result in a misaligned target address on
+ // the other side.
+ //
+ // Here is an example:
+ //
+ // src.iAddr = 3964 (0x0F7C), non-contiguous,
+ // align_mask_src = 1 (alignment = 2 bytes)
+ // dst.iAddr = 16384 (0x4000), contiguous,
+ // align_mask_dst = 7 (alignment = 8 bytes)
+ // count = max_xfer_len = 135 bytes
+ // => max_aligned_len = 128 bytes
+ //
+ // Now, suppose MaxPhysSize() returns 132 bytes because src has 132
+ // contiguous bytes to the end of its current mem page.
+ // Trying to fragment this leads to:
+ //
+ // frag_1 = 128 bytes: src reads from 3964 (0x0F7C),
+ // dst writes to 16384 (0x4000).
+ // (Fragment 1 uses the max_aligned_len instead of 132 bytes because
+ // otherwise the next fragment would start for the destination at
+ // dst.iAddr + 132 = 16516 (0x4084), which is not 8-byte aligned.)
+ //
+ // frag_2 = 4 bytes: src reads from 4092 (0x0FFC),
+ // dst writes to 16512 (0x4080).
+ // (Fragment 2 uses just 4 bytes instead of the remaining 7 bytes
+ // because there is a memory page break on the source side after 4 bytes.)
+ //
+ // frag_3 = 3 bytes: src reads from 4096 (0x1000),
+ // dst writes to 16516 (0x4084).
+ //
+ // And there's the problem: the start address of frag_3 is going to be
+ // misaligned for the destination side - it's not 8-byte aligned!
+ //
+ __KTRACE_OPT(KPANIC, Kern::Printf("Error: Different alignments for src & dst"
+ " + non-contiguous target(s)"));
+ return KErrArgument;
+ }
+
+ TInt r;
+ // Revert any previous fragmentation attempt
+ FreeSrcDesList();
+ FreeDstDesList();
+ __DMA_ASSERTD(iSrcDesCount == iDstDesCount);
+ do
+ {
+ // Allocate fragment
+ r = ExpandSrcDesList(/*1*/);
+ if (r != KErrNone)
+ {
+ break;
+ }
+ r = ExpandDstDesList(/*1*/);
+ if (r != KErrNone)
+ {
+ break;
+ }
+ __DMA_ASSERTD(iSrcDesCount == iDstDesCount);
+ // Compute fragment size
+ TUint c = _Min(aMaxTransferLen, aCount);
+ __KTRACE_OPT(KDMA, Kern::Printf("c = _Min(aMaxTransferLen, aCount) = %d", c));
+
+ // SRC
+ if (mem_src && !(src.iFlags & KDmaMemIsContiguous))
+ {
+ c = MaxPhysSize(src.iAddr, c);
+ __KTRACE_OPT(KDMA, Kern::Printf("c = MaxPhysSize(src.iAddr, c) = %d", c));
+ }
+
+ // DST
+ if (mem_dst && !(dst.iFlags & KDmaMemIsContiguous))
+ {
+ c = MaxPhysSize(dst.iAddr, c);
+ __KTRACE_OPT(KDMA, Kern::Printf("c = MaxPhysSize(dst.iAddr, c) = %d", c));
+ }
+
+ // SRC & DST
+ if ((mem_src || mem_dst) && (c < aCount) && (c > max_aligned_len))
+ {
+ // This is not the last fragment of a transfer to/from memory.
+ // We must round down the fragment size so the next one is
+ // correctly aligned.
+ c = max_aligned_len;
+ __KTRACE_OPT(KDMA, Kern::Printf("c = max_aligned_len = %d", c));
+ //
+ // But can this condition actually occur if src and dst are
+ // properly aligned to start with?
+ //
+ // If we disallow unequal alignment requirements in connection with
+ // non-contiguous memory buffers (see the long comment above in
+ // this function for why) and if both target addresses are
+ // correctly aligned at the beginning of the transfer then it
+ // doesn't seem possible to end up with a fragment which is not
+ // quite the total remaining size (c < aCount) but still larger
+ // than the greatest aligned length (c > max_aligned_len).
+ //
+ // That's because address alignment values are always a power of
+ // two (at least that's what we assume - otherwise
+ // AddressAlignMask() doesn't work), and memory page sizes are also
+ // always a power of two and hence a multiple of the alignment
+ // value (as long as the alignment is not greater than the page
+ // size, which seems a reasonable assumption regardless of the
+ // actual page size). So if we start properly aligned anywhere in a
+ // memory page then the number of bytes to the end of that page is
+ // always a multiple of the aligment value - there's no remainder.
+ //
+ // So let's see if we ever hit this assertion:
+ Kern::Printf("Unexpected: (mem_src || mem_dst) && (c < aCount) && (c > max_aligned_len)");
+ __DMA_ASSERTA(EFalse);
+ }
+
+ // If this is not the last fragment...
+ if (c < aCount)
+ {
+ const TUint es_src = src.iElementSize;
+ const TUint es_dst = dst.iElementSize;
+ const TUint fs_src = es_src * src.iElementsPerFrame;
+ const TUint fs_dst = es_dst * dst.iElementsPerFrame;
+ TUint c_prev;
+ do
+ {
+ c_prev = c;
+ // If fs_src is !0 then es_src must be !0 as well (see
+ // CheckTransferConfig).
+ if (es_src)
+ {
+ r = AdjustFragmentSize(c, es_src, fs_src);
+ if (r != KErrNone)
+ {
+ break; // while (c != c_prev);
+ }
+ }
+ // If fs_dst is !0 then es_dst must be !0 as well (see
+ // CheckTransferConfig).
+ if (es_dst)
+ {
+ r = AdjustFragmentSize(c, es_dst, fs_dst);
+ if (r != KErrNone)
+ {
+ break; // while (c != c_prev);
+ }
+ }
+ } while (c != c_prev);
+ if (r != KErrNone)
+ {
+ break; // while (aCount > 0);
+ }
+ }
+
+ // Set transfer count for the PSL
+ aTransferArgs.iTransferCount = c;
+ __KTRACE_OPT(KDMA, Kern::Printf("this fragm.: %d (0x%x) total remain.: %d (0x%x)",
+ c, c, aCount, aCount));
+ // Initialise SRC fragment
+ r = iChannel.iController->InitSrcHwDes(*iSrcLastHdr, aTransferArgs);
+ if (r != KErrNone)
+ {
+ break;
+ }
+ // Initialise DST fragment
+ r = iChannel.iController->InitDstHwDes(*iDstLastHdr, aTransferArgs);
+ if (r != KErrNone)
+ {
+ break;
+ }
+ // Update for next iteration
+ aCount -= c;
+ if (mem_src)
+ {
+ src.iAddr += c;
+ }
+ if (mem_dst)
+ {
+ dst.iAddr += c;
+ }
+ }
+ while (aCount > 0);
+
+ return r;
+ }
+
+
+EXPORT_C TInt DDmaRequest::Queue()
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::Queue thread %O", &Kern::CurrentThread()));
+ // Not configured? Call Fragment() first!
+ if (iChannel.iDmacCaps->iAsymHwDescriptors)
+ {
+ __DMA_ASSERTD((iSrcDesCount > 0) && (iDstDesCount > 0));
+ }
+ else
+ {
+ __DMA_ASSERTD(iDesCount > 0);
+ }
+ __DMA_ASSERTD(!iQueued);
+
+ // Append request to queue and link new descriptor list to existing one.
+ iChannel.Wait();
+
+ TUint32 req_count = iChannel.iQueuedRequests++;
+ if (iChannel.iCallQueuedRequestFn)
+ {
+ if (req_count == 0)
+ {
+ iChannel.Signal();
+ iChannel.QueuedRequestCountChanged();
+ iChannel.Wait();
+ }
+ }
+
+ TInt r = KErrGeneral;
+ const TBool ch_isr_cb = __e32_atomic_load_acq32(&iChannel.iIsrCbRequest);
+ if (ch_isr_cb)
+ {
+ // Client mustn't try to queue any new request while one with an ISR
+ // callback is already queued on this channel. This is to make sure
+ // that the channel's Transfer() function is not called by both the ISR
+ // and the client thread at the same time.
+ __KTRACE_OPT(KPANIC, Kern::Printf("An ISR cb request exists - not queueing"));
+ // Undo the request count increment...
+ req_count = --iChannel.iQueuedRequests;
+ __DMA_INVARIANT();
+ iChannel.Signal();
+ if (iChannel.iCallQueuedRequestFn)
+ {
+ if (req_count == 0)
+ {
+ iChannel.QueuedRequestCountChanged();
+ }
+ }
+ }
+ else if (iIsrCb && !iChannel.IsQueueEmpty())
+ {
+ // Client mustn't try to queue an ISR callback request whilst any
+ // others are still queued on this channel. This is to make sure that
+ // the ISR callback doesn't get executed together with the DFC(s) of
+ // any previous request(s).
+ __KTRACE_OPT(KPANIC, Kern::Printf("Request queue not empty - not queueing"));
+ // Undo the request count increment...
+ req_count = --iChannel.iQueuedRequests;
+ __DMA_INVARIANT();
+ iChannel.Signal();
+ if (iChannel.iCallQueuedRequestFn)
+ {
+ if (req_count == 0)
+ {
+ iChannel.QueuedRequestCountChanged();
+ }
+ }
+ }
+ else if (iChannel.iIsrDfc & (TUint32)TDmaChannel::KCancelFlagMask)
+ {
+ __KTRACE_OPT(KPANIC, Kern::Printf("Channel requests cancelled - not queueing"));
+ // Someone is cancelling all requests - undo the request count increment...
+ req_count = --iChannel.iQueuedRequests;
+ __DMA_INVARIANT();
+ iChannel.Signal();
+ if (iChannel.iCallQueuedRequestFn)
+ {
+ if (req_count == 0)
+ {
+ iChannel.QueuedRequestCountChanged();
+ }
+ }
+ }
+ else
+ {
+ iQueued = ETrue;
+ iChannel.iReqQ.Add(&iLink);
+ iChannel.SetNullPtr(*this);
+ if (iIsrCb)
+ {
+ // Since we've made sure that there is no other request in the
+ // queue before this, the only thing of relevance is the channel
+ // DFC which might yet have to complete for the previous request,
+ // and this function might indeed have been called from there via
+ // the client callback. This should be all right though as once
+ // we've set the following flag no further Queue()'s will be
+ // possible.
+ __e32_atomic_store_rel32(&iChannel.iIsrCbRequest, ETrue);
+ }
+ iChannel.DoQueue(*this);
+ r = KErrNone;
+ __DMA_INVARIANT();
+ iChannel.Signal();
+ }
+
+ return r;
+ }
+
+
+EXPORT_C TInt DDmaRequest::ExpandDesList(TInt aCount)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::ExpandDesList aCount=%d", aCount));
+ return ExpandDesList(aCount, iDesCount, iFirstHdr, iLastHdr);
+ }
+
+
+EXPORT_C TInt DDmaRequest::ExpandSrcDesList(TInt aCount)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::ExpandSrcDesList"));
+ return ExpandDesList(aCount, iSrcDesCount, iSrcFirstHdr, iSrcLastHdr);
+ }
+
+
+EXPORT_C TInt DDmaRequest::ExpandDstDesList(TInt aCount)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::ExpandDstDesList"));
+ return ExpandDesList(aCount, iDstDesCount, iDstFirstHdr, iDstLastHdr);
+ }
+
+
+TInt DDmaRequest::ExpandDesList(TInt aCount, TInt& aDesCount,
+ SDmaDesHdr*& aFirstHdr,
+ SDmaDesHdr*& aLastHdr)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::ExpandDesList"));
+ __DMA_ASSERTD(!iQueued);
+ __DMA_ASSERTD(aCount > 0);
+
+ if (aCount > iChannel.iAvailDesCount)
+ {
+ return KErrTooBig;
+ }
+
+ iChannel.iAvailDesCount -= aCount;
+ aDesCount += aCount;
+
+ TDmac& c = *(iChannel.iController);
+ c.Wait();
+
+ if (aFirstHdr == NULL)
+ {
+ // Handle an empty list specially to simplify the following loop
+ aFirstHdr = aLastHdr = c.iFreeHdr;
+ c.iFreeHdr = c.iFreeHdr->iNext;
+ --aCount;
+ }
+ else
+ {
+ aLastHdr->iNext = c.iFreeHdr;
+ }
+
+ // Remove as many descriptors and headers from the free pool as necessary
+ // and ensure hardware descriptors are chained together.
+ while (aCount-- > 0)
+ {
+ __DMA_ASSERTD(c.iFreeHdr != NULL);
+ if (c.iCapsHwDes)
+ {
+ c.ChainHwDes(*aLastHdr, *(c.iFreeHdr));
+ }
+ aLastHdr = c.iFreeHdr;
+ c.iFreeHdr = c.iFreeHdr->iNext;
+ }
+
+ c.Signal();
+
+ aLastHdr->iNext = NULL;
+
+ __DMA_INVARIANT();
+ return KErrNone;
+ }
+
+
+EXPORT_C void DDmaRequest::FreeDesList()
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::FreeDesList"));
+ FreeDesList(iDesCount, iFirstHdr, iLastHdr);
+ }
+
+
+EXPORT_C void DDmaRequest::FreeSrcDesList()
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::FreeSrcDesList"));
+ FreeDesList(iSrcDesCount, iSrcFirstHdr, iSrcLastHdr);
+ }
+
+
+EXPORT_C void DDmaRequest::FreeDstDesList()
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::FreeDstDesList"));
+ FreeDesList(iDstDesCount, iDstFirstHdr, iDstLastHdr);
+ }
+
+
+void DDmaRequest::FreeDesList(TInt& aDesCount, SDmaDesHdr*& aFirstHdr, SDmaDesHdr*& aLastHdr)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::FreeDesList count=%d", aDesCount));
+ __DMA_ASSERTD(!iQueued);
+
+ if (aDesCount > 0)
+ {
+ iChannel.iAvailDesCount += aDesCount;
+ TDmac& c = *(iChannel.iController);
+ const SDmaDesHdr* hdr = aFirstHdr;
+ while (hdr)
+ {
+ __DMA_ASSERTD(c.IsValidHdr(hdr));
+
+ // This (potential) PSL call doesn't follow the "overhead
+ // principle", and something should be done about this.
+ c.ClearHwDes(*hdr);
+ hdr = hdr->iNext;
+ };
+
+ c.Wait();
+ __DMA_ASSERTD(c.IsValidHdr(c.iFreeHdr));
+ aLastHdr->iNext = c.iFreeHdr;
+ c.iFreeHdr = aFirstHdr;
+ c.Signal();
+
+ aFirstHdr = aLastHdr = NULL;
+ aDesCount = 0;
+ }
+ }
+
+
+EXPORT_C void DDmaRequest::EnableSrcElementCounting(TBool /*aResetElementCount*/)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::EnableSrcElementCounting"));
+
+ // Not yet implemented.
+ return;
+ }
+
+
+EXPORT_C void DDmaRequest::EnableDstElementCounting(TBool /*aResetElementCount*/)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::EnableDstElementCounting"));
+
+ // Not yet implemented.
+ return;
+ }
+
+
+EXPORT_C void DDmaRequest::DisableSrcElementCounting()
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::DisableSrcElementCounting"));
+
+ // Not yet implemented.
+ return;
+ }
+
+
+EXPORT_C void DDmaRequest::DisableDstElementCounting()
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::DisableDstElementCounting"));
+
+ // Not yet implemented.
+ return;
+ }
+
+
+EXPORT_C TUint32 DDmaRequest::TotalNumSrcElementsTransferred()
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::TotalNumSrcElementsTransferred"));
+
+ // Not yet implemented.
+ return iTotalNumSrcElementsTransferred;
+ }
+
+
+EXPORT_C TUint32 DDmaRequest::TotalNumDstElementsTransferred()
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::TotalNumDstElementsTransferred"));
+
+ // Not yet implemented.
+ return iTotalNumDstElementsTransferred;
+ }
+
+
+EXPORT_C TInt DDmaRequest::FragmentCount()
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::FragmentCount"));
+ return FragmentCount(iFirstHdr);
+ }
+
+
+EXPORT_C TInt DDmaRequest::SrcFragmentCount()
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::SrcFragmentCount"));
+ return FragmentCount(iSrcFirstHdr);
+ }
+
+
+EXPORT_C TInt DDmaRequest::DstFragmentCount()
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::DstFragmentCount"));
+ return FragmentCount(iDstFirstHdr);
+ }
+
+
+TInt DDmaRequest::FragmentCount(const SDmaDesHdr* aHdr)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("DDmaRequest::FragmentCount aHdr=0x%08x", aHdr));
+ TInt count = 0;
+ for (const SDmaDesHdr* pH = aHdr; pH != NULL; pH = pH->iNext)
+ {
+ count++;
+ }
+ return count;
+ }
+
+
+//
+// Called when request is removed from request queue in channel
+//
+inline void DDmaRequest::OnDeque()
+ {
+ iQueued = EFalse;
+ if (iChannel.iDmacCaps->iAsymHwDescriptors)
+ {
+ iSrcLastHdr->iNext = NULL;
+ iDstLastHdr->iNext = NULL;
+ iChannel.DoUnlink(*iSrcLastHdr);
+ iChannel.DoUnlink(*iDstLastHdr);
+ }
+ else
+ {
+ iLastHdr->iNext = NULL;
+ iChannel.DoUnlink(*iLastHdr);
+ }
+ }
+
+
+#ifdef _DEBUG
+void DDmaRequest::Invariant()
+ {
+ // This invariant may be called either with,
+ // or without the channel lock already held
+ TBool channelLockAquired=EFalse;
+ if(!iChannel.iLock.HeldByCurrentThread())
+ {
+ iChannel.Wait();
+ channelLockAquired = ETrue;
+ }
+
+ __DMA_ASSERTD(LOGICAL_XOR(iCb, iDmaCb));
+ if (iChannel.iDmacCaps->iAsymHwDescriptors)
+ {
+ __DMA_ASSERTD((0 <= iSrcDesCount) && (iSrcDesCount <= iChannel.iMaxDesCount) &&
+ (0 <= iDstDesCount) && (iDstDesCount <= iChannel.iMaxDesCount));
+ if (iSrcDesCount == 0)
+ {
+ // Not fragmented yet
+ __DMA_ASSERTD(iDstDesCount == 0);
+ __DMA_ASSERTD(!iQueued);
+ __DMA_ASSERTD(!iSrcFirstHdr && !iSrcLastHdr &&
+ !iDstFirstHdr && !iDstLastHdr);
+ }
+ else if (iDstDesCount == 0)
+ {
+ // Src side only fragmented yet
+ __DMA_ASSERTD(iChannel.iController->IsValidHdr(iSrcFirstHdr));
+ __DMA_ASSERTD(iChannel.iController->IsValidHdr(iSrcLastHdr));
+ }
+ else
+ {
+ // Src & Dst sides fragmented
+ __DMA_ASSERTD(iChannel.iController->IsValidHdr(iSrcFirstHdr));
+ __DMA_ASSERTD(iChannel.iController->IsValidHdr(iSrcLastHdr));
+ __DMA_ASSERTD(iChannel.iController->IsValidHdr(iDstFirstHdr));
+ __DMA_ASSERTD(iChannel.iController->IsValidHdr(iDstLastHdr));
+ }
+ }
+ else
+ {
+ __DMA_ASSERTD((0 <= iDesCount) && (iDesCount <= iChannel.iMaxDesCount));
+ if (iDesCount == 0)
+ {
+ __DMA_ASSERTD(!iQueued);
+ __DMA_ASSERTD(!iFirstHdr && !iLastHdr);
+ }
+ else
+ {
+ __DMA_ASSERTD(iChannel.iController->IsValidHdr(iFirstHdr));
+ __DMA_ASSERTD(iChannel.iController->IsValidHdr(iLastHdr));
+ }
+ }
+
+ if(channelLockAquired)
+ {
+ iChannel.Signal();
+ }
+ }
+#endif
+
+
+//////////////////////////////////////////////////////////////////////////////
+// TDmaChannel
+
+TDmaChannel::TDmaChannel()
+ : iController(NULL),
+ iDmacCaps(NULL),
+ iPslId(0),
+ iDynChannel(EFalse),
+ iPriority(KDmaPriorityNone),
+ iCurHdr(NULL),
+ iNullPtr(&iCurHdr),
+ iDfc(Dfc, NULL, 0),
+ iMaxDesCount(0),
+ iAvailDesCount(0),
+ iIsrDfc(0),
+ iReqQ(),
+ iReqCount(0),
+ iQueuedRequests(0),
+ iCallQueuedRequestFn(ETrue),
+ iCancelInfo(NULL),
+ iRedoRequest(EFalse),
+ iIsrCbRequest(EFalse)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("TDmaChannel::TDmaChannel =0x%08X", this));
+ __DMA_INVARIANT();
+ }
+
+
+//
+// static member function
+//
+EXPORT_C TInt TDmaChannel::Open(const SCreateInfo& aInfo, TDmaChannel*& aChannel)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("TDmaChannel::Open thread %O", &Kern::CurrentThread()));
+
+ if (aInfo.iDesCount < 1)
+ {
+ __KTRACE_OPT(KPANIC, Kern::Printf("DMA channel failed to open: iDescount<1"));
+ return KErrArgument;
+ }
+
+ __DMA_ASSERTD(aInfo.iPriority <= KDmaPriority8);
+ __DMA_ASSERTD(aInfo.iDfcQ != NULL);
+ __DMA_ASSERTD(aInfo.iDfcPriority < KNumDfcPriorities);
+
+ aChannel = NULL;
+
+ DmaChannelMgr::Wait();
+ TDmaChannel* pC = DmaChannelMgr::Open(aInfo.iCookie, aInfo.iDynChannel, aInfo.iPriority);
+ DmaChannelMgr::Signal();
+ if (!pC)
+ {
+ return KErrInUse;
+ }
+ __DMA_ASSERTD(pC->iController != NULL);
+ __DMA_ASSERTD(pC->iDmacCaps != NULL);
+ __DMA_ASSERTD(pC->iController->iCapsHwDes == pC->DmacCaps().iHwDescriptors);
+ // PSL needs to set iDynChannel if and only if dynamic channel was requested
+ __DMA_ASSERTD(!LOGICAL_XOR(aInfo.iDynChannel, pC->iDynChannel));
+
+ const TInt r = pC->iController->ReserveSetOfDes(aInfo.iDesCount);
+ if (r != KErrNone)
+ {
+ pC->Close();
+ return r;
+ }
+ pC->iAvailDesCount = pC->iMaxDesCount = aInfo.iDesCount;
+
+ new (&pC->iDfc) TDfc(&Dfc, pC, aInfo.iDfcQ, aInfo.iDfcPriority);
+
+ aChannel = pC;
+
+#ifdef _DEBUG
+ pC->Invariant();
+#endif
+ __KTRACE_OPT(KDMA, Kern::Printf("opened channel %d", pC->iPslId));
+ return KErrNone;
+ }
+
+
+EXPORT_C void TDmaChannel::Close()
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("TDmaChannel::Close %d iReqCount=%d", iPslId, iReqCount));
+ __DMA_ASSERTD(IsQueueEmpty());
+ __DMA_ASSERTD(iReqCount == 0);
+
+ __DMA_ASSERTD(iQueuedRequests == 0);
+
+ // Descriptor leak? -> bug in request code
+ __DMA_ASSERTD(iAvailDesCount == iMaxDesCount);
+
+ __DMA_ASSERTD(!iRedoRequest);
+ __DMA_ASSERTD(!iIsrCbRequest);
+
+ iController->ReleaseSetOfDes(iMaxDesCount);
+ iAvailDesCount = iMaxDesCount = 0;
+
+ DmaChannelMgr::Wait();
+ DmaChannelMgr::Close(this);
+ // The following assignment will be removed once IsOpened() has been
+ // removed. That's because 'this' shouldn't be touched any more once
+ // Close() has returned from the PSL.
+ iController = NULL;
+ DmaChannelMgr::Signal();
+ }
+
+
+EXPORT_C TInt TDmaChannel::LinkToChannel(TDmaChannel* aChannel)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("TDmaChannel::LinkToChannel thread %O",
+ &Kern::CurrentThread()));
+ if (aChannel)
+ {
+ return iController->LinkChannels(*this, *aChannel);
+ }
+ else
+ {
+ return iController->UnlinkChannel(*this);
+ }
+ }
+
+
+EXPORT_C TInt TDmaChannel::Pause()
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("TDmaChannel::Pause thread %O",
+ &Kern::CurrentThread()));
+ return iController->PauseTransfer(*this);
+ }
+
+
+EXPORT_C TInt TDmaChannel::Resume()
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("TDmaChannel::Resume thread %O",
+ &Kern::CurrentThread()));
+ return iController->ResumeTransfer(*this);
+ }
+
+
+EXPORT_C void TDmaChannel::CancelAll()
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("TDmaChannel::CancelAll thread %O channel - %d",
+ &Kern::CurrentThread(), iPslId));
+ NThread* const nt = NKern::CurrentThread();
+ TBool wait = EFalse;
+ TDmaCancelInfo cancelinfo;
+ TDmaCancelInfo* waiters = NULL;
+
+ NKern::ThreadEnterCS();
+ Wait();
+ const TUint32 req_count_before = iQueuedRequests;
+ NThreadBase* const dfc_nt = iDfc.Thread();
+ // Shouldn't be NULL (i.e. an IDFC)
+ __DMA_ASSERTD(dfc_nt);
+
+ __e32_atomic_store_ord32(&iIsrDfc, (TUint32)KCancelFlagMask);
+ // ISRs after this point will not post a DFC, however a DFC may already be
+ // queued or running or both.
+ if (!IsQueueEmpty())
+ {
+ // There is a transfer in progress. It may complete before the DMAC
+ // has stopped, but the resulting ISR will not post a DFC.
+ // ISR should not happen after this function returns.
+ iController->StopTransfer(*this);
+
+ DoCancelAll();
+ ResetNullPtr();
+
+ // Clean-up the request queue.
+ SDblQueLink* pL;
+ while ((pL = iReqQ.GetFirst()) != NULL)
+ {
+ iQueuedRequests--;
+ DDmaRequest* pR = _LOFF(pL, DDmaRequest, iLink);
+ pR->OnDeque();
+ }
+ }
+ if (dfc_nt == nt)
+ {
+ // DFC runs in this thread, so just cancel it and we're finished
+ iDfc.Cancel();
+
+ // If other calls to CancelAll() are waiting for the DFC, release them here
+ waiters = iCancelInfo;
+ iCancelInfo = NULL;
+
+ // Reset the ISR count
+ __e32_atomic_store_rel32(&iIsrDfc, 0);
+ }
+ else
+ {
+ // DFC runs in another thread. Make sure it's queued and then wait for it to run.
+ if (iCancelInfo)
+ {
+ // Insert cancelinfo into the list so that it precedes iCancelInfo
+ cancelinfo.InsertBefore(iCancelInfo);
+ }
+ else
+ {
+ iCancelInfo = &cancelinfo;
+ }
+ wait = ETrue;
+ iDfc.Enque();
+ }
+
+ const TUint32 req_count_after = iQueuedRequests;
+
+ Signal();
+
+ if (waiters)
+ {
+ waiters->Signal();
+ }
+ else if (wait)
+ {
+ NKern::FSWait(&cancelinfo.iSem);
+ }
+
+ NKern::ThreadLeaveCS();
+
+ // Only call PSL if there were requests queued when we entered AND there
+ // are now no requests left on the queue.
+ if (iCallQueuedRequestFn)
+ {
+ if ((req_count_before != 0) && (req_count_after == 0))
+ {
+ QueuedRequestCountChanged();
+ }
+ }
+
+ __DMA_INVARIANT();
+ }
+
+
+EXPORT_C TInt TDmaChannel::IsrRedoRequest(TUint32 aSrcAddr, TUint32 aDstAddr,
+ TUint aTransferCount,
+ TUint32 aPslRequestInfo,
+ TBool aIsrCb)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("TDmaChannel::IsrRedoRequest src=0x%08X, "
+ "dst=0x%08X, count=%d, pslInfo=0x%08X, isrCb=%d",
+ aSrcAddr, aDstAddr, aTransferCount, aPslRequestInfo,
+ aIsrCb));
+ // Function needs to be called in ISR context.
+ __DMA_ASSERTD(NKern::CurrentContext() == NKern::EInterrupt);
+
+ __DMA_ASSERTD(!iReqQ.IsEmpty());
+ __DMA_ASSERTD(iIsrCbRequest);
+
+#ifdef _DEBUG
+ if ((aSrcAddr != KPhysAddrInvalid) && (aSrcAddr == aDstAddr))
+ {
+ __KTRACE_OPT(KPANIC,
+ Kern::Printf("Error: Updating src & dst to same address: 0x%08X",
+ aSrcAddr));
+ return KErrArgument;
+ }
+#endif
+
+ // We assume here that the just completed request is the first one in the
+ // queue, i.e. that even if there is more than one request in the queue,
+ // their respective last and first (hw) descriptors are *not* linked.
+ // (Although that's what apparently happens in TDmaSgChannel::DoQueue() /
+ // TDmac::AppendHwDes() @@@).
+ DDmaRequest* const pCurReq = _LOFF(iReqQ.First(), DDmaRequest, iLink);
+ TInt r;
+
+ if (iDmacCaps->iAsymHwDescriptors)
+ {
+ // We don't allow multiple-descriptor chains to be updated here.
+ // That we just panic (instead of returning an error), and also only in
+ // the UDEB case (instead of always) is not ideal, but done here in the
+ // interest of performance.
+ __DMA_ASSERTD((pCurReq->iSrcDesCount == 1) && (pCurReq->iDstDesCount == 1));
+
+ // Adjust parameters if necessary (asymmetrical s/g variety)
+ const SDmaDesHdr* const pSrcFirstHdr = pCurReq->iSrcFirstHdr;
+ if ((aSrcAddr != KPhysAddrInvalid) || aTransferCount || aPslRequestInfo)
+ {
+ r = iController->UpdateSrcHwDes(*pSrcFirstHdr, aSrcAddr,
+ aTransferCount, aPslRequestInfo);
+ if (r != KErrNone)
+ {
+ __KTRACE_OPT(KPANIC, Kern::Printf("Src descriptor updating failed in PSL"));
+ return r;
+ }
+ }
+ const SDmaDesHdr* const pDstFirstHdr = pCurReq->iDstFirstHdr;
+ if ((aDstAddr != KPhysAddrInvalid) || aTransferCount || aPslRequestInfo)
+ {
+ r = iController->UpdateDstHwDes(*pDstFirstHdr, aSrcAddr,
+ aTransferCount, aPslRequestInfo);
+ if (r != KErrNone)
+ {
+ __KTRACE_OPT(KPANIC, Kern::Printf("Dst descriptor updating failed in PSL"));
+ return r;
+ }
+ }
+ // Reschedule the request
+ iController->Transfer(*this, *pSrcFirstHdr, *pDstFirstHdr);
+ }
+ else
+ {
+ // We don't allow a multiple-descriptor chain to be updated here.
+ // That we just panic (instead of returning an error), and also only in
+ // the UDEB case (instead of always) is not ideal, but done here in the
+ // interest of performance.
+ __DMA_ASSERTD(pCurReq->iDesCount == 1);
+
+ // Adjust parameters if necessary (symmetrical s/g and non-s/g variety)
+ const SDmaDesHdr* const pFirstHdr = pCurReq->iFirstHdr;
+ if ((aSrcAddr != KPhysAddrInvalid) || (aDstAddr != KPhysAddrInvalid) ||
+ aTransferCount || aPslRequestInfo)
+ {
+ r = iController->UpdateDes(*pFirstHdr, aSrcAddr, aDstAddr,
+ aTransferCount, aPslRequestInfo);
+ if (r != KErrNone)
+ {
+ __KTRACE_OPT(KPANIC, Kern::Printf("Descriptor updating failed"));
+ return r;
+ }
+ }
+ // Reschedule the request
+ iController->Transfer(*this, *pFirstHdr);
+ }
+
+ if (!aIsrCb)
+ {
+ // Not another ISR callback please
+ pCurReq->iIsrCb = aIsrCb;
+ }
+ iRedoRequest = ETrue;
+
+ return KErrNone;
+ }
+
+
+EXPORT_C TInt TDmaChannel::FailNext(TInt /*aFragmentCount*/)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("TDmaChannel::FailNext"));
+ return iController->FailNext(*this);
+ }
+
+
+EXPORT_C TInt TDmaChannel::MissNextInterrupts(TInt aInterruptCount)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("TDmaChannel::MissNextInterrupts"));
+ return iController->MissNextInterrupts(*this, aInterruptCount);
+ }
+
+
+EXPORT_C TInt TDmaChannel::Extension(TInt aCmd, TAny* aArg)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("TDmaChannel::Extension"));
+ return iController->Extension(*this, aCmd, aArg);
+ }
+
+
+//
+// static member function
+//
+EXPORT_C TInt TDmaChannel::StaticExtension(TInt aCmd, TAny* aArg)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("TDmaChannel::StaticExtension"));
+ return DmaChannelMgr::StaticExtension(aCmd, aArg);
+ }
+
+
+EXPORT_C TUint TDmaChannel::MaxTransferLength(TUint aSrcFlags, TUint aDstFlags,
+ TUint32 aPslInfo)
+ {
+ return iController->MaxTransferLength(*this, aSrcFlags, aDstFlags, aPslInfo);
+ }
+
+
+EXPORT_C TUint TDmaChannel::AddressAlignMask(TUint aTargetFlags, TUint aElementSize,
+ TUint32 aPslInfo)
+ {
+ return iController->AddressAlignMask(*this, aTargetFlags, aElementSize, aPslInfo);
+ }
+
+
+EXPORT_C const SDmacCaps& TDmaChannel::DmacCaps()
+ {
+ return *iDmacCaps;
+ }
+
+
+//
+// DFC callback function (static member).
+//
+void TDmaChannel::Dfc(TAny* aArg)
+ {
+ static_cast<TDmaChannel*>(aArg)->DoDfc();
+ }
+
+
+//
+// This is quite a long function, but what can you do...
+//
+void TDmaChannel::DoDfc()
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("TDmaChannel::DoDfc thread %O channel - %d",
+ &Kern::CurrentThread(), iPslId));
+ Wait();
+
+ // Atomically fetch and reset the number of DFCs queued by the ISR and the
+ // error flag. Leave the cancel flag alone for now.
+ const TUint32 w = __e32_atomic_and_ord32(&iIsrDfc, (TUint32)KCancelFlagMask);
+ TUint32 count = w & KDfcCountMask;
+ const TBool error = w & (TUint32)KErrorFlagMask;
+ TBool stop = w & (TUint32)KCancelFlagMask;
+ const TUint32 req_count_before = iQueuedRequests;
+ TUint32 req_count_after = 0;
+
+ __DMA_ASSERTD((count > 0) || stop);
+ __DMA_ASSERTD(!iRedoRequest); // We shouldn't be here if this is true
+
+ while (count && !stop)
+ {
+ --count;
+
+ __DMA_ASSERTA(!iReqQ.IsEmpty());
+
+ // If an error occurred it must have been reported on the last
+ // interrupt since transfers are suspended after an error.
+ DDmaRequest::TResult const res = (count == 0 && error) ?
+ DDmaRequest::EError : DDmaRequest::EOk;
+ DDmaRequest* pCompletedReq = NULL;
+ DDmaRequest* const pCurReq = _LOFF(iReqQ.First(), DDmaRequest, iLink);
+
+ if (res == DDmaRequest::EOk)
+ {
+ // Update state machine, current fragment, completed fragment and
+ // tell the DMAC to transfer the next fragment if necessary.
+ TBool complete;
+ if (iDmacCaps->iAsymHwDescriptors)
+ {
+ SDmaDesHdr* pCompletedSrcHdr = NULL;
+ SDmaDesHdr* pCompletedDstHdr = NULL;
+ DoDfc(*pCurReq, pCompletedSrcHdr, pCompletedDstHdr);
+ // We don't support asymmetrical ISR notifications and request
+ // completions yet, hence we can do the following assert test
+ // here; also 'complete' is determined equally by either the
+ // SRC or DST side.
+ __DMA_ASSERTD(!LOGICAL_XOR((pCompletedSrcHdr == pCurReq->iSrcLastHdr),
+ (pCompletedDstHdr == pCurReq->iDstLastHdr)));
+ complete = (pCompletedDstHdr == pCurReq->iDstLastHdr);
+ }
+ else
+ {
+ SDmaDesHdr* pCompletedHdr = NULL;
+ DoDfc(*pCurReq, pCompletedHdr);
+ complete = (pCompletedHdr == pCurReq->iLastHdr);
+ }
+ // If just completed last fragment from current request, switch to
+ // next request (if any).
+ if (complete)
+ {
+ pCompletedReq = pCurReq;
+ pCurReq->iLink.Deque();
+ iQueuedRequests--;
+ if (iReqQ.IsEmpty())
+ ResetNullPtr();
+ pCompletedReq->OnDeque();
+ }
+ }
+ else
+ {
+ pCompletedReq = pCurReq;
+ }
+
+ if (pCompletedReq && !pCompletedReq->iIsrCb)
+ {
+ // Don't execute ISR callbacks here (they have already been called)
+ DDmaRequest::TCallback const cb = pCompletedReq->iCb;
+ if (cb)
+ {
+ // Old style callback
+ TAny* const arg = pCompletedReq->iCbArg;
+ Signal();
+ __KTRACE_OPT(KDMA, Kern::Printf("Client CB res=%d", res));
+ (*cb)(res, arg);
+ Wait();
+ }
+ else
+ {
+ // New style callback
+ TDmaCallback const ncb = pCompletedReq->iDmaCb;
+ if (ncb)
+ {
+ TAny* const arg = pCompletedReq->iDmaCbArg;
+ TDmaResult const result = (res == DDmaRequest::EOk) ?
+ EDmaResultOK : EDmaResultError;
+ Signal();
+ __KTRACE_OPT(KDMA, Kern::Printf("Client CB result=%d", result));
+ (*ncb)(EDmaCallbackRequestCompletion, result, arg, NULL);
+ Wait();
+ }
+ }
+ }
+ // Allow another thread in, in case they are trying to cancel
+ if (pCompletedReq || Flash())
+ {
+ stop = __e32_atomic_load_acq32(&iIsrDfc) & (TUint32)KCancelFlagMask;
+ }
+ }
+
+ if (stop)
+ {
+ // If another thread set the cancel flag, it should have
+ // cleaned up the request queue
+ __DMA_ASSERTD(IsQueueEmpty());
+
+ TDmaCancelInfo* const waiters = iCancelInfo;
+ iCancelInfo = NULL;
+
+ // make sure DFC doesn't run again until a new request completes
+ iDfc.Cancel();
+
+ // reset the ISR count - new requests can now be processed
+ __e32_atomic_store_rel32(&iIsrDfc, 0);
+
+ req_count_after = iQueuedRequests;
+ Signal();
+
+ // release threads doing CancelAll()
+ waiters->Signal();
+ }
+ else
+ {
+ req_count_after = iQueuedRequests;
+ Signal();
+ }
+
+ // Only call PSL if there were requests queued when we entered AND there
+ // are now no requests left on the queue (after also having executed all
+ // client callbacks).
+ if (iCallQueuedRequestFn)
+ {
+ if ((req_count_before != 0) && (req_count_after == 0))
+ {
+ QueuedRequestCountChanged();
+ }
+ }
+
+ __DMA_INVARIANT();
+ }
+
+
+void TDmaChannel::DoQueue(const DDmaRequest& /*aReq*/)
+ {
+ // Must be overridden
+ __DMA_UNREACHABLE_DEFAULT();
+ }
+
+
+//
+// Unlink the last item of a LLI chain from the next chain.
+// Default implementation does nothing. This is overridden by scatter-gather
+// channels.
+//
+void TDmaChannel::DoUnlink(SDmaDesHdr& /*aHdr*/)
+ {
+ }
+
+
+void TDmaChannel::DoDfc(const DDmaRequest& /*aCurReq*/, SDmaDesHdr*& /*aCompletedHdr*/)
+ {
+ // To make sure this version of the function isn't called for channels for
+ // which it isn't appropriate (and which therefore don't override it) we
+ // put this check in here.
+ __DMA_UNREACHABLE_DEFAULT();
+ }
+
+
+void TDmaChannel::DoDfc(const DDmaRequest& /*aCurReq*/, SDmaDesHdr*& /*aSrcCompletedHdr*/,
+ SDmaDesHdr*& /*aDstCompletedHdr*/)
+ {
+ // To make sure this version of the function isn't called for channels for
+ // which it isn't appropriate (and which therefore don't override it) we
+ // put this check in here.
+ __DMA_UNREACHABLE_DEFAULT();
+ }
+
+
+void TDmaChannel::SetNullPtr(const DDmaRequest& aReq)
+ {
+ // iNullPtr points to iCurHdr for an empty queue
+ *iNullPtr = aReq.iFirstHdr;
+ iNullPtr = &(aReq.iLastHdr->iNext);
+ }
+
+
+void TDmaChannel::ResetNullPtr()
+ {
+ iCurHdr = NULL;
+ iNullPtr = &iCurHdr;
+ }
+
+
+/** PSL may override */
+void TDmaChannel::QueuedRequestCountChanged()
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("TDmaChannel::QueuedRequestCountChanged(): "
+ "disabling further calls"));
+ Wait();
+ iCallQueuedRequestFn = EFalse;
+ Signal();
+ }
+
+
+#ifdef _DEBUG
+void TDmaChannel::Invariant()
+ {
+ Wait();
+
+ __DMA_ASSERTD(iReqCount >= 0);
+
+ __DMA_ASSERTD(iCurHdr == NULL || iController->IsValidHdr(iCurHdr));
+
+ // should always point to NULL pointer ending fragment queue
+ __DMA_ASSERTD(*iNullPtr == NULL);
+
+ __DMA_ASSERTD((0 <= iAvailDesCount) && (iAvailDesCount <= iMaxDesCount));
+
+ __DMA_ASSERTD(LOGICAL_XOR(iCurHdr, IsQueueEmpty()));
+ if (iCurHdr == NULL)
+ {
+ __DMA_ASSERTD(iNullPtr == &iCurHdr);
+ }
+
+ Signal();
+ }
+#endif
+
+
+//////////////////////////////////////////////////////////////////////////////
+// TDmaSbChannel
+
+void TDmaSbChannel::DoQueue(const DDmaRequest& /*aReq*/)
+ {
+ if (iState != ETransferring)
+ {
+ iController->Transfer(*this, *iCurHdr);
+ iState = ETransferring;
+ }
+ }
+
+
+void TDmaSbChannel::DoCancelAll()
+ {
+ __DMA_ASSERTD(iState == ETransferring);
+ iState = EIdle;
+ }
+
+
+void TDmaSbChannel::DoDfc(const DDmaRequest& /*aCurReq*/, SDmaDesHdr*& aCompletedHdr)
+ {
+ __DMA_ASSERTD(iState == ETransferring);
+ aCompletedHdr = iCurHdr;
+ iCurHdr = iCurHdr->iNext;
+ if (iCurHdr != NULL)
+ {
+ iController->Transfer(*this, *iCurHdr);
+ }
+ else
+ {
+ iState = EIdle;
+ }
+ }
+
+
+//////////////////////////////////////////////////////////////////////////////
+// TDmaDbChannel
+
+void TDmaDbChannel::DoQueue(const DDmaRequest& aReq)
+ {
+ switch (iState)
+ {
+ case EIdle:
+ iController->Transfer(*this, *iCurHdr);
+ if (iCurHdr->iNext)
+ {
+ iController->Transfer(*this, *(iCurHdr->iNext));
+ iState = ETransferring;
+ }
+ else
+ iState = ETransferringLast;
+ break;
+ case ETransferring:
+ // nothing to do
+ break;
+ case ETransferringLast:
+ iController->Transfer(*this, *(aReq.iFirstHdr));
+ iState = ETransferring;
+ break;
+ default:
+ __DMA_CANT_HAPPEN();
+ }
+ }
+
+
+void TDmaDbChannel::DoCancelAll()
+ {
+ iState = EIdle;
+ }
+
+
+void TDmaDbChannel::DoDfc(const DDmaRequest& /*aCurReq*/, SDmaDesHdr*& aCompletedHdr)
+ {
+ aCompletedHdr = iCurHdr;
+ iCurHdr = iCurHdr->iNext;
+ switch (iState)
+ {
+ case ETransferringLast:
+ iState = EIdle;
+ break;
+ case ETransferring:
+ if (iCurHdr->iNext == NULL)
+ iState = ETransferringLast;
+ else
+ iController->Transfer(*this, *(iCurHdr->iNext));
+ break;
+ default:
+ __DMA_CANT_HAPPEN();
+ }
+ }
+
+
+//////////////////////////////////////////////////////////////////////////////
+// TDmaSgChannel
+
+void TDmaSgChannel::DoQueue(const DDmaRequest& aReq)
+ {
+ if (iState == ETransferring)
+ {
+ __DMA_ASSERTD(!aReq.iLink.Alone());
+ DDmaRequest* pReqPrev = _LOFF(aReq.iLink.iPrev, DDmaRequest, iLink);
+ iController->AppendHwDes(*this, *(pReqPrev->iLastHdr), *(aReq.iFirstHdr));
+ }
+ else
+ {
+ iController->Transfer(*this, *(aReq.iFirstHdr));
+ iState = ETransferring;
+ }
+ }
+
+
+void TDmaSgChannel::DoCancelAll()
+ {
+ __DMA_ASSERTD(iState == ETransferring);
+ iState = EIdle;
+ }
+
+
+void TDmaSgChannel::DoUnlink(SDmaDesHdr& aHdr)
+ {
+ iController->UnlinkHwDes(*this, aHdr);
+ }
+
+
+void TDmaSgChannel::DoDfc(const DDmaRequest& aCurReq, SDmaDesHdr*& aCompletedHdr)
+ {
+ __DMA_ASSERTD(iState == ETransferring);
+ aCompletedHdr = aCurReq.iLastHdr;
+ iCurHdr = aCompletedHdr->iNext;
+ iState = (iCurHdr != NULL) ? ETransferring : EIdle;
+ }
+
+
+//////////////////////////////////////////////////////////////////////////////
+// TDmaAsymSgChannel
+
+TDmaAsymSgChannel::TDmaAsymSgChannel()
+ : iSrcCurHdr(NULL),
+ iSrcNullPtr(&iSrcCurHdr),
+ iDstCurHdr(NULL),
+ iDstNullPtr(&iDstCurHdr)
+ {
+ __DMA_INVARIANT();
+ }
+
+
+void TDmaAsymSgChannel::SetNullPtr(const DDmaRequest& aReq)
+ {
+ // i{Src|Dst}NullPtr points to i{Src|Dst}CurHdr for an empty queue
+ *iSrcNullPtr = aReq.iSrcFirstHdr;
+ *iDstNullPtr = aReq.iDstFirstHdr;
+ iSrcNullPtr = &(aReq.iSrcLastHdr->iNext);
+ iDstNullPtr = &(aReq.iDstLastHdr->iNext);
+ }
+
+
+void TDmaAsymSgChannel::ResetNullPtr()
+ {
+ iSrcCurHdr = NULL;
+ iSrcNullPtr = &iSrcCurHdr;
+ iDstCurHdr = NULL;
+ iDstNullPtr = &iDstCurHdr;
+ }
+
+
+void TDmaAsymSgChannel::DoQueue(const DDmaRequest& aReq)
+ {
+ if (iState == ETransferring)
+ {
+ __DMA_ASSERTD(!aReq.iLink.Alone());
+ DDmaRequest* pReqPrev = _LOFF(aReq.iLink.iPrev, DDmaRequest, iLink);
+ iController->AppendHwDes(*this,
+ *(pReqPrev->iSrcLastHdr), *(aReq.iSrcFirstHdr),
+ *(pReqPrev->iDstLastHdr), *(aReq.iDstFirstHdr));
+ }
+ else
+ {
+ iController->Transfer(*this, *(aReq.iSrcFirstHdr), *(aReq.iDstFirstHdr));
+ iState = ETransferring;
+ }
+ }
+
+
+void TDmaAsymSgChannel::DoCancelAll()
+ {
+ __DMA_ASSERTD(iState == ETransferring);
+ iState = EIdle;
+ }
+
+
+void TDmaAsymSgChannel::DoUnlink(SDmaDesHdr& aHdr)
+ {
+ iController->UnlinkHwDes(*this, aHdr);
+ }
+
+
+void TDmaAsymSgChannel::DoDfc(const DDmaRequest& aCurReq, SDmaDesHdr*& aSrcCompletedHdr,
+ SDmaDesHdr*& aDstCompletedHdr)
+ {
+ __DMA_ASSERTD(iState == ETransferring);
+ aSrcCompletedHdr = aCurReq.iSrcLastHdr;
+ iSrcCurHdr = aSrcCompletedHdr->iNext;
+ aDstCompletedHdr = aCurReq.iDstLastHdr;
+ iDstCurHdr = aDstCompletedHdr->iNext;
+ // Must be either both NULL or none of them.
+ __DMA_ASSERTD(!LOGICAL_XOR(iSrcCurHdr, iDstCurHdr));
+ iState = (iSrcCurHdr != NULL) ? ETransferring : EIdle;
+ }
+
+
+#ifdef _DEBUG
+void TDmaAsymSgChannel::Invariant()
+ {
+ Wait();
+
+ __DMA_ASSERTD(iReqCount >= 0);
+
+ __DMA_ASSERTD(iSrcCurHdr == NULL || iController->IsValidHdr(iSrcCurHdr));
+ __DMA_ASSERTD(iDstCurHdr == NULL || iController->IsValidHdr(iDstCurHdr));
+
+ // should always point to NULL pointer ending fragment queue
+ __DMA_ASSERTD(*iSrcNullPtr == NULL);
+ __DMA_ASSERTD(*iDstNullPtr == NULL);
+
+ __DMA_ASSERTD((0 <= iAvailDesCount) && (iAvailDesCount <= iMaxDesCount));
+
+ __DMA_ASSERTD((iSrcCurHdr && iDstCurHdr && !IsQueueEmpty()) ||
+ (!iSrcCurHdr && !iDstCurHdr && IsQueueEmpty()));
+ if (iSrcCurHdr == NULL)
+ {
+ __DMA_ASSERTD(iSrcNullPtr == &iSrcCurHdr);
+ }
+ if (iDstCurHdr == NULL)
+ {
+ __DMA_ASSERTD(iDstNullPtr == &iDstCurHdr);
+ }
+
+ Signal();
+ }
+#endif