--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32test/dma/dmasim.cpp Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,927 @@
+// Copyright (c) 2002-2009 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:
+//
+// Description:
+// e32test\dma\dmasim.cpp
+// DMA framework Platform Specific Layer (PSL) for software-emulated
+// DMA controller used for testing the DMA framework PIL.
+//
+//
+
+#include <drivers/dma.h>
+#include <kernel/kern_priv.h>
+
+
+const char KDmaPanicCat[] = "DMASIM";
+
+const TInt KMaxTransferSize = 0x1FFF;
+const TInt KMemAlignMask = 3; // memory addresses passed to DMAC must be multiple of 4
+const TInt KBurstSize = 0x800;
+
+typedef void (*TPseudoIsr)();
+
+const TInt KChannelCount = 4; // # of channels per controller
+const TInt KDesCount = 256; // # of descriptors allocated per controller
+
+//////////////////////////////////////////////////////////////////////////////
+// SOFTWARE DMA CONTROLLER SIMULATION
+//////////////////////////////////////////////////////////////////////////////
+
+class DmacSb
+/** Single-buffer DMA controller software simulation */
+ {
+public:
+ enum { ECsRun = 0x80000000 };
+public:
+ static void DoTransfer();
+private:
+ static void BurstTransfer();
+private:
+ static TInt CurrentChannel;
+public:
+ // pseudo registers
+ static TUint8* SrcAddr[KChannelCount];
+ static TUint8* DestAddr[KChannelCount];
+ static TInt Count[KChannelCount];
+ static TUint32 ControlStatus[KChannelCount];
+ static TUint32 CompletionInt;
+ static TUint32 ErrorInt;
+ // hook for pseudo ISR
+ static TPseudoIsr Isr;
+ // transfer failure simulation
+ static TInt FailCount[KChannelCount];
+ };
+
+TUint8* DmacSb::SrcAddr[KChannelCount];
+TUint8* DmacSb::DestAddr[KChannelCount];
+TInt DmacSb::Count[KChannelCount];
+TUint32 DmacSb::ControlStatus[KChannelCount];
+TUint32 DmacSb::CompletionInt;
+TUint32 DmacSb::ErrorInt;
+TPseudoIsr DmacSb::Isr;
+TInt DmacSb::FailCount[KChannelCount];
+TInt DmacSb::CurrentChannel;
+
+void DmacSb::DoTransfer()
+ {
+ if (ControlStatus[CurrentChannel] & ECsRun)
+ {
+ if (FailCount[CurrentChannel] > 0 && --FailCount[CurrentChannel] == 0)
+ {
+ ControlStatus[CurrentChannel] &= ~ECsRun;
+ ErrorInt |= 1 << CurrentChannel;
+ Isr();
+ }
+ else
+ {
+ //__KTRACE_OPT(KDMA, Kern::Printf("DmacSb::DoTransfer channel %d", CurrentChannel));
+ if (Count[CurrentChannel] == 0)
+ {
+ //__KTRACE_OPT(KDMA, Kern::Printf("DmacSb::DoTransfer transfer complete"));
+ ControlStatus[CurrentChannel] &= ~ECsRun;
+ CompletionInt |= 1 << CurrentChannel;
+ Isr();
+ }
+ else
+ BurstTransfer();
+ }
+ }
+
+ CurrentChannel++;
+ if (CurrentChannel >= KChannelCount)
+ CurrentChannel = 0;
+ }
+
+void DmacSb::BurstTransfer()
+ {
+ //__KTRACE_OPT(KDMA, Kern::Printf("DmacSb::BurstTransfer"));
+ TInt s = Min(Count[CurrentChannel], KBurstSize);
+ memcpy(DestAddr[CurrentChannel], SrcAddr[CurrentChannel], s);
+ Count[CurrentChannel] -= s;
+ SrcAddr[CurrentChannel] += s;
+ DestAddr[CurrentChannel] += s;
+ }
+
+//////////////////////////////////////////////////////////////////////////////
+
+class DmacDb
+/** Double-buffer DMA controller software simulation */
+ {
+public:
+ enum { ECsRun = 0x80000000, ECsPrg = 0x40000000 };
+public:
+ static void Enable(TInt aIdx);
+ static void DoTransfer();
+private:
+ static TInt CurrentChannel;
+private:
+ // internal pseudo-registers
+ static TUint8* ActSrcAddr[KChannelCount];
+ static TUint8* ActDestAddr[KChannelCount];
+ static TInt ActCount[KChannelCount];
+public:
+ // externally accessible pseudo-registers
+ static TUint32 ControlStatus[KChannelCount];
+ static TUint8* PrgSrcAddr[KChannelCount];
+ static TUint8* PrgDestAddr[KChannelCount];
+ static TInt PrgCount[KChannelCount];
+ static TUint32 CompletionInt;
+ static TUint32 ErrorInt;
+ // hook for pseudo ISR
+ static TPseudoIsr Isr;
+ // transfer failure simulation
+ static TInt FailCount[KChannelCount];
+ static TInt InterruptsToMiss[KChannelCount];
+ };
+
+TUint8* DmacDb::PrgSrcAddr[KChannelCount];
+TUint8* DmacDb::PrgDestAddr[KChannelCount];
+TInt DmacDb::PrgCount[KChannelCount];
+TUint8* DmacDb::ActSrcAddr[KChannelCount];
+TUint8* DmacDb::ActDestAddr[KChannelCount];
+TInt DmacDb::ActCount[KChannelCount];
+TUint32 DmacDb::ControlStatus[KChannelCount];
+TUint32 DmacDb::CompletionInt;
+TUint32 DmacDb::ErrorInt;
+TPseudoIsr DmacDb::Isr;
+TInt DmacDb::FailCount[KChannelCount];
+TInt DmacDb::InterruptsToMiss[KChannelCount];
+TInt DmacDb::CurrentChannel;
+
+void DmacDb::Enable(TInt aIdx)
+ {
+ if (ControlStatus[aIdx] & ECsRun)
+ ControlStatus[aIdx] |= ECsPrg;
+ else
+ {
+ ActSrcAddr[aIdx] = PrgSrcAddr[aIdx];
+ ActDestAddr[aIdx] = PrgDestAddr[aIdx];
+ ActCount[aIdx] = PrgCount[aIdx];
+ ControlStatus[aIdx] |= ECsRun;
+ }
+ }
+
+void DmacDb::DoTransfer()
+ {
+ if (ControlStatus[CurrentChannel] & ECsRun)
+ {
+ if (FailCount[CurrentChannel] > 0 && --FailCount[CurrentChannel] == 0)
+ {
+ ControlStatus[CurrentChannel] &= ~ECsRun;
+ ErrorInt |= 1 << CurrentChannel;
+ Isr();
+ }
+ else
+ {
+ if (ActCount[CurrentChannel] == 0)
+ {
+ if (ControlStatus[CurrentChannel] & ECsPrg)
+ {
+ ActSrcAddr[CurrentChannel] = PrgSrcAddr[CurrentChannel];
+ ActDestAddr[CurrentChannel] = PrgDestAddr[CurrentChannel];
+ ActCount[CurrentChannel] = PrgCount[CurrentChannel];
+ ControlStatus[CurrentChannel] &= ~ECsPrg;
+ }
+ else
+ ControlStatus[CurrentChannel] &= ~ECsRun;
+ if (InterruptsToMiss[CurrentChannel] > 0)
+ InterruptsToMiss[CurrentChannel]--;
+ else
+ {
+ CompletionInt |= 1 << CurrentChannel;
+ Isr();
+ }
+ }
+ else
+ {
+ TInt s = Min(ActCount[CurrentChannel], KBurstSize);
+ memcpy(ActDestAddr[CurrentChannel], ActSrcAddr[CurrentChannel], s);
+ ActCount[CurrentChannel] -= s;
+ ActSrcAddr[CurrentChannel] += s;
+ ActDestAddr[CurrentChannel] += s;
+ }
+ }
+ }
+
+ CurrentChannel++;
+ if (CurrentChannel >= KChannelCount)
+ CurrentChannel = 0;
+ }
+
+
+//////////////////////////////////////////////////////////////////////////////
+
+class DmacSg
+/** Scatter/gather DMA controller software simulation */
+ {
+public:
+ enum { EChannelBitRun = 0x80000000 };
+ enum { EDesBitInt = 1 };
+ struct SDes
+ {
+ TUint8* iSrcAddr;
+ TUint8* iDestAddr;
+ TInt iCount;
+ TUint iControl;
+ SDes* iNext;
+ };
+public:
+ static void DoTransfer();
+ static void Enable(TInt aIdx);
+private:
+ static TInt CurrentChannel;
+ static TBool IsDescriptorLoaded[KChannelCount];
+public:
+ // externally accessible pseudo-registers
+ static TUint32 ChannelControl[KChannelCount];
+ static TUint8* SrcAddr[KChannelCount];
+ static TUint8* DestAddr[KChannelCount];
+ static TInt Count[KChannelCount];
+ static TUint Control[KChannelCount];
+ static SDes* NextDes[KChannelCount];
+ static TUint32 CompletionInt;
+ static TUint32 ErrorInt;
+ // hook for pseudo ISR
+ static TPseudoIsr Isr;
+ // transfer failure simulation
+ static TInt FailCount[KChannelCount];
+ static TInt InterruptsToMiss[KChannelCount];
+ };
+
+TUint32 DmacSg::ChannelControl[KChannelCount];
+TUint8* DmacSg::SrcAddr[KChannelCount];
+TUint8* DmacSg::DestAddr[KChannelCount];
+TInt DmacSg::Count[KChannelCount];
+TUint DmacSg::Control[KChannelCount];
+DmacSg::SDes* DmacSg::NextDes[KChannelCount];
+TUint32 DmacSg::CompletionInt;
+TUint32 DmacSg::ErrorInt;
+TPseudoIsr DmacSg::Isr;
+TInt DmacSg::FailCount[KChannelCount];
+TInt DmacSg::InterruptsToMiss[KChannelCount];
+TInt DmacSg::CurrentChannel;
+TBool DmacSg::IsDescriptorLoaded[KChannelCount];
+
+
+void DmacSg::DoTransfer()
+ {
+ if (ChannelControl[CurrentChannel] & EChannelBitRun)
+ {
+ if (FailCount[CurrentChannel] > 0 && --FailCount[CurrentChannel] == 0)
+ {
+ ChannelControl[CurrentChannel] &= ~EChannelBitRun;
+ ErrorInt |= 1 << CurrentChannel;
+ Isr();
+ }
+ else
+ {
+ if (IsDescriptorLoaded[CurrentChannel])
+ {
+ if (Count[CurrentChannel] == 0)
+ {
+ IsDescriptorLoaded[CurrentChannel] = EFalse;
+ if (Control[CurrentChannel] & EDesBitInt)
+ {
+ if (InterruptsToMiss[CurrentChannel] > 0)
+ InterruptsToMiss[CurrentChannel]--;
+ else
+ {
+ CompletionInt |= 1 << CurrentChannel;
+ Isr();
+ }
+ }
+ }
+ else
+ {
+ TInt s = Min(Count[CurrentChannel], KBurstSize);
+ memcpy(DestAddr[CurrentChannel], SrcAddr[CurrentChannel], s);
+ Count[CurrentChannel] -= s;
+ SrcAddr[CurrentChannel] += s;
+ DestAddr[CurrentChannel] += s;
+ }
+ }
+ // Need to test again as new descriptor must be loaded if
+ // completion has just occured.
+ if (! IsDescriptorLoaded[CurrentChannel])
+ {
+ if (NextDes[CurrentChannel] != NULL)
+ {
+ SrcAddr[CurrentChannel] = NextDes[CurrentChannel]->iSrcAddr;
+ DestAddr[CurrentChannel] = NextDes[CurrentChannel]->iDestAddr;
+ Count[CurrentChannel] = NextDes[CurrentChannel]->iCount;
+ Control[CurrentChannel] = NextDes[CurrentChannel]->iControl;
+ NextDes[CurrentChannel] = NextDes[CurrentChannel]->iNext;
+ IsDescriptorLoaded[CurrentChannel] = ETrue;
+ }
+ else
+ ChannelControl[CurrentChannel] &= ~EChannelBitRun;
+ }
+ }
+ }
+
+ CurrentChannel++;
+ if (CurrentChannel >= KChannelCount)
+ CurrentChannel = 0;
+ }
+
+
+void DmacSg::Enable(TInt aIdx)
+ {
+ SrcAddr[aIdx] = NextDes[aIdx]->iSrcAddr;
+ DestAddr[aIdx] = NextDes[aIdx]->iDestAddr;
+ Count[aIdx] = NextDes[aIdx]->iCount;
+ Control[aIdx] = NextDes[aIdx]->iControl;
+ NextDes[aIdx] = NextDes[aIdx]->iNext;
+ IsDescriptorLoaded[aIdx] = ETrue;
+ ChannelControl[aIdx] |= EChannelBitRun;
+ }
+
+//////////////////////////////////////////////////////////////////////////////
+
+class DmacSim
+/**
+ Harness calling the various DMA controller simulators periodically.
+ */
+ {
+public:
+ static void StartEmulation();
+ static void StopEmulation();
+private:
+ enum { KPeriod = 1 }; // in ms
+ static void TickCB(TAny* aThis);
+ static NTimer Timer;
+ };
+
+NTimer DmacSim::Timer;
+
+void DmacSim::StartEmulation()
+ {
+ new (&Timer) NTimer(&TickCB, 0);
+ __DMA_ASSERTA(Timer.OneShot(KPeriod, EFalse) == KErrNone);
+ }
+
+void DmacSim::StopEmulation()
+ {
+ Timer.Cancel();
+ }
+
+void DmacSim::TickCB(TAny*)
+ {
+ DmacSb::DoTransfer();
+ DmacDb::DoTransfer();
+ DmacSg::DoTransfer();
+ __DMA_ASSERTA(Timer.Again(KPeriod) == KErrNone);
+ }
+
+//////////////////////////////////////////////////////////////////////////////
+// PSL FOR DMA SIMULATION
+//////////////////////////////////////////////////////////////////////////////
+
+class DSimSbController : public TDmac
+ {
+public:
+ DSimSbController();
+private:
+ static void Isr();
+ // from TDmac
+ virtual void Transfer(const TDmaChannel& aChannel, const SDmaDesHdr& aHdr);
+ virtual void StopTransfer(const TDmaChannel& aChannel);
+ virtual TInt FailNext(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);
+public:
+ static const SCreateInfo KInfo;
+ TDmaSbChannel iChannels[KChannelCount];
+ };
+
+DSimSbController SbController;
+
+const TDmac::SCreateInfo DSimSbController::KInfo =
+ {
+ KChannelCount,
+ KDesCount,
+ 0,
+ sizeof(SDmaPseudoDes),
+ 0,
+ };
+
+DSimSbController::DSimSbController()
+ : TDmac(KInfo)
+ {
+ DmacSb::Isr = Isr;
+ }
+
+
+void DSimSbController::Transfer(const TDmaChannel& aChannel, const SDmaDesHdr& aHdr)
+ {
+ TUint32 i = aChannel.PslId();
+ const SDmaPseudoDes& des = HdrToDes(aHdr);
+ DmacSb::SrcAddr[i] = (TUint8*) des.iSrc;
+ DmacSb::DestAddr[i] = (TUint8*) des.iDest;
+ DmacSb::Count[i] = des.iCount;
+ DmacSb::ControlStatus[i] |= DmacSb::ECsRun;
+ }
+
+
+void DSimSbController::StopTransfer(const TDmaChannel& aChannel)
+ {
+ __e32_atomic_and_ord32(&DmacSb::ControlStatus[aChannel.PslId()], (TUint32)~DmacSb::ECsRun);
+ }
+
+
+TInt DSimSbController::FailNext(const TDmaChannel& aChannel)
+ {
+ DmacSb::FailCount[aChannel.PslId()] = 1;
+ return KErrNone;
+ }
+
+
+TBool DSimSbController::IsIdle(const TDmaChannel& aChannel)
+ {
+ return (DmacSb::ControlStatus[aChannel.PslId()] & DmacSb::ECsRun) == 0;
+ }
+
+
+TInt DSimSbController::MaxTransferSize(TDmaChannel& /*aChannel*/, TUint /*aFlags*/, TUint32 /*aPslInfo*/)
+ {
+ return KMaxTransferSize;
+ }
+
+
+TUint DSimSbController::MemAlignMask(TDmaChannel& /*aChannel*/, TUint /*aFlags*/, TUint32 /*aPslInfo*/)
+ {
+ return KMemAlignMask;
+ }
+
+
+void DSimSbController::Isr()
+ {
+ for (TInt i = 0; i < KChannelCount; i++)
+ {
+ TUint32 mask = (1 << i);
+ if (DmacSb::CompletionInt & mask)
+ {
+ DmacSb::CompletionInt &= ~mask;
+ HandleIsr(SbController.iChannels[i], ETrue);
+ }
+ if (DmacSb::ErrorInt & mask)
+ {
+ DmacSb::ErrorInt &= ~mask;
+ HandleIsr(SbController.iChannels[i], EFalse);
+ }
+ }
+ }
+
+//////////////////////////////////////////////////////////////////////////////
+
+class DSimDbController : public TDmac
+ {
+public:
+ DSimDbController();
+private:
+ static void Isr();
+ // from TDmac
+ virtual void Transfer(const TDmaChannel& aChannel, const SDmaDesHdr& aHdr);
+ virtual void StopTransfer(const TDmaChannel& aChannel);
+ virtual TInt FailNext(const TDmaChannel& aChannel);
+ virtual TInt MissNextInterrupts(const TDmaChannel& aChannel, TInt aInterruptCount);
+ virtual TBool IsIdle(const TDmaChannel& aChannel);
+ virtual TInt MaxTransferSize(TDmaChannel& aChannel, TUint aFlags, TUint32 aPslInfo);
+ virtual TUint MemAlignMask(TDmaChannel& aChannel, TUint aFlags, TUint32 aPslInfo);
+public:
+ static const SCreateInfo KInfo;
+ TDmaDbChannel iChannels[KChannelCount];
+ };
+
+DSimDbController DbController;
+
+const TDmac::SCreateInfo DSimDbController::KInfo =
+ {
+ KChannelCount,
+ KDesCount,
+ 0,
+ sizeof(SDmaPseudoDes),
+ 0,
+ };
+
+
+DSimDbController::DSimDbController()
+ : TDmac(KInfo)
+ {
+ DmacDb::Isr = Isr;
+ }
+
+
+void DSimDbController::Transfer(const TDmaChannel& aChannel, const SDmaDesHdr& aHdr)
+ {
+ TUint32 i = aChannel.PslId();
+ const SDmaPseudoDes& des = HdrToDes(aHdr);
+ DmacDb::PrgSrcAddr[i] = (TUint8*) des.iSrc;
+ DmacDb::PrgDestAddr[i] = (TUint8*) des.iDest;
+ DmacDb::PrgCount[i] = des.iCount;
+ DmacDb::Enable(i);
+ }
+
+
+void DSimDbController::StopTransfer(const TDmaChannel& aChannel)
+ {
+ __e32_atomic_and_ord32(&DmacDb::ControlStatus[aChannel.PslId()], (TUint32)~(DmacDb::ECsRun|DmacDb::ECsPrg));
+ }
+
+
+TInt DSimDbController::FailNext(const TDmaChannel& aChannel)
+ {
+ DmacDb::FailCount[aChannel.PslId()] = 1;
+ return KErrNone;
+ }
+
+
+TInt DSimDbController::MissNextInterrupts(const TDmaChannel& aChannel, TInt aInterruptCount)
+ {
+ __DMA_ASSERTD((DmacDb::ControlStatus[aChannel.PslId()] & DmacDb::ECsRun) == 0);
+ __DMA_ASSERTD(aInterruptCount >= 0);
+ // At most one interrupt can be missed with double-buffer controller
+ if (aInterruptCount == 1)
+ {
+ DmacDb::InterruptsToMiss[aChannel.PslId()] = aInterruptCount;
+ return KErrNone;
+ }
+ else
+ return KErrNotSupported;
+ }
+
+
+TBool DSimDbController::IsIdle(const TDmaChannel& aChannel)
+ {
+ return (DmacDb::ControlStatus[aChannel.PslId()] & DmacDb::ECsRun) == 0;
+ }
+
+
+TInt DSimDbController::MaxTransferSize(TDmaChannel& /*aChannel*/, TUint /*aFlags*/, TUint32 /*aPslInfo*/)
+ {
+ return KMaxTransferSize;
+ }
+
+
+TUint DSimDbController::MemAlignMask(TDmaChannel& /*aChannel*/, TUint /*aFlags*/, TUint32 /*aPslInfo*/)
+ {
+ return KMemAlignMask;
+ }
+
+
+void DSimDbController::Isr()
+ {
+ for (TInt i = 0; i < KChannelCount; i++)
+ {
+ TUint32 mask = (1 << i);
+ if (DmacDb::CompletionInt & mask)
+ {
+ DmacDb::CompletionInt &= ~mask;
+ HandleIsr(DbController.iChannels[i], ETrue);
+ }
+ if (DmacDb::ErrorInt & mask)
+ {
+ DmacDb::ErrorInt &= ~mask;
+ HandleIsr(DbController.iChannels[i], EFalse);
+ }
+ }
+ }
+
+//////////////////////////////////////////////////////////////////////////////
+
+class DSimSgController : public TDmac
+ {
+public:
+ DSimSgController();
+private:
+ static void Isr();
+ // from TDmac
+ 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);
+ 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);
+ virtual TInt FailNext(const TDmaChannel& aChannel);
+ virtual TInt MissNextInterrupts(const TDmaChannel& aChannel, TInt aInterruptCount);
+private:
+ inline DmacSg::SDes* HdrToHwDes(const SDmaDesHdr& aHdr);
+public:
+ static const SCreateInfo KInfo;
+ TDmaSgChannel iChannels[KChannelCount];
+ };
+
+DSimSgController SgController;
+
+const TDmac::SCreateInfo DSimSgController::KInfo =
+ {
+ KChannelCount,
+ KDesCount,
+ KCapsBitHwDes,
+ sizeof(DmacSg::SDes),
+#ifdef __WINS__
+ 0,
+#else
+ EMapAttrSupRw|EMapAttrFullyBlocking,
+#endif
+ };
+
+
+inline DmacSg::SDes* DSimSgController::HdrToHwDes(const SDmaDesHdr& aHdr)
+ {
+ return static_cast<DmacSg::SDes*>(TDmac::HdrToHwDes(aHdr));
+ }
+
+
+DSimSgController::DSimSgController()
+ : TDmac(KInfo)
+ {
+ DmacSg::Isr = Isr;
+ }
+
+
+void DSimSgController::Transfer(const TDmaChannel& aChannel, const SDmaDesHdr& aHdr)
+ {
+ TUint32 i = aChannel.PslId();
+ DmacSg::NextDes[i] = HdrToHwDes(aHdr);
+ DmacSg::Enable(i);
+ }
+
+
+void DSimSgController::StopTransfer(const TDmaChannel& aChannel)
+ {
+ __e32_atomic_and_ord32(&DmacSg::ChannelControl[aChannel.PslId()], (TUint32)~DmacSg::EChannelBitRun);
+ }
+
+
+void DSimSgController::InitHwDes(const SDmaDesHdr& aHdr, TUint32 aSrc, TUint32 aDest, TInt aCount,
+ TUint /*aFlags*/, TUint32 /*aPslInfo*/, TUint32 /*aCookie*/)
+ {
+ DmacSg::SDes& des = *HdrToHwDes(aHdr);
+ des.iSrcAddr = reinterpret_cast<TUint8*>(aSrc);
+ des.iDestAddr = reinterpret_cast<TUint8*>(aDest);
+ des.iCount = static_cast<TInt16>(aCount);
+ des.iControl |= DmacSg::EDesBitInt;
+ des.iNext = NULL;
+ }
+
+
+void DSimSgController::ChainHwDes(const SDmaDesHdr& aHdr, const SDmaDesHdr& aNextHdr)
+ {
+ DmacSg::SDes& des = *HdrToHwDes(aHdr);
+ des.iControl &= ~DmacSg::EDesBitInt;
+ des.iNext = HdrToHwDes(aNextHdr);
+ }
+
+
+void DSimSgController::AppendHwDes(const TDmaChannel& aChannel, const SDmaDesHdr& aLastHdr,
+ const SDmaDesHdr& aNewHdr)
+ {
+ TUint32 i = aChannel.PslId();
+ DmacSg::SDes* pNewDes = HdrToHwDes(aNewHdr);
+ TInt prevLevel = NKern::DisableAllInterrupts();
+
+ if ((DmacSg::ChannelControl[i] & DmacSg::EChannelBitRun) == 0)
+ {
+ DmacSg::NextDes[i] = pNewDes;
+ DmacSg::Enable(i);
+ }
+ else if (DmacSg::NextDes[i] == NULL)
+ DmacSg::NextDes[i] = pNewDes;
+ else
+ HdrToHwDes(aLastHdr)->iNext = pNewDes;
+
+ NKern::RestoreInterrupts(prevLevel);
+ }
+
+
+void DSimSgController::UnlinkHwDes(const TDmaChannel& /*aChannel*/, SDmaDesHdr& aHdr)
+ {
+ DmacSg::SDes* pD = HdrToHwDes(aHdr);
+ pD->iNext = NULL;
+ pD->iControl |= DmacSg::EDesBitInt;
+ }
+
+
+TInt DSimSgController::FailNext(const TDmaChannel& aChannel)
+ {
+ __DMA_ASSERTD((DmacSg::ChannelControl[aChannel.PslId()] & DmacSg::EChannelBitRun) == 0);
+ DmacSg::FailCount[aChannel.PslId()] = 1;
+ return KErrNone;
+ }
+
+
+TInt DSimSgController::MissNextInterrupts(const TDmaChannel& aChannel, TInt aInterruptCount)
+ {
+ __DMA_ASSERTD((DmacSg::ChannelControl[aChannel.PslId()] & DmacSg::EChannelBitRun) == 0);
+ __DMA_ASSERTD(aInterruptCount >= 0);
+ DmacSg::InterruptsToMiss[aChannel.PslId()] = aInterruptCount;
+ return KErrNone;
+ }
+
+
+TBool DSimSgController::IsIdle(const TDmaChannel& aChannel)
+ {
+ return (DmacSg::ChannelControl[aChannel.PslId()] & DmacSg::EChannelBitRun) == 0;
+ }
+
+
+TInt DSimSgController::MaxTransferSize(TDmaChannel& /*aChannel*/, TUint /*aFlags*/, TUint32 /*aPslInfo*/)
+ {
+ return KMaxTransferSize;
+ }
+
+
+TUint DSimSgController::MemAlignMask(TDmaChannel& /*aChannel*/, TUint /*aFlags*/, TUint32 /*aPslInfo*/)
+ {
+ return KMemAlignMask;
+ }
+
+
+void DSimSgController::Isr()
+ {
+ for (TInt i = 0; i < KChannelCount; i++)
+ {
+ TUint32 mask = (1 << i);
+ if (DmacSg::CompletionInt & mask)
+ {
+ DmacSg::CompletionInt &= ~mask;
+ HandleIsr(SgController.iChannels[i], ETrue);
+ }
+ if (DmacSg::ErrorInt & mask)
+ {
+ DmacSg::ErrorInt &= ~mask;
+ HandleIsr(SgController.iChannels[i], EFalse);
+ }
+ }
+ }
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Channel opening/closing
+
+enum TController { ESb=0, EDb=1, ESg=2 };
+
+const TUint32 KControllerMask = 0x30;
+const TUint32 KControllerShift = 4;
+const TUint32 KChannelIdxMask = 3;
+
+#define MKCHN(type, idx) (((type)<<KControllerShift)|idx)
+
+static TUint32 TestSbChannels[] = { MKCHN(ESb,0), MKCHN(ESb,1), MKCHN(ESb,2), MKCHN(ESb,3) };
+static TUint32 TestDbChannels[] = { MKCHN(EDb,0), MKCHN(EDb,1), MKCHN(EDb,2), MKCHN(EDb,3) };
+static TUint32 TestSgChannels[] = { MKCHN(ESg,0), MKCHN(ESg,1), MKCHN(ESg,2), MKCHN(ESg,3) };
+
+static TDmaTestInfo TestInfo =
+ {
+ KMaxTransferSize,
+ KMemAlignMask,
+ 0,
+ KChannelCount,
+ TestSbChannels,
+ KChannelCount,
+ TestDbChannels,
+ KChannelCount,
+ TestSgChannels,
+ };
+
+EXPORT_C const TDmaTestInfo& DmaTestInfo()
+ {
+ return TestInfo;
+ }
+
+// Keep track of opened channels so Tick callback used to fake DMA
+// transfers is enabled only when necessary.
+static TInt OpenChannelCount = 0;
+
+
+TDmaChannel* DmaChannelMgr::Open(TUint32 aOpenId)
+ {
+ TInt dmac = (aOpenId & KControllerMask) >> KControllerShift;
+ __DMA_ASSERTD(dmac < 3);
+ TInt i = aOpenId & KChannelIdxMask;
+ TDmaChannel* pC = NULL;
+ TDmac* controller = NULL;
+ switch (dmac)
+ {
+ case ESb:
+ pC = SbController.iChannels + i;
+ controller = &SbController;
+ break;
+ case EDb:
+ pC = DbController.iChannels + i;
+ controller = &DbController;
+ break;
+ case ESg:
+ pC = SgController.iChannels + i;
+ controller = &SgController;
+ break;
+ default:
+ __DMA_CANT_HAPPEN();
+ }
+
+ if (++OpenChannelCount == 1)
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("Enabling DMA simulation"));
+ DmacSim::StartEmulation();
+ }
+ if (pC->IsOpened())
+ return NULL;
+ pC->iController = controller;
+ pC->iPslId = i;
+ return pC;
+ }
+
+
+void DmaChannelMgr::Close(TDmaChannel* /*aChannel*/)
+ {
+ if (--OpenChannelCount == 0)
+ {
+ DmacSim::StopEmulation();
+ __KTRACE_OPT(KDMA, Kern::Printf("Stopping DMA simulation"));
+ }
+ }
+
+TInt DmaChannelMgr::StaticExtension(TInt /*aCmd*/, TAny* /*aArg*/)
+ {
+ return KErrNotSupported;
+ }
+
+//////////////////////////////////////////////////////////////////////////////
+
+//
+// On hardware, this code is inside a kernel extension.
+//
+
+DECLARE_STANDARD_EXTENSION()
+ {
+ __KTRACE_OPT(KDMA, Kern::Printf("Starting DMA simulator..."));
+ TInt r;
+ r = SbController.Create(DSimSbController::KInfo);
+ if (r != KErrNone)
+ return r;
+ r = DbController.Create(DSimDbController::KInfo);
+ if (r != KErrNone)
+ return r;
+ r = SgController.Create(DSimSgController::KInfo);
+ if (r != KErrNone)
+ return r;
+
+ return KErrNone;
+ }
+
+//
+// On WINS, this code is inside a LDD (see mmp file) so we need some
+// bootstrapping code to call the kernel extension entry point.
+//
+
+class DDummyLdd : public DLogicalDevice
+ {
+public:
+ // from DLogicalDevice
+ TInt Install();
+ void GetCaps(TDes8& aDes) const;
+ TInt Create(DLogicalChannelBase*& aChannel);
+ };
+
+TInt DDummyLdd::Create(DLogicalChannelBase*& aChannel)
+ {
+ aChannel=NULL;
+ return KErrNone;
+ }
+
+TInt DDummyLdd::Install()
+ {
+ _LIT(KLddName, "DmaSim");
+ TInt r = SetName(&KLddName);
+ if (r == KErrNone)
+ r = InitExtension();
+ return r;
+ }
+
+void DDummyLdd::GetCaps(TDes8& /*aDes*/) const
+ {
+ }
+
+EXPORT_C DLogicalDevice* CreateLogicalDevice()
+ {
+ return new DDummyLdd;
+ }
+
+
+//---