--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32test/dma/t_dma.cpp Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,980 @@
+// 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\t_dma.cpp
+// Overview:
+// Test the DMA channel functionality.
+// API Information:
+// RBusLogicalChannel, DLogicalChannelBase, DLogicalDevice
+// Details:
+// - Load the DMA LDD, create a critical section, an active scheduler and
+// a CPeriodic object.
+// - Test one shot single buffer transfers: test simple transfer, request
+// reconfiguration and cancelling. Verify results are as expected.
+// - Test one shot double buffer transfers: test simple transfer, request
+// reconfiguration and cancelling. Verify results are as expected.
+// - Test streaming single buffer transfers: test simple transfer and
+// cancelling. Test that framework behaves correctly if one or more DMA
+// interrupts are missed. Verify results are as expected.
+// - Test streaming double buffer transfers: test simple transfer and
+// cancelling. Test that framework behaves correctly if one or more DMA
+// interrupts are missed. Verify results are as expected.
+// - Test streaming scatter/gather transfers: test simple transfer and
+// cancelling. Test that framework behaves correctly if one or more DMA
+// interrupts are missed. Verify results are as expected.
+// Platforms/Drives/Compatibility:
+// Hardware (Automatic).
+// Assumptions/Requirement/Pre-requisites:
+// Failures and causes:
+// Base Port information:
+//
+//
+
+#define __E32TEST_EXTENSION__
+#include <e32test.h>
+#include "d_dma.h"
+#include <e32debug.h>
+#include <e32svr.h>
+#include <e32def.h>
+#include <e32def_private.h>
+#include "u32std.h"
+
+#ifdef __DMASIM__
+RTest test(_L("T_DMASIM"));
+#else
+RTest test(_L("T_DMA"));
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+// Mini-framework for running tests either in a single thread or in
+// several concurrent ones.
+
+RTestDma::TInfo Info;
+TBool JitEnabled;
+RCriticalSection TheCriticalSection; // protect following variables
+TInt ThreadCount; // decremented when tester thread dies
+CPeriodic* Bipper; // display dots during tests to detect lock-ups
+
+// Test macro used inside tester threads
+_LIT(KTestFailure, "XTEST");
+static void TestPanic(TInt aLine, TUint32 a1, TUint32 a2, TUint32 a3)
+ {
+ RDebug::Printf("Line %d test failed a1=%08x a2=%08x a3=%08x", aLine, a1, a2, a3);
+ RThread().Panic(KTestFailure, aLine);
+ }
+#define XTEST(e) if (!(e)) TestPanic(__LINE__, 0, 0, 0)
+#define XTEST1(e,a1) if (!(e)) TestPanic(__LINE__, (a1), 0, 0)
+#define XTEST2(e,a1,a2) if (!(e)) TestPanic(__LINE__, (a1), (a2), 0)
+#define XTEST3(e,a1,a2,a3) if (!(e)) TestPanic(__LINE__, (a1), (a2), (a3))
+
+
+/**
+Specifies a DMA test
+@note Have not inherited from CBase so that implicit copy ctors are used
+*/
+class CTest
+ {
+public:
+ typedef void (*TTestFunction)(RTestDma aChannel, TInt aMaxFragment, TInt aFragmentSize);
+
+ CTest(TTestFunction aFn, TInt aMaxIter)
+ :iTestFn(aFn), iChannelId(0), iMaxIter(aMaxIter)
+ {}
+
+ virtual ~CTest()
+ {}
+
+ TInt RunTest();
+
+ virtual TBool OpenChannel(TInt aDesCount, TInt aMaxFragmentSize=0);
+
+ virtual void AnnounceTest(TDes& aDes)
+ {aDes.AppendFormat(_L("Channel Id %d, iMaxIter %d"), iChannelId, iMaxIter);}
+ virtual void ReportState(TDes& aDes)
+ {aDes.AppendFormat(_L("Channel Id %d, iCurIter %d"), iChannelId, iCurIter);}
+
+
+ void SetChannelId(TUint32 aChannelId)
+ {iChannelId = aChannelId;}
+
+ TInt MaxIter() const {return iMaxIter;}
+ TInt CurIter() const {return iCurIter;}
+
+ /**
+ @return A copy of this test
+ */
+ virtual CTest* Clone() const =0;
+
+protected:
+ TInt virtual DoRunTest() =0;
+
+ const TTestFunction iTestFn;
+ TUint32 iChannelId;
+ const TInt iMaxIter;
+ TInt iCurIter;
+ RTestDma iChannel;
+ };
+
+/**
+Specifies a DMA test where the maximum fragmentation is
+explicitly limited. This tests that requests are split
+in to the number of fragments expected.
+
+This test also requires that physically contiguous buffers
+are used. For this reason the product of iMaxFragment and
+iMaxFragmentSize should be kept small
+*/
+class CFragmentationTest : public CTest
+ {
+public:
+ CFragmentationTest(TTestFunction aFn, TInt aMaxIter, TInt aMaxFragment, TInt aMaxFragmentSize)
+ : CTest(aFn, aMaxIter), iMaxFragment(aMaxFragment), iMaxFragmentSize(aMaxFragmentSize), iCurFragment(0)
+ {}
+
+ TInt virtual DoRunTest();
+
+ virtual void AnnounceTest(TDes& aDes)
+ {
+ aDes.AppendFormat(_L("CFragmentationTest: Frag count = [1..%d], Max Frag Size = 0x%08x bytes: "), iMaxFragment, iMaxFragmentSize);
+ CTest::AnnounceTest(aDes);
+ }
+
+ virtual void ReportState(TDes& aDes)
+ {
+ aDes.AppendFormat(_L("CFragmentationTest: Current Fragment %d: "), iCurFragment);
+ CTest::ReportState(aDes);
+ }
+
+ CTest* Clone() const
+ {return new CFragmentationTest(*this);}
+
+private:
+ const TInt iMaxFragment;
+ TInt iMaxFragmentSize;
+ TInt iCurFragment;
+ };
+
+/**
+Specifies a DMA test where the maximum fragment size is
+not limited - and we do not care how many fragments are
+used
+
+- This checks that transfers work correctly with the DMAC's
+default fragment size
+*/
+class CDefaultFragTest : public CTest
+ {
+public:
+ CDefaultFragTest(TTestFunction aFn, TInt aMaxIter, TUint aTotalTransferSize)
+ : CTest(aFn, aMaxIter), iTotalTransferSize(aTotalTransferSize)
+ {}
+
+ TInt virtual DoRunTest();
+
+ virtual void AnnounceTest(TDes& aDes)
+ {
+ aDes.AppendFormat(_L("CDefaultFragTest: Transfer = 0x%08x bytes: "), iTotalTransferSize);
+ CTest::AnnounceTest(aDes);
+ }
+
+ CTest* Clone() const
+ {return new CDefaultFragTest(*this);}
+
+ const TInt iTotalTransferSize;
+ };
+
+
+//
+// Active object used to create a tester thread, log on to it and
+// interpret its exit status.
+//
+class CTesterThread : public CActive
+ {
+public:
+ CTesterThread(TInt aIdx, CTest* aTest);
+ ~CTesterThread()
+ {
+ delete iTest;
+ }
+private:
+ static TInt ThreadFunction(TAny* aSelf);
+ TInt StartThread();
+ // from CActive
+ virtual void DoCancel();
+ virtual void RunL();
+private:
+ RThread iThread;
+ CTest* iTest;
+ };
+
+
+/**
+Run the test for iMaxIter iterations
+*/
+TInt CTest::RunTest()
+ {
+ TInt r = KErrNone;
+ for (iCurIter=0; iCurIter<iMaxIter; ++iCurIter)
+ {
+ r = DoRunTest();
+ if(KErrNone != r)
+ break;
+ }
+ return r;
+ }
+
+/**
+Open iChannel
+
+@pre iChannel is not open
+@return
+ - KErrNotSupported Channel does not exist on DMAC
+ - KErrNone Success
+ - KErrInUse
+*/
+TInt CTest::OpenChannel(TInt aDesCount, TInt aMaxFragmentSize)
+ {
+ ASSERT(!iChannel.Handle());
+ const TInt r = iChannel.Open(iChannelId, aDesCount, aMaxFragmentSize);
+ if (r == KErrNotSupported)
+ return r;
+ XTEST1(KErrNone == r || KErrInUse == r, r);
+
+ if(KErrInUse == r)
+ {
+ // Channel is in use.
+ RDebug::Printf("\nDMA Channel %d is in use",iChannelId);
+ if(0 == iCurIter)
+ {
+ // Terminate thread by returning this error code KErrInUse.
+ return r;
+ }
+ else
+ {
+#ifdef __WINS__
+#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant
+#endif // __WINS__
+ XTEST1(EFalse, iCurIter);
+#ifdef __WINS__
+#pragma warning( default : 4127 ) // warning C4127: conditional expression is constant
+#endif // __WINS__
+ }
+ }
+ return r;
+ }
+
+
+
+// Spawn thread. Will auto-delete when thread exits.
+CTesterThread::CTesterThread(TInt aIdx, CTest* aTest)
+ : CActive(EPriorityStandard), iTest(aTest)
+ {
+ CActiveScheduler::Add(this);
+ TBuf<16> name;
+ name = _L("TESTER-");
+ name.AppendNum(aIdx);
+ test(iThread.Create(name, ThreadFunction, 0x1000, NULL, this) == KErrNone);
+ iThread.SetPriority(EPriorityLess);
+ iThread.Logon(iStatus);
+ SetActive();
+ iThread.Resume();
+ }
+
+
+TInt CTesterThread::ThreadFunction(TAny* aSelf)
+ {
+ CTesterThread* self = (CTesterThread*)aSelf;
+ return self->StartThread();
+ }
+
+TInt CTesterThread::StartThread()
+ {
+ return iTest->RunTest();
+ }
+
+
+
+TInt CFragmentationTest::DoRunTest()
+ {
+ // In case iMaxFragmentSize was larger than suppported (we need to know what fragment
+ // size will actually be used)
+ iMaxFragmentSize = Min(iMaxFragmentSize, Info.iMaxTransferSize);
+
+ // Open channel with enough descriptors for 3 open DMA
+ // requests (see TestStreaming).
+ TInt r = OpenChannel(3* iMaxFragment, iMaxFragmentSize);
+ if(r != KErrNone)
+ return r;
+
+ //we are controlling fragment size, so we know how
+ //many to expect
+ for (iCurFragment=1; iCurFragment<=iMaxFragment; iCurFragment*=2)
+ {
+ const TInt size = iCurFragment * ( iMaxFragmentSize & ~Info.iMemAlignMask);
+ iTestFn(iChannel, iCurFragment, size);
+ }
+ iChannel.Close();
+ return KErrNone;
+ }
+
+TInt CDefaultFragTest::DoRunTest()
+ {
+ // +1 so we don't underestimate maxFragount for inexact division
+ const TUint maxFragCount = (iTotalTransferSize / Info.iMaxTransferSize) +1;
+
+ // Open channel with enough descriptors for 3 open DMA
+ // requests (see TestStreaming).
+ const TUint descriptorCount = 3 * maxFragCount;
+
+ TInt r = OpenChannel(descriptorCount);
+ if(r != KErrNone)
+ return r;
+
+ iTestFn(iChannel, 0, iTotalTransferSize);
+
+ iChannel.Close();
+ return KErrNone;
+ }
+
+
+// Called when thread completed.
+void CTesterThread::RunL()
+ {
+ TExitType et = iThread.ExitType();
+ TInt er = iThread.ExitReason();
+ TExitCategoryName ec = iThread.ExitCategory();
+ TName name = iThread.Name();
+ CLOSE_AND_WAIT(iThread);
+
+ switch (et)
+ {
+ case EExitKill:
+ // nothing to do
+ break;
+ case EExitPanic:
+ {
+ User::SetJustInTime(JitEnabled);
+ TBuf<128> buffer;
+ iTest->ReportState(buffer);
+ test.Printf(_L("Tester Thread Panic: %S: Test: %S\n"),
+ &name, &buffer);
+ if (ec.Match(KTestFailure) == 0)
+ test.Panic(_L("Test failure line %d"), er);
+ else
+ test.Panic(_L("Unexpected panic: %S-%d"), &ec, er);
+ break;
+ }
+ default:
+ test.Panic(_L("Invalid thread exit type"));
+ }
+
+ TheCriticalSection.Wait();
+ if (--ThreadCount == 0)
+ {
+ Bipper->Cancel();
+ test.Console()->Printf(_L("\n"));
+ CActiveScheduler::Stop();
+ }
+ TheCriticalSection.Signal();
+
+ // We commit suicide as the alternative (being deleted by
+ // RunTest()) implies keeping a list of all instances in
+ // RunTest().
+ delete this;
+ }
+
+
+void CTesterThread::DoCancel()
+ {
+ test.Panic(_L("CTesterThread::DoCancel called"));
+ }
+
+
+static TInt Bip(TAny*)
+ {
+ test.Console()->Printf(_L("."));
+ return 0;
+ }
+
+
+// Execute provided test object in one or more tester threads.
+void RunTest(TUint32 aChannelIds[], TInt aMaxThread, CTest* aTest)
+ {
+ test_NotNull(aTest);
+
+ if (aMaxThread == 0)
+ {
+ delete aTest;
+ test.Printf(_L("transfer mode not supported - skipped\n"));
+ return;
+ }
+
+ test.Printf(_L("Using %d thread(s)\n"), aMaxThread);
+
+ // We don't want JIT debugging here because the tester threads may panic
+ JitEnabled = User::JustInTime();
+ User::SetJustInTime(EFalse);
+
+ // must be set before spawning threads to avoid premature active scheduler stop
+ ThreadCount = aMaxThread;
+
+ TBuf<128> buffer;
+ for (TInt i=0; i<aMaxThread; ++i)
+ {
+ //each CTesterThread needs its own CTest object
+ CTest* dmaTest = aTest->Clone();
+ test_NotNull(dmaTest);
+
+ dmaTest->SetChannelId(aChannelIds[i]);
+
+ buffer.Zero();
+ dmaTest->AnnounceTest(buffer);
+ test.Printf(_L("Thread %d: %S\n"), i, &buffer);
+
+ test(new CTesterThread(i, dmaTest) != NULL);
+ dmaTest = NULL; //ownership transferred to CTesterThread
+ }
+ //the orginal isn't needed
+ delete aTest;
+ aTest = NULL;
+
+ const TTimeIntervalMicroSeconds32 KPeriod = 1000000; // 1s
+ Bipper->Start(KPeriod, KPeriod, Bip);
+
+ CActiveScheduler::Start();
+
+ User::SetJustInTime(JitEnabled);
+ }
+
+
+inline void RunSbTest(TInt aMaxThread, CTest* aTest)
+ {
+ RunTest(Info.iSbChannels, Min(aMaxThread,Info.iMaxSbChannels), aTest);
+ }
+
+inline void RunDbTest(TInt aMaxThread, CTest* aTest)
+ {
+ RunTest(Info.iDbChannels, Min(aMaxThread,Info.iMaxDbChannels), aTest);
+ }
+
+inline void RunSgTest(TInt aMaxThread, CTest* aTest)
+ {
+ RunTest(Info.iSgChannels, Min(aMaxThread,Info.iMaxSgChannels), aTest);
+ }
+//////////////////////////////////////////////////////////////////////////////
+
+static void GetChannelInfo()
+ {
+ RTestDma channel;
+ test(channel.GetInfo(Info) == KErrNone);
+ test(Info.iMaxSbChannels>0 || Info.iMaxDbChannels>0 || Info.iMaxSgChannels>0);
+ }
+
+
+static void TestOneShot(RTestDma aChannel, TInt aFragmentCount, TInt aSize)
+ {
+ const TInt KRequest = 0;
+ const TInt KSrcBuf = 0;
+ const TInt KDestBuf1 = 1;
+ const TInt KDestBuf2 = 2;
+
+ TInt r = aChannel.AllocBuffer(KSrcBuf, aSize);
+ XTEST2(r == KErrNone, r, aSize);
+ aChannel.FillBuffer(KSrcBuf, 'A');
+ r = aChannel.AllocBuffer(KDestBuf1, aSize);
+ XTEST2(r == KErrNone, r, aSize);
+ aChannel.FillBuffer(KDestBuf1, '\0');
+ r = aChannel.AllocBuffer(KDestBuf2, aSize);
+ XTEST2(r == KErrNone, r, aSize);
+ aChannel.FillBuffer(KDestBuf2, '\0');
+
+ // Test simple transfer
+ TRequestStatus rs;
+ r = aChannel.Fragment(KRequest, KSrcBuf, KDestBuf1, aSize, &rs);
+ XTEST2(r == KErrNone, r, aSize);
+ test(aChannel.FragmentCheck(KRequest, aFragmentCount));
+ r = aChannel.Execute(_L8("Q0"));
+ XTEST1(r == KErrNone, r);
+ User::WaitForRequest(rs);
+ XTEST1(rs == KErrNone, rs.Int());
+ XTEST(aChannel.CheckBuffer(KDestBuf1, 'A'));
+
+ // Test request reconfiguration.
+ aChannel.FillBuffer(KDestBuf1, '\0');
+ r = aChannel.Fragment(KRequest, KSrcBuf, KDestBuf2, aSize, &rs);
+ XTEST2(r == KErrNone, r, aSize);
+ test(aChannel.FragmentCheck(KRequest, aFragmentCount));
+ r = aChannel.Execute(_L8("Q0"));
+ XTEST1(r == KErrNone, r);
+ User::WaitForRequest(rs);
+ XTEST1(rs == KErrNone, rs.Int());
+ XTEST(aChannel.CheckBuffer(KDestBuf1, '\0')); // previous dest unchanged?
+ XTEST(aChannel.CheckBuffer(KDestBuf2, 'A'));
+
+ // Test cancelling
+ aChannel.FillBuffer(KDestBuf1, '\0');
+ r = aChannel.Fragment(KRequest, KSrcBuf, KDestBuf1, aSize);
+ XTEST2(r == KErrNone, r, aSize);
+ test(aChannel.FragmentCheck(KRequest, aFragmentCount));
+ r = aChannel.Execute(_L8("Q0C"));
+ XTEST1(r == KErrNone, r);
+ // Part of the destination buffer should be unchanged if the
+ // cancel occured before the transfer completed.
+#ifdef __DMASIM__
+ // At least part of the last destination buffer should be
+ // unchanged if cancel occured before the transfer completed.
+ // Assert only on WINS as real DMACs are too fast.
+ XTEST(! aChannel.CheckBuffer(KDestBuf2, 'C'));
+#endif
+
+ // Perform another transfer to ensure cancel operation let the
+ // framework in a consistent state.
+ aChannel.FillBuffer(KDestBuf1, '\0');
+ r = aChannel.Fragment(KRequest, KSrcBuf, KDestBuf1, aSize, &rs);
+ XTEST2(r == KErrNone, r, aSize);
+ test(aChannel.FragmentCheck(KRequest, aFragmentCount));
+ r = aChannel.Execute(_L8("Q0"));
+ XTEST1(r == KErrNone, r);
+ User::WaitForRequest(rs);
+ XTEST1(rs == KErrNone, rs.Int());
+ XTEST(aChannel.CheckBuffer(KDestBuf1, 'A'));
+
+ //
+ // Test failure if the underlying DMA kernel extension allows it.
+ //
+ // As long as only "CancelAllFragments" is supported, it's okay to
+ // always fail on the first fragment.
+ //
+
+ if (aChannel.FailNext(1) == KErrNone)
+ {
+ aChannel.FillBuffer(KDestBuf1, '\0');
+ r = aChannel.Fragment(KRequest, KSrcBuf, KDestBuf1, aSize, &rs);
+ XTEST2(r == KErrNone, r, aSize);
+ test(aChannel.FragmentCheck(KRequest, aFragmentCount));
+ r = aChannel.Execute(_L8("Q0"));
+ XTEST1(r == KErrNone, r);
+ User::WaitForRequest(rs);
+ XTEST1(rs != KErrNone, rs.Int());
+ XTEST(! aChannel.CheckBuffer(KDestBuf1, 'A'));
+ r = aChannel.Execute(_L8("C"));
+ XTEST1(r == KErrNone, r);
+
+ // Perform another transfer to ensure we are still in a
+ // consistent state.
+ aChannel.FillBuffer(KDestBuf1, '\0');
+ r = aChannel.Fragment(KRequest, KSrcBuf, KDestBuf1, aSize, &rs);
+ XTEST2(r == KErrNone, r, aSize);
+ test(aChannel.FragmentCheck(KRequest, aFragmentCount));
+ r = aChannel.Execute(_L8("Q0"));
+ XTEST1(r == KErrNone, r);
+ User::WaitForRequest(rs);
+ XTEST1(rs == KErrNone, rs.Int());
+ XTEST(aChannel.CheckBuffer(KDestBuf1, 'A'));
+ }
+
+ aChannel.FreeAllBuffers();
+ }
+
+
+static void TestStreaming(RTestDma aChannel, TInt aFragmentCount, TInt aSize)
+ {
+ const TInt KRequest0 = 0;
+ const TInt KRequest1 = 1;
+ const TInt KRequest2 = 2;
+ const TInt KSrcBuf0 = 0;
+ const TInt KSrcBuf1 = 1;
+ const TInt KSrcBuf2 = 2;
+ const TInt KDestBuf0 = 3;
+ const TInt KDestBuf1 = 4;
+ const TInt KDestBuf2 = 5;
+
+ //
+ // Allocate and initialise source buffers
+ //
+
+ TInt r = aChannel.AllocBuffer(KSrcBuf0, aSize);
+ XTEST2(r == KErrNone, r, aSize);
+ aChannel.FillBuffer(KSrcBuf0, 'A');
+
+ r = aChannel.AllocBuffer(KSrcBuf1, aSize);
+ XTEST2(r == KErrNone, r, aSize);
+ aChannel.FillBuffer(KSrcBuf1, 'B');
+
+ r = aChannel.AllocBuffer(KSrcBuf2, aSize);
+ XTEST2(r == KErrNone, r, aSize);
+ aChannel.FillBuffer(KSrcBuf2, 'C');
+
+ //
+ // Allocate destination buffers
+ //
+
+ r = aChannel.AllocBuffer(KDestBuf0, aSize);
+ XTEST2(r == KErrNone, r, aSize);
+ r = aChannel.AllocBuffer(KDestBuf1, aSize);
+ XTEST2(r == KErrNone, r, aSize);
+ r = aChannel.AllocBuffer(KDestBuf2, aSize);
+ XTEST2(r == KErrNone, r, aSize);
+
+ //
+ // Test simple transfer.
+ // (no need to test for request reconfiguration afterwards because
+ // this was exercised in the one-shot test case)
+ //
+
+ TRequestStatus rs0;
+ r = aChannel.Fragment(KRequest0, KSrcBuf0, KDestBuf0, aSize, &rs0);
+ XTEST2(r == KErrNone, r, aSize);
+ test(aChannel.FragmentCheck(KRequest0, aFragmentCount));
+ TRequestStatus rs1;
+ r = aChannel.Fragment(KRequest1, KSrcBuf1, KDestBuf1, aSize, &rs1);
+ XTEST2(r == KErrNone, r, aSize);
+ test(aChannel.FragmentCheck(KRequest1, aFragmentCount));
+ TRequestStatus rs2;
+ r = aChannel.Fragment(KRequest2, KSrcBuf2, KDestBuf2, aSize, &rs2);
+ XTEST2(r == KErrNone, r, aSize);
+ test(aChannel.FragmentCheck(KRequest2, aFragmentCount));
+
+ r = aChannel.Execute(_L8("Q0Q1Q2"));
+ XTEST1(r == KErrNone, r);
+ User::WaitForRequest(rs0);
+ XTEST1(rs0 == KErrNone, rs0.Int());
+ XTEST(aChannel.CheckBuffer(KDestBuf0, 'A'));
+ User::WaitForRequest(rs1);
+ XTEST1(rs1 == KErrNone, rs1.Int());
+ XTEST(aChannel.CheckBuffer(KDestBuf1, 'B'));
+ User::WaitForRequest(rs2);
+ XTEST1(rs2 == KErrNone, rs2.Int());
+ XTEST(aChannel.CheckBuffer(KDestBuf2, 'C'));
+
+ //
+ // Test cancel
+ //
+
+ aChannel.FillBuffer(KDestBuf0, '\0');
+ aChannel.FillBuffer(KDestBuf1, '\0');
+ aChannel.FillBuffer(KDestBuf2, '\0');
+
+ r = aChannel.Fragment(KRequest0, KSrcBuf0, KDestBuf0, aSize);
+ XTEST2(r == KErrNone, r, aSize);
+ test(aChannel.FragmentCheck(KRequest0, aFragmentCount));
+ r = aChannel.Fragment(KRequest1, KSrcBuf1, KDestBuf1, aSize);
+ XTEST2(r == KErrNone, r, aSize);
+ test(aChannel.FragmentCheck(KRequest1, aFragmentCount));
+ r = aChannel.Fragment(KRequest2, KSrcBuf2, KDestBuf2, aSize);
+ XTEST2(r == KErrNone, r, aSize);
+ test(aChannel.FragmentCheck(KRequest2, aFragmentCount));
+
+ r = aChannel.Execute(_L8("Q0Q1Q2C"));
+ XTEST1(r == KErrNone, r);
+#ifdef __DMASIM__
+ // At least part of the last destination buffer should be
+ // unchanged if cancel occured before the transfer completed.
+ // Assert only on WINS as real DMACs are too fast.
+ XTEST(! aChannel.CheckBuffer(KDestBuf2, 'C'));
+#endif
+
+ //
+ // Perform another transfer to ensure cancel operation let the
+ // framework in a consistent state.
+ //
+
+ aChannel.FillBuffer(KDestBuf0, '\0');
+ aChannel.FillBuffer(KDestBuf1, '\0');
+ aChannel.FillBuffer(KDestBuf2, '\0');
+ // Reconfigure last request to enable transfer completion notification
+ r = aChannel.Fragment(KRequest2, KSrcBuf2, KDestBuf2, aSize, &rs2);
+ XTEST2(r == KErrNone, r, aSize);
+ test(aChannel.FragmentCheck(KRequest2, aFragmentCount));
+ r = aChannel.Execute(_L8("Q0Q1Q2"));
+ XTEST1(r == KErrNone, r);
+ User::WaitForRequest(rs2);
+ XTEST1(rs2 == KErrNone, rs2.Int());
+ XTEST(aChannel.CheckBuffer(KDestBuf0, 'A'));
+ XTEST(aChannel.CheckBuffer(KDestBuf1, 'B'));
+ XTEST(aChannel.CheckBuffer(KDestBuf2, 'C'));
+
+ //
+ // Test for proper implementation of UnlinkHwDes() in the PSL.
+ //
+
+ aChannel.FillBuffer(KDestBuf0, '\0');
+ aChannel.FillBuffer(KDestBuf1, '\0');
+ aChannel.FillBuffer(KDestBuf2, '\0');
+ // Reconfigure last request to enable transfer completion notification
+ r = aChannel.Fragment(KRequest2, KSrcBuf2, KDestBuf2, aSize, &rs2);
+ XTEST2(r == KErrNone, r, aSize);
+ test(aChannel.FragmentCheck(KRequest2, aFragmentCount));
+ // Queue first request (Q0)
+ r = aChannel.Execute(_L8("Q0"));
+ // Wait a second, so next request will be queued on its own
+ // (instead of being appended to the previous one)
+ User::After(1000000);
+ // Queue third request (Q2)
+ r = aChannel.Execute(_L8("Q2"));
+ XTEST1(r == KErrNone, r);
+ User::WaitForRequest(rs2);
+ XTEST1(rs2 == KErrNone, rs2.Int());
+ XTEST(aChannel.CheckBuffer(KDestBuf0, 'A'));
+ // KDestBuf1 should have been left untouched!
+ // If we find all B's in KDestBuf1, that means the last descriptor of the
+ // first request (Q0) wasn't properly unlinked and still points to the Q1
+ // descriptor chain from the previous run.
+ XTEST(aChannel.CheckBuffer(KDestBuf1, '\0'));
+ XTEST(aChannel.CheckBuffer(KDestBuf2, 'C'));
+
+ //
+ // Test failure if the underlying DMA kernel extension allows it.
+ //
+ // As long as only "CancelAllFragments" is supported, it's okay to
+ // always fail on the first fragment.
+ //
+
+ if (aChannel.FailNext(1) == KErrNone)
+ {
+ aChannel.FillBuffer(KDestBuf0, '\0');
+ aChannel.FillBuffer(KDestBuf1, '\0');
+ aChannel.FillBuffer(KDestBuf2, '\0');
+ XTEST(aChannel.Fragment(KRequest0, KSrcBuf0, KDestBuf0, aSize, &rs0) == KErrNone);
+ test(aChannel.FragmentCheck(KRequest0, aFragmentCount));
+ XTEST(aChannel.Fragment(KRequest1, KSrcBuf1, KDestBuf1, aSize) == KErrNone);
+ test(aChannel.FragmentCheck(KRequest1, aFragmentCount));
+ XTEST(aChannel.Fragment(KRequest2, KSrcBuf2, KDestBuf2, aSize) == KErrNone);
+ test(aChannel.FragmentCheck(KRequest2, aFragmentCount));
+ XTEST(aChannel.Execute(_L8("Q0Q1Q2")) == KErrNone);
+ User::WaitForRequest(rs0);
+ XTEST(rs0 != KErrNone);
+ XTEST(! aChannel.CheckBuffer(KDestBuf0, 'A'));
+ XTEST(! aChannel.CheckBuffer(KDestBuf1, 'B'));
+ XTEST(! aChannel.CheckBuffer(KDestBuf2, 'C'));
+ XTEST(aChannel.Execute(_L8("C")) == KErrNone);
+
+ // Transfer again to ensure cancel cleaned-up correctly
+ aChannel.FillBuffer(KDestBuf0, '\0');
+ aChannel.FillBuffer(KDestBuf1, '\0');
+ aChannel.FillBuffer(KDestBuf2, '\0');
+ XTEST(aChannel.Fragment(KRequest0, KSrcBuf0, KDestBuf0, aSize, &rs0) == KErrNone);
+ test(aChannel.FragmentCheck(KRequest0, aFragmentCount));
+ XTEST(aChannel.Fragment(KRequest1, KSrcBuf1, KDestBuf1, aSize, &rs1) == KErrNone);
+ test(aChannel.FragmentCheck(KRequest1, aFragmentCount));
+ XTEST(aChannel.Fragment(KRequest2, KSrcBuf2, KDestBuf2, aSize, &rs2) == KErrNone);
+ test(aChannel.FragmentCheck(KRequest2, aFragmentCount));
+ XTEST(aChannel.Execute(_L8("Q0Q1Q2")) == KErrNone);
+ User::WaitForRequest(rs0);
+ XTEST(rs0 == KErrNone);
+ User::WaitForRequest(rs1);
+ XTEST(rs1 == KErrNone);
+ User::WaitForRequest(rs2);
+ XTEST(rs2 == KErrNone);
+ XTEST(aChannel.CheckBuffer(KDestBuf0, 'A'));
+ XTEST(aChannel.CheckBuffer(KDestBuf1, 'B'));
+ XTEST(aChannel.CheckBuffer(KDestBuf2, 'C'));
+ }
+
+ //
+ // Test that framework behaves correctly if one or more DMA interrupts are
+ // missed.
+ //
+
+ if (aChannel.MissNextInterrupts(1) == KErrNone)
+ {
+ aChannel.FillBuffer(KDestBuf0, '\0');
+ aChannel.FillBuffer(KDestBuf1, '\0');
+ aChannel.FillBuffer(KDestBuf2, '\0');
+ XTEST(aChannel.Fragment(KRequest0, KSrcBuf0, KDestBuf0, aSize, &rs0) == KErrNone);
+ test(aChannel.FragmentCheck(KRequest0, aFragmentCount));
+ XTEST(aChannel.Fragment(KRequest1, KSrcBuf1, KDestBuf1, aSize, &rs1) == KErrNone);
+ test(aChannel.FragmentCheck(KRequest1, aFragmentCount));
+ XTEST(aChannel.Fragment(KRequest2, KSrcBuf2, KDestBuf2, aSize, &rs2) == KErrNone);
+ test(aChannel.FragmentCheck(KRequest2, aFragmentCount));
+ XTEST(aChannel.Execute(_L8("Q0Q1Q2")) == KErrNone);
+ User::WaitForRequest(rs0);
+ XTEST(rs0 == KErrNone);
+ User::WaitForRequest(rs1);
+ XTEST(rs1 == KErrNone);
+ User::WaitForRequest(rs2);
+ XTEST(rs2 == KErrNone);
+ XTEST(aChannel.CheckBuffer(KDestBuf0, 'A'));
+ XTEST(aChannel.CheckBuffer(KDestBuf1, 'B'));
+ XTEST(aChannel.CheckBuffer(KDestBuf2, 'C'));
+ }
+
+ if (aChannel.MissNextInterrupts(2) == KErrNone)
+ {
+ aChannel.FillBuffer(KDestBuf0, '\0');
+ aChannel.FillBuffer(KDestBuf1, '\0');
+ aChannel.FillBuffer(KDestBuf2, '\0');
+ XTEST(aChannel.Fragment(KRequest0, KSrcBuf0, KDestBuf0, aSize, &rs0) == KErrNone);
+ test(aChannel.FragmentCheck(KRequest0, aFragmentCount));
+ XTEST(aChannel.Fragment(KRequest1, KSrcBuf1, KDestBuf1, aSize, &rs1) == KErrNone);
+ test(aChannel.FragmentCheck(KRequest1, aFragmentCount));
+ XTEST(aChannel.Fragment(KRequest2, KSrcBuf2, KDestBuf2, aSize, &rs2) == KErrNone);
+ test(aChannel.FragmentCheck(KRequest2, aFragmentCount));
+ XTEST(aChannel.Execute(_L8("Q0Q1Q2")) == KErrNone);
+ User::WaitForRequest(rs0);
+ XTEST(rs0 == KErrNone);
+ User::WaitForRequest(rs1);
+ XTEST(rs1 == KErrNone);
+ User::WaitForRequest(rs2);
+ XTEST(rs2 == KErrNone);
+ XTEST(aChannel.CheckBuffer(KDestBuf0, 'A'));
+ XTEST(aChannel.CheckBuffer(KDestBuf1, 'B'));
+ XTEST(aChannel.CheckBuffer(KDestBuf2, 'C'));
+ }
+
+ aChannel.FreeAllBuffers();
+ }
+
+
+static TBool ParseCmdLine(TBool& aCrashDbg, TInt& aMaxfrag, TInt& aMaxIter, TInt& aMaxchannel, TInt& aMaxFragSize)
+//
+// The command line. Syntax is:
+//
+// t_dma [enableCrashDebugger [aMaxFrag [aMaxIter [aMaxchannel [aMaxFragSize]]]]]
+//
+ {
+ TBuf<256> cmdline;
+ User::CommandLine(cmdline);
+ TLex lex(cmdline);
+
+ lex.SkipSpace();
+
+ if (lex.Eos())
+ return ETrue;
+ if (lex.Val(aCrashDbg) != KErrNone)
+ return EFalse;
+ lex.SkipSpace();
+ if (lex.Eos())
+ return ETrue;
+ if (lex.Val(aMaxfrag) != KErrNone)
+ return EFalse;
+ lex.SkipSpace();
+ if (lex.Eos())
+ return ETrue;
+ if (lex.Val(aMaxIter) != KErrNone)
+ return EFalse;
+ lex.SkipSpace();
+ if (lex.Eos())
+ return ETrue;
+ if (lex.Val(aMaxchannel) != KErrNone)
+ return EFalse;
+ lex.SkipSpace();
+ if (lex.Eos())
+ return ETrue;
+
+ return lex.Val(aMaxFragSize) == KErrNone;
+ }
+
+
+TInt E32Main()
+ {
+ test.Title();
+
+ test.Start(_L("Parsing command-line"));
+ // Default values when run with empty command-line
+ TInt maxfrag = 16; // 5 fragments needed to exercise fully double-buffering state machine
+ TInt maxIter = 3;
+ TInt maxchannel = KMaxTInt;
+ TBool crashDbg = EFalse;
+ TInt maxFragSize = 0x4000; //16k
+
+ (void) ParseCmdLine(crashDbg, maxfrag, maxIter, maxchannel, maxFragSize);
+
+ if (crashDbg)
+ {
+ User::SetCritical(User::ESystemCritical);
+ User::SetProcessCritical(User::ESystemCritical);
+ }
+
+ TInt r;
+#if defined(__DMASIM__) && defined(__WINS__)
+ test.Next(_L("Loading DMA simulator"));
+ r = User::LoadLogicalDevice(_L("DMASIM.DLL"));
+ test(r == KErrNone || r == KErrAlreadyExists);
+#endif
+
+ test.Next(_L("Loading test LDD"));
+#ifdef __DMASIM__
+ r = User::LoadLogicalDevice(_L("D_DMASIM"));
+#else
+ r = User::LoadLogicalDevice(_L("D_DMA"));
+ if (r == KErrNotFound)
+ {
+ test.Printf(_L("DMA not supported - test skipped\n"));
+ return 0;
+ }
+#endif
+ test(r == KErrNone || r == KErrAlreadyExists);
+
+ // Turn off evil lazy dll unloading
+ RLoader l;
+ test(l.Connect()==KErrNone);
+ test(l.CancelLazyDllUnload()==KErrNone);
+ l.Close();
+
+ __UHEAP_MARK;
+ __KHEAP_MARK;
+
+ test.Next(_L("Creating critical section"));
+ test(TheCriticalSection.CreateLocal() == KErrNone);
+
+ test.Next(_L("Creating active scheduler"));
+ CActiveScheduler* pS = new CActiveScheduler;
+ test(pS != NULL);
+ CActiveScheduler::Install(pS);
+
+ test.Next(_L("Creating bipper"));
+ Bipper = CPeriodic::New(CActive::EPriorityStandard);
+ test(Bipper != NULL);
+
+ test.Next(_L("Getting channel info"));
+ GetChannelInfo();
+
+ // Size for the single transfer test
+ TInt totalTransferSize = 64 * KKilo;
+
+ test.Next(_L("Testing one shot single buffer transfer"));
+ RunSbTest(maxchannel, new CFragmentationTest(TestOneShot, maxIter, maxfrag, maxFragSize));
+ RunSbTest(maxchannel, new CDefaultFragTest(TestOneShot, maxIter, totalTransferSize));
+
+ test.Next(_L("Testing one shot double buffer transfer"));
+ RunDbTest(maxchannel, new CFragmentationTest(TestOneShot, maxIter, maxfrag, maxFragSize));
+ RunDbTest(maxchannel, new CDefaultFragTest(TestOneShot, maxIter, totalTransferSize));
+
+ test.Next(_L("Testing one shot scatter/gather transfer"));
+ RunSgTest(maxchannel, new CFragmentationTest(TestOneShot, maxIter, maxfrag, maxFragSize));
+ RunSgTest(maxchannel, new CDefaultFragTest(TestOneShot, maxIter, totalTransferSize));
+
+ test.Next(_L("Testing streaming single buffer transfer"));
+ RunSbTest(maxchannel, new CFragmentationTest(TestStreaming, maxIter, maxfrag, maxFragSize));
+ RunSbTest(maxchannel, new CDefaultFragTest(TestStreaming, maxIter, totalTransferSize));
+
+ test.Next(_L("Testing streaming double buffer transfer"));
+ RunDbTest(maxchannel, new CFragmentationTest(TestStreaming, maxIter, maxfrag, maxFragSize));
+ RunDbTest(maxchannel, new CDefaultFragTest(TestStreaming, maxIter, totalTransferSize));
+
+ test.Next(_L("Testing streaming scatter/gather transfer"));
+ RunSgTest(maxchannel, new CFragmentationTest(TestStreaming, maxIter, maxfrag, maxFragSize));
+ RunSgTest(maxchannel, new CDefaultFragTest(TestStreaming, maxIter, totalTransferSize));
+
+ delete pS;
+ delete Bipper;
+ TheCriticalSection.Close();
+
+ UserSvr::HalFunction(EHalGroupKernel, EKernelHalSupervisorBarrier, (TAny*)5000, 0);
+ __KHEAP_MARKEND;
+ __UHEAP_MARKEND;
+
+ test.End();
+ return 0;
+ }