kerneltest/e32test/dma/dmasim.cpp
changeset 0 a41df078684a
child 87 2f92ad2dc5db
child 90 947f0dc9f7a8
--- /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;
+	}
+
+
+//---