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